cdp-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +240 -0
- package/dist/index.js +345 -0
- package/dist/index.js.map +1 -0
- package/dist/server.js +27 -0
- package/dist/server.js.map +1 -0
- package/dist/session/browser.js +394 -0
- package/dist/session/browser.js.map +1 -0
- package/dist/session/buffers.js +43 -0
- package/dist/session/buffers.js.map +1 -0
- package/dist/session/pause.js +99 -0
- package/dist/session/pause.js.map +1 -0
- package/dist/session/state.js +93 -0
- package/dist/session/state.js.map +1 -0
- package/dist/sourcemap/loader.js +138 -0
- package/dist/sourcemap/loader.js.map +1 -0
- package/dist/sourcemap/normalize.js +59 -0
- package/dist/sourcemap/normalize.js.map +1 -0
- package/dist/sourcemap/store.js +185 -0
- package/dist/sourcemap/store.js.map +1 -0
- package/dist/tools/_register.js +30 -0
- package/dist/tools/_register.js.map +1 -0
- package/dist/tools/breakpoints.js +164 -0
- package/dist/tools/breakpoints.js.map +1 -0
- package/dist/tools/console.js +48 -0
- package/dist/tools/console.js.map +1 -0
- package/dist/tools/dom.js +527 -0
- package/dist/tools/dom.js.map +1 -0
- package/dist/tools/execution.js +89 -0
- package/dist/tools/execution.js.map +1 -0
- package/dist/tools/inspect.js +178 -0
- package/dist/tools/inspect.js.map +1 -0
- package/dist/tools/nav.js +136 -0
- package/dist/tools/nav.js.map +1 -0
- package/dist/tools/network.js +137 -0
- package/dist/tools/network.js.map +1 -0
- package/dist/tools/session.js +76 -0
- package/dist/tools/session.js.map +1 -0
- package/dist/tools/source.js +63 -0
- package/dist/tools/source.js.map +1 -0
- package/dist/util/browser-resolve.js +263 -0
- package/dist/util/browser-resolve.js.map +1 -0
- package/dist/util/errors.js +12 -0
- package/dist/util/errors.js.map +1 -0
- package/dist/util/format.js +65 -0
- package/dist/util/format.js.map +1 -0
- package/dist/util/log.js +34 -0
- package/dist/util/log.js.map +1 -0
- package/package.json +74 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/session/state.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAwC,MAAM,cAAc,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAoBzD,MAAM,gBAAgB,GAAG,UAAU,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAE5B,MAAM,YAAY;IAChB,MAAM,GAAsB,IAAI,CAAC;IACjC,MAAM,GAA0B,IAAI,CAAC;IACrC,UAAU,GAAkB,IAAI,CAAC;IACjC,QAAQ,GAAG,KAAK,CAAC,CAAC,oEAAoE;IAEtF,eAAe,GAAkB,IAAI,CAAC;IACtC,gBAAgB,GAAuB,SAAS,CAAC,CAAC,2BAA2B;IAEpE,KAAK,GAAG,IAAI,YAAY,EAAE,CAAC;IAC3B,OAAO,GAAG,IAAI,UAAU,CAAe,IAAI,CAAC,CAAC;IAC7C,OAAO,GAAG,IAAI,UAAU,CAAe,IAAI,CAAC,CAAC;IAC7C,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAC5B,WAAW,GAAG,IAAI,GAAG,EAA4B,CAAC;IAC3D,kFAAkF;IAClF,iEAAiE;IACxD,eAAe,GAAG,IAAI,GAAG,EAA0B,CAAC;IAC7D,uEAAuE;IACvE,yEAAyE;IACzE,sDAAsD;IACtD,iBAAiB,GAAgC,MAAM,CAAC;IAExD,uFAAuF;IAC/E,SAAS,GAAG,CAAC,CAAC;IACtB,QAAQ;QACN,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;QACpB,OAAO,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;IAChC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC5B,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;QAClC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACnB,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QAC7B,IAAI,CAAC,iBAAiB,GAAG,MAAM,CAAC;QAChC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,KAAK;QACT,GAAG,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC5B,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChB,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;gBAC5B,CAAC;gBAAC,MAAM,CAAC;oBACP,YAAY;gBACd,CAAC;YACH,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC;gBACH,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;oBAClC,IAAI,CAAC;wBACH,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;oBACrB,CAAC;oBAAC,MAAM,CAAC;wBACP,YAAY;oBACd,CAAC;gBACH,CAAC;YACH,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC;AAE/C,MAAM,UAAU,UAAU;IACxB,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC,YAAY,CAAC,MAAM;QAAE,MAAM,SAAS,EAAE,CAAC;IAC5C,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,MAAM,CAAC,GAAG,cAAc,EAAE,CAAC;IAC3B,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE;QAAE,MAAM,SAAS,EAAE,CAAC;IAC3C,OAAO,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { log } from "../util/log.js";
|
|
2
|
+
// Wire up Debugger.scriptParsed to populate the store and lazily load source maps.
|
|
3
|
+
// Returns the registered handler so the caller can later `client.removeListener("Debugger.scriptParsed", handler)`.
|
|
4
|
+
export function attachScriptListener(client, store, sessionId) {
|
|
5
|
+
const handler = (params, eventSessionId) => {
|
|
6
|
+
// Each session sees its own events; gate strictly so root and children
|
|
7
|
+
// don't process each other's. (Strict equality — `if (sessionId && …)`
|
|
8
|
+
// was the original bug: sessionId=undefined made the gate vacuously
|
|
9
|
+
// false, so the root handler ran child events too.)
|
|
10
|
+
if (eventSessionId !== sessionId)
|
|
11
|
+
return;
|
|
12
|
+
if (!params.url) {
|
|
13
|
+
// anonymous inline scripts — skip; nothing to debug by file path
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
store.upsert({
|
|
17
|
+
scriptId: params.scriptId,
|
|
18
|
+
url: params.url,
|
|
19
|
+
sourceMapURL: params.sourceMapURL || undefined,
|
|
20
|
+
startLine: params.startLine,
|
|
21
|
+
startColumn: params.startColumn,
|
|
22
|
+
endLine: params.endLine,
|
|
23
|
+
endColumn: params.endColumn,
|
|
24
|
+
executionContextId: params.executionContextId,
|
|
25
|
+
hash: params.hash,
|
|
26
|
+
isModule: params.isModule,
|
|
27
|
+
...(sessionId ? { sessionId } : {}),
|
|
28
|
+
});
|
|
29
|
+
if (params.sourceMapURL) {
|
|
30
|
+
void loadSourceMap(client, store, params.scriptId, params.url, params.sourceMapURL, sessionId);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
client.on("Debugger.scriptParsed", handler);
|
|
34
|
+
return handler;
|
|
35
|
+
}
|
|
36
|
+
async function loadSourceMap(client, store, scriptId, scriptUrl, sourceMapURL, sessionId) {
|
|
37
|
+
try {
|
|
38
|
+
let raw;
|
|
39
|
+
let mapUrl;
|
|
40
|
+
if (sourceMapURL.startsWith("data:")) {
|
|
41
|
+
raw = decodeDataUri(sourceMapURL);
|
|
42
|
+
mapUrl = scriptUrl;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
const resolved = new URL(sourceMapURL, scriptUrl).toString();
|
|
46
|
+
mapUrl = resolved;
|
|
47
|
+
raw = await fetchMap(client, resolved, sessionId);
|
|
48
|
+
}
|
|
49
|
+
store.attachMap(scriptId, sessionId, raw, mapUrl);
|
|
50
|
+
log.debug("source-map loaded", { scriptUrl, mapUrl });
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
store.setLoadError(scriptId, sessionId, `fetch failed: ${String(e)}`);
|
|
54
|
+
log.warn("source-map fetch failed", { scriptUrl, sourceMapURL, error: String(e) });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Decode a data: URI body, supporting the RFC 2397 multi-parameter form
|
|
58
|
+
// (`data:<type>;charset=utf-8;base64,…`) that webpack's `inline-source-map`
|
|
59
|
+
// devtool emits. The previous `[^,;]*` regex rejected anything with extra
|
|
60
|
+
// parameters between the type and `;base64`, so webpack-inlined source maps
|
|
61
|
+
// never loaded and TS-aware tools silently degraded to JS coords.
|
|
62
|
+
export function decodeDataUri(uri) {
|
|
63
|
+
if (!uri.startsWith("data:"))
|
|
64
|
+
throw new Error("Not a data URI");
|
|
65
|
+
const comma = uri.indexOf(",");
|
|
66
|
+
if (comma < 0)
|
|
67
|
+
throw new Error("Malformed data URI (no comma)");
|
|
68
|
+
const meta = uri.slice(5, comma); // between `data:` and `,`
|
|
69
|
+
const payload = uri.slice(comma + 1);
|
|
70
|
+
if (!payload)
|
|
71
|
+
throw new Error("Empty data URI payload");
|
|
72
|
+
const params = meta.split(";");
|
|
73
|
+
const isBase64 = params.includes("base64");
|
|
74
|
+
if (isBase64)
|
|
75
|
+
return Buffer.from(payload, "base64").toString("utf8");
|
|
76
|
+
return decodeURIComponent(payload);
|
|
77
|
+
}
|
|
78
|
+
async function fetchMap(client, url, sessionId) {
|
|
79
|
+
// Prefer using the browser's network stack so we go through the page's
|
|
80
|
+
// origin/CORS context (cookies, auth, dev-server middleware).
|
|
81
|
+
//
|
|
82
|
+
// CDP docs: `Network.loadNetworkResource.frameId` is "Mandatory for frame
|
|
83
|
+
// targets, and should be omitted for worker targets." Without it, the
|
|
84
|
+
// preferred browser-context path fails for page scripts and silently
|
|
85
|
+
// falls back to Node fetch — which works for plain localhost but loses
|
|
86
|
+
// the auth/cookie/origin context we actually want.
|
|
87
|
+
//
|
|
88
|
+
// Strategy: opportunistically resolve the top frame id via Page domain.
|
|
89
|
+
// It succeeds on page/OOPIF sessions, throws on worker sessions where
|
|
90
|
+
// Page is unavailable — exactly the two cases CDP wants us to handle.
|
|
91
|
+
let frameId;
|
|
92
|
+
try {
|
|
93
|
+
const tree = await client.Page.getFrameTree(sessionId);
|
|
94
|
+
frameId = tree.frameTree.frame.id;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Worker / service-worker session — Page domain unavailable; omit frameId.
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
const { resource } = await client.Network.loadNetworkResource({
|
|
101
|
+
...(frameId ? { frameId } : {}),
|
|
102
|
+
url,
|
|
103
|
+
// includeCredentials: true — the whole point of the browser-context
|
|
104
|
+
// path is to inherit the page's auth (cookies, sessions, dev-server
|
|
105
|
+
// middleware). false would defeat the intent and bounce
|
|
106
|
+
// session-protected source maps back to the credential-less Node
|
|
107
|
+
// fetch fallback.
|
|
108
|
+
options: { disableCache: false, includeCredentials: true },
|
|
109
|
+
}, sessionId);
|
|
110
|
+
if (!resource?.success || !resource.stream) {
|
|
111
|
+
throw new Error(`loadNetworkResource failed: ${resource?.netError ?? resource?.httpStatusCode}`);
|
|
112
|
+
}
|
|
113
|
+
const first = await client.IO.read({ handle: resource.stream }, sessionId);
|
|
114
|
+
let combined = first.base64Encoded ? Buffer.from(first.data, "base64").toString("utf8") : first.data;
|
|
115
|
+
let eof = first.eof;
|
|
116
|
+
while (!eof) {
|
|
117
|
+
const next = await client.IO.read({ handle: resource.stream }, sessionId);
|
|
118
|
+
combined += next.base64Encoded ? Buffer.from(next.data, "base64").toString("utf8") : next.data;
|
|
119
|
+
eof = next.eof;
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
await client.IO.close({ handle: resource.stream }, sessionId);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
/* ignore */
|
|
126
|
+
}
|
|
127
|
+
return combined;
|
|
128
|
+
}
|
|
129
|
+
catch (browserErr) {
|
|
130
|
+
// Fallback: fetch directly from Node — works for localhost dev servers.
|
|
131
|
+
log.debug("loadNetworkResource failed, falling back to Node fetch", { url, error: String(browserErr) });
|
|
132
|
+
const res = await fetch(url);
|
|
133
|
+
if (!res.ok)
|
|
134
|
+
throw new Error(`HTTP ${res.status} fetching ${url}`);
|
|
135
|
+
return await res.text();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/sourcemap/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Normalize the messy URLs that bundlers emit into source maps and CDP scripts.
|
|
2
|
+
//
|
|
3
|
+
// Examples we want to fold to "src/foo.ts":
|
|
4
|
+
// webpack:///./src/foo.ts
|
|
5
|
+
// webpack-internal:///./src/foo.ts
|
|
6
|
+
// webpack://app/./src/foo.ts
|
|
7
|
+
// ./src/foo.ts
|
|
8
|
+
// /src/foo.ts
|
|
9
|
+
// file:///C:/proj/src/foo.ts
|
|
10
|
+
//
|
|
11
|
+
// We don't try to resolve to an absolute on-disk path — we only need stable
|
|
12
|
+
// matching keys so a user-supplied "src/foo.ts" can be located.
|
|
13
|
+
const KNOWN_PREFIXES = [
|
|
14
|
+
/^webpack-internal:\/\/\/(?:\.\/)?/,
|
|
15
|
+
/^webpack:\/\/[^/]*\/(?:\.\/)?/,
|
|
16
|
+
/^webpack:\/\/\/(?:\.\/)?/,
|
|
17
|
+
/^rollup:\/\/(?:\.\/)?/,
|
|
18
|
+
/^vite-fs:\/\/(?:\.\/)?/,
|
|
19
|
+
/^source-map:\/\/(?:\.\/)?/,
|
|
20
|
+
];
|
|
21
|
+
export function normalizeSourcePath(raw) {
|
|
22
|
+
if (!raw)
|
|
23
|
+
return raw;
|
|
24
|
+
let s = raw;
|
|
25
|
+
for (const re of KNOWN_PREFIXES) {
|
|
26
|
+
if (re.test(s)) {
|
|
27
|
+
s = s.replace(re, "");
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (s.startsWith("file://")) {
|
|
32
|
+
s = s.slice("file://".length);
|
|
33
|
+
if (/^\/[A-Za-z]:/.test(s))
|
|
34
|
+
s = s.slice(1);
|
|
35
|
+
}
|
|
36
|
+
// strip a single leading slash for matching
|
|
37
|
+
if (s.startsWith("/"))
|
|
38
|
+
s = s.slice(1);
|
|
39
|
+
if (s.startsWith("./"))
|
|
40
|
+
s = s.slice(2);
|
|
41
|
+
return s.replace(/\\/g, "/");
|
|
42
|
+
}
|
|
43
|
+
// Does `candidate` look like the same file as `query`?
|
|
44
|
+
// We accept matches where one is a strict suffix of the other on path segments.
|
|
45
|
+
export function pathMatches(candidate, query) {
|
|
46
|
+
const c = normalizeSourcePath(candidate);
|
|
47
|
+
const q = normalizeSourcePath(query);
|
|
48
|
+
if (c === q)
|
|
49
|
+
return true;
|
|
50
|
+
const cs = c.split("/").filter(Boolean);
|
|
51
|
+
const qs = q.split("/").filter(Boolean);
|
|
52
|
+
if (cs.length === 0 || qs.length === 0)
|
|
53
|
+
return false;
|
|
54
|
+
if (cs.length >= qs.length) {
|
|
55
|
+
return cs.slice(-qs.length).join("/") === qs.join("/");
|
|
56
|
+
}
|
|
57
|
+
return qs.slice(-cs.length).join("/") === cs.join("/");
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/sourcemap/normalize.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,EAAE;AACF,4CAA4C;AAC5C,4BAA4B;AAC5B,qCAAqC;AACrC,+BAA+B;AAC/B,iBAAiB;AACjB,gBAAgB;AAChB,+BAA+B;AAC/B,EAAE;AACF,4EAA4E;AAC5E,gEAAgE;AAEhE,MAAM,cAAc,GAAG;IACrB,mCAAmC;IACnC,+BAA+B;IAC/B,0BAA0B;IAC1B,uBAAuB;IACvB,wBAAwB;IACxB,2BAA2B;CAC5B,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC7C,IAAI,CAAC,GAAG;QAAE,OAAO,GAAG,CAAC;IACrB,IAAI,CAAC,GAAG,GAAG,CAAC;IACZ,KAAK,MAAM,EAAE,IAAI,cAAc,EAAE,CAAC;QAChC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACf,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YACtB,MAAM;QACR,CAAC;IACH,CAAC;IACD,IAAI,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC5B,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC9B,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7C,CAAC;IACD,4CAA4C;IAC5C,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACtC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACvC,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED,uDAAuD;AACvD,gFAAgF;AAChF,MAAM,UAAU,WAAW,CAAC,SAAiB,EAAE,KAAa;IAC1D,MAAM,CAAC,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,CAAC,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACrD,IAAI,EAAE,CAAC,MAAM,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC;QAC3B,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { SourceMapConsumer } from "@jridgewell/source-map";
|
|
2
|
+
import { normalizeSourcePath, pathMatches } from "./normalize.js";
|
|
3
|
+
import { log } from "../util/log.js";
|
|
4
|
+
// CDP scriptIds are scoped per Debugger agent (per flat session). Two
|
|
5
|
+
// targets — page + worker, or two iframes — can independently emit
|
|
6
|
+
// scriptId="42". Keying the store on scriptId alone would let the second
|
|
7
|
+
// upsert silently overwrite the first, attach the wrong source map, or
|
|
8
|
+
// resolve paused frames to the wrong script. Compound key is sessionId
|
|
9
|
+
// (or a synthetic root marker) + scriptId.
|
|
10
|
+
const ROOT_KEY = "__root__";
|
|
11
|
+
const keyFor = (scriptId, sessionId) => `${sessionId ?? ROOT_KEY} ${scriptId}`;
|
|
12
|
+
// Indexes scripts the browser has parsed and their (optional) source maps.
|
|
13
|
+
export class ScriptStore {
|
|
14
|
+
byKey = new Map();
|
|
15
|
+
// HMR / soft-reload note: when a script is re-parsed under the same
|
|
16
|
+
// (sessionId, scriptId), Object.assign(existing, info) copies the new
|
|
17
|
+
// metadata fields but does NOT overwrite `consumer`, `sources`, or
|
|
18
|
+
// `loadError` because they aren't in the upsert payload (the input type
|
|
19
|
+
// explicitly Omit<>'s them). A subsequent Debugger.scriptParsed for the
|
|
20
|
+
// *same* scriptId therefore inherits the OLD source-map consumer until
|
|
21
|
+
// attachMap() is called again. This is intentional for soft reloads
|
|
22
|
+
// (the source-map URL is usually the same), but means HMR-changed maps
|
|
23
|
+
// can leak stale mappings until the next session reset() clears them.
|
|
24
|
+
// close_session/switchTarget call ScriptStore.clear() so cross-session
|
|
25
|
+
// contamination is bounded — within a session, downstream callers must
|
|
26
|
+
// re-attachMap() after any reload that may have changed the map.
|
|
27
|
+
upsert(info) {
|
|
28
|
+
const key = keyFor(info.scriptId, info.sessionId);
|
|
29
|
+
const existing = this.byKey.get(key);
|
|
30
|
+
if (existing) {
|
|
31
|
+
Object.assign(existing, info);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
this.byKey.set(key, { ...info });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
get(scriptId, sessionId) {
|
|
38
|
+
return this.byKey.get(keyFor(scriptId, sessionId));
|
|
39
|
+
}
|
|
40
|
+
all() {
|
|
41
|
+
return Array.from(this.byKey.values());
|
|
42
|
+
}
|
|
43
|
+
remove(scriptId, sessionId) {
|
|
44
|
+
this.byKey.delete(keyFor(scriptId, sessionId));
|
|
45
|
+
}
|
|
46
|
+
clear() {
|
|
47
|
+
for (const s of this.byKey.values())
|
|
48
|
+
s.consumer?.destroy();
|
|
49
|
+
this.byKey.clear();
|
|
50
|
+
}
|
|
51
|
+
// Find scripts whose source map references the given TS file.
|
|
52
|
+
findByOriginalSource(file) {
|
|
53
|
+
const matches = [];
|
|
54
|
+
for (const s of this.byKey.values()) {
|
|
55
|
+
if (!s.sources)
|
|
56
|
+
continue;
|
|
57
|
+
if (s.sources.some((src) => pathMatches(src, file))) {
|
|
58
|
+
matches.push(s);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return matches;
|
|
62
|
+
}
|
|
63
|
+
attachMap(scriptId, sessionId, raw, mapUrl) {
|
|
64
|
+
const script = this.byKey.get(keyFor(scriptId, sessionId));
|
|
65
|
+
if (!script)
|
|
66
|
+
return null;
|
|
67
|
+
try {
|
|
68
|
+
const consumer = new SourceMapConsumer(raw, mapUrl);
|
|
69
|
+
script.consumer = consumer;
|
|
70
|
+
script.sources = (consumer.sources ?? []).map((s) => normalizeSourcePath(s ?? ""));
|
|
71
|
+
script.loadError = undefined;
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
script.loadError = `source-map parse failed: ${String(e)}`;
|
|
75
|
+
log.warn("source-map parse failed", { scriptId, sessionId, url: script.url, error: String(e) });
|
|
76
|
+
}
|
|
77
|
+
return script;
|
|
78
|
+
}
|
|
79
|
+
setLoadError(scriptId, sessionId, err) {
|
|
80
|
+
const s = this.byKey.get(keyFor(scriptId, sessionId));
|
|
81
|
+
if (s)
|
|
82
|
+
s.loadError = err;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Helpers for converting CDP <-> source-map line numbering.
|
|
86
|
+
// CDP: 0-based line, 0-based column.
|
|
87
|
+
// source-map: 1-based line, 0-based column.
|
|
88
|
+
// Public tool API: 1-based line, 0-based column.
|
|
89
|
+
export function cdpToPublic(loc) {
|
|
90
|
+
return { line: loc.lineNumber + 1, column: loc.columnNumber ?? 0 };
|
|
91
|
+
}
|
|
92
|
+
export function publicToCdp(loc) {
|
|
93
|
+
return { lineNumber: loc.line - 1, columnNumber: loc.column ?? 0 };
|
|
94
|
+
}
|
|
95
|
+
// Translate a CDP-paused frame's location to a public TS location, if a map is available.
|
|
96
|
+
export function mapCdpToOriginal(store, frame, sessionId) {
|
|
97
|
+
const script = store.get(frame.scriptId, sessionId);
|
|
98
|
+
if (!script?.consumer)
|
|
99
|
+
return null;
|
|
100
|
+
const orig = script.consumer.originalPositionFor({
|
|
101
|
+
line: frame.lineNumber + 1, // source-map is 1-based
|
|
102
|
+
column: frame.columnNumber ?? 0,
|
|
103
|
+
});
|
|
104
|
+
if (orig.source == null || orig.line == null)
|
|
105
|
+
return null;
|
|
106
|
+
return {
|
|
107
|
+
file: normalizeSourcePath(orig.source),
|
|
108
|
+
line: orig.line, // already 1-based
|
|
109
|
+
column: orig.column ?? 0,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// Translate a public TS coord to one or more CDP coords for setBreakpointByUrl.
|
|
113
|
+
//
|
|
114
|
+
// Uses `allGeneratedPositionsFor` (NOT `generatedPositionFor`) because the
|
|
115
|
+
// latter requires an EXACT (line, column) match against the source map's
|
|
116
|
+
// emitted mappings — and esbuild/vite typically emit mappings only at
|
|
117
|
+
// statement starts (e.g., column 2 for an indented `return x;`), not at
|
|
118
|
+
// column 0. Setting a breakpoint at `handlers.ts:12` should land regardless
|
|
119
|
+
// of whether the caller supplied column 0 (the default) or the exact
|
|
120
|
+
// column of the first statement; `allGeneratedPositionsFor` enumerates
|
|
121
|
+
// every generated position for the source line, which is the contract
|
|
122
|
+
// debuggers want for breakpoint resolution. (PR #11 e2e bug — line 12 of
|
|
123
|
+
// the sample app has mappings at columns 2 and 9 but none at column 0.)
|
|
124
|
+
//
|
|
125
|
+
// If the source line has multiple generated positions across distinct
|
|
126
|
+
// generated lines (rare with single-line vite bundles, common after
|
|
127
|
+
// minification splits or with classic webpack), each is emitted as its
|
|
128
|
+
// own GeneratedLocation — Debugger.setBreakpointByUrl ends up binding at
|
|
129
|
+
// each, which is what users intuitively expect.
|
|
130
|
+
export function mapOriginalToGenerated(store, file, line, // 1-based
|
|
131
|
+
column = 0) {
|
|
132
|
+
const out = [];
|
|
133
|
+
for (const script of store.findByOriginalSource(file)) {
|
|
134
|
+
if (!script.consumer)
|
|
135
|
+
continue;
|
|
136
|
+
const sourceKey = pickSourceKey(script, file);
|
|
137
|
+
if (!sourceKey)
|
|
138
|
+
continue;
|
|
139
|
+
// Forward the caller's column. allGeneratedPositionsFor with column=0
|
|
140
|
+
// returns the first mapping on the source line (the line-broad case
|
|
141
|
+
// set_breakpoint uses by default). With a non-zero column it returns
|
|
142
|
+
// the mapping at-or-after that original column — honoring an
|
|
143
|
+
// explicitly-supplied column without forcing exact-match strictness.
|
|
144
|
+
// (Codex/Opus PR #11 round-2: prior version pinned column:0 here and
|
|
145
|
+
// silently ignored the caller's value.)
|
|
146
|
+
const positions = script.consumer.allGeneratedPositionsFor({
|
|
147
|
+
source: sourceKey,
|
|
148
|
+
line, // 1-based in
|
|
149
|
+
column,
|
|
150
|
+
});
|
|
151
|
+
// De-dup by (gen line, gen col) — different originalColumn mappings on
|
|
152
|
+
// the same source line can collapse to the same generated position
|
|
153
|
+
// after minification, but we only need one breakpoint binding per
|
|
154
|
+
// distinct generated location.
|
|
155
|
+
const seen = new Set();
|
|
156
|
+
for (const gen of positions) {
|
|
157
|
+
if (gen.line == null)
|
|
158
|
+
continue;
|
|
159
|
+
const key = `${gen.line}:${gen.column ?? 0}`;
|
|
160
|
+
if (seen.has(key))
|
|
161
|
+
continue;
|
|
162
|
+
seen.add(key);
|
|
163
|
+
out.push({
|
|
164
|
+
scriptId: script.scriptId,
|
|
165
|
+
scriptUrl: script.url,
|
|
166
|
+
...(script.sessionId ? { sessionId: script.sessionId } : {}),
|
|
167
|
+
lineNumber: gen.line - 1, // back to 0-based for CDP
|
|
168
|
+
columnNumber: gen.column ?? 0,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return out;
|
|
173
|
+
}
|
|
174
|
+
function pickSourceKey(script, file) {
|
|
175
|
+
if (!script.consumer)
|
|
176
|
+
return null;
|
|
177
|
+
// We want the source as it appears in the map (with whatever prefix it had)
|
|
178
|
+
// so the consumer can look it up.
|
|
179
|
+
for (const raw of script.consumer.sources ?? []) {
|
|
180
|
+
if (raw && pathMatches(raw, file))
|
|
181
|
+
return raw;
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/sourcemap/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ToolError } from "../util/errors.js";
|
|
2
|
+
import { log } from "../util/log.js";
|
|
3
|
+
import { toolJson, toolText } from "../util/format.js";
|
|
4
|
+
// Wrap a tool handler with consistent error handling so any thrown error
|
|
5
|
+
// becomes a structured `isError: true` result rather than an MCP transport error.
|
|
6
|
+
export function registerJsonTool(server, name, description, inputShape, handler) {
|
|
7
|
+
server.registerTool(name, {
|
|
8
|
+
description,
|
|
9
|
+
...(inputShape ? { inputSchema: inputShape } : {}),
|
|
10
|
+
}, async (args) => {
|
|
11
|
+
try {
|
|
12
|
+
const result = await handler((args ?? {}));
|
|
13
|
+
if (result === undefined)
|
|
14
|
+
return toolText("ok");
|
|
15
|
+
if (typeof result === "string")
|
|
16
|
+
return toolText(result);
|
|
17
|
+
return toolJson(result);
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
const code = e instanceof ToolError ? e.code : "internal_error";
|
|
21
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
22
|
+
log.warn(`tool error ${name}`, { code, msg });
|
|
23
|
+
return {
|
|
24
|
+
isError: true,
|
|
25
|
+
content: [{ type: "text", text: JSON.stringify({ error: code, message: msg }) }],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=_register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_register.js","sourceRoot":"","sources":["../../src/tools/_register.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAEvD,yEAAyE;AACzE,kFAAkF;AAClF,MAAM,UAAU,gBAAgB,CAC9B,MAAiB,EACjB,IAAY,EACZ,WAAmB,EACnB,UAA2C,EAC3C,OAAsD;IAEtD,MAAM,CAAC,YAAY,CACjB,IAAI,EACJ;QACE,WAAW;QACX,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,UAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1D,EACD,KAAK,EAAE,IAAS,EAAE,EAAE;QAClB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,CAAC,IAAI,IAAI,EAAE,CAAW,CAAC,CAAC;YACrD,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;YAChD,IAAI,OAAO,MAAM,KAAK,QAAQ;gBAAE,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC;YACxD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC1B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,IAAI,GAAG,CAAC,YAAY,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC;YAChE,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACvD,GAAG,CAAC,IAAI,CAAC,cAAc,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;YAC9C,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;aAC1F,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { requireSession, ROOT_SESSION_KEY } from "../session/state.js";
|
|
3
|
+
import { mapOriginalToGenerated, mapCdpToOriginal } from "../sourcemap/store.js";
|
|
4
|
+
import { ToolError } from "../util/errors.js";
|
|
5
|
+
import { registerJsonTool } from "./_register.js";
|
|
6
|
+
// Look up an existing breakpoint by normalized (file, line, column). Column
|
|
7
|
+
// is always a number here — the caller is responsible for collapsing an
|
|
8
|
+
// omitted or undefined `input.column` to 0 before calling, matching the
|
|
9
|
+
// `input.column ?? 0` shape that mapOriginalToGenerated and list_breakpoints
|
|
10
|
+
// already use. If the three normalizations ever diverge, set_breakpoint's
|
|
11
|
+
// idempotent path silently breaks (see PR #20 review).
|
|
12
|
+
function findBreakpointAt(breakpoints, file, line, column) {
|
|
13
|
+
for (const r of breakpoints.values()) {
|
|
14
|
+
if (r.file === file && r.line === line && (r.column ?? 0) === column)
|
|
15
|
+
return r;
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
function breakpointEnvelope(r, status) {
|
|
20
|
+
return {
|
|
21
|
+
id: r.id,
|
|
22
|
+
resolved_locations: r.resolvedLocations,
|
|
23
|
+
binding_count: r.bindings.length,
|
|
24
|
+
sessions_bound: Array.from(new Set(r.bindings.map((b) => b.sessionId ?? ROOT_SESSION_KEY))),
|
|
25
|
+
status,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function registerBreakpointTools(server) {
|
|
29
|
+
registerJsonTool(server, "set_breakpoint", 'Set a breakpoint in TypeScript source. Resolves matching scripts via source maps (including in workers and iframes) and binds in each one\'s session. Returns the resolved JS->TS locations. Idempotent: re-calling with identical (file, line, column, condition, log_message) returns the existing breakpoint id with status: "already-set". Same location with a different condition/log_message returns error: "breakpoint_conflict" — remove the existing breakpoint first.', {
|
|
30
|
+
file: z.string().describe("TS file path or fragment (e.g. src/foo.ts)"),
|
|
31
|
+
line: z.number().int().positive().describe("1-based"),
|
|
32
|
+
column: z.number().int().nonnegative().optional(),
|
|
33
|
+
condition: z.string().optional().describe("Expression — pause only when truthy"),
|
|
34
|
+
log_message: z.string().optional().describe("Logpoint: log instead of pausing"),
|
|
35
|
+
}, async (input) => {
|
|
36
|
+
const s = requireSession();
|
|
37
|
+
// Normalize once at the top so lookup, mapping, and storage cannot
|
|
38
|
+
// drift. column collapses `undefined` and omitted to 0 — matching what
|
|
39
|
+
// mapOriginalToGenerated and list_breakpoints already do. condition /
|
|
40
|
+
// logMessage truthy-normalize so the empty string and undefined match.
|
|
41
|
+
const file = input.file;
|
|
42
|
+
const line = input.line;
|
|
43
|
+
const column = input.column ?? 0;
|
|
44
|
+
const condition = input.condition || undefined;
|
|
45
|
+
const logMessage = input.log_message || undefined;
|
|
46
|
+
const existing = findBreakpointAt(s.breakpoints, file, line, column);
|
|
47
|
+
if (existing) {
|
|
48
|
+
if (existing.condition === condition && existing.logMessage === logMessage) {
|
|
49
|
+
return breakpointEnvelope(existing, "already-set");
|
|
50
|
+
}
|
|
51
|
+
const locStr = `${file}:${line}:${column}`;
|
|
52
|
+
throw new ToolError("breakpoint_conflict", `Breakpoint already exists at ${locStr} (id ${existing.id}) with a different condition or log_message. Remove it first (remove_breakpoint) before setting a new one.`);
|
|
53
|
+
}
|
|
54
|
+
const candidates = mapOriginalToGenerated(s.scripts, file, line, column);
|
|
55
|
+
if (candidates.length === 0) {
|
|
56
|
+
throw new ToolError("no_mapping", `No source-mapped script matches '${file}:${line}'. Try list_scripts to confirm what's loaded.`);
|
|
57
|
+
}
|
|
58
|
+
const id = s.nextBpId();
|
|
59
|
+
const bindings = [];
|
|
60
|
+
const resolved = [];
|
|
61
|
+
const conditionExpr = buildConditionExpression(condition, logMessage);
|
|
62
|
+
for (const c of candidates) {
|
|
63
|
+
// Use the exact `url` field rather than `urlRegex`. CDP's regex is
|
|
64
|
+
// unanchored — `http://localhost/main.js` as a urlRegex also matches
|
|
65
|
+
// `http://localhost/main.js?v=2`, `…?vue&type=template`, etc.
|
|
66
|
+
const params = {
|
|
67
|
+
url: c.scriptUrl,
|
|
68
|
+
lineNumber: c.lineNumber,
|
|
69
|
+
columnNumber: c.columnNumber,
|
|
70
|
+
...(conditionExpr ? { condition: conditionExpr } : {}),
|
|
71
|
+
};
|
|
72
|
+
const res = await s.client.send("Debugger.setBreakpointByUrl", params, c.sessionId);
|
|
73
|
+
bindings.push({ cdpId: res.breakpointId, ...(c.sessionId ? { sessionId: c.sessionId } : {}) });
|
|
74
|
+
for (const loc of res.locations) {
|
|
75
|
+
const orig = mapCdpToOriginal(s.scripts, loc, c.sessionId);
|
|
76
|
+
if (orig)
|
|
77
|
+
resolved.push(orig);
|
|
78
|
+
else
|
|
79
|
+
resolved.push({ file: c.scriptUrl, line: loc.lineNumber + 1, column: loc.columnNumber ?? 0 });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const record = {
|
|
83
|
+
id,
|
|
84
|
+
file,
|
|
85
|
+
line,
|
|
86
|
+
column,
|
|
87
|
+
...(condition ? { condition } : {}),
|
|
88
|
+
...(logMessage ? { logMessage } : {}),
|
|
89
|
+
resolvedLocations: resolved,
|
|
90
|
+
bindings,
|
|
91
|
+
};
|
|
92
|
+
s.breakpoints.set(id, record);
|
|
93
|
+
return breakpointEnvelope(record, "set");
|
|
94
|
+
});
|
|
95
|
+
registerJsonTool(server, "remove_breakpoint", "Remove a breakpoint by ID (returned from set_breakpoint).", { id: z.string() }, async (input) => {
|
|
96
|
+
const s = requireSession();
|
|
97
|
+
const rec = s.breakpoints.get(input.id);
|
|
98
|
+
if (!rec)
|
|
99
|
+
throw new ToolError("not_found", `No breakpoint with id ${input.id}`);
|
|
100
|
+
for (const b of rec.bindings) {
|
|
101
|
+
try {
|
|
102
|
+
await s.client.send("Debugger.removeBreakpoint", { breakpointId: b.cdpId }, b.sessionId);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
/* ignore — session may already be gone */
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
s.breakpoints.delete(input.id);
|
|
109
|
+
return "removed";
|
|
110
|
+
});
|
|
111
|
+
registerJsonTool(server, "list_breakpoints", "List all currently active breakpoints.", undefined, async () => {
|
|
112
|
+
const s = requireSession();
|
|
113
|
+
return Array.from(s.breakpoints.values()).map((bp) => ({
|
|
114
|
+
id: bp.id,
|
|
115
|
+
file: bp.file,
|
|
116
|
+
line: bp.line,
|
|
117
|
+
column: bp.column ?? 0,
|
|
118
|
+
condition: bp.condition,
|
|
119
|
+
log_message: bp.logMessage,
|
|
120
|
+
resolved_locations: bp.resolvedLocations,
|
|
121
|
+
binding_count: bp.bindings.length,
|
|
122
|
+
}));
|
|
123
|
+
});
|
|
124
|
+
registerJsonTool(server, "set_pause_on_exceptions", "Configure whether the debugger pauses on exceptions. Applies to the root AND all currently-attached child sessions (workers/iframes/service workers), and is remembered so newly-attached children inherit the setting.", {
|
|
125
|
+
state: z.enum(["none", "uncaught", "all"]),
|
|
126
|
+
}, async (input) => {
|
|
127
|
+
const s = requireSession();
|
|
128
|
+
// Persist so onChildAttached can replay to future attachments.
|
|
129
|
+
s.pauseOnExceptions = input.state;
|
|
130
|
+
// Apply to every currently-attached session. ROOT_SESSION_KEY → undefined
|
|
131
|
+
// for the CDP call; child keys are the literal sessionId.
|
|
132
|
+
const targetSessions = [];
|
|
133
|
+
for (const key of s.sessionHandlers.keys()) {
|
|
134
|
+
targetSessions.push(key === ROOT_SESSION_KEY ? undefined : key);
|
|
135
|
+
}
|
|
136
|
+
if (targetSessions.length === 0)
|
|
137
|
+
targetSessions.push(undefined); // safety net
|
|
138
|
+
const results = await Promise.allSettled(targetSessions.map((sid) => s.client.send("Debugger.setPauseOnExceptions", { state: input.state }, sid)));
|
|
139
|
+
const failures = results
|
|
140
|
+
.map((r, i) => (r.status === "rejected" ? { sid: targetSessions[i] ?? "__root__", error: String(r.reason) } : null))
|
|
141
|
+
.filter((x) => x !== null);
|
|
142
|
+
return {
|
|
143
|
+
state: input.state,
|
|
144
|
+
sessions_applied: targetSessions.length - failures.length,
|
|
145
|
+
failures,
|
|
146
|
+
};
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
// Combine an optional `condition` predicate with an optional `logMessage`.
|
|
150
|
+
// Logpoint: emit a console.log and never pause (`false` short-circuits).
|
|
151
|
+
/** @internal exported for unit tests; not part of the MCP tool surface. */
|
|
152
|
+
export function buildConditionExpression(condition, logMessage) {
|
|
153
|
+
if (!condition && !logMessage)
|
|
154
|
+
return undefined;
|
|
155
|
+
if (logMessage) {
|
|
156
|
+
const tmpl = JSON.stringify(logMessage);
|
|
157
|
+
const log = `console.log(${tmpl}.replace(/\\{([^}]+)\\}/g, (m, expr) => { try { return String(eval(expr)); } catch (e) { return "{" + expr + "=?}"; } }))`;
|
|
158
|
+
if (condition)
|
|
159
|
+
return `(${condition}) && (${log}, false)`;
|
|
160
|
+
return `(${log}, false)`;
|
|
161
|
+
}
|
|
162
|
+
return condition;
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=breakpoints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"breakpoints.js","sourceRoot":"","sources":["../../src/tools/breakpoints.ts"],"names":[],"mappings":"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"}
|