chrome-relay 0.2.5 → 0.3.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/dist/cli.js +170 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -197,7 +197,13 @@ Notes:
|
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
199
|
function tabOpt(cmd) {
|
|
200
|
-
return cmd.option("-t, --tab <id>", "target tab ID", (v) => Number(v));
|
|
200
|
+
return cmd.option("-t, --tab <id>", "target tab ID", (v) => Number(v)).option("--group <name>", "target the active tab of a named group window (see `chrome-relay group`)");
|
|
201
|
+
}
|
|
202
|
+
function baseArgs(opts) {
|
|
203
|
+
const args = {};
|
|
204
|
+
if (opts.tab !== void 0) args.tabId = opts.tab;
|
|
205
|
+
if (opts.group) args.groupName = opts.group;
|
|
206
|
+
return args;
|
|
201
207
|
}
|
|
202
208
|
program.command("tabs").description("List open Chrome windows and tabs.").action(async () => {
|
|
203
209
|
await run("get_windows_and_tabs", {});
|
|
@@ -224,6 +230,7 @@ Use "chrome-relay switch ${url}" to activate that tab, or "chrome-relay navigate
|
|
|
224
230
|
}
|
|
225
231
|
const args = { url };
|
|
226
232
|
if (opts.tab !== void 0) args.tabId = opts.tab;
|
|
233
|
+
if (opts.group) args.groupName = opts.group;
|
|
227
234
|
if (opts.new) args.newTab = true;
|
|
228
235
|
if (opts.inactive) args.active = false;
|
|
229
236
|
await run("chrome_navigate", args);
|
|
@@ -248,6 +255,7 @@ full-tab screenshot when an agent only needs to see one component.
|
|
|
248
255
|
).action(async (opts) => {
|
|
249
256
|
const args = {};
|
|
250
257
|
if (opts.tab !== void 0) args.tabId = opts.tab;
|
|
258
|
+
if (opts.group) args.groupName = opts.group;
|
|
251
259
|
if (opts.full) args.fullPage = true;
|
|
252
260
|
if (opts.bbox) args.bbox = opts.bbox;
|
|
253
261
|
if (opts.selector) args.selector = opts.selector;
|
|
@@ -277,6 +285,7 @@ full-tab screenshot when an agent only needs to see one component.
|
|
|
277
285
|
).action(async (opts) => {
|
|
278
286
|
const args = {};
|
|
279
287
|
if (opts.tab !== void 0) args.tabId = opts.tab;
|
|
288
|
+
if (opts.group) args.groupName = opts.group;
|
|
280
289
|
if (opts.interactive) args.interactiveOnly = true;
|
|
281
290
|
await run("chrome_read_page", args);
|
|
282
291
|
});
|
|
@@ -285,6 +294,7 @@ full-tab screenshot when an agent only needs to see one component.
|
|
|
285
294
|
).action(async (selector, opts) => {
|
|
286
295
|
const args = { selector };
|
|
287
296
|
if (opts.tab !== void 0) args.tabId = opts.tab;
|
|
297
|
+
if (opts.group) args.groupName = opts.group;
|
|
288
298
|
await run("chrome_click_element", args);
|
|
289
299
|
});
|
|
290
300
|
tabOpt(
|
|
@@ -292,6 +302,7 @@ full-tab screenshot when an agent only needs to see one component.
|
|
|
292
302
|
).action(async (selector, value, opts) => {
|
|
293
303
|
const args = { selector, value };
|
|
294
304
|
if (opts.tab !== void 0) args.tabId = opts.tab;
|
|
305
|
+
if (opts.group) args.groupName = opts.group;
|
|
295
306
|
await run("chrome_fill_or_select", args);
|
|
296
307
|
});
|
|
297
308
|
tabOpt(
|
|
@@ -311,6 +322,7 @@ For typing text into a field, use \`chrome-relay type\` instead.
|
|
|
311
322
|
).action(async (keys, opts) => {
|
|
312
323
|
const args = { keys };
|
|
313
324
|
if (opts.tab !== void 0) args.tabId = opts.tab;
|
|
325
|
+
if (opts.group) args.groupName = opts.group;
|
|
314
326
|
await run("chrome_keyboard", args);
|
|
315
327
|
});
|
|
316
328
|
tabOpt(
|
|
@@ -332,6 +344,7 @@ When to pick which:
|
|
|
332
344
|
).action(async (text, opts) => {
|
|
333
345
|
const args = { text };
|
|
334
346
|
if (opts.tab !== void 0) args.tabId = opts.tab;
|
|
347
|
+
if (opts.group) args.groupName = opts.group;
|
|
335
348
|
if (opts.selector) args.selector = opts.selector;
|
|
336
349
|
await run("chrome_type", args);
|
|
337
350
|
});
|
|
@@ -354,6 +367,7 @@ Notes:
|
|
|
354
367
|
).action(async (code, opts) => {
|
|
355
368
|
const args = { code };
|
|
356
369
|
if (opts.tab !== void 0) args.tabId = opts.tab;
|
|
370
|
+
if (opts.group) args.groupName = opts.group;
|
|
357
371
|
if (typeof opts.timeoutMs === "number") args.timeoutMs = opts.timeoutMs;
|
|
358
372
|
await run("chrome_evaluate", args);
|
|
359
373
|
});
|
|
@@ -400,6 +414,7 @@ Notes:
|
|
|
400
414
|
).action(async (name, opts) => {
|
|
401
415
|
const args = { action: "preset", name };
|
|
402
416
|
if (opts.tab !== void 0) args.tabId = opts.tab;
|
|
417
|
+
if (opts.group) args.groupName = opts.group;
|
|
403
418
|
await run("chrome_viewport", args);
|
|
404
419
|
});
|
|
405
420
|
tabOpt(
|
|
@@ -407,11 +422,165 @@ Notes:
|
|
|
407
422
|
).action(async (opts) => {
|
|
408
423
|
const args = { action: "clear" };
|
|
409
424
|
if (opts.tab !== void 0) args.tabId = opts.tab;
|
|
425
|
+
if (opts.group) args.groupName = opts.group;
|
|
410
426
|
await run("chrome_viewport", args);
|
|
411
427
|
});
|
|
412
428
|
viewport.command("list").description("List available presets.").action(async () => {
|
|
413
429
|
await run("chrome_viewport", { action: "list" });
|
|
414
430
|
});
|
|
431
|
+
program.command("self-reload").description("Restart the chrome-relay extension's service worker (picks up newly built code).").action(async () => {
|
|
432
|
+
await run("chrome_self_reload", {});
|
|
433
|
+
});
|
|
434
|
+
tabOpt(
|
|
435
|
+
program.command("ax").description("Extract the accessibility tree \u2014 ~30\xD7 smaller than `read` and more semantic.").option("-i, --interactive-only", "filter to actionable roles (button, link, textbox, ...)").option("--root <role>", "start from the first node matching this role (e.g. 'main')").option("--include-subframes", "walk subframes too (default: top frame only)").addHelpText(
|
|
436
|
+
"after",
|
|
437
|
+
`
|
|
438
|
+
|
|
439
|
+
Examples:
|
|
440
|
+
chrome-relay ax --tab 123
|
|
441
|
+
chrome-relay ax --tab 123 --interactive-only
|
|
442
|
+
chrome-relay ax --tab 123 --root main --interactive-only
|
|
443
|
+
|
|
444
|
+
Notes:
|
|
445
|
+
Each node carries an "id" \u2014 that's the backendDOMNodeId. Pass it to
|
|
446
|
+
\`chrome-relay click-ax --node <id>\` to click without a CSS selector.
|
|
447
|
+
`
|
|
448
|
+
)
|
|
449
|
+
).action(async (opts) => {
|
|
450
|
+
const args = baseArgs(opts);
|
|
451
|
+
if (opts.interactiveOnly) args.interactiveOnly = true;
|
|
452
|
+
if (opts.root) args.rootRole = opts.root;
|
|
453
|
+
if (opts.includeSubframes) args.includeSubframes = true;
|
|
454
|
+
await run("chrome_ax", args);
|
|
455
|
+
});
|
|
456
|
+
tabOpt(
|
|
457
|
+
program.command("click-ax").description("Click an element by its backendDOMNodeId from a previous `ax` call.").requiredOption("--node <id>", "backendDOMNodeId from `chrome-relay ax`", (v) => Number(v)).addHelpText(
|
|
458
|
+
"after",
|
|
459
|
+
`
|
|
460
|
+
|
|
461
|
+
Examples:
|
|
462
|
+
chrome-relay click-ax --tab 123 --node 456
|
|
463
|
+
|
|
464
|
+
Notes:
|
|
465
|
+
Throws explicitly if the node id is stale (page mutated since you called
|
|
466
|
+
\`ax\`). Re-run \`ax\` and pass the fresh id.
|
|
467
|
+
`
|
|
468
|
+
)
|
|
469
|
+
).action(async (opts) => {
|
|
470
|
+
const args = baseArgs(opts);
|
|
471
|
+
args.node = opts.node;
|
|
472
|
+
await run("chrome_click_ax", args);
|
|
473
|
+
});
|
|
474
|
+
const group = program.command("group").description("Manage named Chrome windows so multiple agents can drive separate windows.").addHelpText(
|
|
475
|
+
"after",
|
|
476
|
+
`
|
|
477
|
+
|
|
478
|
+
Examples:
|
|
479
|
+
chrome-relay group create bidsmith-h01 --url https://reddit.com
|
|
480
|
+
chrome-relay group list
|
|
481
|
+
chrome-relay --group bidsmith-h01 navigate https://news.ycombinator.com
|
|
482
|
+
chrome-relay --group bidsmith-h01 screenshot -o evidence.png
|
|
483
|
+
chrome-relay group close bidsmith-h01
|
|
484
|
+
|
|
485
|
+
Notes:
|
|
486
|
+
Hard lifecycle: if you manually close the group's window, the next
|
|
487
|
+
--group operation fails loudly until you run \`group close\` + \`group create\` again.
|
|
488
|
+
If you pass both --tab and --group on the same command, --tab wins.
|
|
489
|
+
`
|
|
490
|
+
);
|
|
491
|
+
group.command("create <name>").description("Open a new Chrome window and bind it to <name>.").option("--url <url>", "initial URL (default about:blank)").option("--label <label>", "human-readable description shown in popup/list").action(async (name, opts) => {
|
|
492
|
+
const args = { action: "create", name };
|
|
493
|
+
if (opts.url) args.url = opts.url;
|
|
494
|
+
if (opts.label) args.label = opts.label;
|
|
495
|
+
await run("chrome_group", args);
|
|
496
|
+
});
|
|
497
|
+
group.command("list").description("List all known groups + whether their window is still alive.").action(async () => {
|
|
498
|
+
await run("chrome_group", { action: "list" });
|
|
499
|
+
});
|
|
500
|
+
group.command("close <name>").description("Close the group's window (if alive) and remove the binding.").action(async (name) => {
|
|
501
|
+
await run("chrome_group", { action: "close", name });
|
|
502
|
+
});
|
|
503
|
+
const network = program.command("network").description("Capture HTTP request/response metadata. Ring buffer, last 200 per tab.").addHelpText(
|
|
504
|
+
"after",
|
|
505
|
+
`
|
|
506
|
+
|
|
507
|
+
Examples:
|
|
508
|
+
chrome-relay network --tab 123 # last 200 requests
|
|
509
|
+
chrome-relay network --tab 123 --filter api.example.com # url substring
|
|
510
|
+
chrome-relay network --tab 123 --status failed # only failures
|
|
511
|
+
chrome-relay network --tab 123 --method POST
|
|
512
|
+
chrome-relay network --tab 123 --body <requestId> # lazy body fetch
|
|
513
|
+
chrome-relay network --tab 123 har > capture.har # HAR export
|
|
514
|
+
chrome-relay network --tab 123 --clear
|
|
515
|
+
|
|
516
|
+
Privacy:
|
|
517
|
+
Capturing network traffic includes Authorization headers, cookies, and
|
|
518
|
+
request/response bodies. The capture stays in the extension's memory and
|
|
519
|
+
is wiped on tab close. Don't run this on a tab whose state you wouldn't
|
|
520
|
+
share with the agent invoking chrome-relay.
|
|
521
|
+
|
|
522
|
+
Notes:
|
|
523
|
+
Bodies are NOT eagerly buffered \u2014 Chrome GCs response bodies ~30s after
|
|
524
|
+
the request finishes. Use \`--body <id>\` promptly. WebSocket frames and
|
|
525
|
+
SSE streams are out of scope.
|
|
526
|
+
`
|
|
527
|
+
);
|
|
528
|
+
tabOpt(
|
|
529
|
+
network.command("read", { isDefault: true }).description("List captured network entries.").option("--filter <substr>", "url substring filter").option("--status <bucket>", "ok | redirect | client_error | server_error | failed").option("--method <verb>", "exact method, e.g. POST").option("--limit <n>", "cap response length", (v) => Number(v))
|
|
530
|
+
).action(async (opts) => {
|
|
531
|
+
const args = baseArgs(opts);
|
|
532
|
+
if (opts.filter) args.filter = opts.filter;
|
|
533
|
+
if (opts.status) args.status = opts.status;
|
|
534
|
+
if (opts.method) args.method = opts.method;
|
|
535
|
+
if (typeof opts.limit === "number") args.limit = opts.limit;
|
|
536
|
+
await run("chrome_network", args);
|
|
537
|
+
});
|
|
538
|
+
tabOpt(
|
|
539
|
+
network.command("body <requestId>").description("Fetch the response body for one request (lazy; may fail if GC'd).")
|
|
540
|
+
).action(async (requestId, opts) => {
|
|
541
|
+
const args = { ...baseArgs(opts), action: "body", requestId };
|
|
542
|
+
await run("chrome_network", args);
|
|
543
|
+
});
|
|
544
|
+
tabOpt(
|
|
545
|
+
network.command("har").description("Emit HAR-compatible JSON for the captured entries.").option("--filter <substr>", "url substring filter").option("--status <bucket>", "status bucket filter")
|
|
546
|
+
).action(async (opts) => {
|
|
547
|
+
const args = { ...baseArgs(opts), action: "har" };
|
|
548
|
+
if (opts.filter) args.filter = opts.filter;
|
|
549
|
+
if (opts.status) args.status = opts.status;
|
|
550
|
+
await run("chrome_network", args);
|
|
551
|
+
});
|
|
552
|
+
tabOpt(
|
|
553
|
+
network.command("clear").description("Wipe the network buffer for this tab.")
|
|
554
|
+
).action(async (opts) => {
|
|
555
|
+
const args = { ...baseArgs(opts), action: "clear" };
|
|
556
|
+
await run("chrome_network", args);
|
|
557
|
+
});
|
|
558
|
+
tabOpt(
|
|
559
|
+
program.command("console").description("Read console.log/warn/error + page exceptions (ring buffer, last 200).").option("--level <levels>", "comma-separated: log,info,warn,error,debug,exception").option("--since <id>", "only return entries with id > since (live-tail-ish)", (v) => Number(v)).option("--limit <n>", "cap response length", (v) => Number(v)).option("--clear", "wipe the buffer (no read)").addHelpText(
|
|
560
|
+
"after",
|
|
561
|
+
`
|
|
562
|
+
|
|
563
|
+
Examples:
|
|
564
|
+
chrome-relay console --tab 123
|
|
565
|
+
chrome-relay console --tab 123 --level error,exception
|
|
566
|
+
chrome-relay console --tab 123 --since 50 # entries newer than id 50 (tail-style polling)
|
|
567
|
+
chrome-relay console --tab 123 --clear
|
|
568
|
+
|
|
569
|
+
Notes:
|
|
570
|
+
Ring buffer holds the last 200 entries per tab (or 256 KB, whichever first).
|
|
571
|
+
Wipes on tab close. First call on a tab subscribes; subsequent calls are
|
|
572
|
+
instant in-memory reads.
|
|
573
|
+
`
|
|
574
|
+
)
|
|
575
|
+
).action(async (opts) => {
|
|
576
|
+
const args = {};
|
|
577
|
+
if (opts.tab !== void 0) args.tabId = opts.tab;
|
|
578
|
+
if (opts.clear) args.action = "clear";
|
|
579
|
+
if (opts.level) args.levels = opts.level;
|
|
580
|
+
if (typeof opts.since === "number") args.since = opts.since;
|
|
581
|
+
if (typeof opts.limit === "number") args.limit = opts.limit;
|
|
582
|
+
await run("chrome_console", args);
|
|
583
|
+
});
|
|
415
584
|
return program;
|
|
416
585
|
}
|
|
417
586
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chrome-relay",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"tsup": "^8.4.0",
|
|
29
29
|
"vitest": "^3.0.0",
|
|
30
|
-
"@chrome-relay/protocol": "0.
|
|
30
|
+
"@chrome-relay/protocol": "0.3.0"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"build": "tsup",
|