mcp-scraper 0.64.1 → 0.65.1

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 (41) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +2 -2
  3. package/dist/bin/api-server.cjs +1028 -496
  4. package/dist/bin/api-server.cjs.map +1 -1
  5. package/dist/bin/api-server.js +2 -2
  6. package/dist/bin/mcp-scraper-cli.cjs +1 -1
  7. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  8. package/dist/bin/mcp-scraper-cli.js +1 -1
  9. package/dist/bin/mcp-scraper-install.cjs +2 -2
  10. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  11. package/dist/bin/mcp-scraper-install.js +2 -2
  12. package/dist/bin/mcp-stdio-server.cjs +49 -8
  13. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  14. package/dist/bin/mcp-stdio-server.js +4 -4
  15. package/dist/{chunk-TRSZEK2D.js → chunk-2XUXVKT4.js} +3 -3
  16. package/dist/{chunk-TRSZEK2D.js.map → chunk-2XUXVKT4.js.map} +1 -1
  17. package/dist/{chunk-BRKCPONM.js → chunk-5PIO7QBG.js} +2 -2
  18. package/dist/{chunk-5QYSY5KI.js → chunk-AMLFKPLL.js} +139 -3
  19. package/dist/chunk-AMLFKPLL.js.map +1 -0
  20. package/dist/chunk-CGKWNXJR.js +7 -0
  21. package/dist/chunk-CGKWNXJR.js.map +1 -0
  22. package/dist/{chunk-3NXLGTC6.js → chunk-DCWXVAQT.js} +2 -2
  23. package/dist/{chunk-3NXLGTC6.js.map → chunk-DCWXVAQT.js.map} +1 -1
  24. package/dist/{chunk-P3NESXFX.js → chunk-PDAKCYHZ.js} +50 -9
  25. package/dist/chunk-PDAKCYHZ.js.map +1 -0
  26. package/dist/{extract-bundle-FW23CEMG.js → extract-bundle-UWKJT4MU.js} +371 -14
  27. package/dist/extract-bundle-UWKJT4MU.js.map +1 -0
  28. package/dist/{server-PGKMCRF5.js → server-RSHSZBUZ.js} +170 -163
  29. package/dist/server-RSHSZBUZ.js.map +1 -0
  30. package/dist/{site-extract-repository-UXSFD2QM.js → site-extract-repository-J3RH3MOS.js} +3 -3
  31. package/dist/{worker-DD243CON.js → worker-YKRIK5LS.js} +2 -2
  32. package/package.json +3 -1
  33. package/dist/chunk-3WNPHRGG.js +0 -7
  34. package/dist/chunk-3WNPHRGG.js.map +0 -1
  35. package/dist/chunk-5QYSY5KI.js.map +0 -1
  36. package/dist/chunk-P3NESXFX.js.map +0 -1
  37. package/dist/extract-bundle-FW23CEMG.js.map +0 -1
  38. package/dist/server-PGKMCRF5.js.map +0 -1
  39. /package/dist/{chunk-BRKCPONM.js.map → chunk-5PIO7QBG.js.map} +0 -0
  40. /package/dist/{site-extract-repository-UXSFD2QM.js.map → site-extract-repository-J3RH3MOS.js.map} +0 -0
  41. /package/dist/{worker-DD243CON.js.map → worker-YKRIK5LS.js.map} +0 -0
@@ -9070,7 +9070,7 @@ async function scrollAndStitch(page, device = "desktop") {
9070
9070
  await page.setViewportSize(vp);
9071
9071
  let previousHeight = 0;
9072
9072
  let stableRounds = 0;
9073
- for (let round = 0; round < LAZY_SCROLL_ROUNDS && stableRounds < 2; round++) {
9073
+ for (let round2 = 0; round2 < LAZY_SCROLL_ROUNDS && stableRounds < 2; round2++) {
9074
9074
  const height = await page.evaluate(() => {
9075
9075
  for (const image of Array.from(document.querySelectorAll('img[loading="lazy"]'))) {
9076
9076
  image.loading = "eager";
@@ -9719,8 +9719,9 @@ async function fetchText(url, auditedHost, timeoutMs = 1e4) {
9719
9719
  }
9720
9720
  function extractSitemapLocs(xml) {
9721
9721
  const locs = [];
9722
- for (const m of xml.matchAll(/<loc>\s*(https?:\/\/[^<\s]+)\s*<\/loc>/gi)) {
9723
- const candidate = m[1].trim();
9722
+ for (const m of xml.matchAll(/<loc\b[^>]*>\s*(?:<!\[CDATA\[([\s\S]*?)\]\]>|([^<]+))\s*<\/loc>/gi)) {
9723
+ const candidate = (m[1] ?? m[2] ?? "").replace(/&amp;/gi, "&").replace(/&lt;/gi, "<").replace(/&gt;/gi, ">").replace(/&quot;/gi, '"').replace(/&#39;|&apos;/gi, "'").trim();
9724
+ if (!/^https?:\/\//i.test(candidate)) continue;
9724
9725
  if (boundedDiscoveredUrl(candidate)) locs.push(candidate);
9725
9726
  }
9726
9727
  return locs;
@@ -10059,7 +10060,52 @@ async function createBrowserWithRetry(client2, headless) {
10059
10060
  }
10060
10061
  throw new Error("unreachable");
10061
10062
  }
10062
- async function crawlPage(context, url, headless, telemetry) {
10063
+ async function settleRenderedPage(page) {
10064
+ await page.waitForLoadState("load", { timeout: 8e3 }).catch(() => void 0);
10065
+ await page.waitForLoadState("networkidle", { timeout: 5e3 }).catch(() => void 0);
10066
+ await page.evaluate(async () => {
10067
+ const wait = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
10068
+ const height = Math.max(document.documentElement?.scrollHeight ?? 0, document.body?.scrollHeight ?? 0);
10069
+ const viewport = Math.max(1, window.innerHeight);
10070
+ const steps = Math.min(12, Math.max(1, Math.ceil(height / viewport)));
10071
+ for (let index = 1; index <= steps; index++) {
10072
+ window.scrollTo(0, Math.min(height, index * viewport));
10073
+ await wait(120);
10074
+ }
10075
+ window.scrollTo(0, 0);
10076
+ await wait(250);
10077
+ }).catch(() => void 0);
10078
+ }
10079
+ async function captureInertRenderedDom(page) {
10080
+ return page.evaluate((maxBytes) => {
10081
+ const root = document.documentElement?.cloneNode(true);
10082
+ if (!root) return { renderedDom: "", renderedDomTruncated: false, renderedDomSanitized: true };
10083
+ root.querySelectorAll('script,noscript,template,iframe,object,embed,meta[http-equiv="refresh"]').forEach((node) => node.remove());
10084
+ root.querySelectorAll("*").forEach((node) => {
10085
+ for (const attribute of [...node.attributes]) {
10086
+ const name = attribute.name.toLowerCase();
10087
+ const value = attribute.value.trim().toLowerCase();
10088
+ if (name.startsWith("on") || (name === "href" || name === "src" || name === "action") && value.startsWith("javascript:")) {
10089
+ node.removeAttribute(attribute.name);
10090
+ }
10091
+ }
10092
+ });
10093
+ const html = "<!doctype html>\n" + root.outerHTML;
10094
+ const encoder = new TextEncoder();
10095
+ if (encoder.encode(html).byteLength <= maxBytes) {
10096
+ return { renderedDom: html, renderedDomTruncated: false, renderedDomSanitized: true };
10097
+ }
10098
+ let low = 0;
10099
+ let high = html.length;
10100
+ while (low < high) {
10101
+ const middle = Math.ceil((low + high) / 2);
10102
+ if (encoder.encode(html.slice(0, middle)).byteLength <= maxBytes) low = middle;
10103
+ else high = middle - 1;
10104
+ }
10105
+ return { renderedDom: html.slice(0, low), renderedDomTruncated: true, renderedDomSanitized: true };
10106
+ }, MAX_RENDERED_DOM_BYTES);
10107
+ }
10108
+ async function crawlPage(context, url, headless, telemetry, options) {
10063
10109
  const startedAt = Date.now();
10064
10110
  let page = null;
10065
10111
  let guardState = null;
@@ -10068,6 +10114,7 @@ async function crawlPage(context, url, headless, telemetry) {
10068
10114
  guardState = await installBrowserRequestGuard(page, url);
10069
10115
  const work = (async (p) => {
10070
10116
  const resp = await p.goto(url, { waitUntil: "domcontentloaded", timeout: 3e4 });
10117
+ if (options.renderJavaScript || options.captureRenderedDom) await settleRenderedPage(p);
10071
10118
  let html = await p.evaluate((maxBytes) => {
10072
10119
  const value = document.documentElement?.outerHTML ?? "";
10073
10120
  if (new TextEncoder().encode(value).byteLength > maxBytes) throw new Error(`Browser HTML exceeds ${maxBytes} byte limit`);
@@ -10103,6 +10150,7 @@ async function crawlPage(context, url, headless, telemetry) {
10103
10150
  };
10104
10151
  }
10105
10152
  const isHttpError = resp != null && (resp.status() < 200 || resp.status() >= 300);
10153
+ const rendered = options.captureRenderedDom ? await captureInertRenderedDom(p) : null;
10106
10154
  return {
10107
10155
  url,
10108
10156
  html,
@@ -10114,10 +10162,12 @@ async function crawlPage(context, url, headless, telemetry) {
10114
10162
  ...isHttpError ? { errorCode: `browser_http_${resp.status()}`, error: `Browser fetch returned HTTP ${resp.status()}` } : {},
10115
10163
  browserMode: headless ? "stealth_headless" : "stealth_headful",
10116
10164
  challengeDetected: false,
10117
- stopReason: isHttpError ? "http_error" : "usable_page"
10165
+ stopReason: isHttpError ? "http_error" : "usable_page",
10166
+ ...rendered ?? {}
10118
10167
  };
10119
10168
  })(page);
10120
- const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error("page-hard-timeout")), PAGE_HARD_TIMEOUT_MS));
10169
+ const hardTimeoutMs = options.renderJavaScript || options.captureRenderedDom ? 55e3 : PAGE_HARD_TIMEOUT_MS;
10170
+ const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error("page-hard-timeout")), hardTimeoutMs));
10121
10171
  return await Promise.race([work, timeout]);
10122
10172
  } catch (error) {
10123
10173
  const message = guardState?.blockedReason ?? (error instanceof Error ? error.message : String(error));
@@ -10189,7 +10239,7 @@ async function crawlWithRotation(urls, opts) {
10189
10239
  }
10190
10240
  const context = await browser.newContext(withRuntimeUserAgent({ serviceWorkers: "block" }, runtimeUserAgent));
10191
10241
  try {
10192
- r = await crawlPage(context, url, passHeadless, { client: client2, sessionId: kb.session_id });
10242
+ r = await crawlPage(context, url, passHeadless, { client: client2, sessionId: kb.session_id }, opts);
10193
10243
  } finally {
10194
10244
  await context.close().catch(() => {
10195
10245
  });
@@ -10247,7 +10297,7 @@ async function crawlWithRotation(urls, opts) {
10247
10297
  attemptErrors: ["No browser result was recorded."]
10248
10298
  });
10249
10299
  }
10250
- var import_sdk3, import_playwright3, DEFAULT_CONCURRENCY, DEFAULT_URLS_PER_BROWSER, DEFAULT_HEADLESS, PAGE_HARD_TIMEOUT_MS;
10300
+ var import_sdk3, import_playwright3, DEFAULT_CONCURRENCY, DEFAULT_URLS_PER_BROWSER, DEFAULT_HEADLESS, MAX_RENDERED_DOM_BYTES, PAGE_HARD_TIMEOUT_MS;
10251
10301
  var init_rotating_proxy_crawl = __esm({
10252
10302
  "src/api/rotating-proxy-crawl.ts"() {
10253
10303
  "use strict";
@@ -10264,6 +10314,7 @@ var init_rotating_proxy_crawl = __esm({
10264
10314
  DEFAULT_CONCURRENCY = 1;
10265
10315
  DEFAULT_URLS_PER_BROWSER = 10;
10266
10316
  DEFAULT_HEADLESS = true;
10317
+ MAX_RENDERED_DOM_BYTES = 256 * 1024;
10267
10318
  PAGE_HARD_TIMEOUT_MS = 35e3;
10268
10319
  }
10269
10320
  });
@@ -10559,7 +10610,7 @@ function buildLinkReport(edges, metrics, siteUrl) {
10559
10610
  domMap.set(domain, d);
10560
10611
  }
10561
10612
  const externalDomains = [...domMap.entries()].map(([domain, d]) => ({ domain, links: d.links, nofollow: d.nofollow, pages: d.pages.size })).sort((a, b) => b.links - a.links);
10562
- const round = (n) => Math.round(n * 10) / 10;
10613
+ const round2 = (n) => Math.round(n * 10) / 10;
10563
10614
  return {
10564
10615
  summary: {
10565
10616
  internal: {
@@ -10567,8 +10618,8 @@ function buildLinkReport(edges, metrics, siteUrl) {
10567
10618
  pages,
10568
10619
  orphans,
10569
10620
  brokenInternal,
10570
- avgInlinks: pages ? round(sumInlinks / pages) : 0,
10571
- avgOutlinks: pages ? round(sumOutlinks / pages) : 0,
10621
+ avgInlinks: pages ? round2(sumInlinks / pages) : 0,
10622
+ avgOutlinks: pages ? round2(sumOutlinks / pages) : 0,
10572
10623
  distribution,
10573
10624
  topByInlinks
10574
10625
  },
@@ -11832,17 +11883,29 @@ async function mapWithConcurrency(items, concurrency, fn) {
11832
11883
  }
11833
11884
  async function extractPagesRotating(urls, opts) {
11834
11885
  const uniqueUrls = [...new Set(urls)];
11835
- const plainPages = await mapWithConcurrency(uniqueUrls, opts.concurrency ?? EXTRACT_CONCURRENCY, async (url) => {
11886
+ const initialPages = await mapWithConcurrency(uniqueUrls, opts.concurrency ?? EXTRACT_CONCURRENCY, async (url) => {
11836
11887
  const checked = await validatePublicHttpUrl(url, { field: "page URL" });
11837
- if (checked.error || !checked.parsed) return fetchPagePlain(url);
11838
- if (normalizeUrl(checked.parsed.href, checked.parsed.href)) return fetchPagePlain(url);
11839
- const attempt = failedAttempt("fetch", null, 0, "non_page_url", "The URL points to an asset, feed, API, account route, or other non-page path excluded from text-page extraction.");
11840
- const page = emptyPageData(url, null, "fetch", { code: attempt.errorCode, message: attempt.error, attempts: [attempt] });
11888
+ if (checked.error || !checked.parsed) {
11889
+ if (!opts.forceBrowserRender) return fetchPagePlain(url);
11890
+ const attempt2 = failedAttempt("browser", null, 0, "unsafe_url", checked.error ?? "Page URL was rejected.");
11891
+ return emptyPageData(url, null, "browser", { code: attempt2.errorCode, message: attempt2.error, attempts: [attempt2] });
11892
+ }
11893
+ if (normalizeUrl(checked.parsed.href, checked.parsed.href)) {
11894
+ if (!opts.forceBrowserRender) return fetchPagePlain(url);
11895
+ return emptyPageData(url, null, "browser", {
11896
+ code: "browser_pending",
11897
+ message: "Awaiting forced JavaScript rendering.",
11898
+ attempts: []
11899
+ });
11900
+ }
11901
+ const method = opts.forceBrowserRender ? "browser" : "fetch";
11902
+ const attempt = failedAttempt(method, null, 0, "non_page_url", "The URL points to an asset, feed, API, account route, or other non-page path excluded from text-page extraction.");
11903
+ const page = emptyPageData(url, null, method, { code: attempt.errorCode, message: attempt.error, attempts: [attempt] });
11841
11904
  page.contentKind = "excluded_url";
11842
11905
  return page;
11843
11906
  });
11844
- const byUrl = new Map(plainPages.map((page) => [page.url, page]));
11845
- const browserTargets = plainPages.filter((page) => !isPageExtractionSuccessful(page) && ![
11907
+ const byUrl = new Map(initialPages.map((page) => [page.url, page]));
11908
+ const browserTargets = opts.forceBrowserRender ? initialPages.filter((page) => page.failureCode === "browser_pending").map((page) => page.url) : initialPages.filter((page) => !isPageExtractionSuccessful(page) && ![
11846
11909
  "unsafe_url",
11847
11910
  "unsafe_redirect",
11848
11911
  "cross_site_redirect",
@@ -11854,7 +11917,9 @@ async function extractPagesRotating(urls, opts) {
11854
11917
  const fetched = await crawlWithRotation(browserTargets, {
11855
11918
  apiKey: opts.kernelApiKey,
11856
11919
  concurrency: opts.concurrency,
11857
- urlsPerBrowser: opts.urlsPerBrowser
11920
+ urlsPerBrowser: opts.urlsPerBrowser,
11921
+ renderJavaScript: opts.forceBrowserRender,
11922
+ captureRenderedDom: opts.captureRenderedDom
11858
11923
  });
11859
11924
  for (const result of fetched) {
11860
11925
  const plainPage = byUrl.get(result.url);
@@ -11871,12 +11936,17 @@ async function extractPagesRotating(urls, opts) {
11871
11936
  const browserOk = !redirectRejected && Boolean(result.html) && result.status >= 200 && result.status < 300;
11872
11937
  if (browserOk) {
11873
11938
  const replay = parseWaybackReplayUrl(result.url);
11874
- byUrl.set(result.url, parsePageData(result.url, result.html, result.status, "browser", {
11939
+ const page = parsePageData(result.url, result.renderedDom ?? result.html, result.status, "browser", {
11875
11940
  headers: result.headers,
11876
11941
  responseTimeMs,
11877
11942
  redirectUrl: result.redirectUrl,
11878
11943
  documentUrl: replay?.originalUrl ?? result.redirectUrl ?? result.url
11879
- }, [...priorAttempts, ...browserAttemptEntries(result, true)]));
11944
+ }, [...priorAttempts, ...browserAttemptEntries(result, true)]);
11945
+ if (result.renderedDom != null) {
11946
+ page.renderedDomTruncated = result.renderedDomTruncated === true;
11947
+ page.renderedDomSanitized = result.renderedDomSanitized === true;
11948
+ }
11949
+ byUrl.set(result.url, page);
11880
11950
  continue;
11881
11951
  }
11882
11952
  const code = redirectRejected ? "unsafe_browser_redirect" : result.errorCode ?? (result.status > 0 ? `browser_http_${result.status}` : result.html ? "browser_invalid_status" : "browser_empty_response");
@@ -11890,7 +11960,16 @@ async function extractPagesRotating(urls, opts) {
11890
11960
  }));
11891
11961
  }
11892
11962
  }
11893
- return urls.map((url) => byUrl.get(url) ?? emptyPageData(url, null, "fetch", {
11963
+ if (opts.forceBrowserRender && !opts.kernelApiKey) {
11964
+ for (const url of browserTargets) {
11965
+ byUrl.set(url, emptyPageData(url, null, "browser", {
11966
+ code: "browser_service_unconfigured",
11967
+ message: "Forced JavaScript rendering requires the hosted browser service.",
11968
+ attempts: [failedAttempt("browser", null, 0, "browser_service_unconfigured", "Forced JavaScript rendering requires the hosted browser service.")]
11969
+ }));
11970
+ }
11971
+ }
11972
+ return urls.map((url) => byUrl.get(url) ?? emptyPageData(url, null, opts.forceBrowserRender ? "browser" : "fetch", {
11894
11973
  code: "result_missing",
11895
11974
  message: "No extraction result was recorded."
11896
11975
  }));
@@ -12921,8 +13000,8 @@ var init_product_contract_generated = __esm({
12921
13000
  "freeSignupCredits": 0
12922
13001
  },
12923
13002
  "inventory": {
12924
- "totalTools": 258,
12925
- "scraperTools": 157,
13003
+ "totalTools": 259,
13004
+ "scraperTools": 158,
12926
13005
  "memoryTools": 101
12927
13006
  },
12928
13007
  "concurrencyPack": {
@@ -16599,6 +16678,369 @@ var init_extraction_problems = __esm({
16599
16678
  }
16600
16679
  });
16601
16680
 
16681
+ // src/api/commons-embeddings.ts
16682
+ function commonsEmbedModel() {
16683
+ return (process.env.JINA_EMBED_MODEL ?? "jina-embeddings-v5-omni-small").trim();
16684
+ }
16685
+ function commonsEmbedDim() {
16686
+ return Number((process.env.JINA_EMBED_DIM ?? "1024").trim());
16687
+ }
16688
+ function commonsSemanticSearchConfigured() {
16689
+ return Boolean(process.env.JINA_API_KEY?.trim() && process.env.MEMORY_DATABASE_URL?.trim());
16690
+ }
16691
+ function vectorSql() {
16692
+ if (_vectorSql) return _vectorSql;
16693
+ const url = process.env.MEMORY_DATABASE_URL?.trim();
16694
+ if (!url) throw new Error("MEMORY_DATABASE_URL is not set; Commons semantic search needs the shared Postgres.");
16695
+ _vectorSql = (0, import_serverless.neon)(url);
16696
+ return _vectorSql;
16697
+ }
16698
+ async function ensureCommonsVectorSchema() {
16699
+ if (vectorSchemaReady) return;
16700
+ const dimension = commonsEmbedDim();
16701
+ await vectorSql().query("CREATE EXTENSION IF NOT EXISTS vector");
16702
+ await vectorSql().query(`
16703
+ CREATE TABLE IF NOT EXISTS commons_index_vectors (
16704
+ document_id TEXT PRIMARY KEY,
16705
+ entity_id TEXT NOT NULL,
16706
+ document_type TEXT NOT NULL,
16707
+ title TEXT NOT NULL,
16708
+ embedding vector(${dimension}) NOT NULL,
16709
+ model TEXT NOT NULL,
16710
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
16711
+ )
16712
+ `);
16713
+ await vectorSql().query("CREATE INDEX IF NOT EXISTS commons_index_vectors_entity ON commons_index_vectors(entity_id)");
16714
+ vectorSchemaReady = true;
16715
+ }
16716
+ async function embedCommonsTexts(texts) {
16717
+ const apiKey = process.env.JINA_API_KEY?.trim();
16718
+ if (!apiKey) throw new Error("JINA_API_KEY is not set; Commons semantic search cannot embed.");
16719
+ if (!texts.length) return [];
16720
+ const response = await fetch("https://api.jina.ai/v1/embeddings", {
16721
+ method: "POST",
16722
+ headers: { authorization: `Bearer ${apiKey}`, "content-type": "application/json" },
16723
+ body: JSON.stringify({
16724
+ model: commonsEmbedModel(),
16725
+ dimensions: commonsEmbedDim(),
16726
+ input: texts.map((text2) => ({ text: text2.slice(0, 8e3) }))
16727
+ }),
16728
+ signal: AbortSignal.timeout(6e4)
16729
+ });
16730
+ if (!response.ok) {
16731
+ throw new Error(`Jina embedding request failed with HTTP ${response.status}: ${(await response.text()).slice(0, 200)}`);
16732
+ }
16733
+ const payload = await response.json();
16734
+ const vectors = (payload.data ?? []).map((item) => item.embedding);
16735
+ if (vectors.length !== texts.length) {
16736
+ throw new Error(`Jina returned ${vectors.length} embeddings for ${texts.length} inputs.`);
16737
+ }
16738
+ return vectors;
16739
+ }
16740
+ async function embedQueuedCommonsDocuments(limit = 50) {
16741
+ if (!commonsSemanticSearchConfigured()) return { claimed: 0, embedded: 0, failed: 0, remaining: 0 };
16742
+ await ensureCommonsVectorSchema();
16743
+ const bounded2 = Math.max(1, Math.min(200, Math.floor(limit)));
16744
+ const queued = await getDb().execute({
16745
+ sql: `SELECT id, entity_id, document_type, title, text FROM commons_index_documents
16746
+ WHERE embedding_status IN ('queued', 'failed') ORDER BY updated_at ASC LIMIT ?`,
16747
+ args: [bounded2]
16748
+ });
16749
+ const rows = queued.rows;
16750
+ if (!rows.length) return { claimed: 0, embedded: 0, failed: 0, remaining: await queuedCommonsDocumentCount() };
16751
+ let embedded = 0;
16752
+ let failed = 0;
16753
+ const model = commonsEmbedModel();
16754
+ try {
16755
+ const vectors = await embedCommonsTexts(rows.map((row) => `${row.title}
16756
+
16757
+ ${row.text}`));
16758
+ for (const [index, row] of rows.entries()) {
16759
+ const literal = `[${vectors[index].join(",")}]`;
16760
+ await vectorSql().query(
16761
+ `INSERT INTO commons_index_vectors (document_id, entity_id, document_type, title, embedding, model, updated_at)
16762
+ VALUES ($1, $2, $3, $4, $5::vector, $6, now())
16763
+ ON CONFLICT (document_id) DO UPDATE SET entity_id = EXCLUDED.entity_id, document_type = EXCLUDED.document_type,
16764
+ title = EXCLUDED.title, embedding = EXCLUDED.embedding, model = EXCLUDED.model, updated_at = now()`,
16765
+ [row.id, row.entity_id, row.document_type, row.title, literal, model]
16766
+ );
16767
+ await getDb().execute({
16768
+ sql: `UPDATE commons_index_documents SET embedding_status = 'indexed', embedding_provider = ?, embedding_model = ?,
16769
+ vector_ref = ?, indexed_at = ?, error = NULL WHERE id = ?`,
16770
+ args: [COMMONS_EMBED_PROVIDER, model, row.id, (/* @__PURE__ */ new Date()).toISOString(), row.id]
16771
+ });
16772
+ embedded += 1;
16773
+ }
16774
+ } catch (error) {
16775
+ failed = rows.length - embedded;
16776
+ const message = (error instanceof Error ? error.message : String(error)).slice(0, 500);
16777
+ for (const row of rows.slice(embedded)) {
16778
+ await getDb().execute({
16779
+ sql: `UPDATE commons_index_documents SET embedding_status = 'failed', error = ? WHERE id = ?`,
16780
+ args: [message, row.id]
16781
+ }).catch(() => void 0);
16782
+ }
16783
+ }
16784
+ return { claimed: rows.length, embedded, failed, remaining: await queuedCommonsDocumentCount() };
16785
+ }
16786
+ async function queuedCommonsDocumentCount() {
16787
+ const result = await getDb().execute(`SELECT COUNT(*) AS n FROM commons_index_documents WHERE embedding_status IN ('queued', 'failed')`);
16788
+ return Number(result.rows[0]?.n ?? 0);
16789
+ }
16790
+ async function semanticCommonsEntityScores(query, limit = 40) {
16791
+ const scores = /* @__PURE__ */ new Map();
16792
+ if (!commonsSemanticSearchConfigured() || !query.trim()) return scores;
16793
+ await ensureCommonsVectorSchema();
16794
+ const [vector] = await embedCommonsTexts([query]);
16795
+ if (!vector) return scores;
16796
+ const rows = await vectorSql().query(
16797
+ `SELECT entity_id, MAX(1 - (embedding <=> $1::vector)) AS score
16798
+ FROM commons_index_vectors GROUP BY entity_id ORDER BY score DESC LIMIT $2`,
16799
+ [`[${vector.join(",")}]`, Math.max(1, Math.min(100, limit))]
16800
+ );
16801
+ for (const row of rows) scores.set(String(row.entity_id), Number(row.score));
16802
+ return scores;
16803
+ }
16804
+ var import_serverless, COMMONS_EMBED_PROVIDER, _vectorSql, vectorSchemaReady;
16805
+ var init_commons_embeddings = __esm({
16806
+ "src/api/commons-embeddings.ts"() {
16807
+ "use strict";
16808
+ import_serverless = require("@neondatabase/serverless");
16809
+ init_db();
16810
+ COMMONS_EMBED_PROVIDER = "jina";
16811
+ _vectorSql = null;
16812
+ vectorSchemaReady = false;
16813
+ }
16814
+ });
16815
+
16816
+ // src/api/site-content-similarity.ts
16817
+ function round(value, places) {
16818
+ const scale = 10 ** places;
16819
+ return Math.round(value * scale) / scale;
16820
+ }
16821
+ function cosineSimilarity(a, b) {
16822
+ if (a.length === 0 || a.length !== b.length) throw new Error("Similarity vectors must have the same non-zero dimension.");
16823
+ let dot = 0;
16824
+ let normA = 0;
16825
+ let normB = 0;
16826
+ for (let index = 0; index < a.length; index++) {
16827
+ dot += a[index] * b[index];
16828
+ normA += a[index] * a[index];
16829
+ normB += b[index] * b[index];
16830
+ }
16831
+ if (normA === 0 || normB === 0) return 0;
16832
+ return Math.max(-1, Math.min(1, dot / (Math.sqrt(normA) * Math.sqrt(normB))));
16833
+ }
16834
+ function corpusHash(pages) {
16835
+ const digest2 = (0, import_node_crypto6.createHash)("sha256");
16836
+ for (const page of [...pages].sort((a, b) => a.url.localeCompare(b.url))) {
16837
+ digest2.update(page.url);
16838
+ digest2.update("\0");
16839
+ digest2.update(page.contentHash);
16840
+ digest2.update("\0");
16841
+ }
16842
+ return digest2.digest("hex");
16843
+ }
16844
+ function markdownBlockSignature(block) {
16845
+ const normalized = block.replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/\[([^\]]+)\]\([^)]*\)/g, "$1").replace(/https?:\/\/\S+/gi, " ").replace(/&(?:amp|nbsp|quot|#39);/gi, " ").replace(/[#>*_`~|\\-]+/g, " ").replace(/[^\p{L}\p{N}]+/gu, " ").replace(/\s+/g, " ").trim().toLowerCase();
16846
+ return normalized.length >= 12 ? normalized : null;
16847
+ }
16848
+ function prepareEmbeddingTexts(pages) {
16849
+ const blocksByPage = pages.map((page) => page.bodyMarkdown.split(/\n{2,}/).map((block) => block.trim()).filter(Boolean));
16850
+ const minimumPageCount = pages.length >= 4 ? Math.max(3, Math.ceil(pages.length * 0.5)) : null;
16851
+ const pageFrequency = /* @__PURE__ */ new Map();
16852
+ for (const blocks of blocksByPage) {
16853
+ const pageSignatures = new Set(blocks.map(markdownBlockSignature).filter((value) => Boolean(value)));
16854
+ for (const signature of pageSignatures) pageFrequency.set(signature, (pageFrequency.get(signature) ?? 0) + 1);
16855
+ }
16856
+ const repeated = /* @__PURE__ */ new Set();
16857
+ if (minimumPageCount != null) {
16858
+ for (const [signature, count] of pageFrequency) {
16859
+ if (count >= minimumPageCount) repeated.add(signature);
16860
+ }
16861
+ }
16862
+ let removedCharacters = 0;
16863
+ const texts = pages.map((page, pageIndex) => {
16864
+ const kept = [];
16865
+ for (const block of blocksByPage[pageIndex]) {
16866
+ const signature = markdownBlockSignature(block);
16867
+ if (signature && repeated.has(signature)) {
16868
+ removedCharacters += block.length;
16869
+ continue;
16870
+ }
16871
+ kept.push(block);
16872
+ }
16873
+ const cleaned = kept.join("\n\n").trim();
16874
+ const content = cleaned.length >= 60 ? cleaned : page.bodyMarkdown;
16875
+ return `${page.title?.trim() || "Untitled page"}
16876
+
16877
+ ${content}`;
16878
+ });
16879
+ return {
16880
+ texts,
16881
+ minimumPageCount,
16882
+ removedBlockSignatures: repeated.size,
16883
+ removedCharacters
16884
+ };
16885
+ }
16886
+ async function analyzeSiteContentSimilarity(pages, options = {}) {
16887
+ const eligible = pages.filter((page) => page.bodyMarkdown.trim().length > 0).slice(0, MAX_SIMILARITY_PAGES);
16888
+ const requestedThreshold = Number.isFinite(options.threshold) ? options.threshold : DEFAULT_SIMILARITY_THRESHOLD;
16889
+ const requestedMaxPairs = Number.isFinite(options.maxPairs) ? options.maxPairs : DEFAULT_SIMILARITY_MAX_PAIRS;
16890
+ const threshold = Math.max(0, Math.min(1, requestedThreshold));
16891
+ const maxPairs = Math.max(1, Math.min(MAX_SIMILARITY_PAIRS, Math.floor(requestedMaxPairs)));
16892
+ const model = options.model ?? commonsEmbedModel();
16893
+ const dimensions = options.dimensions ?? commonsEmbedDim();
16894
+ const embedTexts = options.embedTexts ?? embedCommonsTexts;
16895
+ const prepared = prepareEmbeddingTexts(eligible);
16896
+ const uniqueTexts = [];
16897
+ const textIndexByHash = /* @__PURE__ */ new Map();
16898
+ const pageTextIndexes = [];
16899
+ for (const [pageIndex, page] of eligible.entries()) {
16900
+ const key = page.contentHash || (0, import_node_crypto6.createHash)("sha256").update(page.bodyMarkdown).digest("hex");
16901
+ let textIndex = textIndexByHash.get(key);
16902
+ if (textIndex == null) {
16903
+ textIndex = uniqueTexts.length;
16904
+ textIndexByHash.set(key, textIndex);
16905
+ uniqueTexts.push(prepared.texts[pageIndex]);
16906
+ }
16907
+ pageTextIndexes.push(textIndex);
16908
+ }
16909
+ const uniqueVectors = [];
16910
+ for (let offset = 0; offset < uniqueTexts.length; offset += 64) {
16911
+ uniqueVectors.push(...await embedTexts(uniqueTexts.slice(offset, offset + 64)));
16912
+ }
16913
+ if (uniqueVectors.length !== uniqueTexts.length) {
16914
+ throw new Error(`Embedding provider returned ${uniqueVectors.length} vectors for ${uniqueTexts.length} unique page bodies.`);
16915
+ }
16916
+ const vectors = pageTextIndexes.map((index) => uniqueVectors[index]);
16917
+ const sets = new DisjointSet(eligible.length);
16918
+ const allScores = [];
16919
+ const qualifying = [];
16920
+ for (let source = 0; source < eligible.length; source++) {
16921
+ for (let target = source + 1; target < eligible.length; target++) {
16922
+ const score = cosineSimilarity(vectors[source], vectors[target]);
16923
+ allScores.push(score);
16924
+ if (score < threshold) continue;
16925
+ qualifying.push({ source, target, score });
16926
+ sets.union(source, target);
16927
+ }
16928
+ }
16929
+ qualifying.sort((a, b) => b.score - a.score || eligible[a.source].url.localeCompare(eligible[b.source].url) || eligible[a.target].url.localeCompare(eligible[b.target].url));
16930
+ const sortedScores = [...allScores].sort((a, b) => a - b);
16931
+ const quantile = (value) => {
16932
+ if (!sortedScores.length) return null;
16933
+ return round(sortedScores[Math.floor((sortedScores.length - 1) * value)], 6);
16934
+ };
16935
+ const membersByRoot = /* @__PURE__ */ new Map();
16936
+ for (let index = 0; index < eligible.length; index++) {
16937
+ const root = sets.find(index);
16938
+ const members = membersByRoot.get(root) ?? [];
16939
+ members.push(index);
16940
+ membersByRoot.set(root, members);
16941
+ }
16942
+ const clusterIdByPage = /* @__PURE__ */ new Map();
16943
+ const clusters = [...membersByRoot.values()].filter((members) => members.length > 1).sort((a, b) => b.length - a.length || eligible[a[0]].url.localeCompare(eligible[b[0]].url)).map((members, index) => {
16944
+ const clusterId = `cluster-${String(index + 1).padStart(3, "0")}`;
16945
+ for (const member of members) clusterIdByPage.set(member, clusterId);
16946
+ return {
16947
+ clusterId,
16948
+ pageCount: members.length,
16949
+ urls: members.map((member) => eligible[member].url).sort()
16950
+ };
16951
+ });
16952
+ const rows = qualifying.slice(0, maxPairs).map((pair, pairIndex) => {
16953
+ const source = eligible[pair.source];
16954
+ const target = eligible[pair.target];
16955
+ const similarity2 = round(pair.score, 6);
16956
+ return {
16957
+ sourceUrl: source.url,
16958
+ sourceTitle: source.title,
16959
+ targetUrl: target.url,
16960
+ targetTitle: target.title,
16961
+ similarity: similarity2,
16962
+ similarityPercent: round(similarity2 * 100, 2),
16963
+ corpusPercentile: sortedScores.length <= 1 ? 100 : round((1 - pairIndex / (sortedScores.length - 1)) * 100, 2),
16964
+ exactContentDuplicate: Boolean(source.contentHash && source.contentHash === target.contentHash),
16965
+ sourceWordCount: source.wordCount,
16966
+ targetWordCount: target.wordCount,
16967
+ clusterId: clusterIdByPage.get(pair.source) ?? null
16968
+ };
16969
+ });
16970
+ const corpusSha256 = corpusHash(eligible);
16971
+ const analysisSha256 = (0, import_node_crypto6.createHash)("sha256").update(JSON.stringify({
16972
+ corpusSha256,
16973
+ model,
16974
+ dimensions,
16975
+ threshold,
16976
+ maxPairs,
16977
+ boilerplateRemoval: {
16978
+ method: "corpus_repeated_markdown_blocks",
16979
+ minimumPageCount: prepared.minimumPageCount,
16980
+ removedBlockSignatures: prepared.removedBlockSignatures
16981
+ }
16982
+ })).digest("hex");
16983
+ return {
16984
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
16985
+ provider: "jina",
16986
+ model,
16987
+ dimensions,
16988
+ threshold,
16989
+ requestedMaxPairs: maxPairs,
16990
+ comparedPages: eligible.length,
16991
+ possiblePairs: eligible.length * Math.max(0, eligible.length - 1) / 2,
16992
+ qualifyingPairs: qualifying.length,
16993
+ returnedPairs: rows.length,
16994
+ pairsTruncated: qualifying.length > rows.length,
16995
+ corpusSha256,
16996
+ analysisSha256,
16997
+ scoreDistribution: {
16998
+ min: quantile(0),
16999
+ p25: quantile(0.25),
17000
+ median: quantile(0.5),
17001
+ p75: quantile(0.75),
17002
+ p90: quantile(0.9),
17003
+ max: quantile(1)
17004
+ },
17005
+ boilerplateRemoval: {
17006
+ method: "corpus_repeated_markdown_blocks",
17007
+ minimumPageCount: prepared.minimumPageCount,
17008
+ removedBlockSignatures: prepared.removedBlockSignatures,
17009
+ removedCharacters: prepared.removedCharacters
17010
+ },
17011
+ rows,
17012
+ clusters
17013
+ };
17014
+ }
17015
+ var import_node_crypto6, MAX_SIMILARITY_PAGES, DEFAULT_SIMILARITY_THRESHOLD, DEFAULT_SIMILARITY_MAX_PAIRS, MAX_SIMILARITY_PAIRS, DisjointSet;
17016
+ var init_site_content_similarity = __esm({
17017
+ "src/api/site-content-similarity.ts"() {
17018
+ "use strict";
17019
+ import_node_crypto6 = require("crypto");
17020
+ init_commons_embeddings();
17021
+ MAX_SIMILARITY_PAGES = 500;
17022
+ DEFAULT_SIMILARITY_THRESHOLD = 0.9;
17023
+ DEFAULT_SIMILARITY_MAX_PAIRS = 1e4;
17024
+ MAX_SIMILARITY_PAIRS = 5e4;
17025
+ DisjointSet = class {
17026
+ parent;
17027
+ constructor(size) {
17028
+ this.parent = Array.from({ length: size }, (_, index) => index);
17029
+ }
17030
+ find(value) {
17031
+ const parent = this.parent[value];
17032
+ if (parent !== value) this.parent[value] = this.find(parent);
17033
+ return this.parent[value];
17034
+ }
17035
+ union(a, b) {
17036
+ const rootA = this.find(a);
17037
+ const rootB = this.find(b);
17038
+ if (rootA !== rootB) this.parent[rootB] = rootA;
17039
+ }
17040
+ };
17041
+ }
17042
+ });
17043
+
16602
17044
  // src/api/extract-bundle.ts
16603
17045
  var extract_bundle_exports = {};
16604
17046
  __export(extract_bundle_exports, {
@@ -16623,6 +17065,15 @@ function safeImageFilename(url, index) {
16623
17065
  return `image-${index}`;
16624
17066
  }
16625
17067
  }
17068
+ function slugFactory() {
17069
+ const counts = /* @__PURE__ */ new Map();
17070
+ return (url) => {
17071
+ const base = url.replace(/^https?:\/\//, "").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "page";
17072
+ const n = counts.get(base) ?? 0;
17073
+ counts.set(base, n + 1);
17074
+ return n ? `${base}-${n}` : base;
17075
+ };
17076
+ }
16626
17077
  async function* pageChunks(jobId2) {
16627
17078
  const db = getDb();
16628
17079
  let lastRowId = 0;
@@ -16641,6 +17092,25 @@ async function* pageChunks(jobId2) {
16641
17092
  yield res.rows.map((r) => JSON.parse(String(r.page)));
16642
17093
  }
16643
17094
  }
17095
+ function csvCell(value) {
17096
+ const text2 = value == null ? "" : String(value);
17097
+ return /[",\r\n]/.test(text2) ? `"${text2.replace(/"/g, '""')}"` : text2;
17098
+ }
17099
+ function similarityTableRow(row) {
17100
+ return {
17101
+ source_url: row.sourceUrl,
17102
+ source_title: row.sourceTitle,
17103
+ target_url: row.targetUrl,
17104
+ target_title: row.targetTitle,
17105
+ similarity: row.similarity,
17106
+ similarity_percent: row.similarityPercent,
17107
+ corpus_percentile: row.corpusPercentile,
17108
+ exact_content_duplicate: row.exactContentDuplicate,
17109
+ source_word_count: row.sourceWordCount,
17110
+ target_word_count: row.targetWordCount,
17111
+ cluster_id: row.clusterId
17112
+ };
17113
+ }
16644
17114
  async function assembleExtractArtifacts(job, extras = {}) {
16645
17115
  const dir = (0, import_node_path3.join)((0, import_node_os3.tmpdir)(), `extract-bundle-${job.id}-${Date.now()}`);
16646
17116
  const downloadImages = job.options.downloadImages === true;
@@ -16648,8 +17118,14 @@ async function assembleExtractArtifacts(job, extras = {}) {
16648
17118
  const readPageContent = createSiteExtractContentReader(String(job.userId));
16649
17119
  (0, import_node_fs3.mkdirSync)(dir, { recursive: true });
16650
17120
  if (downloadImages) (0, import_node_fs3.mkdirSync)((0, import_node_path3.join)(dir, "images"), { recursive: true });
17121
+ const captureRenderedDom = job.options.captureRenderedDom === true;
17122
+ const semanticSimilarity = job.options.semanticSimilarity === true;
17123
+ if (captureRenderedDom) (0, import_node_fs3.mkdirSync)((0, import_node_path3.join)(dir, "rendered-dom"), { recursive: true });
16651
17124
  try {
16652
17125
  const metas = [];
17126
+ const similarityPages = [];
17127
+ const renderedDomManifest = [];
17128
+ const domSlug = slugFactory();
16653
17129
  const pageExportEntries = [];
16654
17130
  const waybackTimeline = job.options.waybackTimeline;
16655
17131
  const statusByUrl = /* @__PURE__ */ new Map();
@@ -16677,12 +17153,32 @@ async function assembleExtractArtifacts(job, extras = {}) {
16677
17153
  schemaTypes: p.schemaTypes.length > 0 ? ["present"] : [],
16678
17154
  outlinks: []
16679
17155
  });
16680
- const pageId = p.pageId ?? (0, import_node_crypto6.createHash)("sha256").update(p.archivedUrl ?? p.url).digest("hex");
17156
+ const pageId = p.pageId ?? (0, import_node_crypto7.createHash)("sha256").update(p.archivedUrl ?? p.url).digest("hex");
16681
17157
  const pageDir = (0, import_node_path3.join)(dir, "pages", pageId);
16682
17158
  (0, import_node_fs3.mkdirSync)(pageDir, { recursive: true });
16683
17159
  const content = p.contentRef ? await readPageContent(p.contentRef) : null;
16684
17160
  const html = content?.html ?? null;
16685
17161
  const markdown = content?.markdown ?? p.bodyMarkdown ?? null;
17162
+ if (semanticSimilarity && p.extractionStatus === "successful" && markdown?.trim()) {
17163
+ similarityPages.push({
17164
+ url: p.url,
17165
+ title: p.title,
17166
+ bodyMarkdown: markdown,
17167
+ contentHash: p.contentHash,
17168
+ wordCount: p.wordCount
17169
+ });
17170
+ }
17171
+ if (captureRenderedDom && html != null) {
17172
+ const relativePath = `rendered-dom/${domSlug(p.url)}.html.txt`;
17173
+ (0, import_node_fs3.writeFileSync)((0, import_node_path3.join)(dir, relativePath), html);
17174
+ renderedDomManifest.push({
17175
+ url: p.url,
17176
+ path: relativePath,
17177
+ bytes: Buffer.byteLength(html),
17178
+ truncated: p.renderedDomTruncated === true,
17179
+ sanitized: p.renderedDomSanitized === true
17180
+ });
17181
+ }
16686
17182
  const htmlPath = html == null ? null : `pages/${pageId}/page.html`;
16687
17183
  const markdownPath = markdown == null ? null : `pages/${pageId}/page.md`;
16688
17184
  const legacyMarkdownPath = markdown == null || !p.archiveRequestedMonth ? null : `pages/${p.archiveRequestedMonth}/${pageId}.md`;
@@ -16702,8 +17198,8 @@ async function assembleExtractArtifacts(job, extras = {}) {
16702
17198
  markdownPath,
16703
17199
  htmlBytes: html == null ? 0 : Buffer.byteLength(html),
16704
17200
  markdownBytes: markdown == null ? 0 : Buffer.byteLength(markdown),
16705
- htmlSha256: html == null ? null : (0, import_node_crypto6.createHash)("sha256").update(html).digest("hex"),
16706
- markdownSha256: markdown == null ? null : (0, import_node_crypto6.createHash)("sha256").update(markdown).digest("hex")
17201
+ htmlSha256: html == null ? null : (0, import_node_crypto7.createHash)("sha256").update(html).digest("hex"),
17202
+ markdownSha256: markdown == null ? null : (0, import_node_crypto7.createHash)("sha256").update(markdown).digest("hex")
16707
17203
  }
16708
17204
  };
16709
17205
  const pageJson = JSON.stringify(pageRecord, null, 2);
@@ -16716,7 +17212,7 @@ async function assembleExtractArtifacts(job, extras = {}) {
16716
17212
  htmlPath,
16717
17213
  markdownPath,
16718
17214
  legacyMarkdownPath,
16719
- jsonSha256: (0, import_node_crypto6.createHash)("sha256").update(pageJson).digest("hex"),
17215
+ jsonSha256: (0, import_node_crypto7.createHash)("sha256").update(pageJson).digest("hex"),
16720
17216
  htmlSha256: pageRecord.content.htmlSha256,
16721
17217
  markdownSha256: pageRecord.content.markdownSha256,
16722
17218
  htmlBytes: pageRecord.content.htmlBytes,
@@ -16784,7 +17280,7 @@ async function assembleExtractArtifacts(job, extras = {}) {
16784
17280
  }
16785
17281
  if (p.imageLinks?.length) {
16786
17282
  for (const [idx, imgUrl] of p.imageLinks.entries()) {
16787
- const imageId = (0, import_node_crypto6.createHash)("sha256").update(`${p.url}\0${imgUrl}`).digest("hex");
17283
+ const imageId = (0, import_node_crypto7.createHash)("sha256").update(`${p.url}\0${imgUrl}`).digest("hex");
16788
17284
  const record = {
16789
17285
  imageId,
16790
17286
  sourcePage: p.url,
@@ -16838,7 +17334,7 @@ async function assembleExtractArtifacts(job, extras = {}) {
16838
17334
  record.artifactId = artifact.key;
16839
17335
  record.mimeType = result.mimeType;
16840
17336
  record.bytes = result.sizeBytes;
16841
- record.sha256 = (0, import_node_crypto6.createHash)("sha256").update(bytes).digest("hex");
17337
+ record.sha256 = (0, import_node_crypto7.createHash)("sha256").update(bytes).digest("hex");
16842
17338
  }, (error) => {
16843
17339
  imagesFailed++;
16844
17340
  record.status = "failed";
@@ -16976,7 +17472,7 @@ async function assembleExtractArtifacts(job, extras = {}) {
16976
17472
 
16977
17473
  ${renderImageSection(extras.imageAudit)}`;
16978
17474
  const metricValues = [...metrics.values()];
16979
- const round = (n) => Math.round(n * 10) / 10;
17475
+ const round2 = (n) => Math.round(n * 10) / 10;
16980
17476
  const distribution = { zero: 0, oneToTwo: 0, threeToTen: 0, elevenPlus: 0 };
16981
17477
  let sumInlinks = 0;
16982
17478
  let sumOutlinks = 0;
@@ -16996,8 +17492,8 @@ ${renderImageSection(extras.imageAudit)}`;
16996
17492
  pages: metas.length,
16997
17493
  orphans: metricValues.filter((m) => m.orphan).length,
16998
17494
  brokenInternal,
16999
- avgInlinks: metas.length ? round(sumInlinks / metas.length) : 0,
17000
- avgOutlinks: metas.length ? round(sumOutlinks / metas.length) : 0,
17495
+ avgInlinks: metas.length ? round2(sumInlinks / metas.length) : 0,
17496
+ avgOutlinks: metas.length ? round2(sumOutlinks / metas.length) : 0,
17001
17497
  distribution,
17002
17498
  topByInlinks: [...metricValues].sort((a, b) => b.inlinks - a.inlinks).slice(0, 20).map((m) => ({ url: m.url, inlinks: m.inlinks, outlinksInternal: m.outlinksInternal, outlinksExternal: m.outlinksExternal }))
17003
17499
  },
@@ -17102,6 +17598,72 @@ ${renderImageSection(extras.imageAudit)}`;
17102
17598
  pagesOut.end();
17103
17599
  captureMatrixOut?.end();
17104
17600
  await Promise.all([pagesFinished, captureMatrixFinished]);
17601
+ if (captureRenderedDom) {
17602
+ (0, import_node_fs3.writeFileSync)(
17603
+ (0, import_node_path3.join)(dir, "rendered-dom", "manifest.jsonl"),
17604
+ renderedDomManifest.map((row) => JSON.stringify(row)).join("\n")
17605
+ );
17606
+ }
17607
+ if (semanticSimilarity) {
17608
+ const analysis = await analyzeSiteContentSimilarity(similarityPages, {
17609
+ threshold: Number(job.options.similarityThreshold ?? void 0),
17610
+ maxPairs: Number(job.options.similarityMaxPairs ?? void 0),
17611
+ embedTexts: extras.embedSimilarityTexts
17612
+ });
17613
+ const tableRows = analysis.rows.map(similarityTableRow);
17614
+ const columns = tableRows.length > 0 ? Object.keys(tableRows[0]) : [
17615
+ "source_url",
17616
+ "source_title",
17617
+ "target_url",
17618
+ "target_title",
17619
+ "similarity",
17620
+ "similarity_percent",
17621
+ "corpus_percentile",
17622
+ "exact_content_duplicate",
17623
+ "source_word_count",
17624
+ "target_word_count",
17625
+ "cluster_id"
17626
+ ];
17627
+ const csv = [
17628
+ columns.join(","),
17629
+ ...tableRows.map((row) => columns.map((column) => csvCell(row[column] ?? null)).join(","))
17630
+ ].join("\n") + "\n";
17631
+ const { rows: _rows, clusters: _clusters, ...summary } = analysis;
17632
+ (0, import_node_fs3.writeFileSync)((0, import_node_path3.join)(dir, "similarity-table.csv"), csv);
17633
+ (0, import_node_fs3.writeFileSync)((0, import_node_path3.join)(dir, "similarity.jsonl"), tableRows.map((row) => JSON.stringify(row)).join("\n"));
17634
+ (0, import_node_fs3.writeFileSync)((0, import_node_path3.join)(dir, "similarity-table-schema.json"), JSON.stringify({
17635
+ tableNameSuggestion: `site_similarity_${new URL(job.startUrl).hostname.replace(/[^a-z0-9]+/gi, "_").replace(/^_+|_+$/g, "").toLowerCase()}`,
17636
+ defaultSort: [{ column: "similarity", direction: "desc" }],
17637
+ columns: {
17638
+ source_url: "text",
17639
+ source_title: "text",
17640
+ target_url: "text",
17641
+ target_title: "text",
17642
+ similarity: "number",
17643
+ similarity_percent: "number",
17644
+ corpus_percentile: "number",
17645
+ exact_content_duplicate: "boolean",
17646
+ source_word_count: "integer",
17647
+ target_word_count: "integer",
17648
+ cluster_id: "text"
17649
+ }
17650
+ }, null, 2));
17651
+ (0, import_node_fs3.writeFileSync)((0, import_node_path3.join)(dir, "similarity-summary.json"), JSON.stringify(summary, null, 2));
17652
+ (0, import_node_fs3.writeFileSync)((0, import_node_path3.join)(dir, "content-clusters.json"), JSON.stringify(analysis.clusters, null, 2));
17653
+ reportMd += [
17654
+ "",
17655
+ "## Rendered content similarity",
17656
+ `- Compared pages: ${analysis.comparedPages}`,
17657
+ `- Raw cosine threshold: ${analysis.threshold}`,
17658
+ `- Corpus score distribution: min ${analysis.scoreDistribution.min ?? "n/a"} / median ${analysis.scoreDistribution.median ?? "n/a"} / p90 ${analysis.scoreDistribution.p90 ?? "n/a"} / max ${analysis.scoreDistribution.max ?? "n/a"}`,
17659
+ `- Qualifying pairs: ${analysis.qualifyingPairs}`,
17660
+ `- Returned table rows: ${analysis.returnedPairs}${analysis.pairsTruncated ? " (capped)" : ""}`,
17661
+ `- Multi-page clusters: ${analysis.clusters.length}`,
17662
+ `- Embedding model: ${analysis.model} (${analysis.dimensions} dimensions)`,
17663
+ `- Corpus boilerplate removed: ${analysis.boilerplateRemoval.removedBlockSignatures} repeated block signatures / ${analysis.boilerplateRemoval.removedCharacters} characters`,
17664
+ `- Corpus SHA-256: ${analysis.corpusSha256}`
17665
+ ].join("\n");
17666
+ }
17105
17667
  (0, import_node_fs3.writeFileSync)((0, import_node_path3.join)(dir, "report.md"), reportMd);
17106
17668
  (0, import_node_fs3.writeFileSync)((0, import_node_path3.join)(dir, "crawl-summary.json"), JSON.stringify(crawlSummary, null, 2));
17107
17669
  (0, import_node_fs3.writeFileSync)((0, import_node_path3.join)(dir, "images-manifest.jsonl"), imageManifest.map((record) => JSON.stringify(record)).join("\n"));
@@ -17167,6 +17729,17 @@ ${renderImageSection(extras.imageAudit)}`;
17167
17729
  { name: "links-summary.json", type: "application/json" },
17168
17730
  { name: "external-domains.json", type: "application/json" }
17169
17731
  ];
17732
+ if (captureRenderedDom) {
17733
+ artifactFiles.push({ name: "rendered-dom/manifest.jsonl", type: "application/x-ndjson" });
17734
+ for (const row of renderedDomManifest) artifactFiles.push({ name: row.path, type: "text/plain" });
17735
+ }
17736
+ if (semanticSimilarity) {
17737
+ artifactFiles.push({ name: "similarity-table.csv", type: "text/csv" });
17738
+ artifactFiles.push({ name: "similarity.jsonl", type: "application/x-ndjson" });
17739
+ artifactFiles.push({ name: "similarity-table-schema.json", type: "application/json" });
17740
+ artifactFiles.push({ name: "similarity-summary.json", type: "application/json" });
17741
+ artifactFiles.push({ name: "content-clusters.json", type: "application/json" });
17742
+ }
17170
17743
  if (waybackTimeline) {
17171
17744
  artifactFiles.push({ name: "wayback-manifest.json", type: "application/json" });
17172
17745
  artifactFiles.push({ name: "capture-matrix.jsonl", type: "application/x-ndjson" });
@@ -17206,12 +17779,12 @@ ${renderImageSection(extras.imageAudit)}`;
17206
17779
  (0, import_node_fs3.rmSync)(dir, { recursive: true, force: true });
17207
17780
  }
17208
17781
  }
17209
- var import_node_fs3, import_node_crypto6, import_node_path3, import_node_os3, import_node_zlib2, import_node_events, import_promises5, import_yazl, import_p_limit4, SITE_EXTRACT_PAGE_CHUNK, MAX_IMAGES_PER_PAGE, MAX_IMAGES_PER_SITE, IMAGE_DOWNLOAD_CONCURRENCY, MAX_IMAGE_BYTES, MAX_SITE_IMAGE_BYTES, MAX_ANALYZED_LINK_EDGES;
17782
+ var import_node_fs3, import_node_crypto7, import_node_path3, import_node_os3, import_node_zlib2, import_node_events, import_promises5, import_yazl, import_p_limit4, SITE_EXTRACT_PAGE_CHUNK, MAX_IMAGES_PER_PAGE, MAX_IMAGES_PER_SITE, IMAGE_DOWNLOAD_CONCURRENCY, MAX_IMAGE_BYTES, MAX_SITE_IMAGE_BYTES, MAX_ANALYZED_LINK_EDGES;
17210
17783
  var init_extract_bundle = __esm({
17211
17784
  "src/api/extract-bundle.ts"() {
17212
17785
  "use strict";
17213
17786
  import_node_fs3 = require("fs");
17214
- import_node_crypto6 = require("crypto");
17787
+ import_node_crypto7 = require("crypto");
17215
17788
  import_node_path3 = require("path");
17216
17789
  import_node_os3 = require("os");
17217
17790
  import_node_zlib2 = require("zlib");
@@ -17228,6 +17801,7 @@ var init_extract_bundle = __esm({
17228
17801
  init_media_extractor();
17229
17802
  init_site_extract_artifacts();
17230
17803
  init_site_extract_content_store();
17804
+ init_site_content_similarity();
17231
17805
  SITE_EXTRACT_PAGE_CHUNK = 25;
17232
17806
  MAX_IMAGES_PER_PAGE = 20;
17233
17807
  MAX_IMAGES_PER_SITE = 500;
@@ -17338,6 +17912,8 @@ var init_site_extract = __esm({
17338
17912
  const maxPages = Number(job.options.maxPages ?? 1e4);
17339
17913
  const concurrency = Number(job.options.concurrency ?? 1);
17340
17914
  const urlsPerBrowser = Number(job.options.urlsPerBrowser ?? job.options.rotateProxyEvery ?? 10);
17915
+ const captureRenderedDom = job.options.captureRenderedDom === true;
17916
+ const forceBrowserRender = job.options.renderJavaScript === true || job.options.semanticSimilarity === true || captureRenderedDom;
17341
17917
  const batchSize = siteExtractBatchSize(concurrency, urlsPerBrowser);
17342
17918
  const waybackReplay = parseWaybackReplayUrl(job.startUrl);
17343
17919
  const waybackTimeline = job.options.waybackTimeline;
@@ -17420,7 +17996,13 @@ var init_site_extract = __esm({
17420
17996
  if (!batch.length) break;
17421
17997
  const frontierCapacity = Math.max(0, maxPages - seen.size);
17422
17998
  const newLinks = await step.run(`crawl-batch-${i}`, async () => {
17423
- const pages = await runWithCostContext(costContext, () => extractPagesRotating(batch, { kernelApiKey: key, concurrency, urlsPerBrowser }));
17999
+ const pages = await runWithCostContext(costContext, () => extractPagesRotating(batch, {
18000
+ kernelApiKey: key,
18001
+ concurrency,
18002
+ urlsPerBrowser,
18003
+ forceBrowserRender,
18004
+ captureRenderedDom
18005
+ }));
17424
18006
  const storedPages = pages.map((p) => withWaybackProvenance({
17425
18007
  ...p,
17426
18008
  // parsePageData deliberately keeps large bodies non-enumerable so
@@ -17709,9 +18291,9 @@ async function signedDownloadUrl2(pathname, expiresAt) {
17709
18291
  async function createConnectedDataArtifact(args) {
17710
18292
  const createdAt = Date.now();
17711
18293
  const filename2 = `${safeFilename2(args.filename).replace(/\.jsonl$/i, "")}.jsonl`;
17712
- const requestedPathname = `${CONNECTED_DATA_ARTIFACT_PREFIX}${args.ownerId}/${createdAt}-${args.exportId}-${(0, import_node_crypto7.randomUUID)()}.jsonl`;
18294
+ const requestedPathname = `${CONNECTED_DATA_ARTIFACT_PREFIX}${args.ownerId}/${createdAt}-${args.exportId}-${(0, import_node_crypto8.randomUUID)()}.jsonl`;
17713
18295
  const bytes = Buffer.byteLength(args.content);
17714
- const sha2565 = (0, import_node_crypto7.createHash)("sha256").update(args.content).digest("hex");
18296
+ const sha2565 = (0, import_node_crypto8.createHash)("sha256").update(args.content).digest("hex");
17715
18297
  const expiresAt = new Date(createdAt + CONNECTED_DATA_ARTIFACT_TTL_MS);
17716
18298
  const token6 = privateBlobToken();
17717
18299
  let artifactId = requestedPathname;
@@ -17824,11 +18406,11 @@ async function cleanupExpiredConnectedDataArtifacts(args = {}) {
17824
18406
  }
17825
18407
  return { deleted, store: "private-vercel-blob" };
17826
18408
  }
17827
- var import_node_crypto7, import_promises6, import_node_os4, import_node_path4, CONNECTED_DATA_ARTIFACT_PREFIX, CONNECTED_DATA_ARTIFACT_TTL_MS, CONNECTED_DATA_DOWNLOAD_TTL_MS;
18409
+ var import_node_crypto8, import_promises6, import_node_os4, import_node_path4, CONNECTED_DATA_ARTIFACT_PREFIX, CONNECTED_DATA_ARTIFACT_TTL_MS, CONNECTED_DATA_DOWNLOAD_TTL_MS;
17828
18410
  var init_connected_data_artifacts = __esm({
17829
18411
  "src/api/connected-data-artifacts.ts"() {
17830
18412
  "use strict";
17831
- import_node_crypto7 = require("crypto");
18413
+ import_node_crypto8 = require("crypto");
17832
18414
  import_promises6 = require("fs/promises");
17833
18415
  import_node_os4 = require("os");
17834
18416
  import_node_path4 = require("path");
@@ -17927,7 +18509,7 @@ var init_directory_artifacts = __esm({
17927
18509
  // src/mcp/report-artifact-offload.ts
17928
18510
  async function offloadReport(toolName, ownerId2, report) {
17929
18511
  const timestamp2 = Date.now();
17930
- const random = (0, import_node_crypto8.randomBytes)(6).toString("hex");
18512
+ const random = (0, import_node_crypto9.randomBytes)(6).toString("hex");
17931
18513
  const stored = await createPrivateArtifact({
17932
18514
  policy: REPORT_ARTIFACT_POLICY,
17933
18515
  ownerId: ownerId2,
@@ -17988,11 +18570,11 @@ function summaryEnvelope(executiveSummary, offloaded) {
17988
18570
  "Read it with report_artifact_read (supports offset/maxBytes windowing)."
17989
18571
  ].join("\n");
17990
18572
  }
17991
- var import_node_crypto8, REPORT_BLOB_TTL_MS, REPORT_BLOB_PREFIX, PREVIEW_CHARS, REPORT_ARTIFACT_POLICY, ARTIFACT_OFFLOAD_ENABLED;
18573
+ var import_node_crypto9, REPORT_BLOB_TTL_MS, REPORT_BLOB_PREFIX, PREVIEW_CHARS, REPORT_ARTIFACT_POLICY, ARTIFACT_OFFLOAD_ENABLED;
17992
18574
  var init_report_artifact_offload = __esm({
17993
18575
  "src/mcp/report-artifact-offload.ts"() {
17994
18576
  "use strict";
17995
- import_node_crypto8 = require("crypto");
18577
+ import_node_crypto9 = require("crypto");
17996
18578
  init_private_artifacts();
17997
18579
  init_connected_data_artifacts();
17998
18580
  init_directory_artifacts();
@@ -19184,7 +19766,7 @@ function buildSeoExport(siteUrl, pages, branding) {
19184
19766
  branding: branding ?? void 0
19185
19767
  };
19186
19768
  }
19187
- function formatBackgroundJobStarted(toolLabel, data, delivery) {
19769
+ function formatBackgroundJobStarted(toolLabel, url, data, delivery) {
19188
19770
  if (data.status !== "pending" || typeof data.jobId !== "string") return null;
19189
19771
  const jobId2 = data.jobId;
19190
19772
  const full = [
@@ -19199,6 +19781,7 @@ Poll \`check_site_export\` with this jobId. When ready, use \`site_export_read\`
19199
19781
  return {
19200
19782
  content: [{ type: "text", text: full }],
19201
19783
  structuredContent: {
19784
+ url,
19202
19785
  jobId: jobId2,
19203
19786
  status: "pending",
19204
19787
  requestedMaxPages: data.requestedMaxPages,
@@ -19219,7 +19802,8 @@ async function formatExtractSite(raw, input, ctx) {
19219
19802
  const parsed = parseData(raw);
19220
19803
  if ("error" in parsed) return formattedErrorResult(parsed.error);
19221
19804
  const started = formatBackgroundJobStarted(
19222
- "Multi-Page Site Content Crawl",
19805
+ input.toolLabel ?? "Multi-Page Site Content Crawl",
19806
+ input.url,
19223
19807
  parsed.data,
19224
19808
  {
19225
19809
  requested: input.delivery ?? "auto",
@@ -19350,6 +19934,7 @@ async function formatAuditSite(raw, input, ctx) {
19350
19934
  if ("error" in parsed) return formattedErrorResult(parsed.error);
19351
19935
  const started = formatBackgroundJobStarted(
19352
19936
  "Technical SEO Audit",
19937
+ input.url,
19353
19938
  parsed.data,
19354
19939
  {
19355
19940
  requested: input.delivery ?? "auto",
@@ -22884,7 +23469,7 @@ function backupProxyAvailable() {
22884
23469
  }
22885
23470
  function randomStickySessionId() {
22886
23471
  let digits = "";
22887
- for (let i = 0; i < 10; i++) digits += String((0, import_node_crypto9.randomInt)(0, 10));
23472
+ for (let i = 0; i < 10; i++) digits += String((0, import_node_crypto10.randomInt)(0, 10));
22888
23473
  return digits;
22889
23474
  }
22890
23475
  function buildBackupUsername(opts = {}) {
@@ -22942,11 +23527,11 @@ async function cleanupBackupProxyId(kernelApiKey, proxyId) {
22942
23527
  } catch {
22943
23528
  }
22944
23529
  }
22945
- var import_node_crypto9, import_sdk7, BACKUP_PROXY_HOST, BACKUP_PROXY_PORT, BACKUP_STICKY_SESSION_MINUTES, BACKUP_LAST_RESORT_ATTEMPTS;
23530
+ var import_node_crypto10, import_sdk7, BACKUP_PROXY_HOST, BACKUP_PROXY_PORT, BACKUP_STICKY_SESSION_MINUTES, BACKUP_LAST_RESORT_ATTEMPTS;
22946
23531
  var init_backup_proxy = __esm({
22947
23532
  "src/backup-proxy.ts"() {
22948
23533
  "use strict";
22949
- import_node_crypto9 = require("crypto");
23534
+ import_node_crypto10 = require("crypto");
22950
23535
  import_sdk7 = __toESM(require("@onkernel/sdk"), 1);
22951
23536
  BACKUP_PROXY_HOST = "pr.oxylabs.io";
22952
23537
  BACKUP_PROXY_PORT = 7777;
@@ -24351,7 +24936,7 @@ function csvRecords(text2) {
24351
24936
  return record;
24352
24937
  });
24353
24938
  }
24354
- function csvCell(value) {
24939
+ function csvCell2(value) {
24355
24940
  if (value === null || value === void 0) return "";
24356
24941
  const text2 = String(value);
24357
24942
  return /[",\n\r]/.test(text2) ? `"${text2.replace(/"/g, '""')}"` : text2;
@@ -24359,7 +24944,7 @@ function csvCell(value) {
24359
24944
  function rowsToCsv(headers, rows) {
24360
24945
  return [
24361
24946
  headers.join(","),
24362
- ...rows.map((row) => headers.map((header) => csvCell(row[header])).join(","))
24947
+ ...rows.map((row) => headers.map((header) => csvCell2(row[header])).join(","))
24363
24948
  ].join("\n") + "\n";
24364
24949
  }
24365
24950
  var init_csv = __esm({
@@ -24755,12 +25340,12 @@ async function getHostedZipGroups(stateInput) {
24755
25340
  async function importHostedZipGroupsCsv(input) {
24756
25341
  await ensureHostedLocationDataSchema();
24757
25342
  const sourceUrl = normalizedSourceUrl(input.sourceUrl);
24758
- const sha2565 = (0, import_node_crypto10.createHash)("sha256").update(input.csv).digest("hex");
25343
+ const sha2565 = (0, import_node_crypto11.createHash)("sha256").update(input.csv).digest("hex");
24759
25344
  const active = await getActiveHostedLocationDataset();
24760
25345
  if (active?.sha256 === sha2565) return { dataset: active, duplicate: true };
24761
25346
  const parsed = parseHostedZipGroupsCsv(input.csv);
24762
25347
  assertNationwideZipCoverage(parsed);
24763
- const id = `loc_${(0, import_node_crypto10.randomUUID)().replaceAll("-", "")}`;
25348
+ const id = `loc_${(0, import_node_crypto11.randomUUID)().replaceAll("-", "")}`;
24764
25349
  const db = getDb();
24765
25350
  await db.execute({
24766
25351
  sql: `
@@ -24828,12 +25413,12 @@ async function importHostedCensusPlacesCsv(input) {
24828
25413
  if (!state) throw new Error("state must be a two-letter US state abbreviation");
24829
25414
  const kind = censusDatasetKind(state);
24830
25415
  const sourceUrl = normalizedSourceUrl(input.sourceUrl);
24831
- const sha2565 = (0, import_node_crypto10.createHash)("sha256").update(input.csv).digest("hex");
25416
+ const sha2565 = (0, import_node_crypto11.createHash)("sha256").update(input.csv).digest("hex");
24832
25417
  const active = await getActiveHostedDataset(kind);
24833
25418
  if (active?.sha256 === sha2565) return { dataset: active, duplicate: true };
24834
25419
  const stateFips = STATE_FIPS_BY_ABBR[state];
24835
25420
  const parsed = parseHostedCensusPlacesCsv(input.csv, stateFips);
24836
- const id = `loc_${(0, import_node_crypto10.randomUUID)().replaceAll("-", "")}`;
25421
+ const id = `loc_${(0, import_node_crypto11.randomUUID)().replaceAll("-", "")}`;
24837
25422
  const db = getDb();
24838
25423
  await db.execute({
24839
25424
  sql: `
@@ -24956,11 +25541,11 @@ async function queryHostedLocationMarkets(input) {
24956
25541
  warnings
24957
25542
  };
24958
25543
  }
24959
- var import_node_crypto10, HOSTED_ZIP_DATASET_KIND, HOSTED_CENSUS_DATASET_PREFIX, MIN_NATIONWIDE_ZIP_COUNT, REQUIRED_LOCATION_STATE_CODES, STATE_FIPS_BY_ABBR, REQUIRED_LOCATION_STATE_SET, IMPORT_BATCH_SIZE, MAX_SOURCE_URL_LENGTH, schemaDb, schemaPromise;
25544
+ var import_node_crypto11, HOSTED_ZIP_DATASET_KIND, HOSTED_CENSUS_DATASET_PREFIX, MIN_NATIONWIDE_ZIP_COUNT, REQUIRED_LOCATION_STATE_CODES, STATE_FIPS_BY_ABBR, REQUIRED_LOCATION_STATE_SET, IMPORT_BATCH_SIZE, MAX_SOURCE_URL_LENGTH, schemaDb, schemaPromise;
24960
25545
  var init_location_data_repository = __esm({
24961
25546
  "src/api/location-data-repository.ts"() {
24962
25547
  "use strict";
24963
- import_node_crypto10 = require("crypto");
25548
+ import_node_crypto11 = require("crypto");
24964
25549
  init_db();
24965
25550
  init_csv();
24966
25551
  HOSTED_ZIP_DATASET_KIND = "us_zip_groups";
@@ -26274,7 +26859,7 @@ async function claimDirectoryWorkflowOutbox(input) {
26274
26859
  const workerId = requiredBoundedString(input.workerId, "outbox worker id", 160);
26275
26860
  const limit = Math.max(1, Math.min(100, Math.trunc(input.limit ?? 25)));
26276
26861
  const leaseSeconds = Math.max(30, Math.min(3600, Math.trunc(input.leaseSeconds ?? 120)));
26277
- const claimToken = `${workerId}:${(0, import_node_crypto11.randomUUID)()}`;
26862
+ const claimToken = `${workerId}:${(0, import_node_crypto12.randomUUID)()}`;
26278
26863
  const leaseModifier = `+${leaseSeconds} seconds`;
26279
26864
  const results = await getDb().batch([
26280
26865
  {
@@ -26334,11 +26919,11 @@ async function markDirectoryWorkflowOutboxFailed(input) {
26334
26919
  });
26335
26920
  return Number(result.rowsAffected ?? 0) > 0;
26336
26921
  }
26337
- var import_node_crypto11, DIRECTORY_EVENT_NAME, TERMINAL_JOB_STATUSES, DIRECTORY_MAX_DISPATCH_ATTEMPTS, DIRECTORY_START_DEADLINE_MINUTES, DIRECTORY_RUNNING_STALE_MINUTES, schemaDb2, schemaPromise2;
26922
+ var import_node_crypto12, DIRECTORY_EVENT_NAME, TERMINAL_JOB_STATUSES, DIRECTORY_MAX_DISPATCH_ATTEMPTS, DIRECTORY_START_DEADLINE_MINUTES, DIRECTORY_RUNNING_STALE_MINUTES, schemaDb2, schemaPromise2;
26338
26923
  var init_directory_workflow_repository = __esm({
26339
26924
  "src/api/directory-workflow-repository.ts"() {
26340
26925
  "use strict";
26341
- import_node_crypto11 = require("crypto");
26926
+ import_node_crypto12 = require("crypto");
26342
26927
  init_db();
26343
26928
  DIRECTORY_EVENT_NAME = "mcp-scraper/directory.requested";
26344
26929
  TERMINAL_JOB_STATUSES = /* @__PURE__ */ new Set(["succeeded", "partial", "failed"]);
@@ -26452,7 +27037,7 @@ async function acquireConcurrencyGate(user, operation, options = {}) {
26452
27037
  return { ok: true, lockId: null, active: await countActiveUsageForUser(user.id, true), limit, operation, reused: true };
26453
27038
  }
26454
27039
  await expireConcurrencyLocksForUser(user.id);
26455
- const lockId = `cl_${(0, import_node_crypto12.randomUUID)().replace(/-/g, "").slice(0, 24)}`;
27040
+ const lockId = `cl_${(0, import_node_crypto13.randomUUID)().replace(/-/g, "").slice(0, 24)}`;
26456
27041
  const res = await getDb().execute({
26457
27042
  sql: `INSERT INTO concurrency_locks (id, user_id, operation, status, expires_at, metadata)
26458
27043
  SELECT ?, ?, ?, 'active', datetime('now', ?), ?
@@ -26502,11 +27087,11 @@ async function extendConcurrencyGate(lockId, ttlSeconds = DEFAULT_LOCK_TTL_SECON
26502
27087
  args: [lockTtlModifier(ttlSeconds), lockId]
26503
27088
  });
26504
27089
  }
26505
- var import_node_crypto12, DEFAULT_LOCK_TTL_SECONDS, DEFAULT_RETRY_AFTER_SECONDS, MAX_LOCK_TTL_SECONDS;
27090
+ var import_node_crypto13, DEFAULT_LOCK_TTL_SECONDS, DEFAULT_RETRY_AFTER_SECONDS, MAX_LOCK_TTL_SECONDS;
26506
27091
  var init_concurrency_gates = __esm({
26507
27092
  "src/api/concurrency-gates.ts"() {
26508
27093
  "use strict";
26509
- import_node_crypto12 = require("crypto");
27094
+ import_node_crypto13 = require("crypto");
26510
27095
  init_db();
26511
27096
  init_rates();
26512
27097
  DEFAULT_LOCK_TTL_SECONDS = 15 * 60;
@@ -27573,7 +28158,7 @@ var init_PAAExtractor = __esm({
27573
28158
  if (remainingHumanClickDelayMs > 0) await page.waitForTimeout(remainingHumanClickDelayMs);
27574
28159
  return "ok";
27575
28160
  };
27576
- let round = 0;
28161
+ let round2 = 0;
27577
28162
  let growthWaits = 0;
27578
28163
  while (true) {
27579
28164
  if (options.softDeadlineMs && Date.now() >= options.softDeadlineMs) break;
@@ -27601,7 +28186,7 @@ var init_PAAExtractor = __esm({
27601
28186
  continue;
27602
28187
  }
27603
28188
  growthWaits = 0;
27604
- this.reporter.onDepth(++round);
28189
+ this.reporter.onDepth(++round2);
27605
28190
  await this.throwIfCaptcha(page, "Google PAA expansion");
27606
28191
  clickedOnceEver.add(target.q);
27607
28192
  const expansionStatus = await expandOneItemSerially(target.q);
@@ -30138,10 +30723,10 @@ function stableCanonicalJson(value) {
30138
30723
  return JSON.stringify(normalize4(value));
30139
30724
  }
30140
30725
  function leadListEnrichmentFingerprint(input) {
30141
- return (0, import_node_crypto13.createHash)("sha256").update(stableCanonicalJson(input)).digest("hex");
30726
+ return (0, import_node_crypto14.createHash)("sha256").update(stableCanonicalJson(input)).digest("hex");
30142
30727
  }
30143
30728
  function leadListRowInputDigest(row) {
30144
- return (0, import_node_crypto13.createHash)("sha256").update(stableCanonicalJson(row)).digest("hex");
30729
+ return (0, import_node_crypto14.createHash)("sha256").update(stableCanonicalJson(row)).digest("hex");
30145
30730
  }
30146
30731
  function emptyUsage() {
30147
30732
  return { mapsAttempts: 0, pageAttempts: 0, pageSuccesses: 0, serpSearches: 0 };
@@ -30614,7 +31199,7 @@ async function claimLeadListEnrichmentOutbox(input) {
30614
31199
  const workerId = requiredBoundedString2(input.workerId, "outbox worker id", 160);
30615
31200
  const limit = Math.max(1, Math.min(100, Math.trunc(input.limit ?? 25)));
30616
31201
  const leaseSeconds = Math.max(30, Math.min(3600, Math.trunc(input.leaseSeconds ?? 120)));
30617
- const claimToken = `${workerId}:${(0, import_node_crypto13.randomUUID)()}`;
31202
+ const claimToken = `${workerId}:${(0, import_node_crypto14.randomUUID)()}`;
30618
31203
  const results = await getDb().batch([{
30619
31204
  sql: `UPDATE lead_list_enrichment_outbox SET status='dispatching',attempts=attempts+1,locked_by=?,
30620
31205
  locked_until=datetime('now',?),updated_at=datetime('now') WHERE id IN (SELECT id FROM lead_list_enrichment_outbox
@@ -30640,11 +31225,11 @@ async function markLeadListEnrichmentOutboxFailed(input) {
30640
31225
  WHERE id=? AND status='dispatching' AND locked_by=?`, args: [`+${retryAfterSeconds} seconds`, String(input.error).slice(0, 2e3), input.id, input.claimToken] });
30641
31226
  return Number(result.rowsAffected ?? 0) > 0;
30642
31227
  }
30643
- var import_node_crypto13, EVENT_NAME, TERMINAL_JOB_STATUSES2, TERMINAL_ROW_STATUSES, LEAD_LIST_MAX_DISPATCH_ATTEMPTS, LEAD_LIST_START_DEADLINE_MINUTES, LEAD_LIST_RUNNING_STALE_MINUTES, schemaDb3, schemaPromise3;
31228
+ var import_node_crypto14, EVENT_NAME, TERMINAL_JOB_STATUSES2, TERMINAL_ROW_STATUSES, LEAD_LIST_MAX_DISPATCH_ATTEMPTS, LEAD_LIST_START_DEADLINE_MINUTES, LEAD_LIST_RUNNING_STALE_MINUTES, schemaDb3, schemaPromise3;
30644
31229
  var init_lead_list_enrichment_repository = __esm({
30645
31230
  "src/api/lead-list-enrichment-repository.ts"() {
30646
31231
  "use strict";
30647
- import_node_crypto13 = require("crypto");
31232
+ import_node_crypto14 = require("crypto");
30648
31233
  init_db();
30649
31234
  EVENT_NAME = "mcp-scraper/lead-list-enrichment.requested";
30650
31235
  TERMINAL_JOB_STATUSES2 = /* @__PURE__ */ new Set(["complete", "partial", "empty", "failed", "cancelled"]);
@@ -31222,7 +31807,7 @@ var init_paa_harvest_settlement = __esm({
31222
31807
 
31223
31808
  // src/api/serp-identity-db.ts
31224
31809
  async function createSerpIdentityRow(input) {
31225
- const id = `serpi_${(0, import_node_crypto14.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
31810
+ const id = `serpi_${(0, import_node_crypto15.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
31226
31811
  await getDb().execute({
31227
31812
  sql: `INSERT INTO serp_identities
31228
31813
  (id, user_id, name, kernel_profile_name, kernel_proxy_id, proxy_type, country, status)
@@ -31267,11 +31852,11 @@ async function deleteSerpIdentityRow(userId, name) {
31267
31852
  args: [userId, name]
31268
31853
  });
31269
31854
  }
31270
- var import_node_crypto14;
31855
+ var import_node_crypto15;
31271
31856
  var init_serp_identity_db = __esm({
31272
31857
  "src/api/serp-identity-db.ts"() {
31273
31858
  "use strict";
31274
- import_node_crypto14 = require("crypto");
31859
+ import_node_crypto15 = require("crypto");
31275
31860
  init_db();
31276
31861
  }
31277
31862
  });
@@ -32018,7 +32603,7 @@ async function runDurablePaaCapture(input) {
32018
32603
  const { gzipSync: gzipSync2 } = await import("zlib");
32019
32604
  rawDomGzip = gzipSync2(transported);
32020
32605
  }
32021
- rawDomSha256 = (0, import_node_crypto15.createHash)("sha256").update(rawDomGzip).digest("hex");
32606
+ rawDomSha256 = (0, import_node_crypto16.createHash)("sha256").update(rawDomGzip).digest("hex");
32022
32607
  }
32023
32608
  }
32024
32609
  const selected = Array.from(records.values()).slice(0, input.maxQuestions);
@@ -32102,12 +32687,12 @@ async function runDurablePaaCapture(input) {
32102
32687
  }
32103
32688
  throw new Error("paa_work_deadline_exhausted_before_capture");
32104
32689
  }
32105
- var import_sdk9, import_node_crypto15, PAA_INVOCATION_BUDGET_MS, PAA_BROWSER_WORK_BUDGET_MS, PAA_SOLVER_BUDGET_MS, RAW_PREPARE_CODE;
32690
+ var import_sdk9, import_node_crypto16, PAA_INVOCATION_BUDGET_MS, PAA_BROWSER_WORK_BUDGET_MS, PAA_SOLVER_BUDGET_MS, RAW_PREPARE_CODE;
32106
32691
  var init_durable_capture = __esm({
32107
32692
  "src/paa/durable-capture.ts"() {
32108
32693
  "use strict";
32109
32694
  import_sdk9 = __toESM(require("@onkernel/sdk"), 1);
32110
- import_node_crypto15 = require("crypto");
32695
+ import_node_crypto16 = require("crypto");
32111
32696
  init_selectors();
32112
32697
  init_uule();
32113
32698
  PAA_INVOCATION_BUDGET_MS = 28e4;
@@ -32535,7 +33120,7 @@ function mapSubmission(row) {
32535
33120
  async function event(submissionId, eventType, actorKind, actorId, metadata = {}) {
32536
33121
  await getDb().execute({
32537
33122
  sql: `INSERT INTO local_sourcebook_events (id, submission_id, event_type, actor_kind, actor_id, metadata_json) VALUES (?, ?, ?, ?, ?, ?)`,
32538
- args: [(0, import_node_crypto16.randomUUID)(), submissionId, eventType, actorKind, actorId, JSON.stringify(metadata)]
33123
+ args: [(0, import_node_crypto17.randomUUID)(), submissionId, eventType, actorKind, actorId, JSON.stringify(metadata)]
32539
33124
  });
32540
33125
  }
32541
33126
  async function createLocalSourcebookSubmission(input) {
@@ -32549,7 +33134,7 @@ async function createLocalSourcebookSubmission(input) {
32549
33134
  return submission;
32550
33135
  }
32551
33136
  }
32552
- const id = `lsb_${(0, import_node_crypto16.randomUUID)().replace(/-/g, "")}`;
33137
+ const id = `lsb_${(0, import_node_crypto17.randomUUID)().replace(/-/g, "")}`;
32553
33138
  const coverage = {
32554
33139
  requested: ["website_crawl", "structured_data", "services_products", "service_areas", "genuine_images", "staff_team", "review_sources"],
32555
33140
  crawl: { state: "queued", pagesDiscovered: 0, pagesCaptured: 0 },
@@ -32690,11 +33275,11 @@ async function getPublicLocalSourcebook(category, state, slug4) {
32690
33275
  const result = await getDb().execute({ sql: `SELECT r.payload_json FROM local_sourcebook_submissions s JOIN local_sourcebook_revisions r ON r.submission_id = s.id AND r.revision = s.published_revision WHERE s.category = ? AND s.state = ? AND s.slug = ? AND s.published_revision IS NOT NULL AND s.status NOT IN ('unpublished', 'rejected') LIMIT 1`, args: [category, state, slug4] });
32691
33276
  return result.rows[0] ? parseJson3(result.rows[0].payload_json) : null;
32692
33277
  }
32693
- var import_node_crypto16, LOCAL_SOURCEBOOK_CATEGORIES, schemaPromise4, schemaDb4;
33278
+ var import_node_crypto17, LOCAL_SOURCEBOOK_CATEGORIES, schemaPromise4, schemaDb4;
32694
33279
  var init_local_sourcebook_repository = __esm({
32695
33280
  "src/api/local-sourcebook-repository.ts"() {
32696
33281
  "use strict";
32697
- import_node_crypto16 = require("crypto");
33282
+ import_node_crypto17 = require("crypto");
32698
33283
  init_db();
32699
33284
  LOCAL_SOURCEBOOK_CATEGORIES = ["home", "professional", "restaurants", "financial", "realestate", "auto", "wellness"];
32700
33285
  schemaPromise4 = null;
@@ -33846,7 +34431,7 @@ var init_local_sourcebook_schema = __esm({
33846
34431
 
33847
34432
  // src/api/local-sourcebook-compiler.ts
33848
34433
  function digest(value) {
33849
- return (0, import_node_crypto17.createHash)("sha256").update(value).digest("hex").slice(0, 20);
34434
+ return (0, import_node_crypto18.createHash)("sha256").update(value).digest("hex").slice(0, 20);
33850
34435
  }
33851
34436
  function unique(values, limit = 100) {
33852
34437
  const seen = /* @__PURE__ */ new Set();
@@ -34283,11 +34868,11 @@ function compileLocalSourcebookListing(input) {
34283
34868
  }
34284
34869
  };
34285
34870
  }
34286
- var import_node_crypto17, CATEGORY_LABELS, STATE_NAMES, GENERIC_HEADINGS, SERVICE_PATH, PRODUCT_PATH, TEAM_PATH, WORK_PATH, CREDENTIAL_PATH, BLOG_PATH, LOCATION_PATH, REVIEW_THEME_RULES;
34871
+ var import_node_crypto18, CATEGORY_LABELS, STATE_NAMES, GENERIC_HEADINGS, SERVICE_PATH, PRODUCT_PATH, TEAM_PATH, WORK_PATH, CREDENTIAL_PATH, BLOG_PATH, LOCATION_PATH, REVIEW_THEME_RULES;
34287
34872
  var init_local_sourcebook_compiler = __esm({
34288
34873
  "src/api/local-sourcebook-compiler.ts"() {
34289
34874
  "use strict";
34290
- import_node_crypto17 = require("crypto");
34875
+ import_node_crypto18 = require("crypto");
34291
34876
  init_local_sourcebook_public_urls();
34292
34877
  init_local_sourcebook_schema();
34293
34878
  CATEGORY_LABELS = {
@@ -34644,6 +35229,11 @@ async function prepareSiteExtractStart(input) {
34644
35229
  urlsPerBrowser: input.urlsPerBrowser,
34645
35230
  formats: input.formats,
34646
35231
  downloadImages: input.downloadImages,
35232
+ renderJavaScript: input.renderJavaScript === true,
35233
+ captureRenderedDom: input.captureRenderedDom === true,
35234
+ semanticSimilarity: input.semanticSimilarity === true,
35235
+ similarityThreshold: input.similarityThreshold,
35236
+ similarityMaxPairs: input.similarityMaxPairs,
34647
35237
  ...input.waybackReplay ? {
34648
35238
  waybackReplay: input.waybackReplay,
34649
35239
  disableLinkDiscovery: true
@@ -35388,15 +35978,15 @@ function normalizeIdempotencyKey(value) {
35388
35978
  return key;
35389
35979
  }
35390
35980
  function tokenFor(id, idempotencyKey4) {
35391
- return (0, import_node_crypto18.createHmac)("sha256", billingSecret()).update(id).update(":").update(idempotencyKey4).digest("base64url");
35981
+ return (0, import_node_crypto19.createHmac)("sha256", billingSecret()).update(id).update(":").update(idempotencyKey4).digest("base64url");
35392
35982
  }
35393
35983
  function tokenHash(token6) {
35394
- return (0, import_node_crypto18.createHash)("sha256").update(token6).digest("hex");
35984
+ return (0, import_node_crypto19.createHash)("sha256").update(token6).digest("hex");
35395
35985
  }
35396
35986
  function verifyToken(row, token6) {
35397
35987
  const expected = Buffer.from(row.token_hash, "hex");
35398
35988
  const actual = Buffer.from(tokenHash(token6), "hex");
35399
- if (expected.length !== actual.length || !(0, import_node_crypto18.timingSafeEqual)(expected, actual)) {
35989
+ if (expected.length !== actual.length || !(0, import_node_crypto19.timingSafeEqual)(expected, actual)) {
35400
35990
  throw new UnifiedBillingError("unauthorized", "invalid billing authorization token", 401);
35401
35991
  }
35402
35992
  }
@@ -35511,7 +36101,7 @@ async function authorizeScheduledRun(args) {
35511
36101
  { balanceMc, requiredMc: SCHEDULED_RUN_BASE_MC }
35512
36102
  );
35513
36103
  }
35514
- const id = (0, import_node_crypto18.randomUUID)();
36104
+ const id = (0, import_node_crypto19.randomUUID)();
35515
36105
  const token6 = tokenFor(id, idempotencyKey4);
35516
36106
  const expiresAt = new Date(Date.now() + AUTHORIZATION_TTL_MS).toISOString();
35517
36107
  const inserted = await getDb().execute({
@@ -35579,7 +36169,7 @@ async function startScheduledRun(args) {
35579
36169
  if (!authorization || !["starting", "started"].includes(authorization.status)) {
35580
36170
  throw new UnifiedBillingError("authorization_closed", "billing authorization is no longer startable", 409);
35581
36171
  }
35582
- const eventId = existing?.id ?? (0, import_node_crypto18.randomUUID)();
36172
+ const eventId = existing?.id ?? (0, import_node_crypto19.randomUUID)();
35583
36173
  if (!existing) {
35584
36174
  await getDb().execute({
35585
36175
  sql: "INSERT OR IGNORE INTO billing_events (id, user_id, authorization_id, idempotency_key, billing_class, source_surface, status, amount_mc, multiplier_bps, metadata) VALUES (?, ?, ?, ?, ?, ?, 'capturing', ?, ?, ?)",
@@ -35718,7 +36308,7 @@ async function settleScheduledRun(args) {
35718
36308
  ...modelCostUnreported ? { modelCostUnreported: true } : {},
35719
36309
  ...pendingReason ? { reason: pendingReason } : {}
35720
36310
  });
35721
- const eventId = existing?.id ?? (0, import_node_crypto18.randomUUID)();
36311
+ const eventId = existing?.id ?? (0, import_node_crypto19.randomUUID)();
35722
36312
  const status = pendingReason ? "cost_pending" : "settling";
35723
36313
  const eventValues = [
35724
36314
  modelMc,
@@ -35843,11 +36433,11 @@ async function voidScheduledRunAuthorization(args) {
35843
36433
  }
35844
36434
  return { ok: true, status: authorization.status };
35845
36435
  }
35846
- var import_node_crypto18, SCHEDULED_RUN_BILLING_CLASS, SCHEDULED_RUN_SOURCE_SURFACE, AUTHORIZATION_TTL_MS, UnifiedBillingError;
36436
+ var import_node_crypto19, SCHEDULED_RUN_BILLING_CLASS, SCHEDULED_RUN_SOURCE_SURFACE, AUTHORIZATION_TTL_MS, UnifiedBillingError;
35847
36437
  var init_unified_billing = __esm({
35848
36438
  "src/api/unified-billing.ts"() {
35849
36439
  "use strict";
35850
- import_node_crypto18 = require("crypto");
36440
+ import_node_crypto19 = require("crypto");
35851
36441
  init_db();
35852
36442
  init_rates();
35853
36443
  init_scheduling_access();
@@ -35879,14 +36469,14 @@ function getSessionSecret() {
35879
36469
  function safeEqualHex(a, b) {
35880
36470
  if (a.length !== b.length) return false;
35881
36471
  try {
35882
- return (0, import_node_crypto19.timingSafeEqual)(Buffer.from(a, "hex"), Buffer.from(b, "hex"));
36472
+ return (0, import_node_crypto20.timingSafeEqual)(Buffer.from(a, "hex"), Buffer.from(b, "hex"));
35883
36473
  } catch {
35884
36474
  return false;
35885
36475
  }
35886
36476
  }
35887
36477
  function signSession(userId) {
35888
36478
  const payload = String(userId);
35889
- const sig = (0, import_node_crypto19.createHmac)("sha256", secret()).update(payload).digest("hex");
36479
+ const sig = (0, import_node_crypto20.createHmac)("sha256", secret()).update(payload).digest("hex");
35890
36480
  return `${payload}.${sig}`;
35891
36481
  }
35892
36482
  function verifySession(token6) {
@@ -35894,16 +36484,16 @@ function verifySession(token6) {
35894
36484
  if (dot === -1) return null;
35895
36485
  const payload = token6.slice(0, dot);
35896
36486
  const sig = token6.slice(dot + 1);
35897
- const expected = (0, import_node_crypto19.createHmac)("sha256", secret()).update(payload).digest("hex");
36487
+ const expected = (0, import_node_crypto20.createHmac)("sha256", secret()).update(payload).digest("hex");
35898
36488
  if (!safeEqualHex(sig, expected)) return null;
35899
36489
  const id = parseInt(payload);
35900
36490
  return isNaN(id) ? null : id;
35901
36491
  }
35902
- var import_node_crypto19, isProduction, secret;
36492
+ var import_node_crypto20, isProduction, secret;
35903
36493
  var init_session = __esm({
35904
36494
  "src/api/session.ts"() {
35905
36495
  "use strict";
35906
- import_node_crypto19 = require("crypto");
36496
+ import_node_crypto20 = require("crypto");
35907
36497
  isProduction = () => process.env.NODE_ENV === "production" || process.env.VERCEL === "1";
35908
36498
  secret = () => getSessionSecret();
35909
36499
  }
@@ -35921,11 +36511,11 @@ function isMemoryOperator(email) {
35921
36511
  return ops.includes(email.trim().toLowerCase());
35922
36512
  }
35923
36513
  function encKey() {
35924
- return (0, import_node_crypto20.scryptSync)(getSessionSecret(), "mcp-memory-key-v1", 32);
36514
+ return (0, import_node_crypto21.scryptSync)(getSessionSecret(), "mcp-memory-key-v1", 32);
35925
36515
  }
35926
36516
  function encryptMemoryKey(secret2) {
35927
- const iv = (0, import_node_crypto20.randomBytes)(12);
35928
- const cipher = (0, import_node_crypto20.createCipheriv)("aes-256-gcm", encKey(), iv);
36517
+ const iv = (0, import_node_crypto21.randomBytes)(12);
36518
+ const cipher = (0, import_node_crypto21.createCipheriv)("aes-256-gcm", encKey(), iv);
35929
36519
  const enc = Buffer.concat([cipher.update(secret2, "utf8"), cipher.final()]);
35930
36520
  const tag = cipher.getAuthTag();
35931
36521
  return `${iv.toString("base64")}:${tag.toString("base64")}:${enc.toString("base64")}`;
@@ -35933,7 +36523,7 @@ function encryptMemoryKey(secret2) {
35933
36523
  function decryptMemoryKey(stored) {
35934
36524
  try {
35935
36525
  const [ivB, tagB, dataB] = stored.split(":");
35936
- const decipher = (0, import_node_crypto20.createDecipheriv)("aes-256-gcm", encKey(), Buffer.from(ivB, "base64"));
36526
+ const decipher = (0, import_node_crypto21.createDecipheriv)("aes-256-gcm", encKey(), Buffer.from(ivB, "base64"));
35937
36527
  decipher.setAuthTag(Buffer.from(tagB, "base64"));
35938
36528
  return Buffer.concat([decipher.update(Buffer.from(dataB, "base64")), decipher.final()]).toString("utf8");
35939
36529
  } catch {
@@ -36047,11 +36637,11 @@ async function syncScheduledActionCredentials(user) {
36047
36637
  }, {});
36048
36638
  return { ok: res.ok, error: res.error };
36049
36639
  }
36050
- var import_node_crypto20, import_provision_defaults, import_set_schedule_entitlement, MEMORY_BASE_URL, ADMIN_KEY;
36640
+ var import_node_crypto21, import_provision_defaults, import_set_schedule_entitlement, MEMORY_BASE_URL, ADMIN_KEY;
36051
36641
  var init_memory = __esm({
36052
36642
  "src/api/memory.ts"() {
36053
36643
  "use strict";
36054
- import_node_crypto20 = require("crypto");
36644
+ import_node_crypto21 = require("crypto");
36055
36645
  init_session();
36056
36646
  init_db();
36057
36647
  init_rates();
@@ -36101,7 +36691,7 @@ var init_connected_cost_telemetry = __esm({
36101
36691
 
36102
36692
  // src/api/connected-usage-billing.ts
36103
36693
  function hash(value) {
36104
- return (0, import_node_crypto21.createHash)("sha256").update(value).digest("hex");
36694
+ return (0, import_node_crypto22.createHash)("sha256").update(value).digest("hex");
36105
36695
  }
36106
36696
  function eventKey(idempotencyKey4) {
36107
36697
  return `connected-usage:${hash(idempotencyKey4)}`;
@@ -36373,7 +36963,7 @@ async function settleConnectedUsage(rawInput) {
36373
36963
  sql: `INSERT OR IGNORE INTO billing_events
36374
36964
  (id, user_id, idempotency_key, billing_class, source_surface, status, amount_mc, metadata)
36375
36965
  VALUES (?, ?, ?, ?, ?, 'settling', ?, ?)`,
36376
- args: [(0, import_node_crypto21.randomUUID)(), user.id, key, CONNECTED_USAGE_BILLING_CLASS, input.sourceSurface, charge2.amountMc, encodeMetadata(storedMetadata)]
36966
+ args: [(0, import_node_crypto22.randomUUID)(), user.id, key, CONNECTED_USAGE_BILLING_CLASS, input.sourceSurface, charge2.amountMc, encodeMetadata(storedMetadata)]
36377
36967
  });
36378
36968
  let event2 = await readEvent(key);
36379
36969
  if (!event2) throw new Error("connected usage receipt insert completed without a readable event");
@@ -36462,11 +37052,11 @@ async function listConnectedUsageHistory(userId, limit = 100) {
36462
37052
  }
36463
37053
  return history;
36464
37054
  }
36465
- var import_node_crypto21, import_zod17, CONNECTED_USAGE_BILLING_CLASS, CONNECTED_USAGE_DEFAULT_SOURCE_SURFACE, ConnectedUsageSafeMetadataSchema, ConnectedUsageSettlementInputSchema, ConnectedUsagePreflightInputSchema, ConnectedUsageBillingError;
37055
+ var import_node_crypto22, import_zod17, CONNECTED_USAGE_BILLING_CLASS, CONNECTED_USAGE_DEFAULT_SOURCE_SURFACE, ConnectedUsageSafeMetadataSchema, ConnectedUsageSettlementInputSchema, ConnectedUsagePreflightInputSchema, ConnectedUsageBillingError;
36466
37056
  var init_connected_usage_billing = __esm({
36467
37057
  "src/api/connected-usage-billing.ts"() {
36468
37058
  "use strict";
36469
- import_node_crypto21 = require("crypto");
37059
+ import_node_crypto22 = require("crypto");
36470
37060
  import_zod17 = require("zod");
36471
37061
  init_connected_cost_telemetry();
36472
37062
  init_db();
@@ -38115,7 +38705,27 @@ var init_server_schemas = __esm({
38115
38705
  downloadImages: import_zod22.z.boolean().optional(),
38116
38706
  preserveMedia: import_zod22.z.boolean().optional(),
38117
38707
  delivery: import_zod22.z.enum(["auto", "artifact"]).optional(),
38118
- formats: import_zod22.z.array(import_zod22.z.enum(["markdown", "html", "links", "json", "images", "branding", "issues"])).optional()
38708
+ formats: import_zod22.z.array(import_zod22.z.enum(["markdown", "html", "links", "json", "images", "branding", "issues"])).optional(),
38709
+ renderJavaScript: import_zod22.z.boolean().optional(),
38710
+ captureRenderedDom: import_zod22.z.boolean().optional(),
38711
+ semanticSimilarity: import_zod22.z.boolean().optional(),
38712
+ similarityThreshold: import_zod22.z.number().min(0).max(1).optional(),
38713
+ similarityMaxPairs: import_zod22.z.number().int().min(1).max(5e4).optional()
38714
+ }).superRefine((value, ctx) => {
38715
+ if (value.semanticSimilarity && (value.maxPages ?? 100) > 500) {
38716
+ ctx.addIssue({
38717
+ code: import_zod22.z.ZodIssueCode.custom,
38718
+ path: ["maxPages"],
38719
+ message: "semanticSimilarity supports at most 500 pages per analysis."
38720
+ });
38721
+ }
38722
+ if (value.semanticSimilarity && value.wayback) {
38723
+ ctx.addIssue({
38724
+ code: import_zod22.z.ZodIssueCode.custom,
38725
+ path: ["wayback"],
38726
+ message: "semanticSimilarity analyzes one live rendered corpus and cannot be combined with a Wayback timeline."
38727
+ });
38728
+ }
38119
38729
  });
38120
38730
  SiteExportReadBodySchema = import_zod22.z.object({
38121
38731
  jobId: import_zod22.z.string().trim().min(1),
@@ -40415,7 +41025,7 @@ async function applyAdjustment(input) {
40415
41025
  const credits = validateCredits(input.credits, input.confirmLarge === true);
40416
41026
  const reason = validateReason(input.reason);
40417
41027
  const actor = validateActor(input.actor);
40418
- const reference = input.reference?.trim() || `adj_${(0, import_node_crypto22.randomUUID)()}`;
41028
+ const reference = input.reference?.trim() || `adj_${(0, import_node_crypto23.randomUUID)()}`;
40419
41029
  if (reference.length > 120) throw new AdminCreditError(400, "reference must be at most 120 characters");
40420
41030
  const db = getDb();
40421
41031
  const existing = await db.execute({
@@ -40560,11 +41170,11 @@ async function lookupAccount(target, limit = 20) {
40560
41170
  }))
40561
41171
  };
40562
41172
  }
40563
- var import_node_crypto22, MIN_ADJUSTMENT_CREDITS, LARGE_ADJUSTMENT_CREDITS, MAX_ADJUSTMENT_CREDITS, MIN_REASON_LENGTH, MAX_REASON_LENGTH, MAX_ACTOR_LENGTH, AdminCreditError;
41173
+ var import_node_crypto23, MIN_ADJUSTMENT_CREDITS, LARGE_ADJUSTMENT_CREDITS, MAX_ADJUSTMENT_CREDITS, MIN_REASON_LENGTH, MAX_REASON_LENGTH, MAX_ACTOR_LENGTH, AdminCreditError;
40564
41174
  var init_admin_credits = __esm({
40565
41175
  "src/api/admin-credits.ts"() {
40566
41176
  "use strict";
40567
- import_node_crypto22 = require("crypto");
41177
+ import_node_crypto23 = require("crypto");
40568
41178
  init_db();
40569
41179
  init_rates();
40570
41180
  MIN_ADJUSTMENT_CREDITS = 1;
@@ -42529,7 +43139,7 @@ async function packageMapsMedia(args) {
42529
43139
  files.push({ path: "summary.json", content: Buffer.from(JSON.stringify(summary, null, 2)) });
42530
43140
  files.push({ path: "images.jsonl", content: Buffer.from(cleanImages.map((image) => JSON.stringify(image)).join("\n") + "\n") });
42531
43141
  const archive = await zipBuffer(files);
42532
- const id = (0, import_node_crypto23.randomBytes)(6).toString("hex");
43142
+ const id = (0, import_node_crypto24.randomBytes)(6).toString("hex");
42533
43143
  const pointer = await createPrivateArtifact({
42534
43144
  policy: policy3(),
42535
43145
  ownerId: args.ownerId,
@@ -42542,13 +43152,13 @@ async function packageMapsMedia(args) {
42542
43152
  const localPath = token() ? null : (0, import_node_path13.join)(process.env.MCP_SCRAPER_OUTPUT_DIR?.trim() || (0, import_node_path13.join)((0, import_node_os9.homedir)(), "Downloads", "mcp-scraper"), "blobs", pointer.artifactId);
42543
43153
  return { media: args.media, artifact: { ...pointer, localPath } };
42544
43154
  }
42545
- var import_node_os9, import_node_path13, import_node_crypto23, import_yazl2, import_p_limit5, MAPS_MEDIA_ARTIFACT_PREFIX, MAPS_MEDIA_ARTIFACT_TTL_MS, MAPS_MEDIA_DOWNLOAD_TTL_MS, MAX_IMAGE_BYTES2, MAX_ARCHIVE_IMAGE_BYTES, MAX_INLINE_IMAGE_BYTES, MAX_INLINE_TOTAL_BYTES, DOWNLOAD_CONCURRENCY, MAX_REDIRECTS;
43155
+ var import_node_os9, import_node_path13, import_node_crypto24, import_yazl2, import_p_limit5, MAPS_MEDIA_ARTIFACT_PREFIX, MAPS_MEDIA_ARTIFACT_TTL_MS, MAPS_MEDIA_DOWNLOAD_TTL_MS, MAX_IMAGE_BYTES2, MAX_ARCHIVE_IMAGE_BYTES, MAX_INLINE_IMAGE_BYTES, MAX_INLINE_TOTAL_BYTES, DOWNLOAD_CONCURRENCY, MAX_REDIRECTS;
42546
43156
  var init_maps_media_artifacts = __esm({
42547
43157
  "src/api/maps-media-artifacts.ts"() {
42548
43158
  "use strict";
42549
43159
  import_node_os9 = require("os");
42550
43160
  import_node_path13 = require("path");
42551
- import_node_crypto23 = require("crypto");
43161
+ import_node_crypto24 = require("crypto");
42552
43162
  import_yazl2 = require("yazl");
42553
43163
  import_p_limit5 = __toESM(require("p-limit"), 1);
42554
43164
  init_private_artifacts();
@@ -43172,7 +43782,7 @@ function retryDelaySeconds(attempts) {
43172
43782
  }
43173
43783
  async function dispatchPendingDirectoryWorkflows(limit = 25) {
43174
43784
  const rows = await claimDirectoryWorkflowOutbox({
43175
- workerId: `directory-dispatch-${process.pid}-${(0, import_node_crypto24.randomUUID)().slice(0, 8)}`,
43785
+ workerId: `directory-dispatch-${process.pid}-${(0, import_node_crypto25.randomUUID)().slice(0, 8)}`,
43176
43786
  limit
43177
43787
  });
43178
43788
  const result = { claimed: rows.length, dispatched: 0, failed: 0 };
@@ -43194,11 +43804,11 @@ async function dispatchPendingDirectoryWorkflows(limit = 25) {
43194
43804
  }
43195
43805
  return result;
43196
43806
  }
43197
- var import_node_crypto24;
43807
+ var import_node_crypto25;
43198
43808
  var init_directory_workflow_dispatch = __esm({
43199
43809
  "src/api/directory-workflow-dispatch.ts"() {
43200
43810
  "use strict";
43201
- import_node_crypto24 = require("crypto");
43811
+ import_node_crypto25 = require("crypto");
43202
43812
  init_client();
43203
43813
  init_directory_workflow_repository();
43204
43814
  }
@@ -43210,7 +43820,7 @@ function safeOptions(options) {
43210
43820
  return safe2;
43211
43821
  }
43212
43822
  function requestFingerprint(options) {
43213
- return (0, import_node_crypto25.createHash)("sha256").update(JSON.stringify(safeOptions(options))).digest("hex");
43823
+ return (0, import_node_crypto26.createHash)("sha256").update(JSON.stringify(safeOptions(options))).digest("hex");
43214
43824
  }
43215
43825
  function idempotencyKey(raw) {
43216
43826
  if (raw !== void 0) {
@@ -43219,14 +43829,14 @@ function idempotencyKey(raw) {
43219
43829
  if (trimmed.length > 500) return { ok: false, message: "Idempotency-Key must be 500 characters or fewer." };
43220
43830
  return { ok: true, key: trimmed };
43221
43831
  }
43222
- return { ok: true, key: `directory-${(0, import_node_crypto25.randomUUID)()}` };
43832
+ return { ok: true, key: `directory-${(0, import_node_crypto26.randomUUID)()}` };
43223
43833
  }
43224
43834
  function debitKeyFor(userId, responseKey) {
43225
- const digest2 = (0, import_node_crypto25.createHash)("sha256").update(String(userId)).update("\0").update(responseKey).digest("hex");
43835
+ const digest2 = (0, import_node_crypto26.createHash)("sha256").update(String(userId)).update("\0").update(responseKey).digest("hex");
43226
43836
  return `directory-workflow:${userId}:${digest2}`;
43227
43837
  }
43228
43838
  function jobId() {
43229
- return `dir_${(0, import_node_crypto25.randomUUID)().replace(/-/g, "")}`;
43839
+ return `dir_${(0, import_node_crypto26.randomUUID)().replace(/-/g, "")}`;
43230
43840
  }
43231
43841
  function publicStatus(job, result) {
43232
43842
  if (job.status === "failed") return "failed";
@@ -43335,7 +43945,7 @@ async function runSynchronously(c, user, options, plan, responseKey) {
43335
43945
  const csv = renderDirectoryWorkflowCsv(result);
43336
43946
  const artifact = await createDirectoryCsvArtifact({
43337
43947
  ownerId: String(user.id),
43338
- jobId: (0, import_node_crypto25.createHash)("sha256").update(debitKey2).digest("hex").slice(0, 32),
43948
+ jobId: (0, import_node_crypto26.createHash)("sha256").update(debitKey2).digest("hex").slice(0, 32),
43339
43949
  createdAt: result.extractedAt,
43340
43950
  filename: `${options.state}-${options.query}-directory.csv`,
43341
43951
  csv,
@@ -43391,11 +44001,11 @@ async function runSynchronously(c, user, options, plan, responseKey) {
43391
44001
  await releaseConcurrencyGate(gate.lockId);
43392
44002
  }
43393
44003
  }
43394
- var import_node_crypto25, import_hono16, directoryApp;
44004
+ var import_node_crypto26, import_hono16, directoryApp;
43395
44005
  var init_directory_routes = __esm({
43396
44006
  "src/api/directory-routes.ts"() {
43397
44007
  "use strict";
43398
- import_node_crypto25 = require("crypto");
44008
+ import_node_crypto26 = require("crypto");
43399
44009
  import_hono16 = require("hono");
43400
44010
  init_api_auth();
43401
44011
  init_db();
@@ -43613,10 +44223,10 @@ function bounded(value, field, min, max) {
43613
44223
  return normalized;
43614
44224
  }
43615
44225
  function newLeadListUploadId() {
43616
- return `upl_${(0, import_node_crypto26.randomUUID)().replace(/-/g, "")}`;
44226
+ return `upl_${(0, import_node_crypto27.randomUUID)().replace(/-/g, "")}`;
43617
44227
  }
43618
44228
  function newImportedLeadListId() {
43619
- return `lst_${(0, import_node_crypto26.randomUUID)().replace(/-/g, "")}`;
44229
+ return `lst_${(0, import_node_crypto27.randomUUID)().replace(/-/g, "")}`;
43620
44230
  }
43621
44231
  async function ensureLeadListImportRepositorySchema() {
43622
44232
  const db = getDb();
@@ -43816,11 +44426,11 @@ async function deleteExpiredLeadListInputRecords(now = /* @__PURE__ */ new Date(
43816
44426
  ], "write");
43817
44427
  return { uploads: Number(uploads.rowsAffected ?? 0), lists: Number(lists.rowsAffected ?? 0) };
43818
44428
  }
43819
- var import_node_crypto26, schemaDb5, schemaPromise5;
44429
+ var import_node_crypto27, schemaDb5, schemaPromise5;
43820
44430
  var init_lead_list_import_repository = __esm({
43821
44431
  "src/api/lead-list-import-repository.ts"() {
43822
44432
  "use strict";
43823
- import_node_crypto26 = require("crypto");
44433
+ import_node_crypto27 = require("crypto");
43824
44434
  init_db();
43825
44435
  schemaDb5 = null;
43826
44436
  schemaPromise5 = null;
@@ -43862,7 +44472,7 @@ function safeFilenameHint(value) {
43862
44472
  return cleaned || null;
43863
44473
  }
43864
44474
  function requestFingerprint2(filenameHint) {
43865
- return (0, import_node_crypto27.createHash)("sha256").update(JSON.stringify({ filenameHint })).digest("hex");
44475
+ return (0, import_node_crypto28.createHash)("sha256").update(JSON.stringify({ filenameHint })).digest("hex");
43866
44476
  }
43867
44477
  function publicBaseUrl() {
43868
44478
  const configured = process.env.MCP_SCRAPER_PUBLIC_BASE_URL?.trim() || process.env.PUBLIC_BASE_URL?.trim() || (process.env.VERCEL_PROJECT_PRODUCTION_URL ? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}` : "") || "https://mcpscraper.dev";
@@ -44018,7 +44628,7 @@ async function inspectLeadListUpload(uploadId, ownerId2) {
44018
44628
  maxBytes: LEAD_LIST_UPLOAD_MAX_BYTES
44019
44629
  });
44020
44630
  if (!buffer) return null;
44021
- const sha2565 = (0, import_node_crypto27.createHash)("sha256").update(buffer).digest("hex");
44631
+ const sha2565 = (0, import_node_crypto28.createHash)("sha256").update(buffer).digest("hex");
44022
44632
  const completed = await completeLeadListUpload({
44023
44633
  id: record.id,
44024
44634
  ownerId: record.ownerId,
@@ -44142,11 +44752,11 @@ async function cleanupExpiredLeadListInputs(args = {}) {
44142
44752
  const records = await deleteExpiredLeadListInputRecords(now);
44143
44753
  return { deletedBlobs, deletedUploadRecords: records.uploads, deletedListRecords: records.lists };
44144
44754
  }
44145
- var import_node_crypto27, import_promises11, import_node_os10, import_node_path14, LEAD_LIST_UPLOAD_PREFIX, LEAD_LIST_NORMALIZED_PREFIX, LEAD_LIST_UPLOAD_MAX_BYTES, LEAD_LIST_UPLOAD_URL_TTL_MS, LEAD_LIST_UPLOAD_SOURCE_TTL_MS, LEAD_LIST_NORMALIZED_TTL_MS, LEAD_LIST_DOWNLOAD_TTL_MS, LEAD_LIST_UPLOAD_MIME_TYPES;
44755
+ var import_node_crypto28, import_promises11, import_node_os10, import_node_path14, LEAD_LIST_UPLOAD_PREFIX, LEAD_LIST_NORMALIZED_PREFIX, LEAD_LIST_UPLOAD_MAX_BYTES, LEAD_LIST_UPLOAD_URL_TTL_MS, LEAD_LIST_UPLOAD_SOURCE_TTL_MS, LEAD_LIST_NORMALIZED_TTL_MS, LEAD_LIST_DOWNLOAD_TTL_MS, LEAD_LIST_UPLOAD_MIME_TYPES;
44146
44756
  var init_lead_list_input_artifacts = __esm({
44147
44757
  "src/api/lead-list-input-artifacts.ts"() {
44148
44758
  "use strict";
44149
- import_node_crypto27 = require("crypto");
44759
+ import_node_crypto28 = require("crypto");
44150
44760
  import_promises11 = require("fs/promises");
44151
44761
  import_node_os10 = require("os");
44152
44762
  import_node_path14 = require("path");
@@ -44529,7 +45139,7 @@ function canonicalize(value) {
44529
45139
  return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${canonicalize(object[key])}`).join(",")}}`;
44530
45140
  }
44531
45141
  function fingerprint(value) {
44532
- return (0, import_node_crypto28.createHash)("sha256").update(canonicalize(value)).digest("hex");
45142
+ return (0, import_node_crypto29.createHash)("sha256").update(canonicalize(value)).digest("hex");
44533
45143
  }
44534
45144
  async function safeJson(c) {
44535
45145
  try {
@@ -44606,11 +45216,11 @@ function textDelimiter(raw, text2, observedMime) {
44606
45216
  if (observedMime === "text/tab-separated-values") return " ";
44607
45217
  return detectLeadListTextDelimiter(text2);
44608
45218
  }
44609
- var import_node_crypto28, import_hono17, leadListInputApp;
45219
+ var import_node_crypto29, import_hono17, leadListInputApp;
44610
45220
  var init_lead_list_input_routes = __esm({
44611
45221
  "src/api/lead-list-input-routes.ts"() {
44612
45222
  "use strict";
44613
- import_node_crypto28 = require("crypto");
45223
+ import_node_crypto29 = require("crypto");
44614
45224
  import_hono17 = require("hono");
44615
45225
  init_api_auth();
44616
45226
  init_lead_list_input_artifacts();
@@ -44802,7 +45412,7 @@ function retryDelaySeconds2(attempts) {
44802
45412
  return Math.min(3600, Math.max(15, 15 * 2 ** Math.min(8, Math.max(0, attempts - 1))));
44803
45413
  }
44804
45414
  async function dispatchPendingLeadListEnrichments(limit = 25) {
44805
- const rows = await claimLeadListEnrichmentOutbox({ workerId: `lead-list-dispatch-${process.pid}-${(0, import_node_crypto29.randomUUID)().slice(0, 8)}`, limit });
45415
+ const rows = await claimLeadListEnrichmentOutbox({ workerId: `lead-list-dispatch-${process.pid}-${(0, import_node_crypto30.randomUUID)().slice(0, 8)}`, limit });
44806
45416
  const result = { claimed: rows.length, dispatched: 0, failed: 0 };
44807
45417
  for (const row of rows) {
44808
45418
  if (!row.claimToken) continue;
@@ -44821,11 +45431,11 @@ async function dispatchPendingLeadListEnrichments(limit = 25) {
44821
45431
  }
44822
45432
  return result;
44823
45433
  }
44824
- var import_node_crypto29;
45434
+ var import_node_crypto30;
44825
45435
  var init_lead_list_enrichment_dispatch = __esm({
44826
45436
  "src/api/lead-list-enrichment-dispatch.ts"() {
44827
45437
  "use strict";
44828
- import_node_crypto29 = require("crypto");
45438
+ import_node_crypto30 = require("crypto");
44829
45439
  init_client();
44830
45440
  init_lead_list_enrichment_repository();
44831
45441
  }
@@ -44841,15 +45451,15 @@ function idempotencyKey2(raw) {
44841
45451
  return { ok: true, value };
44842
45452
  }
44843
45453
  function newJobId() {
44844
- return `lle_${(0, import_node_crypto30.randomUUID)().replace(/-/g, "")}`;
45454
+ return `lle_${(0, import_node_crypto31.randomUUID)().replace(/-/g, "")}`;
44845
45455
  }
44846
45456
  function debitKeyFor2(userId, key) {
44847
- const digest2 = (0, import_node_crypto30.createHash)("sha256").update(String(userId)).update("\0").update(key).digest("hex");
45457
+ const digest2 = (0, import_node_crypto31.createHash)("sha256").update(String(userId)).update("\0").update(key).digest("hex");
44848
45458
  return `lead-list-enrichment:${userId}:${digest2}`;
44849
45459
  }
44850
45460
  function inlineSourceDigest(headers, rows) {
44851
45461
  const values = rows.map((row) => headers.map((header) => row[header] ?? null));
44852
- return (0, import_node_crypto30.createHash)("sha256").update(stableCanonicalJson({ headers, values })).digest("hex");
45462
+ return (0, import_node_crypto31.createHash)("sha256").update(stableCanonicalJson({ headers, values })).digest("hex");
44853
45463
  }
44854
45464
  function issueMessage(error) {
44855
45465
  const issue = error.issues[0];
@@ -45047,11 +45657,11 @@ async function resolveInput(ownerId2, parsed) {
45047
45657
  sourceDigest: sourceDigest ?? inlineSourceDigest(normalized.headers, rows)
45048
45658
  };
45049
45659
  }
45050
- var import_node_crypto30, import_hono18, import_zod31, SourceSchema, StartSchema, TERMINAL, leadListEnrichmentApp;
45660
+ var import_node_crypto31, import_hono18, import_zod31, SourceSchema, StartSchema, TERMINAL, leadListEnrichmentApp;
45051
45661
  var init_lead_list_enrichment_routes = __esm({
45052
45662
  "src/api/lead-list-enrichment-routes.ts"() {
45053
45663
  "use strict";
45054
- import_node_crypto30 = require("crypto");
45664
+ import_node_crypto31 = require("crypto");
45055
45665
  import_hono18 = require("hono");
45056
45666
  import_zod31 = require("zod");
45057
45667
  init_api_auth();
@@ -48244,7 +48854,7 @@ async function readManifestFromSummary(summary) {
48244
48854
  function webhookSignature(body, timestamp2) {
48245
48855
  const secret2 = process.env.MCP_SCRAPER_WEBHOOK_SECRET?.trim();
48246
48856
  if (!secret2) return null;
48247
- return (0, import_node_crypto31.createHmac)("sha256", secret2).update(`${timestamp2}.${body}`).digest("hex");
48857
+ return (0, import_node_crypto32.createHmac)("sha256", secret2).update(`${timestamp2}.${body}`).digest("hex");
48248
48858
  }
48249
48859
  async function deliverWorkflowWebhook(input) {
48250
48860
  if (!input.webhookUrl) return;
@@ -48451,11 +49061,11 @@ async function dispatchDueWorkflowSchedules(apiUrl, limit = 3) {
48451
49061
  }
48452
49062
  return { dispatched: results.length, results };
48453
49063
  }
48454
- var import_node_crypto31, import_promises14, import_hono21, import_zod41, workflowApp, WorkflowInputSchema, WorkflowIdSchema, CadenceSchema, ScheduleStatusSchema, RunBodySchema, ScheduleCreateSchema, SchedulePatchSchema, TERMINAL_RUN_STATUSES;
49064
+ var import_node_crypto32, import_promises14, import_hono21, import_zod41, workflowApp, WorkflowInputSchema, WorkflowIdSchema, CadenceSchema, ScheduleStatusSchema, RunBodySchema, ScheduleCreateSchema, SchedulePatchSchema, TERMINAL_RUN_STATUSES;
48455
49065
  var init_workflow_routes = __esm({
48456
49066
  "src/api/workflow-routes.ts"() {
48457
49067
  "use strict";
48458
- import_node_crypto31 = require("crypto");
49068
+ import_node_crypto32 = require("crypto");
48459
49069
  import_promises14 = require("fs/promises");
48460
49070
  import_hono21 = require("hono");
48461
49071
  import_zod41 = require("zod");
@@ -48736,7 +49346,7 @@ var init_workflow_routes = __esm({
48736
49346
  // src/serp-intelligence/page-snapshot-extractor.ts
48737
49347
  function sha2562(value) {
48738
49348
  if (!value) return null;
48739
- return (0, import_node_crypto32.createHash)("sha256").update(value).digest("hex");
49349
+ return (0, import_node_crypto33.createHash)("sha256").update(value).digest("hex");
48740
49350
  }
48741
49351
  function countWords(markdown) {
48742
49352
  const matches = markdown.trim().match(/\b[\p{L}\p{N}][\p{L}\p{N}'-]*\b/gu);
@@ -49036,11 +49646,11 @@ async function capturePageSnapshots(targets, options = {}) {
49036
49646
  }
49037
49647
  };
49038
49648
  }
49039
- var import_node_crypto32, import_p_limit6, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_CONCURRENCY, DEFAULT_MAX_CONTENT_CHARS;
49649
+ var import_node_crypto33, import_p_limit6, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_CONCURRENCY, DEFAULT_MAX_CONTENT_CHARS;
49040
49650
  var init_page_snapshot_extractor = __esm({
49041
49651
  "src/serp-intelligence/page-snapshot-extractor.ts"() {
49042
49652
  "use strict";
49043
- import_node_crypto32 = require("crypto");
49653
+ import_node_crypto33 = require("crypto");
49044
49654
  import_p_limit6 = __toESM(require("p-limit"), 1);
49045
49655
  init_kpo_extractor();
49046
49656
  init_url_utils();
@@ -49536,21 +50146,21 @@ async function logRequestEventBestEffort(input) {
49536
50146
  }
49537
50147
  }
49538
50148
  function captureBillingKeys(userId, suppliedKey, body) {
49539
- const responseKey = suppliedKey?.trim() || (0, import_node_crypto33.randomUUID)();
49540
- const requestFingerprint3 = (0, import_node_crypto33.createHash)("sha256").update(JSON.stringify(body)).digest("hex");
49541
- const keyDigest = (0, import_node_crypto33.createHash)("sha256").update(String(userId)).update("\0").update(responseKey).digest("hex");
50149
+ const responseKey = suppliedKey?.trim() || (0, import_node_crypto34.randomUUID)();
50150
+ const requestFingerprint3 = (0, import_node_crypto34.createHash)("sha256").update(JSON.stringify(body)).digest("hex");
50151
+ const keyDigest = (0, import_node_crypto34.createHash)("sha256").update(String(userId)).update("\0").update(responseKey).digest("hex");
49542
50152
  return {
49543
50153
  responseKey,
49544
50154
  debitKey: `serp-capture:${userId}:${keyDigest}`,
49545
50155
  debitDescription: `${body.query} [request:${requestFingerprint3}]`
49546
50156
  };
49547
50157
  }
49548
- var import_hono22, import_node_crypto33, SERP_INTELLIGENCE_RATE_LIMIT, SERP_INTELLIGENCE_RATE_WINDOW_SECONDS, POST_CAPTURE_ROUTE_LABEL, POST_PAGE_SNAPSHOTS_ROUTE_LABEL, SERP_CAPTURE_BILLING_SOURCE, serpIntelligenceApp;
50158
+ var import_hono22, import_node_crypto34, SERP_INTELLIGENCE_RATE_LIMIT, SERP_INTELLIGENCE_RATE_WINDOW_SECONDS, POST_CAPTURE_ROUTE_LABEL, POST_PAGE_SNAPSHOTS_ROUTE_LABEL, SERP_CAPTURE_BILLING_SOURCE, serpIntelligenceApp;
49549
50159
  var init_serp_intelligence_routes = __esm({
49550
50160
  "src/api/serp-intelligence-routes.ts"() {
49551
50161
  "use strict";
49552
50162
  import_hono22 = require("hono");
49553
- import_node_crypto33 = require("crypto");
50163
+ import_node_crypto34 = require("crypto");
49554
50164
  init_browser_service_env();
49555
50165
  init_page_snapshot_extractor();
49556
50166
  init_serp_capture_service();
@@ -49787,7 +50397,7 @@ var PACKAGE_VERSION;
49787
50397
  var init_version = __esm({
49788
50398
  "src/version.ts"() {
49789
50399
  "use strict";
49790
- PACKAGE_VERSION = "0.64.1";
50400
+ PACKAGE_VERSION = "0.65.1";
49791
50401
  }
49792
50402
  });
49793
50403
 
@@ -49818,6 +50428,8 @@ seam is noted so you can chain them.
49818
50428
  - Whole site -> **extract_site** (takes a url). It durably retains complete per-page JSON, acquired HTML,
49819
50429
  and Markdown. Poll **check_site_export**, then call **site_export_read** for the manifest or a page view;
49820
50430
  call **site_export_image** for downloaded image IDs.
50431
+ - JavaScript-rendered page overlap/cannibalization -> **analyze_site_similarity**. It returns dedicated
50432
+ raw-cosine page pairs, corpus percentiles, threshold clusters, and rendered-page artifacts.
49821
50433
  - Wayback replay URLs work with the same tools: \`extract_url\` removes playback chrome and can return
49822
50434
  a featured image; \`extract_site\` batches nearby archived HTML captures for the replayed site.
49823
50435
  - For multiple archive months, pass \`extract_site.wayback\` with explicit \`months\` or a \`from\`/\`to\`
@@ -50231,6 +50843,7 @@ var init_output_schema_registry = __esm({
50231
50843
  ESSENTIAL_OUTPUT_SCHEMA_TOOLS = /* @__PURE__ */ new Set([
50232
50844
  "extract_url",
50233
50845
  "extract_site",
50846
+ "analyze_site_similarity",
50234
50847
  "audit_site",
50235
50848
  "check_site_export",
50236
50849
  "site_export_read",
@@ -51628,7 +52241,7 @@ var init_contracts = __esm({
51628
52241
  });
51629
52242
 
51630
52243
  // src/mcp/mcp-tool-schemas.ts
51631
- var import_zod45, WEBSITE_URL_OR_DOMAIN_ERROR, WebsiteUrlOrDomainSchema, HarvestPaaInputSchema, ExtractUrlBaseInputSchema, ExtractUrlInputSchema, ExtractUrlLocalInputSchema, DiffPageBaseInputSchema, DiffPageInputSchema, DiffPageLocalInputSchema, MapSiteUrlsInputSchema, MapWaybackSnapshotsInputSchema, ExtractSiteInputSchema, AuditSiteInputSchema, CheckSiteExportInputSchema, SiteExportReadInputSchema, SiteExportImageInputSchema, ArchiveReadInputSchema, YoutubeHarvestInputSchema, YoutubeTranscribeInputSchema, FacebookPageIntelInputSchema, FacebookAdSearchInputSchema, RedditThreadInputSchema, RedditTrendingInputSchema, VideoFrameAnalysisInputSchema, VideoFrameAnalysisStatusInputSchema, FacebookAdTranscribeInputSchema, FacebookVideoTranscribeInputSchema, GoogleAdsSearchInputSchema, GoogleAdsPageIntelInputSchema, GoogleAdsTranscribeInputSchema, InstagramProfileContentInputSchema, InstagramMediaDownloadInputSchema, MapsPlaceIntelInputSchema, TrustpilotReviewsInputSchema, G2ReviewsInputSchema, ReviewCardSchema, MapsSearchInputSchema, DirectoryWorkflowInputSchema, LeadScalarSchema2, LeadRowSchema, LeadColumnMapSchema2, LeadRowsImportSourceSchema, LeadCsvTextImportSourceSchema, LeadUploadImportSourceSchema, LeadListUploadStartInputSchema, LeadListImportInputSchema, LeadRowsEnrichmentSourceSchema, ImportedLeadListSourceSchema, LeadListEnrichInputSchema, LeadListEnrichStatusInputSchema, LocationMarketsInputSchema, CommonsSearchEntitiesInputSchema, CommonsGetEntityInputSchema, CommonsGetEntityLinksetInputSchema, CommonsFeaturedImageInputSchema, CommonsMediaInputSchema, CommonsCitationInputSchema, CommonsSourceInputSchema, CommonsRelatedLinkInputSchema, CommonsClaimInputSchema, CommonsPrepareEntityInputSchema, CommonsSubmitEntityInputSchema, CommonsValidateEntityInputSchema, CommonsGetEntityLedgerInputSchema, CommonsHostImageInputSchema, CommonsGetProposalInputSchema, CommonsSaveFilterInputSchema, CommonsListFiltersInputSchema, CommonsListNeedsLinksInputSchema, CommonsGenericOutputSchema, DirectoryWorkflowStatusInputSchema, LocalSourcebookSubmitInputSchema, LocalSourcebookCategorySchema, LocalSourcebookSchemaTypeInputSchema, LocalSourcebookTagCandidateObjectSchema, LocalSourcebookTagDecisionObjectSchema, LocalSourcebookIdentityObjectSchema, GetLocalSourcebookContractInputSchema, ListLocalSourcebookTagsInputSchema, ResolveLocalSourcebookTagsInputSchema, PrepareLocalSourcebookWriteInputSchema, ValidateLocalSourcebookWriteInputSchema, LocalSourcebookCaptureInputSchema, LocalSourcebookSubmissionStatusInputSchema, LocalSourcebookRefreshInputSchema, LocalSourcebookOutputSchema, ArtifactPointerOutputSchema, EditorialReadingRoomSiteSchema, EditorialReadingRoomImageSchema, EditorialReadingRoomArticleSchema, EditorialReadingRoomGuideInputSchema, EditorialReadingRoomGuideOutputSchema, CreateEditorialReadingRoomInputSchema, EditorialReadingRoomArtifactSchema, CreateEditorialReadingRoomOutputSchema, RenewEditorialReadingRoomDownloadInputSchema, RenewEditorialReadingRoomDownloadOutputSchema, CommonsPublicationSubdomainSchema, CommonsPreparePublicationInputSchema, CommonsValidatePublicationInputSchema, CommonsClaimPublicationInputSchema, CommonsPublishEditorialInputSchema, CommonsUpdateEditorialArticleInputSchema, CommonsGetPublicationInputSchema, RankTrackerModeSchema, RankTrackerBlueprintInputSchema, NullableString, MapsSearchAttemptOutput, MapsSearchOutputSchema, DirectoryMapsBusinessOutput, DirectoryCsvArtifactOutput, DirectoryWorkflowOutputSchema, LeadSuggestedColumnOutputSchema, LeadColumnMapSuggestionOutputSchema, LeadArtifactOutputSchema, LeadListUploadStartOutputSchema, LeadListImportOutputSchema, LeadCandidateOutputSchema, LeadProgressOutputSchema, LeadBillingOutputSchema, LeadAssociatedPersonSourceOutputSchema, LeadAssociatedPersonOutputSchema, LeadSampleRowOutputSchema, LeadListEnrichmentOutputSchema, LocationDatasetProvenanceOutput, LocationMarketsOutputSchema, RankTrackerToolPlanOutput, RankTrackerTableOutput, RankTrackerCronJobOutput, RankTrackerBlueprintOutputSchema, OrganicResultOutput, AiOverviewOutput, EntityIdsOutput, HarvestPaaOutputSchema, SearchSerpOutputSchema, PageMediaAssetOutput, PageMediaArtifactOutput, ExtractUrlOutputSchema, DiffPageOutputSchema, ExtractSiteOutputSchema, AuditSiteOutputSchema, CheckSiteExportOutputSchema, SiteExportReadOutputSchema, SiteExportImageOutputSchema, ArchiveEntryOutputSchema, ArchiveReadOutputSchema, MapsPlaceIntelOutputSchema, TrustpilotReviewsOutputSchema, G2ReviewsOutputSchema, CreditsInfoOutputSchema, MapSiteUrlsOutputSchema, WaybackCaptureOutputSchema, MapWaybackSnapshotsOutputSchema, YoutubeHarvestOutputSchema, FacebookAdSearchOutputSchema, VideoFrameAnalysisOutputSchema, VideoFrameAnalysisStatusOutputSchema, RedditThreadOutputSchema, RedditTrendingOutputSchema, FacebookPageIntelOutputSchema, GoogleAdsSearchOutputSchema, GoogleAdsPageIntelOutputSchema, TranscriptSignalOutput, FacebookVideoTranscribeOutputSchema, TranscriptChunkOutput, InstagramBrowserOutput, InstagramPaginationOutput, InstagramProfileContentOutputSchema, InstagramMediaTrackOutput, InstagramDownloadOutput, InstagramMediaDownloadOutputSchema, YoutubeTranscribeOutputSchema, FacebookAdTranscribeOutputSchema, GoogleAdsTranscribeOutputSchema, CaptureSerpSnapshotOutputSchema, CaptureSerpPageSnapshotsOutputSchema, CreditsInfoInputSchema, WorkflowIdSchema2, WorkflowListInputSchema, WorkflowSuggestInputSchema, WorkflowRunInputSchema, WorkflowStepInputSchema, WorkflowStatusInputSchema, WorkflowArtifactReadInputSchema, WorkflowRecipeOutput, WorkflowDefinitionOutput, WorkflowArtifactOutput, WorkflowListOutputSchema, WorkflowSuggestOutputSchema, WorkflowRunOutputSchema, WorkflowStepOutputSchema, WorkflowStatusOutputSchema, WorkflowArtifactReadOutputSchema, SearchSerpInputSchema, CaptureSerpSnapshotInputSchema, ScreenshotInputSchema, CaptureSerpPageSnapshotsInputSchema, ReportArtifactReadInputSchema, ReportArtifactReadOutputSchema, ListServiceConnectionsInputSchema, ListServiceConnectionsOutputSchema, TestServiceConnectionInputSchema, TestServiceConnectionOutputSchema, ReadServiceConnectionInputSchema, ReadServiceConnectionOutputSchema, MetaAdCreativeMediaInputSchema, MetaAdCreativeMediaOutputSchema, ImportServiceConnectionToMemoryInputSchema, ImportServiceConnectionToMemoryOutputSchema, DescribeServiceConnectionToolInputSchema, DescribeServiceConnectionToolOutputSchema, ConnectedDataContinuationSchema, ExportConnectedServiceDataInputSchema, ConnectedDataArtifactSchema, ExportConnectedServiceDataOutputSchema, SearchConsoleTableColumnSchema, SearchConsoleTableFilterSchema, ExportSearchConsoleTableDataInputSchema, ExportSearchConsoleTableDataOutputSchema, RenewConnectedDataExportDownloadInputSchema, RenewConnectedDataExportDownloadOutputSchema, CallServiceConnectionActionInputSchema, CallServiceConnectionActionOutputSchema, SetScheduledActionConnectionsInputSchema, SetScheduledActionConnectionsOutputSchema, SlackSendMessageInputSchema, SlackSendMessageOutputSchema, GmailSendMessageInputSchema, GmailSendMessageOutputSchema, GmailSearchContactsInputSchema, GmailSearchContactsOutputSchema, GoogleCalendarCreateEventInputSchema, GoogleCalendarCreateEventOutputSchema, ZoomCreateMeetingInputSchema, ZoomCreateMeetingOutputSchema;
52244
+ var import_zod45, WEBSITE_URL_OR_DOMAIN_ERROR, WebsiteUrlOrDomainSchema, HarvestPaaInputSchema, ExtractUrlBaseInputSchema, ExtractUrlInputSchema, ExtractUrlLocalInputSchema, DiffPageBaseInputSchema, DiffPageInputSchema, DiffPageLocalInputSchema, MapSiteUrlsInputSchema, MapWaybackSnapshotsInputSchema, ExtractSiteInputSchema, AnalyzeSiteSimilarityInputSchema, AuditSiteInputSchema, CheckSiteExportInputSchema, SiteExportReadInputSchema, SiteExportImageInputSchema, ArchiveReadInputSchema, YoutubeHarvestInputSchema, YoutubeTranscribeInputSchema, FacebookPageIntelInputSchema, FacebookAdSearchInputSchema, RedditThreadInputSchema, RedditTrendingInputSchema, VideoFrameAnalysisInputSchema, VideoFrameAnalysisStatusInputSchema, FacebookAdTranscribeInputSchema, FacebookVideoTranscribeInputSchema, GoogleAdsSearchInputSchema, GoogleAdsPageIntelInputSchema, GoogleAdsTranscribeInputSchema, InstagramProfileContentInputSchema, InstagramMediaDownloadInputSchema, MapsPlaceIntelInputSchema, TrustpilotReviewsInputSchema, G2ReviewsInputSchema, ReviewCardSchema, MapsSearchInputSchema, DirectoryWorkflowInputSchema, LeadScalarSchema2, LeadRowSchema, LeadColumnMapSchema2, LeadRowsImportSourceSchema, LeadCsvTextImportSourceSchema, LeadUploadImportSourceSchema, LeadListUploadStartInputSchema, LeadListImportInputSchema, LeadRowsEnrichmentSourceSchema, ImportedLeadListSourceSchema, LeadListEnrichInputSchema, LeadListEnrichStatusInputSchema, LocationMarketsInputSchema, CommonsSearchEntitiesInputSchema, CommonsGetEntityInputSchema, CommonsGetEntityLinksetInputSchema, CommonsFeaturedImageInputSchema, CommonsMediaInputSchema, CommonsCitationInputSchema, CommonsSourceInputSchema, CommonsRelatedLinkInputSchema, CommonsClaimInputSchema, CommonsPrepareEntityInputSchema, CommonsSubmitEntityInputSchema, CommonsValidateEntityInputSchema, CommonsGetEntityLedgerInputSchema, CommonsHostImageInputSchema, CommonsGetProposalInputSchema, CommonsSaveFilterInputSchema, CommonsListFiltersInputSchema, CommonsListNeedsLinksInputSchema, CommonsGenericOutputSchema, DirectoryWorkflowStatusInputSchema, LocalSourcebookSubmitInputSchema, LocalSourcebookCategorySchema, LocalSourcebookSchemaTypeInputSchema, LocalSourcebookTagCandidateObjectSchema, LocalSourcebookTagDecisionObjectSchema, LocalSourcebookIdentityObjectSchema, GetLocalSourcebookContractInputSchema, ListLocalSourcebookTagsInputSchema, ResolveLocalSourcebookTagsInputSchema, PrepareLocalSourcebookWriteInputSchema, ValidateLocalSourcebookWriteInputSchema, LocalSourcebookCaptureInputSchema, LocalSourcebookSubmissionStatusInputSchema, LocalSourcebookRefreshInputSchema, LocalSourcebookOutputSchema, ArtifactPointerOutputSchema, EditorialReadingRoomSiteSchema, EditorialReadingRoomImageSchema, EditorialReadingRoomArticleSchema, EditorialReadingRoomGuideInputSchema, EditorialReadingRoomGuideOutputSchema, CreateEditorialReadingRoomInputSchema, EditorialReadingRoomArtifactSchema, CreateEditorialReadingRoomOutputSchema, RenewEditorialReadingRoomDownloadInputSchema, RenewEditorialReadingRoomDownloadOutputSchema, CommonsPublicationSubdomainSchema, CommonsPreparePublicationInputSchema, CommonsValidatePublicationInputSchema, CommonsClaimPublicationInputSchema, CommonsPublishEditorialInputSchema, CommonsUpdateEditorialArticleInputSchema, CommonsGetPublicationInputSchema, RankTrackerModeSchema, RankTrackerBlueprintInputSchema, NullableString, MapsSearchAttemptOutput, MapsSearchOutputSchema, DirectoryMapsBusinessOutput, DirectoryCsvArtifactOutput, DirectoryWorkflowOutputSchema, LeadSuggestedColumnOutputSchema, LeadColumnMapSuggestionOutputSchema, LeadArtifactOutputSchema, LeadListUploadStartOutputSchema, LeadListImportOutputSchema, LeadCandidateOutputSchema, LeadProgressOutputSchema, LeadBillingOutputSchema, LeadAssociatedPersonSourceOutputSchema, LeadAssociatedPersonOutputSchema, LeadSampleRowOutputSchema, LeadListEnrichmentOutputSchema, LocationDatasetProvenanceOutput, LocationMarketsOutputSchema, RankTrackerToolPlanOutput, RankTrackerTableOutput, RankTrackerCronJobOutput, RankTrackerBlueprintOutputSchema, OrganicResultOutput, AiOverviewOutput, EntityIdsOutput, HarvestPaaOutputSchema, SearchSerpOutputSchema, PageMediaAssetOutput, PageMediaArtifactOutput, ExtractUrlOutputSchema, DiffPageOutputSchema, ExtractSiteOutputSchema, AuditSiteOutputSchema, CheckSiteExportOutputSchema, SiteExportReadOutputSchema, SiteExportImageOutputSchema, ArchiveEntryOutputSchema, ArchiveReadOutputSchema, MapsPlaceIntelOutputSchema, TrustpilotReviewsOutputSchema, G2ReviewsOutputSchema, CreditsInfoOutputSchema, MapSiteUrlsOutputSchema, WaybackCaptureOutputSchema, MapWaybackSnapshotsOutputSchema, YoutubeHarvestOutputSchema, FacebookAdSearchOutputSchema, VideoFrameAnalysisOutputSchema, VideoFrameAnalysisStatusOutputSchema, RedditThreadOutputSchema, RedditTrendingOutputSchema, FacebookPageIntelOutputSchema, GoogleAdsSearchOutputSchema, GoogleAdsPageIntelOutputSchema, TranscriptSignalOutput, FacebookVideoTranscribeOutputSchema, TranscriptChunkOutput, InstagramBrowserOutput, InstagramPaginationOutput, InstagramProfileContentOutputSchema, InstagramMediaTrackOutput, InstagramDownloadOutput, InstagramMediaDownloadOutputSchema, YoutubeTranscribeOutputSchema, FacebookAdTranscribeOutputSchema, GoogleAdsTranscribeOutputSchema, CaptureSerpSnapshotOutputSchema, CaptureSerpPageSnapshotsOutputSchema, CreditsInfoInputSchema, WorkflowIdSchema2, WorkflowListInputSchema, WorkflowSuggestInputSchema, WorkflowRunInputSchema, WorkflowStepInputSchema, WorkflowStatusInputSchema, WorkflowArtifactReadInputSchema, WorkflowRecipeOutput, WorkflowDefinitionOutput, WorkflowArtifactOutput, WorkflowListOutputSchema, WorkflowSuggestOutputSchema, WorkflowRunOutputSchema, WorkflowStepOutputSchema, WorkflowStatusOutputSchema, WorkflowArtifactReadOutputSchema, SearchSerpInputSchema, CaptureSerpSnapshotInputSchema, ScreenshotInputSchema, CaptureSerpPageSnapshotsInputSchema, ReportArtifactReadInputSchema, ReportArtifactReadOutputSchema, ListServiceConnectionsInputSchema, ListServiceConnectionsOutputSchema, TestServiceConnectionInputSchema, TestServiceConnectionOutputSchema, ReadServiceConnectionInputSchema, ReadServiceConnectionOutputSchema, MetaAdCreativeMediaInputSchema, MetaAdCreativeMediaOutputSchema, ImportServiceConnectionToMemoryInputSchema, ImportServiceConnectionToMemoryOutputSchema, DescribeServiceConnectionToolInputSchema, DescribeServiceConnectionToolOutputSchema, ConnectedDataContinuationSchema, ExportConnectedServiceDataInputSchema, ConnectedDataArtifactSchema, ExportConnectedServiceDataOutputSchema, SearchConsoleTableColumnSchema, SearchConsoleTableFilterSchema, ExportSearchConsoleTableDataInputSchema, ExportSearchConsoleTableDataOutputSchema, RenewConnectedDataExportDownloadInputSchema, RenewConnectedDataExportDownloadOutputSchema, CallServiceConnectionActionInputSchema, CallServiceConnectionActionOutputSchema, SetScheduledActionConnectionsInputSchema, SetScheduledActionConnectionsOutputSchema, SlackSendMessageInputSchema, SlackSendMessageOutputSchema, GmailSendMessageInputSchema, GmailSendMessageOutputSchema, GmailSearchContactsInputSchema, GmailSearchContactsOutputSchema, GoogleCalendarCreateEventInputSchema, GoogleCalendarCreateEventOutputSchema, ZoomCreateMeetingInputSchema, ZoomCreateMeetingOutputSchema;
51632
52245
  var init_mcp_tool_schemas = __esm({
51633
52246
  "src/mcp/mcp-tool-schemas.ts"() {
51634
52247
  "use strict";
@@ -51738,6 +52351,13 @@ var init_mcp_tool_schemas = __esm({
51738
52351
  preserveMedia: import_zod45.z.boolean().default(false).describe("Include supported images in the export bundle. This is the preferred replacement for downloadImages."),
51739
52352
  downloadImages: import_zod45.z.boolean().optional().describe("Deprecated alias for preserveMedia. Omit when using preserveMedia; when omitted, image preservation defaults to false.")
51740
52353
  };
52354
+ AnalyzeSiteSimilarityInputSchema = {
52355
+ url: WebsiteUrlOrDomainSchema.describe("Public live site to render and compare. Bare domains default to https://."),
52356
+ maxPages: import_zod45.z.number().int().min(2).max(500).default(100).describe("Maximum rendered pages to compare; default 100, maximum 500."),
52357
+ similarityThreshold: import_zod45.z.number().min(0).max(1).default(0.9).describe("Minimum raw cosine score retained in the pair table; default 0.90."),
52358
+ similarityMaxPairs: import_zod45.z.number().int().min(1).max(5e4).default(1e4).describe("Maximum scored pairs retained, highest first; default 10,000."),
52359
+ idempotencyKey: import_zod45.z.string().trim().min(8).max(200).describe("Required unique opaque ID for this intended analysis. Reuse only when retrying the same call; use a new value for an intentional rerun.")
52360
+ };
51741
52361
  AuditSiteInputSchema = {
51742
52362
  url: WebsiteUrlOrDomainSchema.describe("Public website URL or domain for a full technical SEO audit (issues, link graph, indexability, headings, images). Bare domains default to https://. For plain content use extract_site instead."),
51743
52363
  maxPages: import_zod45.z.number().int().min(1).max(1e4).optional().describe("Maximum pages to crawl and audit. MCP audits always run as durable background exports and return a jobId; poll check_site_export for the hosted audit ZIP."),
@@ -51750,17 +52370,17 @@ var init_mcp_tool_schemas = __esm({
51750
52370
  downloadImages: import_zod45.z.boolean().optional().describe("Deprecated alias for preserveMedia. Omit when using preserveMedia; when omitted, image preservation defaults to false.")
51751
52371
  };
51752
52372
  CheckSiteExportInputSchema = {
51753
- jobId: import_zod45.z.string().min(1).describe("The jobId returned by extract_site or audit_site. Poll until status is complete, partial, or failed; partial jobs still return a downloadable bundle with successful pages and failure details.")
52373
+ jobId: import_zod45.z.string().min(1).describe("The jobId returned by extract_site, analyze_site_similarity, or audit_site. Poll until status is complete, partial, or failed; partial jobs still return successful content and failure details.")
51754
52374
  };
51755
52375
  SiteExportReadInputSchema = {
51756
- jobId: import_zod45.z.string().min(1).describe("Site export job ID returned by extract_site or audit_site."),
52376
+ jobId: import_zod45.z.string().min(1).describe("Site export job ID returned by extract_site, analyze_site_similarity, or audit_site."),
51757
52377
  pageId: import_zod45.z.string().regex(/^[a-f0-9]{64}$/).optional().describe("Page ID returned by a manifest read. Omit to list the export manifest."),
51758
52378
  format: import_zod45.z.enum(["manifest", "json", "html", "markdown"]).default("manifest").describe("manifest lists page/image IDs; JSON, HTML, and Markdown read one page representation."),
51759
52379
  offset: import_zod45.z.number().int().min(0).default(0).describe("UTF-8 byte offset. Continue from nextOffset until it is null."),
51760
52380
  maxBytes: import_zod45.z.number().int().min(1).max(1e6).default(64e3).describe("Maximum UTF-8 bytes returned in this window.")
51761
52381
  };
51762
52382
  SiteExportImageInputSchema = {
51763
- jobId: import_zod45.z.string().min(1).describe("Site export job ID returned by extract_site or audit_site."),
52383
+ jobId: import_zod45.z.string().min(1).describe("Site export job ID returned by extract_site, analyze_site_similarity, or audit_site."),
51764
52384
  imageId: import_zod45.z.string().regex(/^[a-f0-9]{64}$/).describe("Downloaded image ID returned by a site_export_read manifest.")
51765
52385
  };
51766
52386
  ArchiveReadInputSchema = {
@@ -54928,11 +55548,11 @@ function requireTasksCapability(capabilityValue) {
54928
55548
  );
54929
55549
  }
54930
55550
  function taskKey(secret2) {
54931
- return (0, import_node_crypto34.createHash)("sha256").update("mcp-scraper-task-handle\0", "utf8").update(secret2, "utf8").digest();
55551
+ return (0, import_node_crypto35.createHash)("sha256").update("mcp-scraper-task-handle\0", "utf8").update(secret2, "utf8").digest();
54932
55552
  }
54933
55553
  function encodeTaskHandle(payload, secret2) {
54934
- const nonce = (0, import_node_crypto34.randomBytes)(12);
54935
- const cipher = (0, import_node_crypto34.createCipheriv)("aes-256-gcm", taskKey(secret2), nonce);
55554
+ const nonce = (0, import_node_crypto35.randomBytes)(12);
55555
+ const cipher = (0, import_node_crypto35.createCipheriv)("aes-256-gcm", taskKey(secret2), nonce);
54936
55556
  const ciphertext = Buffer.concat([
54937
55557
  cipher.update(JSON.stringify(payload), "utf8"),
54938
55558
  cipher.final()
@@ -54949,7 +55569,7 @@ function decodeTaskHandle(taskId, secret2, ownerId2) {
54949
55569
  const nonce = bytes.subarray(0, 12);
54950
55570
  const tag = bytes.subarray(bytes.length - 16);
54951
55571
  const ciphertext = bytes.subarray(12, bytes.length - 16);
54952
- const decipher = (0, import_node_crypto34.createDecipheriv)("aes-256-gcm", taskKey(secret2), nonce);
55572
+ const decipher = (0, import_node_crypto35.createDecipheriv)("aes-256-gcm", taskKey(secret2), nonce);
54953
55573
  decipher.setAuthTag(tag);
54954
55574
  const parsed = JSON.parse(Buffer.concat([
54955
55575
  decipher.update(ciphertext),
@@ -55177,12 +55797,12 @@ async function handleMcpTasksHttpRequest(request, executor, options) {
55177
55797
  return taskHttpError(id, error instanceof import_server.ProtocolError ? error : new import_server.ProtocolError(-32603, error instanceof Error ? error.message : "Internal error"));
55178
55798
  }
55179
55799
  }
55180
- var import_server, import_node_crypto34, import_zod46, MCP_TASKS_EXTENSION_ID, TASK_HANDLE_VERSION, TASK_TTL_MS, TASK_POLL_INTERVAL_MS, TASK_INVALID_PARAMS, TASK_MISSING_CAPABILITY;
55800
+ var import_server, import_node_crypto35, import_zod46, MCP_TASKS_EXTENSION_ID, TASK_HANDLE_VERSION, TASK_TTL_MS, TASK_POLL_INTERVAL_MS, TASK_INVALID_PARAMS, TASK_MISSING_CAPABILITY;
55181
55801
  var init_mcp_tasks_extension = __esm({
55182
55802
  "src/mcp/mcp-tasks-extension.ts"() {
55183
55803
  "use strict";
55184
55804
  import_server = require("@modelcontextprotocol/server");
55185
- import_node_crypto34 = require("crypto");
55805
+ import_node_crypto35 = require("crypto");
55186
55806
  import_zod46 = require("zod");
55187
55807
  MCP_TASKS_EXTENSION_ID = "io.modelcontextprotocol/tasks";
55188
55808
  TASK_HANDLE_VERSION = "mt1";
@@ -55480,7 +56100,7 @@ var init_analytics_mcp_tools = __esm({
55480
56100
 
55481
56101
  // src/mcp/paa-mcp-server.ts
55482
56102
  function hashOwnerId(callerKey) {
55483
- return (0, import_node_crypto35.createHash)("sha256").update(callerKey).digest("hex").slice(0, 24);
56103
+ return (0, import_node_crypto36.createHash)("sha256").update(callerKey).digest("hex").slice(0, 24);
55484
56104
  }
55485
56105
  function liveWebToolAnnotations(title) {
55486
56106
  return {
@@ -55693,6 +56313,21 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
55693
56313
  await formatExtractSite(await executor.extractSite(input), input, ctx),
55694
56314
  requestContext
55695
56315
  ));
56316
+ server.registerTool("analyze_site_similarity", {
56317
+ title: "Rendered Site Content Similarity",
56318
+ description: "Find overlapping or competing live pages. Forces JavaScript rendering, retains bounded inert DOM plus clean Markdown, and returns raw-cosine pairs, corpus percentiles, clusters, and table-ready files in a private ZIP. Poll check_site_export; the scores measure content overlap, not intent or proof of cannibalization.",
56319
+ inputSchema: AnalyzeSiteSimilarityInputSchema,
56320
+ outputSchema: recordOutputSchema("analyze_site_similarity", ExtractSiteOutputSchema),
56321
+ annotations: { ...liveWebToolAnnotations("Rendered Site Content Similarity"), readOnlyHint: false }
56322
+ }, async (input, requestContext) => tasks.taskify(
56323
+ "site_export",
56324
+ await formatExtractSite(
56325
+ await executor.analyzeSiteSimilarity(input),
56326
+ { ...input, toolLabel: "Rendered Site Content Similarity" },
56327
+ ctx
56328
+ ),
56329
+ requestContext
56330
+ ));
55696
56331
  server.registerTool("audit_site", {
55697
56332
  title: "Technical SEO Audit",
55698
56333
  description: `Run a full technical SEO audit (Screaming-Frog-style) on a public website: on-page issues, internal link graph, indexability, heading/image analysis. Pass a new idempotencyKey for each intended audit and reuse it only when retrying that call. Every MCP audit starts a durable export; poll check_site_export for discovered, attempted, successful, failed, and remaining counts plus ${fileBehavior("the saved ZIP.", "the owner-scoped downloadable ZIP.")} Use extract_site instead for plain page content.`,
@@ -55706,7 +56341,7 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
55706
56341
  ));
55707
56342
  server.registerTool("check_site_export", {
55708
56343
  title: "Check Site Export",
55709
- description: "Poll a background extract_site or audit_site job. Reports discovered, attempted, successful, failed, and remaining pages plus a stable public error envelope when terminal. Complete and partial jobs expose direct AI readback through site_export_read/site_export_image and also return a downloadable ZIP.",
56344
+ description: "Poll a background extract_site, analyze_site_similarity, or audit_site job. Reports page counters and a stable public error envelope when terminal. Complete and partial jobs expose direct AI readback and a downloadable ZIP.",
55710
56345
  inputSchema: CheckSiteExportInputSchema,
55711
56346
  outputSchema: recordOutputSchema("check_site_export", CheckSiteExportOutputSchema),
55712
56347
  annotations: { ...liveWebToolAnnotations("Check Site Export"), readOnlyHint: false }
@@ -56445,7 +57080,7 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
56445
57080
  annotations: { title: "Set Scheduled Action Connections", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
56446
57081
  }, async (input) => executor.setScheduledActionConnections(input));
56447
57082
  }
56448
- var import_server2, import_zod48, import_node_fs11, import_node_path17, import_node_crypto35, ACTION_CONFIRMATION_SCHEMA;
57083
+ var import_server2, import_zod48, import_node_fs11, import_node_path17, import_node_crypto36, ACTION_CONFIRMATION_SCHEMA;
56449
57084
  var init_paa_mcp_server = __esm({
56450
57085
  "src/mcp/paa-mcp-server.ts"() {
56451
57086
  "use strict";
@@ -56453,7 +57088,7 @@ var init_paa_mcp_server = __esm({
56453
57088
  import_zod48 = require("zod");
56454
57089
  import_node_fs11 = require("fs");
56455
57090
  import_node_path17 = require("path");
56456
- import_node_crypto35 = require("crypto");
57091
+ import_node_crypto36 = require("crypto");
56457
57092
  init_version();
56458
57093
  init_rates();
56459
57094
  init_mcp_response_formatter();
@@ -56574,11 +57209,11 @@ function analyticsReportPath(input, report) {
56574
57209
  const suffix2 = query.size ? `?${query.toString()}` : "";
56575
57210
  return `/analytics/sites/${encodeURIComponent(input.siteId)}/${report}${suffix2}`;
56576
57211
  }
56577
- var import_node_crypto36, HttpMcpToolExecutor;
57212
+ var import_node_crypto37, HttpMcpToolExecutor;
56578
57213
  var init_http_mcp_tool_executor = __esm({
56579
57214
  "src/mcp/http-mcp-tool-executor.ts"() {
56580
57215
  "use strict";
56581
- import_node_crypto36 = require("crypto");
57216
+ import_node_crypto37 = require("crypto");
56582
57217
  init_harvest_timeout();
56583
57218
  init_browser_service_env();
56584
57219
  init_errors();
@@ -56647,13 +57282,13 @@ var init_http_mcp_tool_executor = __esm({
56647
57282
  });
56648
57283
  }
56649
57284
  async callDirectoryWorkflowStart(body, explicitIdempotencyKey) {
56650
- const idempotencyKey4 = `mcp-directory-${(0, import_node_crypto36.createHash)("sha256").update(explicitIdempotencyKey).digest("hex")}`;
57285
+ const idempotencyKey4 = `mcp-directory-${(0, import_node_crypto37.createHash)("sha256").update(explicitIdempotencyKey).digest("hex")}`;
56651
57286
  return this.call("/directory/run", body, this.timeoutMs, "POST", {
56652
57287
  "Idempotency-Key": idempotencyKey4
56653
57288
  });
56654
57289
  }
56655
57290
  async callSiteExtractStart(toolName, body, explicitIdempotencyKey) {
56656
- const idempotencyKey4 = `mcp-site-${(0, import_node_crypto36.createHash)("sha256").update(toolName).update("\0").update(explicitIdempotencyKey).digest("hex")}`;
57291
+ const idempotencyKey4 = `mcp-site-${(0, import_node_crypto37.createHash)("sha256").update(toolName).update("\0").update(explicitIdempotencyKey).digest("hex")}`;
56657
57292
  return this.call("/extract-site", body, this.timeoutMs, "POST", {
56658
57293
  "Idempotency-Key": idempotencyKey4
56659
57294
  });
@@ -56741,6 +57376,19 @@ var init_http_mcp_tool_executor = __esm({
56741
57376
  const { idempotencyKey: idempotencyKey4, ...body } = input;
56742
57377
  return this.callSiteExtractStart("extract_site", { ...body, background: true }, idempotencyKey4);
56743
57378
  }
57379
+ analyzeSiteSimilarity(input) {
57380
+ const { idempotencyKey: idempotencyKey4, similarityThreshold, similarityMaxPairs, ...body } = input;
57381
+ return this.callSiteExtractStart("analyze_site_similarity", {
57382
+ ...body,
57383
+ background: true,
57384
+ formats: ["markdown", "links", "json"],
57385
+ renderJavaScript: true,
57386
+ captureRenderedDom: true,
57387
+ semanticSimilarity: true,
57388
+ similarityThreshold,
57389
+ similarityMaxPairs
57390
+ }, idempotencyKey4);
57391
+ }
56744
57392
  auditSite(input) {
56745
57393
  const { idempotencyKey: idempotencyKey4, ...body } = input;
56746
57394
  const requestBody = {
@@ -57075,7 +57723,7 @@ var init_http_mcp_tool_executor = __esm({
57075
57723
  report: input.report,
57076
57724
  format: input.format
57077
57725
  }, this.timeoutMs, "POST", {
57078
- "Idempotency-Key": `analytics-export-${(0, import_node_crypto36.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
57726
+ "Idempotency-Key": `analytics-export-${(0, import_node_crypto37.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
57079
57727
  });
57080
57728
  }
57081
57729
  commonsSearchEntities(input) {
@@ -57114,7 +57762,7 @@ var init_http_mcp_tool_executor = __esm({
57114
57762
  commonsSubmitEntity(input) {
57115
57763
  const { idempotencyKey: idempotencyKey4, ...body } = input;
57116
57764
  return this.call("/commons/entities/propose", { ...body, idempotencyKey: idempotencyKey4 }, this.timeoutMs, "POST", {
57117
- "Idempotency-Key": `commons-${(0, import_node_crypto36.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
57765
+ "Idempotency-Key": `commons-${(0, import_node_crypto37.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
57118
57766
  });
57119
57767
  }
57120
57768
  commonsGetEntityLedger(input) {
@@ -57129,7 +57777,7 @@ var init_http_mcp_tool_executor = __esm({
57129
57777
  commonsUpdateEditorialArticle(input) {
57130
57778
  const { idempotencyKey: idempotencyKey4, ...body } = input;
57131
57779
  return this.call("/commons/publications/articles", { ...body, idempotencyKey: idempotencyKey4 }, this.timeoutMs, "POST", {
57132
- "Idempotency-Key": `commons-article-${(0, import_node_crypto36.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
57780
+ "Idempotency-Key": `commons-article-${(0, import_node_crypto37.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
57133
57781
  });
57134
57782
  }
57135
57783
  commonsSaveFilter(input) {
@@ -57150,13 +57798,13 @@ var init_http_mcp_tool_executor = __esm({
57150
57798
  commonsClaimPublication(input) {
57151
57799
  const { idempotencyKey: idempotencyKey4, ...body } = input;
57152
57800
  return this.call("/commons/publications/claim", { ...body, idempotencyKey: idempotencyKey4 }, this.timeoutMs, "POST", {
57153
- "Idempotency-Key": `commons-publication-claim-${(0, import_node_crypto36.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
57801
+ "Idempotency-Key": `commons-publication-claim-${(0, import_node_crypto37.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
57154
57802
  });
57155
57803
  }
57156
57804
  commonsPublishEditorial(input) {
57157
57805
  const { idempotencyKey: idempotencyKey4, ...body } = input;
57158
57806
  return this.call("/commons/publications/publish", { ...body, idempotencyKey: idempotencyKey4 }, this.timeoutMs, "POST", {
57159
- "Idempotency-Key": `commons-publication-publish-${(0, import_node_crypto36.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
57807
+ "Idempotency-Key": `commons-publication-publish-${(0, import_node_crypto37.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
57160
57808
  });
57161
57809
  }
57162
57810
  commonsGetPublication(input) {
@@ -57164,13 +57812,13 @@ var init_http_mcp_tool_executor = __esm({
57164
57812
  return this.getJson(input.subdomain ? `/commons/publications/${encodeURIComponent(input.subdomain)}?${query}` : `/commons/publications/me?${query}`);
57165
57813
  }
57166
57814
  async captureSerpSnapshot(input) {
57167
- const fingerprint2 = (0, import_node_crypto36.createHash)("sha256").update(JSON.stringify(input)).digest("hex");
57815
+ const fingerprint2 = (0, import_node_crypto37.createHash)("sha256").update(JSON.stringify(input)).digest("hex");
57168
57816
  const now = Date.now();
57169
57817
  for (const [pendingFingerprint, pendingEntry] of this.pendingSerpCaptureBillingKeys) {
57170
57818
  if (pendingEntry.expiresAt <= now) this.pendingSerpCaptureBillingKeys.delete(pendingFingerprint);
57171
57819
  }
57172
57820
  const pending = this.pendingSerpCaptureBillingKeys.get(fingerprint2);
57173
- const idempotencyKey4 = pending && pending.expiresAt > now ? pending.key : (0, import_node_crypto36.randomUUID)();
57821
+ const idempotencyKey4 = pending && pending.expiresAt > now ? pending.key : (0, import_node_crypto37.randomUUID)();
57174
57822
  this.pendingSerpCaptureBillingKeys.set(fingerprint2, {
57175
57823
  key: idempotencyKey4,
57176
57824
  expiresAt: now + 15 * 6e4
@@ -61647,7 +62295,7 @@ async function createImageSourceArtifact(args) {
61647
62295
  if (args.content.length === 0 || args.content.length > IMAGE_SOURCE_MAX_BYTES) {
61648
62296
  throw new Error("image_source_size_invalid");
61649
62297
  }
61650
- const id = (0, import_node_crypto37.randomUUID)().replaceAll("-", "");
62298
+ const id = (0, import_node_crypto38.randomUUID)().replaceAll("-", "");
61651
62299
  return createPrivateArtifact({
61652
62300
  policy: policy4(),
61653
62301
  ownerId: args.ownerId,
@@ -61666,11 +62314,11 @@ async function readOwnedImageSourceArtifact(args) {
61666
62314
  maxBytes: IMAGE_SOURCE_MAX_BYTES
61667
62315
  });
61668
62316
  }
61669
- var import_node_crypto37, IMAGE_SOURCE_ARTIFACT_PREFIX, IMAGE_SOURCE_ARTIFACT_TTL_MS, IMAGE_SOURCE_DOWNLOAD_TTL_MS, IMAGE_SOURCE_MAX_BYTES, ALLOWED_IMAGE_TYPES;
62317
+ var import_node_crypto38, IMAGE_SOURCE_ARTIFACT_PREFIX, IMAGE_SOURCE_ARTIFACT_TTL_MS, IMAGE_SOURCE_DOWNLOAD_TTL_MS, IMAGE_SOURCE_MAX_BYTES, ALLOWED_IMAGE_TYPES;
61670
62318
  var init_image_source_artifacts = __esm({
61671
62319
  "src/api/image-source-artifacts.ts"() {
61672
62320
  "use strict";
61673
- import_node_crypto37 = require("crypto");
62321
+ import_node_crypto38 = require("crypto");
61674
62322
  init_private_artifacts();
61675
62323
  IMAGE_SOURCE_ARTIFACT_PREFIX = "image-sources/";
61676
62324
  IMAGE_SOURCE_ARTIFACT_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
@@ -63284,7 +63932,7 @@ async function runBrowserAgentMigration() {
63284
63932
  }
63285
63933
  async function createExtensionRow(input) {
63286
63934
  const db = getDb();
63287
- const id = `bext_${(0, import_node_crypto38.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
63935
+ const id = `bext_${(0, import_node_crypto39.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
63288
63936
  await db.execute({
63289
63937
  sql: `INSERT INTO browser_agent_extensions (id, user_id, name, backend_id, backend_name, source, source_url, size_bytes)
63290
63938
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -63319,7 +63967,7 @@ async function deleteExtensionRow(userId, name) {
63319
63967
  }
63320
63968
  async function createAuthConnectionRow(input) {
63321
63969
  const db = getDb();
63322
- const connectionId = `authc_${(0, import_node_crypto38.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
63970
+ const connectionId = `authc_${(0, import_node_crypto39.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
63323
63971
  await db.execute({
63324
63972
  sql: `INSERT INTO browser_auth_connections (connection_id, domain, profile, account_email, note, status, browser_agent_session_id)
63325
63973
  VALUES (?, ?, ?, ?, ?, 'NEEDS_AUTH', ?)`,
@@ -63404,7 +64052,7 @@ async function deleteProfileLabel(userId, profile) {
63404
64052
  }
63405
64053
  async function createSessionRow(input) {
63406
64054
  const db = getDb();
63407
- const id = `bas_${(0, import_node_crypto38.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
64055
+ const id = `bas_${(0, import_node_crypto39.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
63408
64056
  await db.execute({
63409
64057
  sql: `INSERT INTO browser_agent_sessions (id, runtime_session_id, live_view_url, cdp_ws_url, status, label, user_id, concurrency_lock_id, last_action_at)
63410
64058
  VALUES (?, ?, ?, ?, 'open', ?, ?, ?, datetime('now'))`,
@@ -63481,7 +64129,7 @@ async function recordAction(input) {
63481
64129
  sql: `INSERT INTO browser_agent_actions (id, session_id, type, params_json, ok, error)
63482
64130
  VALUES (?, ?, ?, ?, ?, ?)`,
63483
64131
  args: [
63484
- `baa_${(0, import_node_crypto38.randomUUID)().replace(/-/g, "").slice(0, 20)}`,
64132
+ `baa_${(0, import_node_crypto39.randomUUID)().replace(/-/g, "").slice(0, 20)}`,
63485
64133
  input.sessionId,
63486
64134
  input.type,
63487
64135
  input.params == null ? null : JSON.stringify(input.params),
@@ -63521,11 +64169,11 @@ async function listReplayRows(sessionId) {
63521
64169
  });
63522
64170
  return res.rows;
63523
64171
  }
63524
- var import_node_crypto38, _ready2, _migrationPromise2, ORPHANED_SESSION_STATUS;
64172
+ var import_node_crypto39, _ready2, _migrationPromise2, ORPHANED_SESSION_STATUS;
63525
64173
  var init_browser_agent_db = __esm({
63526
64174
  "src/api/browser-agent-db.ts"() {
63527
64175
  "use strict";
63528
- import_node_crypto38 = require("crypto");
64176
+ import_node_crypto39 = require("crypto");
63529
64177
  init_db();
63530
64178
  _ready2 = false;
63531
64179
  _migrationPromise2 = null;
@@ -64672,8 +65320,8 @@ function kernelClient() {
64672
65320
  return new import_sdk11.default({ apiKey });
64673
65321
  }
64674
65322
  function backendName(userId, name, resource) {
64675
- const digest2 = (0, import_node_crypto39.createHash)("sha256").update(`${userId}:${name}`).digest("hex").slice(0, 16);
64676
- const nonce = (0, import_node_crypto39.randomUUID)().replace(/-/g, "").slice(0, 8);
65323
+ const digest2 = (0, import_node_crypto40.createHash)("sha256").update(`${userId}:${name}`).digest("hex").slice(0, 16);
65324
+ const nonce = (0, import_node_crypto40.randomUUID)().replace(/-/g, "").slice(0, 8);
64677
65325
  return `mcp-serp-${resource}-${digest2}-${nonce}`;
64678
65326
  }
64679
65327
  function isNotFound2(error) {
@@ -64751,11 +65399,11 @@ async function deleteSerpIdentity(userId, name) {
64751
65399
  throw error;
64752
65400
  }
64753
65401
  }
64754
- var import_node_crypto39, import_sdk11, MAX_SERP_IDENTITIES_PER_USER;
65402
+ var import_node_crypto40, import_sdk11, MAX_SERP_IDENTITIES_PER_USER;
64755
65403
  var init_serp_identity_service = __esm({
64756
65404
  "src/api/serp-identity-service.ts"() {
64757
65405
  "use strict";
64758
- import_node_crypto39 = require("crypto");
65406
+ import_node_crypto40 = require("crypto");
64759
65407
  import_sdk11 = __toESM(require("@onkernel/sdk"), 1);
64760
65408
  init_browser_service_env();
64761
65409
  init_serp_identity_db();
@@ -65466,7 +66114,7 @@ function buildBrowserAgentRoutes() {
65466
66114
  }
65467
66115
  const existing = await getExtensionRow(user.id, name);
65468
66116
  if (existing) return c.json({ error: `an extension named "${name}" already exists \u2014 delete it first or pick another name` }, 409);
65469
- const backendName2 = `u${user.id}_${(0, import_node_crypto40.randomUUID)().replace(/-/g, "")}`;
66117
+ const backendName2 = `u${user.id}_${(0, import_node_crypto41.randomUUID)().replace(/-/g, "")}`;
65470
66118
  try {
65471
66119
  const imported = await importExtensionFromStore(storeUrl, backendName2);
65472
66120
  const row = await createExtensionRow({
@@ -65525,11 +66173,11 @@ function buildBrowserAgentRoutes() {
65525
66173
  });
65526
66174
  return app2;
65527
66175
  }
65528
- var import_node_crypto40, import_hono24, auth, DEFAULT_BROWSER_SESSION_LOCK_TTL_SECONDS, EXTENSION_NAME_RE, SERP_IDENTITY_NAME_RE;
66176
+ var import_node_crypto41, import_hono24, auth, DEFAULT_BROWSER_SESSION_LOCK_TTL_SECONDS, EXTENSION_NAME_RE, SERP_IDENTITY_NAME_RE;
65529
66177
  var init_browser_agent_routes = __esm({
65530
66178
  "src/api/browser-agent-routes.ts"() {
65531
66179
  "use strict";
65532
- import_node_crypto40 = require("crypto");
66180
+ import_node_crypto41 = require("crypto");
65533
66181
  import_hono24 = require("hono");
65534
66182
  init_api_auth();
65535
66183
  init_errors();
@@ -66087,7 +66735,7 @@ async function getKeys() {
66087
66735
  const privateKey = await (0, import_jose2.importPKCS8)(pem, "RS256", { extractable: true });
66088
66736
  const full = await (0, import_jose2.exportJWK)(privateKey);
66089
66737
  const publicJwk = { kty: full.kty, n: full.n, e: full.e };
66090
- const kid = (0, import_node_crypto41.createHash)("sha256").update(JSON.stringify({ e: publicJwk.e, kty: publicJwk.kty, n: publicJwk.n })).digest("base64url").slice(0, 16);
66738
+ const kid = (0, import_node_crypto42.createHash)("sha256").update(JSON.stringify({ e: publicJwk.e, kty: publicJwk.kty, n: publicJwk.n })).digest("base64url").slice(0, 16);
66091
66739
  publicJwk.kid = kid;
66092
66740
  publicJwk.alg = "RS256";
66093
66741
  publicJwk.use = "sig";
@@ -66266,23 +66914,23 @@ async function validateAuthRequest(p) {
66266
66914
  }
66267
66915
  function pkceMatches(verifier, challenge) {
66268
66916
  if (!verifier) return false;
66269
- const computed = (0, import_node_crypto41.createHash)("sha256").update(verifier).digest("base64url");
66917
+ const computed = (0, import_node_crypto42.createHash)("sha256").update(verifier).digest("base64url");
66270
66918
  return computed === challenge;
66271
66919
  }
66272
66920
  async function mintAccessToken(identity, scope, plan, audience) {
66273
66921
  const { privateKey, kid } = await getKeys();
66274
- return new import_jose2.SignJWT({ scope, plan }).setProtectedHeader({ alg: "RS256", kid }).setIssuer(ISSUER).setSubject(identity).setAudience(audience).setIssuedAt().setJti((0, import_node_crypto41.randomUUID)()).setExpirationTime(`${ACCESS_TTL_SECONDS}s`).sign(privateKey);
66922
+ return new import_jose2.SignJWT({ scope, plan }).setProtectedHeader({ alg: "RS256", kid }).setIssuer(ISSUER).setSubject(identity).setAudience(audience).setIssuedAt().setJti((0, import_node_crypto42.randomUUID)()).setExpirationTime(`${ACCESS_TTL_SECONDS}s`).sign(privateKey);
66275
66923
  }
66276
66924
  function tokenErrorResponse(c, error, description, status) {
66277
66925
  return c.json({ error, error_description: description }, status);
66278
66926
  }
66279
- var import_hono26, import_cookie, import_node_crypto41, import_jose2, ISSUER, RESOURCE, SCRAPER_RESOURCE, MEMORY_SCOPES, SCRAPER_SCOPES, SUPPORTED_SCOPES, ACCESS_TTL_SECONDS, REFRESH_TTL_SECONDS, CODE_TTL_SECONDS, ROTATION_GRACE_SECONDS, OAUTH_DATABASE_TIMEOUT_MS, OAuthDatabaseUnavailableError, secureCookies, sessionCookieOptions, cachedKeys, oauthApp;
66927
+ var import_hono26, import_cookie, import_node_crypto42, import_jose2, ISSUER, RESOURCE, SCRAPER_RESOURCE, MEMORY_SCOPES, SCRAPER_SCOPES, SUPPORTED_SCOPES, ACCESS_TTL_SECONDS, REFRESH_TTL_SECONDS, CODE_TTL_SECONDS, ROTATION_GRACE_SECONDS, OAUTH_DATABASE_TIMEOUT_MS, OAuthDatabaseUnavailableError, secureCookies, sessionCookieOptions, cachedKeys, oauthApp;
66280
66928
  var init_oauth_routes = __esm({
66281
66929
  "src/api/oauth-routes.ts"() {
66282
66930
  "use strict";
66283
66931
  import_hono26 = require("hono");
66284
66932
  import_cookie = require("hono/cookie");
66285
- import_node_crypto41 = require("crypto");
66933
+ import_node_crypto42 = require("crypto");
66286
66934
  import_jose2 = require("jose");
66287
66935
  init_session();
66288
66936
  init_db();
@@ -66389,7 +67037,7 @@ var init_oauth_routes = __esm({
66389
67037
  }
66390
67038
  }
66391
67039
  const clientName = typeof body.client_name === "string" ? body.client_name : null;
66392
- const clientId = `client_${(0, import_node_crypto41.randomBytes)(16).toString("hex")}`;
67040
+ const clientId = `client_${(0, import_node_crypto42.randomBytes)(16).toString("hex")}`;
66393
67041
  await withOAuthDatabaseDeadline("register-client", registerClient(clientId, redirectUris, clientName));
66394
67042
  console.log("[oauth-dcr] register OK client_id=%s redirect_uris=%s", clientId, JSON.stringify(redirectUris));
66395
67043
  return c.json({
@@ -66442,7 +67090,7 @@ var init_oauth_routes = __esm({
66442
67090
  if (action3 === "deny") return redirectWithError(p.redirect_uri, p.state, "access_denied");
66443
67091
  if (action3 !== "approve") return c.text("unsupported action", 400);
66444
67092
  const scope = negotiateScope(p.scope, user, p.resource);
66445
- const code = `code_${(0, import_node_crypto41.randomBytes)(32).toString("base64url")}`;
67093
+ const code = `code_${(0, import_node_crypto42.randomBytes)(32).toString("base64url")}`;
66446
67094
  const expiresAt = new Date(Date.now() + CODE_TTL_SECONDS * 1e3).toISOString();
66447
67095
  await withOAuthDatabaseDeadline("put-authorization-code", putCode({
66448
67096
  code,
@@ -66481,7 +67129,7 @@ var init_oauth_routes = __esm({
66481
67129
  const plan = user ? resolvePlan(user) : "free";
66482
67130
  const audience = record.resource ?? RESOURCE();
66483
67131
  const accessToken = await mintAccessToken(record.identity, record.scope, plan, audience);
66484
- const refreshToken = `rt_${(0, import_node_crypto41.randomBytes)(40).toString("base64url")}`;
67132
+ const refreshToken = `rt_${(0, import_node_crypto42.randomBytes)(40).toString("base64url")}`;
66485
67133
  await withOAuthDatabaseDeadline("put-refresh-token", putRefresh({
66486
67134
  refresh_token: refreshToken,
66487
67135
  client_id: clientId,
@@ -66511,7 +67159,7 @@ var init_oauth_routes = __esm({
66511
67159
  const plan = user ? resolvePlan(user) : "free";
66512
67160
  const audience = record.resource ?? RESOURCE();
66513
67161
  const accessToken = await mintAccessToken(record.identity, record.scope, plan, audience);
66514
- const nextRefresh = `rt_${(0, import_node_crypto41.randomBytes)(40).toString("base64url")}`;
67162
+ const nextRefresh = `rt_${(0, import_node_crypto42.randomBytes)(40).toString("base64url")}`;
66515
67163
  await withOAuthDatabaseDeadline("rotate-refresh-token", rotateRefresh(refreshToken, {
66516
67164
  refresh_token: nextRefresh,
66517
67165
  client_id: record.client_id,
@@ -67931,7 +68579,7 @@ function quoteUntrusted(value) {
67931
68579
  return clean3.split("\n").map((line) => `> ${line}`).join("\n");
67932
68580
  }
67933
68581
  function shortHash(value, length = 24) {
67934
- return (0, import_node_crypto42.createHash)("sha256").update(value).digest("hex").slice(0, length);
68582
+ return (0, import_node_crypto43.createHash)("sha256").update(value).digest("hex").slice(0, length);
67935
68583
  }
67936
68584
  function safePathPart(value) {
67937
68585
  return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "contact";
@@ -68419,11 +69067,11 @@ async function captureSupportMessage(memoryKey, email, options) {
68419
69067
  direction: options.direction
68420
69068
  };
68421
69069
  }
68422
- var import_node_crypto42, SUPPORT_TAGS, ISSUE_TAGS;
69070
+ var import_node_crypto43, SUPPORT_TAGS, ISSUE_TAGS;
68423
69071
  var init_resend_support_thread = __esm({
68424
69072
  "src/api/resend-support-thread.ts"() {
68425
69073
  "use strict";
68426
- import_node_crypto42 = require("crypto");
69074
+ import_node_crypto43 = require("crypto");
68427
69075
  init_memory();
68428
69076
  SUPPORT_TAGS = [
68429
69077
  {
@@ -69008,7 +69656,7 @@ async function claimWorkshopRegistration(input) {
69008
69656
  ) < ?
69009
69657
  `,
69010
69658
  args: [
69011
- (0, import_node_crypto43.randomUUID)(),
69659
+ (0, import_node_crypto44.randomUUID)(),
69012
69660
  input.eventSlug,
69013
69661
  input.email,
69014
69662
  input.firstName,
@@ -69025,7 +69673,7 @@ async function claimWorkshopRegistration(input) {
69025
69673
  id, event_slug, email, first_name, last_name, status
69026
69674
  ) VALUES (?, ?, ?, ?, ?, 'waitlisted')
69027
69675
  `,
69028
- args: [(0, import_node_crypto43.randomUUID)(), input.eventSlug, input.email, input.firstName, input.lastName || null]
69676
+ args: [(0, import_node_crypto44.randomUUID)(), input.eventSlug, input.email, input.firstName, input.lastName || null]
69029
69677
  });
69030
69678
  const waitlisted = await getWorkshopRegistration(input.eventSlug, input.email);
69031
69679
  if (!waitlisted) throw new Error("workshop registration could not be claimed");
@@ -69040,7 +69688,7 @@ async function registerWorkshopInterest(input) {
69040
69688
  id, event_slug, email, first_name, last_name, status
69041
69689
  ) VALUES (?, ?, ?, ?, ?, 'interested')
69042
69690
  `,
69043
- args: [(0, import_node_crypto43.randomUUID)(), input.eventSlug, input.email, input.firstName, input.lastName || null]
69691
+ args: [(0, import_node_crypto44.randomUUID)(), input.eventSlug, input.email, input.firstName, input.lastName || null]
69044
69692
  });
69045
69693
  const registration = await getWorkshopRegistration(input.eventSlug, input.email);
69046
69694
  if (!registration) throw new Error("workshop interest could not be recorded");
@@ -69070,11 +69718,11 @@ async function updateWorkshopRegistration(id, patch) {
69070
69718
  args: [...entries.map(([, value]) => value), id]
69071
69719
  });
69072
69720
  }
69073
- var import_node_crypto43;
69721
+ var import_node_crypto44;
69074
69722
  var init_workshop_registration_repository = __esm({
69075
69723
  "src/api/workshop-registration-repository.ts"() {
69076
69724
  "use strict";
69077
- import_node_crypto43 = require("crypto");
69725
+ import_node_crypto44 = require("crypto");
69078
69726
  init_db();
69079
69727
  }
69080
69728
  });
@@ -69647,7 +70295,7 @@ function renderEditorialReadingRoom(input, now = /* @__PURE__ */ new Date()) {
69647
70295
  articleCount: articles.length,
69648
70296
  wordCount: totalWordCount,
69649
70297
  bytes,
69650
- sha256: (0, import_node_crypto44.createHash)("sha256").update(html).digest("hex"),
70298
+ sha256: (0, import_node_crypto45.createHash)("sha256").update(html).digest("hex"),
69651
70299
  warnings
69652
70300
  };
69653
70301
  }
@@ -69730,14 +70378,14 @@ ${provenance}`;
69730
70378
  ...rendered,
69731
70379
  html,
69732
70380
  bytes: Buffer.byteLength(html),
69733
- sha256: (0, import_node_crypto44.createHash)("sha256").update(html).digest("hex")
70381
+ sha256: (0, import_node_crypto45.createHash)("sha256").update(html).digest("hex")
69734
70382
  };
69735
70383
  }
69736
- var import_node_crypto44, import_node_fs14, import_node_path21, import_marked, runtimeEntryDir, assetCache;
70384
+ var import_node_crypto45, import_node_fs14, import_node_path21, import_marked, runtimeEntryDir, assetCache;
69737
70385
  var init_render = __esm({
69738
70386
  "src/editorial-reading-room/render.ts"() {
69739
70387
  "use strict";
69740
- import_node_crypto44 = require("crypto");
70388
+ import_node_crypto45 = require("crypto");
69741
70389
  import_node_fs14 = require("fs");
69742
70390
  import_node_path21 = require("path");
69743
70391
  import_marked = require("marked");
@@ -70001,7 +70649,7 @@ async function hostCommonsImage(input) {
70001
70649
  415
70002
70650
  );
70003
70651
  }
70004
- const digest2 = (0, import_node_crypto45.createHash)("sha256").update(bytes).digest("hex");
70652
+ const digest2 = (0, import_node_crypto46.createHash)("sha256").update(bytes).digest("hex");
70005
70653
  const existing = await getDb().execute({
70006
70654
  sql: "SELECT id, url, content_type, bytes, source_url FROM commons_images WHERE digest = ? LIMIT 1",
70007
70655
  args: [digest2]
@@ -70089,11 +70737,11 @@ async function hostEntityImages(input) {
70089
70737
  }
70090
70738
  return rewrite;
70091
70739
  }
70092
- var import_node_crypto45, import_promises16, import_node_net2, COMMONS_IMAGE_MAX_BYTES, COMMONS_IMAGE_MAX_REDIRECTS, COMMONS_IMAGE_FETCH_TIMEOUT_MS, CommonsImageError, imageSchemaReady, hostOverride;
70740
+ var import_node_crypto46, import_promises16, import_node_net2, COMMONS_IMAGE_MAX_BYTES, COMMONS_IMAGE_MAX_REDIRECTS, COMMONS_IMAGE_FETCH_TIMEOUT_MS, CommonsImageError, imageSchemaReady, hostOverride;
70093
70741
  var init_commons_image_store = __esm({
70094
70742
  "src/api/commons-image-store.ts"() {
70095
70743
  "use strict";
70096
- import_node_crypto45 = require("crypto");
70744
+ import_node_crypto46 = require("crypto");
70097
70745
  import_promises16 = require("dns/promises");
70098
70746
  import_node_net2 = require("net");
70099
70747
  init_blob_store();
@@ -70116,141 +70764,6 @@ var init_commons_image_store = __esm({
70116
70764
  }
70117
70765
  });
70118
70766
 
70119
- // src/api/commons-embeddings.ts
70120
- function commonsEmbedModel() {
70121
- return (process.env.JINA_EMBED_MODEL ?? "jina-embeddings-v5-omni-small").trim();
70122
- }
70123
- function commonsEmbedDim() {
70124
- return Number((process.env.JINA_EMBED_DIM ?? "1024").trim());
70125
- }
70126
- function commonsSemanticSearchConfigured() {
70127
- return Boolean(process.env.JINA_API_KEY?.trim() && process.env.MEMORY_DATABASE_URL?.trim());
70128
- }
70129
- function vectorSql() {
70130
- if (_vectorSql) return _vectorSql;
70131
- const url = process.env.MEMORY_DATABASE_URL?.trim();
70132
- if (!url) throw new Error("MEMORY_DATABASE_URL is not set; Commons semantic search needs the shared Postgres.");
70133
- _vectorSql = (0, import_serverless.neon)(url);
70134
- return _vectorSql;
70135
- }
70136
- async function ensureCommonsVectorSchema() {
70137
- if (vectorSchemaReady) return;
70138
- const dimension = commonsEmbedDim();
70139
- await vectorSql().query("CREATE EXTENSION IF NOT EXISTS vector");
70140
- await vectorSql().query(`
70141
- CREATE TABLE IF NOT EXISTS commons_index_vectors (
70142
- document_id TEXT PRIMARY KEY,
70143
- entity_id TEXT NOT NULL,
70144
- document_type TEXT NOT NULL,
70145
- title TEXT NOT NULL,
70146
- embedding vector(${dimension}) NOT NULL,
70147
- model TEXT NOT NULL,
70148
- updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
70149
- )
70150
- `);
70151
- await vectorSql().query("CREATE INDEX IF NOT EXISTS commons_index_vectors_entity ON commons_index_vectors(entity_id)");
70152
- vectorSchemaReady = true;
70153
- }
70154
- async function embedCommonsTexts(texts) {
70155
- const apiKey = process.env.JINA_API_KEY?.trim();
70156
- if (!apiKey) throw new Error("JINA_API_KEY is not set; Commons semantic search cannot embed.");
70157
- if (!texts.length) return [];
70158
- const response = await fetch("https://api.jina.ai/v1/embeddings", {
70159
- method: "POST",
70160
- headers: { authorization: `Bearer ${apiKey}`, "content-type": "application/json" },
70161
- body: JSON.stringify({
70162
- model: commonsEmbedModel(),
70163
- dimensions: commonsEmbedDim(),
70164
- input: texts.map((text2) => ({ text: text2.slice(0, 8e3) }))
70165
- }),
70166
- signal: AbortSignal.timeout(6e4)
70167
- });
70168
- if (!response.ok) {
70169
- throw new Error(`Jina embedding request failed with HTTP ${response.status}: ${(await response.text()).slice(0, 200)}`);
70170
- }
70171
- const payload = await response.json();
70172
- const vectors = (payload.data ?? []).map((item) => item.embedding);
70173
- if (vectors.length !== texts.length) {
70174
- throw new Error(`Jina returned ${vectors.length} embeddings for ${texts.length} inputs.`);
70175
- }
70176
- return vectors;
70177
- }
70178
- async function embedQueuedCommonsDocuments(limit = 50) {
70179
- if (!commonsSemanticSearchConfigured()) return { claimed: 0, embedded: 0, failed: 0, remaining: 0 };
70180
- await ensureCommonsVectorSchema();
70181
- const bounded2 = Math.max(1, Math.min(200, Math.floor(limit)));
70182
- const queued = await getDb().execute({
70183
- sql: `SELECT id, entity_id, document_type, title, text FROM commons_index_documents
70184
- WHERE embedding_status IN ('queued', 'failed') ORDER BY updated_at ASC LIMIT ?`,
70185
- args: [bounded2]
70186
- });
70187
- const rows = queued.rows;
70188
- if (!rows.length) return { claimed: 0, embedded: 0, failed: 0, remaining: await queuedCommonsDocumentCount() };
70189
- let embedded = 0;
70190
- let failed = 0;
70191
- const model = commonsEmbedModel();
70192
- try {
70193
- const vectors = await embedCommonsTexts(rows.map((row) => `${row.title}
70194
-
70195
- ${row.text}`));
70196
- for (const [index, row] of rows.entries()) {
70197
- const literal = `[${vectors[index].join(",")}]`;
70198
- await vectorSql().query(
70199
- `INSERT INTO commons_index_vectors (document_id, entity_id, document_type, title, embedding, model, updated_at)
70200
- VALUES ($1, $2, $3, $4, $5::vector, $6, now())
70201
- ON CONFLICT (document_id) DO UPDATE SET entity_id = EXCLUDED.entity_id, document_type = EXCLUDED.document_type,
70202
- title = EXCLUDED.title, embedding = EXCLUDED.embedding, model = EXCLUDED.model, updated_at = now()`,
70203
- [row.id, row.entity_id, row.document_type, row.title, literal, model]
70204
- );
70205
- await getDb().execute({
70206
- sql: `UPDATE commons_index_documents SET embedding_status = 'indexed', embedding_provider = ?, embedding_model = ?,
70207
- vector_ref = ?, indexed_at = ?, error = NULL WHERE id = ?`,
70208
- args: [COMMONS_EMBED_PROVIDER, model, row.id, (/* @__PURE__ */ new Date()).toISOString(), row.id]
70209
- });
70210
- embedded += 1;
70211
- }
70212
- } catch (error) {
70213
- failed = rows.length - embedded;
70214
- const message = (error instanceof Error ? error.message : String(error)).slice(0, 500);
70215
- for (const row of rows.slice(embedded)) {
70216
- await getDb().execute({
70217
- sql: `UPDATE commons_index_documents SET embedding_status = 'failed', error = ? WHERE id = ?`,
70218
- args: [message, row.id]
70219
- }).catch(() => void 0);
70220
- }
70221
- }
70222
- return { claimed: rows.length, embedded, failed, remaining: await queuedCommonsDocumentCount() };
70223
- }
70224
- async function queuedCommonsDocumentCount() {
70225
- const result = await getDb().execute(`SELECT COUNT(*) AS n FROM commons_index_documents WHERE embedding_status IN ('queued', 'failed')`);
70226
- return Number(result.rows[0]?.n ?? 0);
70227
- }
70228
- async function semanticCommonsEntityScores(query, limit = 40) {
70229
- const scores = /* @__PURE__ */ new Map();
70230
- if (!commonsSemanticSearchConfigured() || !query.trim()) return scores;
70231
- await ensureCommonsVectorSchema();
70232
- const [vector] = await embedCommonsTexts([query]);
70233
- if (!vector) return scores;
70234
- const rows = await vectorSql().query(
70235
- `SELECT entity_id, MAX(1 - (embedding <=> $1::vector)) AS score
70236
- FROM commons_index_vectors GROUP BY entity_id ORDER BY score DESC LIMIT $2`,
70237
- [`[${vector.join(",")}]`, Math.max(1, Math.min(100, limit))]
70238
- );
70239
- for (const row of rows) scores.set(String(row.entity_id), Number(row.score));
70240
- return scores;
70241
- }
70242
- var import_serverless, COMMONS_EMBED_PROVIDER, _vectorSql, vectorSchemaReady;
70243
- var init_commons_embeddings = __esm({
70244
- "src/api/commons-embeddings.ts"() {
70245
- "use strict";
70246
- import_serverless = require("@neondatabase/serverless");
70247
- init_db();
70248
- COMMONS_EMBED_PROVIDER = "jina";
70249
- _vectorSql = null;
70250
- vectorSchemaReady = false;
70251
- }
70252
- });
70253
-
70254
70767
  // src/api/schema-presence.ts
70255
70768
  async function loadSchemaObjects() {
70256
70769
  const res = await getDb().execute(
@@ -70353,7 +70866,7 @@ function buildCommonsLinkset(entity, claims) {
70353
70866
  context[claim.predicate] = targets;
70354
70867
  }
70355
70868
  const document2 = { linkset: [context] };
70356
- const etag = `"${(0, import_node_crypto46.createHash)("sha256").update(JSON.stringify(document2)).digest("hex")}"`;
70869
+ const etag = `"${(0, import_node_crypto47.createHash)("sha256").update(JSON.stringify(document2)).digest("hex")}"`;
70357
70870
  return { document: document2, etag, publicUrl, linksetUrl, profile: COMMONS_RELATIONSHIP_PROFILE };
70358
70871
  }
70359
70872
  function commonsLinksetDiscoveryHeader(idOrSlug) {
@@ -70421,11 +70934,11 @@ function normalizeHreflang(value) {
70421
70934
  const languages = [...new Set(value.map((item) => optionalText(item, 80)).filter((item) => Boolean(item)))];
70422
70935
  return languages.length ? languages : void 0;
70423
70936
  }
70424
- var import_node_crypto46, COMMONS_LINKSET_MEDIA_TYPE, COMMONS_RELATIONSHIP_PROFILE, REGISTERED_RELATIONS;
70937
+ var import_node_crypto47, COMMONS_LINKSET_MEDIA_TYPE, COMMONS_RELATIONSHIP_PROFILE, REGISTERED_RELATIONS;
70425
70938
  var init_commons_linksets = __esm({
70426
70939
  "src/api/commons-linksets.ts"() {
70427
70940
  "use strict";
70428
- import_node_crypto46 = require("crypto");
70941
+ import_node_crypto47 = require("crypto");
70429
70942
  COMMONS_LINKSET_MEDIA_TYPE = "application/linkset+json";
70430
70943
  COMMONS_RELATIONSHIP_PROFILE = "https://mcpscraper.dev/commons/profiles/relationships/v1";
70431
70944
  REGISTERED_RELATIONS = /* @__PURE__ */ new Set([
@@ -71065,7 +71578,7 @@ async function submitCommonsEntity(input, user) {
71065
71578
  const existingClaims = existing && normalized.claims !== void 0 ? await getCommonsClaimsByEntityId(existing.id) : [];
71066
71579
  const safety = evaluatePublishSafety(normalized, existing);
71067
71580
  const shouldApply = safety.safe && normalized.reviewPolicy !== "always_review";
71068
- const proposalId = `commons-proposal-${(0, import_node_crypto47.randomUUID)()}`;
71581
+ const proposalId = `commons-proposal-${(0, import_node_crypto48.randomUUID)()}`;
71069
71582
  const now = (/* @__PURE__ */ new Date()).toISOString();
71070
71583
  const entityId = existing?.id ?? normalized.entityId ?? allocateEntityId();
71071
71584
  const proposalStatus = shouldApply ? "accepted" : "pending_review";
@@ -71120,7 +71633,7 @@ async function submitCommonsEntity(input, user) {
71120
71633
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
71121
71634
  `,
71122
71635
  args: [
71123
- `commons-ledger-${(0, import_node_crypto47.randomUUID)()}`,
71636
+ `commons-ledger-${(0, import_node_crypto48.randomUUID)()}`,
71124
71637
  nextEntity.id,
71125
71638
  proposalId,
71126
71639
  user.id,
@@ -71226,7 +71739,7 @@ async function saveCommonsFilter(input, user) {
71226
71739
  sql: "SELECT id FROM commons_saved_filters WHERE user_id = ? AND name = ? LIMIT 1",
71227
71740
  args: [user.id, name]
71228
71741
  });
71229
- const id = existing.rows[0]?.id != null ? String(existing.rows[0].id) : `commons-filter-${(0, import_node_crypto47.randomUUID)()}`;
71742
+ const id = existing.rows[0]?.id != null ? String(existing.rows[0].id) : `commons-filter-${(0, import_node_crypto48.randomUUID)()}`;
71230
71743
  await getDb().execute({
71231
71744
  sql: `
71232
71745
  INSERT INTO commons_saved_filters (id, user_id, name, description, filter_json, created_at, updated_at)
@@ -72004,7 +72517,7 @@ function commonsIndexDocuments(entity, now) {
72004
72517
  return documents;
72005
72518
  }
72006
72519
  function commonsIndexDocumentId(entityId, documentType, documentKey) {
72007
- return `commons-index-${(0, import_node_crypto47.createHash)("sha256").update(`${entityId}
72520
+ return `commons-index-${(0, import_node_crypto48.createHash)("sha256").update(`${entityId}
72008
72521
  ${documentType}
72009
72522
  ${documentKey}`).digest("hex").slice(0, 32)}`;
72010
72523
  }
@@ -72570,11 +73083,11 @@ function jsonLikeValue(value) {
72570
73083
  function escapeLike(value) {
72571
73084
  return value.replace(/[%_]/g, "");
72572
73085
  }
72573
- var import_node_crypto47, COMMONS_SCHEMA_VERSION, DEFAULT_COMMONS_BASE_URL, DEFAULT_ENTITY_TYPE, COMMONS_ENTITY_PROFILES, schemaReady, COMMONS_SCHEMA_OBJECTS, SEARCH_CANDIDATE_LIMIT, CommonsRepositoryError;
73086
+ var import_node_crypto48, COMMONS_SCHEMA_VERSION, DEFAULT_COMMONS_BASE_URL, DEFAULT_ENTITY_TYPE, COMMONS_ENTITY_PROFILES, schemaReady, COMMONS_SCHEMA_OBJECTS, SEARCH_CANDIDATE_LIMIT, CommonsRepositoryError;
72574
73087
  var init_commons_repository = __esm({
72575
73088
  "src/api/commons-repository.ts"() {
72576
73089
  "use strict";
72577
- import_node_crypto47 = require("crypto");
73090
+ import_node_crypto48 = require("crypto");
72578
73091
  init_db();
72579
73092
  init_commons_image_store();
72580
73093
  init_commons_embeddings();
@@ -72953,7 +73466,7 @@ async function claimCommonsPublication(input, user) {
72953
73466
  }
72954
73467
  const existingName = await getCommonsPublicationBySubdomain(subdomain);
72955
73468
  if (existingName) throw new CommonsPublicationError("publication_name_unavailable", "That publication name is already claimed.", 409);
72956
- const id = `tcpub_${(0, import_node_crypto48.randomUUID)()}`;
73469
+ const id = `tcpub_${(0, import_node_crypto49.randomUUID)()}`;
72957
73470
  const now = (/* @__PURE__ */ new Date()).toISOString();
72958
73471
  try {
72959
73472
  await getDb().execute({
@@ -73005,7 +73518,7 @@ async function publishCommonsEditorial(input, user) {
73005
73518
  const canonicalUrl = editionPublicUrl(subdomain, editionSlug);
73006
73519
  const rendered = renderEditorialReadingRoom(editionInput);
73007
73520
  const html = addPublicMetadata(rendered.html, canonicalUrl, publication.title);
73008
- const editionId = `tced_${(0, import_node_crypto48.randomUUID)()}`;
73521
+ const editionId = `tced_${(0, import_node_crypto49.randomUUID)()}`;
73009
73522
  const revision = (latest?.revision ?? 0) + 1;
73010
73523
  const now = (/* @__PURE__ */ new Date()).toISOString();
73011
73524
  await getDb().batch([
@@ -73029,7 +73542,7 @@ async function publishCommonsEditorial(input, user) {
73029
73542
  JSON.stringify(editionInput.articles),
73030
73543
  html,
73031
73544
  rendered.filename,
73032
- (0, import_node_crypto48.createHash)("sha256").update(html).digest("hex"),
73545
+ (0, import_node_crypto49.createHash)("sha256").update(html).digest("hex"),
73033
73546
  rendered.articleCount,
73034
73547
  rendered.wordCount,
73035
73548
  Buffer.byteLength(html),
@@ -73087,7 +73600,7 @@ async function updateCommonsEditorialArticle(input, user) {
73087
73600
  const canonicalUrl = editionPublicUrl(subdomain, editionSlug);
73088
73601
  const rendered = renderEditorialReadingRoom(editionInput);
73089
73602
  const html = addPublicMetadata(rendered.html, canonicalUrl, publication.title);
73090
- const editionId = `tced_${(0, import_node_crypto48.randomUUID)()}`;
73603
+ const editionId = `tced_${(0, import_node_crypto49.randomUUID)()}`;
73091
73604
  const revision = latest.revision + 1;
73092
73605
  const now = (/* @__PURE__ */ new Date()).toISOString();
73093
73606
  await getDb().batch([
@@ -73111,7 +73624,7 @@ async function updateCommonsEditorialArticle(input, user) {
73111
73624
  JSON.stringify(nextArticles),
73112
73625
  html,
73113
73626
  rendered.filename,
73114
- (0, import_node_crypto48.createHash)("sha256").update(html).digest("hex"),
73627
+ (0, import_node_crypto49.createHash)("sha256").update(html).digest("hex"),
73115
73628
  rendered.articleCount,
73116
73629
  rendered.wordCount,
73117
73630
  Buffer.byteLength(html),
@@ -73316,11 +73829,11 @@ function addPublicMetadata(html, canonicalUrl, publicationTitle) {
73316
73829
  "</head>"
73317
73830
  ].join("\n"));
73318
73831
  }
73319
- var import_node_crypto48, PUBLICATION_ROOT_DOMAIN, RESERVED_SUBDOMAINS, CommonsPublicationError;
73832
+ var import_node_crypto49, PUBLICATION_ROOT_DOMAIN, RESERVED_SUBDOMAINS, CommonsPublicationError;
73320
73833
  var init_commons_publication_repository = __esm({
73321
73834
  "src/api/commons-publication-repository.ts"() {
73322
73835
  "use strict";
73323
- import_node_crypto48 = require("crypto");
73836
+ import_node_crypto49 = require("crypto");
73324
73837
  init_db();
73325
73838
  init_commons_repository();
73326
73839
  init_render();
@@ -74553,7 +75066,7 @@ async function migrateAnalytics() {
74553
75066
  }
74554
75067
  function normalizeSlug2(value) {
74555
75068
  const slug4 = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
74556
- return slug4 || `site-${(0, import_node_crypto49.randomBytes)(4).toString("hex")}`;
75069
+ return slug4 || `site-${(0, import_node_crypto50.randomBytes)(4).toString("hex")}`;
74557
75070
  }
74558
75071
  function normalizeObservedHostname(origin) {
74559
75072
  if (!origin || origin === "null") return null;
@@ -74566,7 +75079,7 @@ function normalizeObservedHostname(origin) {
74566
75079
  }
74567
75080
  }
74568
75081
  function publicPixelId() {
74569
- return `px_${(0, import_node_crypto49.randomBytes)(18).toString("base64url")}`;
75082
+ return `px_${(0, import_node_crypto50.randomBytes)(18).toString("base64url")}`;
74570
75083
  }
74571
75084
  function mapRows(rows) {
74572
75085
  return rows;
@@ -74598,7 +75111,7 @@ async function requireEditor(client2, siteId, userId) {
74598
75111
  async function createAnalyticsSite(input) {
74599
75112
  const db = getAnalyticsPool();
74600
75113
  const client2 = await db.connect();
74601
- const id = (0, import_node_crypto49.randomUUID)();
75114
+ const id = (0, import_node_crypto50.randomUUID)();
74602
75115
  const baseSlug = normalizeSlug2(input.slug || input.name);
74603
75116
  try {
74604
75117
  await client2.query("BEGIN");
@@ -74614,7 +75127,7 @@ async function createAnalyticsSite(input) {
74614
75127
  } catch (error) {
74615
75128
  const code = error.code;
74616
75129
  if (code !== "23505" || attempt === 2) throw error;
74617
- slug4 = `${baseSlug}-${(0, import_node_crypto49.randomBytes)(2).toString("hex")}`;
75130
+ slug4 = `${baseSlug}-${(0, import_node_crypto50.randomBytes)(2).toString("hex")}`;
74618
75131
  }
74619
75132
  }
74620
75133
  await client2.query(
@@ -74829,7 +75342,7 @@ async function createAnalyticsPixel(input) {
74829
75342
  VALUES ($1, $2, $3, $4, $5)
74830
75343
  RETURNING id, site_id, public_id, name, environment, status, created_at::text, NULL::text AS last_event_at`,
74831
75344
  [
74832
- (0, import_node_crypto49.randomUUID)(),
75345
+ (0, import_node_crypto50.randomUUID)(),
74833
75346
  input.siteId,
74834
75347
  publicPixelId(),
74835
75348
  input.name.trim(),
@@ -74906,7 +75419,7 @@ async function setAnalyticsPixelDomainState(input) {
74906
75419
  SELECT $1, p.id, $4, $5 FROM analytics_pixels p WHERE p.id = $2 AND p.site_id = $3
74907
75420
  ON CONFLICT(pixel_id, hostname) DO UPDATE SET state = EXCLUDED.state, updated_at = now()
74908
75421
  RETURNING id`,
74909
- [(0, import_node_crypto49.randomUUID)(), input.pixelId, input.siteId, hostname, input.state]
75422
+ [(0, import_node_crypto50.randomUUID)(), input.pixelId, input.siteId, hostname, input.state]
74910
75423
  );
74911
75424
  if (!result.rowCount)
74912
75425
  throw new AnalyticsRepositoryError(
@@ -74956,7 +75469,7 @@ function sanitizeAnalyticsProperties(value) {
74956
75469
  }
74957
75470
  async function ingestAnalyticsEvents(input) {
74958
75471
  const db = getAnalyticsPool();
74959
- const requestId = `air_${(0, import_node_crypto49.randomUUID)()}`;
75472
+ const requestId = `air_${(0, import_node_crypto50.randomUUID)()}`;
74960
75473
  const hostname = normalizeObservedHostname(input.origin);
74961
75474
  const pixelResult = await db.query(
74962
75475
  `SELECT p.id, p.site_id, p.status,
@@ -74977,7 +75490,7 @@ async function ingestAnalyticsEvents(input) {
74977
75490
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
74978
75491
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
74979
75492
  [
74980
- (0, import_node_crypto49.randomUUID)(),
75493
+ (0, import_node_crypto50.randomUUID)(),
74981
75494
  requestId,
74982
75495
  pixel?.site_id ?? null,
74983
75496
  pixel?.id ?? null,
@@ -75002,7 +75515,7 @@ async function ingestAnalyticsEvents(input) {
75002
75515
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
75003
75516
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
75004
75517
  [
75005
- (0, import_node_crypto49.randomUUID)(),
75518
+ (0, import_node_crypto50.randomUUID)(),
75006
75519
  requestId,
75007
75520
  pixel.site_id,
75008
75521
  pixel.id,
@@ -75027,7 +75540,7 @@ async function ingestAnalyticsEvents(input) {
75027
75540
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, rejected_count, reason_codes)
75028
75541
  VALUES ($1, $2, $3, $4, $5, $6::jsonb)`,
75029
75542
  [
75030
- (0, import_node_crypto49.randomUUID)(),
75543
+ (0, import_node_crypto50.randomUUID)(),
75031
75544
  requestId,
75032
75545
  pixel.site_id,
75033
75546
  pixel.id,
@@ -75051,7 +75564,7 @@ async function ingestAnalyticsEvents(input) {
75051
75564
  ON CONFLICT(pixel_id, hostname) DO UPDATE
75052
75565
  SET last_seen_at = now(), updated_at = now()
75053
75566
  RETURNING state`,
75054
- [(0, import_node_crypto49.randomUUID)(), pixel.id, hostname]
75567
+ [(0, import_node_crypto50.randomUUID)(), pixel.id, hostname]
75055
75568
  );
75056
75569
  if (domainResult.rows[0]?.state !== "approved") {
75057
75570
  await db.query(
@@ -75065,7 +75578,7 @@ async function ingestAnalyticsEvents(input) {
75065
75578
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
75066
75579
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
75067
75580
  [
75068
- (0, import_node_crypto49.randomUUID)(),
75581
+ (0, import_node_crypto50.randomUUID)(),
75069
75582
  requestId,
75070
75583
  pixel.site_id,
75071
75584
  pixel.id,
@@ -75115,7 +75628,7 @@ async function ingestAnalyticsEvents(input) {
75115
75628
  $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33
75116
75629
  ) ON CONFLICT(site_id, event_id) DO NOTHING`,
75117
75630
  [
75118
- (0, import_node_crypto49.randomUUID)(),
75631
+ (0, import_node_crypto50.randomUUID)(),
75119
75632
  event2.eventId,
75120
75633
  pixel.site_id,
75121
75634
  pixel.id,
@@ -75173,7 +75686,7 @@ async function ingestAnalyticsEvents(input) {
75173
75686
  id, request_id, site_id, pixel_id, hostname, accepted_count, rejected_count, reason_codes
75174
75687
  ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb)`,
75175
75688
  [
75176
- (0, import_node_crypto49.randomUUID)(),
75689
+ (0, import_node_crypto50.randomUUID)(),
75177
75690
  requestId,
75178
75691
  pixel.site_id,
75179
75692
  pixel.id,
@@ -75205,7 +75718,7 @@ function normalizeGeographyCode(value, max) {
75205
75718
  return normalized && /^[A-Z0-9-]+$/.test(normalized) ? normalized.slice(0, max) : null;
75206
75719
  }
75207
75720
  function pageFingerprint(scope, siteId, filters) {
75208
- return (0, import_node_crypto49.createHash)("sha256").update(JSON.stringify({ scope, siteId, filters })).digest("base64url").slice(0, 18);
75721
+ return (0, import_node_crypto50.createHash)("sha256").update(JSON.stringify({ scope, siteId, filters })).digest("base64url").slice(0, 18);
75209
75722
  }
75210
75723
  function decodePageOffset(cursor, fingerprint2) {
75211
75724
  if (!cursor) return 0;
@@ -75255,7 +75768,7 @@ async function createAnalyticsConversion(input) {
75255
75768
  404
75256
75769
  );
75257
75770
  }
75258
- const id = (0, import_node_crypto49.randomUUID)();
75771
+ const id = (0, import_node_crypto50.randomUUID)();
75259
75772
  const resolvedPerson = input.sessionId ? await db.query(
75260
75773
  `SELECT e.person_id FROM analytics_identity_nodes n JOIN analytics_identity_edges e ON e.identity_node_id = n.id
75261
75774
  WHERE n.site_id = $1 AND n.kind = 'session_id' AND n.value_hmac = $2 ORDER BY e.confidence DESC LIMIT 1`,
@@ -76004,7 +76517,7 @@ async function createAnalyticsCampaignLink(input) {
76004
76517
  404
76005
76518
  );
76006
76519
  }
76007
- const shortCode = (input.shortCode?.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-") || (0, import_node_crypto49.randomBytes)(5).toString("base64url").toLowerCase()).slice(0, 48);
76520
+ const shortCode = (input.shortCode?.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-") || (0, import_node_crypto50.randomBytes)(5).toString("base64url").toLowerCase()).slice(0, 48);
76008
76521
  try {
76009
76522
  const result = await db.query(
76010
76523
  `INSERT INTO analytics_campaign_links(
@@ -76013,7 +76526,7 @@ async function createAnalyticsCampaignLink(input) {
76013
76526
  ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15)
76014
76527
  RETURNING *, 0::int AS click_count`,
76015
76528
  [
76016
- (0, import_node_crypto49.randomUUID)(),
76529
+ (0, import_node_crypto50.randomUUID)(),
76017
76530
  input.siteId,
76018
76531
  input.pixelId ?? null,
76019
76532
  input.name.trim(),
@@ -76084,7 +76597,7 @@ async function resolveAnalyticsCampaignLink(shortCode, referrer) {
76084
76597
  if (!row) return null;
76085
76598
  await db.query(
76086
76599
  `INSERT INTO analytics_campaign_clicks(id, link_id, referrer) VALUES ($1, $2, $3)`,
76087
- [(0, import_node_crypto49.randomUUID)(), row.id, normalizeAnalyticsUrl(referrer || void 0)]
76600
+ [(0, import_node_crypto50.randomUUID)(), row.id, normalizeAnalyticsUrl(referrer || void 0)]
76088
76601
  );
76089
76602
  return buildTaggedCampaignUrl(row);
76090
76603
  }
@@ -76102,14 +76615,14 @@ async function createAnalyticsForm(input) {
76102
76615
  404
76103
76616
  );
76104
76617
  const baseSlug = normalizeSlug2(input.name);
76105
- const slug4 = `${baseSlug}-${(0, import_node_crypto49.randomBytes)(3).toString("hex")}`;
76106
- const publicId = `form_${(0, import_node_crypto49.randomBytes)(18).toString("base64url")}`;
76618
+ const slug4 = `${baseSlug}-${(0, import_node_crypto50.randomBytes)(3).toString("hex")}`;
76619
+ const publicId = `form_${(0, import_node_crypto50.randomBytes)(18).toString("base64url")}`;
76107
76620
  const result = await db.query(
76108
76621
  `INSERT INTO analytics_forms(id, public_id, site_id, pixel_id, name, slug, fields, brand, submit_label, success_message, redirect_url, consent_text, status, created_by_user_id)
76109
76622
  VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8::jsonb,$9,$10,$11,$12,$13,$14)
76110
76623
  RETURNING *, 0::int AS submission_count`,
76111
76624
  [
76112
- (0, import_node_crypto49.randomUUID)(),
76625
+ (0, import_node_crypto50.randomUUID)(),
76113
76626
  publicId,
76114
76627
  input.siteId,
76115
76628
  input.pixelId,
@@ -76154,7 +76667,7 @@ async function getPublicAnalyticsForm(publicId) {
76154
76667
  return result.rows[0] ?? null;
76155
76668
  }
76156
76669
  async function recordAnalyticsFormSubmission(input) {
76157
- const id = (0, import_node_crypto49.randomUUID)();
76670
+ const id = (0, import_node_crypto50.randomUUID)();
76158
76671
  await getAnalyticsPool().query(
76159
76672
  `INSERT INTO analytics_form_submissions(id, form_id, site_id, pixel_id, visitor_id, session_id, crm_person_ref, source, medium, campaign, crm_delivery_status, click_ids)
76160
76673
  VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12::jsonb)`,
@@ -76185,7 +76698,7 @@ function sanitizeClickIds(value) {
76185
76698
  return output;
76186
76699
  }
76187
76700
  function identityHmac(kind, value) {
76188
- return (0, import_node_crypto49.createHmac)("sha256", getSessionSecret()).update(`${kind}:${value.trim().toLowerCase()}`).digest("hex");
76701
+ return (0, import_node_crypto50.createHmac)("sha256", getSessionSecret()).update(`${kind}:${value.trim().toLowerCase()}`).digest("hex");
76189
76702
  }
76190
76703
  async function linkAnalyticsFormIdentity(input) {
76191
76704
  const client2 = await getAnalyticsPool().connect();
@@ -76194,7 +76707,7 @@ async function linkAnalyticsFormIdentity(input) {
76194
76707
  const person = await client2.query(
76195
76708
  `INSERT INTO analytics_people(id, site_id, crm_person_ref) VALUES ($1,$2,$3)
76196
76709
  ON CONFLICT(site_id, crm_person_ref) DO UPDATE SET last_seen_at = now() RETURNING id`,
76197
- [(0, import_node_crypto49.randomUUID)(), input.siteId, input.crmPersonRef]
76710
+ [(0, import_node_crypto50.randomUUID)(), input.siteId, input.crmPersonRef]
76198
76711
  );
76199
76712
  const personId = person.rows[0].id;
76200
76713
  const signals = [];
@@ -76241,7 +76754,7 @@ async function linkAnalyticsFormIdentity(input) {
76241
76754
  `INSERT INTO analytics_identity_nodes(id, site_id, kind, value_hmac) VALUES ($1,$2,$3,$4)
76242
76755
  ON CONFLICT(site_id, kind, value_hmac) DO UPDATE SET last_seen_at = now() RETURNING id`,
76243
76756
  [
76244
- (0, import_node_crypto49.randomUUID)(),
76757
+ (0, import_node_crypto50.randomUUID)(),
76245
76758
  input.siteId,
76246
76759
  signal.kind,
76247
76760
  identityHmac(signal.kind, signal.value)
@@ -76252,7 +76765,7 @@ async function linkAnalyticsFormIdentity(input) {
76252
76765
  VALUES ($1,$2,$3,$4,$5,$6)
76253
76766
  ON CONFLICT(person_id, identity_node_id, evidence_kind) DO UPDATE SET last_seen_at = now(), confidence = greatest(analytics_identity_edges.confidence, EXCLUDED.confidence)`,
76254
76767
  [
76255
- (0, import_node_crypto49.randomUUID)(),
76768
+ (0, import_node_crypto50.randomUUID)(),
76256
76769
  input.siteId,
76257
76770
  personId,
76258
76771
  node.rows[0].id,
@@ -76350,7 +76863,7 @@ async function createAnalyticsCrmImport(input) {
76350
76863
  const db = getAnalyticsPool();
76351
76864
  await requireEditor(db, input.siteId, input.userId);
76352
76865
  const client2 = await db.connect();
76353
- const id = (0, import_node_crypto49.randomUUID)();
76866
+ const id = (0, import_node_crypto50.randomUUID)();
76354
76867
  try {
76355
76868
  await client2.query("BEGIN");
76356
76869
  await client2.query(
@@ -76370,7 +76883,7 @@ async function createAnalyticsCrmImport(input) {
76370
76883
  await client2.query(
76371
76884
  `INSERT INTO analytics_crm_import_rows(id, import_id, crm_person_ref, payload_ciphertext)
76372
76885
  VALUES ($1,$2,$3,$4) ON CONFLICT(import_id, crm_person_ref) DO NOTHING`,
76373
- [(0, import_node_crypto49.randomUUID)(), id, row.crmPersonRef, row.payloadCiphertext]
76886
+ [(0, import_node_crypto50.randomUUID)(), id, row.crmPersonRef, row.payloadCiphertext]
76374
76887
  );
76375
76888
  }
76376
76889
  await client2.query("COMMIT");
@@ -76409,7 +76922,7 @@ async function createAnalyticsActivationDestination(input) {
76409
76922
  `INSERT INTO analytics_activation_destinations(id, site_id, platform, name, connection_ref, external_dataset_id, event_mapping, created_by_user_id)
76410
76923
  VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8) RETURNING *`,
76411
76924
  [
76412
- (0, import_node_crypto49.randomUUID)(),
76925
+ (0, import_node_crypto50.randomUUID)(),
76413
76926
  input.siteId,
76414
76927
  input.platform,
76415
76928
  input.name.trim(),
@@ -76461,7 +76974,7 @@ async function queueAnalyticsActivation(input) {
76461
76974
  `INSERT INTO analytics_activation_jobs(id, destination_id, conversion_id, person_id, payload_ciphertext)
76462
76975
  VALUES ($1,$2,$3,$4,$5) ON CONFLICT(destination_id, conversion_id) DO NOTHING`,
76463
76976
  [
76464
- (0, import_node_crypto49.randomUUID)(),
76977
+ (0, import_node_crypto50.randomUUID)(),
76465
76978
  destination.id,
76466
76979
  input.conversionId,
76467
76980
  input.personId ?? null,
@@ -76473,7 +76986,7 @@ async function queueAnalyticsActivation(input) {
76473
76986
  return queued;
76474
76987
  }
76475
76988
  async function queueAnalyticsFormDelivery(input) {
76476
- const id = (0, import_node_crypto49.randomUUID)();
76989
+ const id = (0, import_node_crypto50.randomUUID)();
76477
76990
  await getAnalyticsPool().query(
76478
76991
  `INSERT INTO analytics_form_delivery_jobs(id, submission_id, owner_user_id, payload_ciphertext, last_error_code)
76479
76992
  VALUES ($1,$2,$3,$4,$5)`,
@@ -76600,7 +77113,7 @@ async function analyticsHealth(siteId, userId) {
76600
77113
  }
76601
77114
  async function refreshAnalyticsDailyRollups(input) {
76602
77115
  const db = getAnalyticsPool();
76603
- const runId = (0, import_node_crypto49.randomUUID)();
77116
+ const runId = (0, import_node_crypto50.randomUUID)();
76604
77117
  await db.query(
76605
77118
  `INSERT INTO analytics_rollup_runs(id, window_start, window_end, status) VALUES ($1, $2, $3, 'running')`,
76606
77119
  [runId, input.start, input.end]
@@ -76694,7 +77207,7 @@ async function refreshAnalyticsDailyRollupsIfDue(now = /* @__PURE__ */ new Date(
76694
77207
  lockClient.release();
76695
77208
  }
76696
77209
  }
76697
- function csvCell2(value) {
77210
+ function csvCell3(value) {
76698
77211
  const text2 = value == null ? "" : String(value);
76699
77212
  return /[\n\r,\"]/.test(text2) ? `"${text2.replaceAll('"', '""')}"` : text2;
76700
77213
  }
@@ -76704,7 +77217,7 @@ function rowsToCsv2(rows) {
76704
77217
  return [
76705
77218
  columns.join(","),
76706
77219
  ...rows.map(
76707
- (row) => columns.map((column) => csvCell2(row[column])).join(",")
77220
+ (row) => columns.map((column) => csvCell3(row[column])).join(",")
76708
77221
  )
76709
77222
  ].join("\n");
76710
77223
  }
@@ -76797,7 +77310,7 @@ async function createAnalyticsExport(input) {
76797
77310
  `Generated ${(/* @__PURE__ */ new Date()).toISOString()} from the governed ${input.report} report contract.`
76798
77311
  ].join("\n");
76799
77312
  }
76800
- const id = (0, import_node_crypto49.randomUUID)();
77313
+ const id = (0, import_node_crypto50.randomUUID)();
76801
77314
  const inserted = await getAnalyticsPool().query(
76802
77315
  `INSERT INTO analytics_exports(id, site_id, requested_by_user_id, idempotency_key, report, format, filters, content)
76803
77316
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8)
@@ -76830,11 +77343,11 @@ async function createAnalyticsExport(input) {
76830
77343
  }
76831
77344
  return describeArtifact(replay.rows[0]);
76832
77345
  }
76833
- var import_node_crypto49, import_pg, AnalyticsRepositoryError, pool2, MAX_ENGAGED_MS, ENGAGED_SESSION_MS, blockedPropertyName, inferredFamilySql, ANALYTICS_CONTENT_SORTS, clickIdKeys;
77346
+ var import_node_crypto50, import_pg, AnalyticsRepositoryError, pool2, MAX_ENGAGED_MS, ENGAGED_SESSION_MS, blockedPropertyName, inferredFamilySql, ANALYTICS_CONTENT_SORTS, clickIdKeys;
76834
77347
  var init_analytics_repository = __esm({
76835
77348
  "src/api/analytics-repository.ts"() {
76836
77349
  "use strict";
76837
- import_node_crypto49 = require("crypto");
77350
+ import_node_crypto50 = require("crypto");
76838
77351
  import_pg = require("pg");
76839
77352
  init_session();
76840
77353
  init_analytics_attribution();
@@ -77073,8 +77586,8 @@ function dashboardCallbackUrl() {
77073
77586
  }
77074
77587
  async function createThorbitConnectUrl(userId) {
77075
77588
  if (!bridgeConfigured()) throw new Error("Thorbit X-Ray account bridge is not configured");
77076
- const state = (0, import_node_crypto50.randomBytes)(32).toString("base64url");
77077
- const stateHash = (0, import_node_crypto50.createHash)("sha256").update(state).digest("hex");
77589
+ const state = (0, import_node_crypto51.randomBytes)(32).toString("base64url");
77590
+ const stateHash = (0, import_node_crypto51.createHash)("sha256").update(state).digest("hex");
77078
77591
  await getAnalyticsPool().query(
77079
77592
  `INSERT INTO analytics_thorbit_connect_states(state_hash,user_id,expires_at)
77080
77593
  VALUES ($1,$2,now()+interval '10 minutes')
@@ -77089,9 +77602,9 @@ async function createThorbitConnectUrl(userId) {
77089
77602
  function verifyThorbitAssertion(token6) {
77090
77603
  const [encoded, signature] = token6.split(".");
77091
77604
  if (!encoded || !signature || !bridgeConfigured()) throw new Error("Invalid Thorbit assertion");
77092
- const expected = (0, import_node_crypto50.createHmac)("sha256", bridgeSecret()).update(encoded).digest();
77605
+ const expected = (0, import_node_crypto51.createHmac)("sha256", bridgeSecret()).update(encoded).digest();
77093
77606
  const actual = Buffer.from(signature, "base64url");
77094
- if (actual.length !== expected.length || !(0, import_node_crypto50.timingSafeEqual)(actual, expected)) {
77607
+ if (actual.length !== expected.length || !(0, import_node_crypto51.timingSafeEqual)(actual, expected)) {
77095
77608
  throw new Error("Invalid Thorbit assertion signature");
77096
77609
  }
77097
77610
  const parsed = AssertionSchema.safeParse(
@@ -77103,7 +77616,7 @@ function verifyThorbitAssertion(token6) {
77103
77616
  return parsed.data.entitlement;
77104
77617
  }
77105
77618
  async function consumeThorbitConnectCallback(input) {
77106
- const stateHash = (0, import_node_crypto50.createHash)("sha256").update(input.state).digest("hex");
77619
+ const stateHash = (0, import_node_crypto51.createHash)("sha256").update(input.state).digest("hex");
77107
77620
  const client2 = await getAnalyticsPool().connect();
77108
77621
  try {
77109
77622
  await client2.query("BEGIN");
@@ -77143,11 +77656,11 @@ async function disconnectAnalyticsEntitlement(userId) {
77143
77656
  [userId]
77144
77657
  );
77145
77658
  }
77146
- var import_node_crypto50, import_zod55, THORBIT_PRODUCT_URL, REFRESH_INTERVAL_MS, ELIGIBLE_GRACE_MS, TRIAL_LENGTH_MS, ThorbitEntitlementSchema, AssertionSchema;
77659
+ var import_node_crypto51, import_zod55, THORBIT_PRODUCT_URL, REFRESH_INTERVAL_MS, ELIGIBLE_GRACE_MS, TRIAL_LENGTH_MS, ThorbitEntitlementSchema, AssertionSchema;
77147
77660
  var init_analytics_entitlement = __esm({
77148
77661
  "src/api/analytics-entitlement.ts"() {
77149
77662
  "use strict";
77150
- import_node_crypto50 = require("crypto");
77663
+ import_node_crypto51 = require("crypto");
77151
77664
  import_zod55 = require("zod");
77152
77665
  init_analytics_repository();
77153
77666
  THORBIT_PRODUCT_URL = "https://thorbit.ai";
@@ -77287,14 +77800,14 @@ function renderPublicForm(form, placementUrl) {
77287
77800
  const brand = form.brand;
77288
77801
  return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>:root{font-family:ui-sans-serif,system-ui;color:${escapeHtml8(brand.textColor)};background:${escapeHtml8(brand.backgroundColor)}}*{box-sizing:border-box}body{margin:0;padding:18px}form{display:grid;gap:14px}label{display:grid;gap:6px;font-size:13px;font-weight:650}.check{grid-template-columns:auto 1fr;align-items:center}.check span{grid-column:2}.check input{grid-column:1;grid-row:1}input,textarea,select{width:100%;min-height:44px;padding:10px 12px;border:1px solid #ccd3df;border-radius:${brand.radius}px;background:white;color:inherit;font:inherit}textarea{min-height:110px;resize:vertical}button{min-height:46px;border:0;border-radius:${brand.radius}px;color:white;background:${escapeHtml8(brand.primaryColor)};font:inherit;font-weight:750;cursor:pointer}.consent{margin:0;color:#667085;font-size:11px;line-height:1.45}.notice{display:none;padding:12px;border-radius:${brand.radius}px;background:#edf8f2;color:#17613c}.hp{position:absolute!important;left:-9999px!important}</style></head><body><form id="mcp-form">${fields}<label class="hp">Website<input name="website" autocomplete="off" tabindex="-1"></label>${form.consent_text ? `<p class="consent">${escapeHtml8(form.consent_text)}</p>` : ""}<button>${escapeHtml8(form.submit_label)}</button><div class="notice" role="status"></div></form><script>(()=>{const form=document.querySelector('#mcp-form'),notice=form.querySelector('.notice'),q=new URLSearchParams(location.search);form.addEventListener('submit',async event=>{event.preventDefault();const button=form.querySelector('button');button.disabled=true;const raw=Object.fromEntries(new FormData(form).entries()),website=String(raw.website||''),clickIds={};delete raw.website;for(const k of ['fbclid','gclid','gbraid','wbraid','ttclid','rdt_cid','msclkid']){const v=q.get(k);if(v)clickIds[k]=v}try{const response=await fetch('/analytics/forms/${escapeHtml8(form.public_id)}/submissions',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({data:raw,website,placementUrl:${JSON.stringify(placementUrl)},visitorId:q.get('visitor')||undefined,sessionId:q.get('session')||undefined,source:q.get('source')||undefined,medium:q.get('medium')||undefined,campaign:q.get('campaign')||undefined,clickIds})});if(!response.ok)throw new Error('submit');notice.textContent=${JSON.stringify(form.success_message)};notice.style.display='block';form.reset();${form.redirect_url ? `setTimeout(()=>{top.location.href=${JSON.stringify(form.redirect_url)}},700);` : ""}}catch{notice.textContent='Your response could not be submitted. Please try again.';notice.style.display='block'}finally{button.disabled=false}})})()</script></body></html>`;
77289
77802
  }
77290
- var import_hono34, import_factory6, import_zod56, import_node_crypto51, import_papaparse6, analyticsApp, auth3, entitlementGuard, ThorbitApiKeySchema, ThorbitCallbackSchema, SiteInputSchema, PixelInputSchema, PixelUpdateSchema, EventSchema, IngestionSchema, ConversionSchema, ExportSchema2, CampaignLinkSchema, FormFieldSchema, FormInputSchema, FormSubmissionSchema, CrmImportSchema, ActivationDestinationSchema, BusinessModelSchema, AdSpendSchema;
77803
+ var import_hono34, import_factory6, import_zod56, import_node_crypto52, import_papaparse6, analyticsApp, auth3, entitlementGuard, ThorbitApiKeySchema, ThorbitCallbackSchema, SiteInputSchema, PixelInputSchema, PixelUpdateSchema, EventSchema, IngestionSchema, ConversionSchema, ExportSchema2, CampaignLinkSchema, FormFieldSchema, FormInputSchema, FormSubmissionSchema, CrmImportSchema, ActivationDestinationSchema, BusinessModelSchema, AdSpendSchema;
77291
77804
  var init_analytics_routes = __esm({
77292
77805
  "src/api/analytics-routes.ts"() {
77293
77806
  "use strict";
77294
77807
  import_hono34 = require("hono");
77295
77808
  import_factory6 = require("hono/factory");
77296
77809
  import_zod56 = require("zod");
77297
- import_node_crypto51 = require("crypto");
77810
+ import_node_crypto52 = require("crypto");
77298
77811
  import_papaparse6 = __toESM(require("papaparse"), 1);
77299
77812
  init_api_auth();
77300
77813
  init_db();
@@ -77625,7 +78138,7 @@ var init_analytics_routes = __esm({
77625
78138
  const lastName = typeof data.last_name === "string" ? data.last_name.trim() : "";
77626
78139
  const fullName = [firstName, lastName].filter(Boolean).join(" ") || (typeof data.name === "string" ? data.name.trim() : "") || email || "Website lead";
77627
78140
  const identitySeed = email || `${fullName}:${Date.now()}`;
77628
- const suffix2 = (0, import_node_crypto51.createHash)("sha256").update(identitySeed).digest("hex").slice(0, 12);
78141
+ const suffix2 = (0, import_node_crypto52.createHash)("sha256").update(identitySeed).digest("hex").slice(0, 12);
77629
78142
  const path6 = `Leads/person-${suffix2}`;
77630
78143
  const { key: memoryKey, error: memoryKeyError } = await getOrCreateUserMemoryKey(user);
77631
78144
  const existing = memoryKey ? await memoryCall("getTool", { vault: "People", path: path6 }, memoryKey) : { ok: false, error: memoryKeyError || "memory_credential_unavailable" };
@@ -77724,9 +78237,9 @@ Submitted the published form.
77724
78237
  occurredAt: (/* @__PURE__ */ new Date()).toISOString()
77725
78238
  });
77726
78239
  const match = {
77727
- ...email ? { emailSha256: (0, import_node_crypto51.createHash)("sha256").update(email).digest("hex") } : {},
78240
+ ...email ? { emailSha256: (0, import_node_crypto52.createHash)("sha256").update(email).digest("hex") } : {},
77728
78241
  ...phone ? {
77729
- phoneSha256: (0, import_node_crypto51.createHash)("sha256").update(phone.replace(/\D/g, "")).digest("hex")
78242
+ phoneSha256: (0, import_node_crypto52.createHash)("sha256").update(phone.replace(/\D/g, "")).digest("hex")
77730
78243
  } : {}
77731
78244
  };
77732
78245
  const activationQueued = await queueAnalyticsActivation({
@@ -78154,7 +78667,7 @@ Submitted the published form.
78154
78667
  rejectedCount += 1;
78155
78668
  continue;
78156
78669
  }
78157
- const digest2 = (0, import_node_crypto51.createHash)("sha256").update(
78670
+ const digest2 = (0, import_node_crypto52.createHash)("sha256").update(
78158
78671
  `${parsed.data.sourceSystem}:${externalId || email || phone || `${fullName}:${index}`}`
78159
78672
  ).digest("hex").slice(0, 16);
78160
78673
  const path6 = `Leads/person-${digest2}`;
@@ -78612,13 +79125,13 @@ var init_analytics_delivery = __esm({
78612
79125
 
78613
79126
  // src/api/scheduled-artifact-owner.ts
78614
79127
  function scheduledArtifactOwnerIdForApiKey(apiKey) {
78615
- return (0, import_node_crypto52.createHash)("sha256").update(apiKey).digest("hex").slice(0, 24);
79128
+ return (0, import_node_crypto53.createHash)("sha256").update(apiKey).digest("hex").slice(0, 24);
78616
79129
  }
78617
- var import_node_crypto52;
79130
+ var import_node_crypto53;
78618
79131
  var init_scheduled_artifact_owner = __esm({
78619
79132
  "src/api/scheduled-artifact-owner.ts"() {
78620
79133
  "use strict";
78621
- import_node_crypto52 = require("crypto");
79134
+ import_node_crypto53 = require("crypto");
78622
79135
  }
78623
79136
  });
78624
79137
 
@@ -78675,7 +79188,7 @@ async function ensureScheduledRunViewLinksSchema() {
78675
79188
  schemaReady2 = true;
78676
79189
  }
78677
79190
  function tokenHash2(token6) {
78678
- return (0, import_node_crypto53.createHash)("sha256").update(token6).digest("hex");
79191
+ return (0, import_node_crypto54.createHash)("sha256").update(token6).digest("hex");
78679
79192
  }
78680
79193
  function mapRow(row) {
78681
79194
  return {
@@ -78695,9 +79208,9 @@ function mapRow(row) {
78695
79208
  async function createScheduledRunViewLink(input) {
78696
79209
  await ensureScheduledRunViewLinksSchema();
78697
79210
  const now = input.now ?? /* @__PURE__ */ new Date();
78698
- const token6 = (0, import_node_crypto53.randomBytes)(32).toString("base64url");
79211
+ const token6 = (0, import_node_crypto54.randomBytes)(32).toString("base64url");
78699
79212
  const record = {
78700
- shareId: (0, import_node_crypto53.randomUUID)(),
79213
+ shareId: (0, import_node_crypto54.randomUUID)(),
78701
79214
  ownerId: input.ownerId,
78702
79215
  runId: input.runId,
78703
79216
  artifactId: input.artifactId,
@@ -78760,11 +79273,11 @@ async function revokeScheduledRunViewLink(ownerId2, runId, shareId, now = /* @__
78760
79273
  });
78761
79274
  return result.rowsAffected > 0;
78762
79275
  }
78763
- var import_node_crypto53, schemaReady2;
79276
+ var import_node_crypto54, schemaReady2;
78764
79277
  var init_scheduled_run_view_links = __esm({
78765
79278
  "src/api/scheduled-run-view-links.ts"() {
78766
79279
  "use strict";
78767
- import_node_crypto53 = require("crypto");
79280
+ import_node_crypto54 = require("crypto");
78768
79281
  init_db();
78769
79282
  schemaReady2 = false;
78770
79283
  }
@@ -78830,15 +79343,15 @@ ${section(flags.finalCta && Boolean(finalCta), `<section class="section cta" id=
78830
79343
  html,
78831
79344
  filename: `${input.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "personal-authority"}.html`,
78832
79345
  bytes,
78833
- sha256: (0, import_node_crypto54.createHash)("sha256").update(html).digest("hex"),
79346
+ sha256: (0, import_node_crypto55.createHash)("sha256").update(html).digest("hex"),
78834
79347
  generatedAt: generatedAtIso
78835
79348
  };
78836
79349
  }
78837
- var import_node_crypto54;
79350
+ var import_node_crypto55;
78838
79351
  var init_render2 = __esm({
78839
79352
  "src/personal-authority/render.ts"() {
78840
79353
  "use strict";
78841
- import_node_crypto54 = require("crypto");
79354
+ import_node_crypto55 = require("crypto");
78842
79355
  }
78843
79356
  });
78844
79357
 
@@ -78941,15 +79454,15 @@ ${section2(flags.finalCta && Boolean(finalCta), `<section class="section cta" id
78941
79454
  html,
78942
79455
  filename: `${input.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "personal-authority"}.html`,
78943
79456
  bytes,
78944
- sha256: (0, import_node_crypto55.createHash)("sha256").update(html).digest("hex"),
79457
+ sha256: (0, import_node_crypto56.createHash)("sha256").update(html).digest("hex"),
78945
79458
  generatedAt: generatedAtIso
78946
79459
  };
78947
79460
  }
78948
- var import_node_crypto55, FONT_STACKS;
79461
+ var import_node_crypto56, FONT_STACKS;
78949
79462
  var init_render_v2 = __esm({
78950
79463
  "src/personal-authority/render-v2.ts"() {
78951
79464
  "use strict";
78952
- import_node_crypto55 = require("crypto");
79465
+ import_node_crypto56 = require("crypto");
78953
79466
  init_contracts();
78954
79467
  FONT_STACKS = Object.freeze({
78955
79468
  "editorial-serif": "Iowan Old Style, Baskerville, Times New Roman, serif",
@@ -79065,15 +79578,15 @@ body{overflow-x:hidden}.lead-story__body,.lead-support__item,.story-card__body{m
79065
79578
  @media(max-width:620px){.desk-section__grid--panorama,.desk-section__grid--mosaic,.desk-section__grid--tiles{grid-template-columns:1fr}.story-card--panorama{display:block}.story-card--panorama h3,.story-card--mosaic-lead h3{font-size:34px}.story-card--mosaic-lead{grid-column:auto;grid-row:auto}.desk-section__grid--tiles>.story-card:nth-child(3n+2){margin-top:0}}
79066
79579
  </style></head><body data-theme="${escapeHtml11(config.theme)}" id="top" data-scheduled-renderer="newsroom_publisher_v1">${ticker}${masthead}${navigation}<main>${leadGrid}${latest}${sections}${newsletter}${pressRoom}${trustCenter}</main>${trustFooter}</body></html>`;
79067
79580
  const bytes = Buffer.byteLength(html);
79068
- const sha2565 = (0, import_node_crypto56.createHash)("sha256").update(html).digest("hex");
79581
+ const sha2565 = (0, import_node_crypto57.createHash)("sha256").update(html).digest("hex");
79069
79582
  const filename2 = `${brand.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "newsroom"}-news-site.html`;
79070
79583
  return { html, filename: filename2, bytes, sha256: sha2565, generatedAt: generatedAtIso };
79071
79584
  }
79072
- var import_node_crypto56;
79585
+ var import_node_crypto57;
79073
79586
  var init_render3 = __esm({
79074
79587
  "src/newsroom-publisher/render.ts"() {
79075
79588
  "use strict";
79076
- import_node_crypto56 = require("crypto");
79589
+ import_node_crypto57 = require("crypto");
79077
79590
  }
79078
79591
  });
79079
79592
 
@@ -79165,7 +79678,7 @@ function renderBlogArticleV1(input, config, generatedAt) {
79165
79678
  :root{--ink:#10213d;--ink2:#1c355a;--blue:#2563eb;--blue2:#1749b6;--mint:#45d5aa;--paper:#f7f5f0;--line:#dce4ef;--card:#fff;--body:#475569;--serif:Iowan Old Style,Palatino Linotype,Palatino,Georgia,serif;--sans:Inter,Avenir Next,ui-sans-serif,system-ui,sans-serif}body[data-theme="slate"]{--ink:#1e293b;--ink2:#334155;--blue:#475569;--blue2:#1e293b;--mint:#94a3b8;--paper:#f8fafc}body[data-theme="forest"]{--ink:#12352e;--ink2:#23584c;--blue:#147d64;--blue2:#0c5b48;--mint:#79d8bd;--paper:#f4f8f3}*{box-sizing:border-box}html{scroll-behavior:smooth;background:var(--paper)}body{margin:0;color:#252a33;background:var(--paper);font-family:var(--sans);line-height:1.6}a{color:inherit}button{font:inherit}button:focus-visible,a:focus-visible{outline:3px solid var(--mint);outline-offset:3px}.progress{position:fixed;z-index:50;top:0;left:0;height:4px;width:0;background:var(--mint)}.hero{position:relative;overflow:hidden;color:#fff;background:var(--ink)}.hero:after{position:absolute;right:-110px;top:-150px;width:420px;height:420px;border:1px solid #ffffff1a;border-radius:50%;content:""}.hero-inner{position:relative;z-index:1;max-width:1240px;margin:auto;padding:58px 42px 126px}.breadcrumb{display:flex;gap:8px;margin-bottom:34px;color:#dbeafecc;font-size:14px}.eyebrow{display:block;color:var(--blue);font-size:12px;font-weight:800;letter-spacing:.16em;text-transform:uppercase}.hero .eyebrow{color:var(--mint)}h1,h2,h3,p{margin-top:0}.hero h1{max-width:1000px;margin:14px 0 24px;font:700 clamp(44px,6vw,78px)/.98 var(--serif);letter-spacing:-.035em}.hero .dek{max-width:780px;color:#dbeafe;font-size:20px}.contributors{margin-top:30px;color:#eff6ff;font-size:14px}.contributors a{font-weight:800}.meta{display:flex;flex-wrap:wrap;gap:12px;margin-top:6px;color:#dbeafecc}.layout{position:relative;z-index:2;display:grid;grid-template-columns:minmax(0,760px) 290px;gap:64px;max-width:1240px;margin:-78px auto 0;padding:0 42px 80px}.main{min-width:0}.takeaways{padding:38px 44px;border-radius:24px;background:#fff;box-shadow:0 24px 70px #10213d24}.takeaways h2{margin:5px 0 20px;font:700 32px/1.1 var(--serif);color:var(--ink)}.takeaways ul{display:grid;gap:16px;margin:0;padding:0;list-style:none}.takeaways li{position:relative;padding-left:26px;color:var(--body);font-size:17px}.takeaways li:before{position:absolute;left:2px;top:.7em;width:8px;height:8px;border-radius:50%;background:var(--blue);content:""}.intro{padding:40px 0 4px}.intro p,.article-section>p{margin-bottom:22px;color:var(--body);font:400 18px/1.75 var(--sans)}.article-section{scroll-margin-top:28px;padding-top:45px}.article-section h2,.faq h2{margin:6px 0 20px;color:var(--ink);font:700 38px/1.08 var(--serif);letter-spacing:-.02em}.article-section ul{display:grid;gap:10px;margin:22px 0;padding-left:24px;color:var(--body);font-size:17px}.article-section li::marker{color:var(--blue)}.callout{margin:34px 0;padding:24px 28px;border-left:4px solid var(--blue);border-radius:0 16px 16px 0;background:#fff}.callout span{color:var(--blue);font-size:11px;font-weight:800;letter-spacing:.13em;text-transform:uppercase}.callout h3{margin:5px 0 8px;color:var(--ink);font:700 24px/1.15 var(--serif)}.callout p{margin:0;color:var(--body)}.rail{padding-top:0}.sidebar-media{overflow:hidden;margin:0 0 18px;border:1px solid #dbeafe;border-radius:16px;background:#fff}.sidebar-media img,.sidebar-media svg{display:block;width:100%;aspect-ratio:16/9;object-fit:cover}.sidebar-media figcaption{padding:10px 13px;color:#64748b;font-size:11px}.disclosure{margin-bottom:28px;border-top:1px solid #dbeafe;border-bottom:1px solid #dbeafe}.disclosure button{display:flex;width:100%;align-items:center;justify-content:space-between;gap:14px;padding:15px 0;border:0;color:#64748b;background:transparent;text-align:left;font-size:12px;cursor:pointer}.disclosure button b{color:var(--blue);font-size:18px;transition:transform .2s}.disclosure button[aria-expanded="true"] b{transform:rotate(180deg)}.disclosure p{padding:0 0 16px;margin:0;color:#64748b;font-size:12px}.toc{position:sticky;top:30px;padding:22px;border:1px solid var(--line);border-radius:16px;background:#fff;box-shadow:0 2px 5px #10213d12}.toc header{display:flex;align-items:center;justify-content:space-between}.toc header strong{color:var(--ink);font:700 21px/1 var(--serif)}.toc header button{width:34px;height:34px;border:0;border-radius:50%;color:var(--blue);background:transparent;cursor:pointer}.toc nav{display:grid;margin-top:14px}.toc nav a{display:flex;align-items:flex-start;gap:11px;padding:8px 0;color:#64748b;text-decoration:none;font-size:13px;line-height:1.35}.toc nav i{width:8px;height:8px;margin-top:5px;border:2px solid #bfdbfe;border-radius:50%}.toc nav a[aria-current="location"]{color:var(--ink);font-weight:800}.toc nav a[aria-current="location"] i{border-color:var(--blue);background:var(--blue)}.back-top{width:100%;margin-top:18px;padding:16px 0 0;border:0;border-top:1px solid var(--line);color:var(--blue);background:transparent;text-align:right;font-size:13px;font-weight:800;cursor:pointer}.faq{scroll-margin-top:28px;padding-top:64px}.faq>div{border-top:1px solid var(--line);border-bottom:1px solid var(--line)}.faq article+article{border-top:1px solid var(--line)}.faq h3{margin:0}.faq h3 button{display:flex;width:100%;align-items:center;justify-content:space-between;gap:18px;padding:19px 0;border:0;color:var(--ink);background:transparent;text-align:left;font-weight:800;cursor:pointer}.faq h3 b{color:var(--blue);font-size:22px}.faq article>p{padding:0 0 20px;margin:0;color:var(--body);font-size:16px}.author-card{margin-top:64px;padding:32px 36px;border-radius:18px;background:#fff}.article-actions{display:flex;flex-wrap:wrap;justify-content:space-between;gap:14px}.article-actions button{display:inline-flex;min-height:46px;align-items:center;justify-content:center;gap:8px;padding:8px 18px;border:2px solid var(--blue);border-radius:999px;color:var(--ink);background:#fff;font-weight:800;cursor:pointer}.article-actions button:hover{color:#fff;background:var(--blue)}.action-status{min-height:20px;margin:8px 0 0;color:var(--blue);text-align:right;font-size:13px;font-weight:700}.author-grid{display:grid;grid-template-columns:auto 1fr auto;gap:22px;align-items:center;margin-top:24px}.author-portrait{width:88px;height:88px;border-radius:50%;object-fit:cover}.author-initials{display:grid;place-items:center;color:var(--ink);background:#fecdd3;font-size:22px;font-weight:900}.author-grid span{color:#64748b;font-size:13px}.author-grid h2{margin:2px 0;color:var(--ink);font:700 27px/1 var(--serif)}.author-grid h2 a{text-decoration:none}.author-grid p{margin:8px 0;color:#64748b;font-size:14px}.author-grid nav{display:flex;flex-wrap:wrap;gap:14px}.author-grid nav a{color:#64748b;font-size:12px}.author-more{display:inline-flex;min-height:48px;align-items:center;justify-content:center;padding:10px 24px;border:2px solid var(--blue);color:var(--blue);text-align:center;text-decoration:none;font-size:13px;font-weight:800}.author-bio{margin:24px 0 0!important;padding-top:20px;border-top:1px solid var(--line);color:var(--body);font-size:16px}dialog{width:min(720px,calc(100% - 30px));max-height:calc(100dvh - 30px);padding:0;border:0;border-radius:18px;color:var(--ink);background:#fff;box-shadow:0 24px 70px #10213d38}dialog::backdrop{background:#0f172a7a;backdrop-filter:blur(2px)}dialog>div{position:relative;padding:30px}dialog h2{margin:0 50px 2px 0;font:700 30px/1.1 var(--serif)}dialog>div>p{color:#64748b}.dialog-close{position:absolute;top:13px;right:13px;width:44px;height:44px;border:0;border-radius:50%;color:var(--blue);background:transparent;font-size:29px;cursor:pointer}dialog section{border-top:1px solid var(--line)}dialog section button{display:block;width:100%;padding:17px 5px;border:0;border-bottom:1px solid var(--line);color:var(--body);background:#fff;text-align:left;line-height:1.65;cursor:pointer}.citation-status{min-height:22px;margin:14px 0 0!important;color:var(--blue)!important;font-size:13px;font-weight:800}.generated{max-width:1156px;margin:-52px auto 50px;padding:0 42px;color:#94a3b8;font-size:11px}@media(max-width:900px){.layout{display:block}.rail{display:none}.hero-inner{padding-bottom:116px}}@media(max-width:620px){.hero-inner{padding:36px 20px 104px}.hero h1{font-size:46px}.hero .dek{font-size:17px}.layout{margin-top:-70px;padding:0 16px 54px}.takeaways{padding:28px 24px}.article-section h2,.faq h2{font-size:32px}.author-card{padding:24px}.article-actions{display:grid;justify-content:start}.author-grid{grid-template-columns:1fr;gap:12px}.author-more{width:100%}dialog>div{padding:24px}.generated{padding:0 20px}}
79166
79679
  </style></head><body data-theme="${escapeHtml12(config.theme)}" data-scheduled-renderer="blog_article_v1"><div class="progress" aria-hidden="true"></div><header class="hero"><div class="hero-inner"><nav class="breadcrumb" aria-label="Breadcrumb"><a href="${safeHref4(input.canonicalUrl)}">${escapeHtml12(input.category)}</a><span>/</span><span>Article</span></nav><span class="eyebrow">${escapeHtml12(input.eyebrow)}</span><h1>${escapeHtml12(input.title)}</h1>${input.dek ? `<p class="dek">${escapeHtml12(input.dek)}</p>` : ""}<div class="contributors">${contributorText}<div class="meta"><time datetime="${escapeHtml12(articleDate)}">Updated ${formatDate2(articleDate)}</time><span>\u2022</span><span>${input.readTimeMinutes} min read</span>${input.reviewer ? "<span>\u2022</span><strong>Expert reviewed</strong>" : ""}</div></div></div></header><main class="layout"><article class="main"><section class="takeaways"><span class="eyebrow">The short answer</span><h2>Key takeaways</h2><ul>${input.keyTakeaways.map((item) => `<li>${escapeHtml12(item)}</li>`).join("")}</ul></section><section class="intro">${input.introduction.map((paragraph) => `<p>${escapeHtml12(paragraph)}</p>`).join("")}</section>${sections}${faq}${author}</article><div class="rail">${media}${disclosure}${toc}</div></main>${config.showGeneratedAt ? `<p class="generated">Rendered ${formatDate2(generatedAtIso)} by MCP Scraper.</p>` : ""}${citationDialog}<script>(()=>{const citations=${jsonForScript(citationValues)};const canonical=${jsonForScript(input.canonicalUrl)};const title=${jsonForScript(input.title)};const progress=document.querySelector('.progress');const tocLinks=[...document.querySelectorAll('.toc nav a')];const targets=tocLinks.map(link=>document.querySelector(link.hash)).filter(Boolean);const update=()=>{const max=document.documentElement.scrollHeight-innerHeight;if(progress)progress.style.width=(max>0?scrollY/max*100:0)+'%';let active=targets[0]?.id;for(const target of targets){if(target.getBoundingClientRect().top<=innerHeight*.55)active=target.id}tocLinks.forEach(link=>link.toggleAttribute('aria-current',link.hash==='#'+active));tocLinks.forEach(link=>{if(link.hash==='#'+active)link.setAttribute('aria-current','location');else link.removeAttribute('aria-current')})};addEventListener('scroll',update,{passive:true});addEventListener('resize',update);update();document.querySelectorAll('.faq h3 button').forEach(button=>button.addEventListener('click',()=>{const panel=document.getElementById(button.getAttribute('aria-controls'));const open=button.getAttribute('aria-expanded')==='true';button.setAttribute('aria-expanded',String(!open));panel.hidden=open;button.querySelector('b').textContent=open?'+':'\u2212'}));const disclosure=document.querySelector('.disclosure button');disclosure?.addEventListener('click',()=>{const panel=document.getElementById(disclosure.getAttribute('aria-controls'));const open=disclosure.getAttribute('aria-expanded')==='true';disclosure.setAttribute('aria-expanded',String(!open));panel.hidden=open});const tocToggle=document.querySelector('.toc header button');tocToggle?.addEventListener('click',()=>{const nav=document.getElementById('toc-links');const open=tocToggle.getAttribute('aria-expanded')==='true';tocToggle.setAttribute('aria-expanded',String(!open));nav.hidden=open;tocToggle.textContent=open?'\u2304':'\u2303'});document.querySelector('.back-top')?.addEventListener('click',()=>scrollTo({top:0,behavior:'smooth'}));const copy=async text=>{try{await navigator.clipboard.writeText(text)}catch{const area=document.createElement('textarea');area.value=text;area.style.position='fixed';area.style.opacity='0';document.body.append(area);area.select();document.execCommand('copy');area.remove()}};const dialog=document.getElementById('citation-dialog');document.querySelector('[data-cite]')?.addEventListener('click',()=>dialog.showModal());document.querySelector('.dialog-close')?.addEventListener('click',()=>dialog.close());dialog?.addEventListener('click',event=>{if(event.target===dialog)dialog.close()});document.querySelectorAll('[data-citation]').forEach(button=>button.addEventListener('click',async()=>{const style=button.dataset.citation;await copy(citations[style]);document.querySelector('.citation-status').textContent=style+' citation copied to clipboard.'}));document.querySelector('[data-share]')?.addEventListener('click',async()=>{const status=document.querySelector('.action-status');if(navigator.share){try{await navigator.share({title,url:canonical});status.textContent='Sharing options opened.';return}catch(error){if(error.name==='AbortError')return}}await copy(canonical);status.textContent='Article link copied to clipboard.'})})()</script></body></html>`;
79167
79680
  const bytes = Buffer.byteLength(html);
79168
- const sha2565 = (0, import_node_crypto57.createHash)("sha256").update(html).digest("hex");
79681
+ const sha2565 = (0, import_node_crypto58.createHash)("sha256").update(html).digest("hex");
79169
79682
  return {
79170
79683
  html,
79171
79684
  filename: "blog-article.html",
@@ -79175,11 +79688,11 @@ function renderBlogArticleV1(input, config, generatedAt) {
79175
79688
  generatedAt: generatedAtIso
79176
79689
  };
79177
79690
  }
79178
- var import_node_crypto57;
79691
+ var import_node_crypto58;
79179
79692
  var init_render4 = __esm({
79180
79693
  "src/blog-article/render.ts"() {
79181
79694
  "use strict";
79182
- import_node_crypto57 = require("crypto");
79695
+ import_node_crypto58 = require("crypto");
79183
79696
  }
79184
79697
  });
79185
79698
 
@@ -79510,7 +80023,7 @@ function policy6() {
79510
80023
  };
79511
80024
  }
79512
80025
  function runStorageSegment(runId) {
79513
- return /^[a-zA-Z0-9_-]{1,160}$/.test(runId) ? runId : `run-${(0, import_node_crypto58.createHash)("sha256").update(runId).digest("hex").slice(0, 32)}`;
80026
+ return /^[a-zA-Z0-9_-]{1,160}$/.test(runId) ? runId : `run-${(0, import_node_crypto59.createHash)("sha256").update(runId).digest("hex").slice(0, 32)}`;
79514
80027
  }
79515
80028
  async function createScheduledRunArtifact(args) {
79516
80029
  if (args.rendered.bytes > SCHEDULED_RUN_ARTIFACT_MAX_BYTES) {
@@ -79551,12 +80064,12 @@ async function readScheduledRunArtifact(args) {
79551
80064
  if (!window2 || window2.nextOffset !== null || window2.totalBytes > SCHEDULED_RUN_ARTIFACT_MAX_BYTES) return null;
79552
80065
  return window2.text;
79553
80066
  }
79554
- var import_node_crypto58, SCHEDULED_RUN_ARTIFACT_PREFIX, SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS, SCHEDULED_RUN_ARTIFACT_MAX_BYTES;
80067
+ var import_node_crypto59, SCHEDULED_RUN_ARTIFACT_PREFIX, SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS, SCHEDULED_RUN_ARTIFACT_MAX_BYTES;
79555
80068
  var init_scheduled_run_artifact_store = __esm({
79556
80069
  "src/scheduled-artifacts/scheduled-run-artifact-store.ts"() {
79557
80070
  "use strict";
79558
80071
  init_private_artifacts();
79559
- import_node_crypto58 = require("crypto");
80072
+ import_node_crypto59 = require("crypto");
79560
80073
  SCHEDULED_RUN_ARTIFACT_PREFIX = "scheduled-run-artifacts/";
79561
80074
  SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS = 15 * 60 * 1e3;
79562
80075
  SCHEDULED_RUN_ARTIFACT_MAX_BYTES = 2e6;
@@ -79774,7 +80287,7 @@ async function reconcileDiscoveredNangoConnections(identity, discovered) {
79774
80287
  updated_at = excluded.updated_at
79775
80288
  `,
79776
80289
  args: [
79777
- (0, import_node_crypto59.randomUUID)(),
80290
+ (0, import_node_crypto60.randomUUID)(),
79778
80291
  userId,
79779
80292
  connection.providerConfigKey,
79780
80293
  connection.provider,
@@ -79845,7 +80358,7 @@ async function recordServiceConnectionHealth(args) {
79845
80358
  });
79846
80359
  await getDb().execute({
79847
80360
  sql: `INSERT INTO service_connection_health_events (id, connection_id, operational_status, failure_code, retryable, evidence_source) VALUES (?, ?, ?, ?, ?, ?)`,
79848
- args: [(0, import_node_crypto59.randomUUID)(), args.connectionId, args.operationalStatus, args.failureCode ?? null, args.retryable == null ? null : args.retryable ? 1 : 0, args.evidenceSource]
80361
+ args: [(0, import_node_crypto60.randomUUID)(), args.connectionId, args.operationalStatus, args.failureCode ?? null, args.retryable == null ? null : args.retryable ? 1 : 0, args.evidenceSource]
79849
80362
  });
79850
80363
  }
79851
80364
  async function setServiceConnectionActions(identity, connectionId, enabled) {
@@ -79885,7 +80398,7 @@ async function claimServiceConnectionAction(args) {
79885
80398
  if (!connection) throw new Error("service_connection_not_found");
79886
80399
  const inserted = await getDb().execute({
79887
80400
  sql: `INSERT OR IGNORE INTO service_connection_action_audit (id, connection_id, user_id, tool, request_id, status, request_digest) VALUES (?, ?, ?, ?, ?, 'started', ?)`,
79888
- args: [(0, import_node_crypto59.randomUUID)(), connection.id, connection.userId, args.tool, args.requestId, args.requestDigest]
80401
+ args: [(0, import_node_crypto60.randomUUID)(), connection.id, connection.userId, args.tool, args.requestId, args.requestDigest]
79889
80402
  });
79890
80403
  if (Number(inserted.rowsAffected ?? 0) === 1) return { claimed: true };
79891
80404
  const existing = await getDb().execute({
@@ -79910,11 +80423,11 @@ async function claimServiceConnectionAction(args) {
79910
80423
  ...result !== void 0 ? { result } : {}
79911
80424
  };
79912
80425
  }
79913
- var import_node_crypto59, schemaReady3, schemaDb6;
80426
+ var import_node_crypto60, schemaReady3, schemaDb6;
79914
80427
  var init_service_connections = __esm({
79915
80428
  "src/api/service-connections.ts"() {
79916
80429
  "use strict";
79917
- import_node_crypto59 = require("crypto");
80430
+ import_node_crypto60 = require("crypto");
79918
80431
  init_db();
79919
80432
  schemaReady3 = null;
79920
80433
  schemaDb6 = null;
@@ -79928,8 +80441,8 @@ function signingSecret() {
79928
80441
  return secret2;
79929
80442
  }
79930
80443
  function schedulerIntegrationSignature(args) {
79931
- const bodyHash = (0, import_node_crypto60.createHash)("sha256").update(args.body).digest("hex");
79932
- return (0, import_node_crypto60.createHmac)("sha256", args.secret).update(`${args.method.toUpperCase()}
80444
+ const bodyHash = (0, import_node_crypto61.createHash)("sha256").update(args.body).digest("hex");
80445
+ return (0, import_node_crypto61.createHmac)("sha256", args.secret).update(`${args.method.toUpperCase()}
79933
80446
  ${args.path}
79934
80447
  ${args.timestamp}
79935
80448
  ${args.nonce}
@@ -79976,17 +80489,17 @@ async function verifySchedulerIntegrationRequest(request, rawBody) {
79976
80489
  });
79977
80490
  const suppliedBytes = Buffer.from(signature, "hex");
79978
80491
  const expectedBytes = Buffer.from(expected, "hex");
79979
- if (suppliedBytes.length !== expectedBytes.length || !(0, import_node_crypto60.timingSafeEqual)(suppliedBytes, expectedBytes)) {
80492
+ if (suppliedBytes.length !== expectedBytes.length || !(0, import_node_crypto61.timingSafeEqual)(suppliedBytes, expectedBytes)) {
79980
80493
  throw new SchedulerIntegrationAuthError("invalid_signature");
79981
80494
  }
79982
80495
  await claimNonce(nonce, timestampMs);
79983
80496
  return { requestId };
79984
80497
  }
79985
- var import_node_crypto60, MAX_CLOCK_SKEW_MS, SchedulerIntegrationAuthError;
80498
+ var import_node_crypto61, MAX_CLOCK_SKEW_MS, SchedulerIntegrationAuthError;
79986
80499
  var init_scheduler_integration_auth = __esm({
79987
80500
  "src/api/scheduler-integration-auth.ts"() {
79988
80501
  "use strict";
79989
- import_node_crypto60 = require("crypto");
80502
+ import_node_crypto61 = require("crypto");
79990
80503
  init_db();
79991
80504
  init_service_connections();
79992
80505
  MAX_CLOCK_SKEW_MS = 5 * 60 * 1e3;
@@ -80148,7 +80661,7 @@ function requestedSiteMaxPages(maxPages) {
80148
80661
  return Math.min(ABSOLUTE_SITE_MAX_PAGES, Math.max(1, maxPages ?? DEFAULT_SITE_MAX_PAGES));
80149
80662
  }
80150
80663
  function shouldRunSiteExtractInBackground(input) {
80151
- return input.background === true || input.downloadImages === true || input.preserveMedia === true || requestedSiteMaxPages(input.maxPages) > MAX_SYNCHRONOUS_SITE_PAGES;
80664
+ return input.background === true || input.downloadImages === true || input.preserveMedia === true || input.renderJavaScript === true || input.captureRenderedDom === true || input.semanticSimilarity === true || requestedSiteMaxPages(input.maxPages) > MAX_SYNCHRONOUS_SITE_PAGES;
80152
80665
  }
80153
80666
  var MAX_SYNCHRONOUS_SITE_PAGES, DEFAULT_SITE_MAX_PAGES, ABSOLUTE_SITE_MAX_PAGES;
80154
80667
  var init_site_extract_policy = __esm({
@@ -80294,7 +80807,7 @@ async function fetchMedia(rawUrl, expectedType) {
80294
80807
  finalUrl: checked.parsed.href,
80295
80808
  width: dimensions?.width ?? null,
80296
80809
  height: dimensions?.height ?? null,
80297
- sha256: (0, import_node_crypto61.createHash)("sha256").update(bytes).digest("hex")
80810
+ sha256: (0, import_node_crypto62.createHash)("sha256").update(bytes).digest("hex")
80298
80811
  };
80299
80812
  }
80300
80813
  throw new Error("media_redirect_rejected");
@@ -80408,7 +80921,7 @@ async function packagePageMedia(args) {
80408
80921
  files.push({ path: "summary.json", content: Buffer.from(JSON.stringify(summary, null, 2)) });
80409
80922
  files.push({ path: "media.jsonl", content: Buffer.from(cleanAssets.map((asset) => JSON.stringify(asset)).join("\n") + "\n") });
80410
80923
  const archive = await zipBuffer2(files);
80411
- const id = (0, import_node_crypto61.randomBytes)(6).toString("hex");
80924
+ const id = (0, import_node_crypto62.randomBytes)(6).toString("hex");
80412
80925
  const pointer = await createPrivateArtifact({
80413
80926
  policy: policy7(),
80414
80927
  ownerId: args.ownerId,
@@ -80421,11 +80934,11 @@ async function packagePageMedia(args) {
80421
80934
  const localPath = token5() ? null : (0, import_node_path22.join)(process.env.MCP_SCRAPER_OUTPUT_DIR?.trim() || (0, import_node_path22.join)((0, import_node_os15.homedir)(), "Downloads", "mcp-scraper"), "blobs", pointer.artifactId);
80422
80935
  return { media: args.media, artifact: { ...pointer, localPath } };
80423
80936
  }
80424
- var import_node_crypto61, import_node_os15, import_node_path22, import_p_limit7, import_yazl3, PAGE_MEDIA_ARTIFACT_PREFIX, PAGE_MEDIA_ARTIFACT_TTL_MS, PAGE_MEDIA_DOWNLOAD_TTL_MS, MAX_FILE_BYTES, MAX_ARCHIVE_MEDIA_BYTES, MAX_INLINE_IMAGE_BYTES2, MAX_INLINE_TOTAL_BYTES2, DOWNLOAD_CONCURRENCY2, MAX_REDIRECTS3;
80937
+ var import_node_crypto62, import_node_os15, import_node_path22, import_p_limit7, import_yazl3, PAGE_MEDIA_ARTIFACT_PREFIX, PAGE_MEDIA_ARTIFACT_TTL_MS, PAGE_MEDIA_DOWNLOAD_TTL_MS, MAX_FILE_BYTES, MAX_ARCHIVE_MEDIA_BYTES, MAX_INLINE_IMAGE_BYTES2, MAX_INLINE_TOTAL_BYTES2, DOWNLOAD_CONCURRENCY2, MAX_REDIRECTS3;
80425
80938
  var init_page_media_artifacts = __esm({
80426
80939
  "src/api/page-media-artifacts.ts"() {
80427
80940
  "use strict";
80428
- import_node_crypto61 = require("crypto");
80941
+ import_node_crypto62 = require("crypto");
80429
80942
  import_node_os15 = require("os");
80430
80943
  import_node_path22 = require("path");
80431
80944
  import_p_limit7 = __toESM(require("p-limit"), 1);
@@ -80534,7 +81047,7 @@ var init_site_extract_reconciliation = __esm({
80534
81047
 
80535
81048
  // src/api/page-diff.ts
80536
81049
  function sha256Hex(value) {
80537
- return (0, import_node_crypto62.createHash)("sha256").update(value).digest("hex");
81050
+ return (0, import_node_crypto63.createHash)("sha256").update(value).digest("hex");
80538
81051
  }
80539
81052
  function truncateForStorage(value, maxChars = MAX_SNAPSHOT_CONTENT_CHARS) {
80540
81053
  if (value.length <= maxChars) return { value, truncated: false };
@@ -80591,11 +81104,11 @@ function diffPageContent(oldContent, newContent) {
80591
81104
  totalChangedLineCount
80592
81105
  };
80593
81106
  }
80594
- var import_node_crypto62, import_diff, MAX_SNAPSHOT_CONTENT_CHARS, MAX_DIFF_HUNKS, MAX_DIFF_LINES_PER_RESPONSE;
81107
+ var import_node_crypto63, import_diff, MAX_SNAPSHOT_CONTENT_CHARS, MAX_DIFF_HUNKS, MAX_DIFF_LINES_PER_RESPONSE;
80595
81108
  var init_page_diff = __esm({
80596
81109
  "src/api/page-diff.ts"() {
80597
81110
  "use strict";
80598
- import_node_crypto62 = require("crypto");
81111
+ import_node_crypto63 = require("crypto");
80599
81112
  import_diff = require("diff");
80600
81113
  MAX_SNAPSHOT_CONTENT_CHARS = 25e4;
80601
81114
  MAX_DIFF_HUNKS = 200;
@@ -80669,7 +81182,7 @@ var init_scrape_vault_sink = __esm({
80669
81182
 
80670
81183
  // src/api/scrape-image-sink.ts
80671
81184
  function idempotencyKey3(userId, vault, input) {
80672
- return `scrape-image-${(0, import_node_crypto63.createHash)("sha256").update(`${userId}\0${vault}\0${input.sourceKind}\0${input.sourceUrl}\0${input.imageUrl ?? ""}\0${input.imageBase64 ?? ""}`).digest("hex")}`;
81185
+ return `scrape-image-${(0, import_node_crypto64.createHash)("sha256").update(`${userId}\0${vault}\0${input.sourceKind}\0${input.sourceUrl}\0${input.imageUrl ?? ""}\0${input.imageBase64 ?? ""}`).digest("hex")}`;
80673
81186
  }
80674
81187
  async function persistScrapeImagesToMemory(user, inputs, vault) {
80675
81188
  const selected = inputs.slice(0, MAX_IMAGES_PER_SCRAPE);
@@ -80711,11 +81224,11 @@ async function persistScrapeImagesToMemory(user, inputs, vault) {
80711
81224
  assets
80712
81225
  };
80713
81226
  }
80714
- var import_node_crypto63, MAX_IMAGES_PER_SCRAPE;
81227
+ var import_node_crypto64, MAX_IMAGES_PER_SCRAPE;
80715
81228
  var init_scrape_image_sink = __esm({
80716
81229
  "src/api/scrape-image-sink.ts"() {
80717
81230
  "use strict";
80718
- import_node_crypto63 = require("crypto");
81231
+ import_node_crypto64 = require("crypto");
80719
81232
  init_memory();
80720
81233
  MAX_IMAGES_PER_SCRAPE = 25;
80721
81234
  }
@@ -81168,7 +81681,7 @@ function canonicalJson(value) {
81168
81681
  return JSON.stringify(value);
81169
81682
  }
81170
81683
  function sha2563(value) {
81171
- return (0, import_node_crypto64.createHash)("sha256").update(value).digest("hex");
81684
+ return (0, import_node_crypto65.createHash)("sha256").update(value).digest("hex");
81172
81685
  }
81173
81686
  function sensitiveKey(key) {
81174
81687
  const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
@@ -81383,11 +81896,11 @@ async function importServiceConnectionToMemory(identity, input, dependencies) {
81383
81896
  ...!searchReady ? { warning: "The snapshot was stored, but no search chunks were indexed yet." } : {}
81384
81897
  };
81385
81898
  }
81386
- var import_node_crypto64, CONNECTION_MEMORY_IMPORT_MAX_ARGS_BYTES, CONNECTION_MEMORY_IMPORT_MAX_RESULT_BYTES, CONNECTION_MEMORY_IMPORT_MAX_STRING_CHARS, CONNECTION_MEMORY_IMPORT_MAX_DEPTH, ConnectionMemoryImportError;
81899
+ var import_node_crypto65, CONNECTION_MEMORY_IMPORT_MAX_ARGS_BYTES, CONNECTION_MEMORY_IMPORT_MAX_RESULT_BYTES, CONNECTION_MEMORY_IMPORT_MAX_STRING_CHARS, CONNECTION_MEMORY_IMPORT_MAX_DEPTH, ConnectionMemoryImportError;
81387
81900
  var init_connection_memory_import = __esm({
81388
81901
  "src/api/connection-memory-import.ts"() {
81389
81902
  "use strict";
81390
- import_node_crypto64 = require("crypto");
81903
+ import_node_crypto65 = require("crypto");
81391
81904
  init_slugify();
81392
81905
  CONNECTION_MEMORY_IMPORT_MAX_ARGS_BYTES = 64 * 1024;
81393
81906
  CONNECTION_MEMORY_IMPORT_MAX_RESULT_BYTES = 1e6;
@@ -81471,7 +81984,7 @@ var init_scrape_blob_cleanup = __esm({
81471
81984
 
81472
81985
  // src/api/site-export-reader.ts
81473
81986
  function sha2564(value) {
81474
- return (0, import_node_crypto65.createHash)("sha256").update(value).digest("hex");
81987
+ return (0, import_node_crypto66.createHash)("sha256").update(value).digest("hex");
81475
81988
  }
81476
81989
  function publicPageRecord(page) {
81477
81990
  const { bodyMarkdown: _body, contentRef: _contentRef, discoveryLinks: _discovery, ...metadata } = page;
@@ -81579,11 +82092,11 @@ async function readOwnedSiteExportImage(input) {
81579
82092
  if (!bytes || artifact.sha256 && sha2564(bytes) !== artifact.sha256) return null;
81580
82093
  return { bytes, artifact };
81581
82094
  }
81582
- var import_node_crypto65, SiteExportFormatUnavailableError;
82095
+ var import_node_crypto66, SiteExportFormatUnavailableError;
81583
82096
  var init_site_export_reader = __esm({
81584
82097
  "src/api/site-export-reader.ts"() {
81585
82098
  "use strict";
81586
- import_node_crypto65 = require("crypto");
82099
+ import_node_crypto66 = require("crypto");
81587
82100
  init_site_extract_repository();
81588
82101
  init_site_extract_content_store();
81589
82102
  init_site_extract_artifacts();
@@ -81734,7 +82247,7 @@ function finitePositive(value, fallback) {
81734
82247
  return Number.isFinite(value) && value > 0 ? value : fallback;
81735
82248
  }
81736
82249
  async function collectConnectedDataExport(args) {
81737
- const exportId = (0, import_node_crypto66.randomUUID)();
82250
+ const exportId = (0, import_node_crypto67.randomUUID)();
81738
82251
  const now = args.now ?? Date.now;
81739
82252
  const startedAt = now();
81740
82253
  const budgetMs = finitePositive(CONNECTED_DATA_EXPORT_BUDGET_MS, 24e4);
@@ -81848,11 +82361,11 @@ ${lines.length ? `${lines.join("\n")}
81848
82361
  untrustedContent: true
81849
82362
  };
81850
82363
  }
81851
- var import_node_crypto66, CONNECTED_DATA_INLINE_BUDGET_BYTES, CONNECTED_DATA_MAX_EXPORT_BYTES, CONNECTED_DATA_EXPORT_BUDGET_MS, CONNECTED_DATA_PAGE_START_HEADROOM_MS, CONNECTED_DATA_DATASETS, ConnectedDataExportValidationError;
82364
+ var import_node_crypto67, CONNECTED_DATA_INLINE_BUDGET_BYTES, CONNECTED_DATA_MAX_EXPORT_BYTES, CONNECTED_DATA_EXPORT_BUDGET_MS, CONNECTED_DATA_PAGE_START_HEADROOM_MS, CONNECTED_DATA_DATASETS, ConnectedDataExportValidationError;
81852
82365
  var init_connected_data_export = __esm({
81853
82366
  "src/api/connected-data-export.ts"() {
81854
82367
  "use strict";
81855
- import_node_crypto66 = require("crypto");
82368
+ import_node_crypto67 = require("crypto");
81856
82369
  CONNECTED_DATA_INLINE_BUDGET_BYTES = Number(
81857
82370
  process.env.MCP_SCRAPER_CONNECTED_DATA_INLINE_BUDGET_BYTES ?? 5e4
81858
82371
  );
@@ -81966,7 +82479,7 @@ async function exportSearchConsoleTableData(args) {
81966
82479
  offset += rows.length;
81967
82480
  if (stoppedForBytes || rows.length < limit || offset >= matchedRows) break;
81968
82481
  }
81969
- const exportId = (0, import_node_crypto67.randomUUID)();
82482
+ const exportId = (0, import_node_crypto68.randomUUID)();
81970
82483
  const artifact = await args.writeArtifact({
81971
82484
  ownerId: args.ownerId,
81972
82485
  exportId,
@@ -81988,11 +82501,11 @@ async function exportSearchConsoleTableData(args) {
81988
82501
  warnings
81989
82502
  };
81990
82503
  }
81991
- var import_node_crypto67, SEARCH_CONSOLE_TABLE_EXPORT_MAX_ROWS, SEARCH_CONSOLE_TABLE_EXPORT_PAGE_SIZE, SEARCH_CONSOLE_TABLE_EXPORT_MAX_BYTES, SEARCH_CONSOLE_TABLE_COLUMNS, SearchConsoleTableExportValidationError;
82504
+ var import_node_crypto68, SEARCH_CONSOLE_TABLE_EXPORT_MAX_ROWS, SEARCH_CONSOLE_TABLE_EXPORT_PAGE_SIZE, SEARCH_CONSOLE_TABLE_EXPORT_MAX_BYTES, SEARCH_CONSOLE_TABLE_COLUMNS, SearchConsoleTableExportValidationError;
81992
82505
  var init_search_console_table_export = __esm({
81993
82506
  "src/api/search-console-table-export.ts"() {
81994
82507
  "use strict";
81995
- import_node_crypto67 = require("crypto");
82508
+ import_node_crypto68 = require("crypto");
81996
82509
  SEARCH_CONSOLE_TABLE_EXPORT_MAX_ROWS = 5e4;
81997
82510
  SEARCH_CONSOLE_TABLE_EXPORT_PAGE_SIZE = 2e3;
81998
82511
  SEARCH_CONSOLE_TABLE_EXPORT_MAX_BYTES = 50 * 1024 * 1024;
@@ -83171,7 +83684,7 @@ async function listNangoToolsDirect(identity, connectionId) {
83171
83684
  });
83172
83685
  const readTools = [...policies.values()].filter((policy8) => policy8.classification === "read").map((policy8) => policy8.name);
83173
83686
  const actionTools = [...policies.values()].filter((policy8) => policy8.classification === "action").map((policy8) => policy8.name);
83174
- const revision = (0, import_node_crypto68.createHash)("sha256").update(JSON.stringify(tools.map((tool) => ({ name: tool.name, inputSchema: tool.inputSchema })))).digest("hex");
83687
+ const revision = (0, import_node_crypto69.createHash)("sha256").update(JSON.stringify(tools.map((tool) => ({ name: tool.name, inputSchema: tool.inputSchema })))).digest("hex");
83175
83688
  await updateServiceConnectionTools(connection.id, readTools, actionTools, revision);
83176
83689
  const refreshed = await getOwnedServiceConnection(identity, connection.id);
83177
83690
  return { connection: refreshed ?? { ...connection, readTools, actionTools, toolRevision: revision }, tools };
@@ -83198,8 +83711,8 @@ async function callNangoToolDirect(args) {
83198
83711
  identity: args.identity,
83199
83712
  ratePolicyVersion: CONNECTED_USAGE_RATE_POLICY_VERSION
83200
83713
  });
83201
- const requestId = args.requestId?.trim() || (0, import_node_crypto68.randomUUID)();
83202
- const idempotencyKey4 = `main-nango:${(0, import_node_crypto68.createHash)("sha256").update(args.identity.toLowerCase()).update("\0").update(connection.id).update("\0").update(args.tool).update("\0").update(requestId).digest("hex")}`;
83714
+ const requestId = args.requestId?.trim() || (0, import_node_crypto69.randomUUID)();
83715
+ const idempotencyKey4 = `main-nango:${(0, import_node_crypto69.createHash)("sha256").update(args.identity.toLowerCase()).update("\0").update(connection.id).update("\0").update(args.tool).update("\0").update(requestId).digest("hex")}`;
83203
83716
  const startedAt = /* @__PURE__ */ new Date();
83204
83717
  const started = performance.now();
83205
83718
  let result;
@@ -83224,7 +83737,7 @@ async function callNangoToolDirect(args) {
83224
83737
  toolName: args.tool,
83225
83738
  operationKind: args.operationKind ?? args.classification,
83226
83739
  outcome: providerError ? "error" : "partial",
83227
- requestId: requestId.length <= 200 ? requestId : (0, import_node_crypto68.createHash)("sha256").update(requestId).digest("hex"),
83740
+ requestId: requestId.length <= 200 ? requestId : (0, import_node_crypto69.createHash)("sha256").update(requestId).digest("hex"),
83228
83741
  startedAt: startedAt.toISOString(),
83229
83742
  completedAt: completedAt.toISOString()
83230
83743
  }
@@ -83266,15 +83779,15 @@ async function describeNangoToolDirect(identity, connectionId, toolName) {
83266
83779
  providerContractHash: MAIN_INTEGRATION_CONTRACT_HASH,
83267
83780
  protocolVersion: null,
83268
83781
  schemaSource: "live_tools_list",
83269
- schemaHash: (0, import_node_crypto68.createHash)("sha256").update(JSON.stringify(projected)).digest("hex"),
83782
+ schemaHash: (0, import_node_crypto69.createHash)("sha256").update(JSON.stringify(projected)).digest("hex"),
83270
83783
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
83271
83784
  };
83272
83785
  }
83273
- var import_node_crypto68, import_client15, DEFAULT_NANGO_MCP_URL, NANGO_TIMEOUT_MS, NANGO_CONNECTION_PAGE_SIZE, NANGO_CONNECTION_MAX_PAGES, MAIN_INTEGRATION_CONTRACT_VERSION, MAIN_INTEGRATION_CONTRACT_HASH, MainNangoTransportError;
83786
+ var import_node_crypto69, import_client15, DEFAULT_NANGO_MCP_URL, NANGO_TIMEOUT_MS, NANGO_CONNECTION_PAGE_SIZE, NANGO_CONNECTION_MAX_PAGES, MAIN_INTEGRATION_CONTRACT_VERSION, MAIN_INTEGRATION_CONTRACT_HASH, MainNangoTransportError;
83274
83787
  var init_main_nango_transport = __esm({
83275
83788
  "src/api/main-nango-transport.ts"() {
83276
83789
  "use strict";
83277
- import_node_crypto68 = require("crypto");
83790
+ import_node_crypto69 = require("crypto");
83278
83791
  import_client15 = require("@modelcontextprotocol/client");
83279
83792
  init_service_connections();
83280
83793
  init_connected_usage_billing();
@@ -83898,8 +84411,8 @@ async function setScheduleConnectionActionsEnabled(identity, connectionId, enabl
83898
84411
  return data.connection.actionsEnabled === true;
83899
84412
  }
83900
84413
  async function callScheduleConnectionAction(identity, connectionId, input, tool, idempotencyKey4) {
83901
- const requestId = `main-connected-action:${(0, import_node_crypto69.createHash)("sha256").update(identity).update("\0").update(idempotencyKey4?.trim() || (0, import_node_crypto69.randomUUID)()).digest("hex")}`;
83902
- const requestDigest = (0, import_node_crypto69.createHash)("sha256").update(connectionId).update("\0").update(tool?.trim() ?? "").update("\0").update(canonicalJson2(input)).digest("hex");
84414
+ const requestId = `main-connected-action:${(0, import_node_crypto70.createHash)("sha256").update(identity).update("\0").update(idempotencyKey4?.trim() || (0, import_node_crypto70.randomUUID)()).digest("hex")}`;
84415
+ const requestDigest = (0, import_node_crypto70.createHash)("sha256").update(connectionId).update("\0").update(tool?.trim() ?? "").update("\0").update(canonicalJson2(input)).digest("hex");
83903
84416
  if (mainOwnsIntegrations()) {
83904
84417
  const selectedTool = tool?.trim();
83905
84418
  if (!selectedTool) throw new NangoControlError("An action tool is required.", 400, "invalid_request", false);
@@ -84050,7 +84563,7 @@ function canonicalJson2(value) {
84050
84563
  return JSON.stringify(value);
84051
84564
  }
84052
84565
  function projectedToolSchemaHash(tool) {
84053
- return (0, import_node_crypto69.createHash)("sha256").update(canonicalJson2(tool)).digest("hex");
84566
+ return (0, import_node_crypto70.createHash)("sha256").update(canonicalJson2(tool)).digest("hex");
84054
84567
  }
84055
84568
  async function describeNangoTool(identity, connectionId, tool, fresh) {
84056
84569
  if (mainOwnsIntegrations()) {
@@ -84327,11 +84840,11 @@ async function callMainOwnedExportPage(identity, input) {
84327
84840
  untrustedContent: true
84328
84841
  };
84329
84842
  }
84330
- var import_node_crypto69, DEFAULT_NANGO_CONTROL_URL, DISABLED_NANGO_TOOLS, CONNECTION_SYNC_REQUIRED_TOOLS, CONNECTION_SYNC_OPTIONAL_TOOLS, NangoControlError, ScheduleConnectionValidationError, SAFE_CONTROL_ERROR_CODES, FIXED_CONTROL_ERROR_MESSAGES, CONTROL_ERROR_CODE_ALIASES;
84843
+ var import_node_crypto70, DEFAULT_NANGO_CONTROL_URL, DISABLED_NANGO_TOOLS, CONNECTION_SYNC_REQUIRED_TOOLS, CONNECTION_SYNC_OPTIONAL_TOOLS, NangoControlError, ScheduleConnectionValidationError, SAFE_CONTROL_ERROR_CODES, FIXED_CONTROL_ERROR_MESSAGES, CONTROL_ERROR_CODE_ALIASES;
84331
84844
  var init_nango_control = __esm({
84332
84845
  "src/api/nango-control.ts"() {
84333
84846
  "use strict";
84334
- import_node_crypto69 = require("crypto");
84847
+ import_node_crypto70 = require("crypto");
84335
84848
  init_connected_data_export();
84336
84849
  init_slack_connected_data_export();
84337
84850
  init_main_nango_transport();
@@ -84687,7 +85200,7 @@ async function callResendRead(identity, connectionId, tool, args) {
84687
85200
  return isRecord5(data) ? data.result ?? data : data;
84688
85201
  }
84689
85202
  async function callResendAction(identity, connectionId, tool, input, idempotencyKey4) {
84690
- const requestId = `main-resend-action:${(0, import_node_crypto70.createHash)("sha256").update(identity).update("\0").update(idempotencyKey4.trim()).digest("hex")}`;
85203
+ const requestId = `main-resend-action:${(0, import_node_crypto71.createHash)("sha256").update(identity).update("\0").update(idempotencyKey4.trim()).digest("hex")}`;
84691
85204
  const body = await controlRequest2("/api/internal/resend/actions/call", {
84692
85205
  method: "POST",
84693
85206
  headers: { "x-request-id": requestId },
@@ -84752,12 +85265,12 @@ async function callResendExportPage(identity, input) {
84752
85265
  untrustedContent: true
84753
85266
  };
84754
85267
  }
84755
- var import_node_crypto70, DEFAULT_CONNECTION_CONTROL_URL, RESEND_PROVIDER_CONFIG_KEY, RESEND_LOGO_URL, RESEND_DOCS_URL, RESEND_ADMIN_BLOCKED_TOOLS, RESEND_CONNECTION_SYNC_REQUIRED_TOOLS, ResendControlError;
85268
+ var import_node_crypto71, DEFAULT_CONNECTION_CONTROL_URL, RESEND_PROVIDER_CONFIG_KEY, RESEND_LOGO_URL, RESEND_DOCS_URL, RESEND_ADMIN_BLOCKED_TOOLS, RESEND_CONNECTION_SYNC_REQUIRED_TOOLS, ResendControlError;
84756
85269
  var init_resend_control = __esm({
84757
85270
  "src/api/resend-control.ts"() {
84758
85271
  "use strict";
84759
85272
  init_connected_data_export();
84760
- import_node_crypto70 = require("crypto");
85273
+ import_node_crypto71 = require("crypto");
84761
85274
  DEFAULT_CONNECTION_CONTROL_URL = "https://mcp-scraper-scheduler.vercel.app";
84762
85275
  RESEND_PROVIDER_CONFIG_KEY = "resend";
84763
85276
  RESEND_LOGO_URL = "https://cdn.resend.com/brand/resend-icon-black.svg";
@@ -85364,7 +85877,7 @@ function settleWithinTickBudget(label, unfinished, work, onDeadlineOrError) {
85364
85877
  );
85365
85878
  });
85366
85879
  }
85367
- var import_resend3, import_node_crypto71, import_hono38, import_hono39, import_factory8, import_cookie2, import_stripe2, secureCookies2, isProduction2, sessionCookieOptions2, requireAllowedOrigin, auth4, sessionAuth, requireIntegrationsTier, requirePaidSchedulingTier, app, deploymentProfile, STRIPE_API_VERSION, SYNC_HARVEST_TIMEOUT_OVERRIDE_MS, CRON_TICK_BUDGET_MS, CRON_TICK_DRAIN_BUDGET_MS;
85880
+ var import_resend3, import_node_crypto72, import_hono38, import_hono39, import_factory8, import_cookie2, import_stripe2, secureCookies2, isProduction2, sessionCookieOptions2, requireAllowedOrigin, auth4, sessionAuth, requireIntegrationsTier, requirePaidSchedulingTier, app, deploymentProfile, STRIPE_API_VERSION, SYNC_HARVEST_TIMEOUT_OVERRIDE_MS, CRON_TICK_BUDGET_MS, CRON_TICK_DRAIN_BUDGET_MS;
85368
85881
  var init_server = __esm({
85369
85882
  "src/api/server.ts"() {
85370
85883
  "use strict";
@@ -85377,7 +85890,7 @@ var init_server = __esm({
85377
85890
  init_og();
85378
85891
  import_resend3 = require("resend");
85379
85892
  init_url_utils();
85380
- import_node_crypto71 = require("crypto");
85893
+ import_node_crypto72 = require("crypto");
85381
85894
  init_kpo_extractor();
85382
85895
  init_screenshot();
85383
85896
  init_media_extractor();
@@ -86938,7 +87451,7 @@ var init_server = __esm({
86938
87451
  if (!harvestOk) return c.json(insufficientBalanceResponse(harvestBal, harvestCost), 402);
86939
87452
  jobId2 = await createJob(user.id, options.query, { ...options, billingHoldMc: harvestCost }, body.callback_url);
86940
87453
  } else {
86941
- jobId2 = (0, import_node_crypto71.randomUUID)();
87454
+ jobId2 = (0, import_node_crypto72.randomUUID)();
86942
87455
  const billingDebitKey = `paa-harvest:${jobId2}:hold`;
86943
87456
  const description = `PAA harvest: ${options.query}`.slice(0, 500);
86944
87457
  const hold = await debitMcIdempotent(
@@ -87773,6 +88286,13 @@ var init_server = __esm({
87773
88286
  const bodyResult = ExtractSiteBodySchema.safeParse(raw);
87774
88287
  if (!bodyResult.success) return c.json({ error: bodyResult.error.issues[0]?.message ?? "Invalid request" }, 400);
87775
88288
  const body = bodyResult.data;
88289
+ if (body.semanticSimilarity && !process.env.JINA_API_KEY?.trim()) {
88290
+ return c.json({
88291
+ error: "Semantic site similarity is not configured on this deployment.",
88292
+ errorCode: "semantic_similarity_unconfigured",
88293
+ retryable: false
88294
+ }, 503);
88295
+ }
87776
88296
  if (body.preserveMedia !== void 0 && body.downloadImages !== void 0 && body.preserveMedia !== body.downloadImages) {
87777
88297
  return c.json({ error: "preserveMedia conflicts with deprecated downloadImages." }, 400);
87778
88298
  }
@@ -87826,7 +88346,12 @@ var init_server = __esm({
87826
88346
  maxPages: requestedMaxPages,
87827
88347
  rotateProxyEvery: body.rotateProxyEvery ?? 10,
87828
88348
  formats: [...body.formats ?? []].sort(),
87829
- downloadImages
88349
+ downloadImages,
88350
+ renderJavaScript: body.renderJavaScript === true,
88351
+ captureRenderedDom: body.captureRenderedDom === true,
88352
+ semanticSimilarity: body.semanticSimilarity === true,
88353
+ similarityThreshold: body.similarityThreshold ?? null,
88354
+ similarityMaxPairs: body.similarityMaxPairs ?? null
87830
88355
  }));
87831
88356
  const prepared = await prepareSiteExtractStart({
87832
88357
  jobId: jobId2,
@@ -87840,6 +88365,11 @@ var init_server = __esm({
87840
88365
  urlsPerBrowser: body.rotateProxyEvery ?? 10,
87841
88366
  formats: body.formats,
87842
88367
  downloadImages,
88368
+ renderJavaScript: body.renderJavaScript === true,
88369
+ captureRenderedDom: body.captureRenderedDom === true,
88370
+ semanticSimilarity: body.semanticSimilarity === true,
88371
+ similarityThreshold: body.similarityThreshold,
88372
+ similarityMaxPairs: body.similarityMaxPairs,
87843
88373
  debitKey: `site-extract:${user.id}:${jobId2}:hold`,
87844
88374
  waybackReplay: waybackReplay && !body.wayback ? {
87845
88375
  timestamp: waybackReplay.timestamp,
@@ -87933,8 +88463,10 @@ var init_server = __esm({
87933
88463
  startUrl: crawlStartUrl,
87934
88464
  maxPages: siteMaxPages,
87935
88465
  seedUrls: waybackCaptures?.map((capture) => capture.rawReplayUrl),
87936
- kernelApiKey: rotateProxies || wantsBranding || body.browserFallback || body.kernelFallback ? browserServiceApiKey() : void 0,
88466
+ kernelApiKey: rotateProxies || wantsBranding || body.browserFallback || body.kernelFallback || body.renderJavaScript || body.captureRenderedDom || body.semanticSimilarity ? browserServiceApiKey() : void 0,
87937
88467
  formats: body.formats,
88468
+ forceBrowserRender: body.renderJavaScript || body.captureRenderedDom || body.semanticSimilarity,
88469
+ captureRenderedDom: body.captureRenderedDom,
87938
88470
  ...rotateProxies ? {
87939
88471
  rotateProxyEvery: body.rotateProxyEvery ?? 30,
87940
88472
  parallelism: concurrencyLimitForUser(user)