cdp-mcp 0.2.1 → 0.4.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/README.md +28 -303
- package/bin.js +23 -0
- package/contract.d.ts +1 -0
- package/contract.js +2 -0
- package/index.d.ts +1 -0
- package/index.js +2 -0
- package/package.json +31 -73
- package/dist/contract.d.ts +0 -11
- package/dist/contract.js +0 -11
- package/dist/contract.js.map +0 -1
- package/dist/index.d.ts +0 -18
- package/dist/index.js +0 -402
- package/dist/index.js.map +0 -1
- package/dist/locator.d.ts +0 -108
- package/dist/locator.js +0 -176
- package/dist/locator.js.map +0 -1
- package/dist/server.d.ts +0 -2
- package/dist/server.js +0 -41
- package/dist/server.js.map +0 -1
- package/dist/session/browser.d.ts +0 -29
- package/dist/session/browser.js +0 -409
- package/dist/session/browser.js.map +0 -1
- package/dist/session/buffers.d.ts +0 -48
- package/dist/session/buffers.js +0 -43
- package/dist/session/buffers.js.map +0 -1
- package/dist/session/pause.d.ts +0 -21
- package/dist/session/pause.js +0 -99
- package/dist/session/pause.js.map +0 -1
- package/dist/session/state.d.ts +0 -53
- package/dist/session/state.js +0 -93
- package/dist/session/state.js.map +0 -1
- package/dist/sourcemap/loader.d.ts +0 -4
- package/dist/sourcemap/loader.js +0 -138
- package/dist/sourcemap/loader.js.map +0 -1
- package/dist/sourcemap/normalize.d.ts +0 -2
- package/dist/sourcemap/normalize.js +0 -59
- package/dist/sourcemap/normalize.js.map +0 -1
- package/dist/sourcemap/store.d.ts +0 -57
- package/dist/sourcemap/store.js +0 -185
- package/dist/sourcemap/store.js.map +0 -1
- package/dist/tools/_locator_runtime.d.ts +0 -31
- package/dist/tools/_locator_runtime.js +0 -243
- package/dist/tools/_locator_runtime.js.map +0 -1
- package/dist/tools/_register.d.ts +0 -2
- package/dist/tools/_register.js +0 -30
- package/dist/tools/_register.js.map +0 -1
- package/dist/tools/breakpoints.d.ts +0 -4
- package/dist/tools/breakpoints.js +0 -164
- package/dist/tools/breakpoints.js.map +0 -1
- package/dist/tools/console.d.ts +0 -2
- package/dist/tools/console.js +0 -48
- package/dist/tools/console.js.map +0 -1
- package/dist/tools/dom.d.ts +0 -2
- package/dist/tools/dom.js +0 -309
- package/dist/tools/dom.js.map +0 -1
- package/dist/tools/execution.d.ts +0 -29
- package/dist/tools/execution.js +0 -89
- package/dist/tools/execution.js.map +0 -1
- package/dist/tools/forms.d.ts +0 -8
- package/dist/tools/forms.js +0 -256
- package/dist/tools/forms.js.map +0 -1
- package/dist/tools/inspect.d.ts +0 -2
- package/dist/tools/inspect.js +0 -178
- package/dist/tools/inspect.js.map +0 -1
- package/dist/tools/nav.d.ts +0 -2
- package/dist/tools/nav.js +0 -136
- package/dist/tools/nav.js.map +0 -1
- package/dist/tools/network.d.ts +0 -2
- package/dist/tools/network.js +0 -137
- package/dist/tools/network.js.map +0 -1
- package/dist/tools/session.d.ts +0 -2
- package/dist/tools/session.js +0 -76
- package/dist/tools/session.js.map +0 -1
- package/dist/tools/source.d.ts +0 -2
- package/dist/tools/source.js +0 -63
- package/dist/tools/source.js.map +0 -1
- package/dist/tools/storage.d.ts +0 -2
- package/dist/tools/storage.js +0 -296
- package/dist/tools/storage.js.map +0 -1
- package/dist/util/browser-resolve.d.ts +0 -19
- package/dist/util/browser-resolve.js +0 -263
- package/dist/util/browser-resolve.js.map +0 -1
- package/dist/util/errors.d.ts +0 -7
- package/dist/util/errors.js +0 -12
- package/dist/util/errors.js.map +0 -1
- package/dist/util/format.d.ts +0 -20
- package/dist/util/format.js +0 -65
- package/dist/util/format.js.map +0 -1
- package/dist/util/log.d.ts +0 -6
- package/dist/util/log.js +0 -34
- package/dist/util/log.js.map +0 -1
- package/docs/chromium-sandboxing.md +0 -197
- package/docs/known-chromium-gaps.md +0 -138
- package/docs/launchd-service.md +0 -217
- package/docs/local-l3-e2e-setup.md +0 -199
- package/docs/systemd-service.md +0 -233
package/dist/locator.js
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Canonical `LocatorSpec` contract for cdp-mcp.
|
|
3
|
-
*
|
|
4
|
-
* This module is the single source of truth for the structured element-locator
|
|
5
|
-
* shape that `locate`, `wait_for`, and the form-driving tools accept. It is
|
|
6
|
-
* deliberately **side-effect free** and depends only on `zod`, so external
|
|
7
|
-
* consumers can import it (via `cdp-mcp/contract`) to produce and validate specs
|
|
8
|
-
* without pulling in the CLI/server. Tool code re-imports these symbols rather
|
|
9
|
-
* than redefining them, so the published contract can never silently drift from
|
|
10
|
-
* what the tools actually accept.
|
|
11
|
-
*/
|
|
12
|
-
import { z } from "zod";
|
|
13
|
-
/**
|
|
14
|
-
* Error thrown by {@link normalizeLocator} / {@link parseLocator} for an invalid
|
|
15
|
-
* spec. `code` mirrors the cdp-mcp tool error codes so tool handlers can re-wrap it
|
|
16
|
-
* structurally: `"missing_arg"` when a spec is under-specified for its strategy,
|
|
17
|
-
* `"invalid_locator"` for an unsupported strategy.
|
|
18
|
-
*/
|
|
19
|
-
export class LocatorError extends Error {
|
|
20
|
-
code;
|
|
21
|
-
constructor(message, code = "missing_arg") {
|
|
22
|
-
super(message);
|
|
23
|
-
this.name = "LocatorError";
|
|
24
|
-
this.code = code;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
/** The locator strategies cdp-mcp understands. `css` is the default when a selector is given. */
|
|
28
|
-
export const locatorBySchema = z.enum([
|
|
29
|
-
"css",
|
|
30
|
-
"text",
|
|
31
|
-
"role",
|
|
32
|
-
"test_id",
|
|
33
|
-
"testId",
|
|
34
|
-
"label",
|
|
35
|
-
"placeholder",
|
|
36
|
-
"name",
|
|
37
|
-
]);
|
|
38
|
-
/**
|
|
39
|
-
* The raw Zod shape for a LocatorSpec. Spread into tool input schemas
|
|
40
|
-
* (`{ ...locatorShape, ... }`) so the field docs stay identical everywhere.
|
|
41
|
-
*/
|
|
42
|
-
export const locatorShape = {
|
|
43
|
-
by: locatorBySchema.optional().describe("Locator strategy. Omit when passing selector/css for a CSS lookup."),
|
|
44
|
-
selector: z.string().optional().describe("CSS selector. Equivalent to by=css."),
|
|
45
|
-
css: z.string().optional().describe("CSS selector. Equivalent to selector."),
|
|
46
|
-
text: z.string().optional().describe("Text to match for by=text."),
|
|
47
|
-
role: z.string().optional().describe("ARIA/implicit role for by=role, e.g. button, link, textbox."),
|
|
48
|
-
name: z.string().optional().describe("Accessible name, field name, or fallback value depending on the locator strategy."),
|
|
49
|
-
test_id: z.string().optional().describe("Value for data-testid, data-test-id, or data-test."),
|
|
50
|
-
// Both snake_case (`test_id`) and camelCase (`testId`) are accepted so callers
|
|
51
|
-
// can use whichever matches their convention; `normalizeLocator` /
|
|
52
|
-
// `serializeLocator` fold them to the canonical `test_id`. Likewise `name` is a
|
|
53
|
-
// cross-strategy fallback that gets folded into the strategy-specific field.
|
|
54
|
-
testId: z.string().optional().describe("CamelCase alias for test_id."),
|
|
55
|
-
label: z.string().optional().describe("Label text for by=label."),
|
|
56
|
-
placeholder: z.string().optional().describe("Placeholder text for by=placeholder."),
|
|
57
|
-
exact: z.boolean().optional().describe("Default false: substring match for text/name-like fields."),
|
|
58
|
-
};
|
|
59
|
-
/** A standalone `ZodObject` for a LocatorSpec — external consumers can `.parse()`/`.safeParse()`. */
|
|
60
|
-
export const locatorSchema = z.object(locatorShape);
|
|
61
|
-
/**
|
|
62
|
-
* Validate and canonicalize a spec: infer `by` from selector/css, enforce the
|
|
63
|
-
* required field for the chosen strategy, and fold the `name` fallback into the
|
|
64
|
-
* strategy-specific field. Throws {@link LocatorError} on an under-specified spec.
|
|
65
|
-
*/
|
|
66
|
-
export function normalizeLocator(input) {
|
|
67
|
-
const by = input.by ?? (input.selector || input.css ? "css" : undefined);
|
|
68
|
-
if (!by)
|
|
69
|
-
throw new LocatorError("by is required unless selector/css is supplied");
|
|
70
|
-
switch (by) {
|
|
71
|
-
case "css": {
|
|
72
|
-
const selector = input.selector ?? input.css;
|
|
73
|
-
if (!selector)
|
|
74
|
-
throw new LocatorError("selector or css is required for by=css");
|
|
75
|
-
return { ...input, by, selector };
|
|
76
|
-
}
|
|
77
|
-
case "role":
|
|
78
|
-
if (!input.role)
|
|
79
|
-
throw new LocatorError("role is required for by=role");
|
|
80
|
-
return { ...input, by };
|
|
81
|
-
case "text":
|
|
82
|
-
if (!input.text && !input.name)
|
|
83
|
-
throw new LocatorError("text or name is required for by=text");
|
|
84
|
-
return { ...input, by, text: input.text ?? input.name };
|
|
85
|
-
case "test_id":
|
|
86
|
-
case "testId": {
|
|
87
|
-
const testId = input.test_id ?? input.testId ?? input.name;
|
|
88
|
-
if (!testId)
|
|
89
|
-
throw new LocatorError(`test_id, testId, or name is required for by=${by}`);
|
|
90
|
-
return { ...input, by, test_id: testId };
|
|
91
|
-
}
|
|
92
|
-
case "label": {
|
|
93
|
-
const label = input.label ?? input.name;
|
|
94
|
-
if (!label)
|
|
95
|
-
throw new LocatorError("label or name is required for by=label");
|
|
96
|
-
return { ...input, by, label };
|
|
97
|
-
}
|
|
98
|
-
case "placeholder": {
|
|
99
|
-
const placeholder = input.placeholder ?? input.name;
|
|
100
|
-
if (!placeholder)
|
|
101
|
-
throw new LocatorError("placeholder or name is required for by=placeholder");
|
|
102
|
-
return { ...input, by, placeholder };
|
|
103
|
-
}
|
|
104
|
-
case "name":
|
|
105
|
-
if (!input.name)
|
|
106
|
-
throw new LocatorError("name is required for by=name");
|
|
107
|
-
return { ...input, by };
|
|
108
|
-
default: {
|
|
109
|
-
// Compile-time exhaustiveness: if `locatorBySchema` gains a strategy and a
|
|
110
|
-
// case here is missed, `by` is no longer `never` and this fails to build
|
|
111
|
-
// (pairs with `noFallthroughCasesInSwitch`).
|
|
112
|
-
const _exhaustive = by;
|
|
113
|
-
throw new LocatorError(`unsupported locator strategy: ${String(_exhaustive)}`, "invalid_locator");
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
/** Validate an unknown value as a LocatorSpec (shape + strategy requirements). */
|
|
118
|
-
export function parseLocator(input) {
|
|
119
|
-
return normalizeLocator(locatorSchema.parse(input));
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Serialize a LocatorSpec to a stable, normalized JSON string. Equivalent specs
|
|
123
|
-
* serialize identically regardless of which alias the caller used — e.g.
|
|
124
|
-
* `{ css: ".x" }` and `{ selector: ".x" }` both yield `{"by":"css","selector":".x"}`
|
|
125
|
-
* — so the output is safe to use as a cache key or for equality checks.
|
|
126
|
-
*/
|
|
127
|
-
export function serializeLocator(spec) {
|
|
128
|
-
return JSON.stringify(canonicalLocator(spec));
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Reduce a spec to only its canonical fields, in a fixed key order, dropping
|
|
132
|
-
* alias inputs (`css`, `testId`, and the cross-strategy `name` fallback). This is
|
|
133
|
-
* what makes {@link serializeLocator} stable across equivalent inputs — the raw
|
|
134
|
-
* `normalizeLocator` result still carries whichever aliases the caller passed.
|
|
135
|
-
*/
|
|
136
|
-
function canonicalLocator(spec) {
|
|
137
|
-
const n = normalizeLocator(spec);
|
|
138
|
-
const out = {};
|
|
139
|
-
switch (n.by) {
|
|
140
|
-
case "css":
|
|
141
|
-
out.by = "css";
|
|
142
|
-
out.selector = n.selector;
|
|
143
|
-
break;
|
|
144
|
-
case "role":
|
|
145
|
-
out.by = "role";
|
|
146
|
-
out.role = n.role;
|
|
147
|
-
if (n.name !== undefined)
|
|
148
|
-
out.name = n.name;
|
|
149
|
-
break;
|
|
150
|
-
case "text":
|
|
151
|
-
out.by = "text";
|
|
152
|
-
out.text = n.text;
|
|
153
|
-
break;
|
|
154
|
-
case "test_id":
|
|
155
|
-
case "testId":
|
|
156
|
-
out.by = "test_id";
|
|
157
|
-
out.test_id = n.test_id;
|
|
158
|
-
break;
|
|
159
|
-
case "label":
|
|
160
|
-
out.by = "label";
|
|
161
|
-
out.label = n.label;
|
|
162
|
-
break;
|
|
163
|
-
case "placeholder":
|
|
164
|
-
out.by = "placeholder";
|
|
165
|
-
out.placeholder = n.placeholder;
|
|
166
|
-
break;
|
|
167
|
-
case "name":
|
|
168
|
-
out.by = "name";
|
|
169
|
-
out.name = n.name;
|
|
170
|
-
break;
|
|
171
|
-
}
|
|
172
|
-
if (n.exact !== undefined)
|
|
173
|
-
out.exact = n.exact;
|
|
174
|
-
return out;
|
|
175
|
-
}
|
|
176
|
-
//# sourceMappingURL=locator.js.map
|
package/dist/locator.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"locator.js","sourceRoot":"","sources":["../src/locator.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
DELETED
package/dist/server.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import { registerSessionTools } from "./tools/session.js";
|
|
3
|
-
import { registerNavTools } from "./tools/nav.js";
|
|
4
|
-
import { registerSourceTools } from "./tools/source.js";
|
|
5
|
-
import { registerBreakpointTools } from "./tools/breakpoints.js";
|
|
6
|
-
import { registerExecutionTools } from "./tools/execution.js";
|
|
7
|
-
import { registerInspectTools } from "./tools/inspect.js";
|
|
8
|
-
import { registerConsoleTools } from "./tools/console.js";
|
|
9
|
-
import { registerNetworkTools } from "./tools/network.js";
|
|
10
|
-
import { registerDomTools } from "./tools/dom.js";
|
|
11
|
-
import { registerFormTools } from "./tools/forms.js";
|
|
12
|
-
import { registerStorageTools } from "./tools/storage.js";
|
|
13
|
-
export function buildServer() {
|
|
14
|
-
const server = new McpServer({
|
|
15
|
-
name: "cdp-mcp",
|
|
16
|
-
version: "0.1.2",
|
|
17
|
-
});
|
|
18
|
-
registerSessionTools(server);
|
|
19
|
-
registerNavTools(server);
|
|
20
|
-
registerSourceTools(server);
|
|
21
|
-
registerBreakpointTools(server);
|
|
22
|
-
registerExecutionTools(server);
|
|
23
|
-
registerInspectTools(server);
|
|
24
|
-
registerConsoleTools(server);
|
|
25
|
-
registerNetworkTools(server);
|
|
26
|
-
registerDomTools(server);
|
|
27
|
-
registerFormTools(server);
|
|
28
|
-
registerStorageTools(server);
|
|
29
|
-
// The SDK advertises `tools: { listChanged: true }` as soon as any tool is
|
|
30
|
-
// registered, but never emits the matching notification on its own. Some
|
|
31
|
-
// clients (e.g. GitHub Copilot CLI over SSE) gate their first `tools/list`
|
|
32
|
-
// call on receiving a `notifications/tools/list_changed` and otherwise wait
|
|
33
|
-
// forever — registering zero tools. Emit it once, right after the client's
|
|
34
|
-
// `initialized` (so the transport is connected and the client is ready).
|
|
35
|
-
// Harmless for clients that already fetch eagerly. See issue #1.
|
|
36
|
-
server.server.oninitialized = () => {
|
|
37
|
-
server.sendToolListChanged();
|
|
38
|
-
};
|
|
39
|
-
return server;
|
|
40
|
-
}
|
|
41
|
-
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D,MAAM,UAAU,WAAW;IACzB,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IAEH,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7B,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACzB,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC5B,uBAAuB,CAAC,MAAM,CAAC,CAAC;IAChC,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAC/B,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7B,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7B,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7B,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACzB,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC1B,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAE7B,2EAA2E;IAC3E,yEAAyE;IACzE,2EAA2E;IAC3E,4EAA4E;IAC5E,2EAA2E;IAC3E,yEAAyE;IACzE,iEAAiE;IACjE,MAAM,CAAC,MAAM,CAAC,aAAa,GAAG,GAAG,EAAE;QACjC,MAAM,CAAC,mBAAmB,EAAE,CAAC;IAC/B,CAAC,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
export interface LaunchArgs {
|
|
2
|
-
url?: string;
|
|
3
|
-
headless?: boolean;
|
|
4
|
-
userDataDir?: string;
|
|
5
|
-
args?: string[];
|
|
6
|
-
chromePath?: string;
|
|
7
|
-
sandbox?: boolean;
|
|
8
|
-
}
|
|
9
|
-
export interface AttachArgs {
|
|
10
|
-
port?: number;
|
|
11
|
-
host?: string;
|
|
12
|
-
targetFilter?: {
|
|
13
|
-
type?: string;
|
|
14
|
-
urlIncludes?: string;
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
export declare function launchChrome(opts?: LaunchArgs): Promise<{
|
|
18
|
-
targetId: string;
|
|
19
|
-
url: string;
|
|
20
|
-
}>;
|
|
21
|
-
export declare function attachChrome(opts?: AttachArgs): Promise<{
|
|
22
|
-
targetId: string;
|
|
23
|
-
url: string;
|
|
24
|
-
}>;
|
|
25
|
-
export declare function closeSession(): Promise<void>;
|
|
26
|
-
export declare function switchTarget(targetId: string): Promise<{
|
|
27
|
-
targetId: string;
|
|
28
|
-
url: string;
|
|
29
|
-
}>;
|
package/dist/session/browser.js
DELETED
|
@@ -1,409 +0,0 @@
|
|
|
1
|
-
import { mkdirSync } from "node:fs";
|
|
2
|
-
import CDP from "chrome-remote-interface";
|
|
3
|
-
import { launch } from "chrome-launcher";
|
|
4
|
-
import { sessionState, ROOT_SESSION_KEY } from "./state.js";
|
|
5
|
-
import { attachScriptListener } from "../sourcemap/loader.js";
|
|
6
|
-
import { mapCdpToOriginal } from "../sourcemap/store.js";
|
|
7
|
-
import { alreadySession } from "../util/errors.js";
|
|
8
|
-
import { previewRemoteObject } from "../util/format.js";
|
|
9
|
-
import { log } from "../util/log.js";
|
|
10
|
-
import { snapUserDataDir } from "../util/browser-resolve.js";
|
|
11
|
-
const DEFAULT_PORT = 9222;
|
|
12
|
-
export async function launchChrome(opts = {}) {
|
|
13
|
-
if (sessionState.client)
|
|
14
|
-
throw alreadySession();
|
|
15
|
-
// chrome-launcher manages --remote-debugging-port itself: it picks an
|
|
16
|
-
// unused port, adds the flag, and polls it. Passing our own
|
|
17
|
-
// --remote-debugging-port=0 in chromeFlags appears AFTER chrome-launcher's,
|
|
18
|
-
// and since Chrome honors the last occurrence, Chrome picks an ephemeral
|
|
19
|
-
// port written to DevToolsActivePort while chrome-launcher polls its own
|
|
20
|
-
// (stale) port → ECONNREFUSED on every connect. Don't pass it; let
|
|
21
|
-
// chrome-launcher own port selection. `runningChrome.port` then reflects
|
|
22
|
-
// the actual port Chrome is listening on. (Codex blocker review on PR #11.)
|
|
23
|
-
// Sandbox decision: an explicit `sandbox` arg from the caller always wins.
|
|
24
|
-
// When the caller omits it, fall back to the CDP_SANDBOX env (default off);
|
|
25
|
-
// "true" or "1" enable it (matching the eval runner's EVAL_SANDBOX parsing).
|
|
26
|
-
// This lets a host with a working sandbox path opt a whole run into
|
|
27
|
-
// sandbox-on (e.g. the L4 eval runner via EVAL_SANDBOX → CDP_SANDBOX)
|
|
28
|
-
// without prompt-injecting every launch_chrome call. Unset env → false →
|
|
29
|
-
// the --no-sandbox automation default (unchanged). Explicit `sandbox: false`
|
|
30
|
-
// still forces --no-sandbox even if the env is set.
|
|
31
|
-
const sandboxEnv = process.env.CDP_SANDBOX;
|
|
32
|
-
const useSandbox = opts.sandbox ?? (sandboxEnv === "true" || sandboxEnv === "1");
|
|
33
|
-
const userArgs = opts.args ?? [];
|
|
34
|
-
const userAlreadyDisabled = userArgs.includes("--no-sandbox");
|
|
35
|
-
// A caller can request the sandbox AND still pass --no-sandbox in args; the
|
|
36
|
-
// userArgs spread re-adds it last, so Chromium ends up unsandboxed despite the
|
|
37
|
-
// request. Warn rather than silently dropping the sandbox.
|
|
38
|
-
if (useSandbox && userAlreadyDisabled) {
|
|
39
|
-
log.warn("launch_chrome: sandbox requested but --no-sandbox is in args; the flag wins and the sandbox stays OFF");
|
|
40
|
-
}
|
|
41
|
-
// Snap-confinement auto-profile. When the effective Chrome path (explicit
|
|
42
|
-
// chromePath, or CHROME_PATH env that chrome-launcher will pick up) is
|
|
43
|
-
// under /snap/ AND the caller didn't already specify userDataDir, derive
|
|
44
|
-
// the snap-confined profile path so chrome-launcher doesn't hand snap-
|
|
45
|
-
// Chromium a /tmp/... profile that snap confinement rejects (debug port
|
|
46
|
-
// never opens; chrome-launcher's startup-port poll ECONNREFUSEs). Mirrors
|
|
47
|
-
// the L3 globalSetup logic in test/e2e/setup/global.ts so the L4 eval
|
|
48
|
-
// harness (which steers chrome-launcher via CHROME_PATH) and direct
|
|
49
|
-
// launch_chrome callers inherit the same workaround without making the
|
|
50
|
-
// agent responsible for it. (Codex review on PR #24.)
|
|
51
|
-
const effectiveChromePath = opts.chromePath ?? process.env.CHROME_PATH;
|
|
52
|
-
const autoUserDataDir = !opts.userDataDir && effectiveChromePath?.startsWith("/snap/")
|
|
53
|
-
? snapUserDataDir(effectiveChromePath)
|
|
54
|
-
: undefined;
|
|
55
|
-
if (autoUserDataDir) {
|
|
56
|
-
mkdirSync(autoUserDataDir, { recursive: true });
|
|
57
|
-
}
|
|
58
|
-
const launchOpts = {
|
|
59
|
-
startingUrl: opts.url ?? "about:blank",
|
|
60
|
-
chromeFlags: [
|
|
61
|
-
...(opts.headless ? ["--headless=new"] : []),
|
|
62
|
-
...(!useSandbox && !userAlreadyDisabled ? ["--no-sandbox"] : []),
|
|
63
|
-
...userArgs,
|
|
64
|
-
],
|
|
65
|
-
...(opts.userDataDir
|
|
66
|
-
? { userDataDir: opts.userDataDir }
|
|
67
|
-
: autoUserDataDir
|
|
68
|
-
? { userDataDir: autoUserDataDir }
|
|
69
|
-
: {}),
|
|
70
|
-
...(opts.chromePath ? { chromePath: opts.chromePath } : {}),
|
|
71
|
-
};
|
|
72
|
-
const chrome = await launch(launchOpts);
|
|
73
|
-
sessionState.chrome = chrome;
|
|
74
|
-
sessionState.chromePort = chrome.port;
|
|
75
|
-
log.info("launched chrome", { port: chrome.port, pid: chrome.pid, sandbox: useSandbox });
|
|
76
|
-
// Pick the first page target.
|
|
77
|
-
const targets = await waitForFirstPage(chrome.port);
|
|
78
|
-
const target = targets[0];
|
|
79
|
-
await connectToTarget(chrome.port, target.id);
|
|
80
|
-
return { targetId: target.id, url: target.url };
|
|
81
|
-
}
|
|
82
|
-
export async function attachChrome(opts = {}) {
|
|
83
|
-
if (sessionState.client)
|
|
84
|
-
throw alreadySession();
|
|
85
|
-
const port = opts.port ?? DEFAULT_PORT;
|
|
86
|
-
sessionState.chromePort = port;
|
|
87
|
-
sessionState.attached = true;
|
|
88
|
-
const targets = await CDP.List({ port, host: opts.host });
|
|
89
|
-
const wantType = opts.targetFilter?.type;
|
|
90
|
-
const wantUrl = opts.targetFilter?.urlIncludes;
|
|
91
|
-
const filtered = targets.filter((t) => {
|
|
92
|
-
// When a type filter is supplied, it is authoritative. Otherwise default
|
|
93
|
-
// to "page" targets (the common debugging case).
|
|
94
|
-
if (wantType) {
|
|
95
|
-
if (t.type !== wantType)
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
else if (t.type !== "page") {
|
|
99
|
-
return false;
|
|
100
|
-
}
|
|
101
|
-
if (wantUrl && !t.url.includes(wantUrl))
|
|
102
|
-
return false;
|
|
103
|
-
return true;
|
|
104
|
-
});
|
|
105
|
-
if (filtered.length === 0) {
|
|
106
|
-
throw new Error(`No matching targets on the running Chrome (filter type=${wantType ?? "page"}, urlIncludes=${wantUrl ?? "*"})`);
|
|
107
|
-
}
|
|
108
|
-
const target = filtered[0];
|
|
109
|
-
await connectToTarget(port, target.id, opts.host);
|
|
110
|
-
log.info("attached to chrome", { port, targetId: target.id, url: target.url });
|
|
111
|
-
return { targetId: target.id, url: target.url };
|
|
112
|
-
}
|
|
113
|
-
async function waitForFirstPage(port) {
|
|
114
|
-
const deadline = Date.now() + 5000;
|
|
115
|
-
while (Date.now() < deadline) {
|
|
116
|
-
try {
|
|
117
|
-
const list = await CDP.List({ port });
|
|
118
|
-
const pages = list.filter((t) => t.type === "page");
|
|
119
|
-
if (pages.length > 0)
|
|
120
|
-
return pages;
|
|
121
|
-
}
|
|
122
|
-
catch {
|
|
123
|
-
// Chrome may not be ready yet
|
|
124
|
-
}
|
|
125
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
126
|
-
}
|
|
127
|
-
throw new Error("Chrome did not expose any page targets within 5s");
|
|
128
|
-
}
|
|
129
|
-
async function connectToTarget(port, targetId, host) {
|
|
130
|
-
const client = await CDP({ port, host, target: targetId });
|
|
131
|
-
sessionState.client = client;
|
|
132
|
-
sessionState.currentTargetId = targetId;
|
|
133
|
-
// Wire root-target handlers and Target.attachedToTarget BEFORE setAutoAttach
|
|
134
|
-
// — Chrome immediately enumerates pre-existing eligible children (workers,
|
|
135
|
-
// OOPIFs, service workers) inline with the setAutoAttach response. If the
|
|
136
|
-
// listener is registered after, those attachedToTarget events are dropped.
|
|
137
|
-
wireDomainHandlers(client, undefined);
|
|
138
|
-
const onAttached = (params) => {
|
|
139
|
-
void onChildAttached(client, params);
|
|
140
|
-
};
|
|
141
|
-
const onDetached = (params) => {
|
|
142
|
-
detachSession(client, params.sessionId);
|
|
143
|
-
};
|
|
144
|
-
client.on("Target.attachedToTarget", onAttached);
|
|
145
|
-
client.on("Target.detachedFromTarget", onDetached);
|
|
146
|
-
client.on("disconnect", () => log.warn("CDP disconnect"));
|
|
147
|
-
await enableDomains(client, undefined);
|
|
148
|
-
try {
|
|
149
|
-
await client.Target.setAutoAttach({
|
|
150
|
-
autoAttach: true,
|
|
151
|
-
waitForDebuggerOnStart: false,
|
|
152
|
-
flatten: true,
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
catch (e) {
|
|
156
|
-
log.warn("Target.setAutoAttach failed", { error: String(e) });
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
async function onChildAttached(client, params) {
|
|
160
|
-
const sessionId = params.sessionId;
|
|
161
|
-
log.debug("child target attached", { sessionId, type: params.targetInfo.type, url: params.targetInfo.url });
|
|
162
|
-
try {
|
|
163
|
-
wireDomainHandlers(client, sessionId);
|
|
164
|
-
await enableDomains(client, sessionId);
|
|
165
|
-
await client.Target.setAutoAttach({ autoAttach: true, waitForDebuggerOnStart: false, flatten: true }, sessionId);
|
|
166
|
-
// Inherit pause-on-exceptions setting so child sessions honor it from
|
|
167
|
-
// birth, not only if the user re-issues the tool after they attach.
|
|
168
|
-
if (sessionState.pauseOnExceptions !== "none") {
|
|
169
|
-
try {
|
|
170
|
-
await client.Debugger.setPauseOnExceptions({ state: sessionState.pauseOnExceptions }, sessionId);
|
|
171
|
-
}
|
|
172
|
-
catch (e) {
|
|
173
|
-
log.warn("failed to apply pauseOnExceptions to child", { sessionId, error: String(e) });
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
catch (e) {
|
|
178
|
-
log.warn("failed to enable child target", { sessionId, error: String(e) });
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
function detachSession(client, sessionId) {
|
|
182
|
-
log.debug("child target detached", { sessionId });
|
|
183
|
-
// Remove every event handler we registered for this sub-session and drop
|
|
184
|
-
// its scripts from the store — those scriptIds are now invalid.
|
|
185
|
-
const handlers = sessionState.sessionHandlers.get(sessionId);
|
|
186
|
-
if (handlers) {
|
|
187
|
-
for (const { event, handler } of handlers) {
|
|
188
|
-
client.removeListener(event, handler);
|
|
189
|
-
}
|
|
190
|
-
sessionState.sessionHandlers.delete(sessionId);
|
|
191
|
-
}
|
|
192
|
-
// Drop scripts owned by this session so stale scriptIds don't survive.
|
|
193
|
-
for (const sc of sessionState.scripts.all()) {
|
|
194
|
-
if (sc.sessionId === sessionId)
|
|
195
|
-
sessionState.scripts.remove(sc.scriptId, sc.sessionId);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
async function enableDomains(client, sessionId) {
|
|
199
|
-
// The full debugger surface needs these. Some are no-ops on workers but harmless.
|
|
200
|
-
const swallow = (p) => p.then(() => { }, () => { });
|
|
201
|
-
await swallow(client.Runtime.enable(sessionId));
|
|
202
|
-
await swallow(client.Debugger.enable({}, sessionId));
|
|
203
|
-
await swallow(client.Page.enable(sessionId));
|
|
204
|
-
await swallow(client.DOM.enable({}, sessionId));
|
|
205
|
-
await swallow(client.Network.enable({}, sessionId));
|
|
206
|
-
}
|
|
207
|
-
function wireDomainHandlers(client, sessionId) {
|
|
208
|
-
// The strict gate: only process events from THIS session. The original
|
|
209
|
-
// implementation used `if (sessionId && eventSessionId !== sessionId)`,
|
|
210
|
-
// which is vacuously false when sessionId is undefined (the root) —
|
|
211
|
-
// making the root handler process every child session's events too.
|
|
212
|
-
const own = (eventSessionId) => eventSessionId === sessionId;
|
|
213
|
-
const registered = [];
|
|
214
|
-
const reg = (event, handler) => {
|
|
215
|
-
client.on(event, handler);
|
|
216
|
-
registered.push({ event, handler });
|
|
217
|
-
};
|
|
218
|
-
// Source-map / Debugger.scriptParsed
|
|
219
|
-
const scriptHandler = attachScriptListener(client, sessionState.scripts, sessionId);
|
|
220
|
-
registered.push({ event: "Debugger.scriptParsed", handler: scriptHandler });
|
|
221
|
-
const onPaused = (params, eventSessionId) => {
|
|
222
|
-
if (!own(eventSessionId))
|
|
223
|
-
return;
|
|
224
|
-
sessionState.pause.onPaused({
|
|
225
|
-
reason: params.reason,
|
|
226
|
-
data: params.data,
|
|
227
|
-
hitBreakpoints: params.hitBreakpoints,
|
|
228
|
-
callFrames: params.callFrames,
|
|
229
|
-
asyncStackTrace: params.asyncStackTrace,
|
|
230
|
-
sessionId: eventSessionId,
|
|
231
|
-
pausedAt: Date.now(),
|
|
232
|
-
});
|
|
233
|
-
log.debug("paused", { reason: params.reason, hit: params.hitBreakpoints });
|
|
234
|
-
};
|
|
235
|
-
reg("Debugger.paused", onPaused);
|
|
236
|
-
const onResumed = (_p, eventSessionId) => {
|
|
237
|
-
if (!own(eventSessionId))
|
|
238
|
-
return;
|
|
239
|
-
sessionState.pause.onResumed();
|
|
240
|
-
};
|
|
241
|
-
reg("Debugger.resumed", onResumed);
|
|
242
|
-
const onConsoleApi = (params, eventSessionId) => {
|
|
243
|
-
if (!own(eventSessionId))
|
|
244
|
-
return;
|
|
245
|
-
pushConsoleFromApi(sessionState, params, eventSessionId);
|
|
246
|
-
};
|
|
247
|
-
reg("Runtime.consoleAPICalled", onConsoleApi);
|
|
248
|
-
const onException = (params, eventSessionId) => {
|
|
249
|
-
if (!own(eventSessionId))
|
|
250
|
-
return;
|
|
251
|
-
pushConsoleFromException(sessionState, params, eventSessionId);
|
|
252
|
-
};
|
|
253
|
-
reg("Runtime.exceptionThrown", onException);
|
|
254
|
-
// Predicate that matches an entry by (requestId, sessionId). CDP requestIds
|
|
255
|
-
// are scoped per Network agent — two iframes can both emit requestId="123" —
|
|
256
|
-
// so the predicate must include sessionId to avoid cross-session collisions
|
|
257
|
-
// in RingBuffer.update.
|
|
258
|
-
const matchEntry = (requestId) => (e) => e.requestId === requestId && e.sessionId === sessionId;
|
|
259
|
-
const onRequest = (params, eventSessionId) => {
|
|
260
|
-
if (!own(eventSessionId))
|
|
261
|
-
return;
|
|
262
|
-
sessionState.network.push({
|
|
263
|
-
requestId: params.requestId,
|
|
264
|
-
ts: Date.now(),
|
|
265
|
-
url: params.request.url,
|
|
266
|
-
method: params.request.method,
|
|
267
|
-
resourceType: params.type,
|
|
268
|
-
...(sessionId ? { sessionId } : {}),
|
|
269
|
-
});
|
|
270
|
-
};
|
|
271
|
-
reg("Network.requestWillBeSent", onRequest);
|
|
272
|
-
const onResponse = (params, eventSessionId) => {
|
|
273
|
-
if (!own(eventSessionId))
|
|
274
|
-
return;
|
|
275
|
-
sessionState.network.update(matchEntry(params.requestId), {
|
|
276
|
-
status: params.response.status,
|
|
277
|
-
statusText: params.response.statusText,
|
|
278
|
-
mimeType: params.response.mimeType,
|
|
279
|
-
fromCache: params.response.fromDiskCache || params.response.fromPrefetchCache,
|
|
280
|
-
});
|
|
281
|
-
};
|
|
282
|
-
reg("Network.responseReceived", onResponse);
|
|
283
|
-
const onLoadingFinished = (params, eventSessionId) => {
|
|
284
|
-
if (!own(eventSessionId))
|
|
285
|
-
return;
|
|
286
|
-
// Use the entry's own ts (set at requestWillBeSent) to compute duration.
|
|
287
|
-
const existing = sessionState.network.query({ filter: matchEntry(params.requestId), limit: 1 }).pop();
|
|
288
|
-
sessionState.network.update(matchEntry(params.requestId), {
|
|
289
|
-
...(existing ? { durationMs: Date.now() - existing.ts } : {}),
|
|
290
|
-
finished: true,
|
|
291
|
-
});
|
|
292
|
-
};
|
|
293
|
-
reg("Network.loadingFinished", onLoadingFinished);
|
|
294
|
-
const onLoadingFailed = (params, eventSessionId) => {
|
|
295
|
-
if (!own(eventSessionId))
|
|
296
|
-
return;
|
|
297
|
-
// Symmetric with onLoadingFinished: time-to-failure (DNS error, connect
|
|
298
|
-
// refused, RST, abort, …) is useful for latency/anomaly analysis. Without
|
|
299
|
-
// this, duration_ms is `number | undefined` purely as a function of
|
|
300
|
-
// success vs. failure in the same call to get_network_requests.
|
|
301
|
-
const existing = sessionState.network.query({ filter: matchEntry(params.requestId), limit: 1 }).pop();
|
|
302
|
-
sessionState.network.update(matchEntry(params.requestId), {
|
|
303
|
-
...(existing ? { durationMs: Date.now() - existing.ts } : {}),
|
|
304
|
-
failureReason: params.errorText,
|
|
305
|
-
finished: true,
|
|
306
|
-
});
|
|
307
|
-
};
|
|
308
|
-
reg("Network.loadingFailed", onLoadingFailed);
|
|
309
|
-
sessionState.sessionHandlers.set(sessionId ?? ROOT_SESSION_KEY, registered);
|
|
310
|
-
}
|
|
311
|
-
function pushConsoleFromApi(s, params, sessionId) {
|
|
312
|
-
// Two formatting modes:
|
|
313
|
-
// - String args render as raw text (no surrounding quotes, no \n→\\n
|
|
314
|
-
// escapes) — matches how DevTools / Node / every log shipper renders
|
|
315
|
-
// console.log("Server started"). previewRemoteObject would
|
|
316
|
-
// JSON.stringify them, which is right for REPL/evaluate output but
|
|
317
|
-
// wrong for console buffering.
|
|
318
|
-
// - Everything else (numbers, objects, arrays, functions, …) goes
|
|
319
|
-
// through previewRemoteObject so {foo:1} keeps its shape.
|
|
320
|
-
const renderArg = (a) => a.type === "string" ? (a.value ?? "") : previewRemoteObject(a);
|
|
321
|
-
const text = params.args.length === 0
|
|
322
|
-
? "(no args)"
|
|
323
|
-
: params.args.map(renderArg).join(" ");
|
|
324
|
-
const top = params.stackTrace?.callFrames?.[0];
|
|
325
|
-
const mapped = top ? mapCdpToOriginal(s.scripts, {
|
|
326
|
-
scriptId: top.scriptId,
|
|
327
|
-
lineNumber: top.lineNumber,
|
|
328
|
-
columnNumber: top.columnNumber,
|
|
329
|
-
}, sessionId) : null;
|
|
330
|
-
s.console.push({
|
|
331
|
-
ts: Date.now(),
|
|
332
|
-
level: mapApiLevel(params.type),
|
|
333
|
-
text,
|
|
334
|
-
source: "console-api",
|
|
335
|
-
...(top?.url ? { url: top.url } : {}),
|
|
336
|
-
...(top ? { lineNumber: top.lineNumber, columnNumber: top.columnNumber } : {}),
|
|
337
|
-
...(mapped ? { mappedFile: mapped.file, mappedLine: mapped.line, mappedColumn: mapped.column } : {}),
|
|
338
|
-
...(params.stackTrace ? { stack: params.stackTrace } : {}),
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
function pushConsoleFromException(s, params, sessionId) {
|
|
342
|
-
const det = params.exceptionDetails;
|
|
343
|
-
const top = det.stackTrace?.callFrames?.[0];
|
|
344
|
-
const mapped = top ? mapCdpToOriginal(s.scripts, {
|
|
345
|
-
scriptId: top.scriptId,
|
|
346
|
-
lineNumber: top.lineNumber,
|
|
347
|
-
columnNumber: top.columnNumber,
|
|
348
|
-
}, sessionId) : null;
|
|
349
|
-
const text = (det.exception?.description ?? det.exception?.value ?? det.text) + "";
|
|
350
|
-
s.console.push({
|
|
351
|
-
ts: Date.now(),
|
|
352
|
-
level: "error",
|
|
353
|
-
text,
|
|
354
|
-
source: "runtime-exception",
|
|
355
|
-
...(top?.url ? { url: top.url } : { url: det.url }),
|
|
356
|
-
...(top ? { lineNumber: top.lineNumber, columnNumber: top.columnNumber } : { lineNumber: det.lineNumber, columnNumber: det.columnNumber }),
|
|
357
|
-
...(mapped ? { mappedFile: mapped.file, mappedLine: mapped.line, mappedColumn: mapped.column } : {}),
|
|
358
|
-
...(det.stackTrace ? { stack: det.stackTrace } : {}),
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
function mapApiLevel(t) {
|
|
362
|
-
switch (t) {
|
|
363
|
-
case "warning":
|
|
364
|
-
return "warn";
|
|
365
|
-
case "error":
|
|
366
|
-
return "error";
|
|
367
|
-
case "debug":
|
|
368
|
-
return "debug";
|
|
369
|
-
case "info":
|
|
370
|
-
return "info";
|
|
371
|
-
case "trace":
|
|
372
|
-
return "trace";
|
|
373
|
-
case "log":
|
|
374
|
-
default:
|
|
375
|
-
return "log";
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
export async function closeSession() {
|
|
379
|
-
await sessionState.close();
|
|
380
|
-
}
|
|
381
|
-
// Switch to a different target on the same browser without tearing down the
|
|
382
|
-
// chrome process. Used by select_target.
|
|
383
|
-
export async function switchTarget(targetId) {
|
|
384
|
-
if (!sessionState.client)
|
|
385
|
-
throw new Error("No active session");
|
|
386
|
-
const port = sessionState.chromePort;
|
|
387
|
-
const attached = sessionState.attached;
|
|
388
|
-
const chrome = sessionState.chrome;
|
|
389
|
-
try {
|
|
390
|
-
await sessionState.client.close().catch(() => { });
|
|
391
|
-
}
|
|
392
|
-
catch {
|
|
393
|
-
/* ignore */
|
|
394
|
-
}
|
|
395
|
-
sessionState.client = null;
|
|
396
|
-
sessionState.currentTargetId = null;
|
|
397
|
-
sessionState.pause.reset();
|
|
398
|
-
sessionState.scripts.clear();
|
|
399
|
-
sessionState.breakpoints.clear();
|
|
400
|
-
sessionState.sessionHandlers.clear();
|
|
401
|
-
sessionState.chromePort = port;
|
|
402
|
-
sessionState.attached = attached;
|
|
403
|
-
sessionState.chrome = chrome;
|
|
404
|
-
await connectToTarget(port, targetId);
|
|
405
|
-
const list = await CDP.List({ port });
|
|
406
|
-
const t = list.find((x) => x.id === targetId);
|
|
407
|
-
return { targetId, url: t?.url ?? "" };
|
|
408
|
-
}
|
|
409
|
-
//# sourceMappingURL=browser.js.map
|