Is Coupert Pure: AI-Powered Coupons & Price Compare safe?
Coupert Pure: AI-Powered Coupons & Price Compare is medium risk. When the visual shopping or image-search flow runs, the extension screenshots the visible tab. It blocks internal pages but allows http/https, returns a PNG data URL, and can POST that image to pure.coupert.com after an AI quota check.…
Who publishes itCoupert Science LLC - 3 other listings from the same operator, 1 of them carrying a finding
Coupert Science LLC - 3 other listings from the same operator, 1 of them carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same store account
3 other listings published from this account, 3.0M+ users between them. 1 of them carries a finding.
Shared hosts - 2 hostnames
Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Visual shopping can screenshot the active web page
When the visual shopping or image-search flow runs, the extension screenshots the visible tab.
It blocks internal pages but allows http/https, returns a PNG data URL, and can POST that image to pure.coupert.com after an AI quota check.
What actually happens
You use the visual shopping or image-search feature on a normal web page.
The extension captures the visible area of the active tab as an image.
The source blocks browser-internal pages but permits ordinary http and https pages.
Data exposed by the screenshot and recommendation request
- Visible page imageCheckout page showing item names, shipping options, and order total (illustrative)
The image can include whatever text, images, account context, and page state are visible on your screen at that moment.
- Captured image body fieldimage_base64: iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII= (illustrative one-pixel PNG)
When the captured value is a data URL, the code strips the prefix and places the remaining image data in a request field.
- Recommendation typetype: products
The request labels the image as product-related context for the visual shopping backend.
Captured request
- Content-Type
- application/json
The code that does this
The shipped handler captures the tab and the recommendation path posts image data
Readable background screenshot handler
background.jstt.bp().on("deals:captureVisibleTab", function() { var e = i(u().mark((function e(t, r) { var n, a, i, s, c, l; return u().wrap((function(e) { for (;;) switch (e.prev = e.next) { case 0: return e.prev = 0, e.next = 3, chrome.tabs.query({ active: !0, currentWindow: !0 }); case 3: if (n = e.sent, a = o(n, 1), null == (i = a[0]) || i.id, null == i || i.url, null == i || i.windowId, null != i && i.windowId) { e.next = 9; break } throw new Error("No active window found"); case 9: if (!((s = i.url || "").startsWith("chrome://") || s.startsWith("chrome-extension://") || s.startsWith("edge://") || s.startsWith("about:") || s.startsWith("file://"))) { e.next = 12; break } throw new Error("Cannot capture this page. Please try on a regular web page."); case 12: return e.next = 14, chrome.tabs.captureVisibleTab(i.windowId, { format: "png" }); case 14: c = e.sent, r({ success: !0, dataUrl: c }), e.next = 25; break; case 19: e.prev = 19, e.t0 = e.catch(0), console.error("[Deals Background] Failed to capture tab:", e.t0), ((l = e.t0.message || String(e.t0)).includes("permission") || l.includes("activeTab") || l.includes("<all_urls>")) && (l = "Please use the screenshot button in the side panel."), r({ success: !1, error: l }); case 25: case "end": return e.stop() } }), e, null, [ [0, 19] ]) }))); return function(t, r) { return e.apply(this, arguments) }}())Readable image recommendation request builder
background.jsvar lO = "products"; function pO(e) { return "string" == typeof e && e.startsWith("data:image/") } function fO(e) { var t = e.indexOf("base64,"); if (-1 === t) throw new Error("Invalid base64 data URL"); return e.substring(t + 7) } function dO() { return hO.apply(this, arguments) } function hO() { return (hO = i(u().mark((function e() { var t, r, n, o, a; return u().wrap((function(e) { for (;;) switch (e.prev = e.next) { case 0: return e.next = 2, bx(); case 2: if (t = e.sent, r = cO({}, t), n = r.data, o = cO({}, n), a = o.token, o.can_use) { e.next = 7; break } throw new Error("AI token unavailable: can_use = false"); case 7: if (a) { e.next = 9; break } throw new Error("AI token missing from /api/ai/chance"); case 9: return e.abrupt("return", a); case 10: case "end": return e.stop() } }), e) })))).apply(this, arguments) } function vO(e) { return mO.apply(this, arguments) } function mO() { return mO = i(u().mark((function e(t) { var r, n, o, a, i, s, c, l, p, f, d, h, v = arguments; return u().wrap((function(e) { for (;;) switch (e.prev = e.next) { case 0: return n = v.length > 1 && void 0 !== v[1] ? v[1] : lO, o = v.length > 2 && void 0 !== v[2] ? v[2] : "", e.next = 4, Lo(); case 4: return a = e.sent, e.next = 7, dO(); case 7: return i = e.sent, s = t || "", c = o || "", pO(t) && (c = fO(t), s = ""), l = { image_url: s || "", image_base64: c || "", type: n }, p = "".concat(a, "/api/ai/image_recommend?token=").concat(encodeURIComponent(i)), (s || "").length, (c || "").length, e.next = 16, fetch(p, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(l) }); case 16: if ((f = e.sent).ok) { e.next = 23; break } return e.next = 20, f.text(); case 20: throw d = e.sent, console.error("[Deals Internal] Error response:", d), new Error("image_recommend failed: ".concat(f.status, " ").concat(f.statusText)); case 23: return e.next = 25, f.json(); case 25: return h = e.sent, Array.isArray(null == h || null === (r = h.data) || void 0 === r ? void 0 : r.recommendations) && h.data.recommendations.length, e.abrupt("return", h); case 29: case "end": return e.stop() } }), e) }))), mO.apply(this, arguments) }External service receiving the image recommendation request
- pure.coupert.com
Coupert API host used for the AI quota check and image recommendation POST.
AI agent sends page content to Coupert for actions
When the AI web-automation agent runs, the content script collects the page URL, title, selected/body text, signals, and history.
The background embeds that in a request to pure.coupert.com/api/ai/gem, then runs returned click/type actions.
What actually happens
You start the AI web-automation agent on an active page.
The extension sends a page snapshot to Coupert and can apply returned browser actions.
The path is feature-triggered; passive analysis did not observe the endpoint without invoking the agent.
Fields included in the AI agent request path
- Page URLhttps://shop.example.com/checkout
The URL identifies the site and exact page where the agent is operating.
- Page titleCheckout - Example Store
The page title gives the remote service context about the workflow open in your browser.
- Page contentOrder summary Shipping options Promo code Apply Total $84.17 (illustrative)
The page text can include checkout details, account text, product details, or other visible information used to decide the next action.
- Signals and metricsinteractiveRefCount: 18; visibleCanvasCount: 0
The request includes page-derived context such as interactive element counts and page state used by the agent.
- Recent agent historyStep 3: [click] {"ref":"ref_12"} -> OK
Recent actions and visited pages let the remote service keep context across multiple automation steps.
Captured request
- Content-Type
- application/json
The code that does this
The agent collects page content, builds the remote prompt, and posts it
Readable page text extractor
content.jsfunction BR(e) { if (!e) return LR; for (var t = 0, r = Object.keys(RR); t < r.length; t++) { var n = r[t]; if (e === n || e.endsWith("." + n)) return [].concat((0, Zi.Z)(RR[n]), LR) } return LR } function FR(e) { return e ? "META" === e.tagName ? (e.getAttribute("content") || "").trim() : (e.innerText || e.textContent || "").trim() : "" } function UR() { var e, t = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}, r = t.hostname || ("undefined" != typeof window ? null === (e = window.location) || void 0 === e ? void 0 : e.hostname : "") || "", n = t.maxLength || 2e3; if ("undefined" == typeof document || !document.body) return { text: "", source: "empty", selectorsHit: [], hostname: r }; var o, i = BR(r), a = [], c = new Set, s = [], u = ZR(i); try { for (u.s(); !(o = u.n()).done;) { var l = o.value; try { var p = document.querySelectorAll(l), d = !1; if (p.forEach((function(e) { var t = FR(e); t.length < 3 || t.length > 500 || c.has(t) || (c.add(t), a.push(t), d = !0) })), d && s.push(l), a.join(" ").length >= n) break } catch (e) {} } } catch (e) { u.e(e) } finally { u.f() } if (a.length > 0) return { text: a.join("\n").slice(0, n), source: "selectors", selectorsHit: s, hostname: r }; var f = (document.body.innerText || "").slice(0, n); return console.warn("[ML-EXTRACT] no selector hit on hostname=".concat(r, ", fallback to body.innerText (len=").concat(f.length, ")")), { text: f, source: "fallback", selectorsHit: [], hostname: r } }Readable ai.agent.think sender
content.jsut = function() { var e = (0, l.Z)(d().mark((function e(t) { var n, o, i, a; return d().wrap((function(e) { for (;;) switch (e.prev = e.next) { case 0: return n = (0, Hr.Qc)(window.location.href) || {}, o = n.domain, i = void 0 === o ? "" : o, e.next = 3, x.ZP.cs().send("ai.agent.think", { task: r, observation: { url: t.url, title: t.title, pageContent: t.pageContent, signals: t.signals, viewport: t.viewport, metrics: t.metrics }, history: j.slice(-20), visitedPages: S.slice(-30), stepCount: T, maxSteps: Ky, domain: i || "" }, { to: x.ZP.Env.BACKGROUND_PAGE }); case 3: return a = e.sent, e.abrupt("return", a); case 5: case "end": return e.stop() } }), e) }))); return function(t) { return e.apply(this, arguments) } }(),Readable Gemini POST helper
background.jsvar hx = "".concat(Vt, "/api/ai/gem"); function vx(e, t) { return mx.apply(this, arguments) } function mx() { return mx = i(u().mark((function e(t, r) { var n, o, a, i, s, c, l, p = arguments; return u().wrap((function(e) { for (;;) switch (e.prev = e.next) { case 0: return n = p.length > 2 && void 0 !== p[2] ? p[2] : null, o = p.length > 3 && void 0 !== p[3] ? p[3] : "", a = "".concat(hx, "?m=").concat(t, "&token=").concat(o), e.next = 5, fetch(a, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(r), signal: n, mode: "no-cors" }); case 5: if ((i = e.sent).ok) { e.next = 12; break } return e.next = 9, i.text(); case 9: c = e.sent; try { l = JSON.parse(c) } catch (e) { l = { error: { message: c } } } throw { status: i.status, message: (null === (s = l.error) || void 0 === s ? void 0 : s.message) || "API error: ".concat(i.status) }; case 12: return e.abrupt("return", i); case 13: case "end": return e.stop() } }), e) }))), mx.apply(this, arguments) } function gx(e, t) { return yx.apply(this, arguments) } function yx() { return yx = i(u().mark((function e(t, r) { var n, o, a, i = arguments; return u().wrap((function(e) { for (;;) switch (e.prev = e.next) { case 0: return n = i.length > 2 && void 0 !== i[2] ? i[2] : null, o = i.length > 3 && void 0 !== i[3] ? i[3] : "", e.next = 4, vx(t, r, n, o); case 4: return a = e.sent, e.abrupt("return", a.json()); case 6: case "end": return e.stop() } }), e) }))), yx.apply(this, arguments) } function bx() { return zo("/api/ai/chance") }Readable prompt builder
background.jsvar aO = function(e) { var t = e.task, r = e.url, n = e.title, a = e.signals, i = e.metrics, s = e.pageContent, u = e.history, c = e.visitedPages, l = e.stepCount, p = (l || 0) + 1, f = e.maxSteps || 50, d = Math.max(0, f - p + 1), h = function(e) { return Array.isArray(e) && 0 !== e.length ? e.map((function(e, t) { var r = e || {}, n = r.action, o = r.params, a = r.result, i = r.error, s = r.targetUrl, u = i ? "FAILED: ".concat(i) : a || "OK", c = o ? JSON.stringify(o) : "{}", l = s ? " => ".concat(s) : ""; return " Step ".concat(t + 1, ": [").concat(n, "] ").concat(c).concat(l, " -> ").concat(u) })).join("\n") : "(No actions taken yet - this is the first step)" }(u), v = function(e) { return Array.isArray(e) && 0 !== e.length ? e.map((function(e, t) { return " ".concat(t + 1, ". [").concat(e.title || "Untitled", "](").concat(e.url, ")") })).join("\n") : "(none yet)" }(c), m = a ? Object.entries(a).map((function(e) { var t = o(e, 2), r = t[0], n = t[1]; return " - ".concat(r, ": ").concat(n) })).join("\n") : "(none)", g = i ? Object.entries(i).map((function(e) { var t = o(e, 2), r = t[0], n = t[1]; return " - ".concat(r, ": ").concat(n) })).join("\n") : "(none)", y = ""; return d <= 3 ? y = "\n⚠️ CRITICAL: You have almost NO steps left!\nYou MUST return `done` NOW with a comprehensive summary of everything you have gathered.\nDo NOT scroll, navigate, or explore further." : d <= 7 ? y = "\n⚠️ WARNING: Very few steps remaining (" + d + " left).\nWrap up immediately. Return `done` with a detailed summary." : d <= 15 ? y = "\n⏰ ATTENTION: Steps running low (" + d + " left).\nStart wrapping up. Collect final info and prepare summary." : d <= 25 && (y = "\n📊 Budget check: " + d + " steps remaining. Plan your remaining actions carefully."), ["You are a goal-oriented web automation assistant.", "You have a STRICT step budget. Every action costs 1 step.", "Your job: complete the task WITHIN budget, then return `done` with a useful summary.", "Return ONLY JSON: {action, params, reasoning}.", 0 === l ? "- Also include `taskName`: a short (≤8 words) title summarizing the user goal." : "", "", "## Task", t || "(No task specified)", "", "## Current Page (Step ".concat(p, " of ").concat(f, ")"), "- URL: ".concat(r || "(unknown)"), "- Title: ".concat(n || "(unknown)"), "- Steps Used: ".concat(p), "- Steps Remaining: ".concat(d), "- Signals:", m, "- Metrics:", g, y, "", "## Pages Already Visited (DO NOT revisit unless necessary)", v, "", "## Action History (last steps)", h, "", "## Available Actions", "| Action | Params |", "|--------|--------|", '| click | { "ref": "ref_N" } |', '| type | { "ref": "ref_N", "text": "v" } |', '| select | { "ref": "ref_N", "value": "v" } |', '| scroll | { "ref": "ref_N" } or', '| | { "direction": "up/down", "amount": px } |', '| hover | { "ref": "ref_N" } |', '| navigate | { "url": "<full_url>" } |', "| back | |", "| reload | |", '| wait | { "ms": num } |', '| extract | { "ref": "ref_N" } |', '| done | { "message": "markdown summary", "data": any } |', '| fail | { "reason": "why" } |', '| ask_user | { "question": "what" } |', "", "## Rules", "- LANGUAGE: You MUST respond in the SAME language as the user's task.", " If the task is in Chinese, ALL your output (reasoning, message, taskName) MUST be in Chinese.", " If the task is in English, respond in English. Match the user's language exactly.", "- Use ref IDs from the element list.", ' e.g. [ref_3] means ref: "ref_3".', "- Refs are stable within the SAME page. After navigation, new refs are assigned.", "- Only use refs in the current list.", "- NEVER click elements marked disabled=true.", '- NEVER click elements marked aria-disabled="true".', "- For comment/reply/search tasks, use `type` on textbox or", " contenteditable first.", "- For search tasks: after one `type`, submit via click/navigate; do NOT keep typing the same query repeatedly on the same URL.", "- NEVER invent a city/location (e.g. 无锡/上海/北京) unless explicitly mentioned in the user task or clearly shown on page.", "- If the task is location-ambiguous, search with original keywords first; ask_user for city only if needed.", "- Click submit only when it is enabled.", "- If target not listed, scroll to reveal.", "- If metrics suggest canvas-heavy page with few actionable refs,", " prefer `ask_user` quickly instead of blind retries.", "- Use `reload` ONLY as a last resort for obvious temporary page lockups.", "- Never use repeated `reload` as a loop strategy.", "- Return ONE action. No markdown/fences.", "- If done, return done. If stuck, ask_user.", "- When returning `done`:", " - `message` is shown to the user as the final answer.", " - Use Markdown formatting (headings, lists, bold).", " - ALWAYS include relevant URLs/links from the page.", " - For recommendations: include title, URL, and reason.", " - For data collection: include all gathered details.", " - Prefer structured JSON in `data` for programmatic use.", " - Recommended `data` shape:", " {", ' "summary": {', ' "completed": string[],', ' "pendingOrUnknown": string[],', ' "currentUrl": string,', ' "nextUserAction": string', " }", " }", ' - If history has action=skill and params.skill="auto_coupon_test":', " 1) message should mention coupon test outcome explicitly.", " 2) data should include `couponTest`:", " { outcome, source, summaryText, hasValidCoupons,", " bestCode, savedAmount, testedCount }", " 3) If message already includes coupon outcome, set", " data.couponTestIncludedInMessage=true, else false.", "- If previous action failed, try differently.", "- If you return to a page you have seen before, treat links you already opened from that same page as excluded candidates.", "- Do NOT click the same navigation target twice from the same source page unless the user explicitly asks for a retry.", "- Do NOT guess random URLs. But you CAN navigate to", " well-known sites (Google, Amazon, eBay, etc.) to search.", "- NEVER call `navigate` to the current page URL.", "", "## Multi-Channel Strategy", "- If current page lacks useful info after 2-3 attempts:", " 1. Navigate to a search engine result page with refined query (do not inject new location keywords unless provided).", " 2. Try a different website (e.g. Amazon, eBay, official site).", " 3. Use `done` to return partial results rather than looping.", "- Do NOT keep scrolling/clicking the same page hoping for new content.", "- Prefer navigating to a new source over repeating failed actions.", "- For product/price tasks: try multiple retailers, not just one.", "", "## Execution Strategy (CRITICAL)", "", "### Three-Phase Approach", "- Phase 1 (first 40% of steps): EXPLORE — navigate to relevant pages, search for info.", "- Phase 2 (next 40% of steps): COLLECT — read page content, extract key data.", "- Phase 3 (last 20% of steps): SUMMARIZE — return `done` with comprehensive results.", "- Calculate your phase boundaries NOW:", " Phase 1 ends at step ~".concat(Math.round(.4 * f)), " Phase 2 ends at step ~".concat(Math.round(.8 * f)), " Phase 3 starts at step ~".concat(Math.round(.8 * f), " — you MUST start wrapping up"), "", "### Step Budget Rules", "- Total budget: ".concat(f, " steps. Used: ").concat(p, ". Remaining: ").concat(d, "."), "- EVERY action (click, scroll, navigate) costs 1 step.", "- Reading page content is FREE — it is included in each observation.", "- Prefer READING the current page over clicking into sub-pages.", "- When remaining <= 7: STOP exploring, return `done` immediately.", "- When remaining <= 3: return `done` with whatever you have. No exceptions.", "", "### Efficiency Rules", "- On search results pages: READ the snippets directly. Only click 2-3 best links.", "- On content pages: READ the visible text. Use `extract` only for specific elements.", "- Do NOT click every link. Do NOT scroll endlessly.", "- If you have enough info to answer the task, return `done` IMMEDIATELY.", "- Each click should have a clear purpose stated in your reasoning.", "", "### Anti-Loop Rules", "- Never repeat the same action 3x in a row.", "- If URL changed after a click, the click WORKED. Read the new page, do NOT re-click.", '- If last 3+ actions are same type: STOP and ask yourself "am I making progress?"', "- If stuck on a page after 2 attempts, navigate elsewhere or return `done`.", "- If the last 6 actions are mostly `extract`/`scroll` on the SAME URL", " and no new page is visited, you MUST stop looping and return `done`", " with a best-effort summary.", "- Do NOT keep extracting the same checkout/payment section repeatedly.", "", "### No-Result Recovery (MANDATORY)", "- If you cannot find new actionable elements or new facts after 2-3 attempts,", " return `done` immediately instead of continuing to loop.", "- `done` message must explicitly include:", " 1) Completed items,", " 2) Not completed / uncertain items,", " 3) Current page URL,", " 4) Recommended next user action (if any).", "- For checkout-oriented tasks:", " if already on checkout/payment page and no better next action exists,", " return `done` with current checkout status.", "", "## Page Content", s || "(empty)", "", "Return the next action as JSON now."].join("\n") };Readable ai.agent.think handler
background.jstt.bp().on("ai.agent.think", function() { var e = i(u().mark((function e(t, r, n) { var o, a, i, s, c, l, p, f, d, h, v, m, g, y, b, w, x, k, _, O, j, S, A, P, E, T, D, C; return u().wrap((function(e) { for (;;) switch (e.prev = e.next) { case 0: return e.prev = 0, l = X_({}, t), p = l.content, f = p.task, d = void 0 === f ? "" : f, h = p.observation, v = void 0 === h ? {} : h, m = p.history, g = void 0 === m ? [] : m, y = p.visitedPages, b = void 0 === y ? [] : y, w = p.stepCount, x = void 0 === w ? 0 : w, k = p.maxSteps, _ = void 0 === k ? 50 : k, e.next = 5, bx(); case 5: if (O = e.sent, j = (null == O ? void 0 : O.data) || {}, S = j.token, j.can_use) { e.next = 10; break } return r({ action: "ask_user", params: { question: "AI quota exhausted. Please try again later." }, reasoning: "No AI credits available." }), e.abrupt("return"); case 10: return A = v.pageContent || "", P = aO({ task: d, url: v.url, title: v.title, signals: v.signals, metrics: v.metrics, pageContent: A, history: g, visitedPages: b, stepCount: x, maxSteps: _ }), E = { contents: [{ parts: [{ text: P }] }], generationConfig: { temperature: .3 } }, e.next = 15, gx(6, E, null, S); case 15: return T = e.sent, e.next = 18, oO(t, T); case 18: if (null == T || null === (o = T.candidates) || void 0 === o || null === (a = o[0]) || void 0 === a || null === (i = a.content) || void 0 === i || null === (s = i.parts) || void 0 === s || null === (c = s[0]) || void 0 === c || !c.text) { e.next = 22; break } D = T.candidates[0].content.parts[0].text; try { C = rO(D), r(C) } catch (e) { console.warn("[Agent Think] JSON parse failed:", D), r({ action: "ask_user", params: { question: "AI returned invalid response. Please help." }, reasoning: "Failed to parse AI response." }) } return e.abrupt("return"); case 22: r({ action: "ask_user", params: { question: "AI returned empty response. Please help." }, reasoning: "Empty AI response." }), e.next = 29; break; case 25: e.prev = 25, e.t0 = e.catch(0), console.error("[Agent Think] Error:", e.t0), n(e.t0); case 29: case "end": return e.stop() } }), e, null, [ [0, 25] ]) }))); return function(t, r, n) { return e.apply(this, arguments) } }())The code that does this
Returned actions are dispatched back into the active page
function mw() { return mw = (0, l.Z)(d().mark((function e(t) { var r, n, o, i, a, c, s = arguments; return d().wrap((function(e) { for (;;) switch (e.prev = e.next) { case 0: r = s.length > 1 && void 0 !== s[1] ? s[1] : {}, n = t.type, o = t.params, i = void 0 === o ? {} : o, a = r.shadowHosts, c = void 0 === a ? [] : a, e.t0 = n, e.next = e.t0 === By.CLICK ? 6 : e.t0 === By.TYPE ? 7 : e.t0 === By.SELECT ? 8 : e.t0 === By.SCROLL ? 9 : e.t0 === By.HOVER ? 10 : e.t0 === By.NAVIGATE ? 11 : e.t0 === By.BACK ? 12 : e.t0 === By.RELOAD ? 13 : e.t0 === By.WAIT ? 14 : e.t0 === By.EXTRACT ? 15 : 16; break; case 6: return e.abrupt("return", bw(c, i)); case 7: return e.abrupt("return", yw(c, i)); case 8: return e.abrupt("return", ww(c, i)); case 9: return e.abrupt("return", kw(c, i)); case 10: return e.abrupt("return", Pw(c, i)); case 11: return e.abrupt("return", Ew(i)); case 12: return e.abrupt("return", Sw()); case 13: return e.abrupt("return", jw()); case 14: return e.abrupt("return", Aw(c, i)); case 15: return e.abrupt("return", Zw(c, i)); case 16: return e.abrupt("return", { success: !1, error: "Unknown action: ".concat(n) }); case 17: case "end": return e.stop() } }), e) }))), mw.apply(this, arguments) }Remote service used by the AI agent
- pure.coupert.com
Coupert API host used for the AI quota check and Gemini-format agent request.
