mcp-scraper 0.64.1 → 0.65.0

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 +10 -0
  2. package/README.md +2 -2
  3. package/dist/bin/api-server.cjs +1024 -495
  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 +45 -7
  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-5BODYBIP.js +7 -0
  18. package/dist/chunk-5BODYBIP.js.map +1 -0
  19. package/dist/{chunk-BRKCPONM.js → chunk-5PIO7QBG.js} +2 -2
  20. package/dist/{chunk-5QYSY5KI.js → chunk-AMLFKPLL.js} +139 -3
  21. package/dist/chunk-AMLFKPLL.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-PSKRQDGN.js} +46 -8
  25. package/dist/chunk-PSKRQDGN.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-AFEA235I.js} +170 -163
  29. package/dist/server-AFEA235I.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();
@@ -19219,7 +19801,7 @@ async function formatExtractSite(raw, input, ctx) {
19219
19801
  const parsed = parseData(raw);
19220
19802
  if ("error" in parsed) return formattedErrorResult(parsed.error);
19221
19803
  const started = formatBackgroundJobStarted(
19222
- "Multi-Page Site Content Crawl",
19804
+ input.toolLabel ?? "Multi-Page Site Content Crawl",
19223
19805
  parsed.data,
19224
19806
  {
19225
19807
  requested: input.delivery ?? "auto",
@@ -22884,7 +23466,7 @@ function backupProxyAvailable() {
22884
23466
  }
22885
23467
  function randomStickySessionId() {
22886
23468
  let digits = "";
22887
- for (let i = 0; i < 10; i++) digits += String((0, import_node_crypto9.randomInt)(0, 10));
23469
+ for (let i = 0; i < 10; i++) digits += String((0, import_node_crypto10.randomInt)(0, 10));
22888
23470
  return digits;
22889
23471
  }
22890
23472
  function buildBackupUsername(opts = {}) {
@@ -22942,11 +23524,11 @@ async function cleanupBackupProxyId(kernelApiKey, proxyId) {
22942
23524
  } catch {
22943
23525
  }
22944
23526
  }
22945
- var import_node_crypto9, import_sdk7, BACKUP_PROXY_HOST, BACKUP_PROXY_PORT, BACKUP_STICKY_SESSION_MINUTES, BACKUP_LAST_RESORT_ATTEMPTS;
23527
+ var import_node_crypto10, import_sdk7, BACKUP_PROXY_HOST, BACKUP_PROXY_PORT, BACKUP_STICKY_SESSION_MINUTES, BACKUP_LAST_RESORT_ATTEMPTS;
22946
23528
  var init_backup_proxy = __esm({
22947
23529
  "src/backup-proxy.ts"() {
22948
23530
  "use strict";
22949
- import_node_crypto9 = require("crypto");
23531
+ import_node_crypto10 = require("crypto");
22950
23532
  import_sdk7 = __toESM(require("@onkernel/sdk"), 1);
22951
23533
  BACKUP_PROXY_HOST = "pr.oxylabs.io";
22952
23534
  BACKUP_PROXY_PORT = 7777;
@@ -24351,7 +24933,7 @@ function csvRecords(text2) {
24351
24933
  return record;
24352
24934
  });
24353
24935
  }
24354
- function csvCell(value) {
24936
+ function csvCell2(value) {
24355
24937
  if (value === null || value === void 0) return "";
24356
24938
  const text2 = String(value);
24357
24939
  return /[",\n\r]/.test(text2) ? `"${text2.replace(/"/g, '""')}"` : text2;
@@ -24359,7 +24941,7 @@ function csvCell(value) {
24359
24941
  function rowsToCsv(headers, rows) {
24360
24942
  return [
24361
24943
  headers.join(","),
24362
- ...rows.map((row) => headers.map((header) => csvCell(row[header])).join(","))
24944
+ ...rows.map((row) => headers.map((header) => csvCell2(row[header])).join(","))
24363
24945
  ].join("\n") + "\n";
24364
24946
  }
24365
24947
  var init_csv = __esm({
@@ -24755,12 +25337,12 @@ async function getHostedZipGroups(stateInput) {
24755
25337
  async function importHostedZipGroupsCsv(input) {
24756
25338
  await ensureHostedLocationDataSchema();
24757
25339
  const sourceUrl = normalizedSourceUrl(input.sourceUrl);
24758
- const sha2565 = (0, import_node_crypto10.createHash)("sha256").update(input.csv).digest("hex");
25340
+ const sha2565 = (0, import_node_crypto11.createHash)("sha256").update(input.csv).digest("hex");
24759
25341
  const active = await getActiveHostedLocationDataset();
24760
25342
  if (active?.sha256 === sha2565) return { dataset: active, duplicate: true };
24761
25343
  const parsed = parseHostedZipGroupsCsv(input.csv);
24762
25344
  assertNationwideZipCoverage(parsed);
24763
- const id = `loc_${(0, import_node_crypto10.randomUUID)().replaceAll("-", "")}`;
25345
+ const id = `loc_${(0, import_node_crypto11.randomUUID)().replaceAll("-", "")}`;
24764
25346
  const db = getDb();
24765
25347
  await db.execute({
24766
25348
  sql: `
@@ -24828,12 +25410,12 @@ async function importHostedCensusPlacesCsv(input) {
24828
25410
  if (!state) throw new Error("state must be a two-letter US state abbreviation");
24829
25411
  const kind = censusDatasetKind(state);
24830
25412
  const sourceUrl = normalizedSourceUrl(input.sourceUrl);
24831
- const sha2565 = (0, import_node_crypto10.createHash)("sha256").update(input.csv).digest("hex");
25413
+ const sha2565 = (0, import_node_crypto11.createHash)("sha256").update(input.csv).digest("hex");
24832
25414
  const active = await getActiveHostedDataset(kind);
24833
25415
  if (active?.sha256 === sha2565) return { dataset: active, duplicate: true };
24834
25416
  const stateFips = STATE_FIPS_BY_ABBR[state];
24835
25417
  const parsed = parseHostedCensusPlacesCsv(input.csv, stateFips);
24836
- const id = `loc_${(0, import_node_crypto10.randomUUID)().replaceAll("-", "")}`;
25418
+ const id = `loc_${(0, import_node_crypto11.randomUUID)().replaceAll("-", "")}`;
24837
25419
  const db = getDb();
24838
25420
  await db.execute({
24839
25421
  sql: `
@@ -24956,11 +25538,11 @@ async function queryHostedLocationMarkets(input) {
24956
25538
  warnings
24957
25539
  };
24958
25540
  }
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;
25541
+ 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
25542
  var init_location_data_repository = __esm({
24961
25543
  "src/api/location-data-repository.ts"() {
24962
25544
  "use strict";
24963
- import_node_crypto10 = require("crypto");
25545
+ import_node_crypto11 = require("crypto");
24964
25546
  init_db();
24965
25547
  init_csv();
24966
25548
  HOSTED_ZIP_DATASET_KIND = "us_zip_groups";
@@ -26274,7 +26856,7 @@ async function claimDirectoryWorkflowOutbox(input) {
26274
26856
  const workerId = requiredBoundedString(input.workerId, "outbox worker id", 160);
26275
26857
  const limit = Math.max(1, Math.min(100, Math.trunc(input.limit ?? 25)));
26276
26858
  const leaseSeconds = Math.max(30, Math.min(3600, Math.trunc(input.leaseSeconds ?? 120)));
26277
- const claimToken = `${workerId}:${(0, import_node_crypto11.randomUUID)()}`;
26859
+ const claimToken = `${workerId}:${(0, import_node_crypto12.randomUUID)()}`;
26278
26860
  const leaseModifier = `+${leaseSeconds} seconds`;
26279
26861
  const results = await getDb().batch([
26280
26862
  {
@@ -26334,11 +26916,11 @@ async function markDirectoryWorkflowOutboxFailed(input) {
26334
26916
  });
26335
26917
  return Number(result.rowsAffected ?? 0) > 0;
26336
26918
  }
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;
26919
+ 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
26920
  var init_directory_workflow_repository = __esm({
26339
26921
  "src/api/directory-workflow-repository.ts"() {
26340
26922
  "use strict";
26341
- import_node_crypto11 = require("crypto");
26923
+ import_node_crypto12 = require("crypto");
26342
26924
  init_db();
26343
26925
  DIRECTORY_EVENT_NAME = "mcp-scraper/directory.requested";
26344
26926
  TERMINAL_JOB_STATUSES = /* @__PURE__ */ new Set(["succeeded", "partial", "failed"]);
@@ -26452,7 +27034,7 @@ async function acquireConcurrencyGate(user, operation, options = {}) {
26452
27034
  return { ok: true, lockId: null, active: await countActiveUsageForUser(user.id, true), limit, operation, reused: true };
26453
27035
  }
26454
27036
  await expireConcurrencyLocksForUser(user.id);
26455
- const lockId = `cl_${(0, import_node_crypto12.randomUUID)().replace(/-/g, "").slice(0, 24)}`;
27037
+ const lockId = `cl_${(0, import_node_crypto13.randomUUID)().replace(/-/g, "").slice(0, 24)}`;
26456
27038
  const res = await getDb().execute({
26457
27039
  sql: `INSERT INTO concurrency_locks (id, user_id, operation, status, expires_at, metadata)
26458
27040
  SELECT ?, ?, ?, 'active', datetime('now', ?), ?
@@ -26502,11 +27084,11 @@ async function extendConcurrencyGate(lockId, ttlSeconds = DEFAULT_LOCK_TTL_SECON
26502
27084
  args: [lockTtlModifier(ttlSeconds), lockId]
26503
27085
  });
26504
27086
  }
26505
- var import_node_crypto12, DEFAULT_LOCK_TTL_SECONDS, DEFAULT_RETRY_AFTER_SECONDS, MAX_LOCK_TTL_SECONDS;
27087
+ var import_node_crypto13, DEFAULT_LOCK_TTL_SECONDS, DEFAULT_RETRY_AFTER_SECONDS, MAX_LOCK_TTL_SECONDS;
26506
27088
  var init_concurrency_gates = __esm({
26507
27089
  "src/api/concurrency-gates.ts"() {
26508
27090
  "use strict";
26509
- import_node_crypto12 = require("crypto");
27091
+ import_node_crypto13 = require("crypto");
26510
27092
  init_db();
26511
27093
  init_rates();
26512
27094
  DEFAULT_LOCK_TTL_SECONDS = 15 * 60;
@@ -27573,7 +28155,7 @@ var init_PAAExtractor = __esm({
27573
28155
  if (remainingHumanClickDelayMs > 0) await page.waitForTimeout(remainingHumanClickDelayMs);
27574
28156
  return "ok";
27575
28157
  };
27576
- let round = 0;
28158
+ let round2 = 0;
27577
28159
  let growthWaits = 0;
27578
28160
  while (true) {
27579
28161
  if (options.softDeadlineMs && Date.now() >= options.softDeadlineMs) break;
@@ -27601,7 +28183,7 @@ var init_PAAExtractor = __esm({
27601
28183
  continue;
27602
28184
  }
27603
28185
  growthWaits = 0;
27604
- this.reporter.onDepth(++round);
28186
+ this.reporter.onDepth(++round2);
27605
28187
  await this.throwIfCaptcha(page, "Google PAA expansion");
27606
28188
  clickedOnceEver.add(target.q);
27607
28189
  const expansionStatus = await expandOneItemSerially(target.q);
@@ -30138,10 +30720,10 @@ function stableCanonicalJson(value) {
30138
30720
  return JSON.stringify(normalize4(value));
30139
30721
  }
30140
30722
  function leadListEnrichmentFingerprint(input) {
30141
- return (0, import_node_crypto13.createHash)("sha256").update(stableCanonicalJson(input)).digest("hex");
30723
+ return (0, import_node_crypto14.createHash)("sha256").update(stableCanonicalJson(input)).digest("hex");
30142
30724
  }
30143
30725
  function leadListRowInputDigest(row) {
30144
- return (0, import_node_crypto13.createHash)("sha256").update(stableCanonicalJson(row)).digest("hex");
30726
+ return (0, import_node_crypto14.createHash)("sha256").update(stableCanonicalJson(row)).digest("hex");
30145
30727
  }
30146
30728
  function emptyUsage() {
30147
30729
  return { mapsAttempts: 0, pageAttempts: 0, pageSuccesses: 0, serpSearches: 0 };
@@ -30614,7 +31196,7 @@ async function claimLeadListEnrichmentOutbox(input) {
30614
31196
  const workerId = requiredBoundedString2(input.workerId, "outbox worker id", 160);
30615
31197
  const limit = Math.max(1, Math.min(100, Math.trunc(input.limit ?? 25)));
30616
31198
  const leaseSeconds = Math.max(30, Math.min(3600, Math.trunc(input.leaseSeconds ?? 120)));
30617
- const claimToken = `${workerId}:${(0, import_node_crypto13.randomUUID)()}`;
31199
+ const claimToken = `${workerId}:${(0, import_node_crypto14.randomUUID)()}`;
30618
31200
  const results = await getDb().batch([{
30619
31201
  sql: `UPDATE lead_list_enrichment_outbox SET status='dispatching',attempts=attempts+1,locked_by=?,
30620
31202
  locked_until=datetime('now',?),updated_at=datetime('now') WHERE id IN (SELECT id FROM lead_list_enrichment_outbox
@@ -30640,11 +31222,11 @@ async function markLeadListEnrichmentOutboxFailed(input) {
30640
31222
  WHERE id=? AND status='dispatching' AND locked_by=?`, args: [`+${retryAfterSeconds} seconds`, String(input.error).slice(0, 2e3), input.id, input.claimToken] });
30641
31223
  return Number(result.rowsAffected ?? 0) > 0;
30642
31224
  }
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;
31225
+ 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
31226
  var init_lead_list_enrichment_repository = __esm({
30645
31227
  "src/api/lead-list-enrichment-repository.ts"() {
30646
31228
  "use strict";
30647
- import_node_crypto13 = require("crypto");
31229
+ import_node_crypto14 = require("crypto");
30648
31230
  init_db();
30649
31231
  EVENT_NAME = "mcp-scraper/lead-list-enrichment.requested";
30650
31232
  TERMINAL_JOB_STATUSES2 = /* @__PURE__ */ new Set(["complete", "partial", "empty", "failed", "cancelled"]);
@@ -31222,7 +31804,7 @@ var init_paa_harvest_settlement = __esm({
31222
31804
 
31223
31805
  // src/api/serp-identity-db.ts
31224
31806
  async function createSerpIdentityRow(input) {
31225
- const id = `serpi_${(0, import_node_crypto14.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
31807
+ const id = `serpi_${(0, import_node_crypto15.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
31226
31808
  await getDb().execute({
31227
31809
  sql: `INSERT INTO serp_identities
31228
31810
  (id, user_id, name, kernel_profile_name, kernel_proxy_id, proxy_type, country, status)
@@ -31267,11 +31849,11 @@ async function deleteSerpIdentityRow(userId, name) {
31267
31849
  args: [userId, name]
31268
31850
  });
31269
31851
  }
31270
- var import_node_crypto14;
31852
+ var import_node_crypto15;
31271
31853
  var init_serp_identity_db = __esm({
31272
31854
  "src/api/serp-identity-db.ts"() {
31273
31855
  "use strict";
31274
- import_node_crypto14 = require("crypto");
31856
+ import_node_crypto15 = require("crypto");
31275
31857
  init_db();
31276
31858
  }
31277
31859
  });
@@ -32018,7 +32600,7 @@ async function runDurablePaaCapture(input) {
32018
32600
  const { gzipSync: gzipSync2 } = await import("zlib");
32019
32601
  rawDomGzip = gzipSync2(transported);
32020
32602
  }
32021
- rawDomSha256 = (0, import_node_crypto15.createHash)("sha256").update(rawDomGzip).digest("hex");
32603
+ rawDomSha256 = (0, import_node_crypto16.createHash)("sha256").update(rawDomGzip).digest("hex");
32022
32604
  }
32023
32605
  }
32024
32606
  const selected = Array.from(records.values()).slice(0, input.maxQuestions);
@@ -32102,12 +32684,12 @@ async function runDurablePaaCapture(input) {
32102
32684
  }
32103
32685
  throw new Error("paa_work_deadline_exhausted_before_capture");
32104
32686
  }
32105
- var import_sdk9, import_node_crypto15, PAA_INVOCATION_BUDGET_MS, PAA_BROWSER_WORK_BUDGET_MS, PAA_SOLVER_BUDGET_MS, RAW_PREPARE_CODE;
32687
+ var import_sdk9, import_node_crypto16, PAA_INVOCATION_BUDGET_MS, PAA_BROWSER_WORK_BUDGET_MS, PAA_SOLVER_BUDGET_MS, RAW_PREPARE_CODE;
32106
32688
  var init_durable_capture = __esm({
32107
32689
  "src/paa/durable-capture.ts"() {
32108
32690
  "use strict";
32109
32691
  import_sdk9 = __toESM(require("@onkernel/sdk"), 1);
32110
- import_node_crypto15 = require("crypto");
32692
+ import_node_crypto16 = require("crypto");
32111
32693
  init_selectors();
32112
32694
  init_uule();
32113
32695
  PAA_INVOCATION_BUDGET_MS = 28e4;
@@ -32535,7 +33117,7 @@ function mapSubmission(row) {
32535
33117
  async function event(submissionId, eventType, actorKind, actorId, metadata = {}) {
32536
33118
  await getDb().execute({
32537
33119
  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)]
33120
+ args: [(0, import_node_crypto17.randomUUID)(), submissionId, eventType, actorKind, actorId, JSON.stringify(metadata)]
32539
33121
  });
32540
33122
  }
32541
33123
  async function createLocalSourcebookSubmission(input) {
@@ -32549,7 +33131,7 @@ async function createLocalSourcebookSubmission(input) {
32549
33131
  return submission;
32550
33132
  }
32551
33133
  }
32552
- const id = `lsb_${(0, import_node_crypto16.randomUUID)().replace(/-/g, "")}`;
33134
+ const id = `lsb_${(0, import_node_crypto17.randomUUID)().replace(/-/g, "")}`;
32553
33135
  const coverage = {
32554
33136
  requested: ["website_crawl", "structured_data", "services_products", "service_areas", "genuine_images", "staff_team", "review_sources"],
32555
33137
  crawl: { state: "queued", pagesDiscovered: 0, pagesCaptured: 0 },
@@ -32690,11 +33272,11 @@ async function getPublicLocalSourcebook(category, state, slug4) {
32690
33272
  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
33273
  return result.rows[0] ? parseJson3(result.rows[0].payload_json) : null;
32692
33274
  }
32693
- var import_node_crypto16, LOCAL_SOURCEBOOK_CATEGORIES, schemaPromise4, schemaDb4;
33275
+ var import_node_crypto17, LOCAL_SOURCEBOOK_CATEGORIES, schemaPromise4, schemaDb4;
32694
33276
  var init_local_sourcebook_repository = __esm({
32695
33277
  "src/api/local-sourcebook-repository.ts"() {
32696
33278
  "use strict";
32697
- import_node_crypto16 = require("crypto");
33279
+ import_node_crypto17 = require("crypto");
32698
33280
  init_db();
32699
33281
  LOCAL_SOURCEBOOK_CATEGORIES = ["home", "professional", "restaurants", "financial", "realestate", "auto", "wellness"];
32700
33282
  schemaPromise4 = null;
@@ -33846,7 +34428,7 @@ var init_local_sourcebook_schema = __esm({
33846
34428
 
33847
34429
  // src/api/local-sourcebook-compiler.ts
33848
34430
  function digest(value) {
33849
- return (0, import_node_crypto17.createHash)("sha256").update(value).digest("hex").slice(0, 20);
34431
+ return (0, import_node_crypto18.createHash)("sha256").update(value).digest("hex").slice(0, 20);
33850
34432
  }
33851
34433
  function unique(values, limit = 100) {
33852
34434
  const seen = /* @__PURE__ */ new Set();
@@ -34283,11 +34865,11 @@ function compileLocalSourcebookListing(input) {
34283
34865
  }
34284
34866
  };
34285
34867
  }
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;
34868
+ 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
34869
  var init_local_sourcebook_compiler = __esm({
34288
34870
  "src/api/local-sourcebook-compiler.ts"() {
34289
34871
  "use strict";
34290
- import_node_crypto17 = require("crypto");
34872
+ import_node_crypto18 = require("crypto");
34291
34873
  init_local_sourcebook_public_urls();
34292
34874
  init_local_sourcebook_schema();
34293
34875
  CATEGORY_LABELS = {
@@ -34644,6 +35226,11 @@ async function prepareSiteExtractStart(input) {
34644
35226
  urlsPerBrowser: input.urlsPerBrowser,
34645
35227
  formats: input.formats,
34646
35228
  downloadImages: input.downloadImages,
35229
+ renderJavaScript: input.renderJavaScript === true,
35230
+ captureRenderedDom: input.captureRenderedDom === true,
35231
+ semanticSimilarity: input.semanticSimilarity === true,
35232
+ similarityThreshold: input.similarityThreshold,
35233
+ similarityMaxPairs: input.similarityMaxPairs,
34647
35234
  ...input.waybackReplay ? {
34648
35235
  waybackReplay: input.waybackReplay,
34649
35236
  disableLinkDiscovery: true
@@ -35388,15 +35975,15 @@ function normalizeIdempotencyKey(value) {
35388
35975
  return key;
35389
35976
  }
35390
35977
  function tokenFor(id, idempotencyKey4) {
35391
- return (0, import_node_crypto18.createHmac)("sha256", billingSecret()).update(id).update(":").update(idempotencyKey4).digest("base64url");
35978
+ return (0, import_node_crypto19.createHmac)("sha256", billingSecret()).update(id).update(":").update(idempotencyKey4).digest("base64url");
35392
35979
  }
35393
35980
  function tokenHash(token6) {
35394
- return (0, import_node_crypto18.createHash)("sha256").update(token6).digest("hex");
35981
+ return (0, import_node_crypto19.createHash)("sha256").update(token6).digest("hex");
35395
35982
  }
35396
35983
  function verifyToken(row, token6) {
35397
35984
  const expected = Buffer.from(row.token_hash, "hex");
35398
35985
  const actual = Buffer.from(tokenHash(token6), "hex");
35399
- if (expected.length !== actual.length || !(0, import_node_crypto18.timingSafeEqual)(expected, actual)) {
35986
+ if (expected.length !== actual.length || !(0, import_node_crypto19.timingSafeEqual)(expected, actual)) {
35400
35987
  throw new UnifiedBillingError("unauthorized", "invalid billing authorization token", 401);
35401
35988
  }
35402
35989
  }
@@ -35511,7 +36098,7 @@ async function authorizeScheduledRun(args) {
35511
36098
  { balanceMc, requiredMc: SCHEDULED_RUN_BASE_MC }
35512
36099
  );
35513
36100
  }
35514
- const id = (0, import_node_crypto18.randomUUID)();
36101
+ const id = (0, import_node_crypto19.randomUUID)();
35515
36102
  const token6 = tokenFor(id, idempotencyKey4);
35516
36103
  const expiresAt = new Date(Date.now() + AUTHORIZATION_TTL_MS).toISOString();
35517
36104
  const inserted = await getDb().execute({
@@ -35579,7 +36166,7 @@ async function startScheduledRun(args) {
35579
36166
  if (!authorization || !["starting", "started"].includes(authorization.status)) {
35580
36167
  throw new UnifiedBillingError("authorization_closed", "billing authorization is no longer startable", 409);
35581
36168
  }
35582
- const eventId = existing?.id ?? (0, import_node_crypto18.randomUUID)();
36169
+ const eventId = existing?.id ?? (0, import_node_crypto19.randomUUID)();
35583
36170
  if (!existing) {
35584
36171
  await getDb().execute({
35585
36172
  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 +36305,7 @@ async function settleScheduledRun(args) {
35718
36305
  ...modelCostUnreported ? { modelCostUnreported: true } : {},
35719
36306
  ...pendingReason ? { reason: pendingReason } : {}
35720
36307
  });
35721
- const eventId = existing?.id ?? (0, import_node_crypto18.randomUUID)();
36308
+ const eventId = existing?.id ?? (0, import_node_crypto19.randomUUID)();
35722
36309
  const status = pendingReason ? "cost_pending" : "settling";
35723
36310
  const eventValues = [
35724
36311
  modelMc,
@@ -35843,11 +36430,11 @@ async function voidScheduledRunAuthorization(args) {
35843
36430
  }
35844
36431
  return { ok: true, status: authorization.status };
35845
36432
  }
35846
- var import_node_crypto18, SCHEDULED_RUN_BILLING_CLASS, SCHEDULED_RUN_SOURCE_SURFACE, AUTHORIZATION_TTL_MS, UnifiedBillingError;
36433
+ var import_node_crypto19, SCHEDULED_RUN_BILLING_CLASS, SCHEDULED_RUN_SOURCE_SURFACE, AUTHORIZATION_TTL_MS, UnifiedBillingError;
35847
36434
  var init_unified_billing = __esm({
35848
36435
  "src/api/unified-billing.ts"() {
35849
36436
  "use strict";
35850
- import_node_crypto18 = require("crypto");
36437
+ import_node_crypto19 = require("crypto");
35851
36438
  init_db();
35852
36439
  init_rates();
35853
36440
  init_scheduling_access();
@@ -35879,14 +36466,14 @@ function getSessionSecret() {
35879
36466
  function safeEqualHex(a, b) {
35880
36467
  if (a.length !== b.length) return false;
35881
36468
  try {
35882
- return (0, import_node_crypto19.timingSafeEqual)(Buffer.from(a, "hex"), Buffer.from(b, "hex"));
36469
+ return (0, import_node_crypto20.timingSafeEqual)(Buffer.from(a, "hex"), Buffer.from(b, "hex"));
35883
36470
  } catch {
35884
36471
  return false;
35885
36472
  }
35886
36473
  }
35887
36474
  function signSession(userId) {
35888
36475
  const payload = String(userId);
35889
- const sig = (0, import_node_crypto19.createHmac)("sha256", secret()).update(payload).digest("hex");
36476
+ const sig = (0, import_node_crypto20.createHmac)("sha256", secret()).update(payload).digest("hex");
35890
36477
  return `${payload}.${sig}`;
35891
36478
  }
35892
36479
  function verifySession(token6) {
@@ -35894,16 +36481,16 @@ function verifySession(token6) {
35894
36481
  if (dot === -1) return null;
35895
36482
  const payload = token6.slice(0, dot);
35896
36483
  const sig = token6.slice(dot + 1);
35897
- const expected = (0, import_node_crypto19.createHmac)("sha256", secret()).update(payload).digest("hex");
36484
+ const expected = (0, import_node_crypto20.createHmac)("sha256", secret()).update(payload).digest("hex");
35898
36485
  if (!safeEqualHex(sig, expected)) return null;
35899
36486
  const id = parseInt(payload);
35900
36487
  return isNaN(id) ? null : id;
35901
36488
  }
35902
- var import_node_crypto19, isProduction, secret;
36489
+ var import_node_crypto20, isProduction, secret;
35903
36490
  var init_session = __esm({
35904
36491
  "src/api/session.ts"() {
35905
36492
  "use strict";
35906
- import_node_crypto19 = require("crypto");
36493
+ import_node_crypto20 = require("crypto");
35907
36494
  isProduction = () => process.env.NODE_ENV === "production" || process.env.VERCEL === "1";
35908
36495
  secret = () => getSessionSecret();
35909
36496
  }
@@ -35921,11 +36508,11 @@ function isMemoryOperator(email) {
35921
36508
  return ops.includes(email.trim().toLowerCase());
35922
36509
  }
35923
36510
  function encKey() {
35924
- return (0, import_node_crypto20.scryptSync)(getSessionSecret(), "mcp-memory-key-v1", 32);
36511
+ return (0, import_node_crypto21.scryptSync)(getSessionSecret(), "mcp-memory-key-v1", 32);
35925
36512
  }
35926
36513
  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);
36514
+ const iv = (0, import_node_crypto21.randomBytes)(12);
36515
+ const cipher = (0, import_node_crypto21.createCipheriv)("aes-256-gcm", encKey(), iv);
35929
36516
  const enc = Buffer.concat([cipher.update(secret2, "utf8"), cipher.final()]);
35930
36517
  const tag = cipher.getAuthTag();
35931
36518
  return `${iv.toString("base64")}:${tag.toString("base64")}:${enc.toString("base64")}`;
@@ -35933,7 +36520,7 @@ function encryptMemoryKey(secret2) {
35933
36520
  function decryptMemoryKey(stored) {
35934
36521
  try {
35935
36522
  const [ivB, tagB, dataB] = stored.split(":");
35936
- const decipher = (0, import_node_crypto20.createDecipheriv)("aes-256-gcm", encKey(), Buffer.from(ivB, "base64"));
36523
+ const decipher = (0, import_node_crypto21.createDecipheriv)("aes-256-gcm", encKey(), Buffer.from(ivB, "base64"));
35937
36524
  decipher.setAuthTag(Buffer.from(tagB, "base64"));
35938
36525
  return Buffer.concat([decipher.update(Buffer.from(dataB, "base64")), decipher.final()]).toString("utf8");
35939
36526
  } catch {
@@ -36047,11 +36634,11 @@ async function syncScheduledActionCredentials(user) {
36047
36634
  }, {});
36048
36635
  return { ok: res.ok, error: res.error };
36049
36636
  }
36050
- var import_node_crypto20, import_provision_defaults, import_set_schedule_entitlement, MEMORY_BASE_URL, ADMIN_KEY;
36637
+ var import_node_crypto21, import_provision_defaults, import_set_schedule_entitlement, MEMORY_BASE_URL, ADMIN_KEY;
36051
36638
  var init_memory = __esm({
36052
36639
  "src/api/memory.ts"() {
36053
36640
  "use strict";
36054
- import_node_crypto20 = require("crypto");
36641
+ import_node_crypto21 = require("crypto");
36055
36642
  init_session();
36056
36643
  init_db();
36057
36644
  init_rates();
@@ -36101,7 +36688,7 @@ var init_connected_cost_telemetry = __esm({
36101
36688
 
36102
36689
  // src/api/connected-usage-billing.ts
36103
36690
  function hash(value) {
36104
- return (0, import_node_crypto21.createHash)("sha256").update(value).digest("hex");
36691
+ return (0, import_node_crypto22.createHash)("sha256").update(value).digest("hex");
36105
36692
  }
36106
36693
  function eventKey(idempotencyKey4) {
36107
36694
  return `connected-usage:${hash(idempotencyKey4)}`;
@@ -36373,7 +36960,7 @@ async function settleConnectedUsage(rawInput) {
36373
36960
  sql: `INSERT OR IGNORE INTO billing_events
36374
36961
  (id, user_id, idempotency_key, billing_class, source_surface, status, amount_mc, metadata)
36375
36962
  VALUES (?, ?, ?, ?, ?, 'settling', ?, ?)`,
36376
- args: [(0, import_node_crypto21.randomUUID)(), user.id, key, CONNECTED_USAGE_BILLING_CLASS, input.sourceSurface, charge2.amountMc, encodeMetadata(storedMetadata)]
36963
+ args: [(0, import_node_crypto22.randomUUID)(), user.id, key, CONNECTED_USAGE_BILLING_CLASS, input.sourceSurface, charge2.amountMc, encodeMetadata(storedMetadata)]
36377
36964
  });
36378
36965
  let event2 = await readEvent(key);
36379
36966
  if (!event2) throw new Error("connected usage receipt insert completed without a readable event");
@@ -36462,11 +37049,11 @@ async function listConnectedUsageHistory(userId, limit = 100) {
36462
37049
  }
36463
37050
  return history;
36464
37051
  }
36465
- var import_node_crypto21, import_zod17, CONNECTED_USAGE_BILLING_CLASS, CONNECTED_USAGE_DEFAULT_SOURCE_SURFACE, ConnectedUsageSafeMetadataSchema, ConnectedUsageSettlementInputSchema, ConnectedUsagePreflightInputSchema, ConnectedUsageBillingError;
37052
+ var import_node_crypto22, import_zod17, CONNECTED_USAGE_BILLING_CLASS, CONNECTED_USAGE_DEFAULT_SOURCE_SURFACE, ConnectedUsageSafeMetadataSchema, ConnectedUsageSettlementInputSchema, ConnectedUsagePreflightInputSchema, ConnectedUsageBillingError;
36466
37053
  var init_connected_usage_billing = __esm({
36467
37054
  "src/api/connected-usage-billing.ts"() {
36468
37055
  "use strict";
36469
- import_node_crypto21 = require("crypto");
37056
+ import_node_crypto22 = require("crypto");
36470
37057
  import_zod17 = require("zod");
36471
37058
  init_connected_cost_telemetry();
36472
37059
  init_db();
@@ -38115,7 +38702,27 @@ var init_server_schemas = __esm({
38115
38702
  downloadImages: import_zod22.z.boolean().optional(),
38116
38703
  preserveMedia: import_zod22.z.boolean().optional(),
38117
38704
  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()
38705
+ formats: import_zod22.z.array(import_zod22.z.enum(["markdown", "html", "links", "json", "images", "branding", "issues"])).optional(),
38706
+ renderJavaScript: import_zod22.z.boolean().optional(),
38707
+ captureRenderedDom: import_zod22.z.boolean().optional(),
38708
+ semanticSimilarity: import_zod22.z.boolean().optional(),
38709
+ similarityThreshold: import_zod22.z.number().min(0).max(1).optional(),
38710
+ similarityMaxPairs: import_zod22.z.number().int().min(1).max(5e4).optional()
38711
+ }).superRefine((value, ctx) => {
38712
+ if (value.semanticSimilarity && (value.maxPages ?? 100) > 500) {
38713
+ ctx.addIssue({
38714
+ code: import_zod22.z.ZodIssueCode.custom,
38715
+ path: ["maxPages"],
38716
+ message: "semanticSimilarity supports at most 500 pages per analysis."
38717
+ });
38718
+ }
38719
+ if (value.semanticSimilarity && value.wayback) {
38720
+ ctx.addIssue({
38721
+ code: import_zod22.z.ZodIssueCode.custom,
38722
+ path: ["wayback"],
38723
+ message: "semanticSimilarity analyzes one live rendered corpus and cannot be combined with a Wayback timeline."
38724
+ });
38725
+ }
38119
38726
  });
38120
38727
  SiteExportReadBodySchema = import_zod22.z.object({
38121
38728
  jobId: import_zod22.z.string().trim().min(1),
@@ -40415,7 +41022,7 @@ async function applyAdjustment(input) {
40415
41022
  const credits = validateCredits(input.credits, input.confirmLarge === true);
40416
41023
  const reason = validateReason(input.reason);
40417
41024
  const actor = validateActor(input.actor);
40418
- const reference = input.reference?.trim() || `adj_${(0, import_node_crypto22.randomUUID)()}`;
41025
+ const reference = input.reference?.trim() || `adj_${(0, import_node_crypto23.randomUUID)()}`;
40419
41026
  if (reference.length > 120) throw new AdminCreditError(400, "reference must be at most 120 characters");
40420
41027
  const db = getDb();
40421
41028
  const existing = await db.execute({
@@ -40560,11 +41167,11 @@ async function lookupAccount(target, limit = 20) {
40560
41167
  }))
40561
41168
  };
40562
41169
  }
40563
- var import_node_crypto22, MIN_ADJUSTMENT_CREDITS, LARGE_ADJUSTMENT_CREDITS, MAX_ADJUSTMENT_CREDITS, MIN_REASON_LENGTH, MAX_REASON_LENGTH, MAX_ACTOR_LENGTH, AdminCreditError;
41170
+ var import_node_crypto23, MIN_ADJUSTMENT_CREDITS, LARGE_ADJUSTMENT_CREDITS, MAX_ADJUSTMENT_CREDITS, MIN_REASON_LENGTH, MAX_REASON_LENGTH, MAX_ACTOR_LENGTH, AdminCreditError;
40564
41171
  var init_admin_credits = __esm({
40565
41172
  "src/api/admin-credits.ts"() {
40566
41173
  "use strict";
40567
- import_node_crypto22 = require("crypto");
41174
+ import_node_crypto23 = require("crypto");
40568
41175
  init_db();
40569
41176
  init_rates();
40570
41177
  MIN_ADJUSTMENT_CREDITS = 1;
@@ -42529,7 +43136,7 @@ async function packageMapsMedia(args) {
42529
43136
  files.push({ path: "summary.json", content: Buffer.from(JSON.stringify(summary, null, 2)) });
42530
43137
  files.push({ path: "images.jsonl", content: Buffer.from(cleanImages.map((image) => JSON.stringify(image)).join("\n") + "\n") });
42531
43138
  const archive = await zipBuffer(files);
42532
- const id = (0, import_node_crypto23.randomBytes)(6).toString("hex");
43139
+ const id = (0, import_node_crypto24.randomBytes)(6).toString("hex");
42533
43140
  const pointer = await createPrivateArtifact({
42534
43141
  policy: policy3(),
42535
43142
  ownerId: args.ownerId,
@@ -42542,13 +43149,13 @@ async function packageMapsMedia(args) {
42542
43149
  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
43150
  return { media: args.media, artifact: { ...pointer, localPath } };
42544
43151
  }
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;
43152
+ 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
43153
  var init_maps_media_artifacts = __esm({
42547
43154
  "src/api/maps-media-artifacts.ts"() {
42548
43155
  "use strict";
42549
43156
  import_node_os9 = require("os");
42550
43157
  import_node_path13 = require("path");
42551
- import_node_crypto23 = require("crypto");
43158
+ import_node_crypto24 = require("crypto");
42552
43159
  import_yazl2 = require("yazl");
42553
43160
  import_p_limit5 = __toESM(require("p-limit"), 1);
42554
43161
  init_private_artifacts();
@@ -43172,7 +43779,7 @@ function retryDelaySeconds(attempts) {
43172
43779
  }
43173
43780
  async function dispatchPendingDirectoryWorkflows(limit = 25) {
43174
43781
  const rows = await claimDirectoryWorkflowOutbox({
43175
- workerId: `directory-dispatch-${process.pid}-${(0, import_node_crypto24.randomUUID)().slice(0, 8)}`,
43782
+ workerId: `directory-dispatch-${process.pid}-${(0, import_node_crypto25.randomUUID)().slice(0, 8)}`,
43176
43783
  limit
43177
43784
  });
43178
43785
  const result = { claimed: rows.length, dispatched: 0, failed: 0 };
@@ -43194,11 +43801,11 @@ async function dispatchPendingDirectoryWorkflows(limit = 25) {
43194
43801
  }
43195
43802
  return result;
43196
43803
  }
43197
- var import_node_crypto24;
43804
+ var import_node_crypto25;
43198
43805
  var init_directory_workflow_dispatch = __esm({
43199
43806
  "src/api/directory-workflow-dispatch.ts"() {
43200
43807
  "use strict";
43201
- import_node_crypto24 = require("crypto");
43808
+ import_node_crypto25 = require("crypto");
43202
43809
  init_client();
43203
43810
  init_directory_workflow_repository();
43204
43811
  }
@@ -43210,7 +43817,7 @@ function safeOptions(options) {
43210
43817
  return safe2;
43211
43818
  }
43212
43819
  function requestFingerprint(options) {
43213
- return (0, import_node_crypto25.createHash)("sha256").update(JSON.stringify(safeOptions(options))).digest("hex");
43820
+ return (0, import_node_crypto26.createHash)("sha256").update(JSON.stringify(safeOptions(options))).digest("hex");
43214
43821
  }
43215
43822
  function idempotencyKey(raw) {
43216
43823
  if (raw !== void 0) {
@@ -43219,14 +43826,14 @@ function idempotencyKey(raw) {
43219
43826
  if (trimmed.length > 500) return { ok: false, message: "Idempotency-Key must be 500 characters or fewer." };
43220
43827
  return { ok: true, key: trimmed };
43221
43828
  }
43222
- return { ok: true, key: `directory-${(0, import_node_crypto25.randomUUID)()}` };
43829
+ return { ok: true, key: `directory-${(0, import_node_crypto26.randomUUID)()}` };
43223
43830
  }
43224
43831
  function debitKeyFor(userId, responseKey) {
43225
- const digest2 = (0, import_node_crypto25.createHash)("sha256").update(String(userId)).update("\0").update(responseKey).digest("hex");
43832
+ const digest2 = (0, import_node_crypto26.createHash)("sha256").update(String(userId)).update("\0").update(responseKey).digest("hex");
43226
43833
  return `directory-workflow:${userId}:${digest2}`;
43227
43834
  }
43228
43835
  function jobId() {
43229
- return `dir_${(0, import_node_crypto25.randomUUID)().replace(/-/g, "")}`;
43836
+ return `dir_${(0, import_node_crypto26.randomUUID)().replace(/-/g, "")}`;
43230
43837
  }
43231
43838
  function publicStatus(job, result) {
43232
43839
  if (job.status === "failed") return "failed";
@@ -43335,7 +43942,7 @@ async function runSynchronously(c, user, options, plan, responseKey) {
43335
43942
  const csv = renderDirectoryWorkflowCsv(result);
43336
43943
  const artifact = await createDirectoryCsvArtifact({
43337
43944
  ownerId: String(user.id),
43338
- jobId: (0, import_node_crypto25.createHash)("sha256").update(debitKey2).digest("hex").slice(0, 32),
43945
+ jobId: (0, import_node_crypto26.createHash)("sha256").update(debitKey2).digest("hex").slice(0, 32),
43339
43946
  createdAt: result.extractedAt,
43340
43947
  filename: `${options.state}-${options.query}-directory.csv`,
43341
43948
  csv,
@@ -43391,11 +43998,11 @@ async function runSynchronously(c, user, options, plan, responseKey) {
43391
43998
  await releaseConcurrencyGate(gate.lockId);
43392
43999
  }
43393
44000
  }
43394
- var import_node_crypto25, import_hono16, directoryApp;
44001
+ var import_node_crypto26, import_hono16, directoryApp;
43395
44002
  var init_directory_routes = __esm({
43396
44003
  "src/api/directory-routes.ts"() {
43397
44004
  "use strict";
43398
- import_node_crypto25 = require("crypto");
44005
+ import_node_crypto26 = require("crypto");
43399
44006
  import_hono16 = require("hono");
43400
44007
  init_api_auth();
43401
44008
  init_db();
@@ -43613,10 +44220,10 @@ function bounded(value, field, min, max) {
43613
44220
  return normalized;
43614
44221
  }
43615
44222
  function newLeadListUploadId() {
43616
- return `upl_${(0, import_node_crypto26.randomUUID)().replace(/-/g, "")}`;
44223
+ return `upl_${(0, import_node_crypto27.randomUUID)().replace(/-/g, "")}`;
43617
44224
  }
43618
44225
  function newImportedLeadListId() {
43619
- return `lst_${(0, import_node_crypto26.randomUUID)().replace(/-/g, "")}`;
44226
+ return `lst_${(0, import_node_crypto27.randomUUID)().replace(/-/g, "")}`;
43620
44227
  }
43621
44228
  async function ensureLeadListImportRepositorySchema() {
43622
44229
  const db = getDb();
@@ -43816,11 +44423,11 @@ async function deleteExpiredLeadListInputRecords(now = /* @__PURE__ */ new Date(
43816
44423
  ], "write");
43817
44424
  return { uploads: Number(uploads.rowsAffected ?? 0), lists: Number(lists.rowsAffected ?? 0) };
43818
44425
  }
43819
- var import_node_crypto26, schemaDb5, schemaPromise5;
44426
+ var import_node_crypto27, schemaDb5, schemaPromise5;
43820
44427
  var init_lead_list_import_repository = __esm({
43821
44428
  "src/api/lead-list-import-repository.ts"() {
43822
44429
  "use strict";
43823
- import_node_crypto26 = require("crypto");
44430
+ import_node_crypto27 = require("crypto");
43824
44431
  init_db();
43825
44432
  schemaDb5 = null;
43826
44433
  schemaPromise5 = null;
@@ -43862,7 +44469,7 @@ function safeFilenameHint(value) {
43862
44469
  return cleaned || null;
43863
44470
  }
43864
44471
  function requestFingerprint2(filenameHint) {
43865
- return (0, import_node_crypto27.createHash)("sha256").update(JSON.stringify({ filenameHint })).digest("hex");
44472
+ return (0, import_node_crypto28.createHash)("sha256").update(JSON.stringify({ filenameHint })).digest("hex");
43866
44473
  }
43867
44474
  function publicBaseUrl() {
43868
44475
  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 +44625,7 @@ async function inspectLeadListUpload(uploadId, ownerId2) {
44018
44625
  maxBytes: LEAD_LIST_UPLOAD_MAX_BYTES
44019
44626
  });
44020
44627
  if (!buffer) return null;
44021
- const sha2565 = (0, import_node_crypto27.createHash)("sha256").update(buffer).digest("hex");
44628
+ const sha2565 = (0, import_node_crypto28.createHash)("sha256").update(buffer).digest("hex");
44022
44629
  const completed = await completeLeadListUpload({
44023
44630
  id: record.id,
44024
44631
  ownerId: record.ownerId,
@@ -44142,11 +44749,11 @@ async function cleanupExpiredLeadListInputs(args = {}) {
44142
44749
  const records = await deleteExpiredLeadListInputRecords(now);
44143
44750
  return { deletedBlobs, deletedUploadRecords: records.uploads, deletedListRecords: records.lists };
44144
44751
  }
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;
44752
+ 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
44753
  var init_lead_list_input_artifacts = __esm({
44147
44754
  "src/api/lead-list-input-artifacts.ts"() {
44148
44755
  "use strict";
44149
- import_node_crypto27 = require("crypto");
44756
+ import_node_crypto28 = require("crypto");
44150
44757
  import_promises11 = require("fs/promises");
44151
44758
  import_node_os10 = require("os");
44152
44759
  import_node_path14 = require("path");
@@ -44529,7 +45136,7 @@ function canonicalize(value) {
44529
45136
  return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${canonicalize(object[key])}`).join(",")}}`;
44530
45137
  }
44531
45138
  function fingerprint(value) {
44532
- return (0, import_node_crypto28.createHash)("sha256").update(canonicalize(value)).digest("hex");
45139
+ return (0, import_node_crypto29.createHash)("sha256").update(canonicalize(value)).digest("hex");
44533
45140
  }
44534
45141
  async function safeJson(c) {
44535
45142
  try {
@@ -44606,11 +45213,11 @@ function textDelimiter(raw, text2, observedMime) {
44606
45213
  if (observedMime === "text/tab-separated-values") return " ";
44607
45214
  return detectLeadListTextDelimiter(text2);
44608
45215
  }
44609
- var import_node_crypto28, import_hono17, leadListInputApp;
45216
+ var import_node_crypto29, import_hono17, leadListInputApp;
44610
45217
  var init_lead_list_input_routes = __esm({
44611
45218
  "src/api/lead-list-input-routes.ts"() {
44612
45219
  "use strict";
44613
- import_node_crypto28 = require("crypto");
45220
+ import_node_crypto29 = require("crypto");
44614
45221
  import_hono17 = require("hono");
44615
45222
  init_api_auth();
44616
45223
  init_lead_list_input_artifacts();
@@ -44802,7 +45409,7 @@ function retryDelaySeconds2(attempts) {
44802
45409
  return Math.min(3600, Math.max(15, 15 * 2 ** Math.min(8, Math.max(0, attempts - 1))));
44803
45410
  }
44804
45411
  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 });
45412
+ const rows = await claimLeadListEnrichmentOutbox({ workerId: `lead-list-dispatch-${process.pid}-${(0, import_node_crypto30.randomUUID)().slice(0, 8)}`, limit });
44806
45413
  const result = { claimed: rows.length, dispatched: 0, failed: 0 };
44807
45414
  for (const row of rows) {
44808
45415
  if (!row.claimToken) continue;
@@ -44821,11 +45428,11 @@ async function dispatchPendingLeadListEnrichments(limit = 25) {
44821
45428
  }
44822
45429
  return result;
44823
45430
  }
44824
- var import_node_crypto29;
45431
+ var import_node_crypto30;
44825
45432
  var init_lead_list_enrichment_dispatch = __esm({
44826
45433
  "src/api/lead-list-enrichment-dispatch.ts"() {
44827
45434
  "use strict";
44828
- import_node_crypto29 = require("crypto");
45435
+ import_node_crypto30 = require("crypto");
44829
45436
  init_client();
44830
45437
  init_lead_list_enrichment_repository();
44831
45438
  }
@@ -44841,15 +45448,15 @@ function idempotencyKey2(raw) {
44841
45448
  return { ok: true, value };
44842
45449
  }
44843
45450
  function newJobId() {
44844
- return `lle_${(0, import_node_crypto30.randomUUID)().replace(/-/g, "")}`;
45451
+ return `lle_${(0, import_node_crypto31.randomUUID)().replace(/-/g, "")}`;
44845
45452
  }
44846
45453
  function debitKeyFor2(userId, key) {
44847
- const digest2 = (0, import_node_crypto30.createHash)("sha256").update(String(userId)).update("\0").update(key).digest("hex");
45454
+ const digest2 = (0, import_node_crypto31.createHash)("sha256").update(String(userId)).update("\0").update(key).digest("hex");
44848
45455
  return `lead-list-enrichment:${userId}:${digest2}`;
44849
45456
  }
44850
45457
  function inlineSourceDigest(headers, rows) {
44851
45458
  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");
45459
+ return (0, import_node_crypto31.createHash)("sha256").update(stableCanonicalJson({ headers, values })).digest("hex");
44853
45460
  }
44854
45461
  function issueMessage(error) {
44855
45462
  const issue = error.issues[0];
@@ -45047,11 +45654,11 @@ async function resolveInput(ownerId2, parsed) {
45047
45654
  sourceDigest: sourceDigest ?? inlineSourceDigest(normalized.headers, rows)
45048
45655
  };
45049
45656
  }
45050
- var import_node_crypto30, import_hono18, import_zod31, SourceSchema, StartSchema, TERMINAL, leadListEnrichmentApp;
45657
+ var import_node_crypto31, import_hono18, import_zod31, SourceSchema, StartSchema, TERMINAL, leadListEnrichmentApp;
45051
45658
  var init_lead_list_enrichment_routes = __esm({
45052
45659
  "src/api/lead-list-enrichment-routes.ts"() {
45053
45660
  "use strict";
45054
- import_node_crypto30 = require("crypto");
45661
+ import_node_crypto31 = require("crypto");
45055
45662
  import_hono18 = require("hono");
45056
45663
  import_zod31 = require("zod");
45057
45664
  init_api_auth();
@@ -48244,7 +48851,7 @@ async function readManifestFromSummary(summary) {
48244
48851
  function webhookSignature(body, timestamp2) {
48245
48852
  const secret2 = process.env.MCP_SCRAPER_WEBHOOK_SECRET?.trim();
48246
48853
  if (!secret2) return null;
48247
- return (0, import_node_crypto31.createHmac)("sha256", secret2).update(`${timestamp2}.${body}`).digest("hex");
48854
+ return (0, import_node_crypto32.createHmac)("sha256", secret2).update(`${timestamp2}.${body}`).digest("hex");
48248
48855
  }
48249
48856
  async function deliverWorkflowWebhook(input) {
48250
48857
  if (!input.webhookUrl) return;
@@ -48451,11 +49058,11 @@ async function dispatchDueWorkflowSchedules(apiUrl, limit = 3) {
48451
49058
  }
48452
49059
  return { dispatched: results.length, results };
48453
49060
  }
48454
- var import_node_crypto31, import_promises14, import_hono21, import_zod41, workflowApp, WorkflowInputSchema, WorkflowIdSchema, CadenceSchema, ScheduleStatusSchema, RunBodySchema, ScheduleCreateSchema, SchedulePatchSchema, TERMINAL_RUN_STATUSES;
49061
+ var import_node_crypto32, import_promises14, import_hono21, import_zod41, workflowApp, WorkflowInputSchema, WorkflowIdSchema, CadenceSchema, ScheduleStatusSchema, RunBodySchema, ScheduleCreateSchema, SchedulePatchSchema, TERMINAL_RUN_STATUSES;
48455
49062
  var init_workflow_routes = __esm({
48456
49063
  "src/api/workflow-routes.ts"() {
48457
49064
  "use strict";
48458
- import_node_crypto31 = require("crypto");
49065
+ import_node_crypto32 = require("crypto");
48459
49066
  import_promises14 = require("fs/promises");
48460
49067
  import_hono21 = require("hono");
48461
49068
  import_zod41 = require("zod");
@@ -48736,7 +49343,7 @@ var init_workflow_routes = __esm({
48736
49343
  // src/serp-intelligence/page-snapshot-extractor.ts
48737
49344
  function sha2562(value) {
48738
49345
  if (!value) return null;
48739
- return (0, import_node_crypto32.createHash)("sha256").update(value).digest("hex");
49346
+ return (0, import_node_crypto33.createHash)("sha256").update(value).digest("hex");
48740
49347
  }
48741
49348
  function countWords(markdown) {
48742
49349
  const matches = markdown.trim().match(/\b[\p{L}\p{N}][\p{L}\p{N}'-]*\b/gu);
@@ -49036,11 +49643,11 @@ async function capturePageSnapshots(targets, options = {}) {
49036
49643
  }
49037
49644
  };
49038
49645
  }
49039
- var import_node_crypto32, import_p_limit6, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_CONCURRENCY, DEFAULT_MAX_CONTENT_CHARS;
49646
+ var import_node_crypto33, import_p_limit6, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_CONCURRENCY, DEFAULT_MAX_CONTENT_CHARS;
49040
49647
  var init_page_snapshot_extractor = __esm({
49041
49648
  "src/serp-intelligence/page-snapshot-extractor.ts"() {
49042
49649
  "use strict";
49043
- import_node_crypto32 = require("crypto");
49650
+ import_node_crypto33 = require("crypto");
49044
49651
  import_p_limit6 = __toESM(require("p-limit"), 1);
49045
49652
  init_kpo_extractor();
49046
49653
  init_url_utils();
@@ -49536,21 +50143,21 @@ async function logRequestEventBestEffort(input) {
49536
50143
  }
49537
50144
  }
49538
50145
  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");
50146
+ const responseKey = suppliedKey?.trim() || (0, import_node_crypto34.randomUUID)();
50147
+ const requestFingerprint3 = (0, import_node_crypto34.createHash)("sha256").update(JSON.stringify(body)).digest("hex");
50148
+ const keyDigest = (0, import_node_crypto34.createHash)("sha256").update(String(userId)).update("\0").update(responseKey).digest("hex");
49542
50149
  return {
49543
50150
  responseKey,
49544
50151
  debitKey: `serp-capture:${userId}:${keyDigest}`,
49545
50152
  debitDescription: `${body.query} [request:${requestFingerprint3}]`
49546
50153
  };
49547
50154
  }
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;
50155
+ 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
50156
  var init_serp_intelligence_routes = __esm({
49550
50157
  "src/api/serp-intelligence-routes.ts"() {
49551
50158
  "use strict";
49552
50159
  import_hono22 = require("hono");
49553
- import_node_crypto33 = require("crypto");
50160
+ import_node_crypto34 = require("crypto");
49554
50161
  init_browser_service_env();
49555
50162
  init_page_snapshot_extractor();
49556
50163
  init_serp_capture_service();
@@ -49787,7 +50394,7 @@ var PACKAGE_VERSION;
49787
50394
  var init_version = __esm({
49788
50395
  "src/version.ts"() {
49789
50396
  "use strict";
49790
- PACKAGE_VERSION = "0.64.1";
50397
+ PACKAGE_VERSION = "0.65.0";
49791
50398
  }
49792
50399
  });
49793
50400
 
@@ -49818,6 +50425,8 @@ seam is noted so you can chain them.
49818
50425
  - Whole site -> **extract_site** (takes a url). It durably retains complete per-page JSON, acquired HTML,
49819
50426
  and Markdown. Poll **check_site_export**, then call **site_export_read** for the manifest or a page view;
49820
50427
  call **site_export_image** for downloaded image IDs.
50428
+ - JavaScript-rendered page overlap/cannibalization -> **analyze_site_similarity**. It returns dedicated
50429
+ raw-cosine page pairs, corpus percentiles, threshold clusters, and rendered-page artifacts.
49821
50430
  - Wayback replay URLs work with the same tools: \`extract_url\` removes playback chrome and can return
49822
50431
  a featured image; \`extract_site\` batches nearby archived HTML captures for the replayed site.
49823
50432
  - For multiple archive months, pass \`extract_site.wayback\` with explicit \`months\` or a \`from\`/\`to\`
@@ -50231,6 +50840,7 @@ var init_output_schema_registry = __esm({
50231
50840
  ESSENTIAL_OUTPUT_SCHEMA_TOOLS = /* @__PURE__ */ new Set([
50232
50841
  "extract_url",
50233
50842
  "extract_site",
50843
+ "analyze_site_similarity",
50234
50844
  "audit_site",
50235
50845
  "check_site_export",
50236
50846
  "site_export_read",
@@ -51628,7 +52238,7 @@ var init_contracts = __esm({
51628
52238
  });
51629
52239
 
51630
52240
  // 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;
52241
+ 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
52242
  var init_mcp_tool_schemas = __esm({
51633
52243
  "src/mcp/mcp-tool-schemas.ts"() {
51634
52244
  "use strict";
@@ -51738,6 +52348,13 @@ var init_mcp_tool_schemas = __esm({
51738
52348
  preserveMedia: import_zod45.z.boolean().default(false).describe("Include supported images in the export bundle. This is the preferred replacement for downloadImages."),
51739
52349
  downloadImages: import_zod45.z.boolean().optional().describe("Deprecated alias for preserveMedia. Omit when using preserveMedia; when omitted, image preservation defaults to false.")
51740
52350
  };
52351
+ AnalyzeSiteSimilarityInputSchema = {
52352
+ url: WebsiteUrlOrDomainSchema.describe("Public live site to render and compare. Bare domains default to https://."),
52353
+ maxPages: import_zod45.z.number().int().min(2).max(500).default(100).describe("Maximum rendered pages to compare; default 100, maximum 500."),
52354
+ 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."),
52355
+ similarityMaxPairs: import_zod45.z.number().int().min(1).max(5e4).default(1e4).describe("Maximum scored pairs retained, highest first; default 10,000."),
52356
+ 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.")
52357
+ };
51741
52358
  AuditSiteInputSchema = {
51742
52359
  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
52360
  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 +52367,17 @@ var init_mcp_tool_schemas = __esm({
51750
52367
  downloadImages: import_zod45.z.boolean().optional().describe("Deprecated alias for preserveMedia. Omit when using preserveMedia; when omitted, image preservation defaults to false.")
51751
52368
  };
51752
52369
  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.")
52370
+ 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
52371
  };
51755
52372
  SiteExportReadInputSchema = {
51756
- jobId: import_zod45.z.string().min(1).describe("Site export job ID returned by extract_site or audit_site."),
52373
+ jobId: import_zod45.z.string().min(1).describe("Site export job ID returned by extract_site, analyze_site_similarity, or audit_site."),
51757
52374
  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
52375
  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
52376
  offset: import_zod45.z.number().int().min(0).default(0).describe("UTF-8 byte offset. Continue from nextOffset until it is null."),
51760
52377
  maxBytes: import_zod45.z.number().int().min(1).max(1e6).default(64e3).describe("Maximum UTF-8 bytes returned in this window.")
51761
52378
  };
51762
52379
  SiteExportImageInputSchema = {
51763
- jobId: import_zod45.z.string().min(1).describe("Site export job ID returned by extract_site or audit_site."),
52380
+ jobId: import_zod45.z.string().min(1).describe("Site export job ID returned by extract_site, analyze_site_similarity, or audit_site."),
51764
52381
  imageId: import_zod45.z.string().regex(/^[a-f0-9]{64}$/).describe("Downloaded image ID returned by a site_export_read manifest.")
51765
52382
  };
51766
52383
  ArchiveReadInputSchema = {
@@ -54928,11 +55545,11 @@ function requireTasksCapability(capabilityValue) {
54928
55545
  );
54929
55546
  }
54930
55547
  function taskKey(secret2) {
54931
- return (0, import_node_crypto34.createHash)("sha256").update("mcp-scraper-task-handle\0", "utf8").update(secret2, "utf8").digest();
55548
+ return (0, import_node_crypto35.createHash)("sha256").update("mcp-scraper-task-handle\0", "utf8").update(secret2, "utf8").digest();
54932
55549
  }
54933
55550
  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);
55551
+ const nonce = (0, import_node_crypto35.randomBytes)(12);
55552
+ const cipher = (0, import_node_crypto35.createCipheriv)("aes-256-gcm", taskKey(secret2), nonce);
54936
55553
  const ciphertext = Buffer.concat([
54937
55554
  cipher.update(JSON.stringify(payload), "utf8"),
54938
55555
  cipher.final()
@@ -54949,7 +55566,7 @@ function decodeTaskHandle(taskId, secret2, ownerId2) {
54949
55566
  const nonce = bytes.subarray(0, 12);
54950
55567
  const tag = bytes.subarray(bytes.length - 16);
54951
55568
  const ciphertext = bytes.subarray(12, bytes.length - 16);
54952
- const decipher = (0, import_node_crypto34.createDecipheriv)("aes-256-gcm", taskKey(secret2), nonce);
55569
+ const decipher = (0, import_node_crypto35.createDecipheriv)("aes-256-gcm", taskKey(secret2), nonce);
54953
55570
  decipher.setAuthTag(tag);
54954
55571
  const parsed = JSON.parse(Buffer.concat([
54955
55572
  decipher.update(ciphertext),
@@ -55177,12 +55794,12 @@ async function handleMcpTasksHttpRequest(request, executor, options) {
55177
55794
  return taskHttpError(id, error instanceof import_server.ProtocolError ? error : new import_server.ProtocolError(-32603, error instanceof Error ? error.message : "Internal error"));
55178
55795
  }
55179
55796
  }
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;
55797
+ 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
55798
  var init_mcp_tasks_extension = __esm({
55182
55799
  "src/mcp/mcp-tasks-extension.ts"() {
55183
55800
  "use strict";
55184
55801
  import_server = require("@modelcontextprotocol/server");
55185
- import_node_crypto34 = require("crypto");
55802
+ import_node_crypto35 = require("crypto");
55186
55803
  import_zod46 = require("zod");
55187
55804
  MCP_TASKS_EXTENSION_ID = "io.modelcontextprotocol/tasks";
55188
55805
  TASK_HANDLE_VERSION = "mt1";
@@ -55480,7 +56097,7 @@ var init_analytics_mcp_tools = __esm({
55480
56097
 
55481
56098
  // src/mcp/paa-mcp-server.ts
55482
56099
  function hashOwnerId(callerKey) {
55483
- return (0, import_node_crypto35.createHash)("sha256").update(callerKey).digest("hex").slice(0, 24);
56100
+ return (0, import_node_crypto36.createHash)("sha256").update(callerKey).digest("hex").slice(0, 24);
55484
56101
  }
55485
56102
  function liveWebToolAnnotations(title) {
55486
56103
  return {
@@ -55693,6 +56310,21 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
55693
56310
  await formatExtractSite(await executor.extractSite(input), input, ctx),
55694
56311
  requestContext
55695
56312
  ));
56313
+ server.registerTool("analyze_site_similarity", {
56314
+ title: "Rendered Site Content Similarity",
56315
+ 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.",
56316
+ inputSchema: AnalyzeSiteSimilarityInputSchema,
56317
+ outputSchema: recordOutputSchema("analyze_site_similarity", ExtractSiteOutputSchema),
56318
+ annotations: { ...liveWebToolAnnotations("Rendered Site Content Similarity"), readOnlyHint: false }
56319
+ }, async (input, requestContext) => tasks.taskify(
56320
+ "site_export",
56321
+ await formatExtractSite(
56322
+ await executor.analyzeSiteSimilarity(input),
56323
+ { ...input, toolLabel: "Rendered Site Content Similarity" },
56324
+ ctx
56325
+ ),
56326
+ requestContext
56327
+ ));
55696
56328
  server.registerTool("audit_site", {
55697
56329
  title: "Technical SEO Audit",
55698
56330
  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 +56338,7 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
55706
56338
  ));
55707
56339
  server.registerTool("check_site_export", {
55708
56340
  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.",
56341
+ 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
56342
  inputSchema: CheckSiteExportInputSchema,
55711
56343
  outputSchema: recordOutputSchema("check_site_export", CheckSiteExportOutputSchema),
55712
56344
  annotations: { ...liveWebToolAnnotations("Check Site Export"), readOnlyHint: false }
@@ -56445,7 +57077,7 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
56445
57077
  annotations: { title: "Set Scheduled Action Connections", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
56446
57078
  }, async (input) => executor.setScheduledActionConnections(input));
56447
57079
  }
56448
- var import_server2, import_zod48, import_node_fs11, import_node_path17, import_node_crypto35, ACTION_CONFIRMATION_SCHEMA;
57080
+ var import_server2, import_zod48, import_node_fs11, import_node_path17, import_node_crypto36, ACTION_CONFIRMATION_SCHEMA;
56449
57081
  var init_paa_mcp_server = __esm({
56450
57082
  "src/mcp/paa-mcp-server.ts"() {
56451
57083
  "use strict";
@@ -56453,7 +57085,7 @@ var init_paa_mcp_server = __esm({
56453
57085
  import_zod48 = require("zod");
56454
57086
  import_node_fs11 = require("fs");
56455
57087
  import_node_path17 = require("path");
56456
- import_node_crypto35 = require("crypto");
57088
+ import_node_crypto36 = require("crypto");
56457
57089
  init_version();
56458
57090
  init_rates();
56459
57091
  init_mcp_response_formatter();
@@ -56574,11 +57206,11 @@ function analyticsReportPath(input, report) {
56574
57206
  const suffix2 = query.size ? `?${query.toString()}` : "";
56575
57207
  return `/analytics/sites/${encodeURIComponent(input.siteId)}/${report}${suffix2}`;
56576
57208
  }
56577
- var import_node_crypto36, HttpMcpToolExecutor;
57209
+ var import_node_crypto37, HttpMcpToolExecutor;
56578
57210
  var init_http_mcp_tool_executor = __esm({
56579
57211
  "src/mcp/http-mcp-tool-executor.ts"() {
56580
57212
  "use strict";
56581
- import_node_crypto36 = require("crypto");
57213
+ import_node_crypto37 = require("crypto");
56582
57214
  init_harvest_timeout();
56583
57215
  init_browser_service_env();
56584
57216
  init_errors();
@@ -56647,13 +57279,13 @@ var init_http_mcp_tool_executor = __esm({
56647
57279
  });
56648
57280
  }
56649
57281
  async callDirectoryWorkflowStart(body, explicitIdempotencyKey) {
56650
- const idempotencyKey4 = `mcp-directory-${(0, import_node_crypto36.createHash)("sha256").update(explicitIdempotencyKey).digest("hex")}`;
57282
+ const idempotencyKey4 = `mcp-directory-${(0, import_node_crypto37.createHash)("sha256").update(explicitIdempotencyKey).digest("hex")}`;
56651
57283
  return this.call("/directory/run", body, this.timeoutMs, "POST", {
56652
57284
  "Idempotency-Key": idempotencyKey4
56653
57285
  });
56654
57286
  }
56655
57287
  async callSiteExtractStart(toolName, body, explicitIdempotencyKey) {
56656
- const idempotencyKey4 = `mcp-site-${(0, import_node_crypto36.createHash)("sha256").update(toolName).update("\0").update(explicitIdempotencyKey).digest("hex")}`;
57288
+ const idempotencyKey4 = `mcp-site-${(0, import_node_crypto37.createHash)("sha256").update(toolName).update("\0").update(explicitIdempotencyKey).digest("hex")}`;
56657
57289
  return this.call("/extract-site", body, this.timeoutMs, "POST", {
56658
57290
  "Idempotency-Key": idempotencyKey4
56659
57291
  });
@@ -56741,6 +57373,19 @@ var init_http_mcp_tool_executor = __esm({
56741
57373
  const { idempotencyKey: idempotencyKey4, ...body } = input;
56742
57374
  return this.callSiteExtractStart("extract_site", { ...body, background: true }, idempotencyKey4);
56743
57375
  }
57376
+ analyzeSiteSimilarity(input) {
57377
+ const { idempotencyKey: idempotencyKey4, similarityThreshold, similarityMaxPairs, ...body } = input;
57378
+ return this.callSiteExtractStart("analyze_site_similarity", {
57379
+ ...body,
57380
+ background: true,
57381
+ formats: ["markdown", "links", "json"],
57382
+ renderJavaScript: true,
57383
+ captureRenderedDom: true,
57384
+ semanticSimilarity: true,
57385
+ similarityThreshold,
57386
+ similarityMaxPairs
57387
+ }, idempotencyKey4);
57388
+ }
56744
57389
  auditSite(input) {
56745
57390
  const { idempotencyKey: idempotencyKey4, ...body } = input;
56746
57391
  const requestBody = {
@@ -57075,7 +57720,7 @@ var init_http_mcp_tool_executor = __esm({
57075
57720
  report: input.report,
57076
57721
  format: input.format
57077
57722
  }, this.timeoutMs, "POST", {
57078
- "Idempotency-Key": `analytics-export-${(0, import_node_crypto36.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
57723
+ "Idempotency-Key": `analytics-export-${(0, import_node_crypto37.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
57079
57724
  });
57080
57725
  }
57081
57726
  commonsSearchEntities(input) {
@@ -57114,7 +57759,7 @@ var init_http_mcp_tool_executor = __esm({
57114
57759
  commonsSubmitEntity(input) {
57115
57760
  const { idempotencyKey: idempotencyKey4, ...body } = input;
57116
57761
  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")}`
57762
+ "Idempotency-Key": `commons-${(0, import_node_crypto37.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
57118
57763
  });
57119
57764
  }
57120
57765
  commonsGetEntityLedger(input) {
@@ -57129,7 +57774,7 @@ var init_http_mcp_tool_executor = __esm({
57129
57774
  commonsUpdateEditorialArticle(input) {
57130
57775
  const { idempotencyKey: idempotencyKey4, ...body } = input;
57131
57776
  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")}`
57777
+ "Idempotency-Key": `commons-article-${(0, import_node_crypto37.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
57133
57778
  });
57134
57779
  }
57135
57780
  commonsSaveFilter(input) {
@@ -57150,13 +57795,13 @@ var init_http_mcp_tool_executor = __esm({
57150
57795
  commonsClaimPublication(input) {
57151
57796
  const { idempotencyKey: idempotencyKey4, ...body } = input;
57152
57797
  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")}`
57798
+ "Idempotency-Key": `commons-publication-claim-${(0, import_node_crypto37.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
57154
57799
  });
57155
57800
  }
57156
57801
  commonsPublishEditorial(input) {
57157
57802
  const { idempotencyKey: idempotencyKey4, ...body } = input;
57158
57803
  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")}`
57804
+ "Idempotency-Key": `commons-publication-publish-${(0, import_node_crypto37.createHash)("sha256").update(idempotencyKey4).digest("hex")}`
57160
57805
  });
57161
57806
  }
57162
57807
  commonsGetPublication(input) {
@@ -57164,13 +57809,13 @@ var init_http_mcp_tool_executor = __esm({
57164
57809
  return this.getJson(input.subdomain ? `/commons/publications/${encodeURIComponent(input.subdomain)}?${query}` : `/commons/publications/me?${query}`);
57165
57810
  }
57166
57811
  async captureSerpSnapshot(input) {
57167
- const fingerprint2 = (0, import_node_crypto36.createHash)("sha256").update(JSON.stringify(input)).digest("hex");
57812
+ const fingerprint2 = (0, import_node_crypto37.createHash)("sha256").update(JSON.stringify(input)).digest("hex");
57168
57813
  const now = Date.now();
57169
57814
  for (const [pendingFingerprint, pendingEntry] of this.pendingSerpCaptureBillingKeys) {
57170
57815
  if (pendingEntry.expiresAt <= now) this.pendingSerpCaptureBillingKeys.delete(pendingFingerprint);
57171
57816
  }
57172
57817
  const pending = this.pendingSerpCaptureBillingKeys.get(fingerprint2);
57173
- const idempotencyKey4 = pending && pending.expiresAt > now ? pending.key : (0, import_node_crypto36.randomUUID)();
57818
+ const idempotencyKey4 = pending && pending.expiresAt > now ? pending.key : (0, import_node_crypto37.randomUUID)();
57174
57819
  this.pendingSerpCaptureBillingKeys.set(fingerprint2, {
57175
57820
  key: idempotencyKey4,
57176
57821
  expiresAt: now + 15 * 6e4
@@ -61647,7 +62292,7 @@ async function createImageSourceArtifact(args) {
61647
62292
  if (args.content.length === 0 || args.content.length > IMAGE_SOURCE_MAX_BYTES) {
61648
62293
  throw new Error("image_source_size_invalid");
61649
62294
  }
61650
- const id = (0, import_node_crypto37.randomUUID)().replaceAll("-", "");
62295
+ const id = (0, import_node_crypto38.randomUUID)().replaceAll("-", "");
61651
62296
  return createPrivateArtifact({
61652
62297
  policy: policy4(),
61653
62298
  ownerId: args.ownerId,
@@ -61666,11 +62311,11 @@ async function readOwnedImageSourceArtifact(args) {
61666
62311
  maxBytes: IMAGE_SOURCE_MAX_BYTES
61667
62312
  });
61668
62313
  }
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;
62314
+ 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
62315
  var init_image_source_artifacts = __esm({
61671
62316
  "src/api/image-source-artifacts.ts"() {
61672
62317
  "use strict";
61673
- import_node_crypto37 = require("crypto");
62318
+ import_node_crypto38 = require("crypto");
61674
62319
  init_private_artifacts();
61675
62320
  IMAGE_SOURCE_ARTIFACT_PREFIX = "image-sources/";
61676
62321
  IMAGE_SOURCE_ARTIFACT_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
@@ -63284,7 +63929,7 @@ async function runBrowserAgentMigration() {
63284
63929
  }
63285
63930
  async function createExtensionRow(input) {
63286
63931
  const db = getDb();
63287
- const id = `bext_${(0, import_node_crypto38.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
63932
+ const id = `bext_${(0, import_node_crypto39.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
63288
63933
  await db.execute({
63289
63934
  sql: `INSERT INTO browser_agent_extensions (id, user_id, name, backend_id, backend_name, source, source_url, size_bytes)
63290
63935
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -63319,7 +63964,7 @@ async function deleteExtensionRow(userId, name) {
63319
63964
  }
63320
63965
  async function createAuthConnectionRow(input) {
63321
63966
  const db = getDb();
63322
- const connectionId = `authc_${(0, import_node_crypto38.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
63967
+ const connectionId = `authc_${(0, import_node_crypto39.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
63323
63968
  await db.execute({
63324
63969
  sql: `INSERT INTO browser_auth_connections (connection_id, domain, profile, account_email, note, status, browser_agent_session_id)
63325
63970
  VALUES (?, ?, ?, ?, ?, 'NEEDS_AUTH', ?)`,
@@ -63404,7 +64049,7 @@ async function deleteProfileLabel(userId, profile) {
63404
64049
  }
63405
64050
  async function createSessionRow(input) {
63406
64051
  const db = getDb();
63407
- const id = `bas_${(0, import_node_crypto38.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
64052
+ const id = `bas_${(0, import_node_crypto39.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
63408
64053
  await db.execute({
63409
64054
  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
64055
  VALUES (?, ?, ?, ?, 'open', ?, ?, ?, datetime('now'))`,
@@ -63481,7 +64126,7 @@ async function recordAction(input) {
63481
64126
  sql: `INSERT INTO browser_agent_actions (id, session_id, type, params_json, ok, error)
63482
64127
  VALUES (?, ?, ?, ?, ?, ?)`,
63483
64128
  args: [
63484
- `baa_${(0, import_node_crypto38.randomUUID)().replace(/-/g, "").slice(0, 20)}`,
64129
+ `baa_${(0, import_node_crypto39.randomUUID)().replace(/-/g, "").slice(0, 20)}`,
63485
64130
  input.sessionId,
63486
64131
  input.type,
63487
64132
  input.params == null ? null : JSON.stringify(input.params),
@@ -63521,11 +64166,11 @@ async function listReplayRows(sessionId) {
63521
64166
  });
63522
64167
  return res.rows;
63523
64168
  }
63524
- var import_node_crypto38, _ready2, _migrationPromise2, ORPHANED_SESSION_STATUS;
64169
+ var import_node_crypto39, _ready2, _migrationPromise2, ORPHANED_SESSION_STATUS;
63525
64170
  var init_browser_agent_db = __esm({
63526
64171
  "src/api/browser-agent-db.ts"() {
63527
64172
  "use strict";
63528
- import_node_crypto38 = require("crypto");
64173
+ import_node_crypto39 = require("crypto");
63529
64174
  init_db();
63530
64175
  _ready2 = false;
63531
64176
  _migrationPromise2 = null;
@@ -64672,8 +65317,8 @@ function kernelClient() {
64672
65317
  return new import_sdk11.default({ apiKey });
64673
65318
  }
64674
65319
  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);
65320
+ const digest2 = (0, import_node_crypto40.createHash)("sha256").update(`${userId}:${name}`).digest("hex").slice(0, 16);
65321
+ const nonce = (0, import_node_crypto40.randomUUID)().replace(/-/g, "").slice(0, 8);
64677
65322
  return `mcp-serp-${resource}-${digest2}-${nonce}`;
64678
65323
  }
64679
65324
  function isNotFound2(error) {
@@ -64751,11 +65396,11 @@ async function deleteSerpIdentity(userId, name) {
64751
65396
  throw error;
64752
65397
  }
64753
65398
  }
64754
- var import_node_crypto39, import_sdk11, MAX_SERP_IDENTITIES_PER_USER;
65399
+ var import_node_crypto40, import_sdk11, MAX_SERP_IDENTITIES_PER_USER;
64755
65400
  var init_serp_identity_service = __esm({
64756
65401
  "src/api/serp-identity-service.ts"() {
64757
65402
  "use strict";
64758
- import_node_crypto39 = require("crypto");
65403
+ import_node_crypto40 = require("crypto");
64759
65404
  import_sdk11 = __toESM(require("@onkernel/sdk"), 1);
64760
65405
  init_browser_service_env();
64761
65406
  init_serp_identity_db();
@@ -65466,7 +66111,7 @@ function buildBrowserAgentRoutes() {
65466
66111
  }
65467
66112
  const existing = await getExtensionRow(user.id, name);
65468
66113
  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, "")}`;
66114
+ const backendName2 = `u${user.id}_${(0, import_node_crypto41.randomUUID)().replace(/-/g, "")}`;
65470
66115
  try {
65471
66116
  const imported = await importExtensionFromStore(storeUrl, backendName2);
65472
66117
  const row = await createExtensionRow({
@@ -65525,11 +66170,11 @@ function buildBrowserAgentRoutes() {
65525
66170
  });
65526
66171
  return app2;
65527
66172
  }
65528
- var import_node_crypto40, import_hono24, auth, DEFAULT_BROWSER_SESSION_LOCK_TTL_SECONDS, EXTENSION_NAME_RE, SERP_IDENTITY_NAME_RE;
66173
+ var import_node_crypto41, import_hono24, auth, DEFAULT_BROWSER_SESSION_LOCK_TTL_SECONDS, EXTENSION_NAME_RE, SERP_IDENTITY_NAME_RE;
65529
66174
  var init_browser_agent_routes = __esm({
65530
66175
  "src/api/browser-agent-routes.ts"() {
65531
66176
  "use strict";
65532
- import_node_crypto40 = require("crypto");
66177
+ import_node_crypto41 = require("crypto");
65533
66178
  import_hono24 = require("hono");
65534
66179
  init_api_auth();
65535
66180
  init_errors();
@@ -66087,7 +66732,7 @@ async function getKeys() {
66087
66732
  const privateKey = await (0, import_jose2.importPKCS8)(pem, "RS256", { extractable: true });
66088
66733
  const full = await (0, import_jose2.exportJWK)(privateKey);
66089
66734
  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);
66735
+ 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
66736
  publicJwk.kid = kid;
66092
66737
  publicJwk.alg = "RS256";
66093
66738
  publicJwk.use = "sig";
@@ -66266,23 +66911,23 @@ async function validateAuthRequest(p) {
66266
66911
  }
66267
66912
  function pkceMatches(verifier, challenge) {
66268
66913
  if (!verifier) return false;
66269
- const computed = (0, import_node_crypto41.createHash)("sha256").update(verifier).digest("base64url");
66914
+ const computed = (0, import_node_crypto42.createHash)("sha256").update(verifier).digest("base64url");
66270
66915
  return computed === challenge;
66271
66916
  }
66272
66917
  async function mintAccessToken(identity, scope, plan, audience) {
66273
66918
  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);
66919
+ 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
66920
  }
66276
66921
  function tokenErrorResponse(c, error, description, status) {
66277
66922
  return c.json({ error, error_description: description }, status);
66278
66923
  }
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;
66924
+ 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
66925
  var init_oauth_routes = __esm({
66281
66926
  "src/api/oauth-routes.ts"() {
66282
66927
  "use strict";
66283
66928
  import_hono26 = require("hono");
66284
66929
  import_cookie = require("hono/cookie");
66285
- import_node_crypto41 = require("crypto");
66930
+ import_node_crypto42 = require("crypto");
66286
66931
  import_jose2 = require("jose");
66287
66932
  init_session();
66288
66933
  init_db();
@@ -66389,7 +67034,7 @@ var init_oauth_routes = __esm({
66389
67034
  }
66390
67035
  }
66391
67036
  const clientName = typeof body.client_name === "string" ? body.client_name : null;
66392
- const clientId = `client_${(0, import_node_crypto41.randomBytes)(16).toString("hex")}`;
67037
+ const clientId = `client_${(0, import_node_crypto42.randomBytes)(16).toString("hex")}`;
66393
67038
  await withOAuthDatabaseDeadline("register-client", registerClient(clientId, redirectUris, clientName));
66394
67039
  console.log("[oauth-dcr] register OK client_id=%s redirect_uris=%s", clientId, JSON.stringify(redirectUris));
66395
67040
  return c.json({
@@ -66442,7 +67087,7 @@ var init_oauth_routes = __esm({
66442
67087
  if (action3 === "deny") return redirectWithError(p.redirect_uri, p.state, "access_denied");
66443
67088
  if (action3 !== "approve") return c.text("unsupported action", 400);
66444
67089
  const scope = negotiateScope(p.scope, user, p.resource);
66445
- const code = `code_${(0, import_node_crypto41.randomBytes)(32).toString("base64url")}`;
67090
+ const code = `code_${(0, import_node_crypto42.randomBytes)(32).toString("base64url")}`;
66446
67091
  const expiresAt = new Date(Date.now() + CODE_TTL_SECONDS * 1e3).toISOString();
66447
67092
  await withOAuthDatabaseDeadline("put-authorization-code", putCode({
66448
67093
  code,
@@ -66481,7 +67126,7 @@ var init_oauth_routes = __esm({
66481
67126
  const plan = user ? resolvePlan(user) : "free";
66482
67127
  const audience = record.resource ?? RESOURCE();
66483
67128
  const accessToken = await mintAccessToken(record.identity, record.scope, plan, audience);
66484
- const refreshToken = `rt_${(0, import_node_crypto41.randomBytes)(40).toString("base64url")}`;
67129
+ const refreshToken = `rt_${(0, import_node_crypto42.randomBytes)(40).toString("base64url")}`;
66485
67130
  await withOAuthDatabaseDeadline("put-refresh-token", putRefresh({
66486
67131
  refresh_token: refreshToken,
66487
67132
  client_id: clientId,
@@ -66511,7 +67156,7 @@ var init_oauth_routes = __esm({
66511
67156
  const plan = user ? resolvePlan(user) : "free";
66512
67157
  const audience = record.resource ?? RESOURCE();
66513
67158
  const accessToken = await mintAccessToken(record.identity, record.scope, plan, audience);
66514
- const nextRefresh = `rt_${(0, import_node_crypto41.randomBytes)(40).toString("base64url")}`;
67159
+ const nextRefresh = `rt_${(0, import_node_crypto42.randomBytes)(40).toString("base64url")}`;
66515
67160
  await withOAuthDatabaseDeadline("rotate-refresh-token", rotateRefresh(refreshToken, {
66516
67161
  refresh_token: nextRefresh,
66517
67162
  client_id: record.client_id,
@@ -67931,7 +68576,7 @@ function quoteUntrusted(value) {
67931
68576
  return clean3.split("\n").map((line) => `> ${line}`).join("\n");
67932
68577
  }
67933
68578
  function shortHash(value, length = 24) {
67934
- return (0, import_node_crypto42.createHash)("sha256").update(value).digest("hex").slice(0, length);
68579
+ return (0, import_node_crypto43.createHash)("sha256").update(value).digest("hex").slice(0, length);
67935
68580
  }
67936
68581
  function safePathPart(value) {
67937
68582
  return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "contact";
@@ -68419,11 +69064,11 @@ async function captureSupportMessage(memoryKey, email, options) {
68419
69064
  direction: options.direction
68420
69065
  };
68421
69066
  }
68422
- var import_node_crypto42, SUPPORT_TAGS, ISSUE_TAGS;
69067
+ var import_node_crypto43, SUPPORT_TAGS, ISSUE_TAGS;
68423
69068
  var init_resend_support_thread = __esm({
68424
69069
  "src/api/resend-support-thread.ts"() {
68425
69070
  "use strict";
68426
- import_node_crypto42 = require("crypto");
69071
+ import_node_crypto43 = require("crypto");
68427
69072
  init_memory();
68428
69073
  SUPPORT_TAGS = [
68429
69074
  {
@@ -69008,7 +69653,7 @@ async function claimWorkshopRegistration(input) {
69008
69653
  ) < ?
69009
69654
  `,
69010
69655
  args: [
69011
- (0, import_node_crypto43.randomUUID)(),
69656
+ (0, import_node_crypto44.randomUUID)(),
69012
69657
  input.eventSlug,
69013
69658
  input.email,
69014
69659
  input.firstName,
@@ -69025,7 +69670,7 @@ async function claimWorkshopRegistration(input) {
69025
69670
  id, event_slug, email, first_name, last_name, status
69026
69671
  ) VALUES (?, ?, ?, ?, ?, 'waitlisted')
69027
69672
  `,
69028
- args: [(0, import_node_crypto43.randomUUID)(), input.eventSlug, input.email, input.firstName, input.lastName || null]
69673
+ args: [(0, import_node_crypto44.randomUUID)(), input.eventSlug, input.email, input.firstName, input.lastName || null]
69029
69674
  });
69030
69675
  const waitlisted = await getWorkshopRegistration(input.eventSlug, input.email);
69031
69676
  if (!waitlisted) throw new Error("workshop registration could not be claimed");
@@ -69040,7 +69685,7 @@ async function registerWorkshopInterest(input) {
69040
69685
  id, event_slug, email, first_name, last_name, status
69041
69686
  ) VALUES (?, ?, ?, ?, ?, 'interested')
69042
69687
  `,
69043
- args: [(0, import_node_crypto43.randomUUID)(), input.eventSlug, input.email, input.firstName, input.lastName || null]
69688
+ args: [(0, import_node_crypto44.randomUUID)(), input.eventSlug, input.email, input.firstName, input.lastName || null]
69044
69689
  });
69045
69690
  const registration = await getWorkshopRegistration(input.eventSlug, input.email);
69046
69691
  if (!registration) throw new Error("workshop interest could not be recorded");
@@ -69070,11 +69715,11 @@ async function updateWorkshopRegistration(id, patch) {
69070
69715
  args: [...entries.map(([, value]) => value), id]
69071
69716
  });
69072
69717
  }
69073
- var import_node_crypto43;
69718
+ var import_node_crypto44;
69074
69719
  var init_workshop_registration_repository = __esm({
69075
69720
  "src/api/workshop-registration-repository.ts"() {
69076
69721
  "use strict";
69077
- import_node_crypto43 = require("crypto");
69722
+ import_node_crypto44 = require("crypto");
69078
69723
  init_db();
69079
69724
  }
69080
69725
  });
@@ -69647,7 +70292,7 @@ function renderEditorialReadingRoom(input, now = /* @__PURE__ */ new Date()) {
69647
70292
  articleCount: articles.length,
69648
70293
  wordCount: totalWordCount,
69649
70294
  bytes,
69650
- sha256: (0, import_node_crypto44.createHash)("sha256").update(html).digest("hex"),
70295
+ sha256: (0, import_node_crypto45.createHash)("sha256").update(html).digest("hex"),
69651
70296
  warnings
69652
70297
  };
69653
70298
  }
@@ -69730,14 +70375,14 @@ ${provenance}`;
69730
70375
  ...rendered,
69731
70376
  html,
69732
70377
  bytes: Buffer.byteLength(html),
69733
- sha256: (0, import_node_crypto44.createHash)("sha256").update(html).digest("hex")
70378
+ sha256: (0, import_node_crypto45.createHash)("sha256").update(html).digest("hex")
69734
70379
  };
69735
70380
  }
69736
- var import_node_crypto44, import_node_fs14, import_node_path21, import_marked, runtimeEntryDir, assetCache;
70381
+ var import_node_crypto45, import_node_fs14, import_node_path21, import_marked, runtimeEntryDir, assetCache;
69737
70382
  var init_render = __esm({
69738
70383
  "src/editorial-reading-room/render.ts"() {
69739
70384
  "use strict";
69740
- import_node_crypto44 = require("crypto");
70385
+ import_node_crypto45 = require("crypto");
69741
70386
  import_node_fs14 = require("fs");
69742
70387
  import_node_path21 = require("path");
69743
70388
  import_marked = require("marked");
@@ -70001,7 +70646,7 @@ async function hostCommonsImage(input) {
70001
70646
  415
70002
70647
  );
70003
70648
  }
70004
- const digest2 = (0, import_node_crypto45.createHash)("sha256").update(bytes).digest("hex");
70649
+ const digest2 = (0, import_node_crypto46.createHash)("sha256").update(bytes).digest("hex");
70005
70650
  const existing = await getDb().execute({
70006
70651
  sql: "SELECT id, url, content_type, bytes, source_url FROM commons_images WHERE digest = ? LIMIT 1",
70007
70652
  args: [digest2]
@@ -70089,11 +70734,11 @@ async function hostEntityImages(input) {
70089
70734
  }
70090
70735
  return rewrite;
70091
70736
  }
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;
70737
+ 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
70738
  var init_commons_image_store = __esm({
70094
70739
  "src/api/commons-image-store.ts"() {
70095
70740
  "use strict";
70096
- import_node_crypto45 = require("crypto");
70741
+ import_node_crypto46 = require("crypto");
70097
70742
  import_promises16 = require("dns/promises");
70098
70743
  import_node_net2 = require("net");
70099
70744
  init_blob_store();
@@ -70116,141 +70761,6 @@ var init_commons_image_store = __esm({
70116
70761
  }
70117
70762
  });
70118
70763
 
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
70764
  // src/api/schema-presence.ts
70255
70765
  async function loadSchemaObjects() {
70256
70766
  const res = await getDb().execute(
@@ -70353,7 +70863,7 @@ function buildCommonsLinkset(entity, claims) {
70353
70863
  context[claim.predicate] = targets;
70354
70864
  }
70355
70865
  const document2 = { linkset: [context] };
70356
- const etag = `"${(0, import_node_crypto46.createHash)("sha256").update(JSON.stringify(document2)).digest("hex")}"`;
70866
+ const etag = `"${(0, import_node_crypto47.createHash)("sha256").update(JSON.stringify(document2)).digest("hex")}"`;
70357
70867
  return { document: document2, etag, publicUrl, linksetUrl, profile: COMMONS_RELATIONSHIP_PROFILE };
70358
70868
  }
70359
70869
  function commonsLinksetDiscoveryHeader(idOrSlug) {
@@ -70421,11 +70931,11 @@ function normalizeHreflang(value) {
70421
70931
  const languages = [...new Set(value.map((item) => optionalText(item, 80)).filter((item) => Boolean(item)))];
70422
70932
  return languages.length ? languages : void 0;
70423
70933
  }
70424
- var import_node_crypto46, COMMONS_LINKSET_MEDIA_TYPE, COMMONS_RELATIONSHIP_PROFILE, REGISTERED_RELATIONS;
70934
+ var import_node_crypto47, COMMONS_LINKSET_MEDIA_TYPE, COMMONS_RELATIONSHIP_PROFILE, REGISTERED_RELATIONS;
70425
70935
  var init_commons_linksets = __esm({
70426
70936
  "src/api/commons-linksets.ts"() {
70427
70937
  "use strict";
70428
- import_node_crypto46 = require("crypto");
70938
+ import_node_crypto47 = require("crypto");
70429
70939
  COMMONS_LINKSET_MEDIA_TYPE = "application/linkset+json";
70430
70940
  COMMONS_RELATIONSHIP_PROFILE = "https://mcpscraper.dev/commons/profiles/relationships/v1";
70431
70941
  REGISTERED_RELATIONS = /* @__PURE__ */ new Set([
@@ -71065,7 +71575,7 @@ async function submitCommonsEntity(input, user) {
71065
71575
  const existingClaims = existing && normalized.claims !== void 0 ? await getCommonsClaimsByEntityId(existing.id) : [];
71066
71576
  const safety = evaluatePublishSafety(normalized, existing);
71067
71577
  const shouldApply = safety.safe && normalized.reviewPolicy !== "always_review";
71068
- const proposalId = `commons-proposal-${(0, import_node_crypto47.randomUUID)()}`;
71578
+ const proposalId = `commons-proposal-${(0, import_node_crypto48.randomUUID)()}`;
71069
71579
  const now = (/* @__PURE__ */ new Date()).toISOString();
71070
71580
  const entityId = existing?.id ?? normalized.entityId ?? allocateEntityId();
71071
71581
  const proposalStatus = shouldApply ? "accepted" : "pending_review";
@@ -71120,7 +71630,7 @@ async function submitCommonsEntity(input, user) {
71120
71630
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
71121
71631
  `,
71122
71632
  args: [
71123
- `commons-ledger-${(0, import_node_crypto47.randomUUID)()}`,
71633
+ `commons-ledger-${(0, import_node_crypto48.randomUUID)()}`,
71124
71634
  nextEntity.id,
71125
71635
  proposalId,
71126
71636
  user.id,
@@ -71226,7 +71736,7 @@ async function saveCommonsFilter(input, user) {
71226
71736
  sql: "SELECT id FROM commons_saved_filters WHERE user_id = ? AND name = ? LIMIT 1",
71227
71737
  args: [user.id, name]
71228
71738
  });
71229
- const id = existing.rows[0]?.id != null ? String(existing.rows[0].id) : `commons-filter-${(0, import_node_crypto47.randomUUID)()}`;
71739
+ const id = existing.rows[0]?.id != null ? String(existing.rows[0].id) : `commons-filter-${(0, import_node_crypto48.randomUUID)()}`;
71230
71740
  await getDb().execute({
71231
71741
  sql: `
71232
71742
  INSERT INTO commons_saved_filters (id, user_id, name, description, filter_json, created_at, updated_at)
@@ -72004,7 +72514,7 @@ function commonsIndexDocuments(entity, now) {
72004
72514
  return documents;
72005
72515
  }
72006
72516
  function commonsIndexDocumentId(entityId, documentType, documentKey) {
72007
- return `commons-index-${(0, import_node_crypto47.createHash)("sha256").update(`${entityId}
72517
+ return `commons-index-${(0, import_node_crypto48.createHash)("sha256").update(`${entityId}
72008
72518
  ${documentType}
72009
72519
  ${documentKey}`).digest("hex").slice(0, 32)}`;
72010
72520
  }
@@ -72570,11 +73080,11 @@ function jsonLikeValue(value) {
72570
73080
  function escapeLike(value) {
72571
73081
  return value.replace(/[%_]/g, "");
72572
73082
  }
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;
73083
+ 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
73084
  var init_commons_repository = __esm({
72575
73085
  "src/api/commons-repository.ts"() {
72576
73086
  "use strict";
72577
- import_node_crypto47 = require("crypto");
73087
+ import_node_crypto48 = require("crypto");
72578
73088
  init_db();
72579
73089
  init_commons_image_store();
72580
73090
  init_commons_embeddings();
@@ -72953,7 +73463,7 @@ async function claimCommonsPublication(input, user) {
72953
73463
  }
72954
73464
  const existingName = await getCommonsPublicationBySubdomain(subdomain);
72955
73465
  if (existingName) throw new CommonsPublicationError("publication_name_unavailable", "That publication name is already claimed.", 409);
72956
- const id = `tcpub_${(0, import_node_crypto48.randomUUID)()}`;
73466
+ const id = `tcpub_${(0, import_node_crypto49.randomUUID)()}`;
72957
73467
  const now = (/* @__PURE__ */ new Date()).toISOString();
72958
73468
  try {
72959
73469
  await getDb().execute({
@@ -73005,7 +73515,7 @@ async function publishCommonsEditorial(input, user) {
73005
73515
  const canonicalUrl = editionPublicUrl(subdomain, editionSlug);
73006
73516
  const rendered = renderEditorialReadingRoom(editionInput);
73007
73517
  const html = addPublicMetadata(rendered.html, canonicalUrl, publication.title);
73008
- const editionId = `tced_${(0, import_node_crypto48.randomUUID)()}`;
73518
+ const editionId = `tced_${(0, import_node_crypto49.randomUUID)()}`;
73009
73519
  const revision = (latest?.revision ?? 0) + 1;
73010
73520
  const now = (/* @__PURE__ */ new Date()).toISOString();
73011
73521
  await getDb().batch([
@@ -73029,7 +73539,7 @@ async function publishCommonsEditorial(input, user) {
73029
73539
  JSON.stringify(editionInput.articles),
73030
73540
  html,
73031
73541
  rendered.filename,
73032
- (0, import_node_crypto48.createHash)("sha256").update(html).digest("hex"),
73542
+ (0, import_node_crypto49.createHash)("sha256").update(html).digest("hex"),
73033
73543
  rendered.articleCount,
73034
73544
  rendered.wordCount,
73035
73545
  Buffer.byteLength(html),
@@ -73087,7 +73597,7 @@ async function updateCommonsEditorialArticle(input, user) {
73087
73597
  const canonicalUrl = editionPublicUrl(subdomain, editionSlug);
73088
73598
  const rendered = renderEditorialReadingRoom(editionInput);
73089
73599
  const html = addPublicMetadata(rendered.html, canonicalUrl, publication.title);
73090
- const editionId = `tced_${(0, import_node_crypto48.randomUUID)()}`;
73600
+ const editionId = `tced_${(0, import_node_crypto49.randomUUID)()}`;
73091
73601
  const revision = latest.revision + 1;
73092
73602
  const now = (/* @__PURE__ */ new Date()).toISOString();
73093
73603
  await getDb().batch([
@@ -73111,7 +73621,7 @@ async function updateCommonsEditorialArticle(input, user) {
73111
73621
  JSON.stringify(nextArticles),
73112
73622
  html,
73113
73623
  rendered.filename,
73114
- (0, import_node_crypto48.createHash)("sha256").update(html).digest("hex"),
73624
+ (0, import_node_crypto49.createHash)("sha256").update(html).digest("hex"),
73115
73625
  rendered.articleCount,
73116
73626
  rendered.wordCount,
73117
73627
  Buffer.byteLength(html),
@@ -73316,11 +73826,11 @@ function addPublicMetadata(html, canonicalUrl, publicationTitle) {
73316
73826
  "</head>"
73317
73827
  ].join("\n"));
73318
73828
  }
73319
- var import_node_crypto48, PUBLICATION_ROOT_DOMAIN, RESERVED_SUBDOMAINS, CommonsPublicationError;
73829
+ var import_node_crypto49, PUBLICATION_ROOT_DOMAIN, RESERVED_SUBDOMAINS, CommonsPublicationError;
73320
73830
  var init_commons_publication_repository = __esm({
73321
73831
  "src/api/commons-publication-repository.ts"() {
73322
73832
  "use strict";
73323
- import_node_crypto48 = require("crypto");
73833
+ import_node_crypto49 = require("crypto");
73324
73834
  init_db();
73325
73835
  init_commons_repository();
73326
73836
  init_render();
@@ -74553,7 +75063,7 @@ async function migrateAnalytics() {
74553
75063
  }
74554
75064
  function normalizeSlug2(value) {
74555
75065
  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")}`;
75066
+ return slug4 || `site-${(0, import_node_crypto50.randomBytes)(4).toString("hex")}`;
74557
75067
  }
74558
75068
  function normalizeObservedHostname(origin) {
74559
75069
  if (!origin || origin === "null") return null;
@@ -74566,7 +75076,7 @@ function normalizeObservedHostname(origin) {
74566
75076
  }
74567
75077
  }
74568
75078
  function publicPixelId() {
74569
- return `px_${(0, import_node_crypto49.randomBytes)(18).toString("base64url")}`;
75079
+ return `px_${(0, import_node_crypto50.randomBytes)(18).toString("base64url")}`;
74570
75080
  }
74571
75081
  function mapRows(rows) {
74572
75082
  return rows;
@@ -74598,7 +75108,7 @@ async function requireEditor(client2, siteId, userId) {
74598
75108
  async function createAnalyticsSite(input) {
74599
75109
  const db = getAnalyticsPool();
74600
75110
  const client2 = await db.connect();
74601
- const id = (0, import_node_crypto49.randomUUID)();
75111
+ const id = (0, import_node_crypto50.randomUUID)();
74602
75112
  const baseSlug = normalizeSlug2(input.slug || input.name);
74603
75113
  try {
74604
75114
  await client2.query("BEGIN");
@@ -74614,7 +75124,7 @@ async function createAnalyticsSite(input) {
74614
75124
  } catch (error) {
74615
75125
  const code = error.code;
74616
75126
  if (code !== "23505" || attempt === 2) throw error;
74617
- slug4 = `${baseSlug}-${(0, import_node_crypto49.randomBytes)(2).toString("hex")}`;
75127
+ slug4 = `${baseSlug}-${(0, import_node_crypto50.randomBytes)(2).toString("hex")}`;
74618
75128
  }
74619
75129
  }
74620
75130
  await client2.query(
@@ -74829,7 +75339,7 @@ async function createAnalyticsPixel(input) {
74829
75339
  VALUES ($1, $2, $3, $4, $5)
74830
75340
  RETURNING id, site_id, public_id, name, environment, status, created_at::text, NULL::text AS last_event_at`,
74831
75341
  [
74832
- (0, import_node_crypto49.randomUUID)(),
75342
+ (0, import_node_crypto50.randomUUID)(),
74833
75343
  input.siteId,
74834
75344
  publicPixelId(),
74835
75345
  input.name.trim(),
@@ -74906,7 +75416,7 @@ async function setAnalyticsPixelDomainState(input) {
74906
75416
  SELECT $1, p.id, $4, $5 FROM analytics_pixels p WHERE p.id = $2 AND p.site_id = $3
74907
75417
  ON CONFLICT(pixel_id, hostname) DO UPDATE SET state = EXCLUDED.state, updated_at = now()
74908
75418
  RETURNING id`,
74909
- [(0, import_node_crypto49.randomUUID)(), input.pixelId, input.siteId, hostname, input.state]
75419
+ [(0, import_node_crypto50.randomUUID)(), input.pixelId, input.siteId, hostname, input.state]
74910
75420
  );
74911
75421
  if (!result.rowCount)
74912
75422
  throw new AnalyticsRepositoryError(
@@ -74956,7 +75466,7 @@ function sanitizeAnalyticsProperties(value) {
74956
75466
  }
74957
75467
  async function ingestAnalyticsEvents(input) {
74958
75468
  const db = getAnalyticsPool();
74959
- const requestId = `air_${(0, import_node_crypto49.randomUUID)()}`;
75469
+ const requestId = `air_${(0, import_node_crypto50.randomUUID)()}`;
74960
75470
  const hostname = normalizeObservedHostname(input.origin);
74961
75471
  const pixelResult = await db.query(
74962
75472
  `SELECT p.id, p.site_id, p.status,
@@ -74977,7 +75487,7 @@ async function ingestAnalyticsEvents(input) {
74977
75487
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
74978
75488
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
74979
75489
  [
74980
- (0, import_node_crypto49.randomUUID)(),
75490
+ (0, import_node_crypto50.randomUUID)(),
74981
75491
  requestId,
74982
75492
  pixel?.site_id ?? null,
74983
75493
  pixel?.id ?? null,
@@ -75002,7 +75512,7 @@ async function ingestAnalyticsEvents(input) {
75002
75512
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
75003
75513
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
75004
75514
  [
75005
- (0, import_node_crypto49.randomUUID)(),
75515
+ (0, import_node_crypto50.randomUUID)(),
75006
75516
  requestId,
75007
75517
  pixel.site_id,
75008
75518
  pixel.id,
@@ -75027,7 +75537,7 @@ async function ingestAnalyticsEvents(input) {
75027
75537
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, rejected_count, reason_codes)
75028
75538
  VALUES ($1, $2, $3, $4, $5, $6::jsonb)`,
75029
75539
  [
75030
- (0, import_node_crypto49.randomUUID)(),
75540
+ (0, import_node_crypto50.randomUUID)(),
75031
75541
  requestId,
75032
75542
  pixel.site_id,
75033
75543
  pixel.id,
@@ -75051,7 +75561,7 @@ async function ingestAnalyticsEvents(input) {
75051
75561
  ON CONFLICT(pixel_id, hostname) DO UPDATE
75052
75562
  SET last_seen_at = now(), updated_at = now()
75053
75563
  RETURNING state`,
75054
- [(0, import_node_crypto49.randomUUID)(), pixel.id, hostname]
75564
+ [(0, import_node_crypto50.randomUUID)(), pixel.id, hostname]
75055
75565
  );
75056
75566
  if (domainResult.rows[0]?.state !== "approved") {
75057
75567
  await db.query(
@@ -75065,7 +75575,7 @@ async function ingestAnalyticsEvents(input) {
75065
75575
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
75066
75576
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
75067
75577
  [
75068
- (0, import_node_crypto49.randomUUID)(),
75578
+ (0, import_node_crypto50.randomUUID)(),
75069
75579
  requestId,
75070
75580
  pixel.site_id,
75071
75581
  pixel.id,
@@ -75115,7 +75625,7 @@ async function ingestAnalyticsEvents(input) {
75115
75625
  $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33
75116
75626
  ) ON CONFLICT(site_id, event_id) DO NOTHING`,
75117
75627
  [
75118
- (0, import_node_crypto49.randomUUID)(),
75628
+ (0, import_node_crypto50.randomUUID)(),
75119
75629
  event2.eventId,
75120
75630
  pixel.site_id,
75121
75631
  pixel.id,
@@ -75173,7 +75683,7 @@ async function ingestAnalyticsEvents(input) {
75173
75683
  id, request_id, site_id, pixel_id, hostname, accepted_count, rejected_count, reason_codes
75174
75684
  ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb)`,
75175
75685
  [
75176
- (0, import_node_crypto49.randomUUID)(),
75686
+ (0, import_node_crypto50.randomUUID)(),
75177
75687
  requestId,
75178
75688
  pixel.site_id,
75179
75689
  pixel.id,
@@ -75205,7 +75715,7 @@ function normalizeGeographyCode(value, max) {
75205
75715
  return normalized && /^[A-Z0-9-]+$/.test(normalized) ? normalized.slice(0, max) : null;
75206
75716
  }
75207
75717
  function pageFingerprint(scope, siteId, filters) {
75208
- return (0, import_node_crypto49.createHash)("sha256").update(JSON.stringify({ scope, siteId, filters })).digest("base64url").slice(0, 18);
75718
+ return (0, import_node_crypto50.createHash)("sha256").update(JSON.stringify({ scope, siteId, filters })).digest("base64url").slice(0, 18);
75209
75719
  }
75210
75720
  function decodePageOffset(cursor, fingerprint2) {
75211
75721
  if (!cursor) return 0;
@@ -75255,7 +75765,7 @@ async function createAnalyticsConversion(input) {
75255
75765
  404
75256
75766
  );
75257
75767
  }
75258
- const id = (0, import_node_crypto49.randomUUID)();
75768
+ const id = (0, import_node_crypto50.randomUUID)();
75259
75769
  const resolvedPerson = input.sessionId ? await db.query(
75260
75770
  `SELECT e.person_id FROM analytics_identity_nodes n JOIN analytics_identity_edges e ON e.identity_node_id = n.id
75261
75771
  WHERE n.site_id = $1 AND n.kind = 'session_id' AND n.value_hmac = $2 ORDER BY e.confidence DESC LIMIT 1`,
@@ -76004,7 +76514,7 @@ async function createAnalyticsCampaignLink(input) {
76004
76514
  404
76005
76515
  );
76006
76516
  }
76007
- const shortCode = (input.shortCode?.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-") || (0, import_node_crypto49.randomBytes)(5).toString("base64url").toLowerCase()).slice(0, 48);
76517
+ const shortCode = (input.shortCode?.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-") || (0, import_node_crypto50.randomBytes)(5).toString("base64url").toLowerCase()).slice(0, 48);
76008
76518
  try {
76009
76519
  const result = await db.query(
76010
76520
  `INSERT INTO analytics_campaign_links(
@@ -76013,7 +76523,7 @@ async function createAnalyticsCampaignLink(input) {
76013
76523
  ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15)
76014
76524
  RETURNING *, 0::int AS click_count`,
76015
76525
  [
76016
- (0, import_node_crypto49.randomUUID)(),
76526
+ (0, import_node_crypto50.randomUUID)(),
76017
76527
  input.siteId,
76018
76528
  input.pixelId ?? null,
76019
76529
  input.name.trim(),
@@ -76084,7 +76594,7 @@ async function resolveAnalyticsCampaignLink(shortCode, referrer) {
76084
76594
  if (!row) return null;
76085
76595
  await db.query(
76086
76596
  `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)]
76597
+ [(0, import_node_crypto50.randomUUID)(), row.id, normalizeAnalyticsUrl(referrer || void 0)]
76088
76598
  );
76089
76599
  return buildTaggedCampaignUrl(row);
76090
76600
  }
@@ -76102,14 +76612,14 @@ async function createAnalyticsForm(input) {
76102
76612
  404
76103
76613
  );
76104
76614
  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")}`;
76615
+ const slug4 = `${baseSlug}-${(0, import_node_crypto50.randomBytes)(3).toString("hex")}`;
76616
+ const publicId = `form_${(0, import_node_crypto50.randomBytes)(18).toString("base64url")}`;
76107
76617
  const result = await db.query(
76108
76618
  `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
76619
  VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8::jsonb,$9,$10,$11,$12,$13,$14)
76110
76620
  RETURNING *, 0::int AS submission_count`,
76111
76621
  [
76112
- (0, import_node_crypto49.randomUUID)(),
76622
+ (0, import_node_crypto50.randomUUID)(),
76113
76623
  publicId,
76114
76624
  input.siteId,
76115
76625
  input.pixelId,
@@ -76154,7 +76664,7 @@ async function getPublicAnalyticsForm(publicId) {
76154
76664
  return result.rows[0] ?? null;
76155
76665
  }
76156
76666
  async function recordAnalyticsFormSubmission(input) {
76157
- const id = (0, import_node_crypto49.randomUUID)();
76667
+ const id = (0, import_node_crypto50.randomUUID)();
76158
76668
  await getAnalyticsPool().query(
76159
76669
  `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
76670
  VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12::jsonb)`,
@@ -76185,7 +76695,7 @@ function sanitizeClickIds(value) {
76185
76695
  return output;
76186
76696
  }
76187
76697
  function identityHmac(kind, value) {
76188
- return (0, import_node_crypto49.createHmac)("sha256", getSessionSecret()).update(`${kind}:${value.trim().toLowerCase()}`).digest("hex");
76698
+ return (0, import_node_crypto50.createHmac)("sha256", getSessionSecret()).update(`${kind}:${value.trim().toLowerCase()}`).digest("hex");
76189
76699
  }
76190
76700
  async function linkAnalyticsFormIdentity(input) {
76191
76701
  const client2 = await getAnalyticsPool().connect();
@@ -76194,7 +76704,7 @@ async function linkAnalyticsFormIdentity(input) {
76194
76704
  const person = await client2.query(
76195
76705
  `INSERT INTO analytics_people(id, site_id, crm_person_ref) VALUES ($1,$2,$3)
76196
76706
  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]
76707
+ [(0, import_node_crypto50.randomUUID)(), input.siteId, input.crmPersonRef]
76198
76708
  );
76199
76709
  const personId = person.rows[0].id;
76200
76710
  const signals = [];
@@ -76241,7 +76751,7 @@ async function linkAnalyticsFormIdentity(input) {
76241
76751
  `INSERT INTO analytics_identity_nodes(id, site_id, kind, value_hmac) VALUES ($1,$2,$3,$4)
76242
76752
  ON CONFLICT(site_id, kind, value_hmac) DO UPDATE SET last_seen_at = now() RETURNING id`,
76243
76753
  [
76244
- (0, import_node_crypto49.randomUUID)(),
76754
+ (0, import_node_crypto50.randomUUID)(),
76245
76755
  input.siteId,
76246
76756
  signal.kind,
76247
76757
  identityHmac(signal.kind, signal.value)
@@ -76252,7 +76762,7 @@ async function linkAnalyticsFormIdentity(input) {
76252
76762
  VALUES ($1,$2,$3,$4,$5,$6)
76253
76763
  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
76764
  [
76255
- (0, import_node_crypto49.randomUUID)(),
76765
+ (0, import_node_crypto50.randomUUID)(),
76256
76766
  input.siteId,
76257
76767
  personId,
76258
76768
  node.rows[0].id,
@@ -76350,7 +76860,7 @@ async function createAnalyticsCrmImport(input) {
76350
76860
  const db = getAnalyticsPool();
76351
76861
  await requireEditor(db, input.siteId, input.userId);
76352
76862
  const client2 = await db.connect();
76353
- const id = (0, import_node_crypto49.randomUUID)();
76863
+ const id = (0, import_node_crypto50.randomUUID)();
76354
76864
  try {
76355
76865
  await client2.query("BEGIN");
76356
76866
  await client2.query(
@@ -76370,7 +76880,7 @@ async function createAnalyticsCrmImport(input) {
76370
76880
  await client2.query(
76371
76881
  `INSERT INTO analytics_crm_import_rows(id, import_id, crm_person_ref, payload_ciphertext)
76372
76882
  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]
76883
+ [(0, import_node_crypto50.randomUUID)(), id, row.crmPersonRef, row.payloadCiphertext]
76374
76884
  );
76375
76885
  }
76376
76886
  await client2.query("COMMIT");
@@ -76409,7 +76919,7 @@ async function createAnalyticsActivationDestination(input) {
76409
76919
  `INSERT INTO analytics_activation_destinations(id, site_id, platform, name, connection_ref, external_dataset_id, event_mapping, created_by_user_id)
76410
76920
  VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8) RETURNING *`,
76411
76921
  [
76412
- (0, import_node_crypto49.randomUUID)(),
76922
+ (0, import_node_crypto50.randomUUID)(),
76413
76923
  input.siteId,
76414
76924
  input.platform,
76415
76925
  input.name.trim(),
@@ -76461,7 +76971,7 @@ async function queueAnalyticsActivation(input) {
76461
76971
  `INSERT INTO analytics_activation_jobs(id, destination_id, conversion_id, person_id, payload_ciphertext)
76462
76972
  VALUES ($1,$2,$3,$4,$5) ON CONFLICT(destination_id, conversion_id) DO NOTHING`,
76463
76973
  [
76464
- (0, import_node_crypto49.randomUUID)(),
76974
+ (0, import_node_crypto50.randomUUID)(),
76465
76975
  destination.id,
76466
76976
  input.conversionId,
76467
76977
  input.personId ?? null,
@@ -76473,7 +76983,7 @@ async function queueAnalyticsActivation(input) {
76473
76983
  return queued;
76474
76984
  }
76475
76985
  async function queueAnalyticsFormDelivery(input) {
76476
- const id = (0, import_node_crypto49.randomUUID)();
76986
+ const id = (0, import_node_crypto50.randomUUID)();
76477
76987
  await getAnalyticsPool().query(
76478
76988
  `INSERT INTO analytics_form_delivery_jobs(id, submission_id, owner_user_id, payload_ciphertext, last_error_code)
76479
76989
  VALUES ($1,$2,$3,$4,$5)`,
@@ -76600,7 +77110,7 @@ async function analyticsHealth(siteId, userId) {
76600
77110
  }
76601
77111
  async function refreshAnalyticsDailyRollups(input) {
76602
77112
  const db = getAnalyticsPool();
76603
- const runId = (0, import_node_crypto49.randomUUID)();
77113
+ const runId = (0, import_node_crypto50.randomUUID)();
76604
77114
  await db.query(
76605
77115
  `INSERT INTO analytics_rollup_runs(id, window_start, window_end, status) VALUES ($1, $2, $3, 'running')`,
76606
77116
  [runId, input.start, input.end]
@@ -76694,7 +77204,7 @@ async function refreshAnalyticsDailyRollupsIfDue(now = /* @__PURE__ */ new Date(
76694
77204
  lockClient.release();
76695
77205
  }
76696
77206
  }
76697
- function csvCell2(value) {
77207
+ function csvCell3(value) {
76698
77208
  const text2 = value == null ? "" : String(value);
76699
77209
  return /[\n\r,\"]/.test(text2) ? `"${text2.replaceAll('"', '""')}"` : text2;
76700
77210
  }
@@ -76704,7 +77214,7 @@ function rowsToCsv2(rows) {
76704
77214
  return [
76705
77215
  columns.join(","),
76706
77216
  ...rows.map(
76707
- (row) => columns.map((column) => csvCell2(row[column])).join(",")
77217
+ (row) => columns.map((column) => csvCell3(row[column])).join(",")
76708
77218
  )
76709
77219
  ].join("\n");
76710
77220
  }
@@ -76797,7 +77307,7 @@ async function createAnalyticsExport(input) {
76797
77307
  `Generated ${(/* @__PURE__ */ new Date()).toISOString()} from the governed ${input.report} report contract.`
76798
77308
  ].join("\n");
76799
77309
  }
76800
- const id = (0, import_node_crypto49.randomUUID)();
77310
+ const id = (0, import_node_crypto50.randomUUID)();
76801
77311
  const inserted = await getAnalyticsPool().query(
76802
77312
  `INSERT INTO analytics_exports(id, site_id, requested_by_user_id, idempotency_key, report, format, filters, content)
76803
77313
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8)
@@ -76830,11 +77340,11 @@ async function createAnalyticsExport(input) {
76830
77340
  }
76831
77341
  return describeArtifact(replay.rows[0]);
76832
77342
  }
76833
- var import_node_crypto49, import_pg, AnalyticsRepositoryError, pool2, MAX_ENGAGED_MS, ENGAGED_SESSION_MS, blockedPropertyName, inferredFamilySql, ANALYTICS_CONTENT_SORTS, clickIdKeys;
77343
+ var import_node_crypto50, import_pg, AnalyticsRepositoryError, pool2, MAX_ENGAGED_MS, ENGAGED_SESSION_MS, blockedPropertyName, inferredFamilySql, ANALYTICS_CONTENT_SORTS, clickIdKeys;
76834
77344
  var init_analytics_repository = __esm({
76835
77345
  "src/api/analytics-repository.ts"() {
76836
77346
  "use strict";
76837
- import_node_crypto49 = require("crypto");
77347
+ import_node_crypto50 = require("crypto");
76838
77348
  import_pg = require("pg");
76839
77349
  init_session();
76840
77350
  init_analytics_attribution();
@@ -77073,8 +77583,8 @@ function dashboardCallbackUrl() {
77073
77583
  }
77074
77584
  async function createThorbitConnectUrl(userId) {
77075
77585
  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");
77586
+ const state = (0, import_node_crypto51.randomBytes)(32).toString("base64url");
77587
+ const stateHash = (0, import_node_crypto51.createHash)("sha256").update(state).digest("hex");
77078
77588
  await getAnalyticsPool().query(
77079
77589
  `INSERT INTO analytics_thorbit_connect_states(state_hash,user_id,expires_at)
77080
77590
  VALUES ($1,$2,now()+interval '10 minutes')
@@ -77089,9 +77599,9 @@ async function createThorbitConnectUrl(userId) {
77089
77599
  function verifyThorbitAssertion(token6) {
77090
77600
  const [encoded, signature] = token6.split(".");
77091
77601
  if (!encoded || !signature || !bridgeConfigured()) throw new Error("Invalid Thorbit assertion");
77092
- const expected = (0, import_node_crypto50.createHmac)("sha256", bridgeSecret()).update(encoded).digest();
77602
+ const expected = (0, import_node_crypto51.createHmac)("sha256", bridgeSecret()).update(encoded).digest();
77093
77603
  const actual = Buffer.from(signature, "base64url");
77094
- if (actual.length !== expected.length || !(0, import_node_crypto50.timingSafeEqual)(actual, expected)) {
77604
+ if (actual.length !== expected.length || !(0, import_node_crypto51.timingSafeEqual)(actual, expected)) {
77095
77605
  throw new Error("Invalid Thorbit assertion signature");
77096
77606
  }
77097
77607
  const parsed = AssertionSchema.safeParse(
@@ -77103,7 +77613,7 @@ function verifyThorbitAssertion(token6) {
77103
77613
  return parsed.data.entitlement;
77104
77614
  }
77105
77615
  async function consumeThorbitConnectCallback(input) {
77106
- const stateHash = (0, import_node_crypto50.createHash)("sha256").update(input.state).digest("hex");
77616
+ const stateHash = (0, import_node_crypto51.createHash)("sha256").update(input.state).digest("hex");
77107
77617
  const client2 = await getAnalyticsPool().connect();
77108
77618
  try {
77109
77619
  await client2.query("BEGIN");
@@ -77143,11 +77653,11 @@ async function disconnectAnalyticsEntitlement(userId) {
77143
77653
  [userId]
77144
77654
  );
77145
77655
  }
77146
- var import_node_crypto50, import_zod55, THORBIT_PRODUCT_URL, REFRESH_INTERVAL_MS, ELIGIBLE_GRACE_MS, TRIAL_LENGTH_MS, ThorbitEntitlementSchema, AssertionSchema;
77656
+ var import_node_crypto51, import_zod55, THORBIT_PRODUCT_URL, REFRESH_INTERVAL_MS, ELIGIBLE_GRACE_MS, TRIAL_LENGTH_MS, ThorbitEntitlementSchema, AssertionSchema;
77147
77657
  var init_analytics_entitlement = __esm({
77148
77658
  "src/api/analytics-entitlement.ts"() {
77149
77659
  "use strict";
77150
- import_node_crypto50 = require("crypto");
77660
+ import_node_crypto51 = require("crypto");
77151
77661
  import_zod55 = require("zod");
77152
77662
  init_analytics_repository();
77153
77663
  THORBIT_PRODUCT_URL = "https://thorbit.ai";
@@ -77287,14 +77797,14 @@ function renderPublicForm(form, placementUrl) {
77287
77797
  const brand = form.brand;
77288
77798
  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
77799
  }
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;
77800
+ 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
77801
  var init_analytics_routes = __esm({
77292
77802
  "src/api/analytics-routes.ts"() {
77293
77803
  "use strict";
77294
77804
  import_hono34 = require("hono");
77295
77805
  import_factory6 = require("hono/factory");
77296
77806
  import_zod56 = require("zod");
77297
- import_node_crypto51 = require("crypto");
77807
+ import_node_crypto52 = require("crypto");
77298
77808
  import_papaparse6 = __toESM(require("papaparse"), 1);
77299
77809
  init_api_auth();
77300
77810
  init_db();
@@ -77625,7 +78135,7 @@ var init_analytics_routes = __esm({
77625
78135
  const lastName = typeof data.last_name === "string" ? data.last_name.trim() : "";
77626
78136
  const fullName = [firstName, lastName].filter(Boolean).join(" ") || (typeof data.name === "string" ? data.name.trim() : "") || email || "Website lead";
77627
78137
  const identitySeed = email || `${fullName}:${Date.now()}`;
77628
- const suffix2 = (0, import_node_crypto51.createHash)("sha256").update(identitySeed).digest("hex").slice(0, 12);
78138
+ const suffix2 = (0, import_node_crypto52.createHash)("sha256").update(identitySeed).digest("hex").slice(0, 12);
77629
78139
  const path6 = `Leads/person-${suffix2}`;
77630
78140
  const { key: memoryKey, error: memoryKeyError } = await getOrCreateUserMemoryKey(user);
77631
78141
  const existing = memoryKey ? await memoryCall("getTool", { vault: "People", path: path6 }, memoryKey) : { ok: false, error: memoryKeyError || "memory_credential_unavailable" };
@@ -77724,9 +78234,9 @@ Submitted the published form.
77724
78234
  occurredAt: (/* @__PURE__ */ new Date()).toISOString()
77725
78235
  });
77726
78236
  const match = {
77727
- ...email ? { emailSha256: (0, import_node_crypto51.createHash)("sha256").update(email).digest("hex") } : {},
78237
+ ...email ? { emailSha256: (0, import_node_crypto52.createHash)("sha256").update(email).digest("hex") } : {},
77728
78238
  ...phone ? {
77729
- phoneSha256: (0, import_node_crypto51.createHash)("sha256").update(phone.replace(/\D/g, "")).digest("hex")
78239
+ phoneSha256: (0, import_node_crypto52.createHash)("sha256").update(phone.replace(/\D/g, "")).digest("hex")
77730
78240
  } : {}
77731
78241
  };
77732
78242
  const activationQueued = await queueAnalyticsActivation({
@@ -78154,7 +78664,7 @@ Submitted the published form.
78154
78664
  rejectedCount += 1;
78155
78665
  continue;
78156
78666
  }
78157
- const digest2 = (0, import_node_crypto51.createHash)("sha256").update(
78667
+ const digest2 = (0, import_node_crypto52.createHash)("sha256").update(
78158
78668
  `${parsed.data.sourceSystem}:${externalId || email || phone || `${fullName}:${index}`}`
78159
78669
  ).digest("hex").slice(0, 16);
78160
78670
  const path6 = `Leads/person-${digest2}`;
@@ -78612,13 +79122,13 @@ var init_analytics_delivery = __esm({
78612
79122
 
78613
79123
  // src/api/scheduled-artifact-owner.ts
78614
79124
  function scheduledArtifactOwnerIdForApiKey(apiKey) {
78615
- return (0, import_node_crypto52.createHash)("sha256").update(apiKey).digest("hex").slice(0, 24);
79125
+ return (0, import_node_crypto53.createHash)("sha256").update(apiKey).digest("hex").slice(0, 24);
78616
79126
  }
78617
- var import_node_crypto52;
79127
+ var import_node_crypto53;
78618
79128
  var init_scheduled_artifact_owner = __esm({
78619
79129
  "src/api/scheduled-artifact-owner.ts"() {
78620
79130
  "use strict";
78621
- import_node_crypto52 = require("crypto");
79131
+ import_node_crypto53 = require("crypto");
78622
79132
  }
78623
79133
  });
78624
79134
 
@@ -78675,7 +79185,7 @@ async function ensureScheduledRunViewLinksSchema() {
78675
79185
  schemaReady2 = true;
78676
79186
  }
78677
79187
  function tokenHash2(token6) {
78678
- return (0, import_node_crypto53.createHash)("sha256").update(token6).digest("hex");
79188
+ return (0, import_node_crypto54.createHash)("sha256").update(token6).digest("hex");
78679
79189
  }
78680
79190
  function mapRow(row) {
78681
79191
  return {
@@ -78695,9 +79205,9 @@ function mapRow(row) {
78695
79205
  async function createScheduledRunViewLink(input) {
78696
79206
  await ensureScheduledRunViewLinksSchema();
78697
79207
  const now = input.now ?? /* @__PURE__ */ new Date();
78698
- const token6 = (0, import_node_crypto53.randomBytes)(32).toString("base64url");
79208
+ const token6 = (0, import_node_crypto54.randomBytes)(32).toString("base64url");
78699
79209
  const record = {
78700
- shareId: (0, import_node_crypto53.randomUUID)(),
79210
+ shareId: (0, import_node_crypto54.randomUUID)(),
78701
79211
  ownerId: input.ownerId,
78702
79212
  runId: input.runId,
78703
79213
  artifactId: input.artifactId,
@@ -78760,11 +79270,11 @@ async function revokeScheduledRunViewLink(ownerId2, runId, shareId, now = /* @__
78760
79270
  });
78761
79271
  return result.rowsAffected > 0;
78762
79272
  }
78763
- var import_node_crypto53, schemaReady2;
79273
+ var import_node_crypto54, schemaReady2;
78764
79274
  var init_scheduled_run_view_links = __esm({
78765
79275
  "src/api/scheduled-run-view-links.ts"() {
78766
79276
  "use strict";
78767
- import_node_crypto53 = require("crypto");
79277
+ import_node_crypto54 = require("crypto");
78768
79278
  init_db();
78769
79279
  schemaReady2 = false;
78770
79280
  }
@@ -78830,15 +79340,15 @@ ${section(flags.finalCta && Boolean(finalCta), `<section class="section cta" id=
78830
79340
  html,
78831
79341
  filename: `${input.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "personal-authority"}.html`,
78832
79342
  bytes,
78833
- sha256: (0, import_node_crypto54.createHash)("sha256").update(html).digest("hex"),
79343
+ sha256: (0, import_node_crypto55.createHash)("sha256").update(html).digest("hex"),
78834
79344
  generatedAt: generatedAtIso
78835
79345
  };
78836
79346
  }
78837
- var import_node_crypto54;
79347
+ var import_node_crypto55;
78838
79348
  var init_render2 = __esm({
78839
79349
  "src/personal-authority/render.ts"() {
78840
79350
  "use strict";
78841
- import_node_crypto54 = require("crypto");
79351
+ import_node_crypto55 = require("crypto");
78842
79352
  }
78843
79353
  });
78844
79354
 
@@ -78941,15 +79451,15 @@ ${section2(flags.finalCta && Boolean(finalCta), `<section class="section cta" id
78941
79451
  html,
78942
79452
  filename: `${input.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "personal-authority"}.html`,
78943
79453
  bytes,
78944
- sha256: (0, import_node_crypto55.createHash)("sha256").update(html).digest("hex"),
79454
+ sha256: (0, import_node_crypto56.createHash)("sha256").update(html).digest("hex"),
78945
79455
  generatedAt: generatedAtIso
78946
79456
  };
78947
79457
  }
78948
- var import_node_crypto55, FONT_STACKS;
79458
+ var import_node_crypto56, FONT_STACKS;
78949
79459
  var init_render_v2 = __esm({
78950
79460
  "src/personal-authority/render-v2.ts"() {
78951
79461
  "use strict";
78952
- import_node_crypto55 = require("crypto");
79462
+ import_node_crypto56 = require("crypto");
78953
79463
  init_contracts();
78954
79464
  FONT_STACKS = Object.freeze({
78955
79465
  "editorial-serif": "Iowan Old Style, Baskerville, Times New Roman, serif",
@@ -79065,15 +79575,15 @@ body{overflow-x:hidden}.lead-story__body,.lead-support__item,.story-card__body{m
79065
79575
  @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
79576
  </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
79577
  const bytes = Buffer.byteLength(html);
79068
- const sha2565 = (0, import_node_crypto56.createHash)("sha256").update(html).digest("hex");
79578
+ const sha2565 = (0, import_node_crypto57.createHash)("sha256").update(html).digest("hex");
79069
79579
  const filename2 = `${brand.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "newsroom"}-news-site.html`;
79070
79580
  return { html, filename: filename2, bytes, sha256: sha2565, generatedAt: generatedAtIso };
79071
79581
  }
79072
- var import_node_crypto56;
79582
+ var import_node_crypto57;
79073
79583
  var init_render3 = __esm({
79074
79584
  "src/newsroom-publisher/render.ts"() {
79075
79585
  "use strict";
79076
- import_node_crypto56 = require("crypto");
79586
+ import_node_crypto57 = require("crypto");
79077
79587
  }
79078
79588
  });
79079
79589
 
@@ -79165,7 +79675,7 @@ function renderBlogArticleV1(input, config, generatedAt) {
79165
79675
  :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
79676
  </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
79677
  const bytes = Buffer.byteLength(html);
79168
- const sha2565 = (0, import_node_crypto57.createHash)("sha256").update(html).digest("hex");
79678
+ const sha2565 = (0, import_node_crypto58.createHash)("sha256").update(html).digest("hex");
79169
79679
  return {
79170
79680
  html,
79171
79681
  filename: "blog-article.html",
@@ -79175,11 +79685,11 @@ function renderBlogArticleV1(input, config, generatedAt) {
79175
79685
  generatedAt: generatedAtIso
79176
79686
  };
79177
79687
  }
79178
- var import_node_crypto57;
79688
+ var import_node_crypto58;
79179
79689
  var init_render4 = __esm({
79180
79690
  "src/blog-article/render.ts"() {
79181
79691
  "use strict";
79182
- import_node_crypto57 = require("crypto");
79692
+ import_node_crypto58 = require("crypto");
79183
79693
  }
79184
79694
  });
79185
79695
 
@@ -79510,7 +80020,7 @@ function policy6() {
79510
80020
  };
79511
80021
  }
79512
80022
  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)}`;
80023
+ 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
80024
  }
79515
80025
  async function createScheduledRunArtifact(args) {
79516
80026
  if (args.rendered.bytes > SCHEDULED_RUN_ARTIFACT_MAX_BYTES) {
@@ -79551,12 +80061,12 @@ async function readScheduledRunArtifact(args) {
79551
80061
  if (!window2 || window2.nextOffset !== null || window2.totalBytes > SCHEDULED_RUN_ARTIFACT_MAX_BYTES) return null;
79552
80062
  return window2.text;
79553
80063
  }
79554
- var import_node_crypto58, SCHEDULED_RUN_ARTIFACT_PREFIX, SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS, SCHEDULED_RUN_ARTIFACT_MAX_BYTES;
80064
+ var import_node_crypto59, SCHEDULED_RUN_ARTIFACT_PREFIX, SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS, SCHEDULED_RUN_ARTIFACT_MAX_BYTES;
79555
80065
  var init_scheduled_run_artifact_store = __esm({
79556
80066
  "src/scheduled-artifacts/scheduled-run-artifact-store.ts"() {
79557
80067
  "use strict";
79558
80068
  init_private_artifacts();
79559
- import_node_crypto58 = require("crypto");
80069
+ import_node_crypto59 = require("crypto");
79560
80070
  SCHEDULED_RUN_ARTIFACT_PREFIX = "scheduled-run-artifacts/";
79561
80071
  SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS = 15 * 60 * 1e3;
79562
80072
  SCHEDULED_RUN_ARTIFACT_MAX_BYTES = 2e6;
@@ -79774,7 +80284,7 @@ async function reconcileDiscoveredNangoConnections(identity, discovered) {
79774
80284
  updated_at = excluded.updated_at
79775
80285
  `,
79776
80286
  args: [
79777
- (0, import_node_crypto59.randomUUID)(),
80287
+ (0, import_node_crypto60.randomUUID)(),
79778
80288
  userId,
79779
80289
  connection.providerConfigKey,
79780
80290
  connection.provider,
@@ -79845,7 +80355,7 @@ async function recordServiceConnectionHealth(args) {
79845
80355
  });
79846
80356
  await getDb().execute({
79847
80357
  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]
80358
+ args: [(0, import_node_crypto60.randomUUID)(), args.connectionId, args.operationalStatus, args.failureCode ?? null, args.retryable == null ? null : args.retryable ? 1 : 0, args.evidenceSource]
79849
80359
  });
79850
80360
  }
79851
80361
  async function setServiceConnectionActions(identity, connectionId, enabled) {
@@ -79885,7 +80395,7 @@ async function claimServiceConnectionAction(args) {
79885
80395
  if (!connection) throw new Error("service_connection_not_found");
79886
80396
  const inserted = await getDb().execute({
79887
80397
  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]
80398
+ args: [(0, import_node_crypto60.randomUUID)(), connection.id, connection.userId, args.tool, args.requestId, args.requestDigest]
79889
80399
  });
79890
80400
  if (Number(inserted.rowsAffected ?? 0) === 1) return { claimed: true };
79891
80401
  const existing = await getDb().execute({
@@ -79910,11 +80420,11 @@ async function claimServiceConnectionAction(args) {
79910
80420
  ...result !== void 0 ? { result } : {}
79911
80421
  };
79912
80422
  }
79913
- var import_node_crypto59, schemaReady3, schemaDb6;
80423
+ var import_node_crypto60, schemaReady3, schemaDb6;
79914
80424
  var init_service_connections = __esm({
79915
80425
  "src/api/service-connections.ts"() {
79916
80426
  "use strict";
79917
- import_node_crypto59 = require("crypto");
80427
+ import_node_crypto60 = require("crypto");
79918
80428
  init_db();
79919
80429
  schemaReady3 = null;
79920
80430
  schemaDb6 = null;
@@ -79928,8 +80438,8 @@ function signingSecret() {
79928
80438
  return secret2;
79929
80439
  }
79930
80440
  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()}
80441
+ const bodyHash = (0, import_node_crypto61.createHash)("sha256").update(args.body).digest("hex");
80442
+ return (0, import_node_crypto61.createHmac)("sha256", args.secret).update(`${args.method.toUpperCase()}
79933
80443
  ${args.path}
79934
80444
  ${args.timestamp}
79935
80445
  ${args.nonce}
@@ -79976,17 +80486,17 @@ async function verifySchedulerIntegrationRequest(request, rawBody) {
79976
80486
  });
79977
80487
  const suppliedBytes = Buffer.from(signature, "hex");
79978
80488
  const expectedBytes = Buffer.from(expected, "hex");
79979
- if (suppliedBytes.length !== expectedBytes.length || !(0, import_node_crypto60.timingSafeEqual)(suppliedBytes, expectedBytes)) {
80489
+ if (suppliedBytes.length !== expectedBytes.length || !(0, import_node_crypto61.timingSafeEqual)(suppliedBytes, expectedBytes)) {
79980
80490
  throw new SchedulerIntegrationAuthError("invalid_signature");
79981
80491
  }
79982
80492
  await claimNonce(nonce, timestampMs);
79983
80493
  return { requestId };
79984
80494
  }
79985
- var import_node_crypto60, MAX_CLOCK_SKEW_MS, SchedulerIntegrationAuthError;
80495
+ var import_node_crypto61, MAX_CLOCK_SKEW_MS, SchedulerIntegrationAuthError;
79986
80496
  var init_scheduler_integration_auth = __esm({
79987
80497
  "src/api/scheduler-integration-auth.ts"() {
79988
80498
  "use strict";
79989
- import_node_crypto60 = require("crypto");
80499
+ import_node_crypto61 = require("crypto");
79990
80500
  init_db();
79991
80501
  init_service_connections();
79992
80502
  MAX_CLOCK_SKEW_MS = 5 * 60 * 1e3;
@@ -80148,7 +80658,7 @@ function requestedSiteMaxPages(maxPages) {
80148
80658
  return Math.min(ABSOLUTE_SITE_MAX_PAGES, Math.max(1, maxPages ?? DEFAULT_SITE_MAX_PAGES));
80149
80659
  }
80150
80660
  function shouldRunSiteExtractInBackground(input) {
80151
- return input.background === true || input.downloadImages === true || input.preserveMedia === true || requestedSiteMaxPages(input.maxPages) > MAX_SYNCHRONOUS_SITE_PAGES;
80661
+ 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
80662
  }
80153
80663
  var MAX_SYNCHRONOUS_SITE_PAGES, DEFAULT_SITE_MAX_PAGES, ABSOLUTE_SITE_MAX_PAGES;
80154
80664
  var init_site_extract_policy = __esm({
@@ -80294,7 +80804,7 @@ async function fetchMedia(rawUrl, expectedType) {
80294
80804
  finalUrl: checked.parsed.href,
80295
80805
  width: dimensions?.width ?? null,
80296
80806
  height: dimensions?.height ?? null,
80297
- sha256: (0, import_node_crypto61.createHash)("sha256").update(bytes).digest("hex")
80807
+ sha256: (0, import_node_crypto62.createHash)("sha256").update(bytes).digest("hex")
80298
80808
  };
80299
80809
  }
80300
80810
  throw new Error("media_redirect_rejected");
@@ -80408,7 +80918,7 @@ async function packagePageMedia(args) {
80408
80918
  files.push({ path: "summary.json", content: Buffer.from(JSON.stringify(summary, null, 2)) });
80409
80919
  files.push({ path: "media.jsonl", content: Buffer.from(cleanAssets.map((asset) => JSON.stringify(asset)).join("\n") + "\n") });
80410
80920
  const archive = await zipBuffer2(files);
80411
- const id = (0, import_node_crypto61.randomBytes)(6).toString("hex");
80921
+ const id = (0, import_node_crypto62.randomBytes)(6).toString("hex");
80412
80922
  const pointer = await createPrivateArtifact({
80413
80923
  policy: policy7(),
80414
80924
  ownerId: args.ownerId,
@@ -80421,11 +80931,11 @@ async function packagePageMedia(args) {
80421
80931
  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
80932
  return { media: args.media, artifact: { ...pointer, localPath } };
80423
80933
  }
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;
80934
+ 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
80935
  var init_page_media_artifacts = __esm({
80426
80936
  "src/api/page-media-artifacts.ts"() {
80427
80937
  "use strict";
80428
- import_node_crypto61 = require("crypto");
80938
+ import_node_crypto62 = require("crypto");
80429
80939
  import_node_os15 = require("os");
80430
80940
  import_node_path22 = require("path");
80431
80941
  import_p_limit7 = __toESM(require("p-limit"), 1);
@@ -80534,7 +81044,7 @@ var init_site_extract_reconciliation = __esm({
80534
81044
 
80535
81045
  // src/api/page-diff.ts
80536
81046
  function sha256Hex(value) {
80537
- return (0, import_node_crypto62.createHash)("sha256").update(value).digest("hex");
81047
+ return (0, import_node_crypto63.createHash)("sha256").update(value).digest("hex");
80538
81048
  }
80539
81049
  function truncateForStorage(value, maxChars = MAX_SNAPSHOT_CONTENT_CHARS) {
80540
81050
  if (value.length <= maxChars) return { value, truncated: false };
@@ -80591,11 +81101,11 @@ function diffPageContent(oldContent, newContent) {
80591
81101
  totalChangedLineCount
80592
81102
  };
80593
81103
  }
80594
- var import_node_crypto62, import_diff, MAX_SNAPSHOT_CONTENT_CHARS, MAX_DIFF_HUNKS, MAX_DIFF_LINES_PER_RESPONSE;
81104
+ var import_node_crypto63, import_diff, MAX_SNAPSHOT_CONTENT_CHARS, MAX_DIFF_HUNKS, MAX_DIFF_LINES_PER_RESPONSE;
80595
81105
  var init_page_diff = __esm({
80596
81106
  "src/api/page-diff.ts"() {
80597
81107
  "use strict";
80598
- import_node_crypto62 = require("crypto");
81108
+ import_node_crypto63 = require("crypto");
80599
81109
  import_diff = require("diff");
80600
81110
  MAX_SNAPSHOT_CONTENT_CHARS = 25e4;
80601
81111
  MAX_DIFF_HUNKS = 200;
@@ -80669,7 +81179,7 @@ var init_scrape_vault_sink = __esm({
80669
81179
 
80670
81180
  // src/api/scrape-image-sink.ts
80671
81181
  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")}`;
81182
+ 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
81183
  }
80674
81184
  async function persistScrapeImagesToMemory(user, inputs, vault) {
80675
81185
  const selected = inputs.slice(0, MAX_IMAGES_PER_SCRAPE);
@@ -80711,11 +81221,11 @@ async function persistScrapeImagesToMemory(user, inputs, vault) {
80711
81221
  assets
80712
81222
  };
80713
81223
  }
80714
- var import_node_crypto63, MAX_IMAGES_PER_SCRAPE;
81224
+ var import_node_crypto64, MAX_IMAGES_PER_SCRAPE;
80715
81225
  var init_scrape_image_sink = __esm({
80716
81226
  "src/api/scrape-image-sink.ts"() {
80717
81227
  "use strict";
80718
- import_node_crypto63 = require("crypto");
81228
+ import_node_crypto64 = require("crypto");
80719
81229
  init_memory();
80720
81230
  MAX_IMAGES_PER_SCRAPE = 25;
80721
81231
  }
@@ -81168,7 +81678,7 @@ function canonicalJson(value) {
81168
81678
  return JSON.stringify(value);
81169
81679
  }
81170
81680
  function sha2563(value) {
81171
- return (0, import_node_crypto64.createHash)("sha256").update(value).digest("hex");
81681
+ return (0, import_node_crypto65.createHash)("sha256").update(value).digest("hex");
81172
81682
  }
81173
81683
  function sensitiveKey(key) {
81174
81684
  const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
@@ -81383,11 +81893,11 @@ async function importServiceConnectionToMemory(identity, input, dependencies) {
81383
81893
  ...!searchReady ? { warning: "The snapshot was stored, but no search chunks were indexed yet." } : {}
81384
81894
  };
81385
81895
  }
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;
81896
+ 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
81897
  var init_connection_memory_import = __esm({
81388
81898
  "src/api/connection-memory-import.ts"() {
81389
81899
  "use strict";
81390
- import_node_crypto64 = require("crypto");
81900
+ import_node_crypto65 = require("crypto");
81391
81901
  init_slugify();
81392
81902
  CONNECTION_MEMORY_IMPORT_MAX_ARGS_BYTES = 64 * 1024;
81393
81903
  CONNECTION_MEMORY_IMPORT_MAX_RESULT_BYTES = 1e6;
@@ -81471,7 +81981,7 @@ var init_scrape_blob_cleanup = __esm({
81471
81981
 
81472
81982
  // src/api/site-export-reader.ts
81473
81983
  function sha2564(value) {
81474
- return (0, import_node_crypto65.createHash)("sha256").update(value).digest("hex");
81984
+ return (0, import_node_crypto66.createHash)("sha256").update(value).digest("hex");
81475
81985
  }
81476
81986
  function publicPageRecord(page) {
81477
81987
  const { bodyMarkdown: _body, contentRef: _contentRef, discoveryLinks: _discovery, ...metadata } = page;
@@ -81579,11 +82089,11 @@ async function readOwnedSiteExportImage(input) {
81579
82089
  if (!bytes || artifact.sha256 && sha2564(bytes) !== artifact.sha256) return null;
81580
82090
  return { bytes, artifact };
81581
82091
  }
81582
- var import_node_crypto65, SiteExportFormatUnavailableError;
82092
+ var import_node_crypto66, SiteExportFormatUnavailableError;
81583
82093
  var init_site_export_reader = __esm({
81584
82094
  "src/api/site-export-reader.ts"() {
81585
82095
  "use strict";
81586
- import_node_crypto65 = require("crypto");
82096
+ import_node_crypto66 = require("crypto");
81587
82097
  init_site_extract_repository();
81588
82098
  init_site_extract_content_store();
81589
82099
  init_site_extract_artifacts();
@@ -81734,7 +82244,7 @@ function finitePositive(value, fallback) {
81734
82244
  return Number.isFinite(value) && value > 0 ? value : fallback;
81735
82245
  }
81736
82246
  async function collectConnectedDataExport(args) {
81737
- const exportId = (0, import_node_crypto66.randomUUID)();
82247
+ const exportId = (0, import_node_crypto67.randomUUID)();
81738
82248
  const now = args.now ?? Date.now;
81739
82249
  const startedAt = now();
81740
82250
  const budgetMs = finitePositive(CONNECTED_DATA_EXPORT_BUDGET_MS, 24e4);
@@ -81848,11 +82358,11 @@ ${lines.length ? `${lines.join("\n")}
81848
82358
  untrustedContent: true
81849
82359
  };
81850
82360
  }
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;
82361
+ 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
82362
  var init_connected_data_export = __esm({
81853
82363
  "src/api/connected-data-export.ts"() {
81854
82364
  "use strict";
81855
- import_node_crypto66 = require("crypto");
82365
+ import_node_crypto67 = require("crypto");
81856
82366
  CONNECTED_DATA_INLINE_BUDGET_BYTES = Number(
81857
82367
  process.env.MCP_SCRAPER_CONNECTED_DATA_INLINE_BUDGET_BYTES ?? 5e4
81858
82368
  );
@@ -81966,7 +82476,7 @@ async function exportSearchConsoleTableData(args) {
81966
82476
  offset += rows.length;
81967
82477
  if (stoppedForBytes || rows.length < limit || offset >= matchedRows) break;
81968
82478
  }
81969
- const exportId = (0, import_node_crypto67.randomUUID)();
82479
+ const exportId = (0, import_node_crypto68.randomUUID)();
81970
82480
  const artifact = await args.writeArtifact({
81971
82481
  ownerId: args.ownerId,
81972
82482
  exportId,
@@ -81988,11 +82498,11 @@ async function exportSearchConsoleTableData(args) {
81988
82498
  warnings
81989
82499
  };
81990
82500
  }
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;
82501
+ 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
82502
  var init_search_console_table_export = __esm({
81993
82503
  "src/api/search-console-table-export.ts"() {
81994
82504
  "use strict";
81995
- import_node_crypto67 = require("crypto");
82505
+ import_node_crypto68 = require("crypto");
81996
82506
  SEARCH_CONSOLE_TABLE_EXPORT_MAX_ROWS = 5e4;
81997
82507
  SEARCH_CONSOLE_TABLE_EXPORT_PAGE_SIZE = 2e3;
81998
82508
  SEARCH_CONSOLE_TABLE_EXPORT_MAX_BYTES = 50 * 1024 * 1024;
@@ -83171,7 +83681,7 @@ async function listNangoToolsDirect(identity, connectionId) {
83171
83681
  });
83172
83682
  const readTools = [...policies.values()].filter((policy8) => policy8.classification === "read").map((policy8) => policy8.name);
83173
83683
  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");
83684
+ const revision = (0, import_node_crypto69.createHash)("sha256").update(JSON.stringify(tools.map((tool) => ({ name: tool.name, inputSchema: tool.inputSchema })))).digest("hex");
83175
83685
  await updateServiceConnectionTools(connection.id, readTools, actionTools, revision);
83176
83686
  const refreshed = await getOwnedServiceConnection(identity, connection.id);
83177
83687
  return { connection: refreshed ?? { ...connection, readTools, actionTools, toolRevision: revision }, tools };
@@ -83198,8 +83708,8 @@ async function callNangoToolDirect(args) {
83198
83708
  identity: args.identity,
83199
83709
  ratePolicyVersion: CONNECTED_USAGE_RATE_POLICY_VERSION
83200
83710
  });
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")}`;
83711
+ const requestId = args.requestId?.trim() || (0, import_node_crypto69.randomUUID)();
83712
+ 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
83713
  const startedAt = /* @__PURE__ */ new Date();
83204
83714
  const started = performance.now();
83205
83715
  let result;
@@ -83224,7 +83734,7 @@ async function callNangoToolDirect(args) {
83224
83734
  toolName: args.tool,
83225
83735
  operationKind: args.operationKind ?? args.classification,
83226
83736
  outcome: providerError ? "error" : "partial",
83227
- requestId: requestId.length <= 200 ? requestId : (0, import_node_crypto68.createHash)("sha256").update(requestId).digest("hex"),
83737
+ requestId: requestId.length <= 200 ? requestId : (0, import_node_crypto69.createHash)("sha256").update(requestId).digest("hex"),
83228
83738
  startedAt: startedAt.toISOString(),
83229
83739
  completedAt: completedAt.toISOString()
83230
83740
  }
@@ -83266,15 +83776,15 @@ async function describeNangoToolDirect(identity, connectionId, toolName) {
83266
83776
  providerContractHash: MAIN_INTEGRATION_CONTRACT_HASH,
83267
83777
  protocolVersion: null,
83268
83778
  schemaSource: "live_tools_list",
83269
- schemaHash: (0, import_node_crypto68.createHash)("sha256").update(JSON.stringify(projected)).digest("hex"),
83779
+ schemaHash: (0, import_node_crypto69.createHash)("sha256").update(JSON.stringify(projected)).digest("hex"),
83270
83780
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
83271
83781
  };
83272
83782
  }
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;
83783
+ 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
83784
  var init_main_nango_transport = __esm({
83275
83785
  "src/api/main-nango-transport.ts"() {
83276
83786
  "use strict";
83277
- import_node_crypto68 = require("crypto");
83787
+ import_node_crypto69 = require("crypto");
83278
83788
  import_client15 = require("@modelcontextprotocol/client");
83279
83789
  init_service_connections();
83280
83790
  init_connected_usage_billing();
@@ -83898,8 +84408,8 @@ async function setScheduleConnectionActionsEnabled(identity, connectionId, enabl
83898
84408
  return data.connection.actionsEnabled === true;
83899
84409
  }
83900
84410
  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");
84411
+ 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")}`;
84412
+ const requestDigest = (0, import_node_crypto70.createHash)("sha256").update(connectionId).update("\0").update(tool?.trim() ?? "").update("\0").update(canonicalJson2(input)).digest("hex");
83903
84413
  if (mainOwnsIntegrations()) {
83904
84414
  const selectedTool = tool?.trim();
83905
84415
  if (!selectedTool) throw new NangoControlError("An action tool is required.", 400, "invalid_request", false);
@@ -84050,7 +84560,7 @@ function canonicalJson2(value) {
84050
84560
  return JSON.stringify(value);
84051
84561
  }
84052
84562
  function projectedToolSchemaHash(tool) {
84053
- return (0, import_node_crypto69.createHash)("sha256").update(canonicalJson2(tool)).digest("hex");
84563
+ return (0, import_node_crypto70.createHash)("sha256").update(canonicalJson2(tool)).digest("hex");
84054
84564
  }
84055
84565
  async function describeNangoTool(identity, connectionId, tool, fresh) {
84056
84566
  if (mainOwnsIntegrations()) {
@@ -84327,11 +84837,11 @@ async function callMainOwnedExportPage(identity, input) {
84327
84837
  untrustedContent: true
84328
84838
  };
84329
84839
  }
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;
84840
+ 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
84841
  var init_nango_control = __esm({
84332
84842
  "src/api/nango-control.ts"() {
84333
84843
  "use strict";
84334
- import_node_crypto69 = require("crypto");
84844
+ import_node_crypto70 = require("crypto");
84335
84845
  init_connected_data_export();
84336
84846
  init_slack_connected_data_export();
84337
84847
  init_main_nango_transport();
@@ -84687,7 +85197,7 @@ async function callResendRead(identity, connectionId, tool, args) {
84687
85197
  return isRecord5(data) ? data.result ?? data : data;
84688
85198
  }
84689
85199
  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")}`;
85200
+ const requestId = `main-resend-action:${(0, import_node_crypto71.createHash)("sha256").update(identity).update("\0").update(idempotencyKey4.trim()).digest("hex")}`;
84691
85201
  const body = await controlRequest2("/api/internal/resend/actions/call", {
84692
85202
  method: "POST",
84693
85203
  headers: { "x-request-id": requestId },
@@ -84752,12 +85262,12 @@ async function callResendExportPage(identity, input) {
84752
85262
  untrustedContent: true
84753
85263
  };
84754
85264
  }
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;
85265
+ 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
85266
  var init_resend_control = __esm({
84757
85267
  "src/api/resend-control.ts"() {
84758
85268
  "use strict";
84759
85269
  init_connected_data_export();
84760
- import_node_crypto70 = require("crypto");
85270
+ import_node_crypto71 = require("crypto");
84761
85271
  DEFAULT_CONNECTION_CONTROL_URL = "https://mcp-scraper-scheduler.vercel.app";
84762
85272
  RESEND_PROVIDER_CONFIG_KEY = "resend";
84763
85273
  RESEND_LOGO_URL = "https://cdn.resend.com/brand/resend-icon-black.svg";
@@ -85364,7 +85874,7 @@ function settleWithinTickBudget(label, unfinished, work, onDeadlineOrError) {
85364
85874
  );
85365
85875
  });
85366
85876
  }
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;
85877
+ 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
85878
  var init_server = __esm({
85369
85879
  "src/api/server.ts"() {
85370
85880
  "use strict";
@@ -85377,7 +85887,7 @@ var init_server = __esm({
85377
85887
  init_og();
85378
85888
  import_resend3 = require("resend");
85379
85889
  init_url_utils();
85380
- import_node_crypto71 = require("crypto");
85890
+ import_node_crypto72 = require("crypto");
85381
85891
  init_kpo_extractor();
85382
85892
  init_screenshot();
85383
85893
  init_media_extractor();
@@ -86938,7 +87448,7 @@ var init_server = __esm({
86938
87448
  if (!harvestOk) return c.json(insufficientBalanceResponse(harvestBal, harvestCost), 402);
86939
87449
  jobId2 = await createJob(user.id, options.query, { ...options, billingHoldMc: harvestCost }, body.callback_url);
86940
87450
  } else {
86941
- jobId2 = (0, import_node_crypto71.randomUUID)();
87451
+ jobId2 = (0, import_node_crypto72.randomUUID)();
86942
87452
  const billingDebitKey = `paa-harvest:${jobId2}:hold`;
86943
87453
  const description = `PAA harvest: ${options.query}`.slice(0, 500);
86944
87454
  const hold = await debitMcIdempotent(
@@ -87773,6 +88283,13 @@ var init_server = __esm({
87773
88283
  const bodyResult = ExtractSiteBodySchema.safeParse(raw);
87774
88284
  if (!bodyResult.success) return c.json({ error: bodyResult.error.issues[0]?.message ?? "Invalid request" }, 400);
87775
88285
  const body = bodyResult.data;
88286
+ if (body.semanticSimilarity && !process.env.JINA_API_KEY?.trim()) {
88287
+ return c.json({
88288
+ error: "Semantic site similarity is not configured on this deployment.",
88289
+ errorCode: "semantic_similarity_unconfigured",
88290
+ retryable: false
88291
+ }, 503);
88292
+ }
87776
88293
  if (body.preserveMedia !== void 0 && body.downloadImages !== void 0 && body.preserveMedia !== body.downloadImages) {
87777
88294
  return c.json({ error: "preserveMedia conflicts with deprecated downloadImages." }, 400);
87778
88295
  }
@@ -87826,7 +88343,12 @@ var init_server = __esm({
87826
88343
  maxPages: requestedMaxPages,
87827
88344
  rotateProxyEvery: body.rotateProxyEvery ?? 10,
87828
88345
  formats: [...body.formats ?? []].sort(),
87829
- downloadImages
88346
+ downloadImages,
88347
+ renderJavaScript: body.renderJavaScript === true,
88348
+ captureRenderedDom: body.captureRenderedDom === true,
88349
+ semanticSimilarity: body.semanticSimilarity === true,
88350
+ similarityThreshold: body.similarityThreshold ?? null,
88351
+ similarityMaxPairs: body.similarityMaxPairs ?? null
87830
88352
  }));
87831
88353
  const prepared = await prepareSiteExtractStart({
87832
88354
  jobId: jobId2,
@@ -87840,6 +88362,11 @@ var init_server = __esm({
87840
88362
  urlsPerBrowser: body.rotateProxyEvery ?? 10,
87841
88363
  formats: body.formats,
87842
88364
  downloadImages,
88365
+ renderJavaScript: body.renderJavaScript === true,
88366
+ captureRenderedDom: body.captureRenderedDom === true,
88367
+ semanticSimilarity: body.semanticSimilarity === true,
88368
+ similarityThreshold: body.similarityThreshold,
88369
+ similarityMaxPairs: body.similarityMaxPairs,
87843
88370
  debitKey: `site-extract:${user.id}:${jobId2}:hold`,
87844
88371
  waybackReplay: waybackReplay && !body.wayback ? {
87845
88372
  timestamp: waybackReplay.timestamp,
@@ -87933,8 +88460,10 @@ var init_server = __esm({
87933
88460
  startUrl: crawlStartUrl,
87934
88461
  maxPages: siteMaxPages,
87935
88462
  seedUrls: waybackCaptures?.map((capture) => capture.rawReplayUrl),
87936
- kernelApiKey: rotateProxies || wantsBranding || body.browserFallback || body.kernelFallback ? browserServiceApiKey() : void 0,
88463
+ kernelApiKey: rotateProxies || wantsBranding || body.browserFallback || body.kernelFallback || body.renderJavaScript || body.captureRenderedDom || body.semanticSimilarity ? browserServiceApiKey() : void 0,
87937
88464
  formats: body.formats,
88465
+ forceBrowserRender: body.renderJavaScript || body.captureRenderedDom || body.semanticSimilarity,
88466
+ captureRenderedDom: body.captureRenderedDom,
87938
88467
  ...rotateProxies ? {
87939
88468
  rotateProxyEvery: body.rotateProxyEvery ?? 30,
87940
88469
  parallelism: concurrencyLimitForUser(user)