cdp-toolkit 1.9.7 → 1.11.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 +36 -0
- package/README.md +29 -7
- package/dist/cli.js +1154 -255
- package/dist/mcp.js +601 -257
- package/package.json +2 -1
- package/src/bidi/driver.ts +190 -28
- package/src/bidi/marionette.ts +276 -0
- package/src/bidi/session-lease.ts +265 -0
- package/src/cli.ts +10 -0
- package/src/install/browser.ts +211 -0
- package/src/install/harness.ts +224 -0
- package/src/install/wizard.ts +412 -0
- package/src/manifest.ts +252 -242
- package/src/mcp.ts +16 -2
package/dist/cli.js
CHANGED
|
@@ -5396,6 +5396,252 @@ async function connectBidiSessionUrl(wsUrl, opts = {}) {
|
|
|
5396
5396
|
return conn;
|
|
5397
5397
|
}
|
|
5398
5398
|
|
|
5399
|
+
// src/bidi/session-lease.ts
|
|
5400
|
+
import { mkdir as mkdir12, readFile as readFile8, unlink as unlink5, writeFile as writeFile9 } from "node:fs/promises";
|
|
5401
|
+
import { join as join10 } from "node:path";
|
|
5402
|
+
var DEFAULT_SESSION_WAIT_MS = 1e4;
|
|
5403
|
+
var DEFAULT_POLL_MS = 250;
|
|
5404
|
+
function safeEndpoint(endpoint) {
|
|
5405
|
+
return endpoint.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
5406
|
+
}
|
|
5407
|
+
function sessionSlotFile(endpoint) {
|
|
5408
|
+
return join10(leaseDir(), `ff-session-${safeEndpoint(endpoint)}.json`);
|
|
5409
|
+
}
|
|
5410
|
+
function sessionWaitMs() {
|
|
5411
|
+
const raw = Number(process.env.CDP_FIREFOX_SESSION_WAIT_MS);
|
|
5412
|
+
return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_SESSION_WAIT_MS;
|
|
5413
|
+
}
|
|
5414
|
+
|
|
5415
|
+
class SessionSlotBusyError extends Error {
|
|
5416
|
+
endpoint;
|
|
5417
|
+
holderPid;
|
|
5418
|
+
holderLabel;
|
|
5419
|
+
constructor(endpoint, holder) {
|
|
5420
|
+
const who = holder ? `'${holder.label}' (pid ${holder.pid})` : "another live process";
|
|
5421
|
+
super(`Firefox BiDi session slot for endpoint '${endpoint}' is held by ${who}. ` + `Firefox serves exactly ONE WebDriver-BiDi session per browser instance, and this slot is held ` + `by a LIVE process, so it cannot be taken. Wait for that process to release it and retry, or point ` + `this server at a different endpoint/browser.`);
|
|
5422
|
+
this.name = "SessionSlotBusyError";
|
|
5423
|
+
this.endpoint = endpoint;
|
|
5424
|
+
if (holder) {
|
|
5425
|
+
this.holderPid = holder.pid;
|
|
5426
|
+
this.holderLabel = holder.label;
|
|
5427
|
+
}
|
|
5428
|
+
}
|
|
5429
|
+
}
|
|
5430
|
+
async function readSessionSlot(endpoint) {
|
|
5431
|
+
let raw;
|
|
5432
|
+
try {
|
|
5433
|
+
raw = await readFile8(sessionSlotFile(endpoint), "utf8");
|
|
5434
|
+
} catch (err) {
|
|
5435
|
+
if (err.code === "ENOENT")
|
|
5436
|
+
return;
|
|
5437
|
+
throw err;
|
|
5438
|
+
}
|
|
5439
|
+
try {
|
|
5440
|
+
const rec = JSON.parse(raw);
|
|
5441
|
+
if (typeof rec?.pid === "number" && typeof rec?.endpoint === "string" && typeof rec?.label === "string") {
|
|
5442
|
+
return rec;
|
|
5443
|
+
}
|
|
5444
|
+
} catch {}
|
|
5445
|
+
return;
|
|
5446
|
+
}
|
|
5447
|
+
async function acquireSessionSlot(endpoint, opts) {
|
|
5448
|
+
const waitMs = opts.waitMs ?? sessionWaitMs();
|
|
5449
|
+
const pollMs = opts.pollMs !== undefined && opts.pollMs > 0 ? opts.pollMs : DEFAULT_POLL_MS;
|
|
5450
|
+
const createdAt = opts.now ?? Date.now();
|
|
5451
|
+
const file = sessionSlotFile(endpoint);
|
|
5452
|
+
const record2 = {
|
|
5453
|
+
endpoint,
|
|
5454
|
+
pid: process.pid,
|
|
5455
|
+
label: opts.label,
|
|
5456
|
+
createdAt
|
|
5457
|
+
};
|
|
5458
|
+
if (opts.marionettePort !== undefined)
|
|
5459
|
+
record2.marionettePort = opts.marionettePort;
|
|
5460
|
+
const handle = { endpoint, pid: process.pid, createdAt };
|
|
5461
|
+
const payload = JSON.stringify(record2);
|
|
5462
|
+
await mkdir12(leaseDir(), { recursive: true });
|
|
5463
|
+
const deadline = Date.now() + waitMs;
|
|
5464
|
+
const maxAttempts = Math.max(100, Math.ceil(waitMs / pollMs) + 100);
|
|
5465
|
+
let attempts = 0;
|
|
5466
|
+
let lastLiveHolder;
|
|
5467
|
+
while (true) {
|
|
5468
|
+
if (attempts >= maxAttempts)
|
|
5469
|
+
throw new SessionSlotBusyError(endpoint, lastLiveHolder);
|
|
5470
|
+
attempts++;
|
|
5471
|
+
try {
|
|
5472
|
+
await writeFile9(file, payload, { flag: "wx" });
|
|
5473
|
+
return { handle };
|
|
5474
|
+
} catch (err) {
|
|
5475
|
+
if (err.code !== "EEXIST")
|
|
5476
|
+
throw err;
|
|
5477
|
+
}
|
|
5478
|
+
const holder = await readSessionSlot(endpoint);
|
|
5479
|
+
if (!holder)
|
|
5480
|
+
continue;
|
|
5481
|
+
if (!isPidAlive(holder.pid)) {
|
|
5482
|
+
await unlink5(file).catch(() => {
|
|
5483
|
+
return;
|
|
5484
|
+
});
|
|
5485
|
+
try {
|
|
5486
|
+
await writeFile9(file, payload, { flag: "wx" });
|
|
5487
|
+
return { handle, staleHolder: holder };
|
|
5488
|
+
} catch (err) {
|
|
5489
|
+
if (err.code !== "EEXIST")
|
|
5490
|
+
throw err;
|
|
5491
|
+
continue;
|
|
5492
|
+
}
|
|
5493
|
+
}
|
|
5494
|
+
lastLiveHolder = holder;
|
|
5495
|
+
const remaining = deadline - Date.now();
|
|
5496
|
+
if (remaining <= 0)
|
|
5497
|
+
throw new SessionSlotBusyError(endpoint, holder);
|
|
5498
|
+
await sleep2(Math.min(pollMs, remaining));
|
|
5499
|
+
}
|
|
5500
|
+
}
|
|
5501
|
+
async function releaseSessionSlot(handle) {
|
|
5502
|
+
const current = await readSessionSlot(handle.endpoint);
|
|
5503
|
+
if (!current)
|
|
5504
|
+
return { released: false };
|
|
5505
|
+
if (current.pid !== process.pid || current.createdAt !== handle.createdAt)
|
|
5506
|
+
return { released: false };
|
|
5507
|
+
await unlink5(sessionSlotFile(handle.endpoint)).catch(() => {
|
|
5508
|
+
return;
|
|
5509
|
+
});
|
|
5510
|
+
return { released: true };
|
|
5511
|
+
}
|
|
5512
|
+
function sleep2(ms) {
|
|
5513
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5514
|
+
}
|
|
5515
|
+
|
|
5516
|
+
// src/bidi/marionette.ts
|
|
5517
|
+
import { Socket } from "node:net";
|
|
5518
|
+
var DEFAULT_MARIONETTE_PORT = 2828;
|
|
5519
|
+
var DEFAULT_TIMEOUT_MS3 = 4000;
|
|
5520
|
+
var HOST = "127.0.0.1";
|
|
5521
|
+
var COLON = 58;
|
|
5522
|
+
var DELETE_SESSION_FRAME = encodeFrame([0, 1, "WebDriver:DeleteSession", {}]);
|
|
5523
|
+
function defaultMarionettePort() {
|
|
5524
|
+
const raw = Number(process.env.CDP_FIREFOX_MARIONETTE_PORT);
|
|
5525
|
+
return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_MARIONETTE_PORT;
|
|
5526
|
+
}
|
|
5527
|
+
function encodeFrame(json) {
|
|
5528
|
+
const body = Buffer.from(JSON.stringify(json), "utf8");
|
|
5529
|
+
const prefix = Buffer.from(`${body.length}:`, "ascii");
|
|
5530
|
+
return Buffer.concat([prefix, body]);
|
|
5531
|
+
}
|
|
5532
|
+
|
|
5533
|
+
class MarionetteProtocolError extends Error {
|
|
5534
|
+
constructor(message) {
|
|
5535
|
+
super(message);
|
|
5536
|
+
this.name = "MarionetteProtocolError";
|
|
5537
|
+
}
|
|
5538
|
+
}
|
|
5539
|
+
|
|
5540
|
+
class FrameDecoder {
|
|
5541
|
+
buf = Buffer.alloc(0);
|
|
5542
|
+
push(chunk) {
|
|
5543
|
+
this.buf = this.buf.length === 0 ? chunk : Buffer.concat([this.buf, chunk]);
|
|
5544
|
+
}
|
|
5545
|
+
next() {
|
|
5546
|
+
const colon = this.buf.indexOf(COLON);
|
|
5547
|
+
if (colon === -1) {
|
|
5548
|
+
this.assertDigitsPrefix(this.buf.length);
|
|
5549
|
+
return;
|
|
5550
|
+
}
|
|
5551
|
+
if (colon === 0)
|
|
5552
|
+
throw new MarionetteProtocolError("empty frame length prefix");
|
|
5553
|
+
this.assertDigitsPrefix(colon);
|
|
5554
|
+
const len = Number.parseInt(this.buf.toString("ascii", 0, colon), 10);
|
|
5555
|
+
const start = colon + 1;
|
|
5556
|
+
const end = start + len;
|
|
5557
|
+
if (this.buf.length < end)
|
|
5558
|
+
return;
|
|
5559
|
+
const payload = this.buf.toString("utf8", start, end);
|
|
5560
|
+
this.buf = this.buf.subarray(end);
|
|
5561
|
+
try {
|
|
5562
|
+
return JSON.parse(payload);
|
|
5563
|
+
} catch {
|
|
5564
|
+
throw new MarionetteProtocolError(`frame body was not valid JSON (${len} bytes): ${payload.slice(0, 80)}`);
|
|
5565
|
+
}
|
|
5566
|
+
}
|
|
5567
|
+
assertDigitsPrefix(upTo) {
|
|
5568
|
+
for (let i = 0;i < upTo; i++) {
|
|
5569
|
+
const b = this.buf[i];
|
|
5570
|
+
if (b < 48 || b > 57) {
|
|
5571
|
+
throw new MarionetteProtocolError(`non-digit byte 0x${b.toString(16)} in frame length prefix`);
|
|
5572
|
+
}
|
|
5573
|
+
}
|
|
5574
|
+
}
|
|
5575
|
+
}
|
|
5576
|
+
function isSlotFreedReply(frame) {
|
|
5577
|
+
if (!Array.isArray(frame))
|
|
5578
|
+
return false;
|
|
5579
|
+
const err = frame[2];
|
|
5580
|
+
if (err === null)
|
|
5581
|
+
return true;
|
|
5582
|
+
if (typeof err === "object" && err !== null && err.error === "invalid session id") {
|
|
5583
|
+
return true;
|
|
5584
|
+
}
|
|
5585
|
+
return false;
|
|
5586
|
+
}
|
|
5587
|
+
function forceClearOrphanSession(marionettePort, opts = {}) {
|
|
5588
|
+
const timeoutMs = opts.timeoutMs !== undefined && Number.isFinite(opts.timeoutMs) && opts.timeoutMs > 0 ? opts.timeoutMs : DEFAULT_TIMEOUT_MS3;
|
|
5589
|
+
return new Promise((resolve) => {
|
|
5590
|
+
if (!Number.isFinite(marionettePort) || marionettePort <= 0 || marionettePort > 65535) {
|
|
5591
|
+
resolve(false);
|
|
5592
|
+
return;
|
|
5593
|
+
}
|
|
5594
|
+
const sock = new Socket;
|
|
5595
|
+
const decoder = new FrameDecoder;
|
|
5596
|
+
let settled = false;
|
|
5597
|
+
let handshakeSeen = false;
|
|
5598
|
+
let commandSent = false;
|
|
5599
|
+
const settle = (value) => {
|
|
5600
|
+
if (settled)
|
|
5601
|
+
return;
|
|
5602
|
+
settled = true;
|
|
5603
|
+
clearTimeout(timer);
|
|
5604
|
+
sock.removeAllListeners();
|
|
5605
|
+
sock.destroy();
|
|
5606
|
+
resolve(value);
|
|
5607
|
+
};
|
|
5608
|
+
const timer = setTimeout(() => settle(false), timeoutMs);
|
|
5609
|
+
if (typeof timer.unref === "function")
|
|
5610
|
+
timer.unref();
|
|
5611
|
+
sock.on("error", () => settle(false));
|
|
5612
|
+
sock.on("close", () => settle(false));
|
|
5613
|
+
sock.on("data", (chunk) => {
|
|
5614
|
+
if (settled)
|
|
5615
|
+
return;
|
|
5616
|
+
try {
|
|
5617
|
+
decoder.push(chunk);
|
|
5618
|
+
for (;; ) {
|
|
5619
|
+
const frame = decoder.next();
|
|
5620
|
+
if (frame === undefined)
|
|
5621
|
+
break;
|
|
5622
|
+
if (!handshakeSeen) {
|
|
5623
|
+
handshakeSeen = true;
|
|
5624
|
+
continue;
|
|
5625
|
+
}
|
|
5626
|
+
settle(isSlotFreedReply(frame));
|
|
5627
|
+
return;
|
|
5628
|
+
}
|
|
5629
|
+
if (handshakeSeen && !commandSent) {
|
|
5630
|
+
commandSent = true;
|
|
5631
|
+
sock.write(DELETE_SESSION_FRAME);
|
|
5632
|
+
}
|
|
5633
|
+
} catch {
|
|
5634
|
+
settle(false);
|
|
5635
|
+
}
|
|
5636
|
+
});
|
|
5637
|
+
try {
|
|
5638
|
+
sock.connect(marionettePort, HOST);
|
|
5639
|
+
} catch {
|
|
5640
|
+
settle(false);
|
|
5641
|
+
}
|
|
5642
|
+
});
|
|
5643
|
+
}
|
|
5644
|
+
|
|
5399
5645
|
// src/bidi/snapshot.ts
|
|
5400
5646
|
var STAMP_ATTR = "data-cdp-uid";
|
|
5401
5647
|
async function takeStampedSnapshot(evaluate) {
|
|
@@ -5972,6 +6218,22 @@ var BIDI_CAPABILITIES = new Set([
|
|
|
5972
6218
|
"locate.xpath"
|
|
5973
6219
|
]);
|
|
5974
6220
|
var connections = new Map;
|
|
6221
|
+
var slotHandles = new Map;
|
|
6222
|
+
var connecting = new Map;
|
|
6223
|
+
async function releaseSlotOnDialFailure(endpoint, owned) {
|
|
6224
|
+
if (owned)
|
|
6225
|
+
return;
|
|
6226
|
+
const h = slotHandles.get(endpoint);
|
|
6227
|
+
if (h) {
|
|
6228
|
+
await releaseSessionSlot(h).catch(() => {
|
|
6229
|
+
return;
|
|
6230
|
+
});
|
|
6231
|
+
slotHandles.delete(endpoint);
|
|
6232
|
+
}
|
|
6233
|
+
}
|
|
6234
|
+
function orphanNotClearedError(endpoint) {
|
|
6235
|
+
return driverError2("disconnected", `Firefox holds an orphaned WebDriver BiDi session on ${endpoint} that could not be auto-cleared. ` + `Marionette recovery needs Firefox launched with --marionette (default port 2828, or set ` + `CDP_FIREFOX_MARIONETTE_PORT); otherwise restart Firefox to free the slot. (A client killed without ` + `session.end can leave a session Firefox does not reap.)`);
|
|
6236
|
+
}
|
|
5975
6237
|
async function getConnection(endpoint, timeoutMs) {
|
|
5976
6238
|
const existing = connections.get(endpoint);
|
|
5977
6239
|
if (existing && existing.isOpen) {
|
|
@@ -5980,17 +6242,68 @@ async function getConnection(endpoint, timeoutMs) {
|
|
|
5980
6242
|
}
|
|
5981
6243
|
if (existing)
|
|
5982
6244
|
connections.delete(endpoint);
|
|
5983
|
-
const
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
6245
|
+
const inflight = connecting.get(endpoint);
|
|
6246
|
+
if (inflight) {
|
|
6247
|
+
const conn = await inflight;
|
|
6248
|
+
conn.retain();
|
|
6249
|
+
return conn;
|
|
6250
|
+
}
|
|
6251
|
+
const create = (async () => {
|
|
6252
|
+
const mport = defaultMarionettePort();
|
|
6253
|
+
const owned = slotHandles.has(endpoint);
|
|
6254
|
+
if (!owned) {
|
|
6255
|
+
let acq;
|
|
6256
|
+
try {
|
|
6257
|
+
acq = await acquireSessionSlot(endpoint, { label: defaultLabel(), marionettePort: mport });
|
|
6258
|
+
} catch (e) {
|
|
6259
|
+
if (e instanceof SessionSlotBusyError) {
|
|
6260
|
+
const who = e.holderLabel ? `'${e.holderLabel}' (pid ${e.holderPid})` : "another live process";
|
|
6261
|
+
throw driverError2("disconnected", `Firefox's single WebDriver BiDi session on ${endpoint} is held by a LIVE process ${who}. ` + `Firefox serves exactly ONE BiDi session per browser instance, so this server cannot take it ` + `while that process is running. Wait for it to release the session and retry, or point this ` + `server at a different endpoint/browser.`);
|
|
6262
|
+
}
|
|
6263
|
+
throw e;
|
|
6264
|
+
}
|
|
6265
|
+
slotHandles.set(endpoint, acq.handle);
|
|
6266
|
+
if (acq.staleHolder) {
|
|
6267
|
+
await forceClearOrphanSession(acq.staleHolder.marionettePort ?? mport);
|
|
6268
|
+
}
|
|
5989
6269
|
}
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
6270
|
+
const dialOpts = {
|
|
6271
|
+
...timeoutMs !== undefined ? { timeoutMs } : {},
|
|
6272
|
+
capabilities: { alwaysMatch: { unhandledPromptBehavior: { default: "ignore" } } }
|
|
6273
|
+
};
|
|
6274
|
+
let conn;
|
|
6275
|
+
try {
|
|
6276
|
+
conn = await connectBidiSessionUrl(endpoint, dialOpts);
|
|
6277
|
+
} catch (e) {
|
|
6278
|
+
if (e instanceof BidiError && /maximum number of active sessions/i.test(e.message)) {
|
|
6279
|
+
const cleared = await forceClearOrphanSession(mport);
|
|
6280
|
+
if (!cleared) {
|
|
6281
|
+
await releaseSlotOnDialFailure(endpoint, owned);
|
|
6282
|
+
throw orphanNotClearedError(endpoint);
|
|
6283
|
+
}
|
|
6284
|
+
try {
|
|
6285
|
+
conn = await connectBidiSessionUrl(endpoint, dialOpts);
|
|
6286
|
+
} catch (e2) {
|
|
6287
|
+
await releaseSlotOnDialFailure(endpoint, owned);
|
|
6288
|
+
if (e2 instanceof BidiError && /maximum number of active sessions/i.test(e2.message)) {
|
|
6289
|
+
throw orphanNotClearedError(endpoint);
|
|
6290
|
+
}
|
|
6291
|
+
throw e2;
|
|
6292
|
+
}
|
|
6293
|
+
} else {
|
|
6294
|
+
await releaseSlotOnDialFailure(endpoint, owned);
|
|
6295
|
+
throw e;
|
|
6296
|
+
}
|
|
6297
|
+
}
|
|
6298
|
+
connections.set(endpoint, conn);
|
|
6299
|
+
return conn;
|
|
6300
|
+
})();
|
|
6301
|
+
connecting.set(endpoint, create);
|
|
6302
|
+
try {
|
|
6303
|
+
return await create;
|
|
6304
|
+
} finally {
|
|
6305
|
+
connecting.delete(endpoint);
|
|
6306
|
+
}
|
|
5994
6307
|
}
|
|
5995
6308
|
var bidiBeaconScripts = new Map;
|
|
5996
6309
|
async function safeTitle(conn, contextId) {
|
|
@@ -6756,14 +7069,27 @@ class BidiBrowserDriver {
|
|
|
6756
7069
|
if (key.startsWith(`${this.endpoint}:`))
|
|
6757
7070
|
bidiBeaconScripts.delete(key);
|
|
6758
7071
|
}
|
|
7072
|
+
connecting.delete(this.endpoint);
|
|
6759
7073
|
const conn = connections.get(this.endpoint);
|
|
6760
|
-
if (!conn)
|
|
7074
|
+
if (!conn) {
|
|
7075
|
+
await this.releaseSessionSlotIfHeld();
|
|
6761
7076
|
return;
|
|
7077
|
+
}
|
|
6762
7078
|
connections.delete(this.endpoint);
|
|
6763
7079
|
await conn.send("session.end", {}).catch(() => {
|
|
6764
7080
|
return;
|
|
6765
7081
|
});
|
|
6766
7082
|
conn.dispose();
|
|
7083
|
+
await this.releaseSessionSlotIfHeld();
|
|
7084
|
+
}
|
|
7085
|
+
async releaseSessionSlotIfHeld() {
|
|
7086
|
+
const h = slotHandles.get(this.endpoint);
|
|
7087
|
+
if (!h)
|
|
7088
|
+
return;
|
|
7089
|
+
await releaseSessionSlot(h).catch(() => {
|
|
7090
|
+
return;
|
|
7091
|
+
});
|
|
7092
|
+
slotHandles.delete(this.endpoint);
|
|
6767
7093
|
}
|
|
6768
7094
|
}
|
|
6769
7095
|
function createFirefoxDriver(port, opts) {
|
|
@@ -6778,7 +7104,7 @@ import { spawn as spawn3 } from "node:child_process";
|
|
|
6778
7104
|
import { existsSync, mkdtempSync, rmSync } from "node:fs";
|
|
6779
7105
|
import { createConnection, createServer } from "node:net";
|
|
6780
7106
|
import { tmpdir } from "node:os";
|
|
6781
|
-
import { join as
|
|
7107
|
+
import { join as join11 } from "node:path";
|
|
6782
7108
|
function resolveBinary() {
|
|
6783
7109
|
const candidates = [
|
|
6784
7110
|
process.env.FIREFOX_BIN,
|
|
@@ -6856,7 +7182,7 @@ async function launchFirefox(opts = {}) {
|
|
|
6856
7182
|
const binary = resolveBinary();
|
|
6857
7183
|
const port = await pickPort(opts.port);
|
|
6858
7184
|
const isThrowaway = opts.profilePath === undefined;
|
|
6859
|
-
const profilePath = opts.profilePath ?? mkdtempSync(
|
|
7185
|
+
const profilePath = opts.profilePath ?? mkdtempSync(join11(tmpdir(), "cdp-toolkit-ff-profile-"));
|
|
6860
7186
|
const args = ["--profile", profilePath, "--remote-debugging-port", String(port), "--no-remote"];
|
|
6861
7187
|
if (opts.headless)
|
|
6862
7188
|
args.push("--headless");
|
|
@@ -7210,21 +7536,21 @@ var FIREFOX_TOOLS = {
|
|
|
7210
7536
|
var MANIFEST = [
|
|
7211
7537
|
{
|
|
7212
7538
|
name: "list_pages",
|
|
7213
|
-
description: "Enumerate browser page targets
|
|
7539
|
+
description: "Enumerate browser page targets (GET /json/list); page-type tabs only unless all=true. Each entry carries its targetId plus an origin field: 'agent' means this toolkit created it (also carries the creating label/createdAt, and stays 'agent' after the lease is released or the creator dies, making stray agent tabs findable), 'unknown' means no creation record - it never says 'human', the honest word for any tab it did not create; a record that exists but won't read adds originUnreadable. An entry under an active, readable lease also carries a 'lease' object {label,pid,idleMs,expiresAt,stale}, with idleMs and expiresAt computed fresh every call (never off disk), independent of probe. Under CDP_REQUIRE_LEASE this call also reaps abandoned agent tabs into an additive reaped array - but a lease reads reclaimable the moment its TTL elapses, while the tab is CLOSED only after a further grace (CDP_REAP_GRACE_MS, default 2700000ms/45min, so 60min total after last use); a dead-pid lease is reaped at once, a tab with no lease never. With probe, each page-type entry also gets 'responsive' (and humanActiveMs where human input is seen); both are absent, never false/null, without probe.",
|
|
7214
7540
|
inputSchema: {
|
|
7215
7541
|
type: "object",
|
|
7216
7542
|
properties: {
|
|
7217
7543
|
lease: {
|
|
7218
7544
|
type: "string",
|
|
7219
|
-
description: "
|
|
7545
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7220
7546
|
},
|
|
7221
7547
|
all: {
|
|
7222
7548
|
type: "boolean",
|
|
7223
|
-
description: "Include non-page targets (service/shared workers, background pages)
|
|
7549
|
+
description: "Include non-page targets (service/shared workers, background pages); default false lists page-type tabs only."
|
|
7224
7550
|
},
|
|
7225
7551
|
probe: {
|
|
7226
7552
|
type: "boolean",
|
|
7227
|
-
description: "Ping each page-type
|
|
7553
|
+
description: "Ping each page-type renderer before returning, one bounded 500ms check per target and never more, so a wedged tab can't stall the listing. Adds 'responsive' (false on timeout/exception/unreachable, never an error for the whole call) and, where the same round trip finds human input, humanActiveMs. Default false. On a backend that cannot answer the ping, responsive is absent too (not false)."
|
|
7228
7554
|
}
|
|
7229
7555
|
},
|
|
7230
7556
|
required: [],
|
|
@@ -7239,7 +7565,7 @@ var MANIFEST = [
|
|
|
7239
7565
|
properties: {
|
|
7240
7566
|
lease: {
|
|
7241
7567
|
type: "string",
|
|
7242
|
-
description: "
|
|
7568
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7243
7569
|
},
|
|
7244
7570
|
url: {
|
|
7245
7571
|
type: "string",
|
|
@@ -7247,15 +7573,15 @@ var MANIFEST = [
|
|
|
7247
7573
|
},
|
|
7248
7574
|
claim: {
|
|
7249
7575
|
type: "boolean",
|
|
7250
|
-
description: "Claim the new tab atomically
|
|
7576
|
+
description: "Claim the new tab atomically and return a lease token alongside {targetId,url}. Under CDP_REQUIRE_LEASE the tab is claimed and a lease returned even without claim:true; claim:true makes it an EXPLICIT lease that demands its token on every later call, even from this process. Default false."
|
|
7251
7577
|
},
|
|
7252
7578
|
label: {
|
|
7253
7579
|
type: "string",
|
|
7254
|
-
description: "Agent label recorded on the lease when
|
|
7580
|
+
description: "Agent label recorded on the lease when the tab is claimed; surfaced in conflict errors and list_leases. Defaults to pid-<pid>."
|
|
7255
7581
|
},
|
|
7256
7582
|
ttlMs: {
|
|
7257
7583
|
type: "number",
|
|
7258
|
-
description: "How long
|
|
7584
|
+
description: "How long a claimed lease survives without use before it is reclaimable. Defaults to CDP_LEASE_TTL_MS, else 900000 (15min); every checked call refreshes it. Ignored when not claiming."
|
|
7259
7585
|
}
|
|
7260
7586
|
},
|
|
7261
7587
|
required: [],
|
|
@@ -7264,17 +7590,17 @@ var MANIFEST = [
|
|
|
7264
7590
|
},
|
|
7265
7591
|
{
|
|
7266
7592
|
name: "close_page",
|
|
7267
|
-
description: "Close a page target via Target.closeTarget. Requires an explicit, resolvable target;
|
|
7593
|
+
description: "Close a page target via Target.closeTarget. Requires an explicit, resolvable target; errors rather than guessing when the selector matches nothing.",
|
|
7268
7594
|
inputSchema: {
|
|
7269
7595
|
type: "object",
|
|
7270
7596
|
properties: {
|
|
7271
7597
|
lease: {
|
|
7272
7598
|
type: "string",
|
|
7273
|
-
description: "
|
|
7599
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7274
7600
|
},
|
|
7275
7601
|
target: {
|
|
7276
7602
|
type: "string",
|
|
7277
|
-
description: "
|
|
7603
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
7278
7604
|
}
|
|
7279
7605
|
},
|
|
7280
7606
|
required: [
|
|
@@ -7291,11 +7617,11 @@ var MANIFEST = [
|
|
|
7291
7617
|
properties: {
|
|
7292
7618
|
lease: {
|
|
7293
7619
|
type: "string",
|
|
7294
|
-
description: "
|
|
7620
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7295
7621
|
},
|
|
7296
7622
|
target: {
|
|
7297
7623
|
type: "string",
|
|
7298
|
-
description: "
|
|
7624
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
7299
7625
|
}
|
|
7300
7626
|
},
|
|
7301
7627
|
required: [
|
|
@@ -7312,11 +7638,11 @@ var MANIFEST = [
|
|
|
7312
7638
|
properties: {
|
|
7313
7639
|
lease: {
|
|
7314
7640
|
type: "string",
|
|
7315
|
-
description: "
|
|
7641
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7316
7642
|
},
|
|
7317
7643
|
target: {
|
|
7318
7644
|
type: "string",
|
|
7319
|
-
description: "
|
|
7645
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
7320
7646
|
},
|
|
7321
7647
|
url: {
|
|
7322
7648
|
type: "string",
|
|
@@ -7332,7 +7658,7 @@ var MANIFEST = [
|
|
|
7332
7658
|
"back",
|
|
7333
7659
|
"forward"
|
|
7334
7660
|
],
|
|
7335
|
-
description: "Traverse this tab's session history instead of loading a url: 'back' and 'forward' are the browser's Back and Forward buttons. Mutually exclusive with 'url' and 'reload' — passing two of the three is refused by name
|
|
7661
|
+
description: "Traverse this tab's session history instead of loading a url: 'back' and 'forward' are the browser's Back and Forward buttons. Mutually exclusive with 'url' and 'reload' — passing two of the three is refused by name, not resolved by precedence. Works on BOTH backends and waits for the same load milestone as an ordinary navigation. Going back from the first entry, or forward from the last, is an ERROR naming the direction — never a silent no-op. The result carries traversed:'back'|'forward' alongside the resulting url."
|
|
7336
7662
|
},
|
|
7337
7663
|
ignoreCache: {
|
|
7338
7664
|
type: "boolean",
|
|
@@ -7356,17 +7682,17 @@ var MANIFEST = [
|
|
|
7356
7682
|
},
|
|
7357
7683
|
{
|
|
7358
7684
|
name: "wait_for",
|
|
7359
|
-
description: "Poll
|
|
7685
|
+
description: "Poll the target until the substring appears in document.body.innerText (Runtime.evaluate on a fixed interval), or throw on timeout. Text-substring only -- no aria/role/selector or event variants; throws rather than returning {found:false}.",
|
|
7360
7686
|
inputSchema: {
|
|
7361
7687
|
type: "object",
|
|
7362
7688
|
properties: {
|
|
7363
7689
|
lease: {
|
|
7364
7690
|
type: "string",
|
|
7365
|
-
description: "
|
|
7691
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7366
7692
|
},
|
|
7367
7693
|
target: {
|
|
7368
7694
|
type: "string",
|
|
7369
|
-
description: "
|
|
7695
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
7370
7696
|
},
|
|
7371
7697
|
text: {
|
|
7372
7698
|
type: "string",
|
|
@@ -7374,11 +7700,11 @@ var MANIFEST = [
|
|
|
7374
7700
|
},
|
|
7375
7701
|
timeoutMs: {
|
|
7376
7702
|
type: "number",
|
|
7377
|
-
description: "Total time budget in
|
|
7703
|
+
description: "Total time budget in ms. Default 15000."
|
|
7378
7704
|
},
|
|
7379
7705
|
pollMs: {
|
|
7380
7706
|
type: "number",
|
|
7381
|
-
description: "Poll interval in
|
|
7707
|
+
description: "Poll interval in ms. Default 250."
|
|
7382
7708
|
}
|
|
7383
7709
|
},
|
|
7384
7710
|
required: [
|
|
@@ -7389,37 +7715,37 @@ var MANIFEST = [
|
|
|
7389
7715
|
},
|
|
7390
7716
|
{
|
|
7391
7717
|
name: "evaluate_script",
|
|
7392
|
-
description: "Run arbitrary JavaScript in the target
|
|
7718
|
+
description: "Run arbitrary JavaScript in the target's main-world context over raw CDP and return the evaluated value (returnByValue). With no 'args', 'expression' is a raw expression; with 'args' it must be a function literal (arrow or classic) invoked on globalThis with args passed positionally. A thrown exception surfaces as an error; non-serializable returns (DOM nodes, functions) come back as their CDP description string. savePath writes the value to a JSON file instead -- the response then carries only {path,bytes,type,target} and the value never appears in it, the way to read a credential (a JWT/session token from localStorage) without putting the secret in the transcript. This is the one tool that EVALUATES inside an MV3 extension's background SERVICE WORKER rather than OBSERVING it (Chrome, Capability 'worker.targets'): a worker: target reads chrome.storage.local, chrome.runtime and the worker's own globals directly, with an idle-evicted worker started first (see 'wake').",
|
|
7393
7719
|
inputSchema: {
|
|
7394
7720
|
type: "object",
|
|
7395
7721
|
properties: {
|
|
7396
7722
|
lease: {
|
|
7397
7723
|
type: "string",
|
|
7398
|
-
description: "
|
|
7724
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7399
7725
|
},
|
|
7400
7726
|
target: {
|
|
7401
7727
|
type: "string",
|
|
7402
|
-
description: "Page selector: 'active' (
|
|
7728
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', '<targetId>'; plus 'worker:<substring>' to reach a service/shared worker (e.g. an MV3 extension background worker). The worker: arm is CHROME-ONLY (Capability 'worker.targets'). Grammar: see server instructions."
|
|
7403
7729
|
},
|
|
7404
7730
|
wake: {
|
|
7405
7731
|
type: "boolean",
|
|
7406
|
-
description: "
|
|
7732
|
+
description: "Worker targets only, when starting a capture. Default true: an idle-evicted MV3 service worker exists in no target listing, so it is started first (ServiceWorker.startWorker) and re-confirmed; false fails fast. Rejected on a page target, a bare target id, or a read-only call. See server instructions."
|
|
7407
7733
|
},
|
|
7408
7734
|
expression: {
|
|
7409
7735
|
type: "string",
|
|
7410
|
-
description: "JavaScript to run. Evaluated as an expression when 'args' is omitted; must be a function literal (arrow or classic) whose parameters receive 'args' when
|
|
7736
|
+
description: "JavaScript to run. Evaluated as an expression when 'args' is omitted; must be a function literal (arrow or classic) whose parameters receive 'args' when provided. REQUIRED -- the code always goes in this key, never in 'function', 'code', 'js', 'script', 'fn' or 'body'; a call using one of those is rejected with an error naming the wrong key."
|
|
7411
7737
|
},
|
|
7412
7738
|
awaitPromise: {
|
|
7413
7739
|
type: "boolean",
|
|
7414
|
-
description: "Await the result if it is a Promise
|
|
7740
|
+
description: "Await the result if it is a Promise. Default true."
|
|
7415
7741
|
},
|
|
7416
7742
|
args: {
|
|
7417
7743
|
type: "array",
|
|
7418
|
-
description: "Positional JSON-serializable arguments
|
|
7744
|
+
description: "Positional JSON-serializable arguments passed to the expression (treated as a function). No live element/page handle is bound."
|
|
7419
7745
|
},
|
|
7420
7746
|
savePath: {
|
|
7421
7747
|
type: "string",
|
|
7422
|
-
description: "Write the evaluated value to this file as JSON and KEEP IT OUT OF THE RESPONSE:
|
|
7748
|
+
description: "Write the evaluated value to this file as JSON and KEEP IT OUT OF THE RESPONSE: the result is {path,bytes,type,target} only, with no copy, preview or truncation of the value -- read credentials without putting them in a transcript. An absolute path (starting with /) is used as-is; a relative path resolves under the artifact dir (/tmp/cdp-toolkit); missing parents are created. Omit to get the value back inline."
|
|
7423
7749
|
}
|
|
7424
7750
|
},
|
|
7425
7751
|
required: [
|
|
@@ -7430,21 +7756,21 @@ var MANIFEST = [
|
|
|
7430
7756
|
},
|
|
7431
7757
|
{
|
|
7432
7758
|
name: "list_cookies",
|
|
7433
|
-
description: "Read the cookie store
|
|
7759
|
+
description: "Read the target page's cookie store INCLUDING httpOnly cookies, which document.cookie (and therefore evaluate_script) cannot see. Page-scoped, not browser-wide: returns cookies of the resolved tab, so point at a page on the site you want. Each cookie carries name, value, domain, path, expires (Unix seconds, -1 for a session cookie), size, httpOnly, secure, sameSite ('strict'|'lax'|'none'|'default'), session. Filter with 'domain' and/or 'name'. Pass 'savePath' to write the array to a JSON file instead: the response is then {path,bytes,count,target} with no cookie value in any form, the way to capture a session cookie without putting the credential in the transcript.",
|
|
7434
7760
|
inputSchema: {
|
|
7435
7761
|
type: "object",
|
|
7436
7762
|
properties: {
|
|
7437
7763
|
lease: {
|
|
7438
7764
|
type: "string",
|
|
7439
|
-
description: "
|
|
7765
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7440
7766
|
},
|
|
7441
7767
|
target: {
|
|
7442
7768
|
type: "string",
|
|
7443
|
-
description: "
|
|
7769
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
7444
7770
|
},
|
|
7445
7771
|
domain: {
|
|
7446
7772
|
type: "string",
|
|
7447
|
-
description: "Keep only cookies for this domain. A leading dot is ignored on both sides and subdomains
|
|
7773
|
+
description: "Keep only cookies for this domain. A leading dot is ignored on both sides and subdomains match too, so 'example.test' matches '.example.test' and 'app.example.test'. No wildcards."
|
|
7448
7774
|
},
|
|
7449
7775
|
name: {
|
|
7450
7776
|
type: "string",
|
|
@@ -7452,7 +7778,7 @@ var MANIFEST = [
|
|
|
7452
7778
|
},
|
|
7453
7779
|
savePath: {
|
|
7454
7780
|
type: "string",
|
|
7455
|
-
description: "Write the cookie array
|
|
7781
|
+
description: "Write the cookie array here as JSON and KEEP THE VALUES OUT OF THE RESPONSE: the result is {path,bytes,count,target} only, with no preview or truncation of any value. An absolute path (starting with /) is used as-is; a relative path resolves under the artifact dir (/tmp/cdp-toolkit); missing parents are created. Omit to get the cookies back inline."
|
|
7456
7782
|
}
|
|
7457
7783
|
},
|
|
7458
7784
|
required: [],
|
|
@@ -7461,17 +7787,17 @@ var MANIFEST = [
|
|
|
7461
7787
|
},
|
|
7462
7788
|
{
|
|
7463
7789
|
name: "set_cookie",
|
|
7464
|
-
description: "Write one cookie into the target page's cookie store, INCLUDING
|
|
7790
|
+
description: "Write one cookie into the target page's cookie store, INCLUDING httpOnly or secure cookies that document.cookie (and therefore evaluate_script) cannot create. Either 'url' or 'domain' is REQUIRED: the call is refused when neither is given, rather than guessing the page origin. The response is {set:true,target} and never echoes the value back. A 'set:true' is earned, not assumed: Chrome raises an error when it declines the cookie (a domain the url does not belong to, a secure cookie on an insecure origin, an oversized value). On Firefox 'domain' is derived from 'url' when only the url was given (BiDi has no url parameter), and a url with no host such as about:blank or a data URL is an error, not a silent no-op. Read the result back with list_cookies when you need proof.",
|
|
7465
7791
|
inputSchema: {
|
|
7466
7792
|
type: "object",
|
|
7467
7793
|
properties: {
|
|
7468
7794
|
lease: {
|
|
7469
7795
|
type: "string",
|
|
7470
|
-
description: "
|
|
7796
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7471
7797
|
},
|
|
7472
7798
|
target: {
|
|
7473
7799
|
type: "string",
|
|
7474
|
-
description: "
|
|
7800
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
7475
7801
|
},
|
|
7476
7802
|
name: {
|
|
7477
7803
|
type: "string",
|
|
@@ -7483,19 +7809,19 @@ var MANIFEST = [
|
|
|
7483
7809
|
},
|
|
7484
7810
|
url: {
|
|
7485
7811
|
type: "string",
|
|
7486
|
-
description: "
|
|
7812
|
+
description: "URL the cookie is set for, e.g. 'https://example.com/'. Chrome derives domain, path and secure from it; Firefox derives only the domain from its host. Give this or 'domain'."
|
|
7487
7813
|
},
|
|
7488
7814
|
domain: {
|
|
7489
7815
|
type: "string",
|
|
7490
|
-
description: "Cookie domain,
|
|
7816
|
+
description: "Cookie domain, e.g. 'example.com' or '.example.com' for subdomains. Give this or 'url'. When both are given, this wins on Firefox and Chrome applies its own url plus domain consistency rule."
|
|
7491
7817
|
},
|
|
7492
7818
|
path: {
|
|
7493
7819
|
type: "string",
|
|
7494
|
-
description: "Cookie path
|
|
7820
|
+
description: "Cookie path, passed through exactly as given and NOT defaulted to '/': with a 'url' Chrome derives the path from it, so an invented default here would widen a cookie you meant to scope narrowly."
|
|
7495
7821
|
},
|
|
7496
7822
|
expires: {
|
|
7497
7823
|
type: "number",
|
|
7498
|
-
description: "Expiry as Unix time in SECONDS, matching what list_cookies reports. Omit
|
|
7824
|
+
description: "Expiry as Unix time in SECONDS, matching what list_cookies reports. Omit for a session cookie, which is what both backends do when no expiry is supplied."
|
|
7499
7825
|
},
|
|
7500
7826
|
httpOnly: {
|
|
7501
7827
|
type: "boolean",
|
|
@@ -7507,27 +7833,35 @@ var MANIFEST = [
|
|
|
7507
7833
|
},
|
|
7508
7834
|
sameSite: {
|
|
7509
7835
|
type: "string",
|
|
7510
|
-
enum: [
|
|
7836
|
+
enum: [
|
|
7837
|
+
"strict",
|
|
7838
|
+
"lax",
|
|
7839
|
+
"none",
|
|
7840
|
+
"default"
|
|
7841
|
+
],
|
|
7511
7842
|
description: "SameSite attribute, lowercase, matching what list_cookies reports. 'default' means the attribute is not set at all."
|
|
7512
7843
|
}
|
|
7513
7844
|
},
|
|
7514
|
-
required: [
|
|
7845
|
+
required: [
|
|
7846
|
+
"name",
|
|
7847
|
+
"value"
|
|
7848
|
+
],
|
|
7515
7849
|
additionalProperties: false
|
|
7516
7850
|
}
|
|
7517
7851
|
},
|
|
7518
7852
|
{
|
|
7519
7853
|
name: "delete_cookies",
|
|
7520
|
-
description: "Delete the named cookie from the target page's cookie store, httpOnly cookies included.
|
|
7854
|
+
description: "Delete the named cookie from the target page's cookie store, httpOnly cookies included. Both 'name' and one of 'url' or 'domain' are REQUIRED: without a site constraint it would delete by name across the whole partition, so it is refused with an error instead. Narrow further with 'path'. The response is {deleted:true,target} and carries NO count, because neither protocol reports how many cookies it removed and a number here would be invented; call list_cookies before and after for a real count. 'deleted:true' means the backend accepted and performed the deletion, not that a matching cookie existed, since deleting an absent cookie succeeds on both backends.",
|
|
7521
7855
|
inputSchema: {
|
|
7522
7856
|
type: "object",
|
|
7523
7857
|
properties: {
|
|
7524
7858
|
lease: {
|
|
7525
7859
|
type: "string",
|
|
7526
|
-
description: "
|
|
7860
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7527
7861
|
},
|
|
7528
7862
|
target: {
|
|
7529
7863
|
type: "string",
|
|
7530
|
-
description: "
|
|
7864
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
7531
7865
|
},
|
|
7532
7866
|
name: {
|
|
7533
7867
|
type: "string",
|
|
@@ -7535,7 +7869,7 @@ var MANIFEST = [
|
|
|
7535
7869
|
},
|
|
7536
7870
|
url: {
|
|
7537
7871
|
type: "string",
|
|
7538
|
-
description: "Delete cookies matching this URL,
|
|
7872
|
+
description: "Delete cookies matching this URL, e.g. 'https://example.com/'. Give this or 'domain'. On Firefox only the host is used, because BiDi's filter has no url field."
|
|
7539
7873
|
},
|
|
7540
7874
|
domain: {
|
|
7541
7875
|
type: "string",
|
|
@@ -7546,27 +7880,29 @@ var MANIFEST = [
|
|
|
7546
7880
|
description: "Narrow the deletion to cookies with this exact path. Omit to match any path allowed by the other constraints."
|
|
7547
7881
|
}
|
|
7548
7882
|
},
|
|
7549
|
-
required: [
|
|
7883
|
+
required: [
|
|
7884
|
+
"name"
|
|
7885
|
+
],
|
|
7550
7886
|
additionalProperties: false
|
|
7551
7887
|
}
|
|
7552
7888
|
},
|
|
7553
7889
|
{
|
|
7554
7890
|
name: "take_snapshot",
|
|
7555
|
-
description: "
|
|
7891
|
+
description: "Captures the page's accessibility tree as an indented text tree, each line prefixed [uid] (a CDP backendDOMNodeId). Every interaction tool (click/hover/fill/etc.) resolves that uid via DOM.resolveNode — run this first to discover uids to act on.",
|
|
7556
7892
|
inputSchema: {
|
|
7557
7893
|
type: "object",
|
|
7558
7894
|
properties: {
|
|
7559
7895
|
lease: {
|
|
7560
7896
|
type: "string",
|
|
7561
|
-
description: "
|
|
7897
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7562
7898
|
},
|
|
7563
7899
|
target: {
|
|
7564
7900
|
type: "string",
|
|
7565
|
-
description: "
|
|
7901
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
7566
7902
|
},
|
|
7567
7903
|
interactiveOnly: {
|
|
7568
7904
|
type: "boolean",
|
|
7569
|
-
description: "When true, emit only interactive/meaningful nodes
|
|
7905
|
+
description: "When true, emit only interactive/meaningful nodes as a flat list; default false returns the full hierarchical tree."
|
|
7570
7906
|
}
|
|
7571
7907
|
},
|
|
7572
7908
|
required: [],
|
|
@@ -7575,25 +7911,25 @@ var MANIFEST = [
|
|
|
7575
7911
|
},
|
|
7576
7912
|
{
|
|
7577
7913
|
name: "click",
|
|
7578
|
-
description: "Click an element via a synthetic mouse press/release at
|
|
7914
|
+
description: "Click an element via a synthetic mouse press/release at its scrolled-into-view center. Give exactly one of uid or selector. modifiers (Alt/Control/Meta/Shift) throws on the Firefox backend — use --browser chrome for a modifier-click. clickCount:3 triple-clicks (selects a line/paragraph in most editors).",
|
|
7579
7915
|
inputSchema: {
|
|
7580
7916
|
type: "object",
|
|
7581
7917
|
properties: {
|
|
7582
7918
|
lease: {
|
|
7583
7919
|
type: "string",
|
|
7584
|
-
description: "
|
|
7920
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7585
7921
|
},
|
|
7586
7922
|
target: {
|
|
7587
7923
|
type: "string",
|
|
7588
|
-
description: "
|
|
7924
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
7589
7925
|
},
|
|
7590
7926
|
uid: {
|
|
7591
7927
|
type: "number",
|
|
7592
|
-
description: "CDP backendDOMNodeId of the element to click,
|
|
7928
|
+
description: "CDP backendDOMNodeId of the element to click, from take_snapshot. Exactly one of uid or selector."
|
|
7593
7929
|
},
|
|
7594
7930
|
selector: {
|
|
7595
7931
|
type: "string",
|
|
7596
|
-
description: "CSS selector for the element to click (
|
|
7932
|
+
description: "CSS selector for the element to click (document.querySelector). Exactly one of uid or selector."
|
|
7597
7933
|
},
|
|
7598
7934
|
button: {
|
|
7599
7935
|
type: "string",
|
|
@@ -7606,7 +7942,7 @@ var MANIFEST = [
|
|
|
7606
7942
|
},
|
|
7607
7943
|
clickCount: {
|
|
7608
7944
|
type: "number",
|
|
7609
|
-
description: "Number of clicks: 1
|
|
7945
|
+
description: "Number of clicks: 1 (default), 2 = double-click, 3 = triple-click."
|
|
7610
7946
|
},
|
|
7611
7947
|
modifiers: {
|
|
7612
7948
|
type: "array",
|
|
@@ -7619,7 +7955,7 @@ var MANIFEST = [
|
|
|
7619
7955
|
"Shift"
|
|
7620
7956
|
]
|
|
7621
7957
|
},
|
|
7622
|
-
description: "Modifier keys held for the click's press and release, e.g. ['Shift']
|
|
7958
|
+
description: "Modifier keys held for the click's press and release, e.g. ['Shift']. Non-empty on the Firefox backend throws — not supported over BiDi."
|
|
7623
7959
|
}
|
|
7624
7960
|
},
|
|
7625
7961
|
required: [],
|
|
@@ -7628,25 +7964,25 @@ var MANIFEST = [
|
|
|
7628
7964
|
},
|
|
7629
7965
|
{
|
|
7630
7966
|
name: "hover",
|
|
7631
|
-
description: "
|
|
7967
|
+
description: "Hovers the mouse over an element by dispatching a mouseMoved event at its scrolled-into-view center, firing framework hover handlers. Give exactly one of uid or selector.",
|
|
7632
7968
|
inputSchema: {
|
|
7633
7969
|
type: "object",
|
|
7634
7970
|
properties: {
|
|
7635
7971
|
lease: {
|
|
7636
7972
|
type: "string",
|
|
7637
|
-
description: "
|
|
7973
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7638
7974
|
},
|
|
7639
7975
|
target: {
|
|
7640
7976
|
type: "string",
|
|
7641
|
-
description: "
|
|
7977
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
7642
7978
|
},
|
|
7643
7979
|
uid: {
|
|
7644
7980
|
type: "number",
|
|
7645
|
-
description: "CDP backendDOMNodeId of the element to hover,
|
|
7981
|
+
description: "CDP backendDOMNodeId of the element to hover, from take_snapshot. Exactly one of uid or selector."
|
|
7646
7982
|
},
|
|
7647
7983
|
selector: {
|
|
7648
7984
|
type: "string",
|
|
7649
|
-
description: "CSS selector for the element to hover (
|
|
7985
|
+
description: "CSS selector for the element to hover (document.querySelector). Exactly one of uid or selector."
|
|
7650
7986
|
}
|
|
7651
7987
|
},
|
|
7652
7988
|
required: [],
|
|
@@ -7655,21 +7991,21 @@ var MANIFEST = [
|
|
|
7655
7991
|
},
|
|
7656
7992
|
{
|
|
7657
7993
|
name: "drag",
|
|
7658
|
-
description:
|
|
7994
|
+
description: "Drags from a source element to a destination or by an offset. mode:'mouse' (default) dispatches synthetic mouse press/move/release; at the default steps:2 an HTML5 drop zone (dragover preventDefault) gets ZERO dragover events and refuses the drop. mode:'html5' replays real dragstart/dragEnter/dragOver/drop with the page's own dataTransfer instead — deterministic regardless of pointer path, but CHROME-ONLY (rejected with a clear error on the Firefox backend). 'from' takes uid or selector; destination is exactly one of 'to' (uid/selector/x+y) or 'by' ({dx,dy} offset — sliders, map panning, resize handles).",
|
|
7659
7995
|
inputSchema: {
|
|
7660
7996
|
type: "object",
|
|
7661
7997
|
properties: {
|
|
7662
7998
|
lease: {
|
|
7663
7999
|
type: "string",
|
|
7664
|
-
description: "
|
|
8000
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7665
8001
|
},
|
|
7666
8002
|
target: {
|
|
7667
8003
|
type: "string",
|
|
7668
|
-
description: "
|
|
8004
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
7669
8005
|
},
|
|
7670
8006
|
from: {
|
|
7671
8007
|
type: "object",
|
|
7672
|
-
description: "Source element to drag from.
|
|
8008
|
+
description: "Source element to drag from. Exactly one of uid or selector.",
|
|
7673
8009
|
properties: {
|
|
7674
8010
|
uid: {
|
|
7675
8011
|
type: "number",
|
|
@@ -7684,7 +8020,7 @@ var MANIFEST = [
|
|
|
7684
8020
|
},
|
|
7685
8021
|
to: {
|
|
7686
8022
|
type: "object",
|
|
7687
|
-
description: "Where the drag ends: exactly one of uid, selector, or x+y. Mutually exclusive with 'by'; exactly one of to/by
|
|
8023
|
+
description: "Where the drag ends: exactly one of uid, selector, or x+y. Mutually exclusive with 'by'; exactly one of to/by required.",
|
|
7688
8024
|
properties: {
|
|
7689
8025
|
uid: {
|
|
7690
8026
|
type: "number",
|
|
@@ -7707,7 +8043,7 @@ var MANIFEST = [
|
|
|
7707
8043
|
},
|
|
7708
8044
|
by: {
|
|
7709
8045
|
type: "object",
|
|
7710
|
-
description: "
|
|
8046
|
+
description: "Offset from the source element's center: at least one of dx/dy (other defaults to 0). Mutually exclusive with 'to'; exactly one of to/by required. For sliders (by:{dx:40}), map panning, resize handles.",
|
|
7711
8047
|
properties: {
|
|
7712
8048
|
dx: {
|
|
7713
8049
|
type: "number",
|
|
@@ -7726,11 +8062,11 @@ var MANIFEST = [
|
|
|
7726
8062
|
"mouse",
|
|
7727
8063
|
"html5"
|
|
7728
8064
|
],
|
|
7729
|
-
description: "'mouse' (default): synthetic mouse press/move/release
|
|
8065
|
+
description: "'mouse' (default): synthetic mouse press/move/release — at steps:2 an HTML5 drop zone sees zero dragover events and refuses the drop. 'html5': real HTML5 drag-and-drop, deterministic regardless of pointer path, CHROME-ONLY (rejected on Firefox)."
|
|
7730
8066
|
},
|
|
7731
8067
|
steps: {
|
|
7732
8068
|
type: "number",
|
|
7733
|
-
description: "Number of interpolated mouse-move events
|
|
8069
|
+
description: "Number of interpolated mouse-move events between source and destination, evenly spaced, last landing on the destination. Integer 1-500, default 2. Raise for DnD libraries with a movement threshold or per-frame sampling."
|
|
7734
8070
|
}
|
|
7735
8071
|
},
|
|
7736
8072
|
required: [
|
|
@@ -7741,41 +8077,41 @@ var MANIFEST = [
|
|
|
7741
8077
|
},
|
|
7742
8078
|
{
|
|
7743
8079
|
name: "scroll",
|
|
7744
|
-
description: "
|
|
8080
|
+
description: "Dispatches a wheel/scroll event at an anchor point — element (uid/selector) or x+y, scrolled into view first if an element; omit all three for viewport center. At least one of deltaX/deltaY required: positive deltaY scrolls DOWN, positive deltaX scrolls RIGHT (wheel-event convention — easy to invert). Returns the resolved anchor {x,y} plus the delta actually dispatched.",
|
|
7745
8081
|
inputSchema: {
|
|
7746
8082
|
type: "object",
|
|
7747
8083
|
properties: {
|
|
7748
8084
|
lease: {
|
|
7749
8085
|
type: "string",
|
|
7750
|
-
description: "
|
|
8086
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7751
8087
|
},
|
|
7752
8088
|
target: {
|
|
7753
8089
|
type: "string",
|
|
7754
|
-
description: "
|
|
8090
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
7755
8091
|
},
|
|
7756
8092
|
uid: {
|
|
7757
8093
|
type: "number",
|
|
7758
|
-
description: "CDP backendDOMNodeId of the element to scroll into view and anchor at,
|
|
8094
|
+
description: "CDP backendDOMNodeId of the element to scroll into view and anchor at, from take_snapshot. At most one of uid, selector, or x+y."
|
|
7759
8095
|
},
|
|
7760
8096
|
selector: {
|
|
7761
8097
|
type: "string",
|
|
7762
|
-
description: "CSS selector for the element to
|
|
8098
|
+
description: "CSS selector for the element to anchor at (document.querySelector). At most one of uid, selector, or x+y."
|
|
7763
8099
|
},
|
|
7764
8100
|
x: {
|
|
7765
8101
|
type: "number",
|
|
7766
|
-
description: "Absolute viewport x-coordinate to anchor
|
|
8102
|
+
description: "Absolute viewport x-coordinate to anchor at; must be given with y. At most one of uid, selector, or x+y."
|
|
7767
8103
|
},
|
|
7768
8104
|
y: {
|
|
7769
8105
|
type: "number",
|
|
7770
|
-
description: "Absolute viewport y-coordinate to anchor
|
|
8106
|
+
description: "Absolute viewport y-coordinate to anchor at; must be given with x. At most one of uid, selector, or x+y."
|
|
7771
8107
|
},
|
|
7772
8108
|
deltaX: {
|
|
7773
8109
|
type: "number",
|
|
7774
|
-
description: "Horizontal scroll delta; positive scrolls RIGHT. At least one of deltaX/deltaY
|
|
8110
|
+
description: "Horizontal scroll delta; positive scrolls RIGHT. At least one of deltaX/deltaY required."
|
|
7775
8111
|
},
|
|
7776
8112
|
deltaY: {
|
|
7777
8113
|
type: "number",
|
|
7778
|
-
description: "Vertical scroll delta; positive scrolls DOWN. At least one of deltaX/deltaY
|
|
8114
|
+
description: "Vertical scroll delta; positive scrolls DOWN. At least one of deltaX/deltaY required."
|
|
7779
8115
|
}
|
|
7780
8116
|
},
|
|
7781
8117
|
required: [],
|
|
@@ -7784,17 +8120,17 @@ var MANIFEST = [
|
|
|
7784
8120
|
},
|
|
7785
8121
|
{
|
|
7786
8122
|
name: "dispatch_mouse",
|
|
7787
|
-
description: "
|
|
8123
|
+
description: "Dispatches one raw mouse event (move/down/up) at absolute viewport coordinates — the toolkit's lowest-level input primitive. Compose move/down/move/up calls yourself for anything click/drag's fixed sequences can't reach: canvas drag-painting, marquee/rubber-band selection, custom hit-testing widgets. Chrome-only: absent from tools/list under the Firefox backend, never present-and-throwing.",
|
|
7788
8124
|
inputSchema: {
|
|
7789
8125
|
type: "object",
|
|
7790
8126
|
properties: {
|
|
7791
8127
|
lease: {
|
|
7792
8128
|
type: "string",
|
|
7793
|
-
description: "
|
|
8129
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7794
8130
|
},
|
|
7795
8131
|
target: {
|
|
7796
8132
|
type: "string",
|
|
7797
|
-
description: "
|
|
8133
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
7798
8134
|
},
|
|
7799
8135
|
action: {
|
|
7800
8136
|
type: "string",
|
|
@@ -7807,11 +8143,11 @@ var MANIFEST = [
|
|
|
7807
8143
|
},
|
|
7808
8144
|
x: {
|
|
7809
8145
|
type: "number",
|
|
7810
|
-
description: "Viewport x-coordinate. Required
|
|
8146
|
+
description: "Viewport x-coordinate. Required every call — CDP has no 'current pointer position' to default from."
|
|
7811
8147
|
},
|
|
7812
8148
|
y: {
|
|
7813
8149
|
type: "number",
|
|
7814
|
-
description: "Viewport y-coordinate. Required
|
|
8150
|
+
description: "Viewport y-coordinate. Required every call."
|
|
7815
8151
|
},
|
|
7816
8152
|
button: {
|
|
7817
8153
|
type: "string",
|
|
@@ -7824,7 +8160,7 @@ var MANIFEST = [
|
|
|
7824
8160
|
},
|
|
7825
8161
|
clickCount: {
|
|
7826
8162
|
type: "number",
|
|
7827
|
-
description: "Click-run length for a 'down' or 'up' event (2 =
|
|
8163
|
+
description: "Click-run length for a 'down' or 'up' event (2 = second half of a double-click, 3 = triple-click). Ignored for 'move'; defaults to 1."
|
|
7828
8164
|
},
|
|
7829
8165
|
modifiers: {
|
|
7830
8166
|
type: "array",
|
|
@@ -7850,17 +8186,17 @@ var MANIFEST = [
|
|
|
7850
8186
|
},
|
|
7851
8187
|
{
|
|
7852
8188
|
name: "wait_for_download",
|
|
7853
|
-
description: "Wait for a file download to finish and return it as a real file on disk: {path,suggestedFilename,bytes,url,target}
|
|
8189
|
+
description: "Wait for a file download to finish and return it as a real file on disk: {path,suggestedFilename,bytes,url,target}, written under the artifact dir's downloads/. ORDERING RULE, not optional: capture must be ARMED BEFORE the click that starts the download — call wait_for_download{arm:true} first (returns {armed:true,downloadPath,pending}), then click, then call again to collect the file. The download-behavior override is per-connection state Chrome REVERTS the moment the arming client disconnects, and an unarmed headless Chrome denies the download outright, so one triggered before arming is lost. SIDE EFFECT, browser-global: arming redirects EVERY download in this browser (all tabs, all origins) into the toolkit's downloads dir until this server exits — they no longer land in the user's normal Downloads folder. The arm lives on a connection this server holds open (an MCP-server capability): under the one-shot CLI the connection dies with the process and nothing is captured. Chrome-only (capability 'browser.downloads'): absent from tools/list under Firefox.",
|
|
7854
8190
|
inputSchema: {
|
|
7855
8191
|
type: "object",
|
|
7856
8192
|
properties: {
|
|
7857
8193
|
lease: {
|
|
7858
8194
|
type: "string",
|
|
7859
|
-
description: "
|
|
8195
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7860
8196
|
},
|
|
7861
8197
|
target: {
|
|
7862
8198
|
type: "string",
|
|
7863
|
-
description: "
|
|
8199
|
+
description: "Names the tab for the lease check and the echoed target — a download is browser-scoped, so this does not pick which download is returned. Grammar: server instructions."
|
|
7864
8200
|
},
|
|
7865
8201
|
arm: {
|
|
7866
8202
|
type: "boolean",
|
|
@@ -7868,7 +8204,7 @@ var MANIFEST = [
|
|
|
7868
8204
|
},
|
|
7869
8205
|
timeoutMs: {
|
|
7870
8206
|
type: "number",
|
|
7871
|
-
description: "How long to wait for a download to complete, in milliseconds. Default 30000. Ignored with arm:true. A download that already completed and was not yet collected is returned immediately, so this only bounds the wait for
|
|
8207
|
+
description: "How long to wait for a download to complete, in milliseconds. Default 30000. Ignored with arm:true. A download that already completed and was not yet collected is returned immediately, so this only bounds the wait for one still in flight."
|
|
7872
8208
|
}
|
|
7873
8209
|
},
|
|
7874
8210
|
required: [],
|
|
@@ -7877,17 +8213,17 @@ var MANIFEST = [
|
|
|
7877
8213
|
},
|
|
7878
8214
|
{
|
|
7879
8215
|
name: "grant_permissions",
|
|
7880
|
-
description: "Grant browser permissions for an origin up front, so a page asking for geolocation / notifications / clipboard gets an answer instead of
|
|
8216
|
+
description: "Grant browser permissions for an origin up front, so a page asking for geolocation / notifications / clipboard gets an answer instead of a prompt no agent can click. 'permissions' takes CDP PermissionType values ('geolocation','notifications','clipboardReadWrite','camera',...); an unknown name is refused by Chrome, quoting the bad value. Returns {granted,origin,target}, or {reset:true,target} for a reset-only call. Grants are keyed by ORIGIN, not by tab: every tab on that origin is affected, including ones opened later. reset:true clears this server's previous grants first (or instead, when no permissions are given) — CDP's reset is not origin-scoped, so it clears them for every origin at once. The grant lives on a connection this server holds open and Chrome DISCARDS IT when that connection closes: an MCP-server capability, so under the one-shot CLI the grant dies with the process. Chrome-only (capability 'browser.permissions'): absent from tools/list under Firefox, never present-and-throwing.",
|
|
7881
8217
|
inputSchema: {
|
|
7882
8218
|
type: "object",
|
|
7883
8219
|
properties: {
|
|
7884
8220
|
lease: {
|
|
7885
8221
|
type: "string",
|
|
7886
|
-
description: "
|
|
8222
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7887
8223
|
},
|
|
7888
8224
|
target: {
|
|
7889
8225
|
type: "string",
|
|
7890
|
-
description: "
|
|
8226
|
+
description: "Also the origin source when 'origin' is omitted. Grammar: server instructions."
|
|
7891
8227
|
},
|
|
7892
8228
|
permissions: {
|
|
7893
8229
|
type: "array",
|
|
@@ -7911,29 +8247,29 @@ var MANIFEST = [
|
|
|
7911
8247
|
},
|
|
7912
8248
|
{
|
|
7913
8249
|
name: "fill",
|
|
7914
|
-
description: "
|
|
8250
|
+
description: "Fills an element by focusing it, clearing existing content, then inserting value via Input.insertText (atomic paste-like commit, not per-character keystrokes — won't fire per-keypress handlers). Give exactly one of uid or selector.",
|
|
7915
8251
|
inputSchema: {
|
|
7916
8252
|
type: "object",
|
|
7917
8253
|
properties: {
|
|
7918
8254
|
lease: {
|
|
7919
8255
|
type: "string",
|
|
7920
|
-
description: "
|
|
8256
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7921
8257
|
},
|
|
7922
8258
|
target: {
|
|
7923
8259
|
type: "string",
|
|
7924
|
-
description: "
|
|
8260
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
7925
8261
|
},
|
|
7926
8262
|
uid: {
|
|
7927
8263
|
type: "number",
|
|
7928
|
-
description: "CDP backendDOMNodeId of the field to fill,
|
|
8264
|
+
description: "CDP backendDOMNodeId of the field to fill, from take_snapshot. Exactly one of uid or selector."
|
|
7929
8265
|
},
|
|
7930
8266
|
selector: {
|
|
7931
8267
|
type: "string",
|
|
7932
|
-
description: "CSS selector for the field to fill (
|
|
8268
|
+
description: "CSS selector for the field to fill (document.querySelector). Exactly one of uid or selector."
|
|
7933
8269
|
},
|
|
7934
8270
|
value: {
|
|
7935
8271
|
type: "string",
|
|
7936
|
-
description: "
|
|
8272
|
+
description: "Text value to set; the field is cleared first, overwriting existing content."
|
|
7937
8273
|
}
|
|
7938
8274
|
},
|
|
7939
8275
|
required: [
|
|
@@ -7944,17 +8280,17 @@ var MANIFEST = [
|
|
|
7944
8280
|
},
|
|
7945
8281
|
{
|
|
7946
8282
|
name: "fill_form",
|
|
7947
|
-
description: "
|
|
8283
|
+
description: "Fills multiple form fields in one call; each is focused, cleared, then set via Input.insertText (overwrites existing content, same as fill). Each entry in the non-empty 'fields' array takes exactly one of uid or selector plus a string value.",
|
|
7948
8284
|
inputSchema: {
|
|
7949
8285
|
type: "object",
|
|
7950
8286
|
properties: {
|
|
7951
8287
|
lease: {
|
|
7952
8288
|
type: "string",
|
|
7953
|
-
description: "
|
|
8289
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
7954
8290
|
},
|
|
7955
8291
|
target: {
|
|
7956
8292
|
type: "string",
|
|
7957
|
-
description: "
|
|
8293
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
7958
8294
|
},
|
|
7959
8295
|
fields: {
|
|
7960
8296
|
type: "array",
|
|
@@ -7990,29 +8326,29 @@ var MANIFEST = [
|
|
|
7990
8326
|
},
|
|
7991
8327
|
{
|
|
7992
8328
|
name: "type_text",
|
|
7993
|
-
description: "
|
|
8329
|
+
description: "Focuses an element and appends text via Input.insertText without clearing first — closest primitive to typing, but still atomic (not per-character keystrokes). Give exactly one of uid or selector.",
|
|
7994
8330
|
inputSchema: {
|
|
7995
8331
|
type: "object",
|
|
7996
8332
|
properties: {
|
|
7997
8333
|
lease: {
|
|
7998
8334
|
type: "string",
|
|
7999
|
-
description: "
|
|
8335
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8000
8336
|
},
|
|
8001
8337
|
target: {
|
|
8002
8338
|
type: "string",
|
|
8003
|
-
description: "
|
|
8339
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
8004
8340
|
},
|
|
8005
8341
|
uid: {
|
|
8006
8342
|
type: "number",
|
|
8007
|
-
description: "CDP backendDOMNodeId of the element to type into,
|
|
8343
|
+
description: "CDP backendDOMNodeId of the element to type into, from take_snapshot. Exactly one of uid or selector."
|
|
8008
8344
|
},
|
|
8009
8345
|
selector: {
|
|
8010
8346
|
type: "string",
|
|
8011
|
-
description: "CSS selector for the element to type into (
|
|
8347
|
+
description: "CSS selector for the element to type into (document.querySelector). Exactly one of uid or selector."
|
|
8012
8348
|
},
|
|
8013
8349
|
text: {
|
|
8014
8350
|
type: "string",
|
|
8015
|
-
description: "
|
|
8351
|
+
description: "Text to insert; appended to existing content rather than overwriting it."
|
|
8016
8352
|
}
|
|
8017
8353
|
},
|
|
8018
8354
|
required: [
|
|
@@ -8023,25 +8359,25 @@ var MANIFEST = [
|
|
|
8023
8359
|
},
|
|
8024
8360
|
{
|
|
8025
8361
|
name: "press_key",
|
|
8026
|
-
description: "
|
|
8362
|
+
description: "Dispatches a keyDown/keyUp pair for one key with optional modifiers. 'key' is a named key (Enter, Tab, Escape, ArrowDown, Backspace, etc.) or a single printable character — the named-key table is curated, with no F-keys, numpad, or IME support.",
|
|
8027
8363
|
inputSchema: {
|
|
8028
8364
|
type: "object",
|
|
8029
8365
|
properties: {
|
|
8030
8366
|
lease: {
|
|
8031
8367
|
type: "string",
|
|
8032
|
-
description: "
|
|
8368
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8033
8369
|
},
|
|
8034
8370
|
target: {
|
|
8035
8371
|
type: "string",
|
|
8036
|
-
description: "
|
|
8372
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
8037
8373
|
},
|
|
8038
8374
|
key: {
|
|
8039
8375
|
type: "string",
|
|
8040
|
-
description: "Key to press: a named key
|
|
8376
|
+
description: "Key to press: a named key (Enter, Tab, Escape, ArrowDown, Backspace, ...) or a single printable character."
|
|
8041
8377
|
},
|
|
8042
8378
|
modifiers: {
|
|
8043
8379
|
type: "array",
|
|
8044
|
-
description: "
|
|
8380
|
+
description: "Modifier names held during the press: 'Control'/'Ctrl', 'Shift', 'Alt', 'Meta'/'Cmd'.",
|
|
8045
8381
|
items: {
|
|
8046
8382
|
type: "string"
|
|
8047
8383
|
}
|
|
@@ -8055,32 +8391,32 @@ var MANIFEST = [
|
|
|
8055
8391
|
},
|
|
8056
8392
|
{
|
|
8057
8393
|
name: "upload_file",
|
|
8058
|
-
description: "
|
|
8394
|
+
description: "Attaches one or more files to an <input type=file> element via DOM.setFileInputFiles. Give exactly one of uid or selector for the input; 'files' is an array of absolute path(s) — wrap a single path in an array too.",
|
|
8059
8395
|
inputSchema: {
|
|
8060
8396
|
type: "object",
|
|
8061
8397
|
properties: {
|
|
8062
8398
|
lease: {
|
|
8063
8399
|
type: "string",
|
|
8064
|
-
description: "
|
|
8400
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8065
8401
|
},
|
|
8066
8402
|
target: {
|
|
8067
8403
|
type: "string",
|
|
8068
|
-
description: "
|
|
8404
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
8069
8405
|
},
|
|
8070
8406
|
uid: {
|
|
8071
8407
|
type: "number",
|
|
8072
|
-
description: "CDP backendDOMNodeId of the file input element,
|
|
8408
|
+
description: "CDP backendDOMNodeId of the file input element, from take_snapshot. Exactly one of uid or selector."
|
|
8073
8409
|
},
|
|
8074
8410
|
selector: {
|
|
8075
8411
|
type: "string",
|
|
8076
|
-
description: "CSS selector for the file input element (
|
|
8412
|
+
description: "CSS selector for the file input element (document.querySelector). Exactly one of uid or selector."
|
|
8077
8413
|
},
|
|
8078
8414
|
files: {
|
|
8079
8415
|
type: "array",
|
|
8080
8416
|
items: {
|
|
8081
8417
|
type: "string"
|
|
8082
8418
|
},
|
|
8083
|
-
description: 'Absolute path(s) to
|
|
8419
|
+
description: 'Absolute path(s) to attach to the <input type=file>, as an array — wrap a single path too, e.g. ["/tmp/a.pdf"]. Array-only because some model APIs (Google Gemini function calling) reject union-typed parameters.'
|
|
8084
8420
|
}
|
|
8085
8421
|
},
|
|
8086
8422
|
required: [
|
|
@@ -8091,17 +8427,17 @@ var MANIFEST = [
|
|
|
8091
8427
|
},
|
|
8092
8428
|
{
|
|
8093
8429
|
name: "take_screenshot",
|
|
8094
|
-
description: "
|
|
8430
|
+
description: "Captures the viewport (default), the full scrollable page (fullPage), or one element (uid or selector, mutually exclusive) via CDP Page.captureScreenshot. Past 16384 device px per side Chrome can hang rather than refuse, so size is checked first via Page.getLayoutMetrics; an oversized capture is auto-tiled into vertical bands stitched losslessly into one PNG (see tile) instead of wedging the tab. Writes PNG/JPEG to /tmp/cdp-toolkit (override savePath, dir auto-created); returns {path,bytes,format,target,scale} plus width/height measured from the encoded bytes (omitted if undecodable). renderSize/renderRestored(+renderRestoreError) accompany a renderWidth/renderHeight capture, tiled/bands a banded one. returnBase64 adds raw bytes to the result but is refused on a banded capture. quality applies to jpeg only.",
|
|
8095
8431
|
inputSchema: {
|
|
8096
8432
|
type: "object",
|
|
8097
8433
|
properties: {
|
|
8098
8434
|
lease: {
|
|
8099
8435
|
type: "string",
|
|
8100
|
-
description: "
|
|
8436
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8101
8437
|
},
|
|
8102
8438
|
target: {
|
|
8103
8439
|
type: "string",
|
|
8104
|
-
description: "
|
|
8440
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
8105
8441
|
},
|
|
8106
8442
|
format: {
|
|
8107
8443
|
type: "string",
|
|
@@ -8109,35 +8445,35 @@ var MANIFEST = [
|
|
|
8109
8445
|
"png",
|
|
8110
8446
|
"jpeg"
|
|
8111
8447
|
],
|
|
8112
|
-
description: "Image format
|
|
8448
|
+
description: "Image format, default png. quality applies only to jpeg."
|
|
8113
8449
|
},
|
|
8114
8450
|
quality: {
|
|
8115
8451
|
type: "number",
|
|
8116
|
-
description: "JPEG quality 0-100
|
|
8452
|
+
description: "JPEG quality 0-100, default 80; ignored for png."
|
|
8117
8453
|
},
|
|
8118
8454
|
fullPage: {
|
|
8119
8455
|
type: "boolean",
|
|
8120
|
-
description: "Capture the full scrollable
|
|
8456
|
+
description: "Capture the full scrollable height (via Page.getLayoutMetrics), not just the viewport."
|
|
8121
8457
|
},
|
|
8122
8458
|
scale: {
|
|
8123
8459
|
type: "number",
|
|
8124
|
-
description: "Output-pixel multiplier for this
|
|
8460
|
+
description: "Output-pixel multiplier for this capture only (0<scale<=8, default 1): output px = ceil(css px x scale x devicePixelRatio); the page is never told anything changed. Chrome only (screenshot.scale) — Firefox BiDi has no scale param, use emulate{deviceScaleFactor} then scale:1. A projection over 16384 device px/side is refused up front, naming the largest scale that fits."
|
|
8125
8461
|
},
|
|
8126
8462
|
renderWidth: {
|
|
8127
8463
|
type: "number",
|
|
8128
|
-
description: "
|
|
8464
|
+
description: "Emulates this viewport width (CSS px, 1-16384) for one capture, then restores it. Required with renderHeight. Unlike scale, this changes what the page believes (media queries re-flow) — e.g. capture a responsive page at 1920x1080 from a differently-sized tab. Restore only works for an override this toolkit itself set; check result.renderRestored (false = still emulated, fix via emulate). Works on both backends (capability screenshot.renderSize)."
|
|
8129
8465
|
},
|
|
8130
8466
|
renderHeight: {
|
|
8131
8467
|
type: "number",
|
|
8132
|
-
description: "
|
|
8468
|
+
description: "Emulates this viewport height (CSS px, 1-16384) for one capture. Required with renderWidth — a viewport is two numbers. A narrow renderWidth often reflows pages TALLER, which is the usual way a render-size capture trips the 16384px encode limit."
|
|
8133
8469
|
},
|
|
8134
8470
|
tile: {
|
|
8135
8471
|
type: "boolean",
|
|
8136
|
-
description: "Whether
|
|
8472
|
+
description: "Whether to allow vertical-band stitching: omitted=AUTO (band only if a single shot wouldn't fit), false=never band (refuse instead), true=always band (Chrome-only capability). Banded output is PNG-only, can't returnBase64, and only bands on HEIGHT (oversized WIDTH is refused, not split). Bands are a faithful crop, but lazy/virtualized content the real viewport never scrolled to renders blank."
|
|
8137
8473
|
},
|
|
8138
8474
|
uid: {
|
|
8139
8475
|
type: "number",
|
|
8140
|
-
description: "Element to clip to: a
|
|
8476
|
+
description: "Element to clip to: a backendDOMNodeId from take_snapshot. Mutually exclusive with selector."
|
|
8141
8477
|
},
|
|
8142
8478
|
selector: {
|
|
8143
8479
|
type: "string",
|
|
@@ -8145,11 +8481,11 @@ var MANIFEST = [
|
|
|
8145
8481
|
},
|
|
8146
8482
|
savePath: {
|
|
8147
8483
|
type: "string",
|
|
8148
|
-
description: "Override the output
|
|
8484
|
+
description: "Override the output path (default /tmp/cdp-toolkit/screenshot-<id>-<stamp>.<ext>, stamped at capture START). Directory is created from this path's dirname."
|
|
8149
8485
|
},
|
|
8150
8486
|
returnBase64: {
|
|
8151
8487
|
type: "boolean",
|
|
8152
|
-
description: "Also return
|
|
8488
|
+
description: "Also return raw base64 bytes in the result. Refused on a banded capture (see tile) — a stitched image can run to hundreds of MB; read the file at the returned path instead."
|
|
8153
8489
|
}
|
|
8154
8490
|
},
|
|
8155
8491
|
required: [],
|
|
@@ -8158,41 +8494,41 @@ var MANIFEST = [
|
|
|
8158
8494
|
},
|
|
8159
8495
|
{
|
|
8160
8496
|
name: "emulate",
|
|
8161
|
-
description: "
|
|
8497
|
+
description: "Applies any subset of Chrome emulation overrides in one call: device metrics (width+height together, +deviceScaleFactor/mobile), userAgent, cpuThrottlingRate(>=1), media type+features, networkConditions. Device-metrics overrides PERSIST on the target until cleared or the renderer navigates/reloads; every other override is session-scoped and resets when this call's connection closes. clearOverrides:true resets everything (ignoring other fields) and, as of 1.9.2, genuinely clears a device-metrics override set by a DIFFERENT process — Chrome's clearDeviceMetricsOverride is normally a no-op unless the clearing connection also set the override, so the driver re-asserts the tab's current size first, then clears, throwing rather than reporting cleared:true if it can't verify.",
|
|
8162
8498
|
inputSchema: {
|
|
8163
8499
|
type: "object",
|
|
8164
8500
|
properties: {
|
|
8165
8501
|
lease: {
|
|
8166
8502
|
type: "string",
|
|
8167
|
-
description: "
|
|
8503
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8168
8504
|
},
|
|
8169
8505
|
target: {
|
|
8170
8506
|
type: "string",
|
|
8171
|
-
description: "
|
|
8507
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
8172
8508
|
},
|
|
8173
8509
|
width: {
|
|
8174
8510
|
type: "number",
|
|
8175
|
-
description: "Device-metrics viewport width in CSS
|
|
8511
|
+
description: "Device-metrics viewport width in CSS px. Must be paired with height."
|
|
8176
8512
|
},
|
|
8177
8513
|
height: {
|
|
8178
8514
|
type: "number",
|
|
8179
|
-
description: "Device-metrics viewport height in CSS
|
|
8515
|
+
description: "Device-metrics viewport height in CSS px. Must be paired with width."
|
|
8180
8516
|
},
|
|
8181
8517
|
deviceScaleFactor: {
|
|
8182
8518
|
type: "number",
|
|
8183
|
-
description: "Device pixel ratio
|
|
8519
|
+
description: "Device pixel ratio for the device-metrics override; 0 = platform default."
|
|
8184
8520
|
},
|
|
8185
8521
|
mobile: {
|
|
8186
8522
|
type: "boolean",
|
|
8187
|
-
description: "
|
|
8523
|
+
description: "Emulate a mobile device (viewport meta, scrollbars, etc.) for the device-metrics override."
|
|
8188
8524
|
},
|
|
8189
8525
|
userAgent: {
|
|
8190
8526
|
type: "string",
|
|
8191
|
-
description: "User-Agent string
|
|
8527
|
+
description: "User-Agent string override (Emulation.setUserAgentOverride)."
|
|
8192
8528
|
},
|
|
8193
8529
|
cpuThrottlingRate: {
|
|
8194
8530
|
type: "number",
|
|
8195
|
-
description: "CPU throttling multiplier: 1
|
|
8531
|
+
description: "CPU throttling multiplier: 1=none, 2=2x slower, etc. Must be >=1."
|
|
8196
8532
|
},
|
|
8197
8533
|
media: {
|
|
8198
8534
|
type: "string",
|
|
@@ -8200,7 +8536,7 @@ var MANIFEST = [
|
|
|
8200
8536
|
},
|
|
8201
8537
|
mediaFeatures: {
|
|
8202
8538
|
type: "array",
|
|
8203
|
-
description: "Emulated media features, e.g. [{
|
|
8539
|
+
description: "Emulated media features, e.g. [{name:'prefers-color-scheme', value:'dark'}].",
|
|
8204
8540
|
items: {
|
|
8205
8541
|
type: "object",
|
|
8206
8542
|
properties: {
|
|
@@ -8222,7 +8558,7 @@ var MANIFEST = [
|
|
|
8222
8558
|
},
|
|
8223
8559
|
networkConditions: {
|
|
8224
8560
|
type: "object",
|
|
8225
|
-
description: "Network
|
|
8561
|
+
description: "Network overrides applied via Network.emulateNetworkConditions.",
|
|
8226
8562
|
properties: {
|
|
8227
8563
|
offline: {
|
|
8228
8564
|
type: "boolean",
|
|
@@ -8249,7 +8585,7 @@ var MANIFEST = [
|
|
|
8249
8585
|
},
|
|
8250
8586
|
clearOverrides: {
|
|
8251
8587
|
type: "boolean",
|
|
8252
|
-
description: "Reset every override (
|
|
8588
|
+
description: "Reset every override (metrics, UA, CPU, media, network) to browser default; ignores all other fields when true."
|
|
8253
8589
|
}
|
|
8254
8590
|
},
|
|
8255
8591
|
required: [],
|
|
@@ -8258,33 +8594,33 @@ var MANIFEST = [
|
|
|
8258
8594
|
},
|
|
8259
8595
|
{
|
|
8260
8596
|
name: "resize_page",
|
|
8261
|
-
description: "
|
|
8597
|
+
description: "Sets the page's device-metrics width/height (the narrow case of emulate), optionally deviceScaleFactor/mobile, then verifies by reading back window.innerWidth/innerHeight after applying. Requires positive width and height. The override PERSISTS on the target until cleared (emulate{clearOverrides:true}) or the renderer navigates/reloads.",
|
|
8262
8598
|
inputSchema: {
|
|
8263
8599
|
type: "object",
|
|
8264
8600
|
properties: {
|
|
8265
8601
|
lease: {
|
|
8266
8602
|
type: "string",
|
|
8267
|
-
description: "
|
|
8603
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8268
8604
|
},
|
|
8269
8605
|
target: {
|
|
8270
8606
|
type: "string",
|
|
8271
|
-
description: "
|
|
8607
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
8272
8608
|
},
|
|
8273
8609
|
width: {
|
|
8274
8610
|
type: "number",
|
|
8275
|
-
description: "Viewport width in CSS
|
|
8611
|
+
description: "Viewport width in CSS px (required, must be positive)."
|
|
8276
8612
|
},
|
|
8277
8613
|
height: {
|
|
8278
8614
|
type: "number",
|
|
8279
|
-
description: "Viewport height in CSS
|
|
8615
|
+
description: "Viewport height in CSS px (required, must be positive)."
|
|
8280
8616
|
},
|
|
8281
8617
|
deviceScaleFactor: {
|
|
8282
8618
|
type: "number",
|
|
8283
|
-
description: "Device pixel ratio
|
|
8619
|
+
description: "Device pixel ratio; 0 = platform default."
|
|
8284
8620
|
},
|
|
8285
8621
|
mobile: {
|
|
8286
8622
|
type: "boolean",
|
|
8287
|
-
description: "
|
|
8623
|
+
description: "Emulate a mobile device for the device-metrics override."
|
|
8288
8624
|
}
|
|
8289
8625
|
},
|
|
8290
8626
|
required: [
|
|
@@ -8296,17 +8632,17 @@ var MANIFEST = [
|
|
|
8296
8632
|
},
|
|
8297
8633
|
{
|
|
8298
8634
|
name: "handle_dialog",
|
|
8299
|
-
description: "Wait for the next JavaScript dialog (alert/confirm/prompt/beforeunload) on a page and respond via Page.handleJavaScriptDialog, accepting or dismissing it. Default mode resolves with the first handled dialog or throws on timeout; set autoMs to handle every dialog opening during a fixed window and resolve with the list.
|
|
8635
|
+
description: "Wait for the next JavaScript dialog (alert/confirm/prompt/beforeunload) on a page and respond via Page.handleJavaScriptDialog, accepting or dismissing it. Default mode resolves with the first handled dialog or throws on timeout; set autoMs to handle every dialog opening during a fixed window and resolve with the list. Trigger the dialog out-of-band (e.g. click a button) — a blocking dialog freezes the renderer, so it cannot be triggered inline.",
|
|
8300
8636
|
inputSchema: {
|
|
8301
8637
|
type: "object",
|
|
8302
8638
|
properties: {
|
|
8303
8639
|
lease: {
|
|
8304
8640
|
type: "string",
|
|
8305
|
-
description: "
|
|
8641
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8306
8642
|
},
|
|
8307
8643
|
target: {
|
|
8308
8644
|
type: "string",
|
|
8309
|
-
description: "
|
|
8645
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
8310
8646
|
},
|
|
8311
8647
|
accept: {
|
|
8312
8648
|
type: "boolean",
|
|
@@ -8318,7 +8654,7 @@ var MANIFEST = [
|
|
|
8318
8654
|
},
|
|
8319
8655
|
timeoutMs: {
|
|
8320
8656
|
type: "number",
|
|
8321
|
-
description: "How long to wait for the next dialog in milliseconds (default 15000)."
|
|
8657
|
+
description: "How long to wait for the next dialog, in milliseconds (default 15000)."
|
|
8322
8658
|
},
|
|
8323
8659
|
autoMs: {
|
|
8324
8660
|
type: "number",
|
|
@@ -8333,29 +8669,29 @@ var MANIFEST = [
|
|
|
8333
8669
|
},
|
|
8334
8670
|
{
|
|
8335
8671
|
name: "list_console_messages",
|
|
8336
|
-
description: "Read console output (logs, warnings, exceptions) captured for the target
|
|
8672
|
+
description: "Read console output (logs, warnings, exceptions) captured for the target. Reads the existing shared buffer by default (empty if no capture ran); reload:true reloads and records a fresh capture (console+network together, so a network reload never wipes console history). WORKER capture (Chrome, Capability 'worker.targets'): a worker: target records an MV3 background service worker -- its console.log arrives as Runtime.consoleAPICalled, its outbound requests from CDP's Network domain on the worker's own session -- so the two tricks callers try first fail for plain JS reasons, not worker ones: a value bound at module top level is not visible from evaluate_script's global scope, and assigning self.fetch cannot rebind a fetch the module already captured. A worker has no reload: reload:true means 'listen for durationMs while it runs', so trigger its work (tool call, message, alarm) during the window; a worker woken by the call already ran its top-level code, so a startup message can be missed. SIDE EFFECT: the CDP session KEEPS THE WORKER ALIVE for the capture (an idle MV3 worker is evicted in seconds); Chrome resumes evicting after.",
|
|
8337
8673
|
inputSchema: {
|
|
8338
8674
|
type: "object",
|
|
8339
8675
|
properties: {
|
|
8340
8676
|
lease: {
|
|
8341
8677
|
type: "string",
|
|
8342
|
-
description: "
|
|
8678
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8343
8679
|
},
|
|
8344
8680
|
target: {
|
|
8345
8681
|
type: "string",
|
|
8346
|
-
description: "
|
|
8682
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', '<targetId>'; plus 'worker:<substring>' to reach a service/shared worker (e.g. an MV3 extension background worker). The worker: arm is CHROME-ONLY (Capability 'worker.targets'). Grammar: see server instructions."
|
|
8347
8683
|
},
|
|
8348
8684
|
reload: {
|
|
8349
8685
|
type: "boolean",
|
|
8350
|
-
description: "Record fresh by reloading the page and capturing
|
|
8686
|
+
description: "Record fresh by reloading the page and capturing a window. Default false (read existing buffer). With a worker: target nothing reloads -- records for durationMs while the worker runs and keeps it alive until the capture stops."
|
|
8351
8687
|
},
|
|
8352
8688
|
durationMs: {
|
|
8353
8689
|
type: "number",
|
|
8354
|
-
description: "Capture window for reload mode, in
|
|
8690
|
+
description: "Capture window for reload mode, in ms. Default 2500."
|
|
8355
8691
|
},
|
|
8356
8692
|
wake: {
|
|
8357
8693
|
type: "boolean",
|
|
8358
|
-
description: "Worker targets only,
|
|
8694
|
+
description: "Worker targets only, when starting a capture. Default true: an idle-evicted MV3 service worker exists in no target listing, so it is started first (ServiceWorker.startWorker) and re-confirmed; false fails fast. Rejected on a page target, a bare target id, or a read-only call. See server instructions."
|
|
8359
8695
|
}
|
|
8360
8696
|
},
|
|
8361
8697
|
required: [],
|
|
@@ -8364,17 +8700,17 @@ var MANIFEST = [
|
|
|
8364
8700
|
},
|
|
8365
8701
|
{
|
|
8366
8702
|
name: "get_console_message",
|
|
8367
|
-
description: "Return
|
|
8703
|
+
description: "Return one console entry by zero-based index from the target's existing console buffer. Throws if the index is out of range; run list_console_messages (optionally reload:true) first to populate the buffer.",
|
|
8368
8704
|
inputSchema: {
|
|
8369
8705
|
type: "object",
|
|
8370
8706
|
properties: {
|
|
8371
8707
|
lease: {
|
|
8372
8708
|
type: "string",
|
|
8373
|
-
description: "
|
|
8709
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8374
8710
|
},
|
|
8375
8711
|
target: {
|
|
8376
8712
|
type: "string",
|
|
8377
|
-
description: "
|
|
8713
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
8378
8714
|
},
|
|
8379
8715
|
index: {
|
|
8380
8716
|
type: "number",
|
|
@@ -8387,33 +8723,33 @@ var MANIFEST = [
|
|
|
8387
8723
|
},
|
|
8388
8724
|
{
|
|
8389
8725
|
name: "list_network_requests",
|
|
8390
|
-
description: "Return correlated network
|
|
8726
|
+
description: "Return correlated network rows (one per requestId; status/headers/state) for the target. Reads the existing buffer by default; reload:true records a fresh both-domains capture window. filterUrl keeps only URLs containing a substring. WORKER capture (Chrome, Capability 'worker.targets'): a worker: target records an MV3 background service worker's outbound requests from CDP's Network domain on the worker's own session -- what the code ACTUALLY SENT -- so the two tricks callers try first fail for plain JS reasons, not worker ones: a value bound at module top level is not visible from evaluate_script's global scope, and assigning self.fetch cannot rebind a fetch the module already captured. A worker has no reload: reload:true means 'listen for durationMs while it runs', so trigger its work (tool call, message, alarm) during the window; a worker woken by the call already ran its top-level code, so a startup request can be missed. SIDE EFFECT: the CDP session KEEPS THE WORKER ALIVE for the capture (an idle MV3 worker is evicted in seconds); Chrome resumes evicting after.",
|
|
8391
8727
|
inputSchema: {
|
|
8392
8728
|
type: "object",
|
|
8393
8729
|
properties: {
|
|
8394
8730
|
lease: {
|
|
8395
8731
|
type: "string",
|
|
8396
|
-
description: "
|
|
8732
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8397
8733
|
},
|
|
8398
8734
|
target: {
|
|
8399
8735
|
type: "string",
|
|
8400
|
-
description: "
|
|
8736
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', '<targetId>'; plus 'worker:<substring>' to reach a service/shared worker (e.g. an MV3 extension background worker). The worker: arm is CHROME-ONLY (Capability 'worker.targets'). Grammar: see server instructions."
|
|
8401
8737
|
},
|
|
8402
8738
|
reload: {
|
|
8403
8739
|
type: "boolean",
|
|
8404
|
-
description: "Record fresh by reloading the page and capturing
|
|
8740
|
+
description: "Record fresh by reloading the page and capturing a window. Default false (read existing buffer). With a worker: target nothing reloads -- records for durationMs while the worker runs and keeps it alive until the capture stops."
|
|
8405
8741
|
},
|
|
8406
8742
|
durationMs: {
|
|
8407
8743
|
type: "number",
|
|
8408
|
-
description: "Capture window for reload mode, in
|
|
8744
|
+
description: "Capture window for reload mode, in ms. Default 2500."
|
|
8409
8745
|
},
|
|
8410
8746
|
filterUrl: {
|
|
8411
8747
|
type: "string",
|
|
8412
|
-
description: "
|
|
8748
|
+
description: "Return only requests whose URL contains this substring."
|
|
8413
8749
|
},
|
|
8414
8750
|
wake: {
|
|
8415
8751
|
type: "boolean",
|
|
8416
|
-
description: "Worker targets only,
|
|
8752
|
+
description: "Worker targets only, when starting a capture. Default true: an idle-evicted MV3 service worker exists in no target listing, so it is started first (ServiceWorker.startWorker) and re-confirmed; false fails fast. Rejected on a page target, a bare target id, or a read-only call. See server instructions."
|
|
8417
8753
|
}
|
|
8418
8754
|
},
|
|
8419
8755
|
required: [],
|
|
@@ -8422,21 +8758,21 @@ var MANIFEST = [
|
|
|
8422
8758
|
},
|
|
8423
8759
|
{
|
|
8424
8760
|
name: "get_network_request",
|
|
8425
|
-
description: "Return one network request (matched by exact requestId, else
|
|
8761
|
+
description: "Return one network request (matched by exact requestId, else url substring) with status/headers. Requires requestId or url (throws otherwise). includeBody:true drives a fresh capture (page reload, or worker listen window) matched by url ONLY -- a fresh capture re-mints requestIds, so a carried-over requestId returns metadata plus bodyUnavailableReason. WORKER capture (Chrome, Capability 'worker.targets'): a worker: target records an MV3 background service worker's outbound requests from CDP's Network domain on the worker's own session -- what the code ACTUALLY SENT -- so the two tricks callers try first fail for plain JS reasons, not worker ones: a value bound at module top level is not visible from evaluate_script's global scope, and assigning self.fetch cannot rebind a fetch the module already captured. A worker has no reload: reload means 'listen for durationMs while it runs', so trigger its work (tool call, message, alarm) during the window; a worker woken by the call already ran its top-level code, so a startup request can be missed. SIDE EFFECT: the CDP session KEEPS THE WORKER ALIVE for the capture (an idle MV3 worker is evicted in seconds); Chrome resumes evicting after.",
|
|
8426
8762
|
inputSchema: {
|
|
8427
8763
|
type: "object",
|
|
8428
8764
|
properties: {
|
|
8429
8765
|
lease: {
|
|
8430
8766
|
type: "string",
|
|
8431
|
-
description: "
|
|
8767
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8432
8768
|
},
|
|
8433
8769
|
target: {
|
|
8434
8770
|
type: "string",
|
|
8435
|
-
description: "
|
|
8771
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', '<targetId>'; plus 'worker:<substring>' to reach a service/shared worker (e.g. an MV3 extension background worker). The worker: arm is CHROME-ONLY (Capability 'worker.targets'). Grammar: see server instructions."
|
|
8436
8772
|
},
|
|
8437
8773
|
requestId: {
|
|
8438
8774
|
type: "string",
|
|
8439
|
-
description: "Match by exact requestId (metadata only
|
|
8775
|
+
description: "Match by exact requestId (metadata only; cannot fetch a body, since a reload re-mints requestIds)."
|
|
8440
8776
|
},
|
|
8441
8777
|
url: {
|
|
8442
8778
|
type: "string",
|
|
@@ -8444,15 +8780,15 @@ var MANIFEST = [
|
|
|
8444
8780
|
},
|
|
8445
8781
|
includeBody: {
|
|
8446
8782
|
type: "boolean",
|
|
8447
|
-
description: "Also fetch the response body; drives a fresh
|
|
8783
|
+
description: "Also fetch the response body; drives a fresh capture and requires the url selector."
|
|
8448
8784
|
},
|
|
8449
8785
|
durationMs: {
|
|
8450
8786
|
type: "number",
|
|
8451
|
-
description: "Capture window for the
|
|
8787
|
+
description: "Capture window for the body-fetch reload, in ms. Default 2500."
|
|
8452
8788
|
},
|
|
8453
8789
|
wake: {
|
|
8454
8790
|
type: "boolean",
|
|
8455
|
-
description: "Worker targets only,
|
|
8791
|
+
description: "Worker targets only, when starting a capture. Default true: an idle-evicted MV3 service worker exists in no target listing, so it is started first (ServiceWorker.startWorker) and re-confirmed; false fails fast. Rejected on a page target, a bare target id, or a read-only call. See server instructions."
|
|
8456
8792
|
}
|
|
8457
8793
|
},
|
|
8458
8794
|
required: [],
|
|
@@ -8461,24 +8797,24 @@ var MANIFEST = [
|
|
|
8461
8797
|
},
|
|
8462
8798
|
{
|
|
8463
8799
|
name: "performance_start_trace",
|
|
8464
|
-
description: "
|
|
8800
|
+
description: "Starts a CDP trace recording on the target and holds the recording connection in-process; you MUST call performance_stop_trace from the SAME PROCESS to end it, or use performance_trace instead for a robust cross-call trace. Throws if a trace is already in progress for this target.",
|
|
8465
8801
|
inputSchema: {
|
|
8466
8802
|
type: "object",
|
|
8467
8803
|
properties: {
|
|
8468
8804
|
lease: {
|
|
8469
8805
|
type: "string",
|
|
8470
|
-
description: "
|
|
8806
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8471
8807
|
},
|
|
8472
8808
|
target: {
|
|
8473
8809
|
type: "string",
|
|
8474
|
-
description: "
|
|
8810
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
8475
8811
|
},
|
|
8476
8812
|
categories: {
|
|
8477
8813
|
type: "array",
|
|
8478
8814
|
items: {
|
|
8479
8815
|
type: "string"
|
|
8480
8816
|
},
|
|
8481
|
-
description: "Trace categories to
|
|
8817
|
+
description: "Trace categories to record. Defaults to timeline + user-timing + loading + disabled-by-default timeline tracks carrying LCP/LayoutShift/RunTask."
|
|
8482
8818
|
}
|
|
8483
8819
|
},
|
|
8484
8820
|
required: [],
|
|
@@ -8487,17 +8823,17 @@ var MANIFEST = [
|
|
|
8487
8823
|
},
|
|
8488
8824
|
{
|
|
8489
8825
|
name: "performance_stop_trace",
|
|
8490
|
-
description: "
|
|
8826
|
+
description: "Stops the in-process trace started by performance_start_trace and writes it as JSON under /tmp/cdp-toolkit, returning {path,bytes,events,metrics}. Throws if no live trace exists in THIS process (e.g. start ran in a different process) — use performance_trace instead. The target arg is accepted only for API symmetry; at most one trace is ever live per process.",
|
|
8491
8827
|
inputSchema: {
|
|
8492
8828
|
type: "object",
|
|
8493
8829
|
properties: {
|
|
8494
8830
|
lease: {
|
|
8495
8831
|
type: "string",
|
|
8496
|
-
description: "
|
|
8832
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8497
8833
|
},
|
|
8498
8834
|
target: {
|
|
8499
8835
|
type: "string",
|
|
8500
|
-
description: "
|
|
8836
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
8501
8837
|
}
|
|
8502
8838
|
},
|
|
8503
8839
|
required: [],
|
|
@@ -8506,17 +8842,17 @@ var MANIFEST = [
|
|
|
8506
8842
|
},
|
|
8507
8843
|
{
|
|
8508
8844
|
name: "performance_analyze_insight",
|
|
8509
|
-
description: "
|
|
8845
|
+
description: "Reads a trace JSON file (from performance_trace/performance_stop_trace) at tracePath and returns headline metrics: FCP/LCP/CLS/TBT, long tasks, layout shifts. Requires an EXPLICIT tracePath — there is no implicit 'latest trace'. Numbers approximate DevTools's insight analyzer (no main-thread attribution or frame-scoped LCP).",
|
|
8510
8846
|
inputSchema: {
|
|
8511
8847
|
type: "object",
|
|
8512
8848
|
properties: {
|
|
8513
8849
|
lease: {
|
|
8514
8850
|
type: "string",
|
|
8515
|
-
description: "
|
|
8851
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8516
8852
|
},
|
|
8517
8853
|
tracePath: {
|
|
8518
8854
|
type: "string",
|
|
8519
|
-
description: "
|
|
8855
|
+
description: "Path to a trace JSON file (bare array or {traceEvents:[...]}) written by performance_trace or performance_stop_trace. Required."
|
|
8520
8856
|
}
|
|
8521
8857
|
},
|
|
8522
8858
|
required: [],
|
|
@@ -8525,25 +8861,25 @@ var MANIFEST = [
|
|
|
8525
8861
|
},
|
|
8526
8862
|
{
|
|
8527
8863
|
name: "performance_trace",
|
|
8528
|
-
description: "
|
|
8864
|
+
description: "RECOMMENDED entry point: one-shot trace that starts tracing, optionally reloads or navigates the page, waits durationMs (default 3000ms), stops, and writes JSON under /tmp/cdp-toolkit — returns {path,bytes,events,metrics,target}. Holds one connection open for the whole window, so unlike start/stop it is NOT limited to a single process.",
|
|
8529
8865
|
inputSchema: {
|
|
8530
8866
|
type: "object",
|
|
8531
8867
|
properties: {
|
|
8532
8868
|
lease: {
|
|
8533
8869
|
type: "string",
|
|
8534
|
-
description: "
|
|
8870
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8535
8871
|
},
|
|
8536
8872
|
target: {
|
|
8537
8873
|
type: "string",
|
|
8538
|
-
description: "
|
|
8874
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
8539
8875
|
},
|
|
8540
8876
|
durationMs: {
|
|
8541
8877
|
type: "number",
|
|
8542
|
-
description: "How long to record after the optional reload/navigate, in
|
|
8878
|
+
description: "How long to record after the optional reload/navigate, in ms. Default 3000."
|
|
8543
8879
|
},
|
|
8544
8880
|
reload: {
|
|
8545
8881
|
type: "boolean",
|
|
8546
|
-
description: "Reload the page after starting the trace to capture full navigation timing."
|
|
8882
|
+
description: "Reload the page after starting the trace, to capture full navigation timing."
|
|
8547
8883
|
},
|
|
8548
8884
|
navigateTo: {
|
|
8549
8885
|
type: "string",
|
|
@@ -8554,7 +8890,7 @@ var MANIFEST = [
|
|
|
8554
8890
|
items: {
|
|
8555
8891
|
type: "string"
|
|
8556
8892
|
},
|
|
8557
|
-
description: "Trace categories to
|
|
8893
|
+
description: "Trace categories to record. Defaults to timeline + user-timing + loading + disabled-by-default timeline tracks carrying LCP/LayoutShift/RunTask."
|
|
8558
8894
|
}
|
|
8559
8895
|
},
|
|
8560
8896
|
required: [],
|
|
@@ -8563,17 +8899,17 @@ var MANIFEST = [
|
|
|
8563
8899
|
},
|
|
8564
8900
|
{
|
|
8565
8901
|
name: "start_screen_recording",
|
|
8566
|
-
description: "
|
|
8902
|
+
description: "Starts recording the target tab to video via CDP Page.startScreencast, holding one persistent connection and spooling every frame to disk — MUST be paired with stop_screen_recording WITHIN THE SAME PROCESS (frames are connection-scoped and cannot be reattached from another process, so this only works under the MCP server, not the one-process-per-call CLI). Chrome only: WebDriver BiDi has no screencast primitive, so both tools are absent under --browser firefox. Frames arrive ON REPAINT not on a clock (variable rate); each is timestamped into a ledger that stop_screen_recording turns into per-frame display durations, so a still page holds one long frame instead of racing. ffmpeg is probed here (not at stop) so a missing encoder fails before frames are captured and thrown away. Throws if the target already has a recording in progress; different targets record concurrently.",
|
|
8567
8903
|
inputSchema: {
|
|
8568
8904
|
type: "object",
|
|
8569
8905
|
properties: {
|
|
8570
8906
|
lease: {
|
|
8571
8907
|
type: "string",
|
|
8572
|
-
description: "
|
|
8908
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8573
8909
|
},
|
|
8574
8910
|
target: {
|
|
8575
8911
|
type: "string",
|
|
8576
|
-
description: "
|
|
8912
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
8577
8913
|
},
|
|
8578
8914
|
format: {
|
|
8579
8915
|
type: "string",
|
|
@@ -8581,27 +8917,27 @@ var MANIFEST = [
|
|
|
8581
8917
|
"jpeg",
|
|
8582
8918
|
"png"
|
|
8583
8919
|
],
|
|
8584
|
-
description: "Frame
|
|
8920
|
+
description: "Frame format, default jpeg (much smaller per repaint); png is lossless. quality applies to jpeg only."
|
|
8585
8921
|
},
|
|
8586
8922
|
quality: {
|
|
8587
8923
|
type: "number",
|
|
8588
|
-
description: "JPEG frame quality 0-100. Ignored for png
|
|
8924
|
+
description: "JPEG frame quality 0-100. Ignored for png; omitted leaves Chrome's own default."
|
|
8589
8925
|
},
|
|
8590
8926
|
maxWidth: {
|
|
8591
8927
|
type: "number",
|
|
8592
|
-
description: "
|
|
8928
|
+
description: "Caps the streamed frame width in px; also pins frame size if the viewport may change mid-recording."
|
|
8593
8929
|
},
|
|
8594
8930
|
maxHeight: {
|
|
8595
8931
|
type: "number",
|
|
8596
|
-
description: "
|
|
8932
|
+
description: "Caps the streamed frame height in px."
|
|
8597
8933
|
},
|
|
8598
8934
|
everyNthFrame: {
|
|
8599
8935
|
type: "number",
|
|
8600
|
-
description: "Capture only every Nth repaint (1
|
|
8936
|
+
description: "Capture only every Nth repaint (1=every frame), to shrink spool size on a busy page; per-frame durations keep playback at wallclock speed either way."
|
|
8601
8937
|
},
|
|
8602
8938
|
bringToFront: {
|
|
8603
8939
|
type: "boolean",
|
|
8604
|
-
description: "Activate the tab (Page.bringToFront) before recording
|
|
8940
|
+
description: "Activate the tab (Page.bringToFront) before recording, default false. A backgrounded/occluded tab may never repaint, yielding a 0-frame recording."
|
|
8605
8941
|
}
|
|
8606
8942
|
},
|
|
8607
8943
|
required: [],
|
|
@@ -8610,21 +8946,21 @@ var MANIFEST = [
|
|
|
8610
8946
|
},
|
|
8611
8947
|
{
|
|
8612
8948
|
name: "stop_screen_recording",
|
|
8613
|
-
description: "
|
|
8949
|
+
description: "Stops the recording started by start_screen_recording, assembling spooled frames into an MP4 via ffmpeg with PER-FRAME durations (frame N held until N+1 painted; last frame held until this call) — capped at 25fps by ffmpeg's concat demuxer, so frames under 40ms apart are deliberately coalesced (frameCount=captured, encodedFrames=what reached the video, never silently dropped). Returns {path,bytes,durationMs,frameCount,encodedFrames,codec,encoder,width,height,droppedFrames,target}; durationMs sums encoded durations, the file runs one 40ms step longer (final frame repeated). Encoder ladder (probed at start): hevc_videotoolbox>h264_videotoolbox>libx265>libx264 (+hvc1 tag, faststart, yuv420p, even dims). target: omit with one recording in flight, required with several (avoids guessing which to stop). Throws if nothing is recording in this process, or if 0 frames arrived (no silent empty video). On ffmpeg failure the spooled frames are KEPT — spool path + exact command are in the error.",
|
|
8614
8950
|
inputSchema: {
|
|
8615
8951
|
type: "object",
|
|
8616
8952
|
properties: {
|
|
8617
8953
|
lease: {
|
|
8618
8954
|
type: "string",
|
|
8619
|
-
description: "
|
|
8955
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8620
8956
|
},
|
|
8621
8957
|
target: {
|
|
8622
8958
|
type: "string",
|
|
8623
|
-
description: "Which recording to stop
|
|
8959
|
+
description: "Which recording to stop — 'active' valid only when exactly one recording is in flight. Grammar: server instructions."
|
|
8624
8960
|
},
|
|
8625
8961
|
savePath: {
|
|
8626
8962
|
type: "string",
|
|
8627
|
-
description: "Override the output
|
|
8963
|
+
description: "Override the output path (default /tmp/cdp-toolkit/screen-recording-<targetIdShort>-<stamp>.mp4)."
|
|
8628
8964
|
}
|
|
8629
8965
|
},
|
|
8630
8966
|
required: [],
|
|
@@ -8633,21 +8969,21 @@ var MANIFEST = [
|
|
|
8633
8969
|
},
|
|
8634
8970
|
{
|
|
8635
8971
|
name: "take_heapsnapshot",
|
|
8636
|
-
description: "
|
|
8972
|
+
description: "Captures a V8 heap snapshot of the target page over CDP and writes it as a .heapsnapshot JSON file (loadable in the DevTools Memory panel) under /tmp/cdp-toolkit unless savePath is given. Returns {path,bytes,chunks,target} only — does NOT parse or summarize the snapshot.",
|
|
8637
8973
|
inputSchema: {
|
|
8638
8974
|
type: "object",
|
|
8639
8975
|
properties: {
|
|
8640
8976
|
lease: {
|
|
8641
8977
|
type: "string",
|
|
8642
|
-
description: "
|
|
8978
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8643
8979
|
},
|
|
8644
8980
|
target: {
|
|
8645
8981
|
type: "string",
|
|
8646
|
-
description: "
|
|
8982
|
+
description: "Page selector: 'active' (default), 'index:N', 'url:<substr>', 'title:<substr>', 'label:<name>', or a 32-hex '<targetId>'. Grammar: see server instructions."
|
|
8647
8983
|
},
|
|
8648
8984
|
savePath: {
|
|
8649
8985
|
type: "string",
|
|
8650
|
-
description: "Override the output path.
|
|
8986
|
+
description: "Override the output path. Absolute paths (starting with /) are used as-is; relative paths resolve under the artifact dir (/tmp/cdp-toolkit). Defaults to an auto-named take_heapsnapshot-<targetId>-<timestamp>.heapsnapshot file."
|
|
8651
8987
|
}
|
|
8652
8988
|
},
|
|
8653
8989
|
required: [],
|
|
@@ -8656,24 +8992,24 @@ var MANIFEST = [
|
|
|
8656
8992
|
},
|
|
8657
8993
|
{
|
|
8658
8994
|
name: "lighthouse_audit",
|
|
8659
|
-
description: "
|
|
8995
|
+
description: "Runs a Lighthouse audit against a URL by shelling out to `npx --yes lighthouse` (the toolkit's only non-CDP tool) — it attaches to the already-running Chrome and audits its OWN about:blank tab, NOT any live user tab you've navigated. Writes a JSON report under /tmp/cdp-toolkit; returns {path,bytes} plus per-category scores (0..1 or null).",
|
|
8660
8996
|
inputSchema: {
|
|
8661
8997
|
type: "object",
|
|
8662
8998
|
properties: {
|
|
8663
8999
|
lease: {
|
|
8664
9000
|
type: "string",
|
|
8665
|
-
description: "
|
|
9001
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8666
9002
|
},
|
|
8667
9003
|
url: {
|
|
8668
9004
|
type: "string",
|
|
8669
|
-
description: "The URL to audit. Required
|
|
9005
|
+
description: "The URL to audit. Required — never points at a user tab implicitly."
|
|
8670
9006
|
},
|
|
8671
9007
|
categories: {
|
|
8672
9008
|
type: "array",
|
|
8673
9009
|
items: {
|
|
8674
9010
|
type: "string"
|
|
8675
9011
|
},
|
|
8676
|
-
description: 'Lighthouse categories to run (
|
|
9012
|
+
description: 'Lighthouse categories to run (--only-categories). Defaults to the full set, e.g. ["performance"] or ["performance","accessibility","seo"].'
|
|
8677
9013
|
},
|
|
8678
9014
|
savePath: {
|
|
8679
9015
|
type: "string",
|
|
@@ -8685,11 +9021,11 @@ var MANIFEST = [
|
|
|
8685
9021
|
"desktop",
|
|
8686
9022
|
"mobile"
|
|
8687
9023
|
],
|
|
8688
|
-
description: '
|
|
9024
|
+
description: '"desktop" (default, uses --preset=desktop to avoid heavy mobile throttling) or "mobile".'
|
|
8689
9025
|
},
|
|
8690
9026
|
timeoutMs: {
|
|
8691
9027
|
type: "number",
|
|
8692
|
-
description: "Overall budget for the lighthouse process in
|
|
9028
|
+
description: "Overall budget for the lighthouse process, in ms. Default 120000."
|
|
8693
9029
|
}
|
|
8694
9030
|
},
|
|
8695
9031
|
required: [
|
|
@@ -8700,21 +9036,21 @@ var MANIFEST = [
|
|
|
8700
9036
|
},
|
|
8701
9037
|
{
|
|
8702
9038
|
name: "mock_request",
|
|
8703
|
-
description: "Register a mock rule on a target's persistent fake-backend session (CDP Fetch domain):
|
|
9039
|
+
description: "Register a mock rule on a target's persistent fake-backend session (CDP Fetch domain): match requests by urlPattern and fulfill with a canned response, fail (abort), or continue -- optionally with fault injection (delayMs/failRate). The session survives reloads/navigations and lives until clear_mocks; call repeatedly to mock several endpoints on one target. reload:true applies it immediately. Persistent across calls only via the MCP server, not the one-shot CLI.",
|
|
8704
9040
|
inputSchema: {
|
|
8705
9041
|
type: "object",
|
|
8706
9042
|
properties: {
|
|
8707
9043
|
lease: {
|
|
8708
9044
|
type: "string",
|
|
8709
|
-
description: "
|
|
9045
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8710
9046
|
},
|
|
8711
9047
|
target: {
|
|
8712
9048
|
type: "string",
|
|
8713
|
-
description: "
|
|
9049
|
+
description: "Which page's mock session. Defaults to the active page. Grammar: server instructions."
|
|
8714
9050
|
},
|
|
8715
9051
|
urlPattern: {
|
|
8716
9052
|
type: "string",
|
|
8717
|
-
description: `CDP Fetch urlPattern glob: '*'
|
|
9053
|
+
description: `CDP Fetch urlPattern glob: '*' any run of chars, '?' exactly one, '\\' escapes. Only matching URLs are paused. e.g. "*/api/users*".`
|
|
8718
9054
|
},
|
|
8719
9055
|
action: {
|
|
8720
9056
|
type: "string",
|
|
@@ -8727,7 +9063,7 @@ var MANIFEST = [
|
|
|
8727
9063
|
},
|
|
8728
9064
|
status: {
|
|
8729
9065
|
type: "number",
|
|
8730
|
-
description: "fulfill: HTTP status
|
|
9066
|
+
description: "fulfill: HTTP status for the canned response. Default 200."
|
|
8731
9067
|
},
|
|
8732
9068
|
body: {
|
|
8733
9069
|
type: "string",
|
|
@@ -8747,7 +9083,7 @@ var MANIFEST = [
|
|
|
8747
9083
|
},
|
|
8748
9084
|
method: {
|
|
8749
9085
|
type: "string",
|
|
8750
|
-
description: 'Only mock
|
|
9086
|
+
description: 'Only mock this HTTP method (e.g. "POST"); others pass through.'
|
|
8751
9087
|
},
|
|
8752
9088
|
delayMs: {
|
|
8753
9089
|
type: "number",
|
|
@@ -8755,11 +9091,11 @@ var MANIFEST = [
|
|
|
8755
9091
|
},
|
|
8756
9092
|
failRate: {
|
|
8757
9093
|
type: "number",
|
|
8758
|
-
description: "Fault injection: probability 0..1 of failing a matched request regardless of action
|
|
9094
|
+
description: "Fault injection: probability 0..1 of failing a matched request regardless of action."
|
|
8759
9095
|
},
|
|
8760
9096
|
reload: {
|
|
8761
9097
|
type: "boolean",
|
|
8762
|
-
description: "Reload the target after arming so the mock
|
|
9098
|
+
description: "Reload the target after arming so the mock catches traffic immediately."
|
|
8763
9099
|
}
|
|
8764
9100
|
},
|
|
8765
9101
|
required: [
|
|
@@ -8770,17 +9106,17 @@ var MANIFEST = [
|
|
|
8770
9106
|
},
|
|
8771
9107
|
{
|
|
8772
9108
|
name: "list_mocks",
|
|
8773
|
-
description: "List active fake-backend sessions (one per target) with their rules and hit counts. Prunes sessions whose tab has closed.
|
|
9109
|
+
description: "List active fake-backend sessions (one per target) with their rules and hit counts. Prunes sessions whose tab has closed.",
|
|
8774
9110
|
inputSchema: {
|
|
8775
9111
|
type: "object",
|
|
8776
9112
|
properties: {
|
|
8777
9113
|
lease: {
|
|
8778
9114
|
type: "string",
|
|
8779
|
-
description: "
|
|
9115
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8780
9116
|
},
|
|
8781
9117
|
target: {
|
|
8782
9118
|
type: "string",
|
|
8783
|
-
description: "Unused filter placeholder;
|
|
9119
|
+
description: "Unused filter placeholder; returns all active sessions."
|
|
8784
9120
|
}
|
|
8785
9121
|
},
|
|
8786
9122
|
required: [],
|
|
@@ -8795,11 +9131,11 @@ var MANIFEST = [
|
|
|
8795
9131
|
properties: {
|
|
8796
9132
|
lease: {
|
|
8797
9133
|
type: "string",
|
|
8798
|
-
description: "
|
|
9134
|
+
description: "Lease token from claim_page. Omit for a tab this process holds; required for a tab held by another process or one claimed explicitly. Auto-acquired under CDP_REQUIRE_LEASE (then pass target, not lease). See server instructions."
|
|
8799
9135
|
},
|
|
8800
9136
|
target: {
|
|
8801
9137
|
type: "string",
|
|
8802
|
-
description: "
|
|
9138
|
+
description: "Which page's mock session to clear. Defaults to the active page; ignored when all:true. Grammar: server instructions."
|
|
8803
9139
|
},
|
|
8804
9140
|
all: {
|
|
8805
9141
|
type: "boolean",
|
|
@@ -8812,29 +9148,29 @@ var MANIFEST = [
|
|
|
8812
9148
|
},
|
|
8813
9149
|
{
|
|
8814
9150
|
name: "claim_page",
|
|
8815
|
-
description: "Take exclusive ownership of a browser tab
|
|
9151
|
+
description: "Take exclusive ownership of a browser tab, returning an opaque lease token (needed once under CDP_REQUIRE_LEASE for any tab you drive). Two modes: FRESH TAB (no target - opens one, optionally at url, and claims it: 'give me my own tab' in one call) and TAKEOVER (target - claims an already-open tab, e.g. the human's, resolving the selector against the live page list, creating nothing). The opened flag is true only when this call created the tab, and release_page consults that record, so a taken-over tab is left open on release. Refused from the CLI (its per-call process would make the lease instantly reclaimable). The answer carries humanActiveMs, ms since input this server did not dispatch; null is NO DATA (no activity beacon, or every input was this server's own) and never means 'no human'; absent on a backend that cannot answer. On takeover of a tab a human used within 30s it adds contention, a warning - but THE CLAIM IS NEVER REFUSED FOR THIS: takeover is its purpose, you already hold the lease; it only warns you're fighting a live person, so open your own tab or ask first. Limits: input inside a cross-origin iframe is invisible, and a second MCP server's clicks read as human.",
|
|
8816
9152
|
inputSchema: {
|
|
8817
9153
|
type: "object",
|
|
8818
9154
|
properties: {
|
|
8819
9155
|
target: {
|
|
8820
9156
|
type: "string",
|
|
8821
|
-
description: "Take over an already-open tab
|
|
9157
|
+
description: "Take over an already-open tab (e.g. one the human has open). Resolved against the LIVE page list only, so it never opens a tab — a selector matching nothing is an error. Left open on release. Refused if another live process holds it (including a lease auto-acquired under CDP_REQUIRE_LEASE): takes over unleased human tabs, never steals from a live agent. Mutually exclusive with targetId. Selector grammar: server instructions."
|
|
8822
9158
|
},
|
|
8823
9159
|
targetId: {
|
|
8824
9160
|
type: "string",
|
|
8825
|
-
description: "
|
|
9161
|
+
description: "Exact target id (not a selector) of an already-open page to claim instead of opening one. Back-compat only; prefer target, which also accepts an exact id. Mutually exclusive with target."
|
|
8826
9162
|
},
|
|
8827
9163
|
url: {
|
|
8828
9164
|
type: "string",
|
|
8829
|
-
description: "When opening a fresh tab, navigate it here. Ignored when target or targetId is given
|
|
9165
|
+
description: "When opening a fresh tab, navigate it here. Ignored when target or targetId is given."
|
|
8830
9166
|
},
|
|
8831
9167
|
label: {
|
|
8832
9168
|
type: "string",
|
|
8833
|
-
description: "Agent label recorded on the lease
|
|
9169
|
+
description: "Agent label recorded on the lease; surfaced in conflict errors and list_leases. Defaults to pid-<pid>."
|
|
8834
9170
|
},
|
|
8835
9171
|
ttlMs: {
|
|
8836
9172
|
type: "number",
|
|
8837
|
-
description: "How long the lease survives without use before it is reclaimable. Defaults to CDP_LEASE_TTL_MS, else 900000 (
|
|
9173
|
+
description: "How long the lease survives without use before it is reclaimable. Defaults to CDP_LEASE_TTL_MS, else 900000 (15min); every checked call refreshes it."
|
|
8838
9174
|
}
|
|
8839
9175
|
},
|
|
8840
9176
|
required: [],
|
|
@@ -8843,21 +9179,21 @@ var MANIFEST = [
|
|
|
8843
9179
|
},
|
|
8844
9180
|
{
|
|
8845
9181
|
name: "release_page",
|
|
8846
|
-
description: "Give a lease back,
|
|
9182
|
+
description: "Give a lease back, closing the tab only if this toolkit opened it. Take exactly one of lease (the token) or target (a selector for a tab this process holds - use this to release a lease the gate acquired for you automatically, since that path never handed you a token). A tab with a creation record from this toolkit is closed; a tab that was already open and merely claimed is released and left alone - override either way with close. Idempotent: an already-released, reclaimed, or expired lease reports released:false and closes nothing (by then the tab may belong to another agent). Answers {released, closed, targetId}.",
|
|
8847
9183
|
inputSchema: {
|
|
8848
9184
|
type: "object",
|
|
8849
9185
|
properties: {
|
|
8850
9186
|
lease: {
|
|
8851
9187
|
type: "string",
|
|
8852
|
-
description: "
|
|
9188
|
+
description: "Token from claim_page / new_page{claim:true}. Mutually exclusive with 'target'. Under CDP_REQUIRE_LEASE an auto-acquired lease never produced a token — give 'target' instead."
|
|
8853
9189
|
},
|
|
8854
9190
|
target: {
|
|
8855
9191
|
type: "string",
|
|
8856
|
-
description: "
|
|
9192
|
+
description: "A tab THIS process holds (grammar: server instructions). Mutually exclusive with 'lease'. Refused if another process holds the tab."
|
|
8857
9193
|
},
|
|
8858
9194
|
close: {
|
|
8859
9195
|
type: "boolean",
|
|
8860
|
-
description: "Force the close decision instead of letting the creation
|
|
9196
|
+
description: "Force the close decision instead of letting the creation record decide: true closes even a tab this toolkit did not open; false keeps an agent-created tab open. Omit to close only tabs this toolkit created."
|
|
8861
9197
|
}
|
|
8862
9198
|
},
|
|
8863
9199
|
required: [],
|
|
@@ -8866,7 +9202,7 @@ var MANIFEST = [
|
|
|
8866
9202
|
},
|
|
8867
9203
|
{
|
|
8868
9204
|
name: "list_leases",
|
|
8869
|
-
description: "Enumerate every active lease for diagnosis
|
|
9205
|
+
description: "Enumerate every active lease for diagnosis - backend, target id, label, pid, createdAt, lastUsedAt, ttlMs, whether the owning process is alive, and whether the lease is reclaimable and why. No token required; it never returns the lease nonce, so it cannot be used to forge one. An unreadable or unparseable lease row instead carries unreadable, with label, pid and timestamps zeroed and stale forced false (an unreadable lease must never read as free to take). Under CDP_REQUIRE_LEASE it reaps abandoned agent tabs exactly as list_pages does, into an additive reaped array, omitting their rows. Where the tab can answer, a row also carries humanActiveMs, ms since input this server did not dispatch - note lastUsedAt tracks TOOLKIT calls only, so a tab a person has typed in for ten minutes looks idle, which is what this exposes; the field is absent, never null, when the tab cannot answer, and that absence means 'no answer', never 'nobody is there'. idleMs (now - lastUsedAt) and expiresAt (lastUsedAt + ttlMs) are computed fresh each call and omitted on an unreadable row.",
|
|
8870
9206
|
inputSchema: {
|
|
8871
9207
|
type: "object",
|
|
8872
9208
|
properties: {},
|
|
@@ -8876,6 +9212,566 @@ var MANIFEST = [
|
|
|
8876
9212
|
}
|
|
8877
9213
|
];
|
|
8878
9214
|
|
|
9215
|
+
// src/install/wizard.ts
|
|
9216
|
+
import { createInterface } from "node:readline";
|
|
9217
|
+
import { homedir as homedir3 } from "node:os";
|
|
9218
|
+
import { join as join14 } from "node:path";
|
|
9219
|
+
|
|
9220
|
+
// src/install/browser.ts
|
|
9221
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
9222
|
+
import { mkdir as mkdir13, readFile as readFile9, writeFile as writeFile10 } from "node:fs/promises";
|
|
9223
|
+
import { homedir } from "node:os";
|
|
9224
|
+
import { delimiter, dirname as dirname3, join as join12 } from "node:path";
|
|
9225
|
+
var CANDIDATE_BUILDERS = {
|
|
9226
|
+
chrome: () => [
|
|
9227
|
+
process.env.CHROME_BIN,
|
|
9228
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
9229
|
+
"google-chrome",
|
|
9230
|
+
"google-chrome-stable",
|
|
9231
|
+
"chromium"
|
|
9232
|
+
],
|
|
9233
|
+
arc: () => [process.env.ARC_BIN, "/Applications/Arc.app/Contents/MacOS/Arc", "arc"],
|
|
9234
|
+
firefox: () => [
|
|
9235
|
+
process.env.FIREFOX_BIN,
|
|
9236
|
+
"/Applications/Firefox.app/Contents/MacOS/firefox",
|
|
9237
|
+
"firefox",
|
|
9238
|
+
"/usr/bin/firefox"
|
|
9239
|
+
]
|
|
9240
|
+
};
|
|
9241
|
+
function existsOnPath(cmd) {
|
|
9242
|
+
const pathEnv = process.env.PATH;
|
|
9243
|
+
if (!pathEnv)
|
|
9244
|
+
return false;
|
|
9245
|
+
for (const dir of pathEnv.split(delimiter)) {
|
|
9246
|
+
if (!dir)
|
|
9247
|
+
continue;
|
|
9248
|
+
if (existsSync2(join12(dir, cmd)))
|
|
9249
|
+
return true;
|
|
9250
|
+
}
|
|
9251
|
+
return false;
|
|
9252
|
+
}
|
|
9253
|
+
function resolveCandidate(candidates) {
|
|
9254
|
+
let lastDefined = "";
|
|
9255
|
+
for (const c of candidates) {
|
|
9256
|
+
if (!c)
|
|
9257
|
+
continue;
|
|
9258
|
+
lastDefined = c;
|
|
9259
|
+
if (c.startsWith("/")) {
|
|
9260
|
+
if (existsSync2(c))
|
|
9261
|
+
return { binary: c, found: true };
|
|
9262
|
+
continue;
|
|
9263
|
+
}
|
|
9264
|
+
if (existsOnPath(c))
|
|
9265
|
+
return { binary: c, found: true };
|
|
9266
|
+
}
|
|
9267
|
+
return { binary: lastDefined, found: false };
|
|
9268
|
+
}
|
|
9269
|
+
function detectBrowser(choice) {
|
|
9270
|
+
const { binary, found } = resolveCandidate(CANDIDATE_BUILDERS[choice]());
|
|
9271
|
+
return { choice, binary, found };
|
|
9272
|
+
}
|
|
9273
|
+
function defaultProfileDir(choice) {
|
|
9274
|
+
return `$HOME/.cdp-toolkit/${choice}-profile`;
|
|
9275
|
+
}
|
|
9276
|
+
function defaultAliasName(choice) {
|
|
9277
|
+
return `cdp-${choice}`;
|
|
9278
|
+
}
|
|
9279
|
+
function browserLaunchArgs(o) {
|
|
9280
|
+
const profileDir = o.profileDir ?? defaultProfileDir(o.choice);
|
|
9281
|
+
if (o.choice === "firefox") {
|
|
9282
|
+
return ["--remote-debugging-port", String(o.port), "--marionette", "--no-remote", "--profile", profileDir];
|
|
9283
|
+
}
|
|
9284
|
+
return [`--remote-debugging-port=${o.port}`, `--user-data-dir=${profileDir}`];
|
|
9285
|
+
}
|
|
9286
|
+
function shellQuoteIfSpaced(token) {
|
|
9287
|
+
return /\s/.test(token) ? `"${token}"` : token;
|
|
9288
|
+
}
|
|
9289
|
+
function aliasLine(o) {
|
|
9290
|
+
const name = o.aliasName ?? defaultAliasName(o.choice);
|
|
9291
|
+
const { binary } = detectBrowser(o.choice);
|
|
9292
|
+
const parts = [shellQuoteIfSpaced(binary), ...browserLaunchArgs(o)];
|
|
9293
|
+
return `alias ${name}='${parts.join(" ")}'`;
|
|
9294
|
+
}
|
|
9295
|
+
function detectShellRc() {
|
|
9296
|
+
const shell = process.env.SHELL ?? "";
|
|
9297
|
+
if (shell.endsWith("zsh"))
|
|
9298
|
+
return join12(homedir(), ".zshrc");
|
|
9299
|
+
if (shell.endsWith("bash"))
|
|
9300
|
+
return join12(homedir(), ".bashrc");
|
|
9301
|
+
return join12(homedir(), process.platform === "darwin" ? ".zshrc" : ".bashrc");
|
|
9302
|
+
}
|
|
9303
|
+
var MARKER_START = "# >>> cdp-toolkit alias >>>";
|
|
9304
|
+
var MARKER_END = "# <<< cdp-toolkit alias <<<";
|
|
9305
|
+
function ensureTrailingNewline(s) {
|
|
9306
|
+
return s.endsWith(`
|
|
9307
|
+
`) ? s : `${s}
|
|
9308
|
+
`;
|
|
9309
|
+
}
|
|
9310
|
+
async function appendAliasToRc(rcPath, aliasBlockBody) {
|
|
9311
|
+
let existing = "";
|
|
9312
|
+
try {
|
|
9313
|
+
existing = await readFile9(rcPath, "utf8");
|
|
9314
|
+
} catch (err) {
|
|
9315
|
+
if (err.code !== "ENOENT")
|
|
9316
|
+
throw err;
|
|
9317
|
+
}
|
|
9318
|
+
const block = `${MARKER_START}
|
|
9319
|
+
${aliasBlockBody}
|
|
9320
|
+
${MARKER_END}`;
|
|
9321
|
+
const startIdx = existing.indexOf(MARKER_START);
|
|
9322
|
+
const endIdx = existing.indexOf(MARKER_END);
|
|
9323
|
+
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
9324
|
+
const endOfEndLine = existing.indexOf(`
|
|
9325
|
+
`, endIdx);
|
|
9326
|
+
const before = existing.slice(0, startIdx);
|
|
9327
|
+
const after = endOfEndLine === -1 ? "" : existing.slice(endOfEndLine + 1);
|
|
9328
|
+
const existingBlock = existing.slice(startIdx, endOfEndLine === -1 ? existing.length : endOfEndLine);
|
|
9329
|
+
if (existingBlock === block) {
|
|
9330
|
+
return { rcPath, action: "unchanged" };
|
|
9331
|
+
}
|
|
9332
|
+
const next2 = ensureTrailingNewline(`${before}${block}
|
|
9333
|
+
${after}`);
|
|
9334
|
+
await writeFile10(rcPath, next2, "utf8");
|
|
9335
|
+
return { rcPath, action: "replaced-block" };
|
|
9336
|
+
}
|
|
9337
|
+
await mkdir13(dirname3(rcPath), { recursive: true });
|
|
9338
|
+
const sep = existing.length === 0 || existing.endsWith(`
|
|
9339
|
+
`) ? "" : `
|
|
9340
|
+
`;
|
|
9341
|
+
const next = `${existing}${sep}${block}
|
|
9342
|
+
`;
|
|
9343
|
+
await writeFile10(rcPath, next, "utf8");
|
|
9344
|
+
return { rcPath, action: "created-block" };
|
|
9345
|
+
}
|
|
9346
|
+
|
|
9347
|
+
// src/install/harness.ts
|
|
9348
|
+
import { spawnSync } from "node:child_process";
|
|
9349
|
+
import { mkdir as mkdir14, readFile as readFile10, writeFile as writeFile11 } from "node:fs/promises";
|
|
9350
|
+
import { homedir as homedir2 } from "node:os";
|
|
9351
|
+
import { dirname as dirname4, join as join13 } from "node:path";
|
|
9352
|
+
async function installMcpServer(harness, name, cfg, opts = {}) {
|
|
9353
|
+
switch (harness) {
|
|
9354
|
+
case "claude":
|
|
9355
|
+
return installClaude(name, cfg, opts);
|
|
9356
|
+
case "codex":
|
|
9357
|
+
return installCodex(name, cfg);
|
|
9358
|
+
case "opencode":
|
|
9359
|
+
return installOpencode(name, cfg);
|
|
9360
|
+
default: {
|
|
9361
|
+
const exhaustive = harness;
|
|
9362
|
+
throw new Error(`unknown harness: ${String(exhaustive)}`);
|
|
9363
|
+
}
|
|
9364
|
+
}
|
|
9365
|
+
}
|
|
9366
|
+
async function installClaude(name, cfg, opts) {
|
|
9367
|
+
const path = opts.configPath ?? join13(opts.homeDir ?? homedir2(), ".claude.json");
|
|
9368
|
+
let root;
|
|
9369
|
+
let fileExisted;
|
|
9370
|
+
try {
|
|
9371
|
+
const text = await readFile10(path, "utf8");
|
|
9372
|
+
const parsed = JSON.parse(text);
|
|
9373
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
9374
|
+
throw new Error(`${path} does not contain a JSON object at its top level`);
|
|
9375
|
+
}
|
|
9376
|
+
root = parsed;
|
|
9377
|
+
fileExisted = true;
|
|
9378
|
+
} catch (err) {
|
|
9379
|
+
if (err.code === "ENOENT") {
|
|
9380
|
+
root = {};
|
|
9381
|
+
fileExisted = false;
|
|
9382
|
+
} else {
|
|
9383
|
+
throw err;
|
|
9384
|
+
}
|
|
9385
|
+
}
|
|
9386
|
+
const rawServers = root.mcpServers;
|
|
9387
|
+
const mcpServers = typeof rawServers === "object" && rawServers !== null && !Array.isArray(rawServers) ? rawServers : {};
|
|
9388
|
+
root.mcpServers = mcpServers;
|
|
9389
|
+
const entry = { command: cfg.command, args: cfg.args };
|
|
9390
|
+
if (Object.keys(cfg.env).length > 0)
|
|
9391
|
+
entry.env = cfg.env;
|
|
9392
|
+
const prior = mcpServers[name];
|
|
9393
|
+
const action = prior === undefined ? "added" : canonicalJson(prior) === canonicalJson(entry) ? "unchanged" : "updated";
|
|
9394
|
+
mcpServers[name] = entry;
|
|
9395
|
+
if (action === "unchanged") {
|
|
9396
|
+
return { harness: "claude", method: "file", path, action, detail: "no change needed" };
|
|
9397
|
+
}
|
|
9398
|
+
await mkdir14(dirname4(path), { recursive: true });
|
|
9399
|
+
await writeFile11(path, JSON.stringify(root, null, 2) + `
|
|
9400
|
+
`, "utf8");
|
|
9401
|
+
return {
|
|
9402
|
+
harness: "claude",
|
|
9403
|
+
method: "file",
|
|
9404
|
+
path,
|
|
9405
|
+
action,
|
|
9406
|
+
detail: fileExisted ? undefined : "created new config file"
|
|
9407
|
+
};
|
|
9408
|
+
}
|
|
9409
|
+
function canonicalJson(value) {
|
|
9410
|
+
return JSON.stringify(canonicalize(value));
|
|
9411
|
+
}
|
|
9412
|
+
function canonicalize(value) {
|
|
9413
|
+
if (Array.isArray(value))
|
|
9414
|
+
return value.map(canonicalize);
|
|
9415
|
+
if (value !== null && typeof value === "object") {
|
|
9416
|
+
const out = {};
|
|
9417
|
+
for (const key of Object.keys(value).sort()) {
|
|
9418
|
+
out[key] = canonicalize(value[key]);
|
|
9419
|
+
}
|
|
9420
|
+
return out;
|
|
9421
|
+
}
|
|
9422
|
+
return value;
|
|
9423
|
+
}
|
|
9424
|
+
function codexAddArgs(name, cfg) {
|
|
9425
|
+
const envArgs = Object.entries(cfg.env).flatMap(([k, v]) => ["--env", `${k}=${v}`]);
|
|
9426
|
+
return ["mcp", "add", name, ...envArgs, "--", cfg.command, ...cfg.args];
|
|
9427
|
+
}
|
|
9428
|
+
async function installCodex(name, cfg) {
|
|
9429
|
+
if (!isOnPath("codex")) {
|
|
9430
|
+
throw new Error("codex CLI not found on PATH — install Codex or pick a different harness");
|
|
9431
|
+
}
|
|
9432
|
+
const probe = spawnSync("codex", ["mcp", "get", name], { encoding: "utf8" });
|
|
9433
|
+
const exists = probe.status === 0;
|
|
9434
|
+
if (exists) {
|
|
9435
|
+
const removed = spawnSync("codex", ["mcp", "remove", name], { encoding: "utf8" });
|
|
9436
|
+
if (removed.status !== 0) {
|
|
9437
|
+
throw new Error(`codex mcp remove ${name} failed: ${(removed.stderr || removed.stdout || "").trim()}`);
|
|
9438
|
+
}
|
|
9439
|
+
}
|
|
9440
|
+
const addArgs = codexAddArgs(name, cfg);
|
|
9441
|
+
const added = spawnSync("codex", addArgs, { encoding: "utf8" });
|
|
9442
|
+
if (added.status !== 0) {
|
|
9443
|
+
throw new Error(`codex ${addArgs.join(" ")} failed: ${(added.stderr || added.stdout || "").trim()}`);
|
|
9444
|
+
}
|
|
9445
|
+
return {
|
|
9446
|
+
harness: "codex",
|
|
9447
|
+
method: "cli",
|
|
9448
|
+
action: exists ? "updated" : "added",
|
|
9449
|
+
detail: added.stdout?.trim() || undefined
|
|
9450
|
+
};
|
|
9451
|
+
}
|
|
9452
|
+
function opencodeAddArgs(name, cfg) {
|
|
9453
|
+
const envArgs = Object.entries(cfg.env).flatMap(([k, v]) => ["--env", `${k}=${v}`]);
|
|
9454
|
+
return ["mcp", "add", name, ...envArgs, "--", cfg.command, ...cfg.args];
|
|
9455
|
+
}
|
|
9456
|
+
async function installOpencode(name, cfg) {
|
|
9457
|
+
if (!isOnPath("opencode")) {
|
|
9458
|
+
throw new Error("opencode CLI not found on PATH — install OpenCode or pick a different harness");
|
|
9459
|
+
}
|
|
9460
|
+
const addArgs = opencodeAddArgs(name, cfg);
|
|
9461
|
+
const added = spawnSync("opencode", addArgs, { encoding: "utf8" });
|
|
9462
|
+
if (added.status !== 0) {
|
|
9463
|
+
throw new Error(`opencode ${addArgs.join(" ")} failed: ${(added.stderr || added.stdout || "").trim()}`);
|
|
9464
|
+
}
|
|
9465
|
+
return {
|
|
9466
|
+
harness: "opencode",
|
|
9467
|
+
method: "cli",
|
|
9468
|
+
action: "added",
|
|
9469
|
+
detail: added.stdout?.trim() || undefined
|
|
9470
|
+
};
|
|
9471
|
+
}
|
|
9472
|
+
function isOnPath(bin) {
|
|
9473
|
+
const finder = process.platform === "win32" ? "where" : "which";
|
|
9474
|
+
const result = spawnSync(finder, [bin], { encoding: "utf8" });
|
|
9475
|
+
return result.status === 0;
|
|
9476
|
+
}
|
|
9477
|
+
|
|
9478
|
+
// src/install/wizard.ts
|
|
9479
|
+
var DEFAULT_NAME = "cdp-toolkit";
|
|
9480
|
+
var HARNESSES = ["claude", "codex", "opencode"];
|
|
9481
|
+
var BROWSERS = ["arc", "chrome", "firefox"];
|
|
9482
|
+
function defaultPortFor(browser) {
|
|
9483
|
+
return browser === "firefox" ? 9223 : 9222;
|
|
9484
|
+
}
|
|
9485
|
+
function parseHarness(v) {
|
|
9486
|
+
if (v === undefined)
|
|
9487
|
+
throw new Error("--harness requires a value: claude, codex, or opencode");
|
|
9488
|
+
if (HARNESSES.includes(v))
|
|
9489
|
+
return v;
|
|
9490
|
+
throw new Error(`--harness must be one of claude|codex|opencode (got '${v}')`);
|
|
9491
|
+
}
|
|
9492
|
+
function parseBrowserChoice(v) {
|
|
9493
|
+
if (v === undefined)
|
|
9494
|
+
throw new Error("--browser requires a value: arc, chrome, or firefox");
|
|
9495
|
+
if (BROWSERS.includes(v))
|
|
9496
|
+
return v;
|
|
9497
|
+
throw new Error(`--browser must be one of arc|chrome|firefox (got '${v}')`);
|
|
9498
|
+
}
|
|
9499
|
+
function parsePort(v) {
|
|
9500
|
+
if (v === undefined)
|
|
9501
|
+
throw new Error("--port requires a value (an integer 1-65535)");
|
|
9502
|
+
const n = Number(v);
|
|
9503
|
+
if (!Number.isInteger(n) || n < 1 || n > 65535) {
|
|
9504
|
+
throw new Error(`--port must be an integer 1-65535 (got '${v}')`);
|
|
9505
|
+
}
|
|
9506
|
+
return n;
|
|
9507
|
+
}
|
|
9508
|
+
function normalizeArgv(argv) {
|
|
9509
|
+
const out = [];
|
|
9510
|
+
for (const t of argv) {
|
|
9511
|
+
if (t.startsWith("--") && t.includes("=")) {
|
|
9512
|
+
const eq = t.indexOf("=");
|
|
9513
|
+
out.push(t.slice(0, eq), t.slice(eq + 1));
|
|
9514
|
+
} else {
|
|
9515
|
+
out.push(t);
|
|
9516
|
+
}
|
|
9517
|
+
}
|
|
9518
|
+
return out;
|
|
9519
|
+
}
|
|
9520
|
+
function parseInstallArgs(argv) {
|
|
9521
|
+
const tokens = normalizeArgv(argv);
|
|
9522
|
+
const out = { name: DEFAULT_NAME, alias: true };
|
|
9523
|
+
for (let i = 0;i < tokens.length; i++) {
|
|
9524
|
+
const t = tokens[i];
|
|
9525
|
+
switch (t) {
|
|
9526
|
+
case "--help":
|
|
9527
|
+
case "-h":
|
|
9528
|
+
out.help = true;
|
|
9529
|
+
break;
|
|
9530
|
+
case "--yes":
|
|
9531
|
+
case "-y":
|
|
9532
|
+
out.yes = true;
|
|
9533
|
+
break;
|
|
9534
|
+
case "--no-alias":
|
|
9535
|
+
out.alias = false;
|
|
9536
|
+
break;
|
|
9537
|
+
case "--alias":
|
|
9538
|
+
out.alias = true;
|
|
9539
|
+
break;
|
|
9540
|
+
case "--harness":
|
|
9541
|
+
out.harness = parseHarness(tokens[++i]);
|
|
9542
|
+
break;
|
|
9543
|
+
case "--browser":
|
|
9544
|
+
out.browser = parseBrowserChoice(tokens[++i]);
|
|
9545
|
+
break;
|
|
9546
|
+
case "--port":
|
|
9547
|
+
out.port = parsePort(tokens[++i]);
|
|
9548
|
+
break;
|
|
9549
|
+
case "--name": {
|
|
9550
|
+
const v = tokens[++i];
|
|
9551
|
+
if (v === undefined)
|
|
9552
|
+
throw new Error("--name requires a value");
|
|
9553
|
+
out.name = v;
|
|
9554
|
+
break;
|
|
9555
|
+
}
|
|
9556
|
+
case "--alias-name": {
|
|
9557
|
+
const v = tokens[++i];
|
|
9558
|
+
if (v === undefined)
|
|
9559
|
+
throw new Error("--alias-name requires a value");
|
|
9560
|
+
out.aliasName = v;
|
|
9561
|
+
break;
|
|
9562
|
+
}
|
|
9563
|
+
default:
|
|
9564
|
+
throw new Error(`cdp install: unknown option '${t}' (run \`cdp install --help\`)`);
|
|
9565
|
+
}
|
|
9566
|
+
}
|
|
9567
|
+
return out;
|
|
9568
|
+
}
|
|
9569
|
+
function composeServerConfig(browser, port) {
|
|
9570
|
+
const env = browser === "firefox" ? { CDP_BROWSER: "firefox", CDP_FIREFOX_ENDPOINT: String(port) } : { CDP_BASE: `http://127.0.0.1:${port}` };
|
|
9571
|
+
return { command: "npx", args: ["-y", "cdp-toolkit"], env };
|
|
9572
|
+
}
|
|
9573
|
+
function installHelpText() {
|
|
9574
|
+
return `cdp install — register the cdp-toolkit MCP server with your coding harness and add a
|
|
9575
|
+
browser-launch alias (with the remote-debugging port open) to your shell rc.
|
|
9576
|
+
|
|
9577
|
+
Usage:
|
|
9578
|
+
cdp install [--harness <claude|codex|opencode>] [--browser <arc|chrome|firefox>]
|
|
9579
|
+
[--port <n>] [--name <server-name>] [--alias-name <name>]
|
|
9580
|
+
[--no-alias] [--yes|-y] [--help|-h]
|
|
9581
|
+
|
|
9582
|
+
Options:
|
|
9583
|
+
--harness <h> Coding harness to register the MCP server with: claude, codex, opencode.
|
|
9584
|
+
--browser <b> Browser the alias launches: arc, chrome, firefox.
|
|
9585
|
+
--port <n> Remote-debugging port (1-65535). Default: 9222 (chrome/arc), 9223 (firefox).
|
|
9586
|
+
--name <name> MCP server name to register. Default: ${DEFAULT_NAME}.
|
|
9587
|
+
--alias-name <n> Shell alias name. Default: cdp-<browser> (e.g. cdp-firefox).
|
|
9588
|
+
--no-alias Register the MCP server only; skip writing the shell alias.
|
|
9589
|
+
--yes, -y Skip the confirmation prompt (proceed immediately).
|
|
9590
|
+
--help, -h Show this help.
|
|
9591
|
+
|
|
9592
|
+
Interactive: run \`cdp install\` in a terminal with no flags and it prompts for harness, browser,
|
|
9593
|
+
and port, then asks before writing anything.
|
|
9594
|
+
|
|
9595
|
+
Non-interactive (piped/CI): pass at least --harness and --browser (port defaults per browser); a
|
|
9596
|
+
missing required value fails fast with usage instead of hanging on stdin.`;
|
|
9597
|
+
}
|
|
9598
|
+
async function ask(question) {
|
|
9599
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
9600
|
+
try {
|
|
9601
|
+
return await new Promise((resolve) => rl.question(question, resolve));
|
|
9602
|
+
} finally {
|
|
9603
|
+
rl.close();
|
|
9604
|
+
}
|
|
9605
|
+
}
|
|
9606
|
+
async function promptMenu(label, options) {
|
|
9607
|
+
const lines = [label];
|
|
9608
|
+
options.forEach((o, i) => lines.push(` ${i + 1}) ${o}`));
|
|
9609
|
+
process.stdout.write(`${lines.join(`
|
|
9610
|
+
`)}
|
|
9611
|
+
`);
|
|
9612
|
+
for (;; ) {
|
|
9613
|
+
const ans = (await ask(`Enter 1-${options.length}: `)).trim();
|
|
9614
|
+
const n = Number(ans);
|
|
9615
|
+
if (Number.isInteger(n) && n >= 1 && n <= options.length)
|
|
9616
|
+
return options[n - 1];
|
|
9617
|
+
process.stdout.write(`Please enter a number between 1 and ${options.length}.
|
|
9618
|
+
`);
|
|
9619
|
+
}
|
|
9620
|
+
}
|
|
9621
|
+
async function promptPort(def) {
|
|
9622
|
+
for (;; ) {
|
|
9623
|
+
const ans = (await ask(`Remote-debugging port [${def}]: `)).trim();
|
|
9624
|
+
if (ans === "")
|
|
9625
|
+
return def;
|
|
9626
|
+
const n = Number(ans);
|
|
9627
|
+
if (Number.isInteger(n) && n >= 1 && n <= 65535)
|
|
9628
|
+
return n;
|
|
9629
|
+
process.stdout.write(`Please enter an integer between 1 and 65535.
|
|
9630
|
+
`);
|
|
9631
|
+
}
|
|
9632
|
+
}
|
|
9633
|
+
function harnessTargetDesc(harness, io) {
|
|
9634
|
+
if (harness === "claude") {
|
|
9635
|
+
return io.configPath ?? join14(io.homeDir ?? homedir3(), ".claude.json");
|
|
9636
|
+
}
|
|
9637
|
+
return `via \`${harness} mcp add\``;
|
|
9638
|
+
}
|
|
9639
|
+
function binEnvVar(browser) {
|
|
9640
|
+
return `${browser.toUpperCase()}_BIN`;
|
|
9641
|
+
}
|
|
9642
|
+
async function runInstaller(argv, io = {}) {
|
|
9643
|
+
const write = io.stdout ?? ((s) => void process.stdout.write(s));
|
|
9644
|
+
const writeErr = io.stderr ?? ((s) => void process.stderr.write(s));
|
|
9645
|
+
const isTTY = io.isTTY ?? Boolean(process.stdin.isTTY);
|
|
9646
|
+
let args;
|
|
9647
|
+
try {
|
|
9648
|
+
args = parseInstallArgs(argv);
|
|
9649
|
+
} catch (err) {
|
|
9650
|
+
writeErr(`${err.message}
|
|
9651
|
+
|
|
9652
|
+
`);
|
|
9653
|
+
writeErr(`${installHelpText()}
|
|
9654
|
+
`);
|
|
9655
|
+
return 1;
|
|
9656
|
+
}
|
|
9657
|
+
if (args.help) {
|
|
9658
|
+
write(`${installHelpText()}
|
|
9659
|
+
`);
|
|
9660
|
+
return 0;
|
|
9661
|
+
}
|
|
9662
|
+
let harness = args.harness;
|
|
9663
|
+
let browser = args.browser;
|
|
9664
|
+
let port = args.port;
|
|
9665
|
+
if (isTTY) {
|
|
9666
|
+
if (harness === undefined)
|
|
9667
|
+
harness = await promptMenu("Which coding harness?", HARNESSES);
|
|
9668
|
+
if (browser === undefined)
|
|
9669
|
+
browser = await promptMenu("Which browser?", BROWSERS);
|
|
9670
|
+
if (port === undefined)
|
|
9671
|
+
port = await promptPort(defaultPortFor(browser));
|
|
9672
|
+
} else {
|
|
9673
|
+
const missing = [];
|
|
9674
|
+
if (harness === undefined)
|
|
9675
|
+
missing.push("--harness");
|
|
9676
|
+
if (browser === undefined)
|
|
9677
|
+
missing.push("--browser");
|
|
9678
|
+
if (missing.length > 0) {
|
|
9679
|
+
writeErr(`cdp install: missing required option(s) ${missing.join(", ")} in non-interactive mode.
|
|
9680
|
+
`);
|
|
9681
|
+
writeErr("Run `cdp install` in a terminal to be prompted, or pass the option(s) explicitly.\n\n");
|
|
9682
|
+
writeErr(`${installHelpText()}
|
|
9683
|
+
`);
|
|
9684
|
+
return 1;
|
|
9685
|
+
}
|
|
9686
|
+
if (port === undefined)
|
|
9687
|
+
port = defaultPortFor(browser);
|
|
9688
|
+
}
|
|
9689
|
+
if (harness === undefined || browser === undefined || port === undefined) {
|
|
9690
|
+
writeErr(`cdp install: internal error resolving options
|
|
9691
|
+
`);
|
|
9692
|
+
return 1;
|
|
9693
|
+
}
|
|
9694
|
+
const name = args.name ?? DEFAULT_NAME;
|
|
9695
|
+
const writeAlias = args.alias !== false;
|
|
9696
|
+
const rcPath = io.rcPath ?? detectShellRc();
|
|
9697
|
+
const resolvedAliasName = args.aliasName ?? defaultAliasName(browser);
|
|
9698
|
+
const line = aliasLine({ choice: browser, port, aliasName: args.aliasName });
|
|
9699
|
+
if (!args.yes && isTTY) {
|
|
9700
|
+
const plan = [
|
|
9701
|
+
"",
|
|
9702
|
+
"About to:",
|
|
9703
|
+
` - Register MCP server "${name}" with ${harness} (${harnessTargetDesc(harness, io)})`,
|
|
9704
|
+
writeAlias ? ` - Append a shell alias to ${rcPath}:
|
|
9705
|
+
${line}` : " - (skipping shell alias: --no-alias)",
|
|
9706
|
+
""
|
|
9707
|
+
].join(`
|
|
9708
|
+
`);
|
|
9709
|
+
write(`${plan}
|
|
9710
|
+
`);
|
|
9711
|
+
const ans = (await ask("Proceed? [y/N] ")).trim();
|
|
9712
|
+
if (!/^y(es)?$/i.test(ans)) {
|
|
9713
|
+
write(`Aborted. Nothing was written.
|
|
9714
|
+
`);
|
|
9715
|
+
return 0;
|
|
9716
|
+
}
|
|
9717
|
+
}
|
|
9718
|
+
let harnessResult;
|
|
9719
|
+
try {
|
|
9720
|
+
harnessResult = await installMcpServer(harness, name, composeServerConfig(browser, port), {
|
|
9721
|
+
configPath: io.configPath,
|
|
9722
|
+
homeDir: io.homeDir
|
|
9723
|
+
});
|
|
9724
|
+
} catch (err) {
|
|
9725
|
+
writeErr(`cdp install: could not register the MCP server with ${harness}: ${err.message}
|
|
9726
|
+
`);
|
|
9727
|
+
return 1;
|
|
9728
|
+
}
|
|
9729
|
+
let rcResult;
|
|
9730
|
+
let browserFound = true;
|
|
9731
|
+
if (writeAlias) {
|
|
9732
|
+
rcResult = await appendAliasToRc(rcPath, line);
|
|
9733
|
+
browserFound = detectBrowser(browser).found;
|
|
9734
|
+
if (!browserFound) {
|
|
9735
|
+
writeErr(`
|
|
9736
|
+
Warning: could not find the ${browser} binary at its expected location. The alias was ` + `written anyway, but \`${resolvedAliasName}\` will not launch until the binary is found.
|
|
9737
|
+
` + ` Fix: set ${binEnvVar(browser)}=/path/to/${browser} and re-run \`cdp install\`, or edit the
|
|
9738
|
+
` + ` alias line in ${rcPath} to point at the real binary.
|
|
9739
|
+
`);
|
|
9740
|
+
}
|
|
9741
|
+
}
|
|
9742
|
+
const summary = [
|
|
9743
|
+
"",
|
|
9744
|
+
"cdp-toolkit install complete.",
|
|
9745
|
+
"",
|
|
9746
|
+
`Harness: ${harness}`,
|
|
9747
|
+
` server name: ${name}`,
|
|
9748
|
+
harnessResult.path ? ` config: ${harnessResult.path}` : ` registered: ${harnessTargetDesc(harness, io)}`,
|
|
9749
|
+
` method: ${harnessResult.method}`,
|
|
9750
|
+
` action: ${harnessResult.action}${harnessResult.detail ? ` (${harnessResult.detail})` : ""}`
|
|
9751
|
+
];
|
|
9752
|
+
if (writeAlias && rcResult) {
|
|
9753
|
+
summary.push("", `Browser alias: ${resolvedAliasName} -> ${rcResult.rcPath}`, ` action: ${rcResult.action}`, ` line: ${line}`);
|
|
9754
|
+
if (!browserFound)
|
|
9755
|
+
summary.push(` note: ${browser} binary not found yet (see warning above)`);
|
|
9756
|
+
} else {
|
|
9757
|
+
summary.push("", "Browser alias: skipped (--no-alias).");
|
|
9758
|
+
}
|
|
9759
|
+
summary.push("", "Next steps:");
|
|
9760
|
+
if (writeAlias && rcResult) {
|
|
9761
|
+
summary.push(` 1. Load the alias into your shell: source ${rcResult.rcPath} (or open a new terminal)`, ` 2. Launch the browser with the debug port open: ${resolvedAliasName}`, ` 3. Restart / reconnect ${harness} so it picks up the "${name}" MCP server.`);
|
|
9762
|
+
if (browser === "firefox") {
|
|
9763
|
+
summary.push("", "Firefox note: the alias includes --marionette, which cdp-toolkit needs for its orphan", "auto-recovery path — don't remove it.");
|
|
9764
|
+
}
|
|
9765
|
+
} else {
|
|
9766
|
+
summary.push(` 1. Restart / reconnect ${harness} so it picks up the "${name}" MCP server.`);
|
|
9767
|
+
}
|
|
9768
|
+
summary.push("");
|
|
9769
|
+
write(`${summary.join(`
|
|
9770
|
+
`)}
|
|
9771
|
+
`);
|
|
9772
|
+
return 0;
|
|
9773
|
+
}
|
|
9774
|
+
|
|
8879
9775
|
// src/cli.ts
|
|
8880
9776
|
var USAGE = `cdp-toolkit: raw single-target CDP, 29-tool chrome-devtools-mcp parity, plus a Firefox backend over WebDriver BiDi.
|
|
8881
9777
|
|
|
@@ -9024,6 +9920,9 @@ function printToolHelp(name) {
|
|
|
9024
9920
|
}
|
|
9025
9921
|
async function main() {
|
|
9026
9922
|
const rawArgv = process.argv.slice(2);
|
|
9923
|
+
if (rawArgv[0] === "install") {
|
|
9924
|
+
return await runInstaller(rawArgv.slice(1));
|
|
9925
|
+
}
|
|
9027
9926
|
const { kind: browser, endpoint } = resolveBackend(rawArgv);
|
|
9028
9927
|
const parsed = parseArgv(stripConnectFlag(stripBrowserFlag(rawArgv)));
|
|
9029
9928
|
if (parsed.help) {
|