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