mcp-scraper 0.64.0 → 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 +16 -0
  2. package/README.md +2 -2
  3. package/dist/bin/api-server.cjs +1028 -497
  4. package/dist/bin/api-server.cjs.map +1 -1
  5. package/dist/bin/api-server.js +2 -2
  6. package/dist/bin/mcp-scraper-cli.cjs +1 -1
  7. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  8. package/dist/bin/mcp-scraper-cli.js +1 -1
  9. package/dist/bin/mcp-scraper-install.cjs +2 -2
  10. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  11. package/dist/bin/mcp-scraper-install.js +2 -2
  12. package/dist/bin/mcp-stdio-server.cjs +49 -9
  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-EEW3DMI5.js → chunk-PSKRQDGN.js} +50 -10
  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-TMAWQYZE.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-5QYSY5KI.js.map +0 -1
  34. package/dist/chunk-DYSXI6QU.js +0 -7
  35. package/dist/chunk-DYSXI6QU.js.map +0 -1
  36. package/dist/chunk-EEW3DMI5.js.map +0 -1
  37. package/dist/extract-bundle-FW23CEMG.js.map +0 -1
  38. package/dist/server-TMAWQYZE.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",
@@ -20769,7 +21351,8 @@ function formatLeadListImport(raw) {
20769
21351
  const map = Object.keys(detailedMap).length > 0 ? detailedMap : Object.fromEntries(Object.entries(simpleMap).map(([field, header]) => [field, {
20770
21352
  header: header == null ? null : String(header),
20771
21353
  confidence: header == null ? 0 : 1,
20772
- reason: "server_suggestion"
21354
+ reason: "server_suggestion",
21355
+ accepted: header != null
20773
21356
  }]));
20774
21357
  const mappingLines = Object.entries(map).slice(0, 12).map(([field, suggestion]) => {
20775
21358
  const record = structuredRecord(suggestion);
@@ -22883,7 +23466,7 @@ function backupProxyAvailable() {
22883
23466
  }
22884
23467
  function randomStickySessionId() {
22885
23468
  let digits = "";
22886
- 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));
22887
23470
  return digits;
22888
23471
  }
22889
23472
  function buildBackupUsername(opts = {}) {
@@ -22941,11 +23524,11 @@ async function cleanupBackupProxyId(kernelApiKey, proxyId) {
22941
23524
  } catch {
22942
23525
  }
22943
23526
  }
22944
- 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;
22945
23528
  var init_backup_proxy = __esm({
22946
23529
  "src/backup-proxy.ts"() {
22947
23530
  "use strict";
22948
- import_node_crypto9 = require("crypto");
23531
+ import_node_crypto10 = require("crypto");
22949
23532
  import_sdk7 = __toESM(require("@onkernel/sdk"), 1);
22950
23533
  BACKUP_PROXY_HOST = "pr.oxylabs.io";
22951
23534
  BACKUP_PROXY_PORT = 7777;
@@ -24350,7 +24933,7 @@ function csvRecords(text2) {
24350
24933
  return record;
24351
24934
  });
24352
24935
  }
24353
- function csvCell(value) {
24936
+ function csvCell2(value) {
24354
24937
  if (value === null || value === void 0) return "";
24355
24938
  const text2 = String(value);
24356
24939
  return /[",\n\r]/.test(text2) ? `"${text2.replace(/"/g, '""')}"` : text2;
@@ -24358,7 +24941,7 @@ function csvCell(value) {
24358
24941
  function rowsToCsv(headers, rows) {
24359
24942
  return [
24360
24943
  headers.join(","),
24361
- ...rows.map((row) => headers.map((header) => csvCell(row[header])).join(","))
24944
+ ...rows.map((row) => headers.map((header) => csvCell2(row[header])).join(","))
24362
24945
  ].join("\n") + "\n";
24363
24946
  }
24364
24947
  var init_csv = __esm({
@@ -24754,12 +25337,12 @@ async function getHostedZipGroups(stateInput) {
24754
25337
  async function importHostedZipGroupsCsv(input) {
24755
25338
  await ensureHostedLocationDataSchema();
24756
25339
  const sourceUrl = normalizedSourceUrl(input.sourceUrl);
24757
- 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");
24758
25341
  const active = await getActiveHostedLocationDataset();
24759
25342
  if (active?.sha256 === sha2565) return { dataset: active, duplicate: true };
24760
25343
  const parsed = parseHostedZipGroupsCsv(input.csv);
24761
25344
  assertNationwideZipCoverage(parsed);
24762
- const id = `loc_${(0, import_node_crypto10.randomUUID)().replaceAll("-", "")}`;
25345
+ const id = `loc_${(0, import_node_crypto11.randomUUID)().replaceAll("-", "")}`;
24763
25346
  const db = getDb();
24764
25347
  await db.execute({
24765
25348
  sql: `
@@ -24827,12 +25410,12 @@ async function importHostedCensusPlacesCsv(input) {
24827
25410
  if (!state) throw new Error("state must be a two-letter US state abbreviation");
24828
25411
  const kind = censusDatasetKind(state);
24829
25412
  const sourceUrl = normalizedSourceUrl(input.sourceUrl);
24830
- 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");
24831
25414
  const active = await getActiveHostedDataset(kind);
24832
25415
  if (active?.sha256 === sha2565) return { dataset: active, duplicate: true };
24833
25416
  const stateFips = STATE_FIPS_BY_ABBR[state];
24834
25417
  const parsed = parseHostedCensusPlacesCsv(input.csv, stateFips);
24835
- const id = `loc_${(0, import_node_crypto10.randomUUID)().replaceAll("-", "")}`;
25418
+ const id = `loc_${(0, import_node_crypto11.randomUUID)().replaceAll("-", "")}`;
24836
25419
  const db = getDb();
24837
25420
  await db.execute({
24838
25421
  sql: `
@@ -24955,11 +25538,11 @@ async function queryHostedLocationMarkets(input) {
24955
25538
  warnings
24956
25539
  };
24957
25540
  }
24958
- 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;
24959
25542
  var init_location_data_repository = __esm({
24960
25543
  "src/api/location-data-repository.ts"() {
24961
25544
  "use strict";
24962
- import_node_crypto10 = require("crypto");
25545
+ import_node_crypto11 = require("crypto");
24963
25546
  init_db();
24964
25547
  init_csv();
24965
25548
  HOSTED_ZIP_DATASET_KIND = "us_zip_groups";
@@ -26273,7 +26856,7 @@ async function claimDirectoryWorkflowOutbox(input) {
26273
26856
  const workerId = requiredBoundedString(input.workerId, "outbox worker id", 160);
26274
26857
  const limit = Math.max(1, Math.min(100, Math.trunc(input.limit ?? 25)));
26275
26858
  const leaseSeconds = Math.max(30, Math.min(3600, Math.trunc(input.leaseSeconds ?? 120)));
26276
- const claimToken = `${workerId}:${(0, import_node_crypto11.randomUUID)()}`;
26859
+ const claimToken = `${workerId}:${(0, import_node_crypto12.randomUUID)()}`;
26277
26860
  const leaseModifier = `+${leaseSeconds} seconds`;
26278
26861
  const results = await getDb().batch([
26279
26862
  {
@@ -26333,11 +26916,11 @@ async function markDirectoryWorkflowOutboxFailed(input) {
26333
26916
  });
26334
26917
  return Number(result.rowsAffected ?? 0) > 0;
26335
26918
  }
26336
- 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;
26337
26920
  var init_directory_workflow_repository = __esm({
26338
26921
  "src/api/directory-workflow-repository.ts"() {
26339
26922
  "use strict";
26340
- import_node_crypto11 = require("crypto");
26923
+ import_node_crypto12 = require("crypto");
26341
26924
  init_db();
26342
26925
  DIRECTORY_EVENT_NAME = "mcp-scraper/directory.requested";
26343
26926
  TERMINAL_JOB_STATUSES = /* @__PURE__ */ new Set(["succeeded", "partial", "failed"]);
@@ -26451,7 +27034,7 @@ async function acquireConcurrencyGate(user, operation, options = {}) {
26451
27034
  return { ok: true, lockId: null, active: await countActiveUsageForUser(user.id, true), limit, operation, reused: true };
26452
27035
  }
26453
27036
  await expireConcurrencyLocksForUser(user.id);
26454
- 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)}`;
26455
27038
  const res = await getDb().execute({
26456
27039
  sql: `INSERT INTO concurrency_locks (id, user_id, operation, status, expires_at, metadata)
26457
27040
  SELECT ?, ?, ?, 'active', datetime('now', ?), ?
@@ -26501,11 +27084,11 @@ async function extendConcurrencyGate(lockId, ttlSeconds = DEFAULT_LOCK_TTL_SECON
26501
27084
  args: [lockTtlModifier(ttlSeconds), lockId]
26502
27085
  });
26503
27086
  }
26504
- 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;
26505
27088
  var init_concurrency_gates = __esm({
26506
27089
  "src/api/concurrency-gates.ts"() {
26507
27090
  "use strict";
26508
- import_node_crypto12 = require("crypto");
27091
+ import_node_crypto13 = require("crypto");
26509
27092
  init_db();
26510
27093
  init_rates();
26511
27094
  DEFAULT_LOCK_TTL_SECONDS = 15 * 60;
@@ -27572,7 +28155,7 @@ var init_PAAExtractor = __esm({
27572
28155
  if (remainingHumanClickDelayMs > 0) await page.waitForTimeout(remainingHumanClickDelayMs);
27573
28156
  return "ok";
27574
28157
  };
27575
- let round = 0;
28158
+ let round2 = 0;
27576
28159
  let growthWaits = 0;
27577
28160
  while (true) {
27578
28161
  if (options.softDeadlineMs && Date.now() >= options.softDeadlineMs) break;
@@ -27600,7 +28183,7 @@ var init_PAAExtractor = __esm({
27600
28183
  continue;
27601
28184
  }
27602
28185
  growthWaits = 0;
27603
- this.reporter.onDepth(++round);
28186
+ this.reporter.onDepth(++round2);
27604
28187
  await this.throwIfCaptcha(page, "Google PAA expansion");
27605
28188
  clickedOnceEver.add(target.q);
27606
28189
  const expansionStatus = await expandOneItemSerially(target.q);
@@ -30137,10 +30720,10 @@ function stableCanonicalJson(value) {
30137
30720
  return JSON.stringify(normalize4(value));
30138
30721
  }
30139
30722
  function leadListEnrichmentFingerprint(input) {
30140
- 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");
30141
30724
  }
30142
30725
  function leadListRowInputDigest(row) {
30143
- 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");
30144
30727
  }
30145
30728
  function emptyUsage() {
30146
30729
  return { mapsAttempts: 0, pageAttempts: 0, pageSuccesses: 0, serpSearches: 0 };
@@ -30613,7 +31196,7 @@ async function claimLeadListEnrichmentOutbox(input) {
30613
31196
  const workerId = requiredBoundedString2(input.workerId, "outbox worker id", 160);
30614
31197
  const limit = Math.max(1, Math.min(100, Math.trunc(input.limit ?? 25)));
30615
31198
  const leaseSeconds = Math.max(30, Math.min(3600, Math.trunc(input.leaseSeconds ?? 120)));
30616
- const claimToken = `${workerId}:${(0, import_node_crypto13.randomUUID)()}`;
31199
+ const claimToken = `${workerId}:${(0, import_node_crypto14.randomUUID)()}`;
30617
31200
  const results = await getDb().batch([{
30618
31201
  sql: `UPDATE lead_list_enrichment_outbox SET status='dispatching',attempts=attempts+1,locked_by=?,
30619
31202
  locked_until=datetime('now',?),updated_at=datetime('now') WHERE id IN (SELECT id FROM lead_list_enrichment_outbox
@@ -30639,11 +31222,11 @@ async function markLeadListEnrichmentOutboxFailed(input) {
30639
31222
  WHERE id=? AND status='dispatching' AND locked_by=?`, args: [`+${retryAfterSeconds} seconds`, String(input.error).slice(0, 2e3), input.id, input.claimToken] });
30640
31223
  return Number(result.rowsAffected ?? 0) > 0;
30641
31224
  }
30642
- 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;
30643
31226
  var init_lead_list_enrichment_repository = __esm({
30644
31227
  "src/api/lead-list-enrichment-repository.ts"() {
30645
31228
  "use strict";
30646
- import_node_crypto13 = require("crypto");
31229
+ import_node_crypto14 = require("crypto");
30647
31230
  init_db();
30648
31231
  EVENT_NAME = "mcp-scraper/lead-list-enrichment.requested";
30649
31232
  TERMINAL_JOB_STATUSES2 = /* @__PURE__ */ new Set(["complete", "partial", "empty", "failed", "cancelled"]);
@@ -31221,7 +31804,7 @@ var init_paa_harvest_settlement = __esm({
31221
31804
 
31222
31805
  // src/api/serp-identity-db.ts
31223
31806
  async function createSerpIdentityRow(input) {
31224
- 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)}`;
31225
31808
  await getDb().execute({
31226
31809
  sql: `INSERT INTO serp_identities
31227
31810
  (id, user_id, name, kernel_profile_name, kernel_proxy_id, proxy_type, country, status)
@@ -31266,11 +31849,11 @@ async function deleteSerpIdentityRow(userId, name) {
31266
31849
  args: [userId, name]
31267
31850
  });
31268
31851
  }
31269
- var import_node_crypto14;
31852
+ var import_node_crypto15;
31270
31853
  var init_serp_identity_db = __esm({
31271
31854
  "src/api/serp-identity-db.ts"() {
31272
31855
  "use strict";
31273
- import_node_crypto14 = require("crypto");
31856
+ import_node_crypto15 = require("crypto");
31274
31857
  init_db();
31275
31858
  }
31276
31859
  });
@@ -32017,7 +32600,7 @@ async function runDurablePaaCapture(input) {
32017
32600
  const { gzipSync: gzipSync2 } = await import("zlib");
32018
32601
  rawDomGzip = gzipSync2(transported);
32019
32602
  }
32020
- rawDomSha256 = (0, import_node_crypto15.createHash)("sha256").update(rawDomGzip).digest("hex");
32603
+ rawDomSha256 = (0, import_node_crypto16.createHash)("sha256").update(rawDomGzip).digest("hex");
32021
32604
  }
32022
32605
  }
32023
32606
  const selected = Array.from(records.values()).slice(0, input.maxQuestions);
@@ -32101,12 +32684,12 @@ async function runDurablePaaCapture(input) {
32101
32684
  }
32102
32685
  throw new Error("paa_work_deadline_exhausted_before_capture");
32103
32686
  }
32104
- 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;
32105
32688
  var init_durable_capture = __esm({
32106
32689
  "src/paa/durable-capture.ts"() {
32107
32690
  "use strict";
32108
32691
  import_sdk9 = __toESM(require("@onkernel/sdk"), 1);
32109
- import_node_crypto15 = require("crypto");
32692
+ import_node_crypto16 = require("crypto");
32110
32693
  init_selectors();
32111
32694
  init_uule();
32112
32695
  PAA_INVOCATION_BUDGET_MS = 28e4;
@@ -32534,7 +33117,7 @@ function mapSubmission(row) {
32534
33117
  async function event(submissionId, eventType, actorKind, actorId, metadata = {}) {
32535
33118
  await getDb().execute({
32536
33119
  sql: `INSERT INTO local_sourcebook_events (id, submission_id, event_type, actor_kind, actor_id, metadata_json) VALUES (?, ?, ?, ?, ?, ?)`,
32537
- 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)]
32538
33121
  });
32539
33122
  }
32540
33123
  async function createLocalSourcebookSubmission(input) {
@@ -32548,7 +33131,7 @@ async function createLocalSourcebookSubmission(input) {
32548
33131
  return submission;
32549
33132
  }
32550
33133
  }
32551
- const id = `lsb_${(0, import_node_crypto16.randomUUID)().replace(/-/g, "")}`;
33134
+ const id = `lsb_${(0, import_node_crypto17.randomUUID)().replace(/-/g, "")}`;
32552
33135
  const coverage = {
32553
33136
  requested: ["website_crawl", "structured_data", "services_products", "service_areas", "genuine_images", "staff_team", "review_sources"],
32554
33137
  crawl: { state: "queued", pagesDiscovered: 0, pagesCaptured: 0 },
@@ -32689,11 +33272,11 @@ async function getPublicLocalSourcebook(category, state, slug4) {
32689
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] });
32690
33273
  return result.rows[0] ? parseJson3(result.rows[0].payload_json) : null;
32691
33274
  }
32692
- var import_node_crypto16, LOCAL_SOURCEBOOK_CATEGORIES, schemaPromise4, schemaDb4;
33275
+ var import_node_crypto17, LOCAL_SOURCEBOOK_CATEGORIES, schemaPromise4, schemaDb4;
32693
33276
  var init_local_sourcebook_repository = __esm({
32694
33277
  "src/api/local-sourcebook-repository.ts"() {
32695
33278
  "use strict";
32696
- import_node_crypto16 = require("crypto");
33279
+ import_node_crypto17 = require("crypto");
32697
33280
  init_db();
32698
33281
  LOCAL_SOURCEBOOK_CATEGORIES = ["home", "professional", "restaurants", "financial", "realestate", "auto", "wellness"];
32699
33282
  schemaPromise4 = null;
@@ -33845,7 +34428,7 @@ var init_local_sourcebook_schema = __esm({
33845
34428
 
33846
34429
  // src/api/local-sourcebook-compiler.ts
33847
34430
  function digest(value) {
33848
- 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);
33849
34432
  }
33850
34433
  function unique(values, limit = 100) {
33851
34434
  const seen = /* @__PURE__ */ new Set();
@@ -34282,11 +34865,11 @@ function compileLocalSourcebookListing(input) {
34282
34865
  }
34283
34866
  };
34284
34867
  }
34285
- 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;
34286
34869
  var init_local_sourcebook_compiler = __esm({
34287
34870
  "src/api/local-sourcebook-compiler.ts"() {
34288
34871
  "use strict";
34289
- import_node_crypto17 = require("crypto");
34872
+ import_node_crypto18 = require("crypto");
34290
34873
  init_local_sourcebook_public_urls();
34291
34874
  init_local_sourcebook_schema();
34292
34875
  CATEGORY_LABELS = {
@@ -34643,6 +35226,11 @@ async function prepareSiteExtractStart(input) {
34643
35226
  urlsPerBrowser: input.urlsPerBrowser,
34644
35227
  formats: input.formats,
34645
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,
34646
35234
  ...input.waybackReplay ? {
34647
35235
  waybackReplay: input.waybackReplay,
34648
35236
  disableLinkDiscovery: true
@@ -35387,15 +35975,15 @@ function normalizeIdempotencyKey(value) {
35387
35975
  return key;
35388
35976
  }
35389
35977
  function tokenFor(id, idempotencyKey4) {
35390
- 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");
35391
35979
  }
35392
35980
  function tokenHash(token6) {
35393
- return (0, import_node_crypto18.createHash)("sha256").update(token6).digest("hex");
35981
+ return (0, import_node_crypto19.createHash)("sha256").update(token6).digest("hex");
35394
35982
  }
35395
35983
  function verifyToken(row, token6) {
35396
35984
  const expected = Buffer.from(row.token_hash, "hex");
35397
35985
  const actual = Buffer.from(tokenHash(token6), "hex");
35398
- 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)) {
35399
35987
  throw new UnifiedBillingError("unauthorized", "invalid billing authorization token", 401);
35400
35988
  }
35401
35989
  }
@@ -35510,7 +36098,7 @@ async function authorizeScheduledRun(args) {
35510
36098
  { balanceMc, requiredMc: SCHEDULED_RUN_BASE_MC }
35511
36099
  );
35512
36100
  }
35513
- const id = (0, import_node_crypto18.randomUUID)();
36101
+ const id = (0, import_node_crypto19.randomUUID)();
35514
36102
  const token6 = tokenFor(id, idempotencyKey4);
35515
36103
  const expiresAt = new Date(Date.now() + AUTHORIZATION_TTL_MS).toISOString();
35516
36104
  const inserted = await getDb().execute({
@@ -35578,7 +36166,7 @@ async function startScheduledRun(args) {
35578
36166
  if (!authorization || !["starting", "started"].includes(authorization.status)) {
35579
36167
  throw new UnifiedBillingError("authorization_closed", "billing authorization is no longer startable", 409);
35580
36168
  }
35581
- const eventId = existing?.id ?? (0, import_node_crypto18.randomUUID)();
36169
+ const eventId = existing?.id ?? (0, import_node_crypto19.randomUUID)();
35582
36170
  if (!existing) {
35583
36171
  await getDb().execute({
35584
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', ?, ?, ?)",
@@ -35717,7 +36305,7 @@ async function settleScheduledRun(args) {
35717
36305
  ...modelCostUnreported ? { modelCostUnreported: true } : {},
35718
36306
  ...pendingReason ? { reason: pendingReason } : {}
35719
36307
  });
35720
- const eventId = existing?.id ?? (0, import_node_crypto18.randomUUID)();
36308
+ const eventId = existing?.id ?? (0, import_node_crypto19.randomUUID)();
35721
36309
  const status = pendingReason ? "cost_pending" : "settling";
35722
36310
  const eventValues = [
35723
36311
  modelMc,
@@ -35842,11 +36430,11 @@ async function voidScheduledRunAuthorization(args) {
35842
36430
  }
35843
36431
  return { ok: true, status: authorization.status };
35844
36432
  }
35845
- 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;
35846
36434
  var init_unified_billing = __esm({
35847
36435
  "src/api/unified-billing.ts"() {
35848
36436
  "use strict";
35849
- import_node_crypto18 = require("crypto");
36437
+ import_node_crypto19 = require("crypto");
35850
36438
  init_db();
35851
36439
  init_rates();
35852
36440
  init_scheduling_access();
@@ -35878,14 +36466,14 @@ function getSessionSecret() {
35878
36466
  function safeEqualHex(a, b) {
35879
36467
  if (a.length !== b.length) return false;
35880
36468
  try {
35881
- 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"));
35882
36470
  } catch {
35883
36471
  return false;
35884
36472
  }
35885
36473
  }
35886
36474
  function signSession(userId) {
35887
36475
  const payload = String(userId);
35888
- 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");
35889
36477
  return `${payload}.${sig}`;
35890
36478
  }
35891
36479
  function verifySession(token6) {
@@ -35893,16 +36481,16 @@ function verifySession(token6) {
35893
36481
  if (dot === -1) return null;
35894
36482
  const payload = token6.slice(0, dot);
35895
36483
  const sig = token6.slice(dot + 1);
35896
- 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");
35897
36485
  if (!safeEqualHex(sig, expected)) return null;
35898
36486
  const id = parseInt(payload);
35899
36487
  return isNaN(id) ? null : id;
35900
36488
  }
35901
- var import_node_crypto19, isProduction, secret;
36489
+ var import_node_crypto20, isProduction, secret;
35902
36490
  var init_session = __esm({
35903
36491
  "src/api/session.ts"() {
35904
36492
  "use strict";
35905
- import_node_crypto19 = require("crypto");
36493
+ import_node_crypto20 = require("crypto");
35906
36494
  isProduction = () => process.env.NODE_ENV === "production" || process.env.VERCEL === "1";
35907
36495
  secret = () => getSessionSecret();
35908
36496
  }
@@ -35920,11 +36508,11 @@ function isMemoryOperator(email) {
35920
36508
  return ops.includes(email.trim().toLowerCase());
35921
36509
  }
35922
36510
  function encKey() {
35923
- 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);
35924
36512
  }
35925
36513
  function encryptMemoryKey(secret2) {
35926
- const iv = (0, import_node_crypto20.randomBytes)(12);
35927
- 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);
35928
36516
  const enc = Buffer.concat([cipher.update(secret2, "utf8"), cipher.final()]);
35929
36517
  const tag = cipher.getAuthTag();
35930
36518
  return `${iv.toString("base64")}:${tag.toString("base64")}:${enc.toString("base64")}`;
@@ -35932,7 +36520,7 @@ function encryptMemoryKey(secret2) {
35932
36520
  function decryptMemoryKey(stored) {
35933
36521
  try {
35934
36522
  const [ivB, tagB, dataB] = stored.split(":");
35935
- 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"));
35936
36524
  decipher.setAuthTag(Buffer.from(tagB, "base64"));
35937
36525
  return Buffer.concat([decipher.update(Buffer.from(dataB, "base64")), decipher.final()]).toString("utf8");
35938
36526
  } catch {
@@ -36046,11 +36634,11 @@ async function syncScheduledActionCredentials(user) {
36046
36634
  }, {});
36047
36635
  return { ok: res.ok, error: res.error };
36048
36636
  }
36049
- 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;
36050
36638
  var init_memory = __esm({
36051
36639
  "src/api/memory.ts"() {
36052
36640
  "use strict";
36053
- import_node_crypto20 = require("crypto");
36641
+ import_node_crypto21 = require("crypto");
36054
36642
  init_session();
36055
36643
  init_db();
36056
36644
  init_rates();
@@ -36100,7 +36688,7 @@ var init_connected_cost_telemetry = __esm({
36100
36688
 
36101
36689
  // src/api/connected-usage-billing.ts
36102
36690
  function hash(value) {
36103
- return (0, import_node_crypto21.createHash)("sha256").update(value).digest("hex");
36691
+ return (0, import_node_crypto22.createHash)("sha256").update(value).digest("hex");
36104
36692
  }
36105
36693
  function eventKey(idempotencyKey4) {
36106
36694
  return `connected-usage:${hash(idempotencyKey4)}`;
@@ -36372,7 +36960,7 @@ async function settleConnectedUsage(rawInput) {
36372
36960
  sql: `INSERT OR IGNORE INTO billing_events
36373
36961
  (id, user_id, idempotency_key, billing_class, source_surface, status, amount_mc, metadata)
36374
36962
  VALUES (?, ?, ?, ?, ?, 'settling', ?, ?)`,
36375
- 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)]
36376
36964
  });
36377
36965
  let event2 = await readEvent(key);
36378
36966
  if (!event2) throw new Error("connected usage receipt insert completed without a readable event");
@@ -36461,11 +37049,11 @@ async function listConnectedUsageHistory(userId, limit = 100) {
36461
37049
  }
36462
37050
  return history;
36463
37051
  }
36464
- 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;
36465
37053
  var init_connected_usage_billing = __esm({
36466
37054
  "src/api/connected-usage-billing.ts"() {
36467
37055
  "use strict";
36468
- import_node_crypto21 = require("crypto");
37056
+ import_node_crypto22 = require("crypto");
36469
37057
  import_zod17 = require("zod");
36470
37058
  init_connected_cost_telemetry();
36471
37059
  init_db();
@@ -38114,7 +38702,27 @@ var init_server_schemas = __esm({
38114
38702
  downloadImages: import_zod22.z.boolean().optional(),
38115
38703
  preserveMedia: import_zod22.z.boolean().optional(),
38116
38704
  delivery: import_zod22.z.enum(["auto", "artifact"]).optional(),
38117
- 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
+ }
38118
38726
  });
38119
38727
  SiteExportReadBodySchema = import_zod22.z.object({
38120
38728
  jobId: import_zod22.z.string().trim().min(1),
@@ -40414,7 +41022,7 @@ async function applyAdjustment(input) {
40414
41022
  const credits = validateCredits(input.credits, input.confirmLarge === true);
40415
41023
  const reason = validateReason(input.reason);
40416
41024
  const actor = validateActor(input.actor);
40417
- const reference = input.reference?.trim() || `adj_${(0, import_node_crypto22.randomUUID)()}`;
41025
+ const reference = input.reference?.trim() || `adj_${(0, import_node_crypto23.randomUUID)()}`;
40418
41026
  if (reference.length > 120) throw new AdminCreditError(400, "reference must be at most 120 characters");
40419
41027
  const db = getDb();
40420
41028
  const existing = await db.execute({
@@ -40559,11 +41167,11 @@ async function lookupAccount(target, limit = 20) {
40559
41167
  }))
40560
41168
  };
40561
41169
  }
40562
- 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;
40563
41171
  var init_admin_credits = __esm({
40564
41172
  "src/api/admin-credits.ts"() {
40565
41173
  "use strict";
40566
- import_node_crypto22 = require("crypto");
41174
+ import_node_crypto23 = require("crypto");
40567
41175
  init_db();
40568
41176
  init_rates();
40569
41177
  MIN_ADJUSTMENT_CREDITS = 1;
@@ -42528,7 +43136,7 @@ async function packageMapsMedia(args) {
42528
43136
  files.push({ path: "summary.json", content: Buffer.from(JSON.stringify(summary, null, 2)) });
42529
43137
  files.push({ path: "images.jsonl", content: Buffer.from(cleanImages.map((image) => JSON.stringify(image)).join("\n") + "\n") });
42530
43138
  const archive = await zipBuffer(files);
42531
- const id = (0, import_node_crypto23.randomBytes)(6).toString("hex");
43139
+ const id = (0, import_node_crypto24.randomBytes)(6).toString("hex");
42532
43140
  const pointer = await createPrivateArtifact({
42533
43141
  policy: policy3(),
42534
43142
  ownerId: args.ownerId,
@@ -42541,13 +43149,13 @@ async function packageMapsMedia(args) {
42541
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);
42542
43150
  return { media: args.media, artifact: { ...pointer, localPath } };
42543
43151
  }
42544
- 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;
42545
43153
  var init_maps_media_artifacts = __esm({
42546
43154
  "src/api/maps-media-artifacts.ts"() {
42547
43155
  "use strict";
42548
43156
  import_node_os9 = require("os");
42549
43157
  import_node_path13 = require("path");
42550
- import_node_crypto23 = require("crypto");
43158
+ import_node_crypto24 = require("crypto");
42551
43159
  import_yazl2 = require("yazl");
42552
43160
  import_p_limit5 = __toESM(require("p-limit"), 1);
42553
43161
  init_private_artifacts();
@@ -43171,7 +43779,7 @@ function retryDelaySeconds(attempts) {
43171
43779
  }
43172
43780
  async function dispatchPendingDirectoryWorkflows(limit = 25) {
43173
43781
  const rows = await claimDirectoryWorkflowOutbox({
43174
- 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)}`,
43175
43783
  limit
43176
43784
  });
43177
43785
  const result = { claimed: rows.length, dispatched: 0, failed: 0 };
@@ -43193,11 +43801,11 @@ async function dispatchPendingDirectoryWorkflows(limit = 25) {
43193
43801
  }
43194
43802
  return result;
43195
43803
  }
43196
- var import_node_crypto24;
43804
+ var import_node_crypto25;
43197
43805
  var init_directory_workflow_dispatch = __esm({
43198
43806
  "src/api/directory-workflow-dispatch.ts"() {
43199
43807
  "use strict";
43200
- import_node_crypto24 = require("crypto");
43808
+ import_node_crypto25 = require("crypto");
43201
43809
  init_client();
43202
43810
  init_directory_workflow_repository();
43203
43811
  }
@@ -43209,7 +43817,7 @@ function safeOptions(options) {
43209
43817
  return safe2;
43210
43818
  }
43211
43819
  function requestFingerprint(options) {
43212
- 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");
43213
43821
  }
43214
43822
  function idempotencyKey(raw) {
43215
43823
  if (raw !== void 0) {
@@ -43218,14 +43826,14 @@ function idempotencyKey(raw) {
43218
43826
  if (trimmed.length > 500) return { ok: false, message: "Idempotency-Key must be 500 characters or fewer." };
43219
43827
  return { ok: true, key: trimmed };
43220
43828
  }
43221
- return { ok: true, key: `directory-${(0, import_node_crypto25.randomUUID)()}` };
43829
+ return { ok: true, key: `directory-${(0, import_node_crypto26.randomUUID)()}` };
43222
43830
  }
43223
43831
  function debitKeyFor(userId, responseKey) {
43224
- 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");
43225
43833
  return `directory-workflow:${userId}:${digest2}`;
43226
43834
  }
43227
43835
  function jobId() {
43228
- return `dir_${(0, import_node_crypto25.randomUUID)().replace(/-/g, "")}`;
43836
+ return `dir_${(0, import_node_crypto26.randomUUID)().replace(/-/g, "")}`;
43229
43837
  }
43230
43838
  function publicStatus(job, result) {
43231
43839
  if (job.status === "failed") return "failed";
@@ -43334,7 +43942,7 @@ async function runSynchronously(c, user, options, plan, responseKey) {
43334
43942
  const csv = renderDirectoryWorkflowCsv(result);
43335
43943
  const artifact = await createDirectoryCsvArtifact({
43336
43944
  ownerId: String(user.id),
43337
- 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),
43338
43946
  createdAt: result.extractedAt,
43339
43947
  filename: `${options.state}-${options.query}-directory.csv`,
43340
43948
  csv,
@@ -43390,11 +43998,11 @@ async function runSynchronously(c, user, options, plan, responseKey) {
43390
43998
  await releaseConcurrencyGate(gate.lockId);
43391
43999
  }
43392
44000
  }
43393
- var import_node_crypto25, import_hono16, directoryApp;
44001
+ var import_node_crypto26, import_hono16, directoryApp;
43394
44002
  var init_directory_routes = __esm({
43395
44003
  "src/api/directory-routes.ts"() {
43396
44004
  "use strict";
43397
- import_node_crypto25 = require("crypto");
44005
+ import_node_crypto26 = require("crypto");
43398
44006
  import_hono16 = require("hono");
43399
44007
  init_api_auth();
43400
44008
  init_db();
@@ -43612,10 +44220,10 @@ function bounded(value, field, min, max) {
43612
44220
  return normalized;
43613
44221
  }
43614
44222
  function newLeadListUploadId() {
43615
- return `upl_${(0, import_node_crypto26.randomUUID)().replace(/-/g, "")}`;
44223
+ return `upl_${(0, import_node_crypto27.randomUUID)().replace(/-/g, "")}`;
43616
44224
  }
43617
44225
  function newImportedLeadListId() {
43618
- return `lst_${(0, import_node_crypto26.randomUUID)().replace(/-/g, "")}`;
44226
+ return `lst_${(0, import_node_crypto27.randomUUID)().replace(/-/g, "")}`;
43619
44227
  }
43620
44228
  async function ensureLeadListImportRepositorySchema() {
43621
44229
  const db = getDb();
@@ -43815,11 +44423,11 @@ async function deleteExpiredLeadListInputRecords(now = /* @__PURE__ */ new Date(
43815
44423
  ], "write");
43816
44424
  return { uploads: Number(uploads.rowsAffected ?? 0), lists: Number(lists.rowsAffected ?? 0) };
43817
44425
  }
43818
- var import_node_crypto26, schemaDb5, schemaPromise5;
44426
+ var import_node_crypto27, schemaDb5, schemaPromise5;
43819
44427
  var init_lead_list_import_repository = __esm({
43820
44428
  "src/api/lead-list-import-repository.ts"() {
43821
44429
  "use strict";
43822
- import_node_crypto26 = require("crypto");
44430
+ import_node_crypto27 = require("crypto");
43823
44431
  init_db();
43824
44432
  schemaDb5 = null;
43825
44433
  schemaPromise5 = null;
@@ -43861,7 +44469,7 @@ function safeFilenameHint(value) {
43861
44469
  return cleaned || null;
43862
44470
  }
43863
44471
  function requestFingerprint2(filenameHint) {
43864
- 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");
43865
44473
  }
43866
44474
  function publicBaseUrl() {
43867
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";
@@ -44017,7 +44625,7 @@ async function inspectLeadListUpload(uploadId, ownerId2) {
44017
44625
  maxBytes: LEAD_LIST_UPLOAD_MAX_BYTES
44018
44626
  });
44019
44627
  if (!buffer) return null;
44020
- 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");
44021
44629
  const completed = await completeLeadListUpload({
44022
44630
  id: record.id,
44023
44631
  ownerId: record.ownerId,
@@ -44141,11 +44749,11 @@ async function cleanupExpiredLeadListInputs(args = {}) {
44141
44749
  const records = await deleteExpiredLeadListInputRecords(now);
44142
44750
  return { deletedBlobs, deletedUploadRecords: records.uploads, deletedListRecords: records.lists };
44143
44751
  }
44144
- 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;
44145
44753
  var init_lead_list_input_artifacts = __esm({
44146
44754
  "src/api/lead-list-input-artifacts.ts"() {
44147
44755
  "use strict";
44148
- import_node_crypto27 = require("crypto");
44756
+ import_node_crypto28 = require("crypto");
44149
44757
  import_promises11 = require("fs/promises");
44150
44758
  import_node_os10 = require("os");
44151
44759
  import_node_path14 = require("path");
@@ -44528,7 +45136,7 @@ function canonicalize(value) {
44528
45136
  return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${canonicalize(object[key])}`).join(",")}}`;
44529
45137
  }
44530
45138
  function fingerprint(value) {
44531
- 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");
44532
45140
  }
44533
45141
  async function safeJson(c) {
44534
45142
  try {
@@ -44605,11 +45213,11 @@ function textDelimiter(raw, text2, observedMime) {
44605
45213
  if (observedMime === "text/tab-separated-values") return " ";
44606
45214
  return detectLeadListTextDelimiter(text2);
44607
45215
  }
44608
- var import_node_crypto28, import_hono17, leadListInputApp;
45216
+ var import_node_crypto29, import_hono17, leadListInputApp;
44609
45217
  var init_lead_list_input_routes = __esm({
44610
45218
  "src/api/lead-list-input-routes.ts"() {
44611
45219
  "use strict";
44612
- import_node_crypto28 = require("crypto");
45220
+ import_node_crypto29 = require("crypto");
44613
45221
  import_hono17 = require("hono");
44614
45222
  init_api_auth();
44615
45223
  init_lead_list_input_artifacts();
@@ -44801,7 +45409,7 @@ function retryDelaySeconds2(attempts) {
44801
45409
  return Math.min(3600, Math.max(15, 15 * 2 ** Math.min(8, Math.max(0, attempts - 1))));
44802
45410
  }
44803
45411
  async function dispatchPendingLeadListEnrichments(limit = 25) {
44804
- 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 });
44805
45413
  const result = { claimed: rows.length, dispatched: 0, failed: 0 };
44806
45414
  for (const row of rows) {
44807
45415
  if (!row.claimToken) continue;
@@ -44820,11 +45428,11 @@ async function dispatchPendingLeadListEnrichments(limit = 25) {
44820
45428
  }
44821
45429
  return result;
44822
45430
  }
44823
- var import_node_crypto29;
45431
+ var import_node_crypto30;
44824
45432
  var init_lead_list_enrichment_dispatch = __esm({
44825
45433
  "src/api/lead-list-enrichment-dispatch.ts"() {
44826
45434
  "use strict";
44827
- import_node_crypto29 = require("crypto");
45435
+ import_node_crypto30 = require("crypto");
44828
45436
  init_client();
44829
45437
  init_lead_list_enrichment_repository();
44830
45438
  }
@@ -44840,15 +45448,15 @@ function idempotencyKey2(raw) {
44840
45448
  return { ok: true, value };
44841
45449
  }
44842
45450
  function newJobId() {
44843
- return `lle_${(0, import_node_crypto30.randomUUID)().replace(/-/g, "")}`;
45451
+ return `lle_${(0, import_node_crypto31.randomUUID)().replace(/-/g, "")}`;
44844
45452
  }
44845
45453
  function debitKeyFor2(userId, key) {
44846
- 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");
44847
45455
  return `lead-list-enrichment:${userId}:${digest2}`;
44848
45456
  }
44849
45457
  function inlineSourceDigest(headers, rows) {
44850
45458
  const values = rows.map((row) => headers.map((header) => row[header] ?? null));
44851
- 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");
44852
45460
  }
44853
45461
  function issueMessage(error) {
44854
45462
  const issue = error.issues[0];
@@ -45046,11 +45654,11 @@ async function resolveInput(ownerId2, parsed) {
45046
45654
  sourceDigest: sourceDigest ?? inlineSourceDigest(normalized.headers, rows)
45047
45655
  };
45048
45656
  }
45049
- 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;
45050
45658
  var init_lead_list_enrichment_routes = __esm({
45051
45659
  "src/api/lead-list-enrichment-routes.ts"() {
45052
45660
  "use strict";
45053
- import_node_crypto30 = require("crypto");
45661
+ import_node_crypto31 = require("crypto");
45054
45662
  import_hono18 = require("hono");
45055
45663
  import_zod31 = require("zod");
45056
45664
  init_api_auth();
@@ -48243,7 +48851,7 @@ async function readManifestFromSummary(summary) {
48243
48851
  function webhookSignature(body, timestamp2) {
48244
48852
  const secret2 = process.env.MCP_SCRAPER_WEBHOOK_SECRET?.trim();
48245
48853
  if (!secret2) return null;
48246
- 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");
48247
48855
  }
48248
48856
  async function deliverWorkflowWebhook(input) {
48249
48857
  if (!input.webhookUrl) return;
@@ -48450,11 +49058,11 @@ async function dispatchDueWorkflowSchedules(apiUrl, limit = 3) {
48450
49058
  }
48451
49059
  return { dispatched: results.length, results };
48452
49060
  }
48453
- 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;
48454
49062
  var init_workflow_routes = __esm({
48455
49063
  "src/api/workflow-routes.ts"() {
48456
49064
  "use strict";
48457
- import_node_crypto31 = require("crypto");
49065
+ import_node_crypto32 = require("crypto");
48458
49066
  import_promises14 = require("fs/promises");
48459
49067
  import_hono21 = require("hono");
48460
49068
  import_zod41 = require("zod");
@@ -48735,7 +49343,7 @@ var init_workflow_routes = __esm({
48735
49343
  // src/serp-intelligence/page-snapshot-extractor.ts
48736
49344
  function sha2562(value) {
48737
49345
  if (!value) return null;
48738
- return (0, import_node_crypto32.createHash)("sha256").update(value).digest("hex");
49346
+ return (0, import_node_crypto33.createHash)("sha256").update(value).digest("hex");
48739
49347
  }
48740
49348
  function countWords(markdown) {
48741
49349
  const matches = markdown.trim().match(/\b[\p{L}\p{N}][\p{L}\p{N}'-]*\b/gu);
@@ -49035,11 +49643,11 @@ async function capturePageSnapshots(targets, options = {}) {
49035
49643
  }
49036
49644
  };
49037
49645
  }
49038
- 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;
49039
49647
  var init_page_snapshot_extractor = __esm({
49040
49648
  "src/serp-intelligence/page-snapshot-extractor.ts"() {
49041
49649
  "use strict";
49042
- import_node_crypto32 = require("crypto");
49650
+ import_node_crypto33 = require("crypto");
49043
49651
  import_p_limit6 = __toESM(require("p-limit"), 1);
49044
49652
  init_kpo_extractor();
49045
49653
  init_url_utils();
@@ -49535,21 +50143,21 @@ async function logRequestEventBestEffort(input) {
49535
50143
  }
49536
50144
  }
49537
50145
  function captureBillingKeys(userId, suppliedKey, body) {
49538
- const responseKey = suppliedKey?.trim() || (0, import_node_crypto33.randomUUID)();
49539
- const requestFingerprint3 = (0, import_node_crypto33.createHash)("sha256").update(JSON.stringify(body)).digest("hex");
49540
- 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");
49541
50149
  return {
49542
50150
  responseKey,
49543
50151
  debitKey: `serp-capture:${userId}:${keyDigest}`,
49544
50152
  debitDescription: `${body.query} [request:${requestFingerprint3}]`
49545
50153
  };
49546
50154
  }
49547
- 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;
49548
50156
  var init_serp_intelligence_routes = __esm({
49549
50157
  "src/api/serp-intelligence-routes.ts"() {
49550
50158
  "use strict";
49551
50159
  import_hono22 = require("hono");
49552
- import_node_crypto33 = require("crypto");
50160
+ import_node_crypto34 = require("crypto");
49553
50161
  init_browser_service_env();
49554
50162
  init_page_snapshot_extractor();
49555
50163
  init_serp_capture_service();
@@ -49786,7 +50394,7 @@ var PACKAGE_VERSION;
49786
50394
  var init_version = __esm({
49787
50395
  "src/version.ts"() {
49788
50396
  "use strict";
49789
- PACKAGE_VERSION = "0.64.0";
50397
+ PACKAGE_VERSION = "0.65.0";
49790
50398
  }
49791
50399
  });
49792
50400
 
@@ -49817,6 +50425,8 @@ seam is noted so you can chain them.
49817
50425
  - Whole site -> **extract_site** (takes a url). It durably retains complete per-page JSON, acquired HTML,
49818
50426
  and Markdown. Poll **check_site_export**, then call **site_export_read** for the manifest or a page view;
49819
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.
49820
50430
  - Wayback replay URLs work with the same tools: \`extract_url\` removes playback chrome and can return
49821
50431
  a featured image; \`extract_site\` batches nearby archived HTML captures for the replayed site.
49822
50432
  - For multiple archive months, pass \`extract_site.wayback\` with explicit \`months\` or a \`from\`/\`to\`
@@ -50230,6 +50840,7 @@ var init_output_schema_registry = __esm({
50230
50840
  ESSENTIAL_OUTPUT_SCHEMA_TOOLS = /* @__PURE__ */ new Set([
50231
50841
  "extract_url",
50232
50842
  "extract_site",
50843
+ "analyze_site_similarity",
50233
50844
  "audit_site",
50234
50845
  "check_site_export",
50235
50846
  "site_export_read",
@@ -51627,7 +52238,7 @@ var init_contracts = __esm({
51627
52238
  });
51628
52239
 
51629
52240
  // src/mcp/mcp-tool-schemas.ts
51630
- 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;
51631
52242
  var init_mcp_tool_schemas = __esm({
51632
52243
  "src/mcp/mcp-tool-schemas.ts"() {
51633
52244
  "use strict";
@@ -51737,6 +52348,13 @@ var init_mcp_tool_schemas = __esm({
51737
52348
  preserveMedia: import_zod45.z.boolean().default(false).describe("Include supported images in the export bundle. This is the preferred replacement for downloadImages."),
51738
52349
  downloadImages: import_zod45.z.boolean().optional().describe("Deprecated alias for preserveMedia. Omit when using preserveMedia; when omitted, image preservation defaults to false.")
51739
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
+ };
51740
52358
  AuditSiteInputSchema = {
51741
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."),
51742
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."),
@@ -51749,17 +52367,17 @@ var init_mcp_tool_schemas = __esm({
51749
52367
  downloadImages: import_zod45.z.boolean().optional().describe("Deprecated alias for preserveMedia. Omit when using preserveMedia; when omitted, image preservation defaults to false.")
51750
52368
  };
51751
52369
  CheckSiteExportInputSchema = {
51752
- 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.")
51753
52371
  };
51754
52372
  SiteExportReadInputSchema = {
51755
- 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."),
51756
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."),
51757
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."),
51758
52376
  offset: import_zod45.z.number().int().min(0).default(0).describe("UTF-8 byte offset. Continue from nextOffset until it is null."),
51759
52377
  maxBytes: import_zod45.z.number().int().min(1).max(1e6).default(64e3).describe("Maximum UTF-8 bytes returned in this window.")
51760
52378
  };
51761
52379
  SiteExportImageInputSchema = {
51762
- 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."),
51763
52381
  imageId: import_zod45.z.string().regex(/^[a-f0-9]{64}$/).describe("Downloaded image ID returned by a site_export_read manifest.")
51764
52382
  };
51765
52383
  ArchiveReadInputSchema = {
@@ -52587,7 +53205,8 @@ var init_mcp_tool_schemas = __esm({
52587
53205
  LeadSuggestedColumnOutputSchema = import_zod45.z.object({
52588
53206
  header: NullableString,
52589
53207
  confidence: import_zod45.z.number().min(0).max(1),
52590
- reason: import_zod45.z.string()
53208
+ reason: import_zod45.z.string(),
53209
+ accepted: import_zod45.z.boolean()
52591
53210
  }).strict();
52592
53211
  LeadColumnMapSuggestionOutputSchema = import_zod45.z.object({
52593
53212
  name: LeadSuggestedColumnOutputSchema,
@@ -54926,11 +55545,11 @@ function requireTasksCapability(capabilityValue) {
54926
55545
  );
54927
55546
  }
54928
55547
  function taskKey(secret2) {
54929
- 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();
54930
55549
  }
54931
55550
  function encodeTaskHandle(payload, secret2) {
54932
- const nonce = (0, import_node_crypto34.randomBytes)(12);
54933
- 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);
54934
55553
  const ciphertext = Buffer.concat([
54935
55554
  cipher.update(JSON.stringify(payload), "utf8"),
54936
55555
  cipher.final()
@@ -54947,7 +55566,7 @@ function decodeTaskHandle(taskId, secret2, ownerId2) {
54947
55566
  const nonce = bytes.subarray(0, 12);
54948
55567
  const tag = bytes.subarray(bytes.length - 16);
54949
55568
  const ciphertext = bytes.subarray(12, bytes.length - 16);
54950
- 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);
54951
55570
  decipher.setAuthTag(tag);
54952
55571
  const parsed = JSON.parse(Buffer.concat([
54953
55572
  decipher.update(ciphertext),
@@ -55175,12 +55794,12 @@ async function handleMcpTasksHttpRequest(request, executor, options) {
55175
55794
  return taskHttpError(id, error instanceof import_server.ProtocolError ? error : new import_server.ProtocolError(-32603, error instanceof Error ? error.message : "Internal error"));
55176
55795
  }
55177
55796
  }
55178
- 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;
55179
55798
  var init_mcp_tasks_extension = __esm({
55180
55799
  "src/mcp/mcp-tasks-extension.ts"() {
55181
55800
  "use strict";
55182
55801
  import_server = require("@modelcontextprotocol/server");
55183
- import_node_crypto34 = require("crypto");
55802
+ import_node_crypto35 = require("crypto");
55184
55803
  import_zod46 = require("zod");
55185
55804
  MCP_TASKS_EXTENSION_ID = "io.modelcontextprotocol/tasks";
55186
55805
  TASK_HANDLE_VERSION = "mt1";
@@ -55478,7 +56097,7 @@ var init_analytics_mcp_tools = __esm({
55478
56097
 
55479
56098
  // src/mcp/paa-mcp-server.ts
55480
56099
  function hashOwnerId(callerKey) {
55481
- 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);
55482
56101
  }
55483
56102
  function liveWebToolAnnotations(title) {
55484
56103
  return {
@@ -55691,6 +56310,21 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
55691
56310
  await formatExtractSite(await executor.extractSite(input), input, ctx),
55692
56311
  requestContext
55693
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
+ ));
55694
56328
  server.registerTool("audit_site", {
55695
56329
  title: "Technical SEO Audit",
55696
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.`,
@@ -55704,7 +56338,7 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
55704
56338
  ));
55705
56339
  server.registerTool("check_site_export", {
55706
56340
  title: "Check Site Export",
55707
- 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.",
55708
56342
  inputSchema: CheckSiteExportInputSchema,
55709
56343
  outputSchema: recordOutputSchema("check_site_export", CheckSiteExportOutputSchema),
55710
56344
  annotations: { ...liveWebToolAnnotations("Check Site Export"), readOnlyHint: false }
@@ -56443,7 +57077,7 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
56443
57077
  annotations: { title: "Set Scheduled Action Connections", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
56444
57078
  }, async (input) => executor.setScheduledActionConnections(input));
56445
57079
  }
56446
- 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;
56447
57081
  var init_paa_mcp_server = __esm({
56448
57082
  "src/mcp/paa-mcp-server.ts"() {
56449
57083
  "use strict";
@@ -56451,7 +57085,7 @@ var init_paa_mcp_server = __esm({
56451
57085
  import_zod48 = require("zod");
56452
57086
  import_node_fs11 = require("fs");
56453
57087
  import_node_path17 = require("path");
56454
- import_node_crypto35 = require("crypto");
57088
+ import_node_crypto36 = require("crypto");
56455
57089
  init_version();
56456
57090
  init_rates();
56457
57091
  init_mcp_response_formatter();
@@ -56572,11 +57206,11 @@ function analyticsReportPath(input, report) {
56572
57206
  const suffix2 = query.size ? `?${query.toString()}` : "";
56573
57207
  return `/analytics/sites/${encodeURIComponent(input.siteId)}/${report}${suffix2}`;
56574
57208
  }
56575
- var import_node_crypto36, HttpMcpToolExecutor;
57209
+ var import_node_crypto37, HttpMcpToolExecutor;
56576
57210
  var init_http_mcp_tool_executor = __esm({
56577
57211
  "src/mcp/http-mcp-tool-executor.ts"() {
56578
57212
  "use strict";
56579
- import_node_crypto36 = require("crypto");
57213
+ import_node_crypto37 = require("crypto");
56580
57214
  init_harvest_timeout();
56581
57215
  init_browser_service_env();
56582
57216
  init_errors();
@@ -56645,13 +57279,13 @@ var init_http_mcp_tool_executor = __esm({
56645
57279
  });
56646
57280
  }
56647
57281
  async callDirectoryWorkflowStart(body, explicitIdempotencyKey) {
56648
- 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")}`;
56649
57283
  return this.call("/directory/run", body, this.timeoutMs, "POST", {
56650
57284
  "Idempotency-Key": idempotencyKey4
56651
57285
  });
56652
57286
  }
56653
57287
  async callSiteExtractStart(toolName, body, explicitIdempotencyKey) {
56654
- 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")}`;
56655
57289
  return this.call("/extract-site", body, this.timeoutMs, "POST", {
56656
57290
  "Idempotency-Key": idempotencyKey4
56657
57291
  });
@@ -56739,6 +57373,19 @@ var init_http_mcp_tool_executor = __esm({
56739
57373
  const { idempotencyKey: idempotencyKey4, ...body } = input;
56740
57374
  return this.callSiteExtractStart("extract_site", { ...body, background: true }, idempotencyKey4);
56741
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
+ }
56742
57389
  auditSite(input) {
56743
57390
  const { idempotencyKey: idempotencyKey4, ...body } = input;
56744
57391
  const requestBody = {
@@ -57073,7 +57720,7 @@ var init_http_mcp_tool_executor = __esm({
57073
57720
  report: input.report,
57074
57721
  format: input.format
57075
57722
  }, this.timeoutMs, "POST", {
57076
- "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")}`
57077
57724
  });
57078
57725
  }
57079
57726
  commonsSearchEntities(input) {
@@ -57112,7 +57759,7 @@ var init_http_mcp_tool_executor = __esm({
57112
57759
  commonsSubmitEntity(input) {
57113
57760
  const { idempotencyKey: idempotencyKey4, ...body } = input;
57114
57761
  return this.call("/commons/entities/propose", { ...body, idempotencyKey: idempotencyKey4 }, this.timeoutMs, "POST", {
57115
- "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")}`
57116
57763
  });
57117
57764
  }
57118
57765
  commonsGetEntityLedger(input) {
@@ -57127,7 +57774,7 @@ var init_http_mcp_tool_executor = __esm({
57127
57774
  commonsUpdateEditorialArticle(input) {
57128
57775
  const { idempotencyKey: idempotencyKey4, ...body } = input;
57129
57776
  return this.call("/commons/publications/articles", { ...body, idempotencyKey: idempotencyKey4 }, this.timeoutMs, "POST", {
57130
- "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")}`
57131
57778
  });
57132
57779
  }
57133
57780
  commonsSaveFilter(input) {
@@ -57148,13 +57795,13 @@ var init_http_mcp_tool_executor = __esm({
57148
57795
  commonsClaimPublication(input) {
57149
57796
  const { idempotencyKey: idempotencyKey4, ...body } = input;
57150
57797
  return this.call("/commons/publications/claim", { ...body, idempotencyKey: idempotencyKey4 }, this.timeoutMs, "POST", {
57151
- "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")}`
57152
57799
  });
57153
57800
  }
57154
57801
  commonsPublishEditorial(input) {
57155
57802
  const { idempotencyKey: idempotencyKey4, ...body } = input;
57156
57803
  return this.call("/commons/publications/publish", { ...body, idempotencyKey: idempotencyKey4 }, this.timeoutMs, "POST", {
57157
- "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")}`
57158
57805
  });
57159
57806
  }
57160
57807
  commonsGetPublication(input) {
@@ -57162,13 +57809,13 @@ var init_http_mcp_tool_executor = __esm({
57162
57809
  return this.getJson(input.subdomain ? `/commons/publications/${encodeURIComponent(input.subdomain)}?${query}` : `/commons/publications/me?${query}`);
57163
57810
  }
57164
57811
  async captureSerpSnapshot(input) {
57165
- 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");
57166
57813
  const now = Date.now();
57167
57814
  for (const [pendingFingerprint, pendingEntry] of this.pendingSerpCaptureBillingKeys) {
57168
57815
  if (pendingEntry.expiresAt <= now) this.pendingSerpCaptureBillingKeys.delete(pendingFingerprint);
57169
57816
  }
57170
57817
  const pending = this.pendingSerpCaptureBillingKeys.get(fingerprint2);
57171
- 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)();
57172
57819
  this.pendingSerpCaptureBillingKeys.set(fingerprint2, {
57173
57820
  key: idempotencyKey4,
57174
57821
  expiresAt: now + 15 * 6e4
@@ -61645,7 +62292,7 @@ async function createImageSourceArtifact(args) {
61645
62292
  if (args.content.length === 0 || args.content.length > IMAGE_SOURCE_MAX_BYTES) {
61646
62293
  throw new Error("image_source_size_invalid");
61647
62294
  }
61648
- const id = (0, import_node_crypto37.randomUUID)().replaceAll("-", "");
62295
+ const id = (0, import_node_crypto38.randomUUID)().replaceAll("-", "");
61649
62296
  return createPrivateArtifact({
61650
62297
  policy: policy4(),
61651
62298
  ownerId: args.ownerId,
@@ -61664,11 +62311,11 @@ async function readOwnedImageSourceArtifact(args) {
61664
62311
  maxBytes: IMAGE_SOURCE_MAX_BYTES
61665
62312
  });
61666
62313
  }
61667
- 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;
61668
62315
  var init_image_source_artifacts = __esm({
61669
62316
  "src/api/image-source-artifacts.ts"() {
61670
62317
  "use strict";
61671
- import_node_crypto37 = require("crypto");
62318
+ import_node_crypto38 = require("crypto");
61672
62319
  init_private_artifacts();
61673
62320
  IMAGE_SOURCE_ARTIFACT_PREFIX = "image-sources/";
61674
62321
  IMAGE_SOURCE_ARTIFACT_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
@@ -63282,7 +63929,7 @@ async function runBrowserAgentMigration() {
63282
63929
  }
63283
63930
  async function createExtensionRow(input) {
63284
63931
  const db = getDb();
63285
- 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)}`;
63286
63933
  await db.execute({
63287
63934
  sql: `INSERT INTO browser_agent_extensions (id, user_id, name, backend_id, backend_name, source, source_url, size_bytes)
63288
63935
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -63317,7 +63964,7 @@ async function deleteExtensionRow(userId, name) {
63317
63964
  }
63318
63965
  async function createAuthConnectionRow(input) {
63319
63966
  const db = getDb();
63320
- 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)}`;
63321
63968
  await db.execute({
63322
63969
  sql: `INSERT INTO browser_auth_connections (connection_id, domain, profile, account_email, note, status, browser_agent_session_id)
63323
63970
  VALUES (?, ?, ?, ?, ?, 'NEEDS_AUTH', ?)`,
@@ -63402,7 +64049,7 @@ async function deleteProfileLabel(userId, profile) {
63402
64049
  }
63403
64050
  async function createSessionRow(input) {
63404
64051
  const db = getDb();
63405
- 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)}`;
63406
64053
  await db.execute({
63407
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)
63408
64055
  VALUES (?, ?, ?, ?, 'open', ?, ?, ?, datetime('now'))`,
@@ -63479,7 +64126,7 @@ async function recordAction(input) {
63479
64126
  sql: `INSERT INTO browser_agent_actions (id, session_id, type, params_json, ok, error)
63480
64127
  VALUES (?, ?, ?, ?, ?, ?)`,
63481
64128
  args: [
63482
- `baa_${(0, import_node_crypto38.randomUUID)().replace(/-/g, "").slice(0, 20)}`,
64129
+ `baa_${(0, import_node_crypto39.randomUUID)().replace(/-/g, "").slice(0, 20)}`,
63483
64130
  input.sessionId,
63484
64131
  input.type,
63485
64132
  input.params == null ? null : JSON.stringify(input.params),
@@ -63519,11 +64166,11 @@ async function listReplayRows(sessionId) {
63519
64166
  });
63520
64167
  return res.rows;
63521
64168
  }
63522
- var import_node_crypto38, _ready2, _migrationPromise2, ORPHANED_SESSION_STATUS;
64169
+ var import_node_crypto39, _ready2, _migrationPromise2, ORPHANED_SESSION_STATUS;
63523
64170
  var init_browser_agent_db = __esm({
63524
64171
  "src/api/browser-agent-db.ts"() {
63525
64172
  "use strict";
63526
- import_node_crypto38 = require("crypto");
64173
+ import_node_crypto39 = require("crypto");
63527
64174
  init_db();
63528
64175
  _ready2 = false;
63529
64176
  _migrationPromise2 = null;
@@ -64670,8 +65317,8 @@ function kernelClient() {
64670
65317
  return new import_sdk11.default({ apiKey });
64671
65318
  }
64672
65319
  function backendName(userId, name, resource) {
64673
- const digest2 = (0, import_node_crypto39.createHash)("sha256").update(`${userId}:${name}`).digest("hex").slice(0, 16);
64674
- 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);
64675
65322
  return `mcp-serp-${resource}-${digest2}-${nonce}`;
64676
65323
  }
64677
65324
  function isNotFound2(error) {
@@ -64749,11 +65396,11 @@ async function deleteSerpIdentity(userId, name) {
64749
65396
  throw error;
64750
65397
  }
64751
65398
  }
64752
- var import_node_crypto39, import_sdk11, MAX_SERP_IDENTITIES_PER_USER;
65399
+ var import_node_crypto40, import_sdk11, MAX_SERP_IDENTITIES_PER_USER;
64753
65400
  var init_serp_identity_service = __esm({
64754
65401
  "src/api/serp-identity-service.ts"() {
64755
65402
  "use strict";
64756
- import_node_crypto39 = require("crypto");
65403
+ import_node_crypto40 = require("crypto");
64757
65404
  import_sdk11 = __toESM(require("@onkernel/sdk"), 1);
64758
65405
  init_browser_service_env();
64759
65406
  init_serp_identity_db();
@@ -65464,7 +66111,7 @@ function buildBrowserAgentRoutes() {
65464
66111
  }
65465
66112
  const existing = await getExtensionRow(user.id, name);
65466
66113
  if (existing) return c.json({ error: `an extension named "${name}" already exists \u2014 delete it first or pick another name` }, 409);
65467
- 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, "")}`;
65468
66115
  try {
65469
66116
  const imported = await importExtensionFromStore(storeUrl, backendName2);
65470
66117
  const row = await createExtensionRow({
@@ -65523,11 +66170,11 @@ function buildBrowserAgentRoutes() {
65523
66170
  });
65524
66171
  return app2;
65525
66172
  }
65526
- 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;
65527
66174
  var init_browser_agent_routes = __esm({
65528
66175
  "src/api/browser-agent-routes.ts"() {
65529
66176
  "use strict";
65530
- import_node_crypto40 = require("crypto");
66177
+ import_node_crypto41 = require("crypto");
65531
66178
  import_hono24 = require("hono");
65532
66179
  init_api_auth();
65533
66180
  init_errors();
@@ -66085,7 +66732,7 @@ async function getKeys() {
66085
66732
  const privateKey = await (0, import_jose2.importPKCS8)(pem, "RS256", { extractable: true });
66086
66733
  const full = await (0, import_jose2.exportJWK)(privateKey);
66087
66734
  const publicJwk = { kty: full.kty, n: full.n, e: full.e };
66088
- 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);
66089
66736
  publicJwk.kid = kid;
66090
66737
  publicJwk.alg = "RS256";
66091
66738
  publicJwk.use = "sig";
@@ -66264,23 +66911,23 @@ async function validateAuthRequest(p) {
66264
66911
  }
66265
66912
  function pkceMatches(verifier, challenge) {
66266
66913
  if (!verifier) return false;
66267
- 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");
66268
66915
  return computed === challenge;
66269
66916
  }
66270
66917
  async function mintAccessToken(identity, scope, plan, audience) {
66271
66918
  const { privateKey, kid } = await getKeys();
66272
- 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);
66273
66920
  }
66274
66921
  function tokenErrorResponse(c, error, description, status) {
66275
66922
  return c.json({ error, error_description: description }, status);
66276
66923
  }
66277
- 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;
66278
66925
  var init_oauth_routes = __esm({
66279
66926
  "src/api/oauth-routes.ts"() {
66280
66927
  "use strict";
66281
66928
  import_hono26 = require("hono");
66282
66929
  import_cookie = require("hono/cookie");
66283
- import_node_crypto41 = require("crypto");
66930
+ import_node_crypto42 = require("crypto");
66284
66931
  import_jose2 = require("jose");
66285
66932
  init_session();
66286
66933
  init_db();
@@ -66387,7 +67034,7 @@ var init_oauth_routes = __esm({
66387
67034
  }
66388
67035
  }
66389
67036
  const clientName = typeof body.client_name === "string" ? body.client_name : null;
66390
- const clientId = `client_${(0, import_node_crypto41.randomBytes)(16).toString("hex")}`;
67037
+ const clientId = `client_${(0, import_node_crypto42.randomBytes)(16).toString("hex")}`;
66391
67038
  await withOAuthDatabaseDeadline("register-client", registerClient(clientId, redirectUris, clientName));
66392
67039
  console.log("[oauth-dcr] register OK client_id=%s redirect_uris=%s", clientId, JSON.stringify(redirectUris));
66393
67040
  return c.json({
@@ -66440,7 +67087,7 @@ var init_oauth_routes = __esm({
66440
67087
  if (action3 === "deny") return redirectWithError(p.redirect_uri, p.state, "access_denied");
66441
67088
  if (action3 !== "approve") return c.text("unsupported action", 400);
66442
67089
  const scope = negotiateScope(p.scope, user, p.resource);
66443
- const code = `code_${(0, import_node_crypto41.randomBytes)(32).toString("base64url")}`;
67090
+ const code = `code_${(0, import_node_crypto42.randomBytes)(32).toString("base64url")}`;
66444
67091
  const expiresAt = new Date(Date.now() + CODE_TTL_SECONDS * 1e3).toISOString();
66445
67092
  await withOAuthDatabaseDeadline("put-authorization-code", putCode({
66446
67093
  code,
@@ -66479,7 +67126,7 @@ var init_oauth_routes = __esm({
66479
67126
  const plan = user ? resolvePlan(user) : "free";
66480
67127
  const audience = record.resource ?? RESOURCE();
66481
67128
  const accessToken = await mintAccessToken(record.identity, record.scope, plan, audience);
66482
- const refreshToken = `rt_${(0, import_node_crypto41.randomBytes)(40).toString("base64url")}`;
67129
+ const refreshToken = `rt_${(0, import_node_crypto42.randomBytes)(40).toString("base64url")}`;
66483
67130
  await withOAuthDatabaseDeadline("put-refresh-token", putRefresh({
66484
67131
  refresh_token: refreshToken,
66485
67132
  client_id: clientId,
@@ -66509,7 +67156,7 @@ var init_oauth_routes = __esm({
66509
67156
  const plan = user ? resolvePlan(user) : "free";
66510
67157
  const audience = record.resource ?? RESOURCE();
66511
67158
  const accessToken = await mintAccessToken(record.identity, record.scope, plan, audience);
66512
- const nextRefresh = `rt_${(0, import_node_crypto41.randomBytes)(40).toString("base64url")}`;
67159
+ const nextRefresh = `rt_${(0, import_node_crypto42.randomBytes)(40).toString("base64url")}`;
66513
67160
  await withOAuthDatabaseDeadline("rotate-refresh-token", rotateRefresh(refreshToken, {
66514
67161
  refresh_token: nextRefresh,
66515
67162
  client_id: record.client_id,
@@ -67929,7 +68576,7 @@ function quoteUntrusted(value) {
67929
68576
  return clean3.split("\n").map((line) => `> ${line}`).join("\n");
67930
68577
  }
67931
68578
  function shortHash(value, length = 24) {
67932
- 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);
67933
68580
  }
67934
68581
  function safePathPart(value) {
67935
68582
  return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "contact";
@@ -68417,11 +69064,11 @@ async function captureSupportMessage(memoryKey, email, options) {
68417
69064
  direction: options.direction
68418
69065
  };
68419
69066
  }
68420
- var import_node_crypto42, SUPPORT_TAGS, ISSUE_TAGS;
69067
+ var import_node_crypto43, SUPPORT_TAGS, ISSUE_TAGS;
68421
69068
  var init_resend_support_thread = __esm({
68422
69069
  "src/api/resend-support-thread.ts"() {
68423
69070
  "use strict";
68424
- import_node_crypto42 = require("crypto");
69071
+ import_node_crypto43 = require("crypto");
68425
69072
  init_memory();
68426
69073
  SUPPORT_TAGS = [
68427
69074
  {
@@ -69006,7 +69653,7 @@ async function claimWorkshopRegistration(input) {
69006
69653
  ) < ?
69007
69654
  `,
69008
69655
  args: [
69009
- (0, import_node_crypto43.randomUUID)(),
69656
+ (0, import_node_crypto44.randomUUID)(),
69010
69657
  input.eventSlug,
69011
69658
  input.email,
69012
69659
  input.firstName,
@@ -69023,7 +69670,7 @@ async function claimWorkshopRegistration(input) {
69023
69670
  id, event_slug, email, first_name, last_name, status
69024
69671
  ) VALUES (?, ?, ?, ?, ?, 'waitlisted')
69025
69672
  `,
69026
- 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]
69027
69674
  });
69028
69675
  const waitlisted = await getWorkshopRegistration(input.eventSlug, input.email);
69029
69676
  if (!waitlisted) throw new Error("workshop registration could not be claimed");
@@ -69038,7 +69685,7 @@ async function registerWorkshopInterest(input) {
69038
69685
  id, event_slug, email, first_name, last_name, status
69039
69686
  ) VALUES (?, ?, ?, ?, ?, 'interested')
69040
69687
  `,
69041
- 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]
69042
69689
  });
69043
69690
  const registration = await getWorkshopRegistration(input.eventSlug, input.email);
69044
69691
  if (!registration) throw new Error("workshop interest could not be recorded");
@@ -69068,11 +69715,11 @@ async function updateWorkshopRegistration(id, patch) {
69068
69715
  args: [...entries.map(([, value]) => value), id]
69069
69716
  });
69070
69717
  }
69071
- var import_node_crypto43;
69718
+ var import_node_crypto44;
69072
69719
  var init_workshop_registration_repository = __esm({
69073
69720
  "src/api/workshop-registration-repository.ts"() {
69074
69721
  "use strict";
69075
- import_node_crypto43 = require("crypto");
69722
+ import_node_crypto44 = require("crypto");
69076
69723
  init_db();
69077
69724
  }
69078
69725
  });
@@ -69645,7 +70292,7 @@ function renderEditorialReadingRoom(input, now = /* @__PURE__ */ new Date()) {
69645
70292
  articleCount: articles.length,
69646
70293
  wordCount: totalWordCount,
69647
70294
  bytes,
69648
- sha256: (0, import_node_crypto44.createHash)("sha256").update(html).digest("hex"),
70295
+ sha256: (0, import_node_crypto45.createHash)("sha256").update(html).digest("hex"),
69649
70296
  warnings
69650
70297
  };
69651
70298
  }
@@ -69728,14 +70375,14 @@ ${provenance}`;
69728
70375
  ...rendered,
69729
70376
  html,
69730
70377
  bytes: Buffer.byteLength(html),
69731
- sha256: (0, import_node_crypto44.createHash)("sha256").update(html).digest("hex")
70378
+ sha256: (0, import_node_crypto45.createHash)("sha256").update(html).digest("hex")
69732
70379
  };
69733
70380
  }
69734
- 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;
69735
70382
  var init_render = __esm({
69736
70383
  "src/editorial-reading-room/render.ts"() {
69737
70384
  "use strict";
69738
- import_node_crypto44 = require("crypto");
70385
+ import_node_crypto45 = require("crypto");
69739
70386
  import_node_fs14 = require("fs");
69740
70387
  import_node_path21 = require("path");
69741
70388
  import_marked = require("marked");
@@ -69999,7 +70646,7 @@ async function hostCommonsImage(input) {
69999
70646
  415
70000
70647
  );
70001
70648
  }
70002
- 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");
70003
70650
  const existing = await getDb().execute({
70004
70651
  sql: "SELECT id, url, content_type, bytes, source_url FROM commons_images WHERE digest = ? LIMIT 1",
70005
70652
  args: [digest2]
@@ -70087,11 +70734,11 @@ async function hostEntityImages(input) {
70087
70734
  }
70088
70735
  return rewrite;
70089
70736
  }
70090
- 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;
70091
70738
  var init_commons_image_store = __esm({
70092
70739
  "src/api/commons-image-store.ts"() {
70093
70740
  "use strict";
70094
- import_node_crypto45 = require("crypto");
70741
+ import_node_crypto46 = require("crypto");
70095
70742
  import_promises16 = require("dns/promises");
70096
70743
  import_node_net2 = require("net");
70097
70744
  init_blob_store();
@@ -70114,141 +70761,6 @@ var init_commons_image_store = __esm({
70114
70761
  }
70115
70762
  });
70116
70763
 
70117
- // src/api/commons-embeddings.ts
70118
- function commonsEmbedModel() {
70119
- return (process.env.JINA_EMBED_MODEL ?? "jina-embeddings-v5-omni-small").trim();
70120
- }
70121
- function commonsEmbedDim() {
70122
- return Number((process.env.JINA_EMBED_DIM ?? "1024").trim());
70123
- }
70124
- function commonsSemanticSearchConfigured() {
70125
- return Boolean(process.env.JINA_API_KEY?.trim() && process.env.MEMORY_DATABASE_URL?.trim());
70126
- }
70127
- function vectorSql() {
70128
- if (_vectorSql) return _vectorSql;
70129
- const url = process.env.MEMORY_DATABASE_URL?.trim();
70130
- if (!url) throw new Error("MEMORY_DATABASE_URL is not set; Commons semantic search needs the shared Postgres.");
70131
- _vectorSql = (0, import_serverless.neon)(url);
70132
- return _vectorSql;
70133
- }
70134
- async function ensureCommonsVectorSchema() {
70135
- if (vectorSchemaReady) return;
70136
- const dimension = commonsEmbedDim();
70137
- await vectorSql().query("CREATE EXTENSION IF NOT EXISTS vector");
70138
- await vectorSql().query(`
70139
- CREATE TABLE IF NOT EXISTS commons_index_vectors (
70140
- document_id TEXT PRIMARY KEY,
70141
- entity_id TEXT NOT NULL,
70142
- document_type TEXT NOT NULL,
70143
- title TEXT NOT NULL,
70144
- embedding vector(${dimension}) NOT NULL,
70145
- model TEXT NOT NULL,
70146
- updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
70147
- )
70148
- `);
70149
- await vectorSql().query("CREATE INDEX IF NOT EXISTS commons_index_vectors_entity ON commons_index_vectors(entity_id)");
70150
- vectorSchemaReady = true;
70151
- }
70152
- async function embedCommonsTexts(texts) {
70153
- const apiKey = process.env.JINA_API_KEY?.trim();
70154
- if (!apiKey) throw new Error("JINA_API_KEY is not set; Commons semantic search cannot embed.");
70155
- if (!texts.length) return [];
70156
- const response = await fetch("https://api.jina.ai/v1/embeddings", {
70157
- method: "POST",
70158
- headers: { authorization: `Bearer ${apiKey}`, "content-type": "application/json" },
70159
- body: JSON.stringify({
70160
- model: commonsEmbedModel(),
70161
- dimensions: commonsEmbedDim(),
70162
- input: texts.map((text2) => ({ text: text2.slice(0, 8e3) }))
70163
- }),
70164
- signal: AbortSignal.timeout(6e4)
70165
- });
70166
- if (!response.ok) {
70167
- throw new Error(`Jina embedding request failed with HTTP ${response.status}: ${(await response.text()).slice(0, 200)}`);
70168
- }
70169
- const payload = await response.json();
70170
- const vectors = (payload.data ?? []).map((item) => item.embedding);
70171
- if (vectors.length !== texts.length) {
70172
- throw new Error(`Jina returned ${vectors.length} embeddings for ${texts.length} inputs.`);
70173
- }
70174
- return vectors;
70175
- }
70176
- async function embedQueuedCommonsDocuments(limit = 50) {
70177
- if (!commonsSemanticSearchConfigured()) return { claimed: 0, embedded: 0, failed: 0, remaining: 0 };
70178
- await ensureCommonsVectorSchema();
70179
- const bounded2 = Math.max(1, Math.min(200, Math.floor(limit)));
70180
- const queued = await getDb().execute({
70181
- sql: `SELECT id, entity_id, document_type, title, text FROM commons_index_documents
70182
- WHERE embedding_status IN ('queued', 'failed') ORDER BY updated_at ASC LIMIT ?`,
70183
- args: [bounded2]
70184
- });
70185
- const rows = queued.rows;
70186
- if (!rows.length) return { claimed: 0, embedded: 0, failed: 0, remaining: await queuedCommonsDocumentCount() };
70187
- let embedded = 0;
70188
- let failed = 0;
70189
- const model = commonsEmbedModel();
70190
- try {
70191
- const vectors = await embedCommonsTexts(rows.map((row) => `${row.title}
70192
-
70193
- ${row.text}`));
70194
- for (const [index, row] of rows.entries()) {
70195
- const literal = `[${vectors[index].join(",")}]`;
70196
- await vectorSql().query(
70197
- `INSERT INTO commons_index_vectors (document_id, entity_id, document_type, title, embedding, model, updated_at)
70198
- VALUES ($1, $2, $3, $4, $5::vector, $6, now())
70199
- ON CONFLICT (document_id) DO UPDATE SET entity_id = EXCLUDED.entity_id, document_type = EXCLUDED.document_type,
70200
- title = EXCLUDED.title, embedding = EXCLUDED.embedding, model = EXCLUDED.model, updated_at = now()`,
70201
- [row.id, row.entity_id, row.document_type, row.title, literal, model]
70202
- );
70203
- await getDb().execute({
70204
- sql: `UPDATE commons_index_documents SET embedding_status = 'indexed', embedding_provider = ?, embedding_model = ?,
70205
- vector_ref = ?, indexed_at = ?, error = NULL WHERE id = ?`,
70206
- args: [COMMONS_EMBED_PROVIDER, model, row.id, (/* @__PURE__ */ new Date()).toISOString(), row.id]
70207
- });
70208
- embedded += 1;
70209
- }
70210
- } catch (error) {
70211
- failed = rows.length - embedded;
70212
- const message = (error instanceof Error ? error.message : String(error)).slice(0, 500);
70213
- for (const row of rows.slice(embedded)) {
70214
- await getDb().execute({
70215
- sql: `UPDATE commons_index_documents SET embedding_status = 'failed', error = ? WHERE id = ?`,
70216
- args: [message, row.id]
70217
- }).catch(() => void 0);
70218
- }
70219
- }
70220
- return { claimed: rows.length, embedded, failed, remaining: await queuedCommonsDocumentCount() };
70221
- }
70222
- async function queuedCommonsDocumentCount() {
70223
- const result = await getDb().execute(`SELECT COUNT(*) AS n FROM commons_index_documents WHERE embedding_status IN ('queued', 'failed')`);
70224
- return Number(result.rows[0]?.n ?? 0);
70225
- }
70226
- async function semanticCommonsEntityScores(query, limit = 40) {
70227
- const scores = /* @__PURE__ */ new Map();
70228
- if (!commonsSemanticSearchConfigured() || !query.trim()) return scores;
70229
- await ensureCommonsVectorSchema();
70230
- const [vector] = await embedCommonsTexts([query]);
70231
- if (!vector) return scores;
70232
- const rows = await vectorSql().query(
70233
- `SELECT entity_id, MAX(1 - (embedding <=> $1::vector)) AS score
70234
- FROM commons_index_vectors GROUP BY entity_id ORDER BY score DESC LIMIT $2`,
70235
- [`[${vector.join(",")}]`, Math.max(1, Math.min(100, limit))]
70236
- );
70237
- for (const row of rows) scores.set(String(row.entity_id), Number(row.score));
70238
- return scores;
70239
- }
70240
- var import_serverless, COMMONS_EMBED_PROVIDER, _vectorSql, vectorSchemaReady;
70241
- var init_commons_embeddings = __esm({
70242
- "src/api/commons-embeddings.ts"() {
70243
- "use strict";
70244
- import_serverless = require("@neondatabase/serverless");
70245
- init_db();
70246
- COMMONS_EMBED_PROVIDER = "jina";
70247
- _vectorSql = null;
70248
- vectorSchemaReady = false;
70249
- }
70250
- });
70251
-
70252
70764
  // src/api/schema-presence.ts
70253
70765
  async function loadSchemaObjects() {
70254
70766
  const res = await getDb().execute(
@@ -70351,7 +70863,7 @@ function buildCommonsLinkset(entity, claims) {
70351
70863
  context[claim.predicate] = targets;
70352
70864
  }
70353
70865
  const document2 = { linkset: [context] };
70354
- 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")}"`;
70355
70867
  return { document: document2, etag, publicUrl, linksetUrl, profile: COMMONS_RELATIONSHIP_PROFILE };
70356
70868
  }
70357
70869
  function commonsLinksetDiscoveryHeader(idOrSlug) {
@@ -70419,11 +70931,11 @@ function normalizeHreflang(value) {
70419
70931
  const languages = [...new Set(value.map((item) => optionalText(item, 80)).filter((item) => Boolean(item)))];
70420
70932
  return languages.length ? languages : void 0;
70421
70933
  }
70422
- 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;
70423
70935
  var init_commons_linksets = __esm({
70424
70936
  "src/api/commons-linksets.ts"() {
70425
70937
  "use strict";
70426
- import_node_crypto46 = require("crypto");
70938
+ import_node_crypto47 = require("crypto");
70427
70939
  COMMONS_LINKSET_MEDIA_TYPE = "application/linkset+json";
70428
70940
  COMMONS_RELATIONSHIP_PROFILE = "https://mcpscraper.dev/commons/profiles/relationships/v1";
70429
70941
  REGISTERED_RELATIONS = /* @__PURE__ */ new Set([
@@ -71063,7 +71575,7 @@ async function submitCommonsEntity(input, user) {
71063
71575
  const existingClaims = existing && normalized.claims !== void 0 ? await getCommonsClaimsByEntityId(existing.id) : [];
71064
71576
  const safety = evaluatePublishSafety(normalized, existing);
71065
71577
  const shouldApply = safety.safe && normalized.reviewPolicy !== "always_review";
71066
- const proposalId = `commons-proposal-${(0, import_node_crypto47.randomUUID)()}`;
71578
+ const proposalId = `commons-proposal-${(0, import_node_crypto48.randomUUID)()}`;
71067
71579
  const now = (/* @__PURE__ */ new Date()).toISOString();
71068
71580
  const entityId = existing?.id ?? normalized.entityId ?? allocateEntityId();
71069
71581
  const proposalStatus = shouldApply ? "accepted" : "pending_review";
@@ -71118,7 +71630,7 @@ async function submitCommonsEntity(input, user) {
71118
71630
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
71119
71631
  `,
71120
71632
  args: [
71121
- `commons-ledger-${(0, import_node_crypto47.randomUUID)()}`,
71633
+ `commons-ledger-${(0, import_node_crypto48.randomUUID)()}`,
71122
71634
  nextEntity.id,
71123
71635
  proposalId,
71124
71636
  user.id,
@@ -71224,7 +71736,7 @@ async function saveCommonsFilter(input, user) {
71224
71736
  sql: "SELECT id FROM commons_saved_filters WHERE user_id = ? AND name = ? LIMIT 1",
71225
71737
  args: [user.id, name]
71226
71738
  });
71227
- 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)()}`;
71228
71740
  await getDb().execute({
71229
71741
  sql: `
71230
71742
  INSERT INTO commons_saved_filters (id, user_id, name, description, filter_json, created_at, updated_at)
@@ -72002,7 +72514,7 @@ function commonsIndexDocuments(entity, now) {
72002
72514
  return documents;
72003
72515
  }
72004
72516
  function commonsIndexDocumentId(entityId, documentType, documentKey) {
72005
- return `commons-index-${(0, import_node_crypto47.createHash)("sha256").update(`${entityId}
72517
+ return `commons-index-${(0, import_node_crypto48.createHash)("sha256").update(`${entityId}
72006
72518
  ${documentType}
72007
72519
  ${documentKey}`).digest("hex").slice(0, 32)}`;
72008
72520
  }
@@ -72568,11 +73080,11 @@ function jsonLikeValue(value) {
72568
73080
  function escapeLike(value) {
72569
73081
  return value.replace(/[%_]/g, "");
72570
73082
  }
72571
- 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;
72572
73084
  var init_commons_repository = __esm({
72573
73085
  "src/api/commons-repository.ts"() {
72574
73086
  "use strict";
72575
- import_node_crypto47 = require("crypto");
73087
+ import_node_crypto48 = require("crypto");
72576
73088
  init_db();
72577
73089
  init_commons_image_store();
72578
73090
  init_commons_embeddings();
@@ -72951,7 +73463,7 @@ async function claimCommonsPublication(input, user) {
72951
73463
  }
72952
73464
  const existingName = await getCommonsPublicationBySubdomain(subdomain);
72953
73465
  if (existingName) throw new CommonsPublicationError("publication_name_unavailable", "That publication name is already claimed.", 409);
72954
- const id = `tcpub_${(0, import_node_crypto48.randomUUID)()}`;
73466
+ const id = `tcpub_${(0, import_node_crypto49.randomUUID)()}`;
72955
73467
  const now = (/* @__PURE__ */ new Date()).toISOString();
72956
73468
  try {
72957
73469
  await getDb().execute({
@@ -73003,7 +73515,7 @@ async function publishCommonsEditorial(input, user) {
73003
73515
  const canonicalUrl = editionPublicUrl(subdomain, editionSlug);
73004
73516
  const rendered = renderEditorialReadingRoom(editionInput);
73005
73517
  const html = addPublicMetadata(rendered.html, canonicalUrl, publication.title);
73006
- const editionId = `tced_${(0, import_node_crypto48.randomUUID)()}`;
73518
+ const editionId = `tced_${(0, import_node_crypto49.randomUUID)()}`;
73007
73519
  const revision = (latest?.revision ?? 0) + 1;
73008
73520
  const now = (/* @__PURE__ */ new Date()).toISOString();
73009
73521
  await getDb().batch([
@@ -73027,7 +73539,7 @@ async function publishCommonsEditorial(input, user) {
73027
73539
  JSON.stringify(editionInput.articles),
73028
73540
  html,
73029
73541
  rendered.filename,
73030
- (0, import_node_crypto48.createHash)("sha256").update(html).digest("hex"),
73542
+ (0, import_node_crypto49.createHash)("sha256").update(html).digest("hex"),
73031
73543
  rendered.articleCount,
73032
73544
  rendered.wordCount,
73033
73545
  Buffer.byteLength(html),
@@ -73085,7 +73597,7 @@ async function updateCommonsEditorialArticle(input, user) {
73085
73597
  const canonicalUrl = editionPublicUrl(subdomain, editionSlug);
73086
73598
  const rendered = renderEditorialReadingRoom(editionInput);
73087
73599
  const html = addPublicMetadata(rendered.html, canonicalUrl, publication.title);
73088
- const editionId = `tced_${(0, import_node_crypto48.randomUUID)()}`;
73600
+ const editionId = `tced_${(0, import_node_crypto49.randomUUID)()}`;
73089
73601
  const revision = latest.revision + 1;
73090
73602
  const now = (/* @__PURE__ */ new Date()).toISOString();
73091
73603
  await getDb().batch([
@@ -73109,7 +73621,7 @@ async function updateCommonsEditorialArticle(input, user) {
73109
73621
  JSON.stringify(nextArticles),
73110
73622
  html,
73111
73623
  rendered.filename,
73112
- (0, import_node_crypto48.createHash)("sha256").update(html).digest("hex"),
73624
+ (0, import_node_crypto49.createHash)("sha256").update(html).digest("hex"),
73113
73625
  rendered.articleCount,
73114
73626
  rendered.wordCount,
73115
73627
  Buffer.byteLength(html),
@@ -73314,11 +73826,11 @@ function addPublicMetadata(html, canonicalUrl, publicationTitle) {
73314
73826
  "</head>"
73315
73827
  ].join("\n"));
73316
73828
  }
73317
- var import_node_crypto48, PUBLICATION_ROOT_DOMAIN, RESERVED_SUBDOMAINS, CommonsPublicationError;
73829
+ var import_node_crypto49, PUBLICATION_ROOT_DOMAIN, RESERVED_SUBDOMAINS, CommonsPublicationError;
73318
73830
  var init_commons_publication_repository = __esm({
73319
73831
  "src/api/commons-publication-repository.ts"() {
73320
73832
  "use strict";
73321
- import_node_crypto48 = require("crypto");
73833
+ import_node_crypto49 = require("crypto");
73322
73834
  init_db();
73323
73835
  init_commons_repository();
73324
73836
  init_render();
@@ -74551,7 +75063,7 @@ async function migrateAnalytics() {
74551
75063
  }
74552
75064
  function normalizeSlug2(value) {
74553
75065
  const slug4 = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
74554
- return slug4 || `site-${(0, import_node_crypto49.randomBytes)(4).toString("hex")}`;
75066
+ return slug4 || `site-${(0, import_node_crypto50.randomBytes)(4).toString("hex")}`;
74555
75067
  }
74556
75068
  function normalizeObservedHostname(origin) {
74557
75069
  if (!origin || origin === "null") return null;
@@ -74564,7 +75076,7 @@ function normalizeObservedHostname(origin) {
74564
75076
  }
74565
75077
  }
74566
75078
  function publicPixelId() {
74567
- return `px_${(0, import_node_crypto49.randomBytes)(18).toString("base64url")}`;
75079
+ return `px_${(0, import_node_crypto50.randomBytes)(18).toString("base64url")}`;
74568
75080
  }
74569
75081
  function mapRows(rows) {
74570
75082
  return rows;
@@ -74596,7 +75108,7 @@ async function requireEditor(client2, siteId, userId) {
74596
75108
  async function createAnalyticsSite(input) {
74597
75109
  const db = getAnalyticsPool();
74598
75110
  const client2 = await db.connect();
74599
- const id = (0, import_node_crypto49.randomUUID)();
75111
+ const id = (0, import_node_crypto50.randomUUID)();
74600
75112
  const baseSlug = normalizeSlug2(input.slug || input.name);
74601
75113
  try {
74602
75114
  await client2.query("BEGIN");
@@ -74612,7 +75124,7 @@ async function createAnalyticsSite(input) {
74612
75124
  } catch (error) {
74613
75125
  const code = error.code;
74614
75126
  if (code !== "23505" || attempt === 2) throw error;
74615
- slug4 = `${baseSlug}-${(0, import_node_crypto49.randomBytes)(2).toString("hex")}`;
75127
+ slug4 = `${baseSlug}-${(0, import_node_crypto50.randomBytes)(2).toString("hex")}`;
74616
75128
  }
74617
75129
  }
74618
75130
  await client2.query(
@@ -74827,7 +75339,7 @@ async function createAnalyticsPixel(input) {
74827
75339
  VALUES ($1, $2, $3, $4, $5)
74828
75340
  RETURNING id, site_id, public_id, name, environment, status, created_at::text, NULL::text AS last_event_at`,
74829
75341
  [
74830
- (0, import_node_crypto49.randomUUID)(),
75342
+ (0, import_node_crypto50.randomUUID)(),
74831
75343
  input.siteId,
74832
75344
  publicPixelId(),
74833
75345
  input.name.trim(),
@@ -74904,7 +75416,7 @@ async function setAnalyticsPixelDomainState(input) {
74904
75416
  SELECT $1, p.id, $4, $5 FROM analytics_pixels p WHERE p.id = $2 AND p.site_id = $3
74905
75417
  ON CONFLICT(pixel_id, hostname) DO UPDATE SET state = EXCLUDED.state, updated_at = now()
74906
75418
  RETURNING id`,
74907
- [(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]
74908
75420
  );
74909
75421
  if (!result.rowCount)
74910
75422
  throw new AnalyticsRepositoryError(
@@ -74954,7 +75466,7 @@ function sanitizeAnalyticsProperties(value) {
74954
75466
  }
74955
75467
  async function ingestAnalyticsEvents(input) {
74956
75468
  const db = getAnalyticsPool();
74957
- const requestId = `air_${(0, import_node_crypto49.randomUUID)()}`;
75469
+ const requestId = `air_${(0, import_node_crypto50.randomUUID)()}`;
74958
75470
  const hostname = normalizeObservedHostname(input.origin);
74959
75471
  const pixelResult = await db.query(
74960
75472
  `SELECT p.id, p.site_id, p.status,
@@ -74975,7 +75487,7 @@ async function ingestAnalyticsEvents(input) {
74975
75487
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
74976
75488
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
74977
75489
  [
74978
- (0, import_node_crypto49.randomUUID)(),
75490
+ (0, import_node_crypto50.randomUUID)(),
74979
75491
  requestId,
74980
75492
  pixel?.site_id ?? null,
74981
75493
  pixel?.id ?? null,
@@ -75000,7 +75512,7 @@ async function ingestAnalyticsEvents(input) {
75000
75512
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
75001
75513
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
75002
75514
  [
75003
- (0, import_node_crypto49.randomUUID)(),
75515
+ (0, import_node_crypto50.randomUUID)(),
75004
75516
  requestId,
75005
75517
  pixel.site_id,
75006
75518
  pixel.id,
@@ -75025,7 +75537,7 @@ async function ingestAnalyticsEvents(input) {
75025
75537
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, rejected_count, reason_codes)
75026
75538
  VALUES ($1, $2, $3, $4, $5, $6::jsonb)`,
75027
75539
  [
75028
- (0, import_node_crypto49.randomUUID)(),
75540
+ (0, import_node_crypto50.randomUUID)(),
75029
75541
  requestId,
75030
75542
  pixel.site_id,
75031
75543
  pixel.id,
@@ -75049,7 +75561,7 @@ async function ingestAnalyticsEvents(input) {
75049
75561
  ON CONFLICT(pixel_id, hostname) DO UPDATE
75050
75562
  SET last_seen_at = now(), updated_at = now()
75051
75563
  RETURNING state`,
75052
- [(0, import_node_crypto49.randomUUID)(), pixel.id, hostname]
75564
+ [(0, import_node_crypto50.randomUUID)(), pixel.id, hostname]
75053
75565
  );
75054
75566
  if (domainResult.rows[0]?.state !== "approved") {
75055
75567
  await db.query(
@@ -75063,7 +75575,7 @@ async function ingestAnalyticsEvents(input) {
75063
75575
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
75064
75576
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
75065
75577
  [
75066
- (0, import_node_crypto49.randomUUID)(),
75578
+ (0, import_node_crypto50.randomUUID)(),
75067
75579
  requestId,
75068
75580
  pixel.site_id,
75069
75581
  pixel.id,
@@ -75113,7 +75625,7 @@ async function ingestAnalyticsEvents(input) {
75113
75625
  $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33
75114
75626
  ) ON CONFLICT(site_id, event_id) DO NOTHING`,
75115
75627
  [
75116
- (0, import_node_crypto49.randomUUID)(),
75628
+ (0, import_node_crypto50.randomUUID)(),
75117
75629
  event2.eventId,
75118
75630
  pixel.site_id,
75119
75631
  pixel.id,
@@ -75171,7 +75683,7 @@ async function ingestAnalyticsEvents(input) {
75171
75683
  id, request_id, site_id, pixel_id, hostname, accepted_count, rejected_count, reason_codes
75172
75684
  ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb)`,
75173
75685
  [
75174
- (0, import_node_crypto49.randomUUID)(),
75686
+ (0, import_node_crypto50.randomUUID)(),
75175
75687
  requestId,
75176
75688
  pixel.site_id,
75177
75689
  pixel.id,
@@ -75203,7 +75715,7 @@ function normalizeGeographyCode(value, max) {
75203
75715
  return normalized && /^[A-Z0-9-]+$/.test(normalized) ? normalized.slice(0, max) : null;
75204
75716
  }
75205
75717
  function pageFingerprint(scope, siteId, filters) {
75206
- 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);
75207
75719
  }
75208
75720
  function decodePageOffset(cursor, fingerprint2) {
75209
75721
  if (!cursor) return 0;
@@ -75253,7 +75765,7 @@ async function createAnalyticsConversion(input) {
75253
75765
  404
75254
75766
  );
75255
75767
  }
75256
- const id = (0, import_node_crypto49.randomUUID)();
75768
+ const id = (0, import_node_crypto50.randomUUID)();
75257
75769
  const resolvedPerson = input.sessionId ? await db.query(
75258
75770
  `SELECT e.person_id FROM analytics_identity_nodes n JOIN analytics_identity_edges e ON e.identity_node_id = n.id
75259
75771
  WHERE n.site_id = $1 AND n.kind = 'session_id' AND n.value_hmac = $2 ORDER BY e.confidence DESC LIMIT 1`,
@@ -76002,7 +76514,7 @@ async function createAnalyticsCampaignLink(input) {
76002
76514
  404
76003
76515
  );
76004
76516
  }
76005
- 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);
76006
76518
  try {
76007
76519
  const result = await db.query(
76008
76520
  `INSERT INTO analytics_campaign_links(
@@ -76011,7 +76523,7 @@ async function createAnalyticsCampaignLink(input) {
76011
76523
  ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15)
76012
76524
  RETURNING *, 0::int AS click_count`,
76013
76525
  [
76014
- (0, import_node_crypto49.randomUUID)(),
76526
+ (0, import_node_crypto50.randomUUID)(),
76015
76527
  input.siteId,
76016
76528
  input.pixelId ?? null,
76017
76529
  input.name.trim(),
@@ -76082,7 +76594,7 @@ async function resolveAnalyticsCampaignLink(shortCode, referrer) {
76082
76594
  if (!row) return null;
76083
76595
  await db.query(
76084
76596
  `INSERT INTO analytics_campaign_clicks(id, link_id, referrer) VALUES ($1, $2, $3)`,
76085
- [(0, import_node_crypto49.randomUUID)(), row.id, normalizeAnalyticsUrl(referrer || void 0)]
76597
+ [(0, import_node_crypto50.randomUUID)(), row.id, normalizeAnalyticsUrl(referrer || void 0)]
76086
76598
  );
76087
76599
  return buildTaggedCampaignUrl(row);
76088
76600
  }
@@ -76100,14 +76612,14 @@ async function createAnalyticsForm(input) {
76100
76612
  404
76101
76613
  );
76102
76614
  const baseSlug = normalizeSlug2(input.name);
76103
- const slug4 = `${baseSlug}-${(0, import_node_crypto49.randomBytes)(3).toString("hex")}`;
76104
- 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")}`;
76105
76617
  const result = await db.query(
76106
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)
76107
76619
  VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8::jsonb,$9,$10,$11,$12,$13,$14)
76108
76620
  RETURNING *, 0::int AS submission_count`,
76109
76621
  [
76110
- (0, import_node_crypto49.randomUUID)(),
76622
+ (0, import_node_crypto50.randomUUID)(),
76111
76623
  publicId,
76112
76624
  input.siteId,
76113
76625
  input.pixelId,
@@ -76152,7 +76664,7 @@ async function getPublicAnalyticsForm(publicId) {
76152
76664
  return result.rows[0] ?? null;
76153
76665
  }
76154
76666
  async function recordAnalyticsFormSubmission(input) {
76155
- const id = (0, import_node_crypto49.randomUUID)();
76667
+ const id = (0, import_node_crypto50.randomUUID)();
76156
76668
  await getAnalyticsPool().query(
76157
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)
76158
76670
  VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12::jsonb)`,
@@ -76183,7 +76695,7 @@ function sanitizeClickIds(value) {
76183
76695
  return output;
76184
76696
  }
76185
76697
  function identityHmac(kind, value) {
76186
- 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");
76187
76699
  }
76188
76700
  async function linkAnalyticsFormIdentity(input) {
76189
76701
  const client2 = await getAnalyticsPool().connect();
@@ -76192,7 +76704,7 @@ async function linkAnalyticsFormIdentity(input) {
76192
76704
  const person = await client2.query(
76193
76705
  `INSERT INTO analytics_people(id, site_id, crm_person_ref) VALUES ($1,$2,$3)
76194
76706
  ON CONFLICT(site_id, crm_person_ref) DO UPDATE SET last_seen_at = now() RETURNING id`,
76195
- [(0, import_node_crypto49.randomUUID)(), input.siteId, input.crmPersonRef]
76707
+ [(0, import_node_crypto50.randomUUID)(), input.siteId, input.crmPersonRef]
76196
76708
  );
76197
76709
  const personId = person.rows[0].id;
76198
76710
  const signals = [];
@@ -76239,7 +76751,7 @@ async function linkAnalyticsFormIdentity(input) {
76239
76751
  `INSERT INTO analytics_identity_nodes(id, site_id, kind, value_hmac) VALUES ($1,$2,$3,$4)
76240
76752
  ON CONFLICT(site_id, kind, value_hmac) DO UPDATE SET last_seen_at = now() RETURNING id`,
76241
76753
  [
76242
- (0, import_node_crypto49.randomUUID)(),
76754
+ (0, import_node_crypto50.randomUUID)(),
76243
76755
  input.siteId,
76244
76756
  signal.kind,
76245
76757
  identityHmac(signal.kind, signal.value)
@@ -76250,7 +76762,7 @@ async function linkAnalyticsFormIdentity(input) {
76250
76762
  VALUES ($1,$2,$3,$4,$5,$6)
76251
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)`,
76252
76764
  [
76253
- (0, import_node_crypto49.randomUUID)(),
76765
+ (0, import_node_crypto50.randomUUID)(),
76254
76766
  input.siteId,
76255
76767
  personId,
76256
76768
  node.rows[0].id,
@@ -76348,7 +76860,7 @@ async function createAnalyticsCrmImport(input) {
76348
76860
  const db = getAnalyticsPool();
76349
76861
  await requireEditor(db, input.siteId, input.userId);
76350
76862
  const client2 = await db.connect();
76351
- const id = (0, import_node_crypto49.randomUUID)();
76863
+ const id = (0, import_node_crypto50.randomUUID)();
76352
76864
  try {
76353
76865
  await client2.query("BEGIN");
76354
76866
  await client2.query(
@@ -76368,7 +76880,7 @@ async function createAnalyticsCrmImport(input) {
76368
76880
  await client2.query(
76369
76881
  `INSERT INTO analytics_crm_import_rows(id, import_id, crm_person_ref, payload_ciphertext)
76370
76882
  VALUES ($1,$2,$3,$4) ON CONFLICT(import_id, crm_person_ref) DO NOTHING`,
76371
- [(0, import_node_crypto49.randomUUID)(), id, row.crmPersonRef, row.payloadCiphertext]
76883
+ [(0, import_node_crypto50.randomUUID)(), id, row.crmPersonRef, row.payloadCiphertext]
76372
76884
  );
76373
76885
  }
76374
76886
  await client2.query("COMMIT");
@@ -76407,7 +76919,7 @@ async function createAnalyticsActivationDestination(input) {
76407
76919
  `INSERT INTO analytics_activation_destinations(id, site_id, platform, name, connection_ref, external_dataset_id, event_mapping, created_by_user_id)
76408
76920
  VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8) RETURNING *`,
76409
76921
  [
76410
- (0, import_node_crypto49.randomUUID)(),
76922
+ (0, import_node_crypto50.randomUUID)(),
76411
76923
  input.siteId,
76412
76924
  input.platform,
76413
76925
  input.name.trim(),
@@ -76459,7 +76971,7 @@ async function queueAnalyticsActivation(input) {
76459
76971
  `INSERT INTO analytics_activation_jobs(id, destination_id, conversion_id, person_id, payload_ciphertext)
76460
76972
  VALUES ($1,$2,$3,$4,$5) ON CONFLICT(destination_id, conversion_id) DO NOTHING`,
76461
76973
  [
76462
- (0, import_node_crypto49.randomUUID)(),
76974
+ (0, import_node_crypto50.randomUUID)(),
76463
76975
  destination.id,
76464
76976
  input.conversionId,
76465
76977
  input.personId ?? null,
@@ -76471,7 +76983,7 @@ async function queueAnalyticsActivation(input) {
76471
76983
  return queued;
76472
76984
  }
76473
76985
  async function queueAnalyticsFormDelivery(input) {
76474
- const id = (0, import_node_crypto49.randomUUID)();
76986
+ const id = (0, import_node_crypto50.randomUUID)();
76475
76987
  await getAnalyticsPool().query(
76476
76988
  `INSERT INTO analytics_form_delivery_jobs(id, submission_id, owner_user_id, payload_ciphertext, last_error_code)
76477
76989
  VALUES ($1,$2,$3,$4,$5)`,
@@ -76598,7 +77110,7 @@ async function analyticsHealth(siteId, userId) {
76598
77110
  }
76599
77111
  async function refreshAnalyticsDailyRollups(input) {
76600
77112
  const db = getAnalyticsPool();
76601
- const runId = (0, import_node_crypto49.randomUUID)();
77113
+ const runId = (0, import_node_crypto50.randomUUID)();
76602
77114
  await db.query(
76603
77115
  `INSERT INTO analytics_rollup_runs(id, window_start, window_end, status) VALUES ($1, $2, $3, 'running')`,
76604
77116
  [runId, input.start, input.end]
@@ -76692,7 +77204,7 @@ async function refreshAnalyticsDailyRollupsIfDue(now = /* @__PURE__ */ new Date(
76692
77204
  lockClient.release();
76693
77205
  }
76694
77206
  }
76695
- function csvCell2(value) {
77207
+ function csvCell3(value) {
76696
77208
  const text2 = value == null ? "" : String(value);
76697
77209
  return /[\n\r,\"]/.test(text2) ? `"${text2.replaceAll('"', '""')}"` : text2;
76698
77210
  }
@@ -76702,7 +77214,7 @@ function rowsToCsv2(rows) {
76702
77214
  return [
76703
77215
  columns.join(","),
76704
77216
  ...rows.map(
76705
- (row) => columns.map((column) => csvCell2(row[column])).join(",")
77217
+ (row) => columns.map((column) => csvCell3(row[column])).join(",")
76706
77218
  )
76707
77219
  ].join("\n");
76708
77220
  }
@@ -76795,7 +77307,7 @@ async function createAnalyticsExport(input) {
76795
77307
  `Generated ${(/* @__PURE__ */ new Date()).toISOString()} from the governed ${input.report} report contract.`
76796
77308
  ].join("\n");
76797
77309
  }
76798
- const id = (0, import_node_crypto49.randomUUID)();
77310
+ const id = (0, import_node_crypto50.randomUUID)();
76799
77311
  const inserted = await getAnalyticsPool().query(
76800
77312
  `INSERT INTO analytics_exports(id, site_id, requested_by_user_id, idempotency_key, report, format, filters, content)
76801
77313
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8)
@@ -76828,11 +77340,11 @@ async function createAnalyticsExport(input) {
76828
77340
  }
76829
77341
  return describeArtifact(replay.rows[0]);
76830
77342
  }
76831
- 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;
76832
77344
  var init_analytics_repository = __esm({
76833
77345
  "src/api/analytics-repository.ts"() {
76834
77346
  "use strict";
76835
- import_node_crypto49 = require("crypto");
77347
+ import_node_crypto50 = require("crypto");
76836
77348
  import_pg = require("pg");
76837
77349
  init_session();
76838
77350
  init_analytics_attribution();
@@ -77071,8 +77583,8 @@ function dashboardCallbackUrl() {
77071
77583
  }
77072
77584
  async function createThorbitConnectUrl(userId) {
77073
77585
  if (!bridgeConfigured()) throw new Error("Thorbit X-Ray account bridge is not configured");
77074
- const state = (0, import_node_crypto50.randomBytes)(32).toString("base64url");
77075
- 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");
77076
77588
  await getAnalyticsPool().query(
77077
77589
  `INSERT INTO analytics_thorbit_connect_states(state_hash,user_id,expires_at)
77078
77590
  VALUES ($1,$2,now()+interval '10 minutes')
@@ -77087,9 +77599,9 @@ async function createThorbitConnectUrl(userId) {
77087
77599
  function verifyThorbitAssertion(token6) {
77088
77600
  const [encoded, signature] = token6.split(".");
77089
77601
  if (!encoded || !signature || !bridgeConfigured()) throw new Error("Invalid Thorbit assertion");
77090
- 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();
77091
77603
  const actual = Buffer.from(signature, "base64url");
77092
- 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)) {
77093
77605
  throw new Error("Invalid Thorbit assertion signature");
77094
77606
  }
77095
77607
  const parsed = AssertionSchema.safeParse(
@@ -77101,7 +77613,7 @@ function verifyThorbitAssertion(token6) {
77101
77613
  return parsed.data.entitlement;
77102
77614
  }
77103
77615
  async function consumeThorbitConnectCallback(input) {
77104
- 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");
77105
77617
  const client2 = await getAnalyticsPool().connect();
77106
77618
  try {
77107
77619
  await client2.query("BEGIN");
@@ -77141,11 +77653,11 @@ async function disconnectAnalyticsEntitlement(userId) {
77141
77653
  [userId]
77142
77654
  );
77143
77655
  }
77144
- 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;
77145
77657
  var init_analytics_entitlement = __esm({
77146
77658
  "src/api/analytics-entitlement.ts"() {
77147
77659
  "use strict";
77148
- import_node_crypto50 = require("crypto");
77660
+ import_node_crypto51 = require("crypto");
77149
77661
  import_zod55 = require("zod");
77150
77662
  init_analytics_repository();
77151
77663
  THORBIT_PRODUCT_URL = "https://thorbit.ai";
@@ -77285,14 +77797,14 @@ function renderPublicForm(form, placementUrl) {
77285
77797
  const brand = form.brand;
77286
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>`;
77287
77799
  }
77288
- 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;
77289
77801
  var init_analytics_routes = __esm({
77290
77802
  "src/api/analytics-routes.ts"() {
77291
77803
  "use strict";
77292
77804
  import_hono34 = require("hono");
77293
77805
  import_factory6 = require("hono/factory");
77294
77806
  import_zod56 = require("zod");
77295
- import_node_crypto51 = require("crypto");
77807
+ import_node_crypto52 = require("crypto");
77296
77808
  import_papaparse6 = __toESM(require("papaparse"), 1);
77297
77809
  init_api_auth();
77298
77810
  init_db();
@@ -77623,7 +78135,7 @@ var init_analytics_routes = __esm({
77623
78135
  const lastName = typeof data.last_name === "string" ? data.last_name.trim() : "";
77624
78136
  const fullName = [firstName, lastName].filter(Boolean).join(" ") || (typeof data.name === "string" ? data.name.trim() : "") || email || "Website lead";
77625
78137
  const identitySeed = email || `${fullName}:${Date.now()}`;
77626
- 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);
77627
78139
  const path6 = `Leads/person-${suffix2}`;
77628
78140
  const { key: memoryKey, error: memoryKeyError } = await getOrCreateUserMemoryKey(user);
77629
78141
  const existing = memoryKey ? await memoryCall("getTool", { vault: "People", path: path6 }, memoryKey) : { ok: false, error: memoryKeyError || "memory_credential_unavailable" };
@@ -77722,9 +78234,9 @@ Submitted the published form.
77722
78234
  occurredAt: (/* @__PURE__ */ new Date()).toISOString()
77723
78235
  });
77724
78236
  const match = {
77725
- ...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") } : {},
77726
78238
  ...phone ? {
77727
- 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")
77728
78240
  } : {}
77729
78241
  };
77730
78242
  const activationQueued = await queueAnalyticsActivation({
@@ -78152,7 +78664,7 @@ Submitted the published form.
78152
78664
  rejectedCount += 1;
78153
78665
  continue;
78154
78666
  }
78155
- const digest2 = (0, import_node_crypto51.createHash)("sha256").update(
78667
+ const digest2 = (0, import_node_crypto52.createHash)("sha256").update(
78156
78668
  `${parsed.data.sourceSystem}:${externalId || email || phone || `${fullName}:${index}`}`
78157
78669
  ).digest("hex").slice(0, 16);
78158
78670
  const path6 = `Leads/person-${digest2}`;
@@ -78610,13 +79122,13 @@ var init_analytics_delivery = __esm({
78610
79122
 
78611
79123
  // src/api/scheduled-artifact-owner.ts
78612
79124
  function scheduledArtifactOwnerIdForApiKey(apiKey) {
78613
- 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);
78614
79126
  }
78615
- var import_node_crypto52;
79127
+ var import_node_crypto53;
78616
79128
  var init_scheduled_artifact_owner = __esm({
78617
79129
  "src/api/scheduled-artifact-owner.ts"() {
78618
79130
  "use strict";
78619
- import_node_crypto52 = require("crypto");
79131
+ import_node_crypto53 = require("crypto");
78620
79132
  }
78621
79133
  });
78622
79134
 
@@ -78673,7 +79185,7 @@ async function ensureScheduledRunViewLinksSchema() {
78673
79185
  schemaReady2 = true;
78674
79186
  }
78675
79187
  function tokenHash2(token6) {
78676
- return (0, import_node_crypto53.createHash)("sha256").update(token6).digest("hex");
79188
+ return (0, import_node_crypto54.createHash)("sha256").update(token6).digest("hex");
78677
79189
  }
78678
79190
  function mapRow(row) {
78679
79191
  return {
@@ -78693,9 +79205,9 @@ function mapRow(row) {
78693
79205
  async function createScheduledRunViewLink(input) {
78694
79206
  await ensureScheduledRunViewLinksSchema();
78695
79207
  const now = input.now ?? /* @__PURE__ */ new Date();
78696
- const token6 = (0, import_node_crypto53.randomBytes)(32).toString("base64url");
79208
+ const token6 = (0, import_node_crypto54.randomBytes)(32).toString("base64url");
78697
79209
  const record = {
78698
- shareId: (0, import_node_crypto53.randomUUID)(),
79210
+ shareId: (0, import_node_crypto54.randomUUID)(),
78699
79211
  ownerId: input.ownerId,
78700
79212
  runId: input.runId,
78701
79213
  artifactId: input.artifactId,
@@ -78758,11 +79270,11 @@ async function revokeScheduledRunViewLink(ownerId2, runId, shareId, now = /* @__
78758
79270
  });
78759
79271
  return result.rowsAffected > 0;
78760
79272
  }
78761
- var import_node_crypto53, schemaReady2;
79273
+ var import_node_crypto54, schemaReady2;
78762
79274
  var init_scheduled_run_view_links = __esm({
78763
79275
  "src/api/scheduled-run-view-links.ts"() {
78764
79276
  "use strict";
78765
- import_node_crypto53 = require("crypto");
79277
+ import_node_crypto54 = require("crypto");
78766
79278
  init_db();
78767
79279
  schemaReady2 = false;
78768
79280
  }
@@ -78828,15 +79340,15 @@ ${section(flags.finalCta && Boolean(finalCta), `<section class="section cta" id=
78828
79340
  html,
78829
79341
  filename: `${input.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "personal-authority"}.html`,
78830
79342
  bytes,
78831
- sha256: (0, import_node_crypto54.createHash)("sha256").update(html).digest("hex"),
79343
+ sha256: (0, import_node_crypto55.createHash)("sha256").update(html).digest("hex"),
78832
79344
  generatedAt: generatedAtIso
78833
79345
  };
78834
79346
  }
78835
- var import_node_crypto54;
79347
+ var import_node_crypto55;
78836
79348
  var init_render2 = __esm({
78837
79349
  "src/personal-authority/render.ts"() {
78838
79350
  "use strict";
78839
- import_node_crypto54 = require("crypto");
79351
+ import_node_crypto55 = require("crypto");
78840
79352
  }
78841
79353
  });
78842
79354
 
@@ -78939,15 +79451,15 @@ ${section2(flags.finalCta && Boolean(finalCta), `<section class="section cta" id
78939
79451
  html,
78940
79452
  filename: `${input.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "personal-authority"}.html`,
78941
79453
  bytes,
78942
- sha256: (0, import_node_crypto55.createHash)("sha256").update(html).digest("hex"),
79454
+ sha256: (0, import_node_crypto56.createHash)("sha256").update(html).digest("hex"),
78943
79455
  generatedAt: generatedAtIso
78944
79456
  };
78945
79457
  }
78946
- var import_node_crypto55, FONT_STACKS;
79458
+ var import_node_crypto56, FONT_STACKS;
78947
79459
  var init_render_v2 = __esm({
78948
79460
  "src/personal-authority/render-v2.ts"() {
78949
79461
  "use strict";
78950
- import_node_crypto55 = require("crypto");
79462
+ import_node_crypto56 = require("crypto");
78951
79463
  init_contracts();
78952
79464
  FONT_STACKS = Object.freeze({
78953
79465
  "editorial-serif": "Iowan Old Style, Baskerville, Times New Roman, serif",
@@ -79063,15 +79575,15 @@ body{overflow-x:hidden}.lead-story__body,.lead-support__item,.story-card__body{m
79063
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}}
79064
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>`;
79065
79577
  const bytes = Buffer.byteLength(html);
79066
- 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");
79067
79579
  const filename2 = `${brand.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "newsroom"}-news-site.html`;
79068
79580
  return { html, filename: filename2, bytes, sha256: sha2565, generatedAt: generatedAtIso };
79069
79581
  }
79070
- var import_node_crypto56;
79582
+ var import_node_crypto57;
79071
79583
  var init_render3 = __esm({
79072
79584
  "src/newsroom-publisher/render.ts"() {
79073
79585
  "use strict";
79074
- import_node_crypto56 = require("crypto");
79586
+ import_node_crypto57 = require("crypto");
79075
79587
  }
79076
79588
  });
79077
79589
 
@@ -79163,7 +79675,7 @@ function renderBlogArticleV1(input, config, generatedAt) {
79163
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}}
79164
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>`;
79165
79677
  const bytes = Buffer.byteLength(html);
79166
- 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");
79167
79679
  return {
79168
79680
  html,
79169
79681
  filename: "blog-article.html",
@@ -79173,11 +79685,11 @@ function renderBlogArticleV1(input, config, generatedAt) {
79173
79685
  generatedAt: generatedAtIso
79174
79686
  };
79175
79687
  }
79176
- var import_node_crypto57;
79688
+ var import_node_crypto58;
79177
79689
  var init_render4 = __esm({
79178
79690
  "src/blog-article/render.ts"() {
79179
79691
  "use strict";
79180
- import_node_crypto57 = require("crypto");
79692
+ import_node_crypto58 = require("crypto");
79181
79693
  }
79182
79694
  });
79183
79695
 
@@ -79508,7 +80020,7 @@ function policy6() {
79508
80020
  };
79509
80021
  }
79510
80022
  function runStorageSegment(runId) {
79511
- 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)}`;
79512
80024
  }
79513
80025
  async function createScheduledRunArtifact(args) {
79514
80026
  if (args.rendered.bytes > SCHEDULED_RUN_ARTIFACT_MAX_BYTES) {
@@ -79549,12 +80061,12 @@ async function readScheduledRunArtifact(args) {
79549
80061
  if (!window2 || window2.nextOffset !== null || window2.totalBytes > SCHEDULED_RUN_ARTIFACT_MAX_BYTES) return null;
79550
80062
  return window2.text;
79551
80063
  }
79552
- 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;
79553
80065
  var init_scheduled_run_artifact_store = __esm({
79554
80066
  "src/scheduled-artifacts/scheduled-run-artifact-store.ts"() {
79555
80067
  "use strict";
79556
80068
  init_private_artifacts();
79557
- import_node_crypto58 = require("crypto");
80069
+ import_node_crypto59 = require("crypto");
79558
80070
  SCHEDULED_RUN_ARTIFACT_PREFIX = "scheduled-run-artifacts/";
79559
80071
  SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS = 15 * 60 * 1e3;
79560
80072
  SCHEDULED_RUN_ARTIFACT_MAX_BYTES = 2e6;
@@ -79772,7 +80284,7 @@ async function reconcileDiscoveredNangoConnections(identity, discovered) {
79772
80284
  updated_at = excluded.updated_at
79773
80285
  `,
79774
80286
  args: [
79775
- (0, import_node_crypto59.randomUUID)(),
80287
+ (0, import_node_crypto60.randomUUID)(),
79776
80288
  userId,
79777
80289
  connection.providerConfigKey,
79778
80290
  connection.provider,
@@ -79843,7 +80355,7 @@ async function recordServiceConnectionHealth(args) {
79843
80355
  });
79844
80356
  await getDb().execute({
79845
80357
  sql: `INSERT INTO service_connection_health_events (id, connection_id, operational_status, failure_code, retryable, evidence_source) VALUES (?, ?, ?, ?, ?, ?)`,
79846
- 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]
79847
80359
  });
79848
80360
  }
79849
80361
  async function setServiceConnectionActions(identity, connectionId, enabled) {
@@ -79883,7 +80395,7 @@ async function claimServiceConnectionAction(args) {
79883
80395
  if (!connection) throw new Error("service_connection_not_found");
79884
80396
  const inserted = await getDb().execute({
79885
80397
  sql: `INSERT OR IGNORE INTO service_connection_action_audit (id, connection_id, user_id, tool, request_id, status, request_digest) VALUES (?, ?, ?, ?, ?, 'started', ?)`,
79886
- 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]
79887
80399
  });
79888
80400
  if (Number(inserted.rowsAffected ?? 0) === 1) return { claimed: true };
79889
80401
  const existing = await getDb().execute({
@@ -79908,11 +80420,11 @@ async function claimServiceConnectionAction(args) {
79908
80420
  ...result !== void 0 ? { result } : {}
79909
80421
  };
79910
80422
  }
79911
- var import_node_crypto59, schemaReady3, schemaDb6;
80423
+ var import_node_crypto60, schemaReady3, schemaDb6;
79912
80424
  var init_service_connections = __esm({
79913
80425
  "src/api/service-connections.ts"() {
79914
80426
  "use strict";
79915
- import_node_crypto59 = require("crypto");
80427
+ import_node_crypto60 = require("crypto");
79916
80428
  init_db();
79917
80429
  schemaReady3 = null;
79918
80430
  schemaDb6 = null;
@@ -79926,8 +80438,8 @@ function signingSecret() {
79926
80438
  return secret2;
79927
80439
  }
79928
80440
  function schedulerIntegrationSignature(args) {
79929
- const bodyHash = (0, import_node_crypto60.createHash)("sha256").update(args.body).digest("hex");
79930
- 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()}
79931
80443
  ${args.path}
79932
80444
  ${args.timestamp}
79933
80445
  ${args.nonce}
@@ -79974,17 +80486,17 @@ async function verifySchedulerIntegrationRequest(request, rawBody) {
79974
80486
  });
79975
80487
  const suppliedBytes = Buffer.from(signature, "hex");
79976
80488
  const expectedBytes = Buffer.from(expected, "hex");
79977
- 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)) {
79978
80490
  throw new SchedulerIntegrationAuthError("invalid_signature");
79979
80491
  }
79980
80492
  await claimNonce(nonce, timestampMs);
79981
80493
  return { requestId };
79982
80494
  }
79983
- var import_node_crypto60, MAX_CLOCK_SKEW_MS, SchedulerIntegrationAuthError;
80495
+ var import_node_crypto61, MAX_CLOCK_SKEW_MS, SchedulerIntegrationAuthError;
79984
80496
  var init_scheduler_integration_auth = __esm({
79985
80497
  "src/api/scheduler-integration-auth.ts"() {
79986
80498
  "use strict";
79987
- import_node_crypto60 = require("crypto");
80499
+ import_node_crypto61 = require("crypto");
79988
80500
  init_db();
79989
80501
  init_service_connections();
79990
80502
  MAX_CLOCK_SKEW_MS = 5 * 60 * 1e3;
@@ -80146,7 +80658,7 @@ function requestedSiteMaxPages(maxPages) {
80146
80658
  return Math.min(ABSOLUTE_SITE_MAX_PAGES, Math.max(1, maxPages ?? DEFAULT_SITE_MAX_PAGES));
80147
80659
  }
80148
80660
  function shouldRunSiteExtractInBackground(input) {
80149
- 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;
80150
80662
  }
80151
80663
  var MAX_SYNCHRONOUS_SITE_PAGES, DEFAULT_SITE_MAX_PAGES, ABSOLUTE_SITE_MAX_PAGES;
80152
80664
  var init_site_extract_policy = __esm({
@@ -80292,7 +80804,7 @@ async function fetchMedia(rawUrl, expectedType) {
80292
80804
  finalUrl: checked.parsed.href,
80293
80805
  width: dimensions?.width ?? null,
80294
80806
  height: dimensions?.height ?? null,
80295
- sha256: (0, import_node_crypto61.createHash)("sha256").update(bytes).digest("hex")
80807
+ sha256: (0, import_node_crypto62.createHash)("sha256").update(bytes).digest("hex")
80296
80808
  };
80297
80809
  }
80298
80810
  throw new Error("media_redirect_rejected");
@@ -80406,7 +80918,7 @@ async function packagePageMedia(args) {
80406
80918
  files.push({ path: "summary.json", content: Buffer.from(JSON.stringify(summary, null, 2)) });
80407
80919
  files.push({ path: "media.jsonl", content: Buffer.from(cleanAssets.map((asset) => JSON.stringify(asset)).join("\n") + "\n") });
80408
80920
  const archive = await zipBuffer2(files);
80409
- const id = (0, import_node_crypto61.randomBytes)(6).toString("hex");
80921
+ const id = (0, import_node_crypto62.randomBytes)(6).toString("hex");
80410
80922
  const pointer = await createPrivateArtifact({
80411
80923
  policy: policy7(),
80412
80924
  ownerId: args.ownerId,
@@ -80419,11 +80931,11 @@ async function packagePageMedia(args) {
80419
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);
80420
80932
  return { media: args.media, artifact: { ...pointer, localPath } };
80421
80933
  }
80422
- 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;
80423
80935
  var init_page_media_artifacts = __esm({
80424
80936
  "src/api/page-media-artifacts.ts"() {
80425
80937
  "use strict";
80426
- import_node_crypto61 = require("crypto");
80938
+ import_node_crypto62 = require("crypto");
80427
80939
  import_node_os15 = require("os");
80428
80940
  import_node_path22 = require("path");
80429
80941
  import_p_limit7 = __toESM(require("p-limit"), 1);
@@ -80532,7 +81044,7 @@ var init_site_extract_reconciliation = __esm({
80532
81044
 
80533
81045
  // src/api/page-diff.ts
80534
81046
  function sha256Hex(value) {
80535
- return (0, import_node_crypto62.createHash)("sha256").update(value).digest("hex");
81047
+ return (0, import_node_crypto63.createHash)("sha256").update(value).digest("hex");
80536
81048
  }
80537
81049
  function truncateForStorage(value, maxChars = MAX_SNAPSHOT_CONTENT_CHARS) {
80538
81050
  if (value.length <= maxChars) return { value, truncated: false };
@@ -80589,11 +81101,11 @@ function diffPageContent(oldContent, newContent) {
80589
81101
  totalChangedLineCount
80590
81102
  };
80591
81103
  }
80592
- 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;
80593
81105
  var init_page_diff = __esm({
80594
81106
  "src/api/page-diff.ts"() {
80595
81107
  "use strict";
80596
- import_node_crypto62 = require("crypto");
81108
+ import_node_crypto63 = require("crypto");
80597
81109
  import_diff = require("diff");
80598
81110
  MAX_SNAPSHOT_CONTENT_CHARS = 25e4;
80599
81111
  MAX_DIFF_HUNKS = 200;
@@ -80667,7 +81179,7 @@ var init_scrape_vault_sink = __esm({
80667
81179
 
80668
81180
  // src/api/scrape-image-sink.ts
80669
81181
  function idempotencyKey3(userId, vault, input) {
80670
- 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")}`;
80671
81183
  }
80672
81184
  async function persistScrapeImagesToMemory(user, inputs, vault) {
80673
81185
  const selected = inputs.slice(0, MAX_IMAGES_PER_SCRAPE);
@@ -80709,11 +81221,11 @@ async function persistScrapeImagesToMemory(user, inputs, vault) {
80709
81221
  assets
80710
81222
  };
80711
81223
  }
80712
- var import_node_crypto63, MAX_IMAGES_PER_SCRAPE;
81224
+ var import_node_crypto64, MAX_IMAGES_PER_SCRAPE;
80713
81225
  var init_scrape_image_sink = __esm({
80714
81226
  "src/api/scrape-image-sink.ts"() {
80715
81227
  "use strict";
80716
- import_node_crypto63 = require("crypto");
81228
+ import_node_crypto64 = require("crypto");
80717
81229
  init_memory();
80718
81230
  MAX_IMAGES_PER_SCRAPE = 25;
80719
81231
  }
@@ -81166,7 +81678,7 @@ function canonicalJson(value) {
81166
81678
  return JSON.stringify(value);
81167
81679
  }
81168
81680
  function sha2563(value) {
81169
- return (0, import_node_crypto64.createHash)("sha256").update(value).digest("hex");
81681
+ return (0, import_node_crypto65.createHash)("sha256").update(value).digest("hex");
81170
81682
  }
81171
81683
  function sensitiveKey(key) {
81172
81684
  const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
@@ -81381,11 +81893,11 @@ async function importServiceConnectionToMemory(identity, input, dependencies) {
81381
81893
  ...!searchReady ? { warning: "The snapshot was stored, but no search chunks were indexed yet." } : {}
81382
81894
  };
81383
81895
  }
81384
- 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;
81385
81897
  var init_connection_memory_import = __esm({
81386
81898
  "src/api/connection-memory-import.ts"() {
81387
81899
  "use strict";
81388
- import_node_crypto64 = require("crypto");
81900
+ import_node_crypto65 = require("crypto");
81389
81901
  init_slugify();
81390
81902
  CONNECTION_MEMORY_IMPORT_MAX_ARGS_BYTES = 64 * 1024;
81391
81903
  CONNECTION_MEMORY_IMPORT_MAX_RESULT_BYTES = 1e6;
@@ -81469,7 +81981,7 @@ var init_scrape_blob_cleanup = __esm({
81469
81981
 
81470
81982
  // src/api/site-export-reader.ts
81471
81983
  function sha2564(value) {
81472
- return (0, import_node_crypto65.createHash)("sha256").update(value).digest("hex");
81984
+ return (0, import_node_crypto66.createHash)("sha256").update(value).digest("hex");
81473
81985
  }
81474
81986
  function publicPageRecord(page) {
81475
81987
  const { bodyMarkdown: _body, contentRef: _contentRef, discoveryLinks: _discovery, ...metadata } = page;
@@ -81577,11 +82089,11 @@ async function readOwnedSiteExportImage(input) {
81577
82089
  if (!bytes || artifact.sha256 && sha2564(bytes) !== artifact.sha256) return null;
81578
82090
  return { bytes, artifact };
81579
82091
  }
81580
- var import_node_crypto65, SiteExportFormatUnavailableError;
82092
+ var import_node_crypto66, SiteExportFormatUnavailableError;
81581
82093
  var init_site_export_reader = __esm({
81582
82094
  "src/api/site-export-reader.ts"() {
81583
82095
  "use strict";
81584
- import_node_crypto65 = require("crypto");
82096
+ import_node_crypto66 = require("crypto");
81585
82097
  init_site_extract_repository();
81586
82098
  init_site_extract_content_store();
81587
82099
  init_site_extract_artifacts();
@@ -81732,7 +82244,7 @@ function finitePositive(value, fallback) {
81732
82244
  return Number.isFinite(value) && value > 0 ? value : fallback;
81733
82245
  }
81734
82246
  async function collectConnectedDataExport(args) {
81735
- const exportId = (0, import_node_crypto66.randomUUID)();
82247
+ const exportId = (0, import_node_crypto67.randomUUID)();
81736
82248
  const now = args.now ?? Date.now;
81737
82249
  const startedAt = now();
81738
82250
  const budgetMs = finitePositive(CONNECTED_DATA_EXPORT_BUDGET_MS, 24e4);
@@ -81846,11 +82358,11 @@ ${lines.length ? `${lines.join("\n")}
81846
82358
  untrustedContent: true
81847
82359
  };
81848
82360
  }
81849
- 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;
81850
82362
  var init_connected_data_export = __esm({
81851
82363
  "src/api/connected-data-export.ts"() {
81852
82364
  "use strict";
81853
- import_node_crypto66 = require("crypto");
82365
+ import_node_crypto67 = require("crypto");
81854
82366
  CONNECTED_DATA_INLINE_BUDGET_BYTES = Number(
81855
82367
  process.env.MCP_SCRAPER_CONNECTED_DATA_INLINE_BUDGET_BYTES ?? 5e4
81856
82368
  );
@@ -81964,7 +82476,7 @@ async function exportSearchConsoleTableData(args) {
81964
82476
  offset += rows.length;
81965
82477
  if (stoppedForBytes || rows.length < limit || offset >= matchedRows) break;
81966
82478
  }
81967
- const exportId = (0, import_node_crypto67.randomUUID)();
82479
+ const exportId = (0, import_node_crypto68.randomUUID)();
81968
82480
  const artifact = await args.writeArtifact({
81969
82481
  ownerId: args.ownerId,
81970
82482
  exportId,
@@ -81986,11 +82498,11 @@ async function exportSearchConsoleTableData(args) {
81986
82498
  warnings
81987
82499
  };
81988
82500
  }
81989
- 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;
81990
82502
  var init_search_console_table_export = __esm({
81991
82503
  "src/api/search-console-table-export.ts"() {
81992
82504
  "use strict";
81993
- import_node_crypto67 = require("crypto");
82505
+ import_node_crypto68 = require("crypto");
81994
82506
  SEARCH_CONSOLE_TABLE_EXPORT_MAX_ROWS = 5e4;
81995
82507
  SEARCH_CONSOLE_TABLE_EXPORT_PAGE_SIZE = 2e3;
81996
82508
  SEARCH_CONSOLE_TABLE_EXPORT_MAX_BYTES = 50 * 1024 * 1024;
@@ -83169,7 +83681,7 @@ async function listNangoToolsDirect(identity, connectionId) {
83169
83681
  });
83170
83682
  const readTools = [...policies.values()].filter((policy8) => policy8.classification === "read").map((policy8) => policy8.name);
83171
83683
  const actionTools = [...policies.values()].filter((policy8) => policy8.classification === "action").map((policy8) => policy8.name);
83172
- 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");
83173
83685
  await updateServiceConnectionTools(connection.id, readTools, actionTools, revision);
83174
83686
  const refreshed = await getOwnedServiceConnection(identity, connection.id);
83175
83687
  return { connection: refreshed ?? { ...connection, readTools, actionTools, toolRevision: revision }, tools };
@@ -83196,8 +83708,8 @@ async function callNangoToolDirect(args) {
83196
83708
  identity: args.identity,
83197
83709
  ratePolicyVersion: CONNECTED_USAGE_RATE_POLICY_VERSION
83198
83710
  });
83199
- const requestId = args.requestId?.trim() || (0, import_node_crypto68.randomUUID)();
83200
- 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")}`;
83201
83713
  const startedAt = /* @__PURE__ */ new Date();
83202
83714
  const started = performance.now();
83203
83715
  let result;
@@ -83222,7 +83734,7 @@ async function callNangoToolDirect(args) {
83222
83734
  toolName: args.tool,
83223
83735
  operationKind: args.operationKind ?? args.classification,
83224
83736
  outcome: providerError ? "error" : "partial",
83225
- 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"),
83226
83738
  startedAt: startedAt.toISOString(),
83227
83739
  completedAt: completedAt.toISOString()
83228
83740
  }
@@ -83264,15 +83776,15 @@ async function describeNangoToolDirect(identity, connectionId, toolName) {
83264
83776
  providerContractHash: MAIN_INTEGRATION_CONTRACT_HASH,
83265
83777
  protocolVersion: null,
83266
83778
  schemaSource: "live_tools_list",
83267
- 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"),
83268
83780
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
83269
83781
  };
83270
83782
  }
83271
- 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;
83272
83784
  var init_main_nango_transport = __esm({
83273
83785
  "src/api/main-nango-transport.ts"() {
83274
83786
  "use strict";
83275
- import_node_crypto68 = require("crypto");
83787
+ import_node_crypto69 = require("crypto");
83276
83788
  import_client15 = require("@modelcontextprotocol/client");
83277
83789
  init_service_connections();
83278
83790
  init_connected_usage_billing();
@@ -83896,8 +84408,8 @@ async function setScheduleConnectionActionsEnabled(identity, connectionId, enabl
83896
84408
  return data.connection.actionsEnabled === true;
83897
84409
  }
83898
84410
  async function callScheduleConnectionAction(identity, connectionId, input, tool, idempotencyKey4) {
83899
- 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")}`;
83900
- 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");
83901
84413
  if (mainOwnsIntegrations()) {
83902
84414
  const selectedTool = tool?.trim();
83903
84415
  if (!selectedTool) throw new NangoControlError("An action tool is required.", 400, "invalid_request", false);
@@ -84048,7 +84560,7 @@ function canonicalJson2(value) {
84048
84560
  return JSON.stringify(value);
84049
84561
  }
84050
84562
  function projectedToolSchemaHash(tool) {
84051
- 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");
84052
84564
  }
84053
84565
  async function describeNangoTool(identity, connectionId, tool, fresh) {
84054
84566
  if (mainOwnsIntegrations()) {
@@ -84325,11 +84837,11 @@ async function callMainOwnedExportPage(identity, input) {
84325
84837
  untrustedContent: true
84326
84838
  };
84327
84839
  }
84328
- 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;
84329
84841
  var init_nango_control = __esm({
84330
84842
  "src/api/nango-control.ts"() {
84331
84843
  "use strict";
84332
- import_node_crypto69 = require("crypto");
84844
+ import_node_crypto70 = require("crypto");
84333
84845
  init_connected_data_export();
84334
84846
  init_slack_connected_data_export();
84335
84847
  init_main_nango_transport();
@@ -84685,7 +85197,7 @@ async function callResendRead(identity, connectionId, tool, args) {
84685
85197
  return isRecord5(data) ? data.result ?? data : data;
84686
85198
  }
84687
85199
  async function callResendAction(identity, connectionId, tool, input, idempotencyKey4) {
84688
- 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")}`;
84689
85201
  const body = await controlRequest2("/api/internal/resend/actions/call", {
84690
85202
  method: "POST",
84691
85203
  headers: { "x-request-id": requestId },
@@ -84750,12 +85262,12 @@ async function callResendExportPage(identity, input) {
84750
85262
  untrustedContent: true
84751
85263
  };
84752
85264
  }
84753
- 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;
84754
85266
  var init_resend_control = __esm({
84755
85267
  "src/api/resend-control.ts"() {
84756
85268
  "use strict";
84757
85269
  init_connected_data_export();
84758
- import_node_crypto70 = require("crypto");
85270
+ import_node_crypto71 = require("crypto");
84759
85271
  DEFAULT_CONNECTION_CONTROL_URL = "https://mcp-scraper-scheduler.vercel.app";
84760
85272
  RESEND_PROVIDER_CONFIG_KEY = "resend";
84761
85273
  RESEND_LOGO_URL = "https://cdn.resend.com/brand/resend-icon-black.svg";
@@ -85362,7 +85874,7 @@ function settleWithinTickBudget(label, unfinished, work, onDeadlineOrError) {
85362
85874
  );
85363
85875
  });
85364
85876
  }
85365
- 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;
85366
85878
  var init_server = __esm({
85367
85879
  "src/api/server.ts"() {
85368
85880
  "use strict";
@@ -85375,7 +85887,7 @@ var init_server = __esm({
85375
85887
  init_og();
85376
85888
  import_resend3 = require("resend");
85377
85889
  init_url_utils();
85378
- import_node_crypto71 = require("crypto");
85890
+ import_node_crypto72 = require("crypto");
85379
85891
  init_kpo_extractor();
85380
85892
  init_screenshot();
85381
85893
  init_media_extractor();
@@ -86936,7 +87448,7 @@ var init_server = __esm({
86936
87448
  if (!harvestOk) return c.json(insufficientBalanceResponse(harvestBal, harvestCost), 402);
86937
87449
  jobId2 = await createJob(user.id, options.query, { ...options, billingHoldMc: harvestCost }, body.callback_url);
86938
87450
  } else {
86939
- jobId2 = (0, import_node_crypto71.randomUUID)();
87451
+ jobId2 = (0, import_node_crypto72.randomUUID)();
86940
87452
  const billingDebitKey = `paa-harvest:${jobId2}:hold`;
86941
87453
  const description = `PAA harvest: ${options.query}`.slice(0, 500);
86942
87454
  const hold = await debitMcIdempotent(
@@ -87771,6 +88283,13 @@ var init_server = __esm({
87771
88283
  const bodyResult = ExtractSiteBodySchema.safeParse(raw);
87772
88284
  if (!bodyResult.success) return c.json({ error: bodyResult.error.issues[0]?.message ?? "Invalid request" }, 400);
87773
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
+ }
87774
88293
  if (body.preserveMedia !== void 0 && body.downloadImages !== void 0 && body.preserveMedia !== body.downloadImages) {
87775
88294
  return c.json({ error: "preserveMedia conflicts with deprecated downloadImages." }, 400);
87776
88295
  }
@@ -87824,7 +88343,12 @@ var init_server = __esm({
87824
88343
  maxPages: requestedMaxPages,
87825
88344
  rotateProxyEvery: body.rotateProxyEvery ?? 10,
87826
88345
  formats: [...body.formats ?? []].sort(),
87827
- 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
87828
88352
  }));
87829
88353
  const prepared = await prepareSiteExtractStart({
87830
88354
  jobId: jobId2,
@@ -87838,6 +88362,11 @@ var init_server = __esm({
87838
88362
  urlsPerBrowser: body.rotateProxyEvery ?? 10,
87839
88363
  formats: body.formats,
87840
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,
87841
88370
  debitKey: `site-extract:${user.id}:${jobId2}:hold`,
87842
88371
  waybackReplay: waybackReplay && !body.wayback ? {
87843
88372
  timestamp: waybackReplay.timestamp,
@@ -87931,8 +88460,10 @@ var init_server = __esm({
87931
88460
  startUrl: crawlStartUrl,
87932
88461
  maxPages: siteMaxPages,
87933
88462
  seedUrls: waybackCaptures?.map((capture) => capture.rawReplayUrl),
87934
- 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,
87935
88464
  formats: body.formats,
88465
+ forceBrowserRender: body.renderJavaScript || body.captureRenderedDom || body.semanticSimilarity,
88466
+ captureRenderedDom: body.captureRenderedDom,
87936
88467
  ...rotateProxies ? {
87937
88468
  rotateProxyEvery: body.rotateProxyEvery ?? 30,
87938
88469
  parallelism: concurrencyLimitForUser(user)