mcp-scraper 0.3.17 → 0.3.19
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/README.md +26 -58
- package/dist/bin/api-server.cjs +346 -60
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +2 -2
- package/dist/bin/mcp-scraper-cli.cjs +4 -4
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +5 -5
- package/dist/bin/mcp-scraper-cli.js.map +1 -1
- package/dist/bin/mcp-scraper-install.cjs +12 -12
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +3 -3
- package/dist/bin/mcp-scraper-install.js.map +1 -1
- package/dist/bin/mcp-stdio-server.cjs +2441 -785
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +1377 -9
- package/dist/bin/mcp-stdio-server.js.map +1 -1
- package/dist/bin/paa-harvest.js +2 -3
- package/dist/bin/paa-harvest.js.map +1 -1
- package/dist/{chunk-N33YEY6W.js → chunk-A46NKAAM.js} +210 -10
- package/dist/{chunk-N33YEY6W.js.map → chunk-A46NKAAM.js.map} +1 -1
- package/dist/chunk-AUKOY2IV.js +7 -0
- package/dist/chunk-AUKOY2IV.js.map +1 -0
- package/dist/{chunk-ACIUCZ27.js → chunk-DE7WP4UU.js} +11 -11
- package/dist/chunk-DE7WP4UU.js.map +1 -0
- package/dist/{chunk-4OPKIDON.js → chunk-IGOEAKC7.js} +4 -6
- package/dist/{chunk-4OPKIDON.js.map → chunk-IGOEAKC7.js.map} +1 -1
- package/dist/{chunk-ZAUMSBV3.js → chunk-T337IGVE.js} +8 -8
- package/dist/chunk-T337IGVE.js.map +1 -0
- package/dist/{chunk-M2S27J6Z.js → chunk-ZAP7MYVO.js} +26 -1
- package/dist/chunk-ZAP7MYVO.js.map +1 -0
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/{server-TKWY47JA.js → server-EYNMXWUJ.js} +379 -167
- package/dist/server-EYNMXWUJ.js.map +1 -0
- package/dist/{worker-2WVKKCC7.js → worker-YZ2ZJE4F.js} +9 -10
- package/dist/{worker-2WVKKCC7.js.map → worker-YZ2ZJE4F.js.map} +1 -1
- package/docs/mcp-tool-manifest.generated.json +28 -27
- package/package.json +3 -3
- package/dist/bin/browser-agent-stdio-server.cjs +0 -1523
- package/dist/bin/browser-agent-stdio-server.cjs.map +0 -1
- package/dist/bin/browser-agent-stdio-server.d.cts +0 -1
- package/dist/bin/browser-agent-stdio-server.d.ts +0 -1
- package/dist/bin/browser-agent-stdio-server.js +0 -43
- package/dist/bin/browser-agent-stdio-server.js.map +0 -1
- package/dist/bin/mcp-scraper-combined-stdio-server.cjs +0 -5718
- package/dist/bin/mcp-scraper-combined-stdio-server.cjs.map +0 -1
- package/dist/bin/mcp-scraper-combined-stdio-server.d.cts +0 -1
- package/dist/bin/mcp-scraper-combined-stdio-server.d.ts +0 -1
- package/dist/bin/mcp-scraper-combined-stdio-server.js +0 -70
- package/dist/bin/mcp-scraper-combined-stdio-server.js.map +0 -1
- package/dist/chunk-3YGKXXUG.js +0 -133
- package/dist/chunk-3YGKXXUG.js.map +0 -1
- package/dist/chunk-ACIUCZ27.js.map +0 -1
- package/dist/chunk-M2S27J6Z.js.map +0 -1
- package/dist/chunk-PVWWY5NV.js +0 -7
- package/dist/chunk-PVWWY5NV.js.map +0 -1
- package/dist/chunk-UFD7N36R.js +0 -1332
- package/dist/chunk-UFD7N36R.js.map +0 -1
- package/dist/chunk-WN7PBKMV.js +0 -28
- package/dist/chunk-WN7PBKMV.js.map +0 -1
- package/dist/chunk-ZAUMSBV3.js.map +0 -1
- package/dist/server-TKWY47JA.js.map +0 -1
package/dist/bin/api-server.cjs
CHANGED
|
@@ -117,18 +117,26 @@ var init_errors = __esm({
|
|
|
117
117
|
});
|
|
118
118
|
|
|
119
119
|
// src/api/outbound-sanitize.ts
|
|
120
|
+
function redactVendorUrls(value) {
|
|
121
|
+
return value.replace(VENDOR_URL_RE, "[browser-service]");
|
|
122
|
+
}
|
|
120
123
|
function sanitizeOutboundDiagnostics(value, parentKey = "") {
|
|
121
124
|
if (typeof value === "string") {
|
|
122
|
-
|
|
123
|
-
|
|
125
|
+
let out = redactVendorUrls(value);
|
|
126
|
+
if (SANITIZED_VALUE_KEYS.test(parentKey) && /kernel/i.test(out)) {
|
|
127
|
+
out = sanitizeVendorName(out);
|
|
124
128
|
}
|
|
125
|
-
return
|
|
129
|
+
return out;
|
|
126
130
|
}
|
|
127
131
|
if (Array.isArray(value)) return value.map((v) => sanitizeOutboundDiagnostics(v, parentKey));
|
|
128
132
|
if (value !== null && typeof value === "object") {
|
|
129
133
|
const out = {};
|
|
130
134
|
for (const [key, val] of Object.entries(value)) {
|
|
131
135
|
const renamed = KEY_RENAMES[key] ?? key;
|
|
136
|
+
if (VENDOR_URL_KEYS.has(key)) {
|
|
137
|
+
out[renamed] = null;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
132
140
|
out[renamed] = sanitizeOutboundDiagnostics(val, key);
|
|
133
141
|
}
|
|
134
142
|
return out;
|
|
@@ -149,7 +157,7 @@ function sanitizeHarvestResult(result) {
|
|
|
149
157
|
}
|
|
150
158
|
};
|
|
151
159
|
}
|
|
152
|
-
var KEY_RENAMES, SANITIZED_VALUE_KEYS;
|
|
160
|
+
var KEY_RENAMES, SANITIZED_VALUE_KEYS, VENDOR_URL_KEYS, VENDOR_URL_RE;
|
|
153
161
|
var init_outbound_sanitize = __esm({
|
|
154
162
|
"src/api/outbound-sanitize.ts"() {
|
|
155
163
|
"use strict";
|
|
@@ -167,6 +175,16 @@ var init_outbound_sanitize = __esm({
|
|
|
167
175
|
kernelProxyId: "proxyId"
|
|
168
176
|
};
|
|
169
177
|
SANITIZED_VALUE_KEYS = /error|message/i;
|
|
178
|
+
VENDOR_URL_KEYS = /* @__PURE__ */ new Set([
|
|
179
|
+
"hosted_url",
|
|
180
|
+
"hostedUrl",
|
|
181
|
+
"live_view_url",
|
|
182
|
+
"liveViewUrl",
|
|
183
|
+
"cdp_ws_url",
|
|
184
|
+
"cdpWsUrl",
|
|
185
|
+
"browser_live_view_url"
|
|
186
|
+
]);
|
|
187
|
+
VENDOR_URL_RE = /\b(?:wss?|https?):\/\/[^\s"'<>]*\bonkernel\.com[^\s"'<>]*/gi;
|
|
170
188
|
}
|
|
171
189
|
});
|
|
172
190
|
|
|
@@ -9643,8 +9661,8 @@ var init_rates = __esm({
|
|
|
9643
9661
|
label: "Interactive browser session",
|
|
9644
9662
|
aliases: ["browser_open", "browser agent", "browser_agent", "live browser", "browse", "browser control", "interactive browser"],
|
|
9645
9663
|
credits: mcToCredits(MC_COSTS.browser_minute),
|
|
9646
|
-
unit: "per minute
|
|
9647
|
-
notes: "Metered
|
|
9664
|
+
unit: "per minute the browser is open",
|
|
9665
|
+
notes: "Metered for the full time the browser session is open (active or idle) \u2014 every minute the session stays alive is billed. Close the session to stop the meter; abandoned sessions are auto-closed after a short idle window."
|
|
9648
9666
|
}
|
|
9649
9667
|
];
|
|
9650
9668
|
CONCURRENCY_PRICE_ID = "price_1Ta1NRS8aAcsk3TGwsRnYbix";
|
|
@@ -9812,11 +9830,6 @@ var init_site_extract_repository = __esm({
|
|
|
9812
9830
|
});
|
|
9813
9831
|
|
|
9814
9832
|
// src/inngest/functions/site-extract.ts
|
|
9815
|
-
function chunk(items, size) {
|
|
9816
|
-
const out = [];
|
|
9817
|
-
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
9818
|
-
return out;
|
|
9819
|
-
}
|
|
9820
9833
|
function buildArtifacts(job, pages, branding, imageAudit) {
|
|
9821
9834
|
const { edges, metrics } = buildLinkGraph(pages, job.startUrl);
|
|
9822
9835
|
const issues = computeIssues(pages, metrics);
|
|
@@ -9891,18 +9904,62 @@ var init_site_extract = __esm({
|
|
|
9891
9904
|
const maxPages = Number(job.options.maxPages ?? 1e4);
|
|
9892
9905
|
const concurrency = Number(job.options.concurrency ?? 1);
|
|
9893
9906
|
const urlsPerBrowser = Number(job.options.urlsPerBrowser ?? job.options.rotateProxyEvery ?? 10);
|
|
9894
|
-
const
|
|
9907
|
+
const seed = await step.run("discover", async () => {
|
|
9895
9908
|
const discovered = await discoverUrls(job.startUrl, maxPages, key);
|
|
9896
|
-
|
|
9897
|
-
|
|
9909
|
+
const out = discovered.length ? discovered : [job.startUrl];
|
|
9910
|
+
await setExtractJobTotal(jobId, out.length);
|
|
9911
|
+
return out;
|
|
9898
9912
|
});
|
|
9899
|
-
const
|
|
9900
|
-
|
|
9901
|
-
|
|
9902
|
-
|
|
9913
|
+
const siteHost = (() => {
|
|
9914
|
+
try {
|
|
9915
|
+
return new URL(job.startUrl).hostname.replace(/^www\./, "");
|
|
9916
|
+
} catch {
|
|
9917
|
+
return "";
|
|
9918
|
+
}
|
|
9919
|
+
})();
|
|
9920
|
+
const norm = (u) => {
|
|
9921
|
+
try {
|
|
9922
|
+
const x = new URL(u);
|
|
9923
|
+
x.hash = "";
|
|
9924
|
+
return (x.origin + x.pathname).replace(/\/+$/, "") + x.search;
|
|
9925
|
+
} catch {
|
|
9926
|
+
return u;
|
|
9927
|
+
}
|
|
9928
|
+
};
|
|
9929
|
+
const sameSite = (u) => {
|
|
9930
|
+
try {
|
|
9931
|
+
return new URL(u).hostname.replace(/^www\./, "") === siteHost;
|
|
9932
|
+
} catch {
|
|
9933
|
+
return false;
|
|
9934
|
+
}
|
|
9935
|
+
};
|
|
9936
|
+
const queue = [...seed];
|
|
9937
|
+
const seen = new Set(seed.map(norm));
|
|
9938
|
+
let crawled = 0;
|
|
9939
|
+
const MAX_BATCHES = Math.ceil(maxPages / BATCH) + 30;
|
|
9940
|
+
for (let i = 0; i < MAX_BATCHES && crawled < maxPages && crawled < queue.length; i++) {
|
|
9941
|
+
const batch = queue.slice(crawled, Math.min(crawled + BATCH, maxPages));
|
|
9942
|
+
if (!batch.length) break;
|
|
9943
|
+
const newLinks = await step.run(`crawl-batch-${i}`, async () => {
|
|
9944
|
+
const pages = await extractPagesRotating(batch, { kernelApiKey: key, concurrency, urlsPerBrowser });
|
|
9903
9945
|
await saveExtractPages(jobId, pages);
|
|
9904
|
-
|
|
9946
|
+
const found = [];
|
|
9947
|
+
for (const p of pages) for (const l of p.outlinks ?? []) {
|
|
9948
|
+
if (l.internal && l.href && sameSite(l.href)) found.push(l.href);
|
|
9949
|
+
}
|
|
9950
|
+
return [...new Set(found)];
|
|
9905
9951
|
});
|
|
9952
|
+
crawled += batch.length;
|
|
9953
|
+
let added = false;
|
|
9954
|
+
for (const href of newLinks) {
|
|
9955
|
+
const n = norm(href);
|
|
9956
|
+
if (!seen.has(n) && seen.size < maxPages) {
|
|
9957
|
+
seen.add(n);
|
|
9958
|
+
queue.push(href);
|
|
9959
|
+
added = true;
|
|
9960
|
+
}
|
|
9961
|
+
}
|
|
9962
|
+
if (added) await setExtractJobTotal(jobId, Math.min(seen.size, maxPages));
|
|
9906
9963
|
}
|
|
9907
9964
|
const branding = Array.isArray(job.options.formats) && job.options.formats.includes("branding") ? await step.run("branding", () => extractBranding(job.startUrl, key).catch(() => null)) : null;
|
|
9908
9965
|
const imageAudit = Array.isArray(job.options.formats) && job.options.formats.includes("images") ? await step.run("image-audit", async () => {
|
|
@@ -12247,10 +12304,10 @@ function buildTranscriptMarkdown(result) {
|
|
|
12247
12304
|
if (result.chunks?.length) {
|
|
12248
12305
|
lines.push("## Timestamped Segments");
|
|
12249
12306
|
lines.push("");
|
|
12250
|
-
for (const
|
|
12251
|
-
const start = fmtTs(
|
|
12252
|
-
const end = fmtTs(
|
|
12253
|
-
lines.push(`**[${start} \u2192 ${end}]** ${
|
|
12307
|
+
for (const chunk of result.chunks) {
|
|
12308
|
+
const start = fmtTs(chunk.timestamp[0]);
|
|
12309
|
+
const end = fmtTs(chunk.timestamp[1]);
|
|
12310
|
+
lines.push(`**[${start} \u2192 ${end}]** ${chunk.text.trim()}`);
|
|
12254
12311
|
lines.push("");
|
|
12255
12312
|
}
|
|
12256
12313
|
}
|
|
@@ -12421,6 +12478,8 @@ var init_screenshot_routes = __esm({
|
|
|
12421
12478
|
init_api_auth();
|
|
12422
12479
|
init_url_utils();
|
|
12423
12480
|
init_concurrency_gates();
|
|
12481
|
+
init_db();
|
|
12482
|
+
init_rates();
|
|
12424
12483
|
ScreenshotBodySchema = import_zod14.z.object({
|
|
12425
12484
|
url: import_zod14.z.string().trim().min(1, "url is required"),
|
|
12426
12485
|
device: import_zod14.z.string().trim().optional(),
|
|
@@ -12459,7 +12518,11 @@ var init_screenshot_routes = __esm({
|
|
|
12459
12518
|
metadata: { url: targetUrl, device }
|
|
12460
12519
|
});
|
|
12461
12520
|
if (!gate.ok) return c.json(concurrencyLimitExceededResponse(gate), 429, { "Retry-After": String(gate.retryAfterSeconds) });
|
|
12521
|
+
let debited = false;
|
|
12462
12522
|
try {
|
|
12523
|
+
const { ok, balance_mc } = await debitMc(user.id, MC_COSTS.page_scrape, LedgerOperation.EXTRACT_URL, targetUrl);
|
|
12524
|
+
if (!ok) return c.json(insufficientBalanceResponse(balance_mc, MC_COSTS.page_scrape), 402);
|
|
12525
|
+
debited = true;
|
|
12463
12526
|
const buf = await captureScreenshot(targetUrl, browserServiceApiKey(), device);
|
|
12464
12527
|
return new Response(new Uint8Array(buf), {
|
|
12465
12528
|
status: 200,
|
|
@@ -12470,6 +12533,7 @@ var init_screenshot_routes = __esm({
|
|
|
12470
12533
|
}
|
|
12471
12534
|
});
|
|
12472
12535
|
} catch (err) {
|
|
12536
|
+
if (debited) await creditMc(user.id, MC_COSTS.page_scrape, LedgerOperation.EXTRACT_URL, "failed screenshot refund");
|
|
12473
12537
|
const msg = err instanceof Error ? err.message : String(err);
|
|
12474
12538
|
return c.json({ error: msg }, 502);
|
|
12475
12539
|
} finally {
|
|
@@ -12888,20 +12952,20 @@ var init_FacebookAdExtractor = __esm({
|
|
|
12888
12952
|
if (splitRe.lastIndex === m.index) splitRe.lastIndex++;
|
|
12889
12953
|
}
|
|
12890
12954
|
if (last < bodyText.length) adChunks.push(bodyText.slice(last));
|
|
12891
|
-
const ads = adChunks.filter((c) => /Library ID[:\s]+\d{10,20}/i.test(c)).slice(0, maxAds).map((
|
|
12892
|
-
const lidM =
|
|
12955
|
+
const ads = adChunks.filter((c) => /Library ID[:\s]+\d{10,20}/i.test(c)).slice(0, maxAds).map((chunk) => {
|
|
12956
|
+
const lidM = chunk.match(/Library ID[:\s]+([0-9]{10,20})/i);
|
|
12893
12957
|
const libraryId = lidM ? lidM[1] : null;
|
|
12894
|
-
const statusM =
|
|
12958
|
+
const statusM = chunk.match(/^(Active|Inactive)/i);
|
|
12895
12959
|
const status = statusM ? statusM[1] : null;
|
|
12896
|
-
const startM =
|
|
12960
|
+
const startM = chunk.match(/Started running on\s+([A-Za-z]+\.?\s+\d{1,2},?\s*\d{4})/i);
|
|
12897
12961
|
const started = startM ? startM[1].replace(/\s+/g, " ").trim() : null;
|
|
12898
12962
|
const visual = adVisuals[libraryId ?? ""] ?? { imageSrc: null, videoSrc: null, videoPoster: null };
|
|
12899
|
-
const hasVideoText = /0:00\s*\/\s*0:00|\d+:\d+\s*\/\s*\d+:\d+/.test(
|
|
12963
|
+
const hasVideoText = /0:00\s*\/\s*0:00|\d+:\d+\s*\/\s*\d+:\d+/.test(chunk);
|
|
12900
12964
|
const creativeType = visual.videoSrc || hasVideoText ? "video" : visual.imageSrc ? "image" : "unknown";
|
|
12901
12965
|
let domain = null;
|
|
12902
12966
|
const domainRe = /\b([A-Z0-9]{2,}(?:\.[A-Z]{2,})+)\b/g;
|
|
12903
12967
|
let dm;
|
|
12904
|
-
while ((dm = domainRe.exec(
|
|
12968
|
+
while ((dm = domainRe.exec(chunk)) !== null) {
|
|
12905
12969
|
const c = dm[1];
|
|
12906
12970
|
if (c.length >= 6 && c !== "LIBRARY") {
|
|
12907
12971
|
domain = c;
|
|
@@ -12910,9 +12974,9 @@ var init_FacebookAdExtractor = __esm({
|
|
|
12910
12974
|
}
|
|
12911
12975
|
let primaryText = null;
|
|
12912
12976
|
let headline = null;
|
|
12913
|
-
const sponsoredIdx =
|
|
12977
|
+
const sponsoredIdx = chunk.search(/\bSponsored\b/i);
|
|
12914
12978
|
if (sponsoredIdx >= 0) {
|
|
12915
|
-
let afterSponsored =
|
|
12979
|
+
let afterSponsored = chunk.slice(sponsoredIdx + "Sponsored".length).trim();
|
|
12916
12980
|
afterSponsored = afterSponsored.replace(/\s*\d*:?\d+\s*\/\s*\d*:?\d+.*$/, "");
|
|
12917
12981
|
if (domain) {
|
|
12918
12982
|
const di = afterSponsored.indexOf(domain);
|
|
@@ -12923,18 +12987,18 @@ var init_FacebookAdExtractor = __esm({
|
|
|
12923
12987
|
}
|
|
12924
12988
|
const ctaRe2 = new RegExp("\\b(" + CTA_LABELS.map((l) => l.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|") + ")\\b", "i");
|
|
12925
12989
|
if (domain) {
|
|
12926
|
-
const di =
|
|
12990
|
+
const di = chunk.indexOf(domain);
|
|
12927
12991
|
if (di >= 0) {
|
|
12928
|
-
let afterDomain =
|
|
12992
|
+
let afterDomain = chunk.slice(di + domain.length).trim();
|
|
12929
12993
|
const ctaMatch = afterDomain.search(ctaRe2);
|
|
12930
12994
|
if (ctaMatch >= 0) afterDomain = afterDomain.slice(0, ctaMatch).trim();
|
|
12931
12995
|
if (afterDomain.length > 3 && afterDomain.length < 120) headline = afterDomain;
|
|
12932
12996
|
}
|
|
12933
12997
|
}
|
|
12934
12998
|
if (!headline) {
|
|
12935
|
-
const ctaIdx =
|
|
12999
|
+
const ctaIdx = chunk.search(ctaRe2);
|
|
12936
13000
|
if (ctaIdx > 0) {
|
|
12937
|
-
const beforeCta =
|
|
13001
|
+
const beforeCta = chunk.slice(0, ctaIdx).trimEnd();
|
|
12938
13002
|
const sentenceEnd = Math.max(beforeCta.lastIndexOf(". "), beforeCta.lastIndexOf("! "), beforeCta.lastIndexOf("? "));
|
|
12939
13003
|
if (sentenceEnd >= 0) {
|
|
12940
13004
|
const candidate = beforeCta.slice(sentenceEnd + 2).trim();
|
|
@@ -12946,12 +13010,12 @@ var init_FacebookAdExtractor = __esm({
|
|
|
12946
13010
|
}
|
|
12947
13011
|
let cta = null;
|
|
12948
13012
|
for (const lbl of CTA_LABELS) {
|
|
12949
|
-
if (
|
|
13013
|
+
if (chunk.toLowerCase().includes(lbl.toLowerCase())) {
|
|
12950
13014
|
cta = lbl;
|
|
12951
13015
|
break;
|
|
12952
13016
|
}
|
|
12953
13017
|
}
|
|
12954
|
-
const multiM =
|
|
13018
|
+
const multiM = chunk.match(/(\d+)\s+ad[s]?\s+use[s]?\s+this/i);
|
|
12955
13019
|
const clusterCount = multiM ? parseInt(multiM[1]) : null;
|
|
12956
13020
|
return {
|
|
12957
13021
|
libraryId,
|
|
@@ -12974,8 +13038,8 @@ var init_FacebookAdExtractor = __esm({
|
|
|
12974
13038
|
const unknownCreativeCount = ads.filter((a) => a.creativeType === "unknown").length;
|
|
12975
13039
|
const advertiserPageId = new URL(page.url()).searchParams.get("view_all_page_id") ?? null;
|
|
12976
13040
|
const nameFreq = /* @__PURE__ */ new Map();
|
|
12977
|
-
for (const
|
|
12978
|
-
const detailsParts =
|
|
13041
|
+
for (const chunk of adChunks) {
|
|
13042
|
+
const detailsParts = chunk.split("See ad details ");
|
|
12979
13043
|
if (detailsParts.length < 2) continue;
|
|
12980
13044
|
const sponsoredIdx = detailsParts[1].indexOf(" Sponsored");
|
|
12981
13045
|
if (sponsoredIdx < 0) continue;
|
|
@@ -14394,11 +14458,11 @@ var init_facebook_ad_routes = __esm({
|
|
|
14394
14458
|
}
|
|
14395
14459
|
if (last < bodyText.length) adChunks.push(bodyText.slice(last));
|
|
14396
14460
|
const advertiserMap = /* @__PURE__ */ new Map();
|
|
14397
|
-
for (const
|
|
14398
|
-
const lidM =
|
|
14461
|
+
for (const chunk of adChunks) {
|
|
14462
|
+
const lidM = chunk.match(/Library ID[:\s]+([0-9]{10,20})/i);
|
|
14399
14463
|
if (!lidM) continue;
|
|
14400
14464
|
const libraryId = lidM[1];
|
|
14401
|
-
const detailsParts =
|
|
14465
|
+
const detailsParts = chunk.split("See ad details ");
|
|
14402
14466
|
if (detailsParts.length < 2) continue;
|
|
14403
14467
|
const afterDetails = detailsParts[1];
|
|
14404
14468
|
const sponsoredIdx = afterDetails.indexOf(" Sponsored");
|
|
@@ -14922,8 +14986,8 @@ function runFfmpegMux(videoPath, audioPath, outPath) {
|
|
|
14922
14986
|
stdio: ["ignore", "ignore", "pipe"]
|
|
14923
14987
|
});
|
|
14924
14988
|
let stderr = "";
|
|
14925
|
-
child.stderr.on("data", (
|
|
14926
|
-
stderr += String(
|
|
14989
|
+
child.stderr.on("data", (chunk) => {
|
|
14990
|
+
stderr += String(chunk);
|
|
14927
14991
|
});
|
|
14928
14992
|
child.on("error", (err) => resolve({ ok: false, error: err.message }));
|
|
14929
14993
|
child.on("close", (code) => resolve({ ok: code === 0, error: code === 0 ? null : stderr.trim() || `ffmpeg exited ${code}` }));
|
|
@@ -23698,7 +23762,7 @@ var PACKAGE_VERSION;
|
|
|
23698
23762
|
var init_version = __esm({
|
|
23699
23763
|
"src/version.ts"() {
|
|
23700
23764
|
"use strict";
|
|
23701
|
-
PACKAGE_VERSION = "0.3.
|
|
23765
|
+
PACKAGE_VERSION = "0.3.19";
|
|
23702
23766
|
}
|
|
23703
23767
|
});
|
|
23704
23768
|
|
|
@@ -23722,7 +23786,11 @@ ROUTING
|
|
|
23722
23786
|
- Workflows (multi-step): local directory build -> directory_workflow; rank blueprint -> rank_tracker_workflow;
|
|
23723
23787
|
AI-answer citation fan-out (AEO) -> query_fanout_workflow; deep research -> deep_research_workflow.
|
|
23724
23788
|
- Anything without a dedicated tool (e.g. Reddit, arbitrary logged-in sites) -> the browser_* agent
|
|
23725
|
-
(browser_open, then navigate/read)
|
|
23789
|
+
(browser_open, then navigate/read).
|
|
23790
|
+
- Logged-in sites (ChatGPT, Claude, Reddit, any account): save the login first -> browser_profile_connect
|
|
23791
|
+
(returns an mcpscraper.dev sign-in link for the user; one profile holds MANY logins, call it again with
|
|
23792
|
+
the same profile + a new domain to add accounts) -> poll browser_profile_list until AUTHENTICATED ->
|
|
23793
|
+
browser_open with that profile. browser_profile_list also shows what a profile is connected to.
|
|
23726
23794
|
|
|
23727
23795
|
NOTES
|
|
23728
23796
|
- Bulk / full-site crawls: call extract_site with rotateProxies:true for blocked or rate-limited sites.
|
|
@@ -25518,8 +25586,64 @@ async function migrateBrowserAgent() {
|
|
|
25518
25586
|
)
|
|
25519
25587
|
`);
|
|
25520
25588
|
await db.execute(`CREATE INDEX IF NOT EXISTS browser_agent_replays_session ON browser_agent_replays(session_id)`);
|
|
25589
|
+
await db.execute(`
|
|
25590
|
+
CREATE TABLE IF NOT EXISTS browser_auth_connections (
|
|
25591
|
+
connection_id TEXT PRIMARY KEY,
|
|
25592
|
+
hosted_url TEXT,
|
|
25593
|
+
domain TEXT,
|
|
25594
|
+
profile TEXT,
|
|
25595
|
+
account_email TEXT,
|
|
25596
|
+
note TEXT,
|
|
25597
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
25598
|
+
updated_at TEXT
|
|
25599
|
+
)
|
|
25600
|
+
`);
|
|
25601
|
+
try {
|
|
25602
|
+
await db.execute(`ALTER TABLE browser_auth_connections ADD COLUMN account_email TEXT`);
|
|
25603
|
+
} catch {
|
|
25604
|
+
}
|
|
25605
|
+
try {
|
|
25606
|
+
await db.execute(`ALTER TABLE browser_auth_connections ADD COLUMN note TEXT`);
|
|
25607
|
+
} catch {
|
|
25608
|
+
}
|
|
25609
|
+
try {
|
|
25610
|
+
await db.execute(`ALTER TABLE browser_auth_connections ADD COLUMN updated_at TEXT`);
|
|
25611
|
+
} catch {
|
|
25612
|
+
}
|
|
25613
|
+
await db.execute(`CREATE INDEX IF NOT EXISTS browser_auth_connections_profile ON browser_auth_connections(profile)`);
|
|
25521
25614
|
_ready = true;
|
|
25522
25615
|
}
|
|
25616
|
+
async function saveAuthConnection(input) {
|
|
25617
|
+
if (!input.connectionId) return;
|
|
25618
|
+
const db = getDb();
|
|
25619
|
+
await db.execute({
|
|
25620
|
+
sql: `INSERT INTO browser_auth_connections (connection_id, hosted_url, domain, profile, account_email, note, updated_at)
|
|
25621
|
+
VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
|
|
25622
|
+
ON CONFLICT(connection_id) DO UPDATE SET
|
|
25623
|
+
hosted_url = COALESCE(excluded.hosted_url, browser_auth_connections.hosted_url),
|
|
25624
|
+
domain = COALESCE(excluded.domain, browser_auth_connections.domain),
|
|
25625
|
+
profile = COALESCE(excluded.profile, browser_auth_connections.profile),
|
|
25626
|
+
account_email = COALESCE(excluded.account_email, browser_auth_connections.account_email),
|
|
25627
|
+
note = COALESCE(excluded.note, browser_auth_connections.note),
|
|
25628
|
+
updated_at = datetime('now')`,
|
|
25629
|
+
args: [input.connectionId, input.hostedUrl, input.domain, input.profile, input.accountEmail ?? null, input.note ?? null]
|
|
25630
|
+
});
|
|
25631
|
+
}
|
|
25632
|
+
async function getAuthConnectionRow(connectionId) {
|
|
25633
|
+
if (!connectionId) return null;
|
|
25634
|
+
const db = getDb();
|
|
25635
|
+
const res = await db.execute({ sql: `SELECT * FROM browser_auth_connections WHERE connection_id = ?`, args: [connectionId] });
|
|
25636
|
+
return res.rows[0] ?? null;
|
|
25637
|
+
}
|
|
25638
|
+
async function listAuthConnectionsByProfile(profile) {
|
|
25639
|
+
if (!profile) return [];
|
|
25640
|
+
const db = getDb();
|
|
25641
|
+
const res = await db.execute({
|
|
25642
|
+
sql: `SELECT * FROM browser_auth_connections WHERE profile = ? ORDER BY COALESCE(updated_at, created_at) DESC`,
|
|
25643
|
+
args: [profile]
|
|
25644
|
+
});
|
|
25645
|
+
return res.rows;
|
|
25646
|
+
}
|
|
25523
25647
|
async function createSessionRow(input) {
|
|
25524
25648
|
const db = getDb();
|
|
25525
25649
|
const id = `bas_${(0, import_node_crypto6.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
|
|
@@ -25553,6 +25677,14 @@ async function listSessionRows(userId, includeClosed = false) {
|
|
|
25553
25677
|
});
|
|
25554
25678
|
return res.rows;
|
|
25555
25679
|
}
|
|
25680
|
+
async function listIdleOpenSessionRows(idleSeconds) {
|
|
25681
|
+
const db = getDb();
|
|
25682
|
+
const res = await db.execute({
|
|
25683
|
+
sql: `SELECT * FROM browser_agent_sessions WHERE status = 'open' AND COALESCE(last_action_at, created_at) <= datetime('now', ?) ORDER BY created_at LIMIT 200`,
|
|
25684
|
+
args: [`-${Math.max(1, Math.round(idleSeconds))} seconds`]
|
|
25685
|
+
});
|
|
25686
|
+
return res.rows;
|
|
25687
|
+
}
|
|
25556
25688
|
async function addActiveMs(id, deltaMs) {
|
|
25557
25689
|
const db = getDb();
|
|
25558
25690
|
await db.execute({
|
|
@@ -26547,6 +26679,17 @@ async function findProfileConnection(k, profileName, domain) {
|
|
|
26547
26679
|
}
|
|
26548
26680
|
return null;
|
|
26549
26681
|
}
|
|
26682
|
+
async function listProfileConnections(profileName) {
|
|
26683
|
+
if (!profileName) return [];
|
|
26684
|
+
const k = client();
|
|
26685
|
+
const out = [];
|
|
26686
|
+
for await (const connection of k.auth.connections.list({ profile_name: profileName })) {
|
|
26687
|
+
const data = connection;
|
|
26688
|
+
if (data.profile_name && data.profile_name !== profileName) continue;
|
|
26689
|
+
out.push(profileOnboardResult(connection));
|
|
26690
|
+
}
|
|
26691
|
+
return out;
|
|
26692
|
+
}
|
|
26550
26693
|
async function findOrCreateProfileConnection(k, opts) {
|
|
26551
26694
|
try {
|
|
26552
26695
|
return await k.auth.connections.create({
|
|
@@ -26934,29 +27077,61 @@ function browserSessionLockTtlSeconds(timeoutSeconds) {
|
|
|
26934
27077
|
}
|
|
26935
27078
|
return DEFAULT_BROWSER_SESSION_LOCK_TTL_SECONDS;
|
|
26936
27079
|
}
|
|
26937
|
-
async function charge(sessionId, userId,
|
|
26938
|
-
const
|
|
26939
|
-
|
|
26940
|
-
const
|
|
26941
|
-
|
|
27080
|
+
async function charge(sessionId, userId, _startedAtMs) {
|
|
27081
|
+
const row = await getSessionRow(sessionId);
|
|
27082
|
+
if (!row) return;
|
|
27083
|
+
const createdMs = parseAgentDate(row.created_at);
|
|
27084
|
+
if (createdMs == null) return;
|
|
27085
|
+
const uptimeMs = Math.max(0, Date.now() - createdMs);
|
|
27086
|
+
const owed = browserActiveCostMc(uptimeMs);
|
|
27087
|
+
await addActiveMs(sessionId, 0);
|
|
27088
|
+
const delta = owed - Number(row.billed_mc ?? 0);
|
|
26942
27089
|
if (delta > 0) {
|
|
26943
27090
|
const res = await debitMc(userId, delta, LedgerOperation.BROWSER_SESSION, sessionId);
|
|
26944
27091
|
if (res.ok) await setBilledMc(sessionId, owed);
|
|
26945
27092
|
}
|
|
26946
27093
|
}
|
|
27094
|
+
async function reapIdleBrowserSessions(idleSeconds = 120) {
|
|
27095
|
+
const rows = await listIdleOpenSessionRows(idleSeconds);
|
|
27096
|
+
let reaped = 0;
|
|
27097
|
+
for (const row of rows) {
|
|
27098
|
+
if (row.user_id != null) {
|
|
27099
|
+
try {
|
|
27100
|
+
await charge(row.id, row.user_id);
|
|
27101
|
+
} catch {
|
|
27102
|
+
}
|
|
27103
|
+
}
|
|
27104
|
+
try {
|
|
27105
|
+
await closeSession(row.runtime_session_id);
|
|
27106
|
+
} catch {
|
|
27107
|
+
}
|
|
27108
|
+
await markSessionClosed(row.id).catch(() => {
|
|
27109
|
+
});
|
|
27110
|
+
if (row.concurrency_lock_id) await releaseConcurrencyGate(row.concurrency_lock_id).catch(() => {
|
|
27111
|
+
});
|
|
27112
|
+
reaped++;
|
|
27113
|
+
}
|
|
27114
|
+
return { reaped };
|
|
27115
|
+
}
|
|
26947
27116
|
function publicSession(row) {
|
|
26948
27117
|
return {
|
|
26949
27118
|
session_id: row.id,
|
|
26950
27119
|
status: row.status,
|
|
26951
27120
|
label: row.label,
|
|
26952
|
-
|
|
27121
|
+
watch_url: `/console/${row.id}`,
|
|
26953
27122
|
created_at: row.created_at,
|
|
26954
27123
|
last_action_at: row.last_action_at,
|
|
26955
27124
|
closed_at: row.closed_at,
|
|
26956
|
-
|
|
27125
|
+
uptime_seconds: uptimeSeconds(row),
|
|
26957
27126
|
credits_used: Math.round((row.billed_mc ?? 0) / 10) / 100
|
|
26958
27127
|
};
|
|
26959
27128
|
}
|
|
27129
|
+
function uptimeSeconds(row) {
|
|
27130
|
+
const createdMs = parseAgentDate(row.created_at);
|
|
27131
|
+
const endMs = row.closed_at ? parseAgentDate(row.closed_at) : Date.now();
|
|
27132
|
+
if (createdMs == null || endMs == null) return 0;
|
|
27133
|
+
return Math.max(0, Math.round((endMs - createdMs) / 1e3));
|
|
27134
|
+
}
|
|
26960
27135
|
function failure(err) {
|
|
26961
27136
|
const msg = err instanceof Error ? err.message : String(err);
|
|
26962
27137
|
return { error: sanitizeVendorName(msg) };
|
|
@@ -27005,10 +27180,23 @@ function buildBrowserAgentRoutes() {
|
|
|
27005
27180
|
const profileName = typeof body.profile === "string" ? body.profile.trim() : "";
|
|
27006
27181
|
const domain = typeof body.domain === "string" ? body.domain.trim().replace(/^https?:\/\//, "").replace(/\/.*$/, "") : "chatgpt.com";
|
|
27007
27182
|
const loginUrl = typeof body.login_url === "string" ? body.login_url.trim() : void 0;
|
|
27183
|
+
const accountEmail = typeof body.account_email === "string" ? body.account_email.trim() || null : null;
|
|
27184
|
+
const note = typeof body.note === "string" ? body.note.trim() || null : null;
|
|
27008
27185
|
if (!profileName) return c.json({ error: "profile is required" }, 400);
|
|
27009
27186
|
if (!domain) return c.json({ error: "domain is required" }, 400);
|
|
27010
27187
|
try {
|
|
27011
27188
|
const result = await startProfileOnboarding({ profileName, domain, loginUrl });
|
|
27189
|
+
try {
|
|
27190
|
+
await saveAuthConnection({
|
|
27191
|
+
connectionId: result.connectionId,
|
|
27192
|
+
hostedUrl: result.hostedUrl,
|
|
27193
|
+
domain: result.domain,
|
|
27194
|
+
profile: result.profileName,
|
|
27195
|
+
accountEmail,
|
|
27196
|
+
note
|
|
27197
|
+
});
|
|
27198
|
+
} catch {
|
|
27199
|
+
}
|
|
27012
27200
|
return c.json({
|
|
27013
27201
|
connection_id: result.connectionId,
|
|
27014
27202
|
profile: result.profileName,
|
|
@@ -27017,8 +27205,6 @@ function buildBrowserAgentRoutes() {
|
|
|
27017
27205
|
flow_status: result.flowStatus,
|
|
27018
27206
|
flow_step: result.flowStep,
|
|
27019
27207
|
flow_expires_at: result.flowExpiresAt,
|
|
27020
|
-
hosted_url: result.hostedUrl,
|
|
27021
|
-
live_view_url: result.liveViewUrl,
|
|
27022
27208
|
browser_session_id: result.browserSessionId,
|
|
27023
27209
|
post_login_url: result.postLoginUrl
|
|
27024
27210
|
});
|
|
@@ -27033,6 +27219,17 @@ function buildBrowserAgentRoutes() {
|
|
|
27033
27219
|
const domain = typeof body.domain === "string" ? body.domain.trim().replace(/^https?:\/\//, "").replace(/\/.*$/, "") : void 0;
|
|
27034
27220
|
try {
|
|
27035
27221
|
const result = await getProfileOnboardingStatus({ connectionId, profileName, domain });
|
|
27222
|
+
if (result.hostedUrl) {
|
|
27223
|
+
try {
|
|
27224
|
+
await saveAuthConnection({
|
|
27225
|
+
connectionId: result.connectionId,
|
|
27226
|
+
hostedUrl: result.hostedUrl,
|
|
27227
|
+
domain: result.domain,
|
|
27228
|
+
profile: result.profileName
|
|
27229
|
+
});
|
|
27230
|
+
} catch {
|
|
27231
|
+
}
|
|
27232
|
+
}
|
|
27036
27233
|
return c.json({
|
|
27037
27234
|
connection_id: result.connectionId,
|
|
27038
27235
|
profile: result.profileName,
|
|
@@ -27041,8 +27238,6 @@ function buildBrowserAgentRoutes() {
|
|
|
27041
27238
|
flow_status: result.flowStatus,
|
|
27042
27239
|
flow_step: result.flowStep,
|
|
27043
27240
|
flow_expires_at: result.flowExpiresAt,
|
|
27044
|
-
hosted_url: result.hostedUrl,
|
|
27045
|
-
live_view_url: result.liveViewUrl,
|
|
27046
27241
|
browser_session_id: result.browserSessionId,
|
|
27047
27242
|
post_login_url: result.postLoginUrl
|
|
27048
27243
|
});
|
|
@@ -27050,6 +27245,37 @@ function buildBrowserAgentRoutes() {
|
|
|
27050
27245
|
return c.json(failure(err), 404);
|
|
27051
27246
|
}
|
|
27052
27247
|
});
|
|
27248
|
+
app2.post("/profiles/list", async (c) => {
|
|
27249
|
+
const body = await c.req.json().catch(() => ({}));
|
|
27250
|
+
const profileName = typeof body.profile === "string" ? body.profile.trim() : "";
|
|
27251
|
+
if (!profileName) return c.json({ error: "profile is required" }, 400);
|
|
27252
|
+
const domainFilter = typeof body.domain === "string" ? body.domain.trim().replace(/^https?:\/\//, "").replace(/\/.*$/, "") : void 0;
|
|
27253
|
+
const connectionIdFilter = typeof body.connection_id === "string" ? body.connection_id.trim() : void 0;
|
|
27254
|
+
try {
|
|
27255
|
+
const [live, stored] = await Promise.all([
|
|
27256
|
+
listProfileConnections(profileName),
|
|
27257
|
+
listAuthConnectionsByProfile(profileName).catch(() => [])
|
|
27258
|
+
]);
|
|
27259
|
+
const notesByDomain = new Map(stored.map((row) => [row.domain ?? "", row]));
|
|
27260
|
+
const notesByConnection = new Map(stored.map((row) => [row.connection_id, row]));
|
|
27261
|
+
let connections = live.map((conn) => {
|
|
27262
|
+
const record = notesByConnection.get(conn.connectionId) ?? notesByDomain.get(conn.domain);
|
|
27263
|
+
return {
|
|
27264
|
+
connection_id: conn.connectionId,
|
|
27265
|
+
domain: conn.domain,
|
|
27266
|
+
status: conn.status,
|
|
27267
|
+
account_email: record?.account_email ?? null,
|
|
27268
|
+
note: record?.note ?? null,
|
|
27269
|
+
last_connected_at: record?.updated_at ?? record?.created_at ?? null
|
|
27270
|
+
};
|
|
27271
|
+
});
|
|
27272
|
+
if (connectionIdFilter) connections = connections.filter((c2) => c2.connection_id === connectionIdFilter);
|
|
27273
|
+
if (domainFilter) connections = connections.filter((c2) => c2.domain === domainFilter);
|
|
27274
|
+
return c.json({ profile: profileName, connections });
|
|
27275
|
+
} catch (err) {
|
|
27276
|
+
return c.json(failure(err), 502);
|
|
27277
|
+
}
|
|
27278
|
+
});
|
|
27053
27279
|
app2.post("/sessions", async (c) => {
|
|
27054
27280
|
const user = c.get("user");
|
|
27055
27281
|
if (Number(user.balance_mc ?? 0) < BROWSER_OPEN_MIN_BALANCE_MC) {
|
|
@@ -27117,6 +27343,7 @@ function buildBrowserAgentRoutes() {
|
|
|
27117
27343
|
const user = c.get("user");
|
|
27118
27344
|
const row = await loadOpenSession(c.req.param("id"), user.id);
|
|
27119
27345
|
if (!row) return c.json({ error: "not found" }, 404);
|
|
27346
|
+
await charge(row.id, user.id);
|
|
27120
27347
|
try {
|
|
27121
27348
|
await closeSession(row.runtime_session_id);
|
|
27122
27349
|
} catch {
|
|
@@ -27422,6 +27649,50 @@ var init_browser_agent_routes = __esm({
|
|
|
27422
27649
|
});
|
|
27423
27650
|
|
|
27424
27651
|
// src/api/browser-agent-console.ts
|
|
27652
|
+
function renderAuthConsoleHtml(connectionId, hostedUrl) {
|
|
27653
|
+
const safeId = String(connectionId).replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]);
|
|
27654
|
+
const goUrl = `/console/auth/${encodeURIComponent(connectionId)}/go`;
|
|
27655
|
+
const body = hostedUrl ? `<iframe class="stage" allow="clipboard-read; clipboard-write" src="${goUrl}"></iframe>
|
|
27656
|
+
<div class="fallback">
|
|
27657
|
+
<span>Sign-in window not showing?</span>
|
|
27658
|
+
<a class="btn" href="${goUrl}" target="_blank" rel="noopener">Open sign-in in a new tab</a>
|
|
27659
|
+
</div>` : `<div class="empty">
|
|
27660
|
+
<div>This sign-in link is no longer active.</div>
|
|
27661
|
+
<div class="muted">Ask the assistant to reconnect the profile, then open the new link.</div>
|
|
27662
|
+
</div>`;
|
|
27663
|
+
return `<!DOCTYPE html>
|
|
27664
|
+
<html lang="en">
|
|
27665
|
+
<head>
|
|
27666
|
+
<meta charset="UTF-8" />
|
|
27667
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
27668
|
+
<title>MCP Scraper \u2014 Secure Sign-in</title>
|
|
27669
|
+
<style>
|
|
27670
|
+
:root { color-scheme: dark; }
|
|
27671
|
+
:where(*) { box-sizing: border-box; }
|
|
27672
|
+
body { margin: 0; height: 100vh; display: flex; flex-direction: column; font: 14px/1.5 ui-sans-serif, system-ui, -apple-system, sans-serif; background: #0b0e14; color: #d7dce5; }
|
|
27673
|
+
header { display: flex; align-items: center; gap: 10px; padding: 12px 18px; border-bottom: 1px solid #1c2230; background: #0f131c; }
|
|
27674
|
+
header h1 { font-size: 14px; margin: 0; font-weight: 600; color: #fff; }
|
|
27675
|
+
header .muted { color: #6b7689; font-size: 12px; }
|
|
27676
|
+
.spacer { flex: 1; }
|
|
27677
|
+
.stage { flex: 1; width: 100%; border: 0; display: block; background: #05070b; }
|
|
27678
|
+
.fallback { display: flex; align-items: center; gap: 12px; padding: 10px 18px; border-top: 1px solid #1c2230; background: #0f131c; font-size: 13px; color: #8893a7; }
|
|
27679
|
+
.btn { background: #2b6cff; color: #fff; border-radius: 7px; padding: 7px 12px; text-decoration: none; font-weight: 500; }
|
|
27680
|
+
.empty { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; color: #8893a7; }
|
|
27681
|
+
.muted { color: #6b7689; font-size: 12px; }
|
|
27682
|
+
</style>
|
|
27683
|
+
${ATTRIBUTION_PIXEL_SCRIPT}
|
|
27684
|
+
</head>
|
|
27685
|
+
<body>
|
|
27686
|
+
<header>
|
|
27687
|
+
<h1>MCP Scraper</h1>
|
|
27688
|
+
<span class="muted">Secure sign-in \xB7 ${safeId}</span>
|
|
27689
|
+
<span class="spacer"></span>
|
|
27690
|
+
<span class="muted">Complete the login below \u2014 your session is saved to your profile.</span>
|
|
27691
|
+
</header>
|
|
27692
|
+
${body}
|
|
27693
|
+
</body>
|
|
27694
|
+
</html>`;
|
|
27695
|
+
}
|
|
27425
27696
|
function renderConsoleHtml(initialSessionId) {
|
|
27426
27697
|
const initial = JSON.stringify(initialSessionId ?? "");
|
|
27427
27698
|
return `<!DOCTYPE html>
|
|
@@ -28207,6 +28478,7 @@ var init_server = __esm({
|
|
|
28207
28478
|
init_mcp_routes();
|
|
28208
28479
|
init_browser_agent_routes();
|
|
28209
28480
|
init_browser_agent_console();
|
|
28481
|
+
init_browser_agent_db();
|
|
28210
28482
|
init_stripe_routes();
|
|
28211
28483
|
init_site_audit_worker();
|
|
28212
28484
|
import_factory6 = require("hono/factory");
|
|
@@ -29016,11 +29288,12 @@ var init_server = __esm({
|
|
|
29016
29288
|
const { drainQueue: drainQueue2 } = await Promise.resolve().then(() => (init_worker(), worker_exports));
|
|
29017
29289
|
const budget = { maxJobs: 10, deadlineMs: Date.now() + 28e4 };
|
|
29018
29290
|
const workflowDispatchResult = await dispatchDueWorkflowSchedules(`${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`);
|
|
29019
|
-
const [results, sweepResult] = await Promise.all([
|
|
29291
|
+
const [results, sweepResult, reapResult] = await Promise.all([
|
|
29020
29292
|
drainQueue2(budget),
|
|
29021
|
-
runMonthlyRefreshSweep()
|
|
29293
|
+
runMonthlyRefreshSweep(),
|
|
29294
|
+
reapIdleBrowserSessions(120).catch(() => ({ reaped: 0 }))
|
|
29022
29295
|
]);
|
|
29023
|
-
return c.json({ drained: results.length, results, sweepResult, workflowDispatch: workflowDispatchResult });
|
|
29296
|
+
return c.json({ drained: results.length, results, sweepResult, reaped: reapResult, workflowDispatch: workflowDispatchResult });
|
|
29024
29297
|
});
|
|
29025
29298
|
app.on(["GET", "POST", "PUT"], "/api/inngest", (0, import_hono14.serve)({ client: inngest, functions: [siteAuditFn, siteExtractFn] }));
|
|
29026
29299
|
app.route("/api/internal/site-architecture-auditor", siteAuditApp);
|
|
@@ -29035,6 +29308,19 @@ var init_server = __esm({
|
|
|
29035
29308
|
app.route("/mcp", mcpApp);
|
|
29036
29309
|
app.route("/agent", buildBrowserAgentRoutes());
|
|
29037
29310
|
app.get("/console", (c) => c.html(renderConsoleHtml()));
|
|
29311
|
+
app.get("/console/auth/:id", async (c) => {
|
|
29312
|
+
const id = c.req.param("id");
|
|
29313
|
+
await migrateBrowserAgent();
|
|
29314
|
+
const row = await getAuthConnectionRow(id);
|
|
29315
|
+
return c.html(renderAuthConsoleHtml(id, row?.hosted_url ?? null));
|
|
29316
|
+
});
|
|
29317
|
+
app.get("/console/auth/:id/go", async (c) => {
|
|
29318
|
+
const id = c.req.param("id");
|
|
29319
|
+
await migrateBrowserAgent();
|
|
29320
|
+
const row = await getAuthConnectionRow(id);
|
|
29321
|
+
if (!row?.hosted_url) return c.text("This sign-in link is no longer active.", 404);
|
|
29322
|
+
return c.redirect(row.hosted_url, 302);
|
|
29323
|
+
});
|
|
29038
29324
|
app.get("/console/:id", (c) => c.html(renderConsoleHtml(c.req.param("id"))));
|
|
29039
29325
|
app.route("/stripe", stripeApp);
|
|
29040
29326
|
if (!process.env.INNGEST_EVENT_KEY) {
|