mcp-scraper 0.3.11 → 0.3.13
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 +2 -2
- package/dist/bin/api-server.cjs +98 -24
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +2 -2
- package/dist/bin/browser-agent-stdio-server.cjs +198 -27
- package/dist/bin/browser-agent-stdio-server.cjs.map +1 -1
- package/dist/bin/browser-agent-stdio-server.js +3 -2
- package/dist/bin/browser-agent-stdio-server.js.map +1 -1
- 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-combined-stdio-server.cjs +275 -68
- package/dist/bin/mcp-scraper-combined-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-scraper-combined-stdio-server.js +4 -3
- package/dist/bin/mcp-scraper-combined-stdio-server.js.map +1 -1
- package/dist/bin/mcp-scraper-install.cjs +1 -1
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.cjs +62 -13
- 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 +21 -1
- package/dist/bin/paa-harvest.cjs.map +1 -1
- package/dist/bin/paa-harvest.js +1 -1
- package/dist/chunk-3YGKXXUG.js +133 -0
- package/dist/chunk-3YGKXXUG.js.map +1 -0
- package/dist/{chunk-UWSG3C5J.js → chunk-4OPKIDON.js} +22 -2
- package/dist/chunk-4OPKIDON.js.map +1 -0
- package/dist/chunk-BEZJ4G32.js +7 -0
- package/dist/chunk-BEZJ4G32.js.map +1 -0
- package/dist/{chunk-L27GJQV7.js → chunk-QAOIH5U2.js} +28 -5
- package/dist/chunk-QAOIH5U2.js.map +1 -0
- package/dist/{chunk-VMH7SRWY.js → chunk-YGV2XZDT.js} +54 -14
- package/dist/chunk-YGV2XZDT.js.map +1 -0
- package/dist/index.cjs +21 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{server-3SA5Q4OF.js → server-LX65PB7D.js} +42 -151
- package/dist/server-LX65PB7D.js.map +1 -0
- package/dist/{worker-56IXWOQU.js → worker-FG7ZWEGA.js} +2 -2
- package/docs/mcp-tool-craft-lint.generated.md +1 -1
- package/docs/mcp-tool-manifest.generated.json +1 -1
- package/package.json +1 -1
- package/dist/chunk-L27GJQV7.js.map +0 -1
- package/dist/chunk-RRE7WVHQ.js +0 -7
- package/dist/chunk-RRE7WVHQ.js.map +0 -1
- package/dist/chunk-UWSG3C5J.js.map +0 -1
- package/dist/chunk-VMH7SRWY.js.map +0 -1
- package/dist/server-3SA5Q4OF.js.map +0 -1
- /package/dist/{worker-56IXWOQU.js.map → worker-FG7ZWEGA.js.map} +0 -0
|
@@ -1,10 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
3
25
|
|
|
4
26
|
// bin/mcp-scraper-combined-stdio-server.ts
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var
|
|
27
|
+
var import_node_fs5 = require("fs");
|
|
28
|
+
var import_node_os5 = require("os");
|
|
29
|
+
var import_node_path6 = require("path");
|
|
8
30
|
var import_mcp3 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
9
31
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
10
32
|
|
|
@@ -22,6 +44,19 @@ function harvestTimeoutBudget(maxQuestions, serpOnly = false) {
|
|
|
22
44
|
return { serverMs, clientMs };
|
|
23
45
|
}
|
|
24
46
|
|
|
47
|
+
// src/lib/browser-service-env.ts
|
|
48
|
+
function browserServiceProfileName() {
|
|
49
|
+
const value = (process.env.BROWSER_AGENT_PROFILE_NAME ?? process.env.BROWSER_SERVICE_PROFILE_NAME ?? process.env.KERNEL_BROWSER_PROFILE_NAME ?? process.env.KERNEL_PROFILE_NAME)?.trim();
|
|
50
|
+
return value || void 0;
|
|
51
|
+
}
|
|
52
|
+
function browserServiceProfileSaveChanges() {
|
|
53
|
+
const value = (process.env.BROWSER_AGENT_PROFILE_SAVE_CHANGES ?? process.env.BROWSER_SERVICE_PROFILE_SAVE_CHANGES ?? process.env.KERNEL_BROWSER_PROFILE_SAVE_CHANGES ?? process.env.KERNEL_PROFILE_SAVE_CHANGES)?.trim().toLowerCase();
|
|
54
|
+
if (!value) return void 0;
|
|
55
|
+
if (["1", "true", "yes", "on"].includes(value)) return true;
|
|
56
|
+
if (["0", "false", "no", "off"].includes(value)) return false;
|
|
57
|
+
return void 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
25
60
|
// src/mcp/http-mcp-tool-executor.ts
|
|
26
61
|
function youtubeVideoIdFromUrl(url) {
|
|
27
62
|
if (!url) return null;
|
|
@@ -44,6 +79,38 @@ function youtubeVideoIdFromUrl(url) {
|
|
|
44
79
|
}
|
|
45
80
|
return null;
|
|
46
81
|
}
|
|
82
|
+
async function readResponseData(res) {
|
|
83
|
+
const text = await res.text();
|
|
84
|
+
if (!text.trim()) return null;
|
|
85
|
+
try {
|
|
86
|
+
return JSON.parse(text);
|
|
87
|
+
} catch {
|
|
88
|
+
return text;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function httpErrorPayload(path, res, data) {
|
|
92
|
+
const objectData = data && typeof data === "object" && !Array.isArray(data) ? data : null;
|
|
93
|
+
const bodyMessage = objectData ? objectData.message ?? objectData.error ?? objectData.error_code : data;
|
|
94
|
+
return {
|
|
95
|
+
...objectData ?? { body: data },
|
|
96
|
+
error: objectData?.error ?? objectData?.error_code ?? "mcp_http_error",
|
|
97
|
+
error_type: "http",
|
|
98
|
+
retryable: res.status === 429 || res.status >= 500,
|
|
99
|
+
status: res.status,
|
|
100
|
+
statusText: res.statusText,
|
|
101
|
+
path,
|
|
102
|
+
message: typeof bodyMessage === "string" && bodyMessage.trim() ? bodyMessage : `MCP Scraper HTTP ${res.status}${res.statusText ? ` ${res.statusText}` : ""} for ${path}`
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function withDefaultHostedProfile(input) {
|
|
106
|
+
const profile = input.profile?.trim() || browserServiceProfileName();
|
|
107
|
+
const saveProfileChanges = input.saveProfileChanges ?? browserServiceProfileSaveChanges();
|
|
108
|
+
return {
|
|
109
|
+
...input,
|
|
110
|
+
...profile ? { profile } : {},
|
|
111
|
+
...typeof saveProfileChanges === "boolean" ? { saveProfileChanges } : {}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
47
114
|
var HttpMcpToolExecutor = class {
|
|
48
115
|
baseUrl;
|
|
49
116
|
apiKey;
|
|
@@ -71,9 +138,9 @@ var HttpMcpToolExecutor = class {
|
|
|
71
138
|
body: JSON.stringify(body),
|
|
72
139
|
signal: AbortSignal.timeout(timeoutMs)
|
|
73
140
|
});
|
|
74
|
-
const data = await res
|
|
141
|
+
const data = await readResponseData(res);
|
|
75
142
|
if (!res.ok) {
|
|
76
|
-
return { content: [{ type: "text", text: JSON.stringify(data) }], isError: true };
|
|
143
|
+
return { content: [{ type: "text", text: JSON.stringify(httpErrorPayload(path, res, data)) }], isError: true };
|
|
77
144
|
}
|
|
78
145
|
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
79
146
|
} catch (err) {
|
|
@@ -106,9 +173,9 @@ var HttpMcpToolExecutor = class {
|
|
|
106
173
|
},
|
|
107
174
|
signal: AbortSignal.timeout(timeoutMs)
|
|
108
175
|
});
|
|
109
|
-
const data = await res
|
|
176
|
+
const data = await readResponseData(res);
|
|
110
177
|
if (!res.ok) {
|
|
111
|
-
return { content: [{ type: "text", text: JSON.stringify(data) }], isError: true };
|
|
178
|
+
return { content: [{ type: "text", text: JSON.stringify(httpErrorPayload(path, res, data)) }], isError: true };
|
|
112
179
|
}
|
|
113
180
|
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
114
181
|
} catch (err) {
|
|
@@ -198,10 +265,10 @@ var HttpMcpToolExecutor = class {
|
|
|
198
265
|
return this.call("/facebook/video-transcribe", input, this.httpTimeoutOverrideMs ?? 24e4);
|
|
199
266
|
}
|
|
200
267
|
instagramProfileContent(input) {
|
|
201
|
-
return this.call("/instagram/profile-content", input, this.httpTimeoutOverrideMs ?? 24e4);
|
|
268
|
+
return this.call("/instagram/profile-content", withDefaultHostedProfile(input), this.httpTimeoutOverrideMs ?? 24e4);
|
|
202
269
|
}
|
|
203
270
|
instagramMediaDownload(input) {
|
|
204
|
-
return this.call("/instagram/media-download", input, this.httpTimeoutOverrideMs ?? 3e5);
|
|
271
|
+
return this.call("/instagram/media-download", withDefaultHostedProfile(input), this.httpTimeoutOverrideMs ?? 3e5);
|
|
205
272
|
}
|
|
206
273
|
mapsPlaceIntel(input) {
|
|
207
274
|
return this.call("/maps/place", input);
|
|
@@ -252,25 +319,141 @@ var HttpMcpToolExecutor = class {
|
|
|
252
319
|
|
|
253
320
|
// src/mcp/browser-agent-mcp-server.ts
|
|
254
321
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
255
|
-
var
|
|
256
|
-
var
|
|
257
|
-
var
|
|
322
|
+
var import_node_fs2 = require("fs");
|
|
323
|
+
var import_node_os3 = require("os");
|
|
324
|
+
var import_node_path3 = require("path");
|
|
258
325
|
|
|
259
|
-
// src/
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
326
|
+
// src/services/fanout/export.ts
|
|
327
|
+
var import_node_fs = require("fs");
|
|
328
|
+
var import_node_os = require("os");
|
|
329
|
+
var import_node_path = require("path");
|
|
330
|
+
var import_papaparse = __toESM(require("papaparse"), 1);
|
|
331
|
+
function outputBaseDir() {
|
|
332
|
+
return process.env.MCP_SCRAPER_OUTPUT_DIR?.trim() || (0, import_node_path.join)((0, import_node_os.homedir)(), "Downloads", "mcp-scraper");
|
|
263
333
|
}
|
|
264
|
-
function
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
334
|
+
function safe(value) {
|
|
335
|
+
return value.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "capture";
|
|
336
|
+
}
|
|
337
|
+
function writeTable(path, rows, delimiter) {
|
|
338
|
+
(0, import_node_fs.writeFileSync)(path, import_papaparse.default.unparse(rows.length ? rows : [{}], { delimiter }));
|
|
339
|
+
}
|
|
340
|
+
function exportFanout(enriched) {
|
|
341
|
+
const stamp = safe(enriched.capturedAt.replace(/[:.]/g, "-"));
|
|
342
|
+
const outputDir = outputBaseDir();
|
|
343
|
+
const relativeDir = (0, import_node_path.join)("fanout", `${stamp}-${safe(enriched.platform)}`);
|
|
344
|
+
const dir = (0, import_node_path.join)(outputDir, relativeDir);
|
|
345
|
+
(0, import_node_fs.mkdirSync)(dir, { recursive: true });
|
|
346
|
+
const queryRows = enriched.queries.map((q, i) => ({ index: i + 1, query: q }));
|
|
347
|
+
const citationRows = enriched.aggregates.citationOrder.map((c) => {
|
|
348
|
+
const src = enriched.citedUrls.find((s) => s.url === c.url);
|
|
349
|
+
return { rank: c.rank, domain: c.domain, url: c.url, timesCited: c.timesCited, siteType: src?.siteType ?? "", title: src?.title ?? "" };
|
|
350
|
+
});
|
|
351
|
+
const sourceRows = enriched.browsedUrls.map((s) => ({ domain: s.domain, url: s.url, cited: s.cited, timesCited: s.timesCited, siteType: s.siteType, title: s.title, round: s.round ?? "" }));
|
|
352
|
+
const browsedOnlyRows = enriched.browsedOnly.map((s) => ({ domain: s.domain, url: s.url, cited: s.cited, timesCited: s.timesCited, siteType: s.siteType, title: s.title, round: s.round ?? "" }));
|
|
353
|
+
const snippetRows = enriched.snippets.map((s) => ({ domain: s.domain, url: s.url, title: s.title, text: s.text }));
|
|
354
|
+
const domainRows = enriched.aggregates.topSites.map((d) => ({ domain: d.domain, count: d.count, cited: d.cited, timesCited: d.timesCited, siteType: d.siteType }));
|
|
355
|
+
const relativePaths = {
|
|
356
|
+
relativeTo: "MCP_SCRAPER_OUTPUT_DIR or ~/Downloads/mcp-scraper",
|
|
357
|
+
dir: relativeDir,
|
|
358
|
+
json: (0, import_node_path.join)(relativeDir, "fanout.json"),
|
|
359
|
+
queriesCsv: (0, import_node_path.join)(relativeDir, "queries.csv"),
|
|
360
|
+
queriesTsv: (0, import_node_path.join)(relativeDir, "queries.tsv"),
|
|
361
|
+
citationsCsv: (0, import_node_path.join)(relativeDir, "citations.csv"),
|
|
362
|
+
sourcesCsv: (0, import_node_path.join)(relativeDir, "sources.csv"),
|
|
363
|
+
browsedOnlyCsv: (0, import_node_path.join)(relativeDir, "browsed-only.csv"),
|
|
364
|
+
snippetsCsv: (0, import_node_path.join)(relativeDir, "snippets.csv"),
|
|
365
|
+
domainsCsv: (0, import_node_path.join)(relativeDir, "domains.csv"),
|
|
366
|
+
report: (0, import_node_path.join)(relativeDir, "report.html")
|
|
367
|
+
};
|
|
368
|
+
(0, import_node_fs.writeFileSync)((0, import_node_path.join)(outputDir, relativePaths.json), JSON.stringify(enriched, null, 2));
|
|
369
|
+
writeTable((0, import_node_path.join)(outputDir, relativePaths.queriesCsv), queryRows, ",");
|
|
370
|
+
writeTable((0, import_node_path.join)(outputDir, relativePaths.queriesTsv), queryRows, " ");
|
|
371
|
+
writeTable((0, import_node_path.join)(outputDir, relativePaths.citationsCsv), citationRows, ",");
|
|
372
|
+
writeTable((0, import_node_path.join)(outputDir, relativePaths.sourcesCsv), sourceRows, ",");
|
|
373
|
+
writeTable((0, import_node_path.join)(outputDir, relativePaths.browsedOnlyCsv), browsedOnlyRows, ",");
|
|
374
|
+
writeTable((0, import_node_path.join)(outputDir, relativePaths.snippetsCsv), snippetRows, ",");
|
|
375
|
+
writeTable((0, import_node_path.join)(outputDir, relativePaths.domainsCsv), domainRows, ",");
|
|
376
|
+
(0, import_node_fs.writeFileSync)((0, import_node_path.join)(outputDir, relativePaths.report), renderReportHtml(enriched));
|
|
377
|
+
return relativePaths;
|
|
378
|
+
}
|
|
379
|
+
function esc(s) {
|
|
380
|
+
return String(s).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
381
|
+
}
|
|
382
|
+
function renderReportHtml(enriched) {
|
|
383
|
+
const data = JSON.stringify(enriched).replace(/</g, "\\u003c");
|
|
384
|
+
return `<!doctype html>
|
|
385
|
+
<html lang="en">
|
|
386
|
+
<head>
|
|
387
|
+
<meta charset="utf-8" />
|
|
388
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
389
|
+
<title>AI Search Fan-Out \u2014 ${esc(enriched.platform)}</title>
|
|
390
|
+
<style>
|
|
391
|
+
:root{--bg:#0b0d12;--panel:#151922;--line:#252b38;--ink:#e8ecf3;--mut:#8b95a7;--acc:#5b9dff;--cite:#39d98a;--browse:#5a6677}
|
|
392
|
+
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.5 -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}
|
|
393
|
+
.wrap{max-width:980px;margin:0 auto;padding:28px 20px 80px}
|
|
394
|
+
h1{font-size:20px;margin:0 0 2px}.sub{color:var(--mut);margin:0 0 20px;font-size:13px}
|
|
395
|
+
.stat{font-size:30px;font-weight:700}.stat small{font-size:13px;font-weight:400;color:var(--mut)}
|
|
396
|
+
.cards{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin:18px 0}
|
|
397
|
+
.card{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:12px 14px}
|
|
398
|
+
.card b{font-size:22px;display:block}.card span{color:var(--mut);font-size:12px}
|
|
399
|
+
.bars{margin:8px 0 22px}.bar{display:flex;align-items:center;gap:8px;margin:4px 0}
|
|
400
|
+
.bar .lab{width:150px;color:var(--mut);font-size:12px;text-align:right}.bar .track{flex:1;background:#10141c;border-radius:5px;overflow:hidden}
|
|
401
|
+
.bar .fill{background:var(--acc);height:16px}.bar .n{width:34px;font-size:12px;color:var(--mut)}
|
|
402
|
+
.tabs{display:flex;gap:8px;margin:22px 0 12px}.tab{background:var(--panel);border:1px solid var(--line);color:var(--ink);padding:7px 14px;border-radius:8px;cursor:pointer}
|
|
403
|
+
.tab.on{background:var(--acc);border-color:var(--acc);color:#04101f;font-weight:600}
|
|
404
|
+
.chips{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:12px}
|
|
405
|
+
.chip{background:var(--panel);border:1px solid var(--line);color:var(--mut);padding:4px 10px;border-radius:20px;cursor:pointer;font-size:12px}
|
|
406
|
+
.chip.on{border-color:var(--acc);color:var(--ink)}.chip b{color:var(--ink)}
|
|
407
|
+
.row{background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:10px 12px;margin:6px 0;display:flex;gap:10px;align-items:flex-start}
|
|
408
|
+
.row .idx{color:var(--mut);min-width:22px}.row .main{flex:1}
|
|
409
|
+
.tag{font-size:11px;padding:2px 7px;border-radius:6px;border:1px solid var(--line);color:var(--mut);margin-right:5px}
|
|
410
|
+
.dom{font-weight:600}.url{color:var(--mut);font-size:12px;word-break:break-all}
|
|
411
|
+
.pill{font-size:11px;padding:2px 7px;border-radius:6px}.pill.cite{background:rgba(57,217,138,.15);color:var(--cite)}.pill.brow{background:rgba(90,102,119,.2);color:#aeb8c8}
|
|
412
|
+
.copy{background:none;border:1px solid var(--line);color:var(--mut);border-radius:6px;cursor:pointer;font-size:11px;padding:2px 8px}
|
|
413
|
+
.hide{display:none}
|
|
414
|
+
</style>
|
|
415
|
+
</head>
|
|
416
|
+
<body><div class="wrap">
|
|
417
|
+
<h1>AI Search Fan-Out</h1>
|
|
418
|
+
<p class="sub" id="sub"></p>
|
|
419
|
+
<div class="stat" id="stat"></div>
|
|
420
|
+
<div class="cards" id="cards"></div>
|
|
421
|
+
<div id="charts"></div>
|
|
422
|
+
<div class="tabs"><button class="tab on" data-tab="q">Queries</button><button class="tab" data-tab="u">URLs</button></div>
|
|
423
|
+
<div class="chips" id="chips"></div>
|
|
424
|
+
<div id="list"></div>
|
|
425
|
+
</div>
|
|
426
|
+
<script>
|
|
427
|
+
const D=${data};
|
|
428
|
+
const el=(t,c,h)=>{const e=document.createElement(t);if(c)e.className=c;if(h!=null)e.innerHTML=h;return e};
|
|
429
|
+
document.getElementById('sub').textContent=D.platform+' \xB7 '+(D.meta.model||'model n/a')+' \xB7 '+D.meta.rounds+' search round(s) \xB7 prompt: '+(D.prompt||'');
|
|
430
|
+
document.getElementById('stat').innerHTML='Total Fan-Out: '+D.counts.subQueries+' <small>sub-queries \xB7 '+D.counts.browsed+' URLs researched \xB7 '+D.counts.cited+' cited</small>';
|
|
431
|
+
const cards=[['Sub-queries',D.counts.subQueries],['Researched',D.counts.browsed],['Cited',D.counts.cited],['Browsed-only',D.counts.browsedOnly]];
|
|
432
|
+
const cc=document.getElementById('cards');cards.forEach(([k,v])=>{const c=el('div','card');c.appendChild(el('b',null,v));c.appendChild(el('span',null,k));cc.appendChild(c)});
|
|
433
|
+
function barChart(title,obj){const max=Math.max(1,...Object.values(obj));const box=el('div','bars');box.appendChild(el('div',null,'<b>'+title+'</b>'));Object.entries(obj).forEach(([k,v])=>{const r=el('div','bar');r.appendChild(el('div','lab',k));const t=el('div','track');const f=el('div','fill');f.style.width=(v/max*100)+'%';t.appendChild(f);r.appendChild(t);r.appendChild(el('div','n',v));box.appendChild(r)});return box}
|
|
434
|
+
const charts=document.getElementById('charts');
|
|
435
|
+
charts.appendChild(barChart('URL Categories',D.aggregates.byCategory));
|
|
436
|
+
let tab='q',filter=null;
|
|
437
|
+
const chips=document.getElementById('chips'),list=document.getElementById('list');
|
|
438
|
+
function render(){
|
|
439
|
+
chips.innerHTML='';list.innerHTML='';
|
|
440
|
+
if(tab==='q'){
|
|
441
|
+
D.queries.forEach((q,i)=>{const r=el('div','row');r.appendChild(el('div','idx',i+1));const m=el('div','main');m.appendChild(el('div',null,esc(q)));r.appendChild(m);const b=el('button','copy','copy');b.onclick=()=>navigator.clipboard.writeText(q);r.appendChild(b);list.appendChild(r)});
|
|
442
|
+
} else {
|
|
443
|
+
const counts={};D.browsedUrls.forEach(s=>{counts[s.siteType]=(counts[s.siteType]||0)+1});
|
|
444
|
+
Object.entries(counts).forEach(([k,v])=>{const c=el('button','chip'+(filter===k?' on':''),k+' <b>'+v+'</b>');c.onclick=()=>{filter=filter===k?null:k;render()};chips.appendChild(c)});
|
|
445
|
+
D.browsedUrls.forEach(s=>{if(filter&&s.siteType!==filter)return;const r=el('div','row');const m=el('div','main');m.appendChild(el('div',null,'<span class="dom">'+esc(s.domain)+'</span> <span class="pill '+(s.cited?'cite':'brow')+'">'+(s.cited?'cited '+s.timesCited+'\xD7':'browsed')+'</span> <span class="tag">'+s.siteType+'</span>'));m.appendChild(el('div','url',esc(s.url)));r.appendChild(m);list.appendChild(r)});
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
function esc(s){return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')}
|
|
449
|
+
document.querySelectorAll('.tab').forEach(t=>t.onclick=()=>{document.querySelectorAll('.tab').forEach(x=>x.classList.remove('on'));t.classList.add('on');tab=t.dataset.tab;filter=null;render()});
|
|
450
|
+
render();
|
|
451
|
+
</script>
|
|
452
|
+
</body></html>`;
|
|
270
453
|
}
|
|
271
454
|
|
|
272
455
|
// src/version.ts
|
|
273
|
-
var PACKAGE_VERSION = "0.3.
|
|
456
|
+
var PACKAGE_VERSION = "0.3.13";
|
|
274
457
|
|
|
275
458
|
// src/mcp/browser-agent-tool-schemas.ts
|
|
276
459
|
var import_zod = require("zod");
|
|
@@ -612,8 +795,8 @@ var BrowserListSessionsOutputSchema = {
|
|
|
612
795
|
// src/mcp/replay-annotator.ts
|
|
613
796
|
var import_node_child_process = require("child_process");
|
|
614
797
|
var import_promises = require("fs/promises");
|
|
615
|
-
var
|
|
616
|
-
var
|
|
798
|
+
var import_node_os2 = require("os");
|
|
799
|
+
var import_node_path2 = require("path");
|
|
617
800
|
var import_node_util = require("util");
|
|
618
801
|
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
619
802
|
function finiteNumber(value) {
|
|
@@ -623,11 +806,11 @@ function clamp(value, min, max) {
|
|
|
623
806
|
return Math.min(max, Math.max(min, value));
|
|
624
807
|
}
|
|
625
808
|
function formatAssTime(seconds) {
|
|
626
|
-
const
|
|
627
|
-
const hours = Math.floor(
|
|
628
|
-
const minutes = Math.floor(
|
|
629
|
-
const wholeSeconds = Math.floor(
|
|
630
|
-
const centiseconds = Math.floor((
|
|
809
|
+
const safe2 = Math.max(0, seconds);
|
|
810
|
+
const hours = Math.floor(safe2 / 3600);
|
|
811
|
+
const minutes = Math.floor(safe2 % 3600 / 60);
|
|
812
|
+
const wholeSeconds = Math.floor(safe2 % 60);
|
|
813
|
+
const centiseconds = Math.floor((safe2 - Math.floor(safe2)) * 100);
|
|
631
814
|
return `${hours}:${String(minutes).padStart(2, "0")}:${String(wholeSeconds).padStart(2, "0")}.${String(centiseconds).padStart(2, "0")}`;
|
|
632
815
|
}
|
|
633
816
|
function cssHexToAssColor(color) {
|
|
@@ -836,8 +1019,8 @@ function ffmpegFilterPath(path) {
|
|
|
836
1019
|
async function annotateReplayVideo(inputFilePath, outputFilePath, options) {
|
|
837
1020
|
if (!options.annotations.length) throw new Error("annotations must include at least one item");
|
|
838
1021
|
const size = await videoSize(inputFilePath);
|
|
839
|
-
const tmp = await (0, import_promises.mkdtemp)((0,
|
|
840
|
-
const assPath = (0,
|
|
1022
|
+
const tmp = await (0, import_promises.mkdtemp)((0, import_node_path2.join)((0, import_node_os2.tmpdir)(), "mcp-scraper-ass-"));
|
|
1023
|
+
const assPath = (0, import_node_path2.join)(tmp, "annotations.ass");
|
|
841
1024
|
try {
|
|
842
1025
|
await (0, import_promises.writeFile)(assPath, buildAssSubtitle(options, size), "utf8");
|
|
843
1026
|
await execFileAsync("ffmpeg", [
|
|
@@ -905,8 +1088,8 @@ function actionResult(tool, sessionId, ok, data, nextRecommendedTool = "browser_
|
|
|
905
1088
|
nextRecommendedTool
|
|
906
1089
|
});
|
|
907
1090
|
}
|
|
908
|
-
function
|
|
909
|
-
return process.env.MCP_SCRAPER_OUTPUT_DIR?.trim() || (0,
|
|
1091
|
+
function outputBaseDir2() {
|
|
1092
|
+
return process.env.MCP_SCRAPER_OUTPUT_DIR?.trim() || (0, import_node_path3.join)((0, import_node_os3.homedir)(), "Downloads", "mcp-scraper");
|
|
910
1093
|
}
|
|
911
1094
|
function safeFilePart(value) {
|
|
912
1095
|
return value.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 120) || "replay";
|
|
@@ -915,7 +1098,7 @@ function replayFilePath(sessionId, replayId, filename) {
|
|
|
915
1098
|
const requested = filename?.trim();
|
|
916
1099
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
917
1100
|
const name = requested ? safeFilePart(requested).replace(/\.mp4$/i, "") : `${stamp}-${safeFilePart(sessionId)}-${safeFilePart(replayId)}`;
|
|
918
|
-
return (0,
|
|
1101
|
+
return (0, import_node_path3.join)(outputBaseDir2(), "browser-replays", `${name}.mp4`);
|
|
919
1102
|
}
|
|
920
1103
|
function slugPart(value) {
|
|
921
1104
|
const trimmed = value?.trim().toLowerCase();
|
|
@@ -928,6 +1111,11 @@ function savedProfileNameFromEmail(email, domain) {
|
|
|
928
1111
|
const domainPart = slugPart(domain);
|
|
929
1112
|
return `${value}${domainPart ? `-${domainPart}` : ""}`.slice(0, 80) || null;
|
|
930
1113
|
}
|
|
1114
|
+
function isEnrichedFanout(value) {
|
|
1115
|
+
if (!value || typeof value !== "object") return false;
|
|
1116
|
+
const candidate = value;
|
|
1117
|
+
return typeof candidate.platform === "string" && typeof candidate.capturedAt === "string" && typeof candidate.prompt === "string" && Array.isArray(candidate.queries) && Array.isArray(candidate.browsedUrls) && Array.isArray(candidate.citedUrls) && Array.isArray(candidate.browsedOnly) && Array.isArray(candidate.snippets) && !!candidate.counts && !!candidate.aggregates;
|
|
1118
|
+
}
|
|
931
1119
|
function annotatedReplayFilePath(sessionId, replayId, filename) {
|
|
932
1120
|
const requested = filename?.trim();
|
|
933
1121
|
if (requested) return replayFilePath(sessionId, replayId, requested);
|
|
@@ -985,8 +1173,8 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
985
1173
|
}
|
|
986
1174
|
const bytes = Buffer.from(await res.arrayBuffer());
|
|
987
1175
|
const filePath = replayFilePath(sessionId, replayId, filename);
|
|
988
|
-
(0,
|
|
989
|
-
(0,
|
|
1176
|
+
(0, import_node_fs2.mkdirSync)((0, import_node_path3.join)(outputBaseDir2(), "browser-replays"), { recursive: true });
|
|
1177
|
+
(0, import_node_fs2.writeFileSync)(filePath, bytes);
|
|
990
1178
|
return {
|
|
991
1179
|
ok: true,
|
|
992
1180
|
data: {
|
|
@@ -1445,7 +1633,7 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
1445
1633
|
try {
|
|
1446
1634
|
const sourcePath = String(downloaded.data.file_path);
|
|
1447
1635
|
const outputPath = annotatedReplayFilePath(input.session_id, input.replay_id, input.filename);
|
|
1448
|
-
(0,
|
|
1636
|
+
(0, import_node_fs2.mkdirSync)((0, import_node_path3.join)(outputBaseDir2(), "browser-replays"), { recursive: true });
|
|
1449
1637
|
const result = await annotateReplayVideo(sourcePath, outputPath, {
|
|
1450
1638
|
annotations: input.annotations,
|
|
1451
1639
|
sourceWidth: input.source_width,
|
|
@@ -1524,7 +1712,7 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
1524
1712
|
annotations: annotations("Capture AI Search Fan-Out")
|
|
1525
1713
|
},
|
|
1526
1714
|
async (input) => {
|
|
1527
|
-
const emit = (result,
|
|
1715
|
+
const emit = (result, exports3) => structuredResult({
|
|
1528
1716
|
ok: true,
|
|
1529
1717
|
tool: "browser_capture_fanout",
|
|
1530
1718
|
session_id: input.session_id,
|
|
@@ -1540,7 +1728,7 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
1540
1728
|
counts: result.counts,
|
|
1541
1729
|
aggregates: result.aggregates,
|
|
1542
1730
|
first_party_domain: result.firstPartyDomain,
|
|
1543
|
-
exports:
|
|
1731
|
+
exports: exports3 ?? null,
|
|
1544
1732
|
...result.debug ? { debug: result.debug } : {}
|
|
1545
1733
|
});
|
|
1546
1734
|
const res = await req("POST", `/agent/sessions/${input.session_id}/capture-fanout`, {
|
|
@@ -1552,20 +1740,35 @@ function registerBrowserAgentMcpTools(server2, opts) {
|
|
|
1552
1740
|
});
|
|
1553
1741
|
if (!res.ok) return errorResult("browser_capture_fanout", res.data, input.session_id);
|
|
1554
1742
|
const hosted = res.data?.result ?? res.data;
|
|
1555
|
-
|
|
1743
|
+
let exports2 = res.data?.exports ?? null;
|
|
1744
|
+
if (input.export && !exports2 && isEnrichedFanout(hosted)) {
|
|
1745
|
+
try {
|
|
1746
|
+
exports2 = exportFanout(hosted);
|
|
1747
|
+
} catch (err) {
|
|
1748
|
+
return errorResult(
|
|
1749
|
+
"browser_capture_fanout",
|
|
1750
|
+
{
|
|
1751
|
+
error: `Fan-out captured but local export failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
1752
|
+
result: hosted
|
|
1753
|
+
},
|
|
1754
|
+
input.session_id
|
|
1755
|
+
);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
return emit(hosted, exports2);
|
|
1556
1759
|
}
|
|
1557
1760
|
);
|
|
1558
1761
|
}
|
|
1559
1762
|
|
|
1560
1763
|
// src/mcp/paa-mcp-server.ts
|
|
1561
1764
|
var import_mcp2 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
1562
|
-
var
|
|
1563
|
-
var
|
|
1765
|
+
var import_node_fs4 = require("fs");
|
|
1766
|
+
var import_node_path5 = require("path");
|
|
1564
1767
|
|
|
1565
1768
|
// src/mcp/mcp-response-formatter.ts
|
|
1566
|
-
var
|
|
1567
|
-
var
|
|
1568
|
-
var
|
|
1769
|
+
var import_node_fs3 = require("fs");
|
|
1770
|
+
var import_node_os4 = require("os");
|
|
1771
|
+
var import_node_path4 = require("path");
|
|
1569
1772
|
|
|
1570
1773
|
// src/errors.ts
|
|
1571
1774
|
function sanitizeVendorName(message) {
|
|
@@ -1701,17 +1904,17 @@ function reportTitle(full) {
|
|
|
1701
1904
|
const title = full.split("\n").find((line) => line.startsWith("# "));
|
|
1702
1905
|
return title?.replace(/^#\s+/, "").trim() || "MCP Scraper Report";
|
|
1703
1906
|
}
|
|
1704
|
-
function
|
|
1705
|
-
return process.env.MCP_SCRAPER_OUTPUT_DIR?.trim() || (0,
|
|
1907
|
+
function outputBaseDir3() {
|
|
1908
|
+
return process.env.MCP_SCRAPER_OUTPUT_DIR?.trim() || (0, import_node_path4.join)((0, import_node_os4.homedir)(), "Downloads", "mcp-scraper");
|
|
1706
1909
|
}
|
|
1707
1910
|
function saveFullReport(full) {
|
|
1708
1911
|
if (!reportSavingEnabled || process.env.MCP_SCRAPER_SAVE_REPORTS === "false") return null;
|
|
1709
|
-
const outDir =
|
|
1912
|
+
const outDir = outputBaseDir3();
|
|
1710
1913
|
try {
|
|
1711
|
-
(0,
|
|
1914
|
+
(0, import_node_fs3.mkdirSync)(outDir, { recursive: true });
|
|
1712
1915
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1713
|
-
const file = (0,
|
|
1714
|
-
(0,
|
|
1916
|
+
const file = (0, import_node_path4.join)(outDir, `${stamp}-${slugifyReportName(reportTitle(full))}.md`);
|
|
1917
|
+
(0, import_node_fs3.writeFileSync)(file, full, "utf8");
|
|
1715
1918
|
return file;
|
|
1716
1919
|
} catch {
|
|
1717
1920
|
return null;
|
|
@@ -1720,12 +1923,12 @@ function saveFullReport(full) {
|
|
|
1720
1923
|
function persistScreenshotLocally(base64, url) {
|
|
1721
1924
|
if (!reportSavingEnabled || process.env.MCP_SCRAPER_SAVE_REPORTS === "false") return null;
|
|
1722
1925
|
try {
|
|
1723
|
-
const dir = (0,
|
|
1724
|
-
(0,
|
|
1926
|
+
const dir = (0, import_node_path4.join)(outputBaseDir3(), "screenshots");
|
|
1927
|
+
(0, import_node_fs3.mkdirSync)(dir, { recursive: true });
|
|
1725
1928
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1726
1929
|
const slug = url.replace(/^https?:\/\//, "").replace(/[^a-z0-9]+/gi, "-").replace(/^-+|-+$/g, "").slice(0, 60);
|
|
1727
|
-
const filePath = (0,
|
|
1728
|
-
(0,
|
|
1930
|
+
const filePath = (0, import_node_path4.join)(dir, `${stamp}-${slug}.png`);
|
|
1931
|
+
(0, import_node_fs3.writeFileSync)(filePath, Buffer.from(base64, "base64"));
|
|
1729
1932
|
return filePath;
|
|
1730
1933
|
} catch {
|
|
1731
1934
|
return null;
|
|
@@ -3066,7 +3269,7 @@ function formatInstagramProfileContent(raw, input) {
|
|
|
3066
3269
|
const itemRows = items.slice(0, 100).map(
|
|
3067
3270
|
(item, i) => `| ${i + 1} | ${item.type} | \`${item.shortcode}\` | ${item.url} | ${cell(item.firstSeenStage ?? "")} |`
|
|
3068
3271
|
).join("\n");
|
|
3069
|
-
const browserLabel = "hosted browser";
|
|
3272
|
+
const browserLabel = browser.profileName ? `hosted browser profile ${browser.profileName}` : "hosted browser";
|
|
3070
3273
|
const full = [
|
|
3071
3274
|
`# Instagram Profile Content: ${d.handle ?? input.handle ?? input.url ?? "profile"}`,
|
|
3072
3275
|
`**Collected:** ${items.length} items \xB7 posts ${typeCounts?.post ?? 0} \xB7 reels ${typeCounts?.reel ?? 0} \xB7 tv ${typeCounts?.tv ?? 0}`,
|
|
@@ -3141,7 +3344,7 @@ function formatInstagramMediaDownload(raw, input) {
|
|
|
3141
3344
|
const transcriptText = transcript?.text ?? "";
|
|
3142
3345
|
const chunks = transcript?.chunks ?? [];
|
|
3143
3346
|
const browser = structuredInstagramBrowser(d.browser);
|
|
3144
|
-
const browserLabel = "hosted browser";
|
|
3347
|
+
const browserLabel = browser.profileName ? `hosted browser profile ${browser.profileName}` : "hosted browser";
|
|
3145
3348
|
const downloadRows = downloads.map((download, i) => {
|
|
3146
3349
|
const status = download.error ? `error: ${cell(download.error)}` : `${download.sizeBytes ?? 0} bytes`;
|
|
3147
3350
|
return `| ${i + 1} | ${download.kind} | ${download.savedPath ? `\`${download.savedPath}\`` : "\u2014"} | ${status} |`;
|
|
@@ -3386,13 +3589,17 @@ var FacebookVideoTranscribeInputSchema = {
|
|
|
3386
3589
|
var InstagramProfileContentInputSchema = {
|
|
3387
3590
|
handle: import_zod3.z.string().min(1).optional().describe("Instagram handle, with or without @. Provide handle or url."),
|
|
3388
3591
|
url: import_zod3.z.string().url().optional().describe("Instagram profile URL, e.g. https://www.instagram.com/nasaartemis/. Provide handle or url."),
|
|
3389
|
-
|
|
3390
|
-
|
|
3592
|
+
profile: import_zod3.z.string().min(1).optional().describe("Optional saved hosted browser profile name to load authenticated Instagram access. If omitted, the server uses its configured default profile when present."),
|
|
3593
|
+
saveProfileChanges: import_zod3.z.boolean().optional().describe("Whether to save browser changes back to the hosted profile. Leave unset unless intentionally updating the saved login session."),
|
|
3594
|
+
maxItems: import_zod3.z.number().int().min(1).max(2e3).default(50).describe("Maximum profile grid post/reel/tv URLs to collect. Default 50, maximum 2000. Use higher values only when the user asks for a fuller archive."),
|
|
3595
|
+
maxScrolls: import_zod3.z.number().int().min(0).max(250).default(10).describe("Maximum pagination scroll attempts. Default 10, maximum 250. Increase for long profiles when Instagram continues loading more grid links."),
|
|
3391
3596
|
scrollDelayMs: import_zod3.z.number().int().min(250).max(5e3).default(1200).describe("Delay after each pagination scroll before collecting newly loaded links. Default 1200ms. Increase to 2000-3000ms when Instagram loads slowly."),
|
|
3392
3597
|
stableScrollLimit: import_zod3.z.number().int().min(1).max(10).default(4).describe("Stop after this many consecutive scrolls with no new links or scroll progress. Default 4.")
|
|
3393
3598
|
};
|
|
3394
3599
|
var InstagramMediaDownloadInputSchema = {
|
|
3395
3600
|
url: import_zod3.z.string().url().describe("Instagram post, reel, or tv URL, e.g. https://www.instagram.com/reel/SHORTCODE/. The tool renders the page, extracts text, image metadata, and Instagram CDN media tracks."),
|
|
3601
|
+
profile: import_zod3.z.string().min(1).optional().describe("Optional saved hosted browser profile name to load authenticated Instagram access. If omitted, the server uses its configured default profile when present."),
|
|
3602
|
+
saveProfileChanges: import_zod3.z.boolean().optional().describe("Whether to save browser changes back to the hosted profile. Leave unset unless intentionally updating the saved login session."),
|
|
3396
3603
|
mediaTypes: import_zod3.z.array(import_zod3.z.enum(["image", "video", "audio"])).default(["image", "video", "audio"]).describe("Which media types to download when downloadMedia is true. Reels commonly expose separate video-only and audio-only MP4 tracks."),
|
|
3397
3604
|
downloadMedia: import_zod3.z.boolean().default(true).describe("Download extracted text/media files to the MCP Scraper output directory when the API server can write files. Always returns extracted media URLs even when false."),
|
|
3398
3605
|
downloadAllTracks: import_zod3.z.boolean().default(false).describe("Download every captured Instagram MP4 track instead of only the selected best video and audio tracks. Use false by default to avoid duplicate bitrates."),
|
|
@@ -3804,8 +4011,8 @@ var InstagramBrowserOutput = import_zod3.z.object({
|
|
|
3804
4011
|
executablePathConfigured: import_zod3.z.boolean()
|
|
3805
4012
|
});
|
|
3806
4013
|
var InstagramPaginationOutput = import_zod3.z.object({
|
|
3807
|
-
maxItems: import_zod3.z.number().int().min(1).max(
|
|
3808
|
-
maxScrolls: import_zod3.z.number().int().min(0).max(
|
|
4014
|
+
maxItems: import_zod3.z.number().int().min(1).max(2e3),
|
|
4015
|
+
maxScrolls: import_zod3.z.number().int().min(0).max(250),
|
|
3809
4016
|
attemptedScrolls: import_zod3.z.number().int().min(0),
|
|
3810
4017
|
stableScrolls: import_zod3.z.number().int().min(0),
|
|
3811
4018
|
stableScrollLimit: import_zod3.z.number().int().min(1).max(10),
|
|
@@ -4420,8 +4627,8 @@ function localPlanningToolAnnotations(title) {
|
|
|
4420
4627
|
}
|
|
4421
4628
|
function listSavedReports() {
|
|
4422
4629
|
try {
|
|
4423
|
-
const dir =
|
|
4424
|
-
return (0,
|
|
4630
|
+
const dir = outputBaseDir3();
|
|
4631
|
+
return (0, import_node_fs4.readdirSync)(dir).filter((f) => f.endsWith(".md")).map((f) => ({ filename: f, mtimeMs: (0, import_node_fs4.statSync)((0, import_node_path5.join)(dir, f)).mtimeMs })).sort((a, b) => b.mtimeMs - a.mtimeMs).slice(0, 100);
|
|
4425
4632
|
} catch {
|
|
4426
4633
|
return [];
|
|
4427
4634
|
}
|
|
@@ -4445,9 +4652,9 @@ function registerSavedReportResources(server2) {
|
|
|
4445
4652
|
},
|
|
4446
4653
|
async (uri, variables) => {
|
|
4447
4654
|
const requested = Array.isArray(variables.filename) ? variables.filename[0] : variables.filename;
|
|
4448
|
-
const filename = (0,
|
|
4655
|
+
const filename = (0, import_node_path5.basename)(decodeURIComponent(String(requested ?? "")));
|
|
4449
4656
|
if (!filename.endsWith(".md")) throw new Error("Only saved .md reports can be read");
|
|
4450
|
-
const text = (0,
|
|
4657
|
+
const text = (0, import_node_fs4.readFileSync)((0, import_node_path5.join)(outputBaseDir3(), filename), "utf8");
|
|
4451
4658
|
return { contents: [{ uri: uri.href, mimeType: "text/markdown", text }] };
|
|
4452
4659
|
}
|
|
4453
4660
|
);
|
|
@@ -4747,10 +4954,10 @@ if (!forceStdio && (interactiveTerminal || wantsHelp)) {
|
|
|
4747
4954
|
}
|
|
4748
4955
|
function readApiKeyFile() {
|
|
4749
4956
|
const explicitPath = process.env.MCP_SCRAPER_KEY_PATH?.trim();
|
|
4750
|
-
const paths = [explicitPath, (0,
|
|
4957
|
+
const paths = [explicitPath, (0, import_node_path6.join)((0, import_node_os5.homedir)(), ".mcp-scraper-key")].filter(Boolean);
|
|
4751
4958
|
for (const path of paths) {
|
|
4752
4959
|
try {
|
|
4753
|
-
const value = (0,
|
|
4960
|
+
const value = (0, import_node_fs5.readFileSync)(path, "utf8").trim();
|
|
4754
4961
|
if (value) return value;
|
|
4755
4962
|
} catch {
|
|
4756
4963
|
}
|