mcp-scraper 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/bin/api-server.cjs +917 -221
  2. package/dist/bin/api-server.cjs.map +1 -1
  3. package/dist/bin/api-server.js +2 -2
  4. package/dist/bin/mcp-scraper-cli.cjs +2 -1
  5. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  6. package/dist/bin/mcp-scraper-cli.js +3 -3
  7. package/dist/bin/mcp-scraper-install.cjs +1 -1
  8. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  9. package/dist/bin/mcp-scraper-install.js +1 -1
  10. package/dist/bin/mcp-stdio-server.cjs +64 -21
  11. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  12. package/dist/bin/mcp-stdio-server.js +3 -3
  13. package/dist/bin/paa-harvest.cjs +1 -0
  14. package/dist/bin/paa-harvest.cjs.map +1 -1
  15. package/dist/bin/paa-harvest.js +2 -2
  16. package/dist/{chunk-6TDUY7CA.js → chunk-4MFYMUIF.js} +25 -3
  17. package/dist/chunk-4MFYMUIF.js.map +1 -0
  18. package/dist/chunk-7HICT4GY.js +7 -0
  19. package/dist/chunk-7HICT4GY.js.map +1 -0
  20. package/dist/{chunk-NNEIXK5L.js → chunk-FUWZWKGO.js} +2 -2
  21. package/dist/{chunk-QUOZ3LZG.js → chunk-GFY6XHZS.js} +63 -21
  22. package/dist/chunk-GFY6XHZS.js.map +1 -0
  23. package/dist/{chunk-HBC4C75S.js → chunk-NJK5BTUK.js} +2 -2
  24. package/dist/{chunk-JOEZJL4I.js → chunk-NVUBFCJK.js} +2 -2
  25. package/dist/{chunk-BP27CZ5Q.js → chunk-XGIPATLV.js} +2 -1
  26. package/dist/chunk-XGIPATLV.js.map +1 -0
  27. package/dist/index.cjs +1 -0
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.js +2 -2
  30. package/dist/{server-U7V6DUSY.js → server-6IYQQK6X.js} +793 -210
  31. package/dist/server-6IYQQK6X.js.map +1 -0
  32. package/dist/{site-extract-repository-2MQBAOX5.js → site-extract-repository-4NVO5KQA.js} +3 -3
  33. package/dist/{worker-RVZXIALX.js → worker-7XRZNLKB.js} +4 -4
  34. package/package.json +2 -1
  35. package/dist/chunk-2ULBZRQQ.js +0 -7
  36. package/dist/chunk-2ULBZRQQ.js.map +0 -1
  37. package/dist/chunk-6TDUY7CA.js.map +0 -1
  38. package/dist/chunk-BP27CZ5Q.js.map +0 -1
  39. package/dist/chunk-QUOZ3LZG.js.map +0 -1
  40. package/dist/server-U7V6DUSY.js.map +0 -1
  41. /package/dist/{chunk-NNEIXK5L.js.map → chunk-FUWZWKGO.js.map} +0 -0
  42. /package/dist/{chunk-HBC4C75S.js.map → chunk-NJK5BTUK.js.map} +0 -0
  43. /package/dist/{chunk-JOEZJL4I.js.map → chunk-NVUBFCJK.js.map} +0 -0
  44. /package/dist/{site-extract-repository-2MQBAOX5.js.map → site-extract-repository-4NVO5KQA.js.map} +0 -0
  45. /package/dist/{worker-RVZXIALX.js.map → worker-7XRZNLKB.js.map} +0 -0
@@ -16,7 +16,7 @@ import {
16
16
  recordVendorUsage,
17
17
  resolveKernelProxyId,
18
18
  runWithCostContext
19
- } from "./chunk-HBC4C75S.js";
19
+ } from "./chunk-NJK5BTUK.js";
20
20
  import {
21
21
  HttpMcpToolExecutor,
22
22
  MemoryMcpToolExecutor,
@@ -31,7 +31,7 @@ import {
31
31
  registerSerpIntelligenceCaptureTools,
32
32
  sanitizeAttempts,
33
33
  sanitizeHarvestResult
34
- } from "./chunk-QUOZ3LZG.js";
34
+ } from "./chunk-GFY6XHZS.js";
35
35
  import {
36
36
  auditImages,
37
37
  getBlobStore
@@ -53,7 +53,7 @@ import {
53
53
  workflowOutputBaseDir,
54
54
  workflowStepCount,
55
55
  workflowSupportsSteps
56
- } from "./chunk-NNEIXK5L.js";
56
+ } from "./chunk-FUWZWKGO.js";
57
57
  import "./chunk-K443GQY5.js";
58
58
  import {
59
59
  DEFAULT_MAPS_PROXY_MODE,
@@ -64,8 +64,8 @@ import {
64
64
  RawMapsHoursRowSchema,
65
65
  RawMapsOverviewSchema,
66
66
  RawMapsReviewStatsSchema
67
- } from "./chunk-BP27CZ5Q.js";
68
- import "./chunk-2ULBZRQQ.js";
67
+ } from "./chunk-XGIPATLV.js";
68
+ import "./chunk-7HICT4GY.js";
69
69
  import {
70
70
  completeExtractJob,
71
71
  countSuccessfulPages,
@@ -76,7 +76,7 @@ import {
76
76
  saveExtractPages,
77
77
  setExtractJobTotal,
78
78
  settleExtractJob
79
- } from "./chunk-JOEZJL4I.js";
79
+ } from "./chunk-NVUBFCJK.js";
80
80
  import {
81
81
  BROWSER_OPEN_MIN_BALANCE_MC,
82
82
  CONCURRENCY_PRICE_ID,
@@ -95,7 +95,7 @@ import {
95
95
  browserActiveCostMc,
96
96
  concurrencySlotBillingInfo,
97
97
  insufficientBalanceResponse
98
- } from "./chunk-6TDUY7CA.js";
98
+ } from "./chunk-4MFYMUIF.js";
99
99
  import {
100
100
  CaptchaError,
101
101
  RECAPTCHA_INSTRUCTIONS,
@@ -11846,6 +11846,8 @@ function verifySession(token) {
11846
11846
  }
11847
11847
 
11848
11848
  // src/api/memory.ts
11849
+ import { provisionDefaultsTool } from "mcpscraper-memory-tools/tools/vaults/provision-defaults";
11850
+ import { setScheduleEntitlementTool } from "mcpscraper-memory-tools/tools/schedule/set-schedule-entitlement";
11849
11851
  var MEMORY_BASE_URL = () => (process.env.MCP_MEMORY_BASE_URL ?? "https://memory.mcpscraper.dev").replace(/\/$/, "");
11850
11852
  var ADMIN_KEY = () => process.env.MCP_MEMORY_ADMIN_KEY?.trim() ?? "";
11851
11853
  function parseMemoryRpcBody(text) {
@@ -11933,6 +11935,24 @@ async function provisionMemoryForUser(user) {
11933
11935
  } catch {
11934
11936
  }
11935
11937
  }
11938
+ function memoryPlanForUser(user) {
11939
+ switch (user.subscription_tier) {
11940
+ case "starter":
11941
+ return "pro";
11942
+ case "growth":
11943
+ return "team";
11944
+ case "scale":
11945
+ return "enterprise";
11946
+ default:
11947
+ return "free";
11948
+ }
11949
+ }
11950
+ var MEMORY_PLAN_RANK = { free: 0, pro: 1, team: 2, enterprise: 3 };
11951
+ function resolveEffectiveMemoryPlan(user) {
11952
+ const tierPlan = memoryPlanForUser(user);
11953
+ const memoryProPlan = user.memory_plan === "pro" || user.memory_plan === "team" ? user.memory_plan : "free";
11954
+ return MEMORY_PLAN_RANK[tierPlan] >= MEMORY_PLAN_RANK[memoryProPlan] ? tierPlan : memoryProPlan;
11955
+ }
11936
11956
  async function getOrCreateUserMemoryKey(user) {
11937
11957
  const creds = await getUserMemoryCreds(user.id);
11938
11958
  if (creds.memory_key) {
@@ -11943,10 +11963,9 @@ async function getOrCreateUserMemoryKey(user) {
11943
11963
  if (!admin) return { key: null, error: "memory not configured (MCP_MEMORY_ADMIN_KEY missing)" };
11944
11964
  const identity = memoryIdentity(user);
11945
11965
  const plan = isMemoryOperator(user.email) ? "unlimited" : (await getMemoryPlan(user.id)).plan;
11946
- const provisioned = await memoryCall(
11947
- "provisionDefaultsTool",
11948
- { granteeIdentity: identity, issueKey: true, plan },
11949
- admin
11966
+ const provisioned = await provisionDefaultsTool.execute(
11967
+ { apiKey: admin, granteeIdentity: identity, issueKey: true, plan },
11968
+ {}
11950
11969
  );
11951
11970
  if (!provisioned.ok || !provisioned.secret) {
11952
11971
  return { key: null, error: provisioned.error ?? "failed to provision default vaults" };
@@ -11973,12 +11992,13 @@ async function syncScheduleEntitlement(user, enabled, quotaPerPeriod) {
11973
11992
  const admin = ADMIN_KEY();
11974
11993
  if (!admin) return { ok: false, error: "memory not configured (MCP_MEMORY_ADMIN_KEY missing)" };
11975
11994
  const identity = resolveMemoryIdentity(user);
11976
- const res = await memoryCall("setScheduleEntitlementTool", {
11995
+ const res = await setScheduleEntitlementTool.execute({
11996
+ apiKey: admin,
11977
11997
  granteeIdentity: identity,
11978
11998
  enabled,
11979
11999
  quotaPerPeriod,
11980
12000
  mcpScraperApiKey: user.api_key
11981
- }, admin);
12001
+ }, {});
11982
12002
  return { ok: res.ok, error: res.error };
11983
12003
  }
11984
12004
 
@@ -12138,6 +12158,33 @@ var MapsNavigator = class {
12138
12158
  await this.page.waitForSelector('[role="main"] h2', { timeout: 5e3 }).catch(() => {
12139
12159
  });
12140
12160
  }
12161
+ async navigateToSerpBusinessPanel(businessName, location2) {
12162
+ const query = `${businessName} ${location2}`;
12163
+ const searchUrl = `https://www.google.com/search?q=${encodeURIComponent(query)}&udm=1`;
12164
+ await this.page.goto(searchUrl, { waitUntil: "domcontentloaded", timeout: 45e3 }).catch(() => {
12165
+ });
12166
+ await this.page.waitForTimeout(2e3);
12167
+ const clicked = await this.page.evaluate((name) => {
12168
+ const trimmedName = name.trim();
12169
+ const words = trimmedName.split(/\s+/);
12170
+ const prefix = words.slice(0, Math.min(2, words.length)).join(" ");
12171
+ const headings = Array.from(document.querySelectorAll('h3, div[role="heading"]'));
12172
+ const heading = headings.find((el2) => el2.innerText?.trim() === trimmedName) ?? headings.find((el2) => el2.innerText?.trim().startsWith(prefix));
12173
+ const clickable = heading?.closest('a, div[role="link"], div[jsaction]');
12174
+ if (clickable) {
12175
+ clickable.click();
12176
+ return true;
12177
+ }
12178
+ if (heading) {
12179
+ heading.click();
12180
+ return true;
12181
+ }
12182
+ return false;
12183
+ }, businessName);
12184
+ if (!clicked) return false;
12185
+ await this.page.waitForTimeout(2e3);
12186
+ return true;
12187
+ }
12141
12188
  buildReviewsUrl(placeUrl) {
12142
12189
  const fid = placeUrl.match(/!1s(0x[0-9a-f]+:0x[0-9a-f]+)/i);
12143
12190
  const pin = placeUrl.match(/!3d(-?\d+\.\d+)!4d(-?\d+\.\d+)/);
@@ -12307,6 +12354,25 @@ var MapsExtractor = class {
12307
12354
  }
12308
12355
  await navigator.navigateToAboutTab();
12309
12356
  const aboutAttributes = await this.extractAbout(page);
12357
+ let services = [];
12358
+ let areasServed = [];
12359
+ let servicesStatus = "not_requested";
12360
+ if (options.includeServices) {
12361
+ try {
12362
+ const opened = await navigator.navigateToSerpBusinessPanel(options.businessName, options.location);
12363
+ if (opened) {
12364
+ const result = await this.extractServicesAndAreasServed(page);
12365
+ services = result.services;
12366
+ areasServed = result.areasServed;
12367
+ servicesStatus = services.length > 0 || areasServed.length > 0 ? "collected" : "none_exist";
12368
+ } else {
12369
+ servicesStatus = "unavailable";
12370
+ }
12371
+ } catch (err) {
12372
+ console.warn("[MapsExtractor] services/areas-served extraction failed:", err instanceof Error ? err.message : String(err));
12373
+ servicesStatus = "unavailable";
12374
+ }
12375
+ }
12310
12376
  const reviewCountFinal = overview.reviewCount ?? (reviewStats.reviewHistogram.length > 0 ? reviewStats.reviewHistogram.reduce((s, r) => s + parseInt(r.count, 10), 0).toString() : null);
12311
12377
  return {
12312
12378
  businessName: options.businessName,
@@ -12322,6 +12388,9 @@ var MapsExtractor = class {
12322
12388
  aboutAttributes,
12323
12389
  reviews,
12324
12390
  reviewsStatus,
12391
+ services,
12392
+ areasServed,
12393
+ servicesStatus,
12325
12394
  durationMs: Date.now() - startMs
12326
12395
  };
12327
12396
  } finally {
@@ -12510,6 +12579,81 @@ var MapsExtractor = class {
12510
12579
  }
12511
12580
  return result.data;
12512
12581
  }
12582
+ async extractServicesAndAreasServed(page) {
12583
+ await page.waitForTimeout(1500);
12584
+ let services = [];
12585
+ if (await this.clickRowStartingWithRetry(page, "Services:")) {
12586
+ await page.waitForSelector('[role="dialog"]', { timeout: 5e3 }).catch(() => {
12587
+ });
12588
+ await page.waitForTimeout(800);
12589
+ services = await this.extractOpenDialogItems(page, "Services");
12590
+ await this.closeOpenDialog(page, "Services");
12591
+ await page.waitForTimeout(500);
12592
+ }
12593
+ let areasServed = [];
12594
+ if (await this.clickRowStartingWithRetry(page, "Serves ")) {
12595
+ await page.waitForSelector('[role="dialog"]', { timeout: 5e3 }).catch(() => {
12596
+ });
12597
+ await page.waitForTimeout(800);
12598
+ areasServed = await this.extractOpenDialogItems(page, "Areas Served");
12599
+ await this.closeOpenDialog(page, "Areas Served");
12600
+ }
12601
+ return { services, areasServed };
12602
+ }
12603
+ async clickRowStartingWithRetry(page, prefix) {
12604
+ if (await this.clickRowStartingWith(page, prefix)) return true;
12605
+ await page.waitForTimeout(2e3);
12606
+ return this.clickRowStartingWith(page, prefix);
12607
+ }
12608
+ async clickRowStartingWith(page, prefix) {
12609
+ return page.evaluate((p) => {
12610
+ const el2 = Array.from(document.querySelectorAll('button, a, div[role="button"]')).find(
12611
+ (e) => (e.innerText ?? "").trim().startsWith(p)
12612
+ );
12613
+ if (!el2) return false;
12614
+ el2.click();
12615
+ return true;
12616
+ }, prefix);
12617
+ }
12618
+ async extractOpenDialogItems(page, expectedHeading) {
12619
+ return page.evaluate((expected) => {
12620
+ const dialogs = Array.from(document.querySelectorAll('[role="dialog"]'));
12621
+ const withHeading = dialogs.find((d) => {
12622
+ const h = d.querySelector('h1, h2, [role="heading"]');
12623
+ const headingText = h?.innerText?.trim() ?? "";
12624
+ return headingText.startsWith(expected);
12625
+ });
12626
+ if (!withHeading) return [];
12627
+ const headingEl = withHeading.querySelector('h1, h2, [role="heading"]');
12628
+ const headingLines = new Set(
12629
+ (headingEl?.innerText ?? "").split("\n").map((s) => s.trim()).filter(Boolean)
12630
+ );
12631
+ const seen = /* @__PURE__ */ new Set();
12632
+ const items = [];
12633
+ withHeading.querySelectorAll("*").forEach((el2) => {
12634
+ if (el2.children.length > 0) return;
12635
+ const text = el2.innerText?.trim() ?? el2.textContent?.trim() ?? "";
12636
+ if (!text || headingLines.has(text)) return;
12637
+ if (/^(back|close|done|thanks)$/i.test(text)) return;
12638
+ if (/^serves\s.*following/i.test(text)) return;
12639
+ if (seen.has(text)) return;
12640
+ seen.add(text);
12641
+ items.push(text);
12642
+ });
12643
+ return items;
12644
+ }, expectedHeading);
12645
+ }
12646
+ async closeOpenDialog(page, expectedHeading) {
12647
+ await page.evaluate((expected) => {
12648
+ const dialog = Array.from(document.querySelectorAll('[role="dialog"]')).find((d) => {
12649
+ const h = d.querySelector('h1, h2, [role="heading"]');
12650
+ const headingText = h?.innerText?.trim() ?? "";
12651
+ return headingText.startsWith(expected);
12652
+ });
12653
+ const closeBtn = dialog?.querySelector('[aria-label*="Close" i], button[aria-label*="close" i]');
12654
+ closeBtn?.click();
12655
+ }, expectedHeading);
12656
+ }
12513
12657
  };
12514
12658
 
12515
12659
  // src/extractor/MapsSearchExtractor.ts
@@ -14908,6 +15052,444 @@ serpIntelligenceApp.post("/page-snapshots", async (c) => {
14908
15052
  import { Hono as Hono14 } from "hono";
14909
15053
  import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
14910
15054
 
15055
+ // src/mcp/memory-inprocess-executor.ts
15056
+ var CUTOVER = /* @__PURE__ */ new Map();
15057
+ function registerCutover(upstreamName, handler) {
15058
+ CUTOVER.set(upstreamName, handler);
15059
+ }
15060
+ var HybridMemoryToolExecutor = class {
15061
+ constructor(httpExecutor, user) {
15062
+ this.httpExecutor = httpExecutor;
15063
+ this.user = user;
15064
+ }
15065
+ httpExecutor;
15066
+ user;
15067
+ mkKeyPromise = null;
15068
+ async resolveMkKey() {
15069
+ if (!this.mkKeyPromise) {
15070
+ this.mkKeyPromise = getOrCreateUserMemoryKey(this.user).then((r) => r.key);
15071
+ }
15072
+ return this.mkKeyPromise;
15073
+ }
15074
+ async callMemoryTool(upstreamName, args) {
15075
+ const handler = CUTOVER.get(upstreamName);
15076
+ if (!handler) return this.httpExecutor.callMemoryTool(upstreamName, args);
15077
+ const mkKey = await this.resolveMkKey();
15078
+ if (!mkKey) return { content: [{ type: "text", text: "memory unavailable" }], isError: true };
15079
+ try {
15080
+ return await handler(args, mkKey);
15081
+ } catch (err) {
15082
+ const message = err instanceof Error ? err.message : `memory ${upstreamName} failed in-process`;
15083
+ return { content: [{ type: "text", text: message }], isError: true };
15084
+ }
15085
+ }
15086
+ };
15087
+
15088
+ // src/mcp/memory-cutover/reads.ts
15089
+ import { listApprovedSendersTool } from "mcpscraper-memory-tools/tools/access/list-approved-senders";
15090
+ import { noteInboxTool } from "mcpscraper-memory-tools/tools/access/note-inbox";
15091
+ import { getMessageNoteTool } from "mcpscraper-memory-tools/tools/channels/get-message-note";
15092
+ import { listChannelMembersTool } from "mcpscraper-memory-tools/tools/channels/list-channel-members";
15093
+ import { myMentionsTool } from "mcpscraper-memory-tools/tools/channels/my-mentions";
15094
+ import { factHistoryTool } from "mcpscraper-memory-tools/tools/facts/fact-history";
15095
+ import { exportTool } from "mcpscraper-memory-tools/tools/memory/export";
15096
+ import { getTool } from "mcpscraper-memory-tools/tools/memory/get";
15097
+ import { listTool } from "mcpscraper-memory-tools/tools/memory/list";
15098
+ import { searchTool } from "mcpscraper-memory-tools/tools/memory/search";
15099
+ import { suggestTool } from "mcpscraper-memory-tools/tools/memory/suggest";
15100
+ import { temporalRecallTool } from "mcpscraper-memory-tools/tools/recall/temporal-recall";
15101
+ import { listScheduledActionsTool } from "mcpscraper-memory-tools/tools/schedule/list-scheduled-actions";
15102
+ import { costUsageTool } from "mcpscraper-memory-tools/tools/storage/cost-usage";
15103
+ import { storageUsageTool } from "mcpscraper-memory-tools/tools/storage/storage-usage";
15104
+ import { describeTableTool } from "mcpscraper-memory-tools/tools/tables/table-describe";
15105
+ import { listTablesTool } from "mcpscraper-memory-tools/tools/tables/table-list";
15106
+ import { queryTableTool } from "mcpscraper-memory-tools/tools/tables/table-query";
15107
+ import { listSharedWithMeTool } from "mcpscraper-memory-tools/tools/vaults/list-shared-with-me";
15108
+ import { listVaultsTool } from "mcpscraper-memory-tools/tools/vaults/list-vaults";
15109
+ import { videoAnalyzeStatusTool } from "mcpscraper-memory-tools/tools/video/status";
15110
+ import { listWebhooksTool } from "mcpscraper-memory-tools/tools/webhooks/list-webhooks";
15111
+ var registered = false;
15112
+ function registerReadCutovers() {
15113
+ if (registered) return;
15114
+ registered = true;
15115
+ registerCutover("listApprovedSendersTool", async (input, apiKey) => {
15116
+ const result = await listApprovedSendersTool.execute({ ...input, apiKey }, {});
15117
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15118
+ });
15119
+ registerCutover("noteInboxTool", async (input, apiKey) => {
15120
+ const result = await noteInboxTool.execute({ ...input, apiKey }, {});
15121
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15122
+ });
15123
+ registerCutover("getMessageNoteTool", async (input, apiKey) => {
15124
+ const result = await getMessageNoteTool.execute({ ...input, apiKey }, {});
15125
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15126
+ });
15127
+ registerCutover("listChannelMembersTool", async (input, apiKey) => {
15128
+ const result = await listChannelMembersTool.execute({ ...input, apiKey }, {});
15129
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15130
+ });
15131
+ registerCutover("myMentionsTool", async (input, apiKey) => {
15132
+ const result = await myMentionsTool.execute({ ...input, apiKey }, {});
15133
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15134
+ });
15135
+ registerCutover("factHistoryTool", async (input, apiKey) => {
15136
+ const result = await factHistoryTool.execute({ ...input, apiKey }, {});
15137
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15138
+ });
15139
+ registerCutover("exportTool", async (input, apiKey) => {
15140
+ const result = await exportTool.execute({ ...input, apiKey }, {});
15141
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15142
+ });
15143
+ registerCutover("getTool", async (input, apiKey) => {
15144
+ const result = await getTool.execute({ ...input, apiKey }, {});
15145
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15146
+ });
15147
+ registerCutover("listTool", async (input, apiKey) => {
15148
+ const result = await listTool.execute({ ...input, apiKey }, {});
15149
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15150
+ });
15151
+ registerCutover("searchTool", async (input, apiKey) => {
15152
+ const result = await searchTool.execute({ ...input, apiKey }, {});
15153
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15154
+ });
15155
+ registerCutover("suggestTool", async (input, apiKey) => {
15156
+ const result = await suggestTool.execute({ ...input, apiKey }, {});
15157
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15158
+ });
15159
+ registerCutover("temporalRecallTool", async (input, apiKey) => {
15160
+ const result = await temporalRecallTool.execute({ ...input, apiKey }, {});
15161
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15162
+ });
15163
+ registerCutover("listScheduledActionsTool", async (input, apiKey) => {
15164
+ const result = await listScheduledActionsTool.execute({ ...input, apiKey }, {});
15165
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15166
+ });
15167
+ registerCutover("costUsageTool", async (input, apiKey) => {
15168
+ const result = await costUsageTool.execute({ ...input, apiKey }, {});
15169
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15170
+ });
15171
+ registerCutover("storageUsageTool", async (input, apiKey) => {
15172
+ const result = await storageUsageTool.execute({ ...input, apiKey }, {});
15173
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15174
+ });
15175
+ registerCutover("describeTableTool", async (input, apiKey) => {
15176
+ const result = await describeTableTool.execute({ ...input, apiKey }, {});
15177
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15178
+ });
15179
+ registerCutover("listTablesTool", async (input, apiKey) => {
15180
+ const result = await listTablesTool.execute({ ...input, apiKey }, {});
15181
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15182
+ });
15183
+ registerCutover("queryTableTool", async (input, apiKey) => {
15184
+ const result = await queryTableTool.execute({ ...input, apiKey }, {});
15185
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15186
+ });
15187
+ registerCutover("listSharedWithMeTool", async (input, apiKey) => {
15188
+ const result = await listSharedWithMeTool.execute({ ...input, apiKey }, {});
15189
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15190
+ });
15191
+ registerCutover("listVaultsTool", async (input, apiKey) => {
15192
+ const result = await listVaultsTool.execute({ ...input, apiKey }, {});
15193
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15194
+ });
15195
+ registerCutover("videoAnalyzeStatusTool", async (input, apiKey) => {
15196
+ const result = await videoAnalyzeStatusTool.execute({ ...input, apiKey }, {});
15197
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15198
+ });
15199
+ registerCutover("listWebhooksTool", async (input, apiKey) => {
15200
+ const result = await listWebhooksTool.execute({ ...input, apiKey }, {});
15201
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15202
+ });
15203
+ }
15204
+
15205
+ // src/mcp/memory-cutover/writes.ts
15206
+ import { acceptShareTool } from "mcpscraper-memory-tools/tools/access/accept-share";
15207
+ import { approveSenderTool } from "mcpscraper-memory-tools/tools/access/approve-sender";
15208
+ import { declineShareTool } from "mcpscraper-memory-tools/tools/access/decline-share";
15209
+ import { inboxSettingsTool } from "mcpscraper-memory-tools/tools/access/inbox-settings";
15210
+ import { removeApprovedSenderTool } from "mcpscraper-memory-tools/tools/access/remove-approved-sender";
15211
+ import { revokeShareTool } from "mcpscraper-memory-tools/tools/access/revoke-share";
15212
+ import { setAgentIdentityTool } from "mcpscraper-memory-tools/tools/access/set-agent-identity";
15213
+ import { shareNoteTool } from "mcpscraper-memory-tools/tools/access/share-note";
15214
+ import { shareVaultTool } from "mcpscraper-memory-tools/tools/access/share-vault";
15215
+ import { swapVaultTool } from "mcpscraper-memory-tools/tools/access/swap-vault";
15216
+ import { switchAccountTool } from "mcpscraper-memory-tools/tools/access/switch-account";
15217
+ import { unlinkShareTool } from "mcpscraper-memory-tools/tools/access/unlink-share";
15218
+ import { memoryQuestionsTool } from "mcpscraper-memory-tools/tools/capture/memory-questions";
15219
+ import { createChannelTool } from "mcpscraper-memory-tools/tools/channels/create-channel";
15220
+ import { listChannelMessagesTool } from "mcpscraper-memory-tools/tools/channels/list-channel-messages";
15221
+ import { pollChannelTool } from "mcpscraper-memory-tools/tools/channels/poll-channel";
15222
+ import { postMessageTool } from "mcpscraper-memory-tools/tools/channels/post-message";
15223
+ import { reactMessageTool } from "mcpscraper-memory-tools/tools/channels/react-message";
15224
+ import { removeChannelMemberTool } from "mcpscraper-memory-tools/tools/channels/remove-channel-member";
15225
+ import { replyMessageTool } from "mcpscraper-memory-tools/tools/channels/reply-message";
15226
+ import { recordFactTool } from "mcpscraper-memory-tools/tools/facts/record-fact";
15227
+ import { libraryIngestTool } from "mcpscraper-memory-tools/tools/library/library-ingest";
15228
+ import { deleteNoteTool } from "mcpscraper-memory-tools/tools/memory/delete-note";
15229
+ import { putTool } from "mcpscraper-memory-tools/tools/memory/put";
15230
+ import { uploadTool } from "mcpscraper-memory-tools/tools/memory/upload";
15231
+ import { deleteScheduledActionTool } from "mcpscraper-memory-tools/tools/schedule/delete-scheduled-action";
15232
+ import { pauseScheduledActionTool } from "mcpscraper-memory-tools/tools/schedule/pause-scheduled-action";
15233
+ import { resumeScheduledActionTool } from "mcpscraper-memory-tools/tools/schedule/resume-scheduled-action";
15234
+ import { createTableTool } from "mcpscraper-memory-tools/tools/tables/table-create";
15235
+ import { deleteTableRowsTool } from "mcpscraper-memory-tools/tools/tables/table-delete-rows";
15236
+ import { dropTableTool } from "mcpscraper-memory-tools/tools/tables/table-drop";
15237
+ import { insertTableRowsTool } from "mcpscraper-memory-tools/tools/tables/table-insert-rows";
15238
+ import { addVaultTool } from "mcpscraper-memory-tools/tools/vaults/add-vault";
15239
+ import { deleteVaultTool } from "mcpscraper-memory-tools/tools/vaults/delete-vault";
15240
+ var registered2 = false;
15241
+ function registerWriteCutovers() {
15242
+ if (registered2) return;
15243
+ registered2 = true;
15244
+ registerCutover("acceptShareTool", async (input, apiKey) => {
15245
+ const result = await acceptShareTool.execute({ ...input, apiKey }, {});
15246
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15247
+ });
15248
+ registerCutover("approveSenderTool", async (input, apiKey) => {
15249
+ const result = await approveSenderTool.execute({ ...input, apiKey }, {});
15250
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15251
+ });
15252
+ registerCutover("declineShareTool", async (input, apiKey) => {
15253
+ const result = await declineShareTool.execute({ ...input, apiKey }, {});
15254
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15255
+ });
15256
+ registerCutover("inboxSettingsTool", async (input, apiKey) => {
15257
+ const result = await inboxSettingsTool.execute({ ...input, apiKey }, {});
15258
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15259
+ });
15260
+ registerCutover("removeApprovedSenderTool", async (input, apiKey) => {
15261
+ const result = await removeApprovedSenderTool.execute({ ...input, apiKey }, {});
15262
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15263
+ });
15264
+ registerCutover("revokeShareTool", async (input, apiKey) => {
15265
+ const result = await revokeShareTool.execute({ ...input, apiKey }, {});
15266
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15267
+ });
15268
+ registerCutover("setAgentIdentityTool", async (input, apiKey) => {
15269
+ const result = await setAgentIdentityTool.execute({ ...input, apiKey }, {});
15270
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15271
+ });
15272
+ registerCutover("shareNoteTool", async (input, apiKey) => {
15273
+ const result = await shareNoteTool.execute({ ...input, apiKey }, {});
15274
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15275
+ });
15276
+ registerCutover("shareVaultTool", async (input, apiKey) => {
15277
+ const result = await shareVaultTool.execute({ ...input, apiKey }, {});
15278
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15279
+ });
15280
+ registerCutover("swapVaultTool", async (input, apiKey) => {
15281
+ const result = await swapVaultTool.execute({ ...input, apiKey }, {});
15282
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15283
+ });
15284
+ registerCutover("switchAccountTool", async (input, apiKey) => {
15285
+ const result = await switchAccountTool.execute({ ...input, apiKey }, {});
15286
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15287
+ });
15288
+ registerCutover("unlinkShareTool", async (input, apiKey) => {
15289
+ const result = await unlinkShareTool.execute({ ...input, apiKey }, {});
15290
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15291
+ });
15292
+ registerCutover("memoryQuestionsTool", async (input, apiKey) => {
15293
+ const result = await memoryQuestionsTool.execute({ ...input, apiKey }, {});
15294
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15295
+ });
15296
+ registerCutover("createChannelTool", async (input, apiKey) => {
15297
+ const result = await createChannelTool.execute({ ...input, apiKey }, {});
15298
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15299
+ });
15300
+ registerCutover("listChannelMessagesTool", async (input, apiKey) => {
15301
+ const result = await listChannelMessagesTool.execute({ ...input, apiKey }, {});
15302
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15303
+ });
15304
+ registerCutover("pollChannelTool", async (input, apiKey) => {
15305
+ const result = await pollChannelTool.execute({ ...input, apiKey }, {});
15306
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15307
+ });
15308
+ registerCutover("postMessageTool", async (input, apiKey) => {
15309
+ const result = await postMessageTool.execute({ ...input, apiKey }, {});
15310
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15311
+ });
15312
+ registerCutover("reactMessageTool", async (input, apiKey) => {
15313
+ const result = await reactMessageTool.execute({ ...input, apiKey }, {});
15314
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15315
+ });
15316
+ registerCutover("removeChannelMemberTool", async (input, apiKey) => {
15317
+ const result = await removeChannelMemberTool.execute({ ...input, apiKey }, {});
15318
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15319
+ });
15320
+ registerCutover("replyMessageTool", async (input, apiKey) => {
15321
+ const result = await replyMessageTool.execute({ ...input, apiKey }, {});
15322
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15323
+ });
15324
+ registerCutover("recordFactTool", async (input, apiKey) => {
15325
+ const result = await recordFactTool.execute({ ...input, apiKey }, {});
15326
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15327
+ });
15328
+ registerCutover("libraryIngestTool", async (input, apiKey) => {
15329
+ const result = await libraryIngestTool.execute({ ...input, apiKey }, {});
15330
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15331
+ });
15332
+ registerCutover("deleteNoteTool", async (input, apiKey) => {
15333
+ const result = await deleteNoteTool.execute({ ...input, apiKey }, {});
15334
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15335
+ });
15336
+ registerCutover("putTool", async (input, apiKey) => {
15337
+ const result = await putTool.execute({ ...input, apiKey }, {});
15338
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15339
+ });
15340
+ registerCutover("uploadTool", async (input, apiKey) => {
15341
+ const result = await uploadTool.execute({ ...input, apiKey }, {});
15342
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15343
+ });
15344
+ registerCutover("deleteScheduledActionTool", async (input, apiKey) => {
15345
+ const result = await deleteScheduledActionTool.execute({ ...input, apiKey }, {});
15346
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15347
+ });
15348
+ registerCutover("pauseScheduledActionTool", async (input, apiKey) => {
15349
+ const result = await pauseScheduledActionTool.execute({ ...input, apiKey }, {});
15350
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15351
+ });
15352
+ registerCutover("resumeScheduledActionTool", async (input, apiKey) => {
15353
+ const result = await resumeScheduledActionTool.execute({ ...input, apiKey }, {});
15354
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15355
+ });
15356
+ registerCutover("createTableTool", async (input, apiKey) => {
15357
+ const result = await createTableTool.execute({ ...input, apiKey }, {});
15358
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15359
+ });
15360
+ registerCutover("deleteTableRowsTool", async (input, apiKey) => {
15361
+ const result = await deleteTableRowsTool.execute({ ...input, apiKey }, {});
15362
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15363
+ });
15364
+ registerCutover("dropTableTool", async (input, apiKey) => {
15365
+ const result = await dropTableTool.execute({ ...input, apiKey }, {});
15366
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15367
+ });
15368
+ registerCutover("insertTableRowsTool", async (input, apiKey) => {
15369
+ const result = await insertTableRowsTool.execute({ ...input, apiKey }, {});
15370
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15371
+ });
15372
+ registerCutover("addVaultTool", async (input, apiKey) => {
15373
+ const result = await addVaultTool.execute({ ...input, apiKey }, {});
15374
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15375
+ });
15376
+ registerCutover("deleteVaultTool", async (input, apiKey) => {
15377
+ const result = await deleteVaultTool.execute({ ...input, apiKey }, {});
15378
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15379
+ });
15380
+ }
15381
+
15382
+ // src/mcp/memory-cutover/billing.ts
15383
+ import { issueKeyTool } from "mcpscraper-memory-tools/tools/access/issue-key";
15384
+ import { listKeysTool } from "mcpscraper-memory-tools/tools/access/list-keys";
15385
+ import { revokeKeyTool } from "mcpscraper-memory-tools/tools/access/revoke-key";
15386
+ import { setScopeTool } from "mcpscraper-memory-tools/tools/access/set-scope";
15387
+ import { inviteAccountTool } from "mcpscraper-memory-tools/tools/access/invite-account";
15388
+ import { getChatLinkTool } from "mcpscraper-memory-tools/tools/access/get-chat-link";
15389
+ import { revokeChatLinkTool } from "mcpscraper-memory-tools/tools/access/revoke-chat-link";
15390
+ import { provisionDefaultsTool as provisionDefaultsTool2 } from "mcpscraper-memory-tools/tools/vaults/provision-defaults";
15391
+ import { createSecureVaultTool } from "mcpscraper-memory-tools/tools/vaults/create-secure-vault";
15392
+ import { createScheduledActionTool } from "mcpscraper-memory-tools/tools/schedule/create-scheduled-action";
15393
+ import { getScheduleStatusTool } from "mcpscraper-memory-tools/tools/schedule/get-schedule-status";
15394
+ import { getScheduleLinkTool } from "mcpscraper-memory-tools/tools/schedule/get-schedule-link";
15395
+ import { revokeScheduleLinkTool } from "mcpscraper-memory-tools/tools/schedule/revoke-schedule-link";
15396
+ import { setScheduleEntitlementTool as setScheduleEntitlementTool2 } from "mcpscraper-memory-tools/tools/schedule/set-schedule-entitlement";
15397
+ import { createWebhookTool } from "mcpscraper-memory-tools/tools/webhooks/create-webhook";
15398
+ import { revokeWebhookTool } from "mcpscraper-memory-tools/tools/webhooks/revoke-webhook";
15399
+ var registered3 = false;
15400
+ function registerBillingCutovers() {
15401
+ if (registered3) return;
15402
+ registered3 = true;
15403
+ registerCutover("issueKeyTool", async (input, apiKey) => {
15404
+ const result = await issueKeyTool.execute({ ...input, apiKey }, {});
15405
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15406
+ });
15407
+ registerCutover("listKeysTool", async (input, apiKey) => {
15408
+ const result = await listKeysTool.execute({ ...input, apiKey }, {});
15409
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15410
+ });
15411
+ registerCutover("revokeKeyTool", async (input, apiKey) => {
15412
+ const result = await revokeKeyTool.execute({ ...input, apiKey }, {});
15413
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15414
+ });
15415
+ registerCutover("setScopeTool", async (input, apiKey) => {
15416
+ const result = await setScopeTool.execute({ ...input, apiKey }, {});
15417
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15418
+ });
15419
+ registerCutover("inviteAccountTool", async (input, apiKey) => {
15420
+ const result = await inviteAccountTool.execute({ ...input, apiKey }, {});
15421
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15422
+ });
15423
+ registerCutover("getChatLinkTool", async (input, apiKey) => {
15424
+ const result = await getChatLinkTool.execute({ ...input, apiKey }, {});
15425
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15426
+ });
15427
+ registerCutover("revokeChatLinkTool", async (input, apiKey) => {
15428
+ const result = await revokeChatLinkTool.execute({ ...input, apiKey }, {});
15429
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15430
+ });
15431
+ registerCutover("provisionDefaultsTool", async (input, apiKey) => {
15432
+ const result = await provisionDefaultsTool2.execute({ ...input, apiKey }, {});
15433
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15434
+ });
15435
+ registerCutover("createSecureVaultTool", async (input, apiKey) => {
15436
+ const result = await createSecureVaultTool.execute({ ...input, apiKey }, {});
15437
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15438
+ });
15439
+ registerCutover("createScheduledActionTool", async (input, apiKey) => {
15440
+ const result = await createScheduledActionTool.execute({ ...input, apiKey }, {});
15441
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15442
+ });
15443
+ registerCutover("getScheduleStatusTool", async (input, apiKey) => {
15444
+ const result = await getScheduleStatusTool.execute({ ...input, apiKey }, {});
15445
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15446
+ });
15447
+ registerCutover("getScheduleLinkTool", async (input, apiKey) => {
15448
+ const result = await getScheduleLinkTool.execute({ ...input, apiKey }, {});
15449
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15450
+ });
15451
+ registerCutover("revokeScheduleLinkTool", async (input, apiKey) => {
15452
+ const result = await revokeScheduleLinkTool.execute({ ...input, apiKey }, {});
15453
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15454
+ });
15455
+ registerCutover("setScheduleEntitlementTool", async (input, apiKey) => {
15456
+ const result = await setScheduleEntitlementTool2.execute({ ...input, apiKey }, {});
15457
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15458
+ });
15459
+ registerCutover("createWebhookTool", async (input, apiKey) => {
15460
+ const result = await createWebhookTool.execute({ ...input, apiKey }, {});
15461
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15462
+ });
15463
+ registerCutover("revokeWebhookTool", async (input, apiKey) => {
15464
+ const result = await revokeWebhookTool.execute({ ...input, apiKey }, {});
15465
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15466
+ });
15467
+ }
15468
+
15469
+ // src/mcp/memory-cutover/cross-service.ts
15470
+ import { proposeScheduledActionTool } from "mcpscraper-memory-tools/tools/schedule/propose-scheduled-action";
15471
+ var registered4 = false;
15472
+ function registerCrossServiceCutovers() {
15473
+ if (registered4) return;
15474
+ registered4 = true;
15475
+ registerCutover("proposeScheduledActionTool", async (input, apiKey) => {
15476
+ const result = await proposeScheduledActionTool.execute({ ...input, apiKey }, {});
15477
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15478
+ });
15479
+ }
15480
+
15481
+ // src/mcp/memory-cutover/video.ts
15482
+ import { videoAnalyzeStartTool } from "mcpscraper-memory-tools/tools/video/start";
15483
+ var registered5 = false;
15484
+ function registerVideoCutovers() {
15485
+ if (registered5) return;
15486
+ registered5 = true;
15487
+ registerCutover("videoAnalyzeStartTool", async (input, apiKey) => {
15488
+ const result = await videoAnalyzeStartTool.execute({ ...input, apiKey }, {});
15489
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
15490
+ });
15491
+ }
15492
+
14911
15493
  // src/mcp/mcp-oauth.ts
14912
15494
  import { createRemoteJWKSet, jwtVerify } from "jose";
14913
15495
  function oauthIssuer() {
@@ -14948,6 +15530,11 @@ async function verifyScraperOAuthJwt(token) {
14948
15530
  }
14949
15531
 
14950
15532
  // src/mcp/mcp-routes.ts
15533
+ registerReadCutovers();
15534
+ registerWriteCutovers();
15535
+ registerBillingCutovers();
15536
+ registerCrossServiceCutovers();
15537
+ registerVideoCutovers();
14951
15538
  configureReportSaving(false);
14952
15539
  function mcpAuthError(message = "authentication required") {
14953
15540
  const body = JSON.stringify({
@@ -14981,20 +15568,20 @@ async function requireMcpCallerKey(c) {
14981
15568
  if (!identity) return mcpAuthError("Bearer token has no subject");
14982
15569
  const user2 = await getUserByEmail(identity);
14983
15570
  if (!user2 || !user2.api_key || user2.key_active !== 1) return mcpAuthError("No active MCP Scraper account for this identity");
14984
- return user2.api_key;
15571
+ return { key: user2.api_key, user: user2 };
14985
15572
  }
14986
15573
  const callerKey = xApiKey ?? bearerToken;
14987
15574
  if (!callerKey) return mcpAuthError();
14988
15575
  const user = await getUserByApiKey(callerKey);
14989
15576
  if (!user) return mcpAuthError();
14990
- return callerKey;
15577
+ return { key: callerKey, user };
14991
15578
  }
14992
15579
  var mcpApp = new Hono14();
14993
15580
  mcpApp.all("/", async (c) => {
14994
15581
  try {
14995
15582
  const keyOrError = await requireMcpCallerKey(c);
14996
15583
  if (keyOrError instanceof Response) return keyOrError;
14997
- const callerKey = keyOrError;
15584
+ const { key: callerKey, user } = keyOrError;
14998
15585
  const baseUrl = process.env.MCP_SCRAPER_BASE_URL?.trim() ?? process.env.MCP_BASE_URL?.trim() ?? "https://mcpscraper.dev";
14999
15586
  const executor = new HttpMcpToolExecutor(baseUrl, callerKey);
15000
15587
  const transport = new WebStandardStreamableHTTPServerTransport({
@@ -15005,7 +15592,8 @@ mcpApp.all("/", async (c) => {
15005
15592
  const server = buildPaaExtractorMcpServer(executor, { savesReportsLocally: false, ownerId: hashOwnerId(callerKey) });
15006
15593
  registerSerpIntelligenceCaptureTools(server, executor);
15007
15594
  registerBrowserAgentMcpTools(server, { baseUrl, apiKey: callerKey, consoleBaseUrl, savesReportsLocally: false });
15008
- registerMemoryMcpTools(server, new MemoryMcpToolExecutor(baseUrl, callerKey));
15595
+ const memoryExecutor = new HybridMemoryToolExecutor(new MemoryMcpToolExecutor(baseUrl, callerKey), user);
15596
+ registerMemoryMcpTools(server, memoryExecutor);
15009
15597
  await server.connect(transport);
15010
15598
  return transport.handleRequest(c.req.raw);
15011
15599
  } catch {
@@ -15093,23 +15681,34 @@ async function migrateBrowserAgent() {
15093
15681
  await db.execute(`ALTER TABLE browser_auth_connections ADD COLUMN updated_at TEXT`);
15094
15682
  } catch {
15095
15683
  }
15684
+ try {
15685
+ await db.execute(`ALTER TABLE browser_auth_connections ADD COLUMN status TEXT NOT NULL DEFAULT 'NEEDS_AUTH'`);
15686
+ } catch {
15687
+ }
15688
+ try {
15689
+ await db.execute(`ALTER TABLE browser_auth_connections ADD COLUMN browser_agent_session_id TEXT`);
15690
+ } catch {
15691
+ }
15096
15692
  await db.execute(`CREATE INDEX IF NOT EXISTS browser_auth_connections_profile ON browser_auth_connections(profile)`);
15097
15693
  _ready = true;
15098
15694
  }
15099
- async function saveAuthConnection(input) {
15100
- if (!input.connectionId) return;
15695
+ async function createAuthConnectionRow(input) {
15696
+ const db = getDb();
15697
+ const connectionId = `authc_${randomUUID2().replace(/-/g, "").slice(0, 20)}`;
15698
+ await db.execute({
15699
+ sql: `INSERT INTO browser_auth_connections (connection_id, domain, profile, account_email, note, status, browser_agent_session_id)
15700
+ VALUES (?, ?, ?, ?, ?, 'NEEDS_AUTH', ?)`,
15701
+ args: [connectionId, input.domain, input.profile, input.accountEmail, input.note, input.browserAgentSessionId]
15702
+ });
15703
+ const row = await getAuthConnectionRow(connectionId);
15704
+ if (!row) throw new Error("auth connection insert failed");
15705
+ return row;
15706
+ }
15707
+ async function markAuthConnectionAuthenticated(connectionId) {
15101
15708
  const db = getDb();
15102
15709
  await db.execute({
15103
- sql: `INSERT INTO browser_auth_connections (connection_id, hosted_url, domain, profile, account_email, note, updated_at)
15104
- VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
15105
- ON CONFLICT(connection_id) DO UPDATE SET
15106
- hosted_url = COALESCE(excluded.hosted_url, browser_auth_connections.hosted_url),
15107
- domain = COALESCE(excluded.domain, browser_auth_connections.domain),
15108
- profile = COALESCE(excluded.profile, browser_auth_connections.profile),
15109
- account_email = COALESCE(excluded.account_email, browser_auth_connections.account_email),
15110
- note = COALESCE(excluded.note, browser_auth_connections.note),
15111
- updated_at = datetime('now')`,
15112
- args: [input.connectionId, input.hostedUrl, input.domain, input.profile, input.accountEmail ?? null, input.note ?? null]
15710
+ sql: `UPDATE browser_auth_connections SET status = 'AUTHENTICATED', updated_at = datetime('now') WHERE connection_id = ?`,
15711
+ args: [connectionId]
15113
15712
  });
15114
15713
  }
15115
15714
  async function getAuthConnectionRow(connectionId) {
@@ -15956,79 +16555,10 @@ async function ensureProfile(k, name) {
15956
16555
  if (!isProfileConflict(err)) throw err;
15957
16556
  }
15958
16557
  }
15959
- function profileConnectionMatches(connection, profileName, domain) {
15960
- if (!connection || typeof connection !== "object") return false;
15961
- const data = connection;
15962
- return data.profile_name === profileName && data.domain === domain;
15963
- }
15964
- async function findProfileConnection(k, profileName, domain) {
15965
- for await (const connection of k.auth.connections.list({ profile_name: profileName, domain })) {
15966
- if (profileConnectionMatches(connection, profileName, domain)) return connection;
15967
- }
15968
- return null;
15969
- }
15970
- async function listProfileConnections(profileName) {
15971
- if (!profileName) return [];
15972
- const k = client();
15973
- const out = [];
15974
- for await (const connection of k.auth.connections.list({ profile_name: profileName })) {
15975
- const data = connection;
15976
- if (data.profile_name && data.profile_name !== profileName) continue;
15977
- out.push(profileOnboardResult(connection));
15978
- }
15979
- return out;
15980
- }
15981
- async function findOrCreateProfileConnection(k, opts) {
15982
- try {
15983
- return await k.auth.connections.create({
15984
- domain: opts.domain,
15985
- profile_name: opts.profileName,
15986
- ...opts.loginUrl ? { login_url: opts.loginUrl } : {}
15987
- });
15988
- } catch (err) {
15989
- if (!isProfileConflict(err)) throw err;
15990
- const existing = await findProfileConnection(k, opts.profileName, opts.domain);
15991
- if (existing) return existing;
15992
- throw err;
15993
- }
15994
- }
15995
- function profileOnboardResult(connection, hostedUrl = null, liveViewUrl = null) {
15996
- return {
15997
- connectionId: String(connection.id),
15998
- profileName: String(connection.profile_name),
15999
- domain: String(connection.domain),
16000
- status: String(connection.status),
16001
- flowStatus: typeof connection.flow_status === "string" ? connection.flow_status : null,
16002
- flowStep: typeof connection.flow_step === "string" ? connection.flow_step : null,
16003
- flowExpiresAt: typeof connection.flow_expires_at === "string" ? connection.flow_expires_at : null,
16004
- hostedUrl: hostedUrl ?? (typeof connection.hosted_url === "string" ? connection.hosted_url : null),
16005
- liveViewUrl: liveViewUrl ?? (typeof connection.live_view_url === "string" ? connection.live_view_url : null),
16006
- browserSessionId: typeof connection.browser_session_id === "string" ? connection.browser_session_id : null,
16007
- postLoginUrl: typeof connection.post_login_url === "string" ? connection.post_login_url : null
16008
- };
16009
- }
16010
16558
  function explicitProxyId(value) {
16011
16559
  const trimmed = value?.trim();
16012
16560
  return trimmed || void 0;
16013
16561
  }
16014
- async function startProfileOnboarding(opts) {
16015
- const k = client();
16016
- const connection = await findOrCreateProfileConnection(k, opts);
16017
- const login = await k.auth.connections.login(connection.id);
16018
- const state = await k.auth.connections.retrieve(connection.id);
16019
- return profileOnboardResult(state, login.hosted_url ?? null, login.live_view_url ?? null);
16020
- }
16021
- async function getProfileOnboardingStatus(input) {
16022
- const k = client();
16023
- let connection = null;
16024
- if (input.connectionId) {
16025
- connection = await k.auth.connections.retrieve(input.connectionId);
16026
- } else if (input.profileName && input.domain) {
16027
- connection = await findProfileConnection(k, input.profileName, input.domain);
16028
- }
16029
- if (!connection) throw new Error("profile auth connection not found");
16030
- return profileOnboardResult(connection);
16031
- }
16032
16562
  async function createSession(opts = {}) {
16033
16563
  const k = client();
16034
16564
  const resolvedProxyId = explicitProxyId(opts.proxyId);
@@ -16370,24 +16900,39 @@ async function charge(sessionId, userId, _startedAtMs) {
16370
16900
  if (res.ok) await setBilledMc(sessionId, owed);
16371
16901
  }
16372
16902
  }
16903
+ async function closeAndSettleSession(row) {
16904
+ if (row.user_id != null) {
16905
+ try {
16906
+ await charge(row.id, row.user_id);
16907
+ } catch {
16908
+ }
16909
+ }
16910
+ try {
16911
+ await closeSession(row.runtime_session_id);
16912
+ } catch {
16913
+ }
16914
+ await markSessionClosed(row.id).catch(() => {
16915
+ });
16916
+ await releaseConcurrencyGate(row.concurrency_lock_id).catch(() => {
16917
+ });
16918
+ }
16919
+ async function completeAuthConnection(connectionId) {
16920
+ const connection = await getAuthConnectionRow(connectionId);
16921
+ if (!connection) return { ok: false, error: "not found" };
16922
+ if (connection.browser_agent_session_id) {
16923
+ const sessionRow = await getSessionRow(connection.browser_agent_session_id);
16924
+ if (sessionRow && sessionRow.status === "open") {
16925
+ await closeAndSettleSession(sessionRow);
16926
+ }
16927
+ }
16928
+ await markAuthConnectionAuthenticated(connectionId);
16929
+ return { ok: true };
16930
+ }
16373
16931
  async function reapIdleBrowserSessions(idleSeconds = 120) {
16374
16932
  const rows = await listIdleOpenSessionRows(idleSeconds);
16375
16933
  let reaped = 0;
16376
16934
  for (const row of rows) {
16377
- if (row.user_id != null) {
16378
- try {
16379
- await charge(row.id, row.user_id);
16380
- } catch {
16381
- }
16382
- }
16383
- try {
16384
- await closeSession(row.runtime_session_id);
16385
- } catch {
16386
- }
16387
- await markSessionClosed(row.id).catch(() => {
16388
- });
16389
- if (row.concurrency_lock_id) await releaseConcurrencyGate(row.concurrency_lock_id).catch(() => {
16390
- });
16935
+ await closeAndSettleSession(row);
16391
16936
  reaped++;
16392
16937
  }
16393
16938
  return { reaped };
@@ -16455,74 +17000,87 @@ function buildBrowserAgentRoutes() {
16455
17000
  });
16456
17001
  app2.use("*", auth);
16457
17002
  app2.post("/profiles/onboard", async (c) => {
17003
+ const user = c.get("user");
17004
+ if (Number(user.balance_mc ?? 0) < BROWSER_OPEN_MIN_BALANCE_MC) {
17005
+ return c.json(insufficientBalanceResponse(Number(user.balance_mc ?? 0), BROWSER_OPEN_MIN_BALANCE_MC), 402);
17006
+ }
16458
17007
  const body = await c.req.json().catch(() => ({}));
16459
17008
  const profileName = typeof body.profile === "string" ? body.profile.trim() : "";
16460
17009
  const domain = typeof body.domain === "string" ? body.domain.trim().replace(/^https?:\/\//, "").replace(/\/.*$/, "") : "chatgpt.com";
16461
17010
  const loginUrl = typeof body.login_url === "string" ? body.login_url.trim() : void 0;
16462
17011
  const accountEmail = typeof body.account_email === "string" ? body.account_email.trim() || null : null;
16463
17012
  const note = typeof body.note === "string" ? body.note.trim() || null : null;
17013
+ const timeoutSeconds = typeof body.timeout_seconds === "number" ? body.timeout_seconds : void 0;
16464
17014
  if (!profileName) return c.json({ error: "profile is required" }, 400);
16465
17015
  if (!domain) return c.json({ error: "domain is required" }, 400);
17016
+ const gate = await acquireConcurrencyGate(user, "browser_agent_session", {
17017
+ ttlSeconds: browserSessionLockTtlSeconds(timeoutSeconds),
17018
+ metadata: { label: typeof body.label === "string" ? body.label : null }
17019
+ });
17020
+ if (!gate.ok) return c.json(concurrencyLimitExceededResponse(gate), 429, { "Retry-After": String(gate.retryAfterSeconds) });
17021
+ let runtimeSessionId = null;
16466
17022
  try {
16467
- const result = await startProfileOnboarding({ profileName, domain, loginUrl });
16468
- try {
16469
- await saveAuthConnection({
16470
- connectionId: result.connectionId,
16471
- hostedUrl: result.hostedUrl,
16472
- domain: result.domain,
16473
- profile: result.profileName,
16474
- accountEmail,
16475
- note
16476
- });
16477
- } catch {
16478
- }
17023
+ const created = await createSession({
17024
+ timeoutSeconds,
17025
+ profileName,
17026
+ saveProfileChanges: true,
17027
+ startUrl: loginUrl || `https://${domain}/`,
17028
+ disableDefaultProxy: true
17029
+ });
17030
+ runtimeSessionId = created.runtimeSessionId;
17031
+ const sessionRow = await createSessionRow({
17032
+ runtimeSessionId: created.runtimeSessionId,
17033
+ liveViewUrl: created.liveViewUrl,
17034
+ cdpWsUrl: created.cdpWsUrl,
17035
+ label: typeof body.label === "string" ? body.label : `Login setup: ${domain} \u2192 ${profileName}`,
17036
+ userId: user.id,
17037
+ concurrencyLockId: gate.lockId
17038
+ });
17039
+ const connection = await createAuthConnectionRow({
17040
+ domain,
17041
+ profile: profileName,
17042
+ accountEmail,
17043
+ note,
17044
+ browserAgentSessionId: sessionRow.id
17045
+ });
16479
17046
  return c.json({
16480
- connection_id: result.connectionId,
16481
- profile: result.profileName,
16482
- domain: result.domain,
16483
- status: result.status,
16484
- flow_status: result.flowStatus,
16485
- flow_step: result.flowStep,
16486
- flow_expires_at: result.flowExpiresAt,
16487
- browser_session_id: result.browserSessionId,
16488
- post_login_url: result.postLoginUrl
17047
+ connection_id: connection.connection_id,
17048
+ profile: profileName,
17049
+ domain,
17050
+ status: connection.status,
17051
+ browser_session_id: sessionRow.id
16489
17052
  });
16490
17053
  } catch (err) {
17054
+ if (runtimeSessionId) {
17055
+ try {
17056
+ await closeSession(runtimeSessionId);
17057
+ } catch {
17058
+ }
17059
+ }
17060
+ await releaseConcurrencyGate(gate.lockId);
16491
17061
  return c.json(failure(err), 502);
16492
17062
  }
16493
17063
  });
16494
17064
  app2.post("/profiles/status", async (c) => {
16495
17065
  const body = await c.req.json().catch(() => ({}));
16496
- const connectionId = typeof body.connection_id === "string" ? body.connection_id.trim() : void 0;
16497
- const profileName = typeof body.profile === "string" ? body.profile.trim() : void 0;
17066
+ const connectionId = typeof body.connection_id === "string" ? body.connection_id.trim() : "";
17067
+ const profileName = typeof body.profile === "string" ? body.profile.trim() : "";
16498
17068
  const domain = typeof body.domain === "string" ? body.domain.trim().replace(/^https?:\/\//, "").replace(/\/.*$/, "") : void 0;
16499
- try {
16500
- const result = await getProfileOnboardingStatus({ connectionId, profileName, domain });
16501
- if (result.hostedUrl) {
16502
- try {
16503
- await saveAuthConnection({
16504
- connectionId: result.connectionId,
16505
- hostedUrl: result.hostedUrl,
16506
- domain: result.domain,
16507
- profile: result.profileName
16508
- });
16509
- } catch {
16510
- }
16511
- }
16512
- return c.json({
16513
- connection_id: result.connectionId,
16514
- profile: result.profileName,
16515
- domain: result.domain,
16516
- status: result.status,
16517
- flow_status: result.flowStatus,
16518
- flow_step: result.flowStep,
16519
- flow_expires_at: result.flowExpiresAt,
16520
- browser_session_id: result.browserSessionId,
16521
- post_login_url: result.postLoginUrl
16522
- });
16523
- } catch (err) {
16524
- return c.json(failure(err), 404);
16525
- }
17069
+ let row = null;
17070
+ if (connectionId) {
17071
+ row = await getAuthConnectionRow(connectionId);
17072
+ } else if (profileName && domain) {
17073
+ const rows = await listAuthConnectionsByProfile(profileName);
17074
+ row = rows.find((r) => r.domain === domain) ?? null;
17075
+ }
17076
+ if (!row) return c.json({ error: "auth connection not found" }, 404);
17077
+ return c.json({
17078
+ connection_id: row.connection_id,
17079
+ profile: row.profile,
17080
+ domain: row.domain,
17081
+ status: row.status,
17082
+ browser_session_id: row.browser_agent_session_id
17083
+ });
16526
17084
  });
16527
17085
  app2.post("/profiles/list", async (c) => {
16528
17086
  const body = await c.req.json().catch(() => ({}));
@@ -16531,23 +17089,15 @@ function buildBrowserAgentRoutes() {
16531
17089
  const domainFilter = typeof body.domain === "string" ? body.domain.trim().replace(/^https?:\/\//, "").replace(/\/.*$/, "") : void 0;
16532
17090
  const connectionIdFilter = typeof body.connection_id === "string" ? body.connection_id.trim() : void 0;
16533
17091
  try {
16534
- const [live, stored] = await Promise.all([
16535
- listProfileConnections(profileName),
16536
- listAuthConnectionsByProfile(profileName).catch(() => [])
16537
- ]);
16538
- const notesByDomain = new Map(stored.map((row) => [row.domain ?? "", row]));
16539
- const notesByConnection = new Map(stored.map((row) => [row.connection_id, row]));
16540
- let connections = live.map((conn) => {
16541
- const record = notesByConnection.get(conn.connectionId) ?? notesByDomain.get(conn.domain);
16542
- return {
16543
- connection_id: conn.connectionId,
16544
- domain: conn.domain,
16545
- status: conn.status,
16546
- account_email: record?.account_email ?? null,
16547
- note: record?.note ?? null,
16548
- last_connected_at: record?.updated_at ?? record?.created_at ?? null
16549
- };
16550
- });
17092
+ const stored = await listAuthConnectionsByProfile(profileName);
17093
+ let connections = stored.map((row) => ({
17094
+ connection_id: row.connection_id,
17095
+ domain: row.domain,
17096
+ status: row.status,
17097
+ account_email: row.account_email,
17098
+ note: row.note,
17099
+ last_connected_at: row.updated_at ?? row.created_at
17100
+ }));
16551
17101
  if (connectionIdFilter) connections = connections.filter((c2) => c2.connection_id === connectionIdFilter);
16552
17102
  if (domainFilter) connections = connections.filter((c2) => c2.domain === domainFilter);
16553
17103
  return c.json({ profile: profileName, connections });
@@ -16622,13 +17172,7 @@ function buildBrowserAgentRoutes() {
16622
17172
  const user = c.get("user");
16623
17173
  const row = await loadOpenSession(c.req.param("id"), user.id);
16624
17174
  if (!row) return c.json({ error: "not found" }, 404);
16625
- await charge(row.id, user.id);
16626
- try {
16627
- await closeSession(row.runtime_session_id);
16628
- } catch {
16629
- }
16630
- await markSessionClosed(row.id);
16631
- await releaseConcurrencyGate(row.concurrency_lock_id);
17175
+ await closeAndSettleSession(row);
16632
17176
  return c.json({ ok: true });
16633
17177
  });
16634
17178
  app2.post("/sessions/:id/goto", async (c) => {
@@ -16912,17 +17456,30 @@ function buildBrowserAgentRoutes() {
16912
17456
  }
16913
17457
 
16914
17458
  // src/api/browser-agent-console.ts
16915
- function renderAuthConsoleHtml(connectionId, hostedUrl) {
16916
- const safeId = String(connectionId).replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]);
16917
- const goUrl = `/console/auth/${encodeURIComponent(connectionId)}/go`;
16918
- const body = hostedUrl ? `<iframe class="stage" allow="clipboard-read; clipboard-write" src="${goUrl}"></iframe>
16919
- <div class="fallback">
16920
- <span>Sign-in window not showing?</span>
16921
- <a class="btn" href="${goUrl}" target="_blank" rel="noopener">Open sign-in in a new tab</a>
16922
- </div>` : `<div class="empty">
17459
+ function renderAuthConsoleHtml(connectionId, info) {
17460
+ const esc = (s) => String(s == null ? "" : s).replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]);
17461
+ const safeId = esc(connectionId);
17462
+ const domainLabel = info.domain ? esc(info.domain) : "this site";
17463
+ let body;
17464
+ if (info.status === "AUTHENTICATED") {
17465
+ body = `<div class="empty">
17466
+ <div class="check">\u2713</div>
17467
+ <div>You're signed in to ${domainLabel}.</div>
17468
+ <div class="muted">This login is saved. You can close this tab.</div>
17469
+ </div>`;
17470
+ } else if (info.sessionOpen && info.liveViewUrl) {
17471
+ body = `<iframe class="stage" allow="clipboard-read; clipboard-write" src="${esc(info.liveViewUrl)}"></iframe>
17472
+ <div class="footer">
17473
+ <span class="muted">Log into ${domainLabel} directly in the window above, then confirm below.</span>
17474
+ <span class="spacer"></span>
17475
+ <button class="btn" id="done">Done \u2014 I'm signed in</button>
17476
+ </div>`;
17477
+ } else {
17478
+ body = `<div class="empty">
16923
17479
  <div>This sign-in link is no longer active.</div>
16924
17480
  <div class="muted">Ask the assistant to reconnect the profile, then open the new link.</div>
16925
17481
  </div>`;
17482
+ }
16926
17483
  return `<!DOCTYPE html>
16927
17484
  <html lang="en">
16928
17485
  <head>
@@ -16938,9 +17495,11 @@ function renderAuthConsoleHtml(connectionId, hostedUrl) {
16938
17495
  header .muted { color: #6b7689; font-size: 12px; }
16939
17496
  .spacer { flex: 1; }
16940
17497
  .stage { flex: 1; width: 100%; border: 0; display: block; background: #05070b; }
16941
- .fallback { display: flex; align-items: center; gap: 12px; padding: 10px 18px; border-top: 1px solid #1c2230; background: #0f131c; font-size: 13px; color: #8893a7; }
16942
- .btn { background: #2b6cff; color: #fff; border-radius: 7px; padding: 7px 12px; text-decoration: none; font-weight: 500; }
17498
+ .footer { display: flex; align-items: center; gap: 12px; padding: 10px 18px; border-top: 1px solid #1c2230; background: #0f131c; font-size: 13px; color: #8893a7; }
17499
+ .btn { background: #2b6cff; color: #fff; border: 0; border-radius: 7px; padding: 8px 14px; text-decoration: none; font-weight: 500; font: inherit; cursor: pointer; }
17500
+ .btn:disabled { opacity: .6; cursor: default; }
16943
17501
  .empty { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; color: #8893a7; }
17502
+ .check { font-size: 32px; color: #36d399; }
16944
17503
  .muted { color: #6b7689; font-size: 12px; }
16945
17504
  </style>
16946
17505
  ${ATTRIBUTION_PIXEL_SCRIPT}
@@ -16950,9 +17509,23 @@ ${ATTRIBUTION_PIXEL_SCRIPT}
16950
17509
  <h1>MCP Scraper</h1>
16951
17510
  <span class="muted">Secure sign-in \xB7 ${safeId}</span>
16952
17511
  <span class="spacer"></span>
16953
- <span class="muted">Complete the login below \u2014 your session is saved to your profile.</span>
17512
+ <span class="muted">Log in directly to the real site above \u2014 your session is saved to your profile.</span>
16954
17513
  </header>
16955
17514
  ${body}
17515
+ <script>
17516
+ (function(){
17517
+ var doneBtn = document.getElementById('done');
17518
+ if (!doneBtn) return;
17519
+ doneBtn.onclick = function() {
17520
+ doneBtn.disabled = true;
17521
+ doneBtn.textContent = 'Saving\u2026';
17522
+ fetch('/console/auth/${encodeURIComponent(connectionId)}/complete', { method: 'POST' })
17523
+ .then(function(r){ return r.json().catch(function(){ return {}; }); })
17524
+ .then(function(){ location.reload(); })
17525
+ .catch(function(){ doneBtn.disabled = false; doneBtn.textContent = "Done \u2014 I'm signed in"; });
17526
+ };
17527
+ })();
17528
+ </script>
16956
17529
  </body>
16957
17530
  </html>`;
16958
17531
  }
@@ -17190,8 +17763,9 @@ stripeApp.post("/webhooks", async (c) => {
17190
17763
  const user2 = await resolveUser(sub.customer, sub.customer_email);
17191
17764
  if (user2) {
17192
17765
  const live = sub.status === "active" || sub.status === "trialing";
17193
- await setMemoryPlan(user2.id, live ? mem.plan : "free", live ? sub.id : null);
17194
- await syncMemoryKeyPlan(user2, live ? mem.plan : "free");
17766
+ const newMemoryPlan = live ? mem.plan : "free";
17767
+ await setMemoryPlan(user2.id, newMemoryPlan, live ? sub.id : null);
17768
+ await syncMemoryKeyPlan(user2, resolveEffectiveMemoryPlan({ ...user2, memory_plan: newMemoryPlan }));
17195
17769
  }
17196
17770
  return c.json({ received: true });
17197
17771
  }
@@ -17209,7 +17783,9 @@ stripeApp.post("/webhooks", async (c) => {
17209
17783
  if (priceId && priceId in SUBSCRIPTION_TIERS) {
17210
17784
  const tier = SUBSCRIPTION_TIERS[priceId];
17211
17785
  const live = sub.status === "active" || sub.status === "trialing";
17212
- await setSubscriptionTier(user.id, live ? tier.tier : null, live ? tier.concurrency : 0, live ? sub.id : null);
17786
+ const newTier = live ? tier.tier : null;
17787
+ await setSubscriptionTier(user.id, newTier, live ? tier.concurrency : 0, live ? sub.id : null);
17788
+ await syncMemoryKeyPlan(user, resolveEffectiveMemoryPlan({ ...user, subscription_tier: newTier }));
17213
17789
  } else if (priceId === CONCURRENCY_PRICE_ID && event.type === "customer.subscription.created") {
17214
17790
  await setExtraConcurrencySlots(user.id, user.extra_concurrency_slots + 1);
17215
17791
  await setConcurrencySubId(user.id, sub.id);
@@ -17222,7 +17798,7 @@ stripeApp.post("/webhooks", async (c) => {
17222
17798
  const user2 = await resolveUserByMemorySubscription(sub.id) ?? await getUserByStripeCustomerId(sub.customer);
17223
17799
  if (user2) {
17224
17800
  await setMemoryPlan(user2.id, "free", null);
17225
- await syncMemoryKeyPlan(user2, "free");
17801
+ await syncMemoryKeyPlan(user2, resolveEffectiveMemoryPlan({ ...user2, memory_plan: "free" }));
17226
17802
  }
17227
17803
  return c.json({ received: true });
17228
17804
  }
@@ -17235,6 +17811,7 @@ stripeApp.post("/webhooks", async (c) => {
17235
17811
  if (!user) return c.json({ received: true });
17236
17812
  if (priceId && priceId in SUBSCRIPTION_TIERS) {
17237
17813
  await setSubscriptionTier(user.id, null, 0, null);
17814
+ await syncMemoryKeyPlan(user, resolveEffectiveMemoryPlan({ ...user, subscription_tier: null }));
17238
17815
  } else if (priceId === CONCURRENCY_PRICE_ID) {
17239
17816
  await setExtraConcurrencySlots(user.id, Math.max(0, user.extra_concurrency_slots - 1));
17240
17817
  await setConcurrencySubId(user.id, null);
@@ -20467,7 +21044,7 @@ app.get("/cron/tick", async (c) => {
20467
21044
  if (!process.env.CRON_SECRET || secret2 !== `Bearer ${process.env.CRON_SECRET}`) {
20468
21045
  return c.json({ error: "Unauthorized" }, 401);
20469
21046
  }
20470
- const { drainQueue } = await import("./worker-RVZXIALX.js");
21047
+ const { drainQueue } = await import("./worker-7XRZNLKB.js");
20471
21048
  const budget = { maxJobs: 10, deadlineMs: Date.now() + 28e4 };
20472
21049
  const workflowDispatchResult = await dispatchDueWorkflowSchedules(`${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`);
20473
21050
  const [results, sweepResult, reapResult, expiredResult, blobCleanup] = await Promise.all([
@@ -20485,7 +21062,7 @@ app.post("/api/internal/extract-refinalize/:id", async (c) => {
20485
21062
  return c.json({ error: "Unauthorized" }, 401);
20486
21063
  }
20487
21064
  const jobId = c.req.param("id");
20488
- const { getExtractJob: getExtractJob2, completeExtractJob: completeExtractJob2 } = await import("./site-extract-repository-2MQBAOX5.js");
21065
+ const { getExtractJob: getExtractJob2, completeExtractJob: completeExtractJob2 } = await import("./site-extract-repository-4NVO5KQA.js");
20489
21066
  const { assembleExtractArtifacts } = await import("./extract-bundle-UKE273TV.js");
20490
21067
  const job = await getExtractJob2(jobId);
20491
21068
  if (!job) return c.json({ error: "job not found" }, 404);
@@ -20493,7 +21070,7 @@ app.post("/api/internal/extract-refinalize/:id", async (c) => {
20493
21070
  await completeExtractJob2(jobId, stored);
20494
21071
  let settlement = "already_settled_or_refunded";
20495
21072
  if (job.billedMc == null && job.userId != null) {
20496
- const { settleExtractJob: settleExtractJob2, countSuccessfulPages: countSuccessfulPages2 } = await import("./site-extract-repository-2MQBAOX5.js");
21073
+ const { settleExtractJob: settleExtractJob2, countSuccessfulPages: countSuccessfulPages2 } = await import("./site-extract-repository-4NVO5KQA.js");
20497
21074
  const heldMc = Number(job.options.heldMc ?? 0);
20498
21075
  const successful = await countSuccessfulPages2(jobId);
20499
21076
  const usedMc = Math.min(successful * MC_COSTS.page_scrape, heldMc);
@@ -20531,14 +21108,20 @@ app.get("/console/auth/:id", async (c) => {
20531
21108
  const id = c.req.param("id");
20532
21109
  await migrateBrowserAgent();
20533
21110
  const row = await getAuthConnectionRow(id);
20534
- return c.html(renderAuthConsoleHtml(id, row?.hosted_url ?? null));
21111
+ const sessionRow = row?.browser_agent_session_id ? await getSessionRow(row.browser_agent_session_id) : null;
21112
+ return c.html(renderAuthConsoleHtml(id, {
21113
+ domain: row?.domain ?? null,
21114
+ status: row?.status ?? null,
21115
+ liveViewUrl: sessionRow?.live_view_url ?? null,
21116
+ sessionOpen: sessionRow?.status === "open"
21117
+ }));
20535
21118
  });
20536
- app.get("/console/auth/:id/go", async (c) => {
21119
+ app.post("/console/auth/:id/complete", async (c) => {
20537
21120
  const id = c.req.param("id");
20538
21121
  await migrateBrowserAgent();
20539
- const row = await getAuthConnectionRow(id);
20540
- if (!row?.hosted_url) return c.text("This sign-in link is no longer active.", 404);
20541
- return c.redirect(row.hosted_url, 302);
21122
+ const result = await completeAuthConnection(id);
21123
+ if (!result.ok) return c.json({ error: result.error }, 404);
21124
+ return c.json({ ok: true });
20542
21125
  });
20543
21126
  app.get("/console/:id", (c) => c.html(renderConsoleHtml(c.req.param("id"))));
20544
21127
  app.route("/stripe", stripeApp);
@@ -20649,4 +21232,4 @@ app.get("/blog/:slug/", (c) => {
20649
21232
  export {
20650
21233
  app
20651
21234
  };
20652
- //# sourceMappingURL=server-U7V6DUSY.js.map
21235
+ //# sourceMappingURL=server-6IYQQK6X.js.map