mcp-scraper 0.2.5 → 0.2.7
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 +13 -7
- package/dist/bin/api-server.cjs +617 -11
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +1 -1
- package/dist/bin/browser-agent-stdio-server.cjs +442 -9
- package/dist/bin/browser-agent-stdio-server.cjs.map +1 -1
- package/dist/bin/browser-agent-stdio-server.js +2 -2
- package/dist/bin/mcp-scraper-combined-stdio-server.cjs +842 -19
- package/dist/bin/mcp-scraper-combined-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-scraper-combined-stdio-server.js +3 -3
- package/dist/bin/mcp-scraper-install.cjs +3 -2
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +3 -2
- package/dist/bin/mcp-scraper-install.js.map +1 -1
- package/dist/bin/mcp-stdio-server.cjs +391 -1
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +2 -2
- package/dist/chunk-3TF6UT2P.js +793 -0
- package/dist/chunk-3TF6UT2P.js.map +1 -0
- package/dist/{chunk-ENI4DM3S.js → chunk-7SI6XIR3.js} +392 -2
- package/dist/chunk-7SI6XIR3.js.map +1 -0
- package/dist/chunk-IZE5UW7Y.js +7 -0
- package/dist/chunk-IZE5UW7Y.js.map +1 -0
- package/dist/{server-Q3WHIS63.js → server-RIKRBDOI.js} +221 -12
- package/dist/{server-Q3WHIS63.js.map → server-RIKRBDOI.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-ENI4DM3S.js.map +0 -1
- package/dist/chunk-JDX57SEC.js +0 -7
- package/dist/chunk-JDX57SEC.js.map +0 -1
- package/dist/chunk-O36HHUKX.js +0 -360
- package/dist/chunk-O36HHUKX.js.map +0 -1
|
@@ -41,13 +41,13 @@ import {
|
|
|
41
41
|
harvestTimeoutBudget,
|
|
42
42
|
liveWebToolAnnotations,
|
|
43
43
|
outputBaseDir
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-7SI6XIR3.js";
|
|
45
45
|
import {
|
|
46
46
|
CaptchaError,
|
|
47
47
|
RECAPTCHA_INSTRUCTIONS,
|
|
48
48
|
sanitizeVendorName
|
|
49
49
|
} from "./chunk-M2S27J6Z.js";
|
|
50
|
-
import "./chunk-
|
|
50
|
+
import "./chunk-IZE5UW7Y.js";
|
|
51
51
|
import {
|
|
52
52
|
SiteAuditJobRowSchema,
|
|
53
53
|
cancelJob,
|
|
@@ -11710,18 +11710,26 @@ async function recordAction(input) {
|
|
|
11710
11710
|
async function recordReplayStart(input) {
|
|
11711
11711
|
const db = getDb();
|
|
11712
11712
|
await db.execute({
|
|
11713
|
-
sql: `INSERT INTO browser_agent_replays (replay_id, session_id, view_url, label)
|
|
11714
|
-
VALUES (?, ?, ?, ?)`,
|
|
11715
|
-
args: [input.replayId, input.sessionId, input.viewUrl, input.label]
|
|
11713
|
+
sql: `INSERT INTO browser_agent_replays (replay_id, session_id, view_url, label, started_at)
|
|
11714
|
+
VALUES (?, ?, ?, ?, ?)`,
|
|
11715
|
+
args: [input.replayId, input.sessionId, input.viewUrl, input.label, (/* @__PURE__ */ new Date()).toISOString()]
|
|
11716
11716
|
});
|
|
11717
11717
|
}
|
|
11718
11718
|
async function recordReplayStop(replayId, viewUrl) {
|
|
11719
11719
|
const db = getDb();
|
|
11720
11720
|
await db.execute({
|
|
11721
|
-
sql: `UPDATE browser_agent_replays SET stopped_at =
|
|
11722
|
-
args: [viewUrl, replayId]
|
|
11721
|
+
sql: `UPDATE browser_agent_replays SET stopped_at = ?, view_url = COALESCE(?, view_url) WHERE replay_id = ?`,
|
|
11722
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), viewUrl, replayId]
|
|
11723
11723
|
});
|
|
11724
11724
|
}
|
|
11725
|
+
async function getActiveReplayRow(sessionId) {
|
|
11726
|
+
const db = getDb();
|
|
11727
|
+
const res = await db.execute({
|
|
11728
|
+
sql: `SELECT * FROM browser_agent_replays WHERE session_id = ? AND stopped_at IS NULL ORDER BY started_at DESC LIMIT 1`,
|
|
11729
|
+
args: [sessionId]
|
|
11730
|
+
});
|
|
11731
|
+
return res.rows[0] ?? null;
|
|
11732
|
+
}
|
|
11725
11733
|
async function listReplayRows(sessionId) {
|
|
11726
11734
|
const db = getDb();
|
|
11727
11735
|
const res = await db.execute({
|
|
@@ -11835,7 +11843,13 @@ async function readPage(cdpWsUrl) {
|
|
|
11835
11843
|
role: el2.getAttribute("role") || el2.tagName.toLowerCase(),
|
|
11836
11844
|
name,
|
|
11837
11845
|
x: Math.round(r.left + r.width / 2),
|
|
11838
|
-
y: Math.round(r.top + r.height / 2)
|
|
11846
|
+
y: Math.round(r.top + r.height / 2),
|
|
11847
|
+
left: Math.round(r.left),
|
|
11848
|
+
top: Math.round(r.top),
|
|
11849
|
+
width: Math.round(r.width),
|
|
11850
|
+
height: Math.round(r.height),
|
|
11851
|
+
right: Math.round(r.right),
|
|
11852
|
+
bottom: Math.round(r.bottom)
|
|
11839
11853
|
});
|
|
11840
11854
|
}
|
|
11841
11855
|
const text = (document.body?.innerText || "").replace(/\n{3,}/g, "\n\n").trim().slice(0, 6e3);
|
|
@@ -11845,13 +11859,168 @@ async function readPage(cdpWsUrl) {
|
|
|
11845
11859
|
url,
|
|
11846
11860
|
title,
|
|
11847
11861
|
text: data.text,
|
|
11848
|
-
elements: data.els.map((e, i) => ({
|
|
11862
|
+
elements: data.els.map((e, i) => ({
|
|
11863
|
+
ref: i + 1,
|
|
11864
|
+
role: e.role,
|
|
11865
|
+
name: e.name,
|
|
11866
|
+
x: e.x,
|
|
11867
|
+
y: e.y,
|
|
11868
|
+
left: e.left,
|
|
11869
|
+
top: e.top,
|
|
11870
|
+
width: e.width,
|
|
11871
|
+
height: e.height,
|
|
11872
|
+
right: e.right,
|
|
11873
|
+
bottom: e.bottom
|
|
11874
|
+
}))
|
|
11849
11875
|
};
|
|
11850
11876
|
} finally {
|
|
11851
11877
|
await browser.close().catch(() => {
|
|
11852
11878
|
});
|
|
11853
11879
|
}
|
|
11854
11880
|
}
|
|
11881
|
+
async function locatePageTargets(cdpWsUrl, targets) {
|
|
11882
|
+
const browser = await playwrightChromium.connectOverCDP(cdpWsUrl);
|
|
11883
|
+
try {
|
|
11884
|
+
const context = browser.contexts()[0] ?? await browser.newContext();
|
|
11885
|
+
const page = context.pages()[0] ?? await context.newPage();
|
|
11886
|
+
const url = page.url();
|
|
11887
|
+
const title = await page.title().catch(() => "");
|
|
11888
|
+
await page.evaluate(() => {
|
|
11889
|
+
;
|
|
11890
|
+
globalThis.__name = (target) => target;
|
|
11891
|
+
}).catch(() => {
|
|
11892
|
+
});
|
|
11893
|
+
const data = await page.evaluate((rawTargets) => {
|
|
11894
|
+
const normalize = (value) => value.replace(/\s+/g, " ").trim();
|
|
11895
|
+
const isVisibleRect = (r) => r.width >= 1 && r.height >= 1 && r.bottom >= 0 && r.right >= 0 && r.top <= window.innerHeight && r.left <= window.innerWidth;
|
|
11896
|
+
const roleOf = (el2) => el2.getAttribute("role") || el2.tagName.toLowerCase();
|
|
11897
|
+
const nameOf = (el2) => {
|
|
11898
|
+
const e = el2;
|
|
11899
|
+
return normalize(
|
|
11900
|
+
e.getAttribute("aria-label") || e.placeholder || e.innerText || e.getAttribute("value") || e.getAttribute("title") || ""
|
|
11901
|
+
).slice(0, 160);
|
|
11902
|
+
};
|
|
11903
|
+
const textOf = (el2) => normalize(el2.innerText || el2.textContent || "").slice(0, 500);
|
|
11904
|
+
const unionRects = (rects) => {
|
|
11905
|
+
const visible = rects.filter(isVisibleRect);
|
|
11906
|
+
if (!visible.length) return null;
|
|
11907
|
+
const left = Math.min(...visible.map((r) => r.left));
|
|
11908
|
+
const top = Math.min(...visible.map((r) => r.top));
|
|
11909
|
+
const right = Math.max(...visible.map((r) => r.right));
|
|
11910
|
+
const bottom = Math.max(...visible.map((r) => r.bottom));
|
|
11911
|
+
return {
|
|
11912
|
+
left,
|
|
11913
|
+
top,
|
|
11914
|
+
right,
|
|
11915
|
+
bottom,
|
|
11916
|
+
width: right - left,
|
|
11917
|
+
height: bottom - top,
|
|
11918
|
+
x: left,
|
|
11919
|
+
y: top,
|
|
11920
|
+
toJSON: () => ({})
|
|
11921
|
+
};
|
|
11922
|
+
};
|
|
11923
|
+
const elementPayload = (el2, rect) => ({
|
|
11924
|
+
role: roleOf(el2),
|
|
11925
|
+
tag: el2.tagName.toLowerCase(),
|
|
11926
|
+
name: nameOf(el2),
|
|
11927
|
+
text: textOf(el2),
|
|
11928
|
+
x: Math.round(rect.left + rect.width / 2),
|
|
11929
|
+
y: Math.round(rect.top + rect.height / 2),
|
|
11930
|
+
left: Math.round(rect.left),
|
|
11931
|
+
top: Math.round(rect.top),
|
|
11932
|
+
width: Math.round(rect.width),
|
|
11933
|
+
height: Math.round(rect.height),
|
|
11934
|
+
right: Math.round(rect.right),
|
|
11935
|
+
bottom: Math.round(rect.bottom)
|
|
11936
|
+
});
|
|
11937
|
+
const selectorMatches = (selector) => {
|
|
11938
|
+
const nodes = Array.from(document.querySelectorAll(selector));
|
|
11939
|
+
return nodes.map((el2) => ({ el: el2, rect: el2.getBoundingClientRect() })).filter((item) => isVisibleRect(item.rect));
|
|
11940
|
+
};
|
|
11941
|
+
const textMatches = (needle, match) => {
|
|
11942
|
+
const wanted = normalize(needle);
|
|
11943
|
+
const wantedLower = wanted.toLowerCase();
|
|
11944
|
+
if (!wantedLower) return [];
|
|
11945
|
+
const matches = [];
|
|
11946
|
+
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
|
|
11947
|
+
let node = walker.nextNode();
|
|
11948
|
+
while (node) {
|
|
11949
|
+
const raw = node.textContent || "";
|
|
11950
|
+
const normalizedRaw = normalize(raw);
|
|
11951
|
+
const rawLower = raw.toLowerCase();
|
|
11952
|
+
const normalizedLower = normalizedRaw.toLowerCase();
|
|
11953
|
+
const ok = match === "exact" ? normalizedLower === wantedLower : normalizedLower.includes(wantedLower);
|
|
11954
|
+
if (ok) {
|
|
11955
|
+
const start = match === "exact" ? 0 : rawLower.indexOf(wantedLower);
|
|
11956
|
+
const parent = node.parentElement;
|
|
11957
|
+
if (parent && start >= 0) {
|
|
11958
|
+
try {
|
|
11959
|
+
const range = document.createRange();
|
|
11960
|
+
range.setStart(node, Math.min(start, raw.length));
|
|
11961
|
+
range.setEnd(node, Math.min(raw.length, start + needle.length));
|
|
11962
|
+
const rect = unionRects(Array.from(range.getClientRects()));
|
|
11963
|
+
range.detach();
|
|
11964
|
+
if (rect) matches.push({ el: parent, rect });
|
|
11965
|
+
} catch {
|
|
11966
|
+
const rect = parent.getBoundingClientRect();
|
|
11967
|
+
if (isVisibleRect(rect)) matches.push({ el: parent, rect });
|
|
11968
|
+
}
|
|
11969
|
+
}
|
|
11970
|
+
}
|
|
11971
|
+
node = walker.nextNode();
|
|
11972
|
+
}
|
|
11973
|
+
if (matches.length) return matches;
|
|
11974
|
+
const fallback = Array.from(document.querySelectorAll("body *")).map((el2) => ({ el: el2, rect: el2.getBoundingClientRect(), text: textOf(el2) })).filter((item) => isVisibleRect(item.rect)).filter((item) => {
|
|
11975
|
+
const value = item.text.toLowerCase();
|
|
11976
|
+
return match === "exact" ? value === wantedLower : value.includes(wantedLower);
|
|
11977
|
+
}).sort((a, b) => a.rect.width * a.rect.height - b.rect.width * b.rect.height);
|
|
11978
|
+
return fallback.map(({ el: el2, rect }) => ({ el: el2, rect }));
|
|
11979
|
+
};
|
|
11980
|
+
const viewport = {
|
|
11981
|
+
width: window.innerWidth,
|
|
11982
|
+
height: window.innerHeight,
|
|
11983
|
+
devicePixelRatio: window.devicePixelRatio || 1
|
|
11984
|
+
};
|
|
11985
|
+
const results = rawTargets.map((target) => {
|
|
11986
|
+
const index = Math.max(0, Math.floor(target.index ?? 0));
|
|
11987
|
+
const match = target.match === "exact" ? "exact" : "contains";
|
|
11988
|
+
try {
|
|
11989
|
+
const selector = target.selector?.trim();
|
|
11990
|
+
const text = target.text?.trim();
|
|
11991
|
+
const candidates = selector ? selectorMatches(selector) : text ? textMatches(text, match) : [];
|
|
11992
|
+
const selected = candidates[index];
|
|
11993
|
+
return {
|
|
11994
|
+
name: target.name ?? null,
|
|
11995
|
+
selector: selector || null,
|
|
11996
|
+
text: text || null,
|
|
11997
|
+
match,
|
|
11998
|
+
index,
|
|
11999
|
+
found: Boolean(selected),
|
|
12000
|
+
element: selected ? elementPayload(selected.el, selected.rect) : null,
|
|
12001
|
+
...selected ? {} : { error: "target not found in current viewport" }
|
|
12002
|
+
};
|
|
12003
|
+
} catch (err) {
|
|
12004
|
+
return {
|
|
12005
|
+
name: target.name ?? null,
|
|
12006
|
+
selector: target.selector ?? null,
|
|
12007
|
+
text: target.text ?? null,
|
|
12008
|
+
match,
|
|
12009
|
+
index,
|
|
12010
|
+
found: false,
|
|
12011
|
+
element: null,
|
|
12012
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12013
|
+
};
|
|
12014
|
+
}
|
|
12015
|
+
});
|
|
12016
|
+
return { viewport, targets: results };
|
|
12017
|
+
}, targets);
|
|
12018
|
+
return { url, title, viewport: data.viewport, targets: data.targets };
|
|
12019
|
+
} finally {
|
|
12020
|
+
await browser.close().catch(() => {
|
|
12021
|
+
});
|
|
12022
|
+
}
|
|
12023
|
+
}
|
|
11855
12024
|
async function replayStart(runtimeSessionId) {
|
|
11856
12025
|
const k = client();
|
|
11857
12026
|
const res = await k.browsers.replays.start(runtimeSessionId);
|
|
@@ -11905,6 +12074,22 @@ function failure(err) {
|
|
|
11905
12074
|
const msg = err instanceof Error ? err.message : String(err);
|
|
11906
12075
|
return { error: sanitizeVendorName(msg) };
|
|
11907
12076
|
}
|
|
12077
|
+
function parseAgentDate(value) {
|
|
12078
|
+
if (!value) return null;
|
|
12079
|
+
const parsed = Date.parse(value);
|
|
12080
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
12081
|
+
const legacyParsed = Date.parse(`${value.replace(" ", "T")}Z`);
|
|
12082
|
+
return Number.isFinite(legacyParsed) ? legacyParsed : null;
|
|
12083
|
+
}
|
|
12084
|
+
function replayClock(row) {
|
|
12085
|
+
if (!row) return null;
|
|
12086
|
+
const startedAtMs = parseAgentDate(row.started_at);
|
|
12087
|
+
return {
|
|
12088
|
+
replay_id: row.replay_id,
|
|
12089
|
+
started_at: row.started_at,
|
|
12090
|
+
replay_elapsed_seconds: startedAtMs == null ? null : Math.max(0, (Date.now() - startedAtMs) / 1e3)
|
|
12091
|
+
};
|
|
12092
|
+
}
|
|
11908
12093
|
function replayDownloadUrl(sessionId, replayId) {
|
|
11909
12094
|
return `/agent/sessions/${encodeURIComponent(sessionId)}/replays/${encodeURIComponent(replayId)}/download`;
|
|
11910
12095
|
}
|
|
@@ -12015,13 +12200,15 @@ function buildBrowserAgentRoutes() {
|
|
|
12015
12200
|
}
|
|
12016
12201
|
await charge(row.id, user.id, t0);
|
|
12017
12202
|
await recordAction({ sessionId: row.id, type: "screenshot", params: null, ok: true });
|
|
12203
|
+
const activeReplay = await getActiveReplayRow(row.id);
|
|
12018
12204
|
return c.json({
|
|
12019
12205
|
image_base64: shot.base64,
|
|
12020
12206
|
mime_type: shot.mimeType,
|
|
12021
12207
|
url: page?.url ?? null,
|
|
12022
12208
|
title: page?.title ?? null,
|
|
12023
12209
|
elements: page?.elements ?? [],
|
|
12024
|
-
text: page?.text ?? null
|
|
12210
|
+
text: page?.text ?? null,
|
|
12211
|
+
replay: replayClock(activeReplay)
|
|
12025
12212
|
});
|
|
12026
12213
|
} catch (err) {
|
|
12027
12214
|
await charge(row.id, user.id, t0);
|
|
@@ -12038,7 +12225,8 @@ function buildBrowserAgentRoutes() {
|
|
|
12038
12225
|
const page = await readPage(row.cdp_ws_url);
|
|
12039
12226
|
await charge(row.id, user.id, t0);
|
|
12040
12227
|
await recordAction({ sessionId: row.id, type: "read", params: null, ok: true });
|
|
12041
|
-
|
|
12228
|
+
const activeReplay = await getActiveReplayRow(row.id);
|
|
12229
|
+
return c.json({ ...page, replay: replayClock(activeReplay) });
|
|
12042
12230
|
} catch (err) {
|
|
12043
12231
|
await charge(row.id, user.id, t0);
|
|
12044
12232
|
return c.json(failure(err), 502);
|
|
@@ -12067,6 +12255,27 @@ function buildBrowserAgentRoutes() {
|
|
|
12067
12255
|
return c.json(failure(err), 502);
|
|
12068
12256
|
}
|
|
12069
12257
|
});
|
|
12258
|
+
app2.post("/sessions/:id/locate", async (c) => {
|
|
12259
|
+
const user = c.get("user");
|
|
12260
|
+
const row = await loadOpenSession(c.req.param("id"), user.id);
|
|
12261
|
+
if (!row) return c.json({ error: "not found" }, 404);
|
|
12262
|
+
const body = await c.req.json().catch(() => ({}));
|
|
12263
|
+
const rawTargets = Array.isArray(body.targets) ? body.targets : [];
|
|
12264
|
+
const targets = rawTargets.filter((value) => value && typeof value === "object").slice(0, 20).map((value) => value);
|
|
12265
|
+
if (!targets.length) return c.json({ error: "targets is required" }, 400);
|
|
12266
|
+
const t0 = Date.now();
|
|
12267
|
+
try {
|
|
12268
|
+
const result = await locatePageTargets(row.cdp_ws_url, targets);
|
|
12269
|
+
await charge(row.id, user.id, t0);
|
|
12270
|
+
await recordAction({ sessionId: row.id, type: "locate", params: { targets: targets.length }, ok: true });
|
|
12271
|
+
const activeReplay = await getActiveReplayRow(row.id);
|
|
12272
|
+
return c.json({ ...result, replay: replayClock(activeReplay) });
|
|
12273
|
+
} catch (err) {
|
|
12274
|
+
await charge(row.id, user.id, t0);
|
|
12275
|
+
await recordAction({ sessionId: row.id, type: "locate", params: { targets: targets.length }, ok: false, error: String(err) });
|
|
12276
|
+
return c.json(failure(err), 502);
|
|
12277
|
+
}
|
|
12278
|
+
});
|
|
12070
12279
|
app2.post("/sessions/:id/type", async (c) => {
|
|
12071
12280
|
const user = c.get("user");
|
|
12072
12281
|
const row = await loadOpenSession(c.req.param("id"), user.id);
|
|
@@ -13511,4 +13720,4 @@ app.get("/blog/:slug/", (c) => {
|
|
|
13511
13720
|
export {
|
|
13512
13721
|
app
|
|
13513
13722
|
};
|
|
13514
|
-
//# sourceMappingURL=server-
|
|
13723
|
+
//# sourceMappingURL=server-RIKRBDOI.js.map
|