mcp-scraper 0.2.19 → 0.2.21
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 +36 -5
- package/dist/bin/api-server.cjs +1072 -349
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +3 -3
- package/dist/bin/browser-agent-stdio-server.cjs +1274 -75
- package/dist/bin/browser-agent-stdio-server.cjs.map +1 -1
- package/dist/bin/browser-agent-stdio-server.js +4 -2
- package/dist/bin/browser-agent-stdio-server.js.map +1 -1
- package/dist/bin/mcp-scraper-cli.cjs +659 -210
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +108 -9
- package/dist/bin/mcp-scraper-cli.js.map +1 -1
- package/dist/bin/mcp-scraper-combined-stdio-server.cjs +2104 -566
- package/dist/bin/mcp-scraper-combined-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-scraper-combined-stdio-server.js +7 -4
- package/dist/bin/mcp-scraper-combined-stdio-server.js.map +1 -1
- package/dist/bin/mcp-scraper-install.cjs +3 -3
- 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 +726 -387
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +3 -2
- package/dist/bin/mcp-stdio-server.js.map +1 -1
- package/dist/bin/paa-harvest.cjs +5 -3
- package/dist/bin/paa-harvest.cjs.map +1 -1
- package/dist/bin/paa-harvest.js +5 -2
- package/dist/bin/paa-harvest.js.map +1 -1
- package/dist/{chunk-TL7YTFLH.js → chunk-DBQDG7EH.js} +99 -27
- package/dist/chunk-DBQDG7EH.js.map +1 -0
- package/dist/chunk-DUEW4EOO.js +214 -0
- package/dist/chunk-DUEW4EOO.js.map +1 -0
- package/dist/{chunk-F44RBOJ5.js → chunk-H2R232HK.js} +216 -108
- package/dist/chunk-H2R232HK.js.map +1 -0
- package/dist/chunk-HS6OKELW.js +1786 -0
- package/dist/chunk-HS6OKELW.js.map +1 -0
- package/dist/{chunk-CQTAKXBN.js → chunk-IPW4LFOT.js} +11 -116
- package/dist/chunk-IPW4LFOT.js.map +1 -0
- package/dist/chunk-LFATOGDF.js +111 -0
- package/dist/chunk-LFATOGDF.js.map +1 -0
- package/dist/{chunk-MCBOZ2JF.js → chunk-MLNCKQ2B.js} +3 -3
- package/dist/chunk-MLNCKQ2B.js.map +1 -0
- package/dist/{chunk-4UEVYYDW.js → chunk-O5J7E4VX.js} +394 -57
- package/dist/chunk-O5J7E4VX.js.map +1 -0
- package/dist/{chunk-BSYPATSM.js → chunk-SXTXMFEQ.js} +2 -2
- package/dist/chunk-WN7PBKMV.js +28 -0
- package/dist/chunk-WN7PBKMV.js.map +1 -0
- package/dist/chunk-XKUDVN2E.js +7 -0
- package/dist/chunk-XKUDVN2E.js.map +1 -0
- package/dist/{db-P5X6UQ3E.js → db-BE4JVB3V.js} +8 -2
- package/dist/index.cjs +5 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/{server-CRUSZA2Q.js → server-24XIQ6W6.js} +248 -40
- package/dist/server-24XIQ6W6.js.map +1 -0
- package/dist/{worker-OZSWIS3F.js → worker-SLQ375UG.js} +8 -5
- package/dist/{worker-OZSWIS3F.js.map → worker-SLQ375UG.js.map} +1 -1
- package/docs/adr/0001-in-page-graphql-interception-for-anti-bot-scraping.md +2 -2
- package/docs/mcp-tool-craft-lint.generated.md +51 -0
- package/docs/mcp-tool-manifest.generated.json +1534 -0
- package/docs/specs/local-competitive-audit-spec.md +1 -2
- package/docs/specs/mcp-tool-definition-quality-audit-spec.md +1602 -0
- package/package.json +4 -1
- package/dist/chunk-4UEVYYDW.js.map +0 -1
- package/dist/chunk-76P4DKWR.js +0 -793
- package/dist/chunk-76P4DKWR.js.map +0 -1
- package/dist/chunk-CQTAKXBN.js.map +0 -1
- package/dist/chunk-F44RBOJ5.js.map +0 -1
- package/dist/chunk-MCBOZ2JF.js.map +0 -1
- package/dist/chunk-RU2HZD55.js +0 -7
- package/dist/chunk-RU2HZD55.js.map +0 -1
- package/dist/chunk-TL7YTFLH.js.map +0 -1
- package/dist/server-CRUSZA2Q.js.map +0 -1
- /package/dist/{chunk-BSYPATSM.js.map → chunk-SXTXMFEQ.js.map} +0 -0
- /package/dist/{db-P5X6UQ3E.js.map → db-BE4JVB3V.js.map} +0 -0
|
@@ -16,9 +16,11 @@ import {
|
|
|
16
16
|
harvestProblemResponse,
|
|
17
17
|
insufficientBalanceResponse,
|
|
18
18
|
serializeHarvestProblem
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-SXTXMFEQ.js";
|
|
20
20
|
import {
|
|
21
21
|
SiteAuditJobRowSchema,
|
|
22
|
+
advanceWorkflowRunStep,
|
|
23
|
+
appendWorkflowArtifacts,
|
|
22
24
|
cancelJob,
|
|
23
25
|
checkRateLimit,
|
|
24
26
|
claimMonthlyFreeRefresh,
|
|
@@ -48,6 +50,7 @@ import {
|
|
|
48
50
|
getUserStats,
|
|
49
51
|
getWorkflowArtifact,
|
|
50
52
|
getWorkflowRun,
|
|
53
|
+
getWorkflowRunStepState,
|
|
51
54
|
getWorkflowSchedule,
|
|
52
55
|
ledgerExistsForOperation,
|
|
53
56
|
ledgerExistsForStripePI,
|
|
@@ -74,47 +77,62 @@ import {
|
|
|
74
77
|
setPassword,
|
|
75
78
|
setStripeCustomerId,
|
|
76
79
|
verifyPassword
|
|
77
|
-
} from "./chunk-
|
|
80
|
+
} from "./chunk-DBQDG7EH.js";
|
|
78
81
|
import {
|
|
79
82
|
BrowserDriver,
|
|
80
|
-
MapsPlaceOptionsSchema,
|
|
81
|
-
MapsSearchOptionsSchema,
|
|
82
83
|
MapsSelectors,
|
|
83
|
-
RawMapsAboutAttributeSchema,
|
|
84
|
-
RawMapsHoursRowSchema,
|
|
85
|
-
RawMapsOverviewSchema,
|
|
86
|
-
RawMapsReviewStatsSchema,
|
|
87
|
-
browserServiceApiKey,
|
|
88
|
-
browserServiceProxyId,
|
|
89
84
|
buildYouTubeChannelVideosUrl,
|
|
90
85
|
deleteKernelProxyId,
|
|
91
86
|
harvest,
|
|
92
87
|
resolveKernelProxyId
|
|
93
|
-
} from "./chunk-
|
|
88
|
+
} from "./chunk-IPW4LFOT.js";
|
|
94
89
|
import {
|
|
95
90
|
CaptureSerpPageSnapshotsInputSchema,
|
|
91
|
+
CaptureSerpPageSnapshotsOutputSchema,
|
|
96
92
|
CaptureSerpSnapshotInputSchema,
|
|
93
|
+
CaptureSerpSnapshotOutputSchema,
|
|
97
94
|
HttpMcpToolExecutor,
|
|
98
95
|
buildPaaExtractorMcpServer,
|
|
99
96
|
configureReportSaving,
|
|
97
|
+
formatCaptureSerpPageSnapshots,
|
|
98
|
+
formatCaptureSerpSnapshot,
|
|
100
99
|
harvestTimeoutBudget,
|
|
101
100
|
liveWebToolAnnotations,
|
|
102
101
|
outputBaseDir
|
|
103
|
-
} from "./chunk-
|
|
102
|
+
} from "./chunk-O5J7E4VX.js";
|
|
104
103
|
import {
|
|
105
104
|
CaptchaError,
|
|
106
105
|
RECAPTCHA_INSTRUCTIONS,
|
|
107
106
|
sanitizeVendorName
|
|
108
107
|
} from "./chunk-M2S27J6Z.js";
|
|
108
|
+
import {
|
|
109
|
+
browserServiceApiKey,
|
|
110
|
+
browserServiceProfileName,
|
|
111
|
+
browserServiceProfileSaveChanges,
|
|
112
|
+
browserServiceProxyId
|
|
113
|
+
} from "./chunk-WN7PBKMV.js";
|
|
109
114
|
import {
|
|
110
115
|
csvRecords,
|
|
111
116
|
listWorkflowDefinitions,
|
|
112
117
|
rowsToCsv,
|
|
113
118
|
runWorkflow,
|
|
119
|
+
runWorkflowStep,
|
|
114
120
|
workflowDefinition,
|
|
115
|
-
workflowOutputBaseDir
|
|
116
|
-
|
|
117
|
-
|
|
121
|
+
workflowOutputBaseDir,
|
|
122
|
+
workflowStepCount,
|
|
123
|
+
workflowSupportsSteps
|
|
124
|
+
} from "./chunk-H2R232HK.js";
|
|
125
|
+
import {
|
|
126
|
+
DEFAULT_MAPS_PROXY_MODE,
|
|
127
|
+
DEFAULT_PROXY_MODE,
|
|
128
|
+
MapsPlaceOptionsSchema,
|
|
129
|
+
MapsSearchOptionsSchema,
|
|
130
|
+
RawMapsAboutAttributeSchema,
|
|
131
|
+
RawMapsHoursRowSchema,
|
|
132
|
+
RawMapsOverviewSchema,
|
|
133
|
+
RawMapsReviewStatsSchema
|
|
134
|
+
} from "./chunk-LFATOGDF.js";
|
|
135
|
+
import "./chunk-XKUDVN2E.js";
|
|
118
136
|
|
|
119
137
|
// src/api/outbound-sanitize.ts
|
|
120
138
|
var KEY_RENAMES = {
|
|
@@ -2463,6 +2481,10 @@ var posts = [
|
|
|
2463
2481
|
}
|
|
2464
2482
|
];
|
|
2465
2483
|
|
|
2484
|
+
// src/attribution-pixel.ts
|
|
2485
|
+
var ATTRIBUTION_PIXEL_SRC = "https://hyros-attribution-crm.vercel.app/api/track/sdk?token=35c27ba33dd0c6cc640f9fef415ee3dc9ead67fefe99d269be602ee269900e25";
|
|
2486
|
+
var ATTRIBUTION_PIXEL_SCRIPT = `<script src="${ATTRIBUTION_PIXEL_SRC}" async></script>`;
|
|
2487
|
+
|
|
2466
2488
|
// src/blog/template.ts
|
|
2467
2489
|
function faqJsonLd(post) {
|
|
2468
2490
|
const firstFive = post.sections.flatMap((s) => s.cards).slice(0, 5);
|
|
@@ -3257,6 +3279,7 @@ function renderPost(post) {
|
|
|
3257
3279
|
<script type="application/ld+json">${faqJsonLd(post)}</script>
|
|
3258
3280
|
<script type="application/ld+json">${breadcrumbJsonLd(post)}</script>
|
|
3259
3281
|
|
|
3282
|
+
${ATTRIBUTION_PIXEL_SCRIPT}
|
|
3260
3283
|
<style>${CSS}</style>
|
|
3261
3284
|
</head>
|
|
3262
3285
|
<body>
|
|
@@ -7245,6 +7268,7 @@ var YouTubeHarvestOptionsSchema = z10.object({
|
|
|
7245
7268
|
downloadMp3: z10.boolean().default(false),
|
|
7246
7269
|
outputDir: z10.string().default("./yt-output"),
|
|
7247
7270
|
kernelApiKey: z10.string().optional(),
|
|
7271
|
+
kernelProxyId: z10.string().optional(),
|
|
7248
7272
|
headless: z10.boolean().default(false)
|
|
7249
7273
|
}).refine(
|
|
7250
7274
|
(o) => o.mode === "search" ? !!o.query : !!o.channelHandle,
|
|
@@ -7498,6 +7522,7 @@ var YouTubeExtractor = class {
|
|
|
7498
7522
|
const config = {
|
|
7499
7523
|
headless: options.headless,
|
|
7500
7524
|
kernelApiKey: options.kernelApiKey,
|
|
7525
|
+
kernelProxyId: options.kernelProxyId,
|
|
7501
7526
|
viewport: { width: 1280, height: 800 },
|
|
7502
7527
|
locale: "en-US"
|
|
7503
7528
|
};
|
|
@@ -7697,7 +7722,11 @@ async function ytHarvest(rawOptions) {
|
|
|
7697
7722
|
throw new Error("A browser backend API key is required \u2014 YouTube harvesting requires a stealth session.");
|
|
7698
7723
|
}
|
|
7699
7724
|
const raw = typeof rawOptions === "object" && rawOptions !== null ? rawOptions : {};
|
|
7700
|
-
const options = YouTubeHarvestOptionsSchema.parse({
|
|
7725
|
+
const options = YouTubeHarvestOptionsSchema.parse({
|
|
7726
|
+
kernelApiKey,
|
|
7727
|
+
kernelProxyId: browserServiceProxyId(),
|
|
7728
|
+
...raw
|
|
7729
|
+
});
|
|
7701
7730
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
7702
7731
|
try {
|
|
7703
7732
|
const result = await extractOnce(options);
|
|
@@ -7789,10 +7818,11 @@ function parseTimedtextXml(xml) {
|
|
|
7789
7818
|
async function fetchViaKernelInnertube(videoId) {
|
|
7790
7819
|
const kernelApiKey = browserServiceApiKey();
|
|
7791
7820
|
if (!kernelApiKey) return null;
|
|
7821
|
+
const kernelProxyId = browserServiceProxyId();
|
|
7792
7822
|
const driver = new BrowserDriver();
|
|
7793
7823
|
const start = Date.now();
|
|
7794
7824
|
try {
|
|
7795
|
-
await driver.launch({ kernelApiKey, headless: true, viewport: { width: 1280, height: 800 }, locale: "en-US" });
|
|
7825
|
+
await driver.launch({ kernelApiKey, kernelProxyId, headless: true, viewport: { width: 1280, height: 800 }, locale: "en-US" });
|
|
7796
7826
|
const page = driver.getPage();
|
|
7797
7827
|
await driver.navigateTo(`https://www.youtube.com/watch?v=${videoId}`);
|
|
7798
7828
|
await page.waitForFunction(
|
|
@@ -7887,10 +7917,10 @@ async function captureAudioViaMediaRecorder(page) {
|
|
|
7887
7917
|
if (!result.ok || !result.base64 || !result.mimeType) return null;
|
|
7888
7918
|
return { bytes: Buffer.from(result.base64, "base64"), mimeType: result.mimeType };
|
|
7889
7919
|
}
|
|
7890
|
-
async function attemptKernelWhisper(videoId, kernelApiKey, falKey, start) {
|
|
7920
|
+
async function attemptKernelWhisper(videoId, kernelApiKey, kernelProxyId, falKey, start) {
|
|
7891
7921
|
const driver = new BrowserDriver();
|
|
7892
7922
|
try {
|
|
7893
|
-
await driver.launch({ kernelApiKey, headless: true, viewport: { width: 1280, height: 800 }, locale: "en-US" });
|
|
7923
|
+
await driver.launch({ kernelApiKey, kernelProxyId, headless: true, viewport: { width: 1280, height: 800 }, locale: "en-US" });
|
|
7894
7924
|
const page = driver.getPage();
|
|
7895
7925
|
await driver.navigateTo(`https://www.youtube.com/watch?v=${videoId}`);
|
|
7896
7926
|
await page.waitForFunction(
|
|
@@ -7932,12 +7962,13 @@ async function attemptKernelWhisper(videoId, kernelApiKey, falKey, start) {
|
|
|
7932
7962
|
}
|
|
7933
7963
|
async function fetchViaKernelWhisper(videoId) {
|
|
7934
7964
|
const kernelApiKey = browserServiceApiKey();
|
|
7965
|
+
const kernelProxyId = browserServiceProxyId();
|
|
7935
7966
|
const falKey = process.env.FAL_KEY;
|
|
7936
7967
|
if (!kernelApiKey || !falKey) return null;
|
|
7937
7968
|
const start = Date.now();
|
|
7938
7969
|
for (let attempt = 0; attempt < 4; attempt++) {
|
|
7939
7970
|
if (attempt > 0) await new Promise((r) => setTimeout(r, 2e3 * attempt));
|
|
7940
|
-
const result = await attemptKernelWhisper(videoId, kernelApiKey, falKey, start);
|
|
7971
|
+
const result = await attemptKernelWhisper(videoId, kernelApiKey, kernelProxyId, falKey, start);
|
|
7941
7972
|
if (result) return result;
|
|
7942
7973
|
}
|
|
7943
7974
|
return null;
|
|
@@ -8341,6 +8372,7 @@ function buildTranscriptHtml(result) {
|
|
|
8341
8372
|
(ch) => `<div class="chunk"><span class="ts">${fmtSecs(ch.timestamp[0])} \u2192 ${fmtSecs(ch.timestamp[1])}</span><span class="ct">${esc(ch.text.trim())}</span></div>`
|
|
8342
8373
|
).join("\n");
|
|
8343
8374
|
return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
8375
|
+
${ATTRIBUTION_PIXEL_SCRIPT}
|
|
8344
8376
|
<style>body{font-family:system-ui,sans-serif;max-width:800px;margin:1.5rem auto;padding:0 1rem;color:#111;line-height:1.7}
|
|
8345
8377
|
h1{font-size:1.2rem;margin-bottom:.25rem}.meta{color:#666;font-size:.85rem;margin-bottom:1.5rem}
|
|
8346
8378
|
.full-text{background:#f5f5f3;border-radius:8px;padding:1rem 1.25rem;font-size:.875rem;white-space:pre-wrap;margin-bottom:2rem;word-break:break-word}
|
|
@@ -9396,9 +9428,8 @@ async function kernelLaunchOptsResidential() {
|
|
|
9396
9428
|
try {
|
|
9397
9429
|
const resolution = await resolveKernelProxyId({
|
|
9398
9430
|
kernelApiKey: browserServiceApiKey(),
|
|
9399
|
-
proxyMode:
|
|
9431
|
+
proxyMode: DEFAULT_PROXY_MODE,
|
|
9400
9432
|
configuredKernelProxyId: browserServiceProxyId(),
|
|
9401
|
-
location: "New York, NY",
|
|
9402
9433
|
gl: "us"
|
|
9403
9434
|
});
|
|
9404
9435
|
if (resolution.kernelProxyId) proxyId = resolution.kernelProxyId;
|
|
@@ -10845,7 +10876,7 @@ var DirectoryWorkflowOptionsSchema = z15.object({
|
|
|
10845
10876
|
saveCsv: z15.boolean().default(true),
|
|
10846
10877
|
gl: z15.string().length(2).default("us"),
|
|
10847
10878
|
hl: z15.string().length(2).default("en"),
|
|
10848
|
-
proxyMode: z15.enum(["location", "configured", "none"]).default(
|
|
10879
|
+
proxyMode: z15.enum(["location", "configured", "none"]).default(DEFAULT_MAPS_PROXY_MODE),
|
|
10849
10880
|
proxyZip: z15.string().regex(/^\d{5}$/).optional(),
|
|
10850
10881
|
debug: z15.boolean().default(false),
|
|
10851
10882
|
headless: z15.boolean().default(true),
|
|
@@ -11302,6 +11333,88 @@ async function executeWorkflowRun(input) {
|
|
|
11302
11333
|
throw err;
|
|
11303
11334
|
}
|
|
11304
11335
|
}
|
|
11336
|
+
async function runWorkflowStepAndPersist(input) {
|
|
11337
|
+
const runId = input.run.id;
|
|
11338
|
+
const workflowId = input.run.workflow_id;
|
|
11339
|
+
await markWorkflowRunRunning(runId);
|
|
11340
|
+
let result;
|
|
11341
|
+
try {
|
|
11342
|
+
result = await runWorkflowStep(workflowId, input.workflowInput, {
|
|
11343
|
+
stepIndex: input.stepIndex,
|
|
11344
|
+
state: input.priorState ?? void 0,
|
|
11345
|
+
options: {
|
|
11346
|
+
apiKey: input.user.api_key,
|
|
11347
|
+
apiUrl: input.apiUrl,
|
|
11348
|
+
outputDir: hostedWorkflowOutputDir(),
|
|
11349
|
+
runId,
|
|
11350
|
+
headers: input.concurrencyLockId ? { "x-mcp-scraper-concurrency-lock": input.concurrencyLockId } : void 0
|
|
11351
|
+
}
|
|
11352
|
+
});
|
|
11353
|
+
} catch (err) {
|
|
11354
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
11355
|
+
await failWorkflowRunRecord(runId, message, { error: message });
|
|
11356
|
+
const failedRun = await getWorkflowRun(runId, input.user.id);
|
|
11357
|
+
if (failedRun) await deliverWorkflowWebhook({ userId: input.user.id, run: failedRun, scheduleId: failedRun.schedule_id, webhookUrl: input.webhookUrl ?? null, summary: null, artifacts: [] });
|
|
11358
|
+
throw err;
|
|
11359
|
+
}
|
|
11360
|
+
if (result.artifacts.length) {
|
|
11361
|
+
await appendWorkflowArtifacts(runId, input.user.id, result.artifacts.map((a) => ({
|
|
11362
|
+
kind: a.kind,
|
|
11363
|
+
label: a.label,
|
|
11364
|
+
path: a.path,
|
|
11365
|
+
bytes: a.bytes,
|
|
11366
|
+
rows: a.rows,
|
|
11367
|
+
content: a.content
|
|
11368
|
+
})));
|
|
11369
|
+
}
|
|
11370
|
+
const logEntry = {
|
|
11371
|
+
stepIndex: result.stepIndex,
|
|
11372
|
+
stepId: result.stepId,
|
|
11373
|
+
stepTitle: result.stepTitle,
|
|
11374
|
+
output: result.output,
|
|
11375
|
+
warnings: result.warnings,
|
|
11376
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
11377
|
+
};
|
|
11378
|
+
const status = result.isLast ? result.summary?.status ?? "succeeded" : "running";
|
|
11379
|
+
await advanceWorkflowRunStep(runId, {
|
|
11380
|
+
stepIndex: result.stepIndex + 1,
|
|
11381
|
+
totalSteps: result.totalSteps,
|
|
11382
|
+
state: result.state,
|
|
11383
|
+
status,
|
|
11384
|
+
logEntry,
|
|
11385
|
+
appendLog: input.priorLog
|
|
11386
|
+
});
|
|
11387
|
+
if (result.isLast && result.summary) {
|
|
11388
|
+
const manifest = await readManifestFromSummary(result.summary);
|
|
11389
|
+
await completeWorkflowRunRecord(runId, result.summary.status, manifest ?? { summary: result.summary });
|
|
11390
|
+
}
|
|
11391
|
+
const run = await getWorkflowRun(runId, input.user.id);
|
|
11392
|
+
const artifacts = await listWorkflowArtifacts(runId, input.user.id);
|
|
11393
|
+
if (result.isLast && run) {
|
|
11394
|
+
await deliverWorkflowWebhook({ userId: input.user.id, run, scheduleId: run.schedule_id, webhookUrl: input.webhookUrl ?? null, summary: result.summary, artifacts });
|
|
11395
|
+
}
|
|
11396
|
+
const definition = workflowDefinition(workflowId);
|
|
11397
|
+
const nextIndex = result.stepIndex + 1;
|
|
11398
|
+
const nextStep = result.isLast ? null : {
|
|
11399
|
+
index: nextIndex,
|
|
11400
|
+
id: definition.steps?.[nextIndex]?.id ?? null,
|
|
11401
|
+
title: definition.steps?.[nextIndex]?.title ?? null
|
|
11402
|
+
};
|
|
11403
|
+
return {
|
|
11404
|
+
run: run ? exposeRun(input.c, run, artifacts) : null,
|
|
11405
|
+
summary: result.isLast ? result.summary : null,
|
|
11406
|
+
step: {
|
|
11407
|
+
index: result.stepIndex,
|
|
11408
|
+
id: result.stepId,
|
|
11409
|
+
title: result.stepTitle,
|
|
11410
|
+
totalSteps: result.totalSteps,
|
|
11411
|
+
output: result.output,
|
|
11412
|
+
warnings: result.warnings
|
|
11413
|
+
},
|
|
11414
|
+
nextStep,
|
|
11415
|
+
done: result.isLast
|
|
11416
|
+
};
|
|
11417
|
+
}
|
|
11305
11418
|
workflowApp.get("/definitions", createApiKeyAuth(), (c) => {
|
|
11306
11419
|
return c.json({ workflows: listWorkflowDefinitions() });
|
|
11307
11420
|
});
|
|
@@ -11324,7 +11437,29 @@ workflowApp.post("/run", createApiKeyAuth(), async (c) => {
|
|
|
11324
11437
|
if (!gate.ok) return c.json(concurrencyLimitExceededResponse(gate), 429, { "Retry-After": String(gate.retryAfterSeconds) });
|
|
11325
11438
|
let run = null;
|
|
11326
11439
|
try {
|
|
11327
|
-
|
|
11440
|
+
const stepwise = workflowSupportsSteps(parsed.data.workflowId);
|
|
11441
|
+
run = await createWorkflowRun({
|
|
11442
|
+
userId: user.id,
|
|
11443
|
+
workflowId: parsed.data.workflowId,
|
|
11444
|
+
workflowInput: input,
|
|
11445
|
+
webhookUrl,
|
|
11446
|
+
totalSteps: stepwise ? workflowStepCount(parsed.data.workflowId) : 1
|
|
11447
|
+
});
|
|
11448
|
+
if (stepwise) {
|
|
11449
|
+
const response = await runWorkflowStepAndPersist({
|
|
11450
|
+
c,
|
|
11451
|
+
user,
|
|
11452
|
+
run,
|
|
11453
|
+
workflowInput: input,
|
|
11454
|
+
stepIndex: 0,
|
|
11455
|
+
priorState: null,
|
|
11456
|
+
priorLog: [],
|
|
11457
|
+
apiUrl: originFromUrl(c.req.url),
|
|
11458
|
+
webhookUrl,
|
|
11459
|
+
concurrencyLockId: gate.lockId
|
|
11460
|
+
});
|
|
11461
|
+
return c.json(response);
|
|
11462
|
+
}
|
|
11328
11463
|
const executed = await executeWorkflowRun({
|
|
11329
11464
|
runId: run.id,
|
|
11330
11465
|
user,
|
|
@@ -11334,7 +11469,7 @@ workflowApp.post("/run", createApiKeyAuth(), async (c) => {
|
|
|
11334
11469
|
webhookUrl,
|
|
11335
11470
|
concurrencyLockId: gate.lockId
|
|
11336
11471
|
});
|
|
11337
|
-
return c.json({ run: exposeRun(c, executed.run, executed.artifacts), summary: executed.summary });
|
|
11472
|
+
return c.json({ run: exposeRun(c, executed.run, executed.artifacts), summary: executed.summary, done: true });
|
|
11338
11473
|
} catch (err) {
|
|
11339
11474
|
const failed = run ? await getWorkflowRun(run.id, user.id) : null;
|
|
11340
11475
|
return c.json({ run: failed, error: err instanceof Error ? err.message : String(err) }, 500);
|
|
@@ -11342,6 +11477,50 @@ workflowApp.post("/run", createApiKeyAuth(), async (c) => {
|
|
|
11342
11477
|
await releaseConcurrencyGate(gate.lockId);
|
|
11343
11478
|
}
|
|
11344
11479
|
});
|
|
11480
|
+
var TERMINAL_RUN_STATUSES = ["succeeded", "partial", "failed", "cancelled"];
|
|
11481
|
+
workflowApp.post("/runs/:id/step", createApiKeyAuth(), async (c) => {
|
|
11482
|
+
const user = c.get("user");
|
|
11483
|
+
const run = await getWorkflowRun(c.req.param("id"), user.id);
|
|
11484
|
+
if (!run) return c.json({ error: "Run not found" }, 404);
|
|
11485
|
+
if (!workflowSupportsSteps(run.workflow_id)) {
|
|
11486
|
+
return c.json({ error: `Workflow "${run.workflow_id}" does not support stepwise execution` }, 400);
|
|
11487
|
+
}
|
|
11488
|
+
const artifactsNow = await listWorkflowArtifacts(run.id, user.id);
|
|
11489
|
+
if (TERMINAL_RUN_STATUSES.includes(run.status)) {
|
|
11490
|
+
return c.json({ run: exposeRun(c, run, artifactsNow), done: true, error: `Run is already ${run.status}` }, 409);
|
|
11491
|
+
}
|
|
11492
|
+
const stepState = await getWorkflowRunStepState(run.id, user.id);
|
|
11493
|
+
const stepIndex = stepState?.stepIndex ?? run.step_index;
|
|
11494
|
+
const totalSteps = workflowStepCount(run.workflow_id);
|
|
11495
|
+
if (stepIndex >= totalSteps) {
|
|
11496
|
+
return c.json({ run: exposeRun(c, run, artifactsNow), done: true, error: "No remaining steps" }, 409);
|
|
11497
|
+
}
|
|
11498
|
+
const gate = await acquireConcurrencyGate(user, "workflow_run", {
|
|
11499
|
+
ttlSeconds: 60 * 60,
|
|
11500
|
+
metadata: { workflowId: run.workflow_id, step: stepIndex }
|
|
11501
|
+
});
|
|
11502
|
+
if (!gate.ok) return c.json(concurrencyLimitExceededResponse(gate), 429, { "Retry-After": String(gate.retryAfterSeconds) });
|
|
11503
|
+
try {
|
|
11504
|
+
const response = await runWorkflowStepAndPersist({
|
|
11505
|
+
c,
|
|
11506
|
+
user,
|
|
11507
|
+
run,
|
|
11508
|
+
workflowInput: run.input,
|
|
11509
|
+
stepIndex,
|
|
11510
|
+
priorState: stepState?.state ?? null,
|
|
11511
|
+
priorLog: stepState?.stepLog ?? run.step_log,
|
|
11512
|
+
apiUrl: originFromUrl(c.req.url),
|
|
11513
|
+
webhookUrl: run.webhook_url,
|
|
11514
|
+
concurrencyLockId: gate.lockId
|
|
11515
|
+
});
|
|
11516
|
+
return c.json(response);
|
|
11517
|
+
} catch (err) {
|
|
11518
|
+
const failed = await getWorkflowRun(run.id, user.id);
|
|
11519
|
+
return c.json({ run: failed, error: err instanceof Error ? err.message : String(err) }, 500);
|
|
11520
|
+
} finally {
|
|
11521
|
+
await releaseConcurrencyGate(gate.lockId);
|
|
11522
|
+
}
|
|
11523
|
+
});
|
|
11345
11524
|
workflowApp.get("/runs", createApiKeyAuth(), async (c) => {
|
|
11346
11525
|
const user = c.get("user");
|
|
11347
11526
|
const runs = await listWorkflowRuns(user.id);
|
|
@@ -11360,14 +11539,17 @@ workflowApp.get("/runs/:id/artifacts/:artifactId", createApiKeyAuth(), async (c)
|
|
|
11360
11539
|
if (!run) return c.json({ error: "Run not found" }, 404);
|
|
11361
11540
|
const artifact = await getWorkflowArtifact(c.req.param("artifactId"), user.id);
|
|
11362
11541
|
if (!artifact || artifact.run_id !== run.id) return c.json({ error: "Artifact not found" }, 404);
|
|
11542
|
+
const filename = artifact.path.split("/").pop() || artifact.id;
|
|
11543
|
+
const headers = {
|
|
11544
|
+
"Content-Type": artifact.content_type,
|
|
11545
|
+
"Content-Disposition": `attachment; filename="${filename}"`
|
|
11546
|
+
};
|
|
11547
|
+
if (artifact.content != null) {
|
|
11548
|
+
return new Response(artifact.content, { headers });
|
|
11549
|
+
}
|
|
11363
11550
|
try {
|
|
11364
11551
|
const content = await readFile2(artifact.path);
|
|
11365
|
-
return new Response(content, {
|
|
11366
|
-
headers: {
|
|
11367
|
-
"Content-Type": artifact.content_type,
|
|
11368
|
-
"Content-Disposition": `attachment; filename="${artifact.path.split("/").pop() || artifact.id}"`
|
|
11369
|
-
}
|
|
11370
|
-
});
|
|
11552
|
+
return new Response(content, { headers });
|
|
11371
11553
|
} catch {
|
|
11372
11554
|
return c.json({ error: "Artifact file is no longer available" }, 410);
|
|
11373
11555
|
}
|
|
@@ -11905,7 +12087,7 @@ var SerpIntelligenceCaptureBodySchema = z17.object({
|
|
|
11905
12087
|
gl: z17.string().trim().length(2).default("us"),
|
|
11906
12088
|
hl: z17.string().trim().length(2).default("en"),
|
|
11907
12089
|
device: z17.enum(SerpIntelligenceDeviceValues).default("desktop"),
|
|
11908
|
-
proxyMode: z17.enum(SerpIntelligenceProxyModeValues).default(
|
|
12090
|
+
proxyMode: z17.enum(SerpIntelligenceProxyModeValues).default(DEFAULT_PROXY_MODE),
|
|
11909
12091
|
proxyZip: z17.string().regex(/^\d{5}$/).optional(),
|
|
11910
12092
|
pages: z17.number().int().min(1).max(2).default(1),
|
|
11911
12093
|
debug: z17.boolean().default(false),
|
|
@@ -12437,16 +12619,18 @@ var mcpApp = new Hono9();
|
|
|
12437
12619
|
function registerSerpIntelligenceCaptureTools(server, executor) {
|
|
12438
12620
|
server.registerTool("capture_serp_snapshot", {
|
|
12439
12621
|
title: "SERP Intelligence Snapshot",
|
|
12440
|
-
description: "Capture a structured SERP Intelligence Google snapshot through POST /serp-intelligence/capture, the same product capture path used by Phoenix. Split query from location
|
|
12622
|
+
description: "Capture a structured SERP Intelligence Google snapshot through POST /serp-intelligence/capture, the same product capture path used by Phoenix. Split query from location and infer gl/hl. Omit proxyMode for normal use; the service defaults to the configured browser-service proxy without city/ZIP targeting for the highest general success rate. Use proxyMode location only when the user explicitly needs city/ZIP-targeted residential proxy evidence. Use none only for direct-network debugging. Set debug true when investigating location evidence, proxy behavior, CAPTCHA, or capture reliability.",
|
|
12441
12623
|
inputSchema: CaptureSerpSnapshotInputSchema,
|
|
12624
|
+
outputSchema: CaptureSerpSnapshotOutputSchema,
|
|
12442
12625
|
annotations: liveWebToolAnnotations("SERP Intelligence Snapshot")
|
|
12443
|
-
}, async (input) => executor.captureSerpSnapshot(input));
|
|
12626
|
+
}, async (input) => formatCaptureSerpSnapshot(await executor.captureSerpSnapshot(input), input));
|
|
12444
12627
|
server.registerTool("capture_serp_page_snapshots", {
|
|
12445
12628
|
title: "SERP Intelligence Page Snapshots",
|
|
12446
12629
|
description: "Capture public ranking-page evidence through POST /serp-intelligence/page-snapshots, the same product page snapshot path used by Phoenix. Provide urls for simple captures or targets when preserving organic, AI citation, local-pack, configured target, or site-subject source metadata. Private IPs, localhost, file URLs, and internal URLs are rejected by the service. Use timeoutMs for slow pages and debug true for sanitized proxy/browser diagnostics.",
|
|
12447
12630
|
inputSchema: CaptureSerpPageSnapshotsInputSchema,
|
|
12631
|
+
outputSchema: CaptureSerpPageSnapshotsOutputSchema,
|
|
12448
12632
|
annotations: liveWebToolAnnotations("SERP Intelligence Page Snapshots")
|
|
12449
|
-
}, async (input) => executor.captureSerpPageSnapshots(input));
|
|
12633
|
+
}, async (input) => formatCaptureSerpPageSnapshots(await executor.captureSerpPageSnapshots(input), input));
|
|
12450
12634
|
}
|
|
12451
12635
|
mcpApp.all("/", async (c) => {
|
|
12452
12636
|
try {
|
|
@@ -12639,14 +12823,35 @@ function client() {
|
|
|
12639
12823
|
if (!apiKey) throw new Error("Browser backend API key is required");
|
|
12640
12824
|
return new Kernel3({ apiKey });
|
|
12641
12825
|
}
|
|
12826
|
+
function isProfileConflict(err) {
|
|
12827
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
12828
|
+
return /\b409\b|conflict|already exists/i.test(message);
|
|
12829
|
+
}
|
|
12830
|
+
async function ensureProfile(k, name) {
|
|
12831
|
+
try {
|
|
12832
|
+
await k.profiles.create({ name });
|
|
12833
|
+
} catch (err) {
|
|
12834
|
+
if (!isProfileConflict(err)) throw err;
|
|
12835
|
+
}
|
|
12836
|
+
}
|
|
12642
12837
|
async function createSession(opts = {}) {
|
|
12643
12838
|
const k = client();
|
|
12644
12839
|
const resolvedProxyId = opts.proxyId ?? browserServiceProxyId();
|
|
12840
|
+
const resolvedProfileName = opts.profileName ?? browserServiceProfileName();
|
|
12841
|
+
const resolvedSaveProfileChanges = opts.saveProfileChanges ?? browserServiceProfileSaveChanges();
|
|
12842
|
+
if (resolvedProfileName && resolvedSaveProfileChanges === true) {
|
|
12843
|
+
await ensureProfile(k, resolvedProfileName);
|
|
12844
|
+
}
|
|
12645
12845
|
const browser = await k.browsers.create({
|
|
12646
12846
|
stealth: opts.stealth ?? true,
|
|
12647
12847
|
timeout_seconds: opts.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS,
|
|
12648
12848
|
...resolvedProxyId ? { proxy_id: resolvedProxyId } : {},
|
|
12649
|
-
...
|
|
12849
|
+
...resolvedProfileName ? {
|
|
12850
|
+
profile: {
|
|
12851
|
+
name: resolvedProfileName,
|
|
12852
|
+
...typeof resolvedSaveProfileChanges === "boolean" ? { save_changes: resolvedSaveProfileChanges } : {}
|
|
12853
|
+
}
|
|
12854
|
+
} : {}
|
|
12650
12855
|
});
|
|
12651
12856
|
const runtimeSessionId = browser.session_id;
|
|
12652
12857
|
if (opts.disableDefaultProxy) {
|
|
@@ -13029,6 +13234,7 @@ function buildBrowserAgentRoutes() {
|
|
|
13029
13234
|
timeoutSeconds,
|
|
13030
13235
|
proxyId: typeof body.proxy_id === "string" ? body.proxy_id : void 0,
|
|
13031
13236
|
profileName: typeof body.profile === "string" ? body.profile : void 0,
|
|
13237
|
+
saveProfileChanges: typeof body.save_profile_changes === "boolean" ? body.save_profile_changes : void 0,
|
|
13032
13238
|
disableDefaultProxy: body.disable_default_proxy === true,
|
|
13033
13239
|
viewport: body.viewport && typeof body.viewport === "object" ? body.viewport : void 0
|
|
13034
13240
|
});
|
|
@@ -13388,6 +13594,7 @@ function renderConsoleHtml(initialSessionId) {
|
|
|
13388
13594
|
.gate button { width: 100%; }
|
|
13389
13595
|
.muted { color: #6b7689; font-size: 12px; }
|
|
13390
13596
|
</style>
|
|
13597
|
+
${ATTRIBUTION_PIXEL_SCRIPT}
|
|
13391
13598
|
</head>
|
|
13392
13599
|
<body>
|
|
13393
13600
|
<div id="app"></div>
|
|
@@ -14103,7 +14310,7 @@ app.post("/harvest", auth2, async (c) => {
|
|
|
14103
14310
|
gl: body.gl ?? "us",
|
|
14104
14311
|
hl: body.hl ?? "en",
|
|
14105
14312
|
device: body.device ?? "desktop",
|
|
14106
|
-
proxyMode: body.proxyMode ??
|
|
14313
|
+
proxyMode: body.proxyMode ?? DEFAULT_PROXY_MODE,
|
|
14107
14314
|
proxyZip: body.proxyZip,
|
|
14108
14315
|
debug: body.debug ?? false,
|
|
14109
14316
|
serpOnly: body.serpOnly ?? false,
|
|
@@ -14139,7 +14346,7 @@ app.post("/harvest/sync", auth2, async (c) => {
|
|
|
14139
14346
|
gl: body.gl ?? "us",
|
|
14140
14347
|
hl: body.hl ?? "en",
|
|
14141
14348
|
device: body.device ?? "desktop",
|
|
14142
|
-
proxyMode: body.proxyMode ??
|
|
14349
|
+
proxyMode: body.proxyMode ?? DEFAULT_PROXY_MODE,
|
|
14143
14350
|
proxyZip: body.proxyZip,
|
|
14144
14351
|
debug: body.debug ?? false,
|
|
14145
14352
|
serpOnly: body.serpOnly ?? false,
|
|
@@ -14605,7 +14812,7 @@ app.get("/cron/tick", async (c) => {
|
|
|
14605
14812
|
if (!process.env.CRON_SECRET || secret2 !== `Bearer ${process.env.CRON_SECRET}`) {
|
|
14606
14813
|
return c.json({ error: "Unauthorized" }, 401);
|
|
14607
14814
|
}
|
|
14608
|
-
const { drainQueue } = await import("./worker-
|
|
14815
|
+
const { drainQueue } = await import("./worker-SLQ375UG.js");
|
|
14609
14816
|
const budget = { maxJobs: 10, deadlineMs: Date.now() + 28e4 };
|
|
14610
14817
|
const workflowDispatchResult = await dispatchDueWorkflowSchedules(`${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`);
|
|
14611
14818
|
const [results, sweepResult] = await Promise.all([
|
|
@@ -14684,6 +14891,7 @@ app.get("/font-test", (c) => {
|
|
|
14684
14891
|
.i6 em{font-family:"GTF";font-weight:700;font-style:italic}
|
|
14685
14892
|
.i7 em{font-family:"GTF";font-weight:900;font-style:italic}
|
|
14686
14893
|
</style>
|
|
14894
|
+
${ATTRIBUTION_PIXEL_SCRIPT}
|
|
14687
14895
|
</head>
|
|
14688
14896
|
<body>
|
|
14689
14897
|
|
|
@@ -14733,4 +14941,4 @@ app.get("/blog/:slug/", (c) => {
|
|
|
14733
14941
|
export {
|
|
14734
14942
|
app
|
|
14735
14943
|
};
|
|
14736
|
-
//# sourceMappingURL=server-
|
|
14944
|
+
//# sourceMappingURL=server-24XIQ6W6.js.map
|