cdp-toolkit 1.9.5 → 1.10.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/CHANGELOG.md +54 -0
- package/dist/cli.js +287 -269
- package/dist/index.js +21 -19
- package/dist/mcp.js +297 -271
- package/package.json +1 -1
- package/src/bidi/client.ts +11 -0
- package/src/bidi/driver.ts +7 -1
- package/src/bidi/launch.ts +9 -2
- package/src/manifest.ts +253 -246
- package/src/mcp.ts +16 -2
- package/src/tools/recorder.ts +8 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cdp-toolkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "A lightweight chrome-devtools-mcp alternative: an MCP server + CLI that drives the tabs you name, one target per call over one direct WebSocket, with a bounded timeout on every call, so a stuck page can't wedge your agent or force a /mcp restart. Chrome over CDP and Firefox over WebDriver BiDi, with tools a backend can't run absent from tools/list rather than throwing mid-task. 45 tools including built-in network mocking, a cookie group, tab leases with human-activity detection, real HTML5 drag-and-drop, raw mouse/scroll dispatch, downloads, permission grants, and tab-to-video screen recording so several agents can drive one browser without stealing each other's tabs or a human's. No Puppeteer, no all-target fan-out.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/bidi/client.ts
CHANGED
|
@@ -88,6 +88,17 @@ export class BidiConnection {
|
|
|
88
88
|
return this.refCount;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
/**
|
|
92
|
+
* True while this connection still has a live socket. Goes false once the
|
|
93
|
+
* socket closes (ws.onclose) or dispose() runs — both set `closed` and
|
|
94
|
+
* reject all in-flight calls. getConnection uses this to skip and evict a
|
|
95
|
+
* dead cached connection so the next call re-dials a fresh session instead
|
|
96
|
+
* of reusing a socket that can only reject with "connection not open".
|
|
97
|
+
*/
|
|
98
|
+
get isOpen(): boolean {
|
|
99
|
+
return !this.closed && this.ws !== undefined;
|
|
100
|
+
}
|
|
101
|
+
|
|
91
102
|
connect(): Promise<this> {
|
|
92
103
|
return new Promise((resolve, reject) => {
|
|
93
104
|
const ws = new WebSocket(this.wsUrl);
|
package/src/bidi/driver.ts
CHANGED
|
@@ -322,7 +322,13 @@ const BIDI_CAPABILITIES: ReadonlySet<Capability> = new Set<Capability>([
|
|
|
322
322
|
const connections = new Map<string, BidiConnection>();
|
|
323
323
|
async function getConnection(endpoint: string, timeoutMs?: number): Promise<BidiConnection> {
|
|
324
324
|
const existing = connections.get(endpoint);
|
|
325
|
-
if (existing) { existing.retain(); return existing; }
|
|
325
|
+
if (existing && existing.isOpen) { existing.retain(); return existing; }
|
|
326
|
+
// A cached connection whose socket has died (ws.onclose fired) can only reject every
|
|
327
|
+
// send() with "connection not open" — reusing it would strand the endpoint permanently.
|
|
328
|
+
// Evict it so the code below re-dials and creates a fresh BiDi session. No cleanup is
|
|
329
|
+
// needed: a dead connection already had `closed` set and rejectAll() cleared its per-call
|
|
330
|
+
// timers, and BidiConnection holds no heartbeat/keepalive resource to leak (see client.ts).
|
|
331
|
+
if (existing) connections.delete(endpoint);
|
|
326
332
|
// Firefox 153's default unhandledPromptBehavior is "dismiss": a page-blocking alert()/confirm()
|
|
327
333
|
// gets auto-dismissed before handleDialog() ever runs, and Firefox's own userPromptOpened event
|
|
328
334
|
// reports handler:"dismiss" on it (verified: a bare session.new left "no such alert" on the very
|
package/src/bidi/launch.ts
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* node:os and node:path.
|
|
23
23
|
*/
|
|
24
24
|
import { spawn, type ChildProcess } from "node:child_process";
|
|
25
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
25
|
+
import { existsSync, mkdtempSync, rmSync } from "node:fs";
|
|
26
26
|
import { createConnection, createServer } from "node:net";
|
|
27
27
|
import { tmpdir } from "node:os";
|
|
28
28
|
import { join } from "node:path";
|
|
@@ -58,7 +58,14 @@ function resolveBinary(): string {
|
|
|
58
58
|
"firefox",
|
|
59
59
|
];
|
|
60
60
|
for (const c of candidates) {
|
|
61
|
-
if (c)
|
|
61
|
+
if (!c) continue;
|
|
62
|
+
// An ABSOLUTE candidate (FIREFOX_BIN, the macOS app path) is only usable if it exists on this
|
|
63
|
+
// box — otherwise the hardcoded macOS path would win on Linux and shadow the "firefox" PATH
|
|
64
|
+
// fallback, yielding ENOENT at spawn. A bare name like "firefox" is not a filesystem path, so
|
|
65
|
+
// let spawn resolve it against PATH.
|
|
66
|
+
const isAbsolute = c.startsWith("/");
|
|
67
|
+
if (isAbsolute && !existsSync(c)) continue;
|
|
68
|
+
return c;
|
|
62
69
|
}
|
|
63
70
|
throw new Error(
|
|
64
71
|
"Firefox binary not found. Tried FIREFOX_BIN env var, " +
|