chrome-relay 0.5.13 → 0.5.14
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 +153 -126
- package/dist/index.js +1 -1
- package/dist/native-host.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -37,26 +37,60 @@ function requireString(obj, key, tool) {
|
|
|
37
37
|
}
|
|
38
38
|
return v;
|
|
39
39
|
}
|
|
40
|
-
function
|
|
40
|
+
function rejectWrongType(obj, key, expected, tool) {
|
|
41
|
+
throw new RelayError({
|
|
42
|
+
code: "invalid_arguments",
|
|
43
|
+
message: `${tool ?? "<unknown tool>"}: \`${key}\` must be ${expected} (got ${typeof obj[key]}).`,
|
|
44
|
+
tool,
|
|
45
|
+
phase: "parse_arguments",
|
|
46
|
+
details: { field: key, expected, received: obj[key] },
|
|
47
|
+
retryable: false
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function optString(obj, key, tool) {
|
|
41
51
|
const v = obj[key];
|
|
42
|
-
|
|
52
|
+
if (v === void 0 || v === null)
|
|
53
|
+
return void 0;
|
|
54
|
+
if (typeof v !== "string")
|
|
55
|
+
rejectWrongType(obj, key, "a string", tool);
|
|
56
|
+
return v || void 0;
|
|
43
57
|
}
|
|
44
|
-
function optNumber(obj, key) {
|
|
58
|
+
function optNumber(obj, key, tool) {
|
|
45
59
|
const v = obj[key];
|
|
46
|
-
|
|
60
|
+
if (v === void 0 || v === null)
|
|
61
|
+
return void 0;
|
|
62
|
+
if (typeof v !== "number" || !Number.isFinite(v))
|
|
63
|
+
rejectWrongType(obj, key, "a finite number", tool);
|
|
64
|
+
return v;
|
|
47
65
|
}
|
|
48
|
-
function optBool(obj, key) {
|
|
66
|
+
function optBool(obj, key, tool) {
|
|
49
67
|
const v = obj[key];
|
|
50
|
-
|
|
68
|
+
if (v === void 0 || v === null)
|
|
69
|
+
return void 0;
|
|
70
|
+
if (typeof v !== "boolean")
|
|
71
|
+
rejectWrongType(obj, key, "a boolean", tool);
|
|
72
|
+
return v;
|
|
51
73
|
}
|
|
52
|
-
function parseTargetArgs(obj) {
|
|
74
|
+
function parseTargetArgs(obj, tool) {
|
|
53
75
|
const out = {};
|
|
54
|
-
if (
|
|
76
|
+
if (obj.tabId !== void 0 && obj.tabId !== null) {
|
|
77
|
+
if (typeof obj.tabId !== "number" || !Number.isFinite(obj.tabId)) {
|
|
78
|
+
rejectWrongType(obj, "tabId", "a finite number", tool);
|
|
79
|
+
}
|
|
55
80
|
out.tabId = obj.tabId;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
81
|
+
}
|
|
82
|
+
if (obj.workspaceName !== void 0 && obj.workspaceName !== null) {
|
|
83
|
+
if (typeof obj.workspaceName !== "string")
|
|
84
|
+
rejectWrongType(obj, "workspaceName", "a string", tool);
|
|
85
|
+
if (obj.workspaceName)
|
|
86
|
+
out.workspaceName = obj.workspaceName;
|
|
87
|
+
}
|
|
88
|
+
if (obj.groupName !== void 0 && obj.groupName !== null) {
|
|
89
|
+
if (typeof obj.groupName !== "string")
|
|
90
|
+
rejectWrongType(obj, "groupName", "a string", tool);
|
|
91
|
+
if (obj.groupName)
|
|
92
|
+
out.groupName = obj.groupName;
|
|
93
|
+
}
|
|
60
94
|
return out;
|
|
61
95
|
}
|
|
62
96
|
var init_shared = __esm({
|
|
@@ -69,10 +103,7 @@ var init_shared = __esm({
|
|
|
69
103
|
// ../protocol/dist/args/navigate.js
|
|
70
104
|
function parseChromeNavigateArgs(input) {
|
|
71
105
|
const obj = asObject(input, TOOL_NAMES.NAVIGATE);
|
|
72
|
-
const out = {
|
|
73
|
-
url: requireString(obj, "url", TOOL_NAMES.NAVIGATE),
|
|
74
|
-
...parseTargetArgs(obj)
|
|
75
|
-
};
|
|
106
|
+
const out = { url: requireString(obj, "url", TOOL_NAMES.NAVIGATE) };
|
|
76
107
|
if (typeof obj.tabId === "string" && obj.tabId) {
|
|
77
108
|
const n = Number(obj.tabId);
|
|
78
109
|
if (!Number.isFinite(n)) {
|
|
@@ -87,17 +118,23 @@ function parseChromeNavigateArgs(input) {
|
|
|
87
118
|
}
|
|
88
119
|
out.tabId = n;
|
|
89
120
|
} else {
|
|
90
|
-
const n = optNumber(obj, "tabId");
|
|
121
|
+
const n = optNumber(obj, "tabId", TOOL_NAMES.NAVIGATE);
|
|
91
122
|
if (n !== void 0)
|
|
92
123
|
out.tabId = n;
|
|
93
124
|
}
|
|
94
|
-
const
|
|
125
|
+
const { tabId: _, ...rest } = obj;
|
|
126
|
+
const target = parseTargetArgs(rest, TOOL_NAMES.NAVIGATE);
|
|
127
|
+
if (target.workspaceName)
|
|
128
|
+
out.workspaceName = target.workspaceName;
|
|
129
|
+
if (target.groupName)
|
|
130
|
+
out.groupName = target.groupName;
|
|
131
|
+
const newTab = optBool(obj, "newTab", TOOL_NAMES.NAVIGATE);
|
|
95
132
|
if (newTab !== void 0)
|
|
96
133
|
out.newTab = newTab;
|
|
97
|
-
const active = optBool(obj, "active");
|
|
134
|
+
const active = optBool(obj, "active", TOOL_NAMES.NAVIGATE);
|
|
98
135
|
if (active !== void 0)
|
|
99
136
|
out.active = active;
|
|
100
|
-
const allowPartial = optBool(obj, "allowPartial");
|
|
137
|
+
const allowPartial = optBool(obj, "allowPartial", TOOL_NAMES.NAVIGATE);
|
|
101
138
|
if (allowPartial !== void 0)
|
|
102
139
|
out.allowPartial = allowPartial;
|
|
103
140
|
void optString;
|
|
@@ -114,13 +151,13 @@ var init_navigate = __esm({
|
|
|
114
151
|
// ../protocol/dist/args/hover.js
|
|
115
152
|
function parseChromeHoverArgs(input) {
|
|
116
153
|
const obj = asObject(input, TOOL_NAMES.HOVER);
|
|
117
|
-
const target = parseTargetArgs(obj);
|
|
118
|
-
const x = optNumber(obj, "x");
|
|
119
|
-
const y = optNumber(obj, "y");
|
|
154
|
+
const target = parseTargetArgs(obj, TOOL_NAMES.HOVER);
|
|
155
|
+
const x = optNumber(obj, "x", TOOL_NAMES.HOVER);
|
|
156
|
+
const y = optNumber(obj, "y", TOOL_NAMES.HOVER);
|
|
120
157
|
if (x !== void 0 && y !== void 0) {
|
|
121
158
|
return { ...target, kind: "coords", x, y };
|
|
122
159
|
}
|
|
123
|
-
const selector = optString(obj, "selector");
|
|
160
|
+
const selector = optString(obj, "selector", TOOL_NAMES.HOVER);
|
|
124
161
|
if (selector) {
|
|
125
162
|
return { ...target, kind: "selector", selector };
|
|
126
163
|
}
|
|
@@ -866,7 +903,7 @@ var init_dist = __esm({
|
|
|
866
903
|
import { Command } from "commander";
|
|
867
904
|
|
|
868
905
|
// src/index.ts
|
|
869
|
-
var CHROME_RELAY_VERSION = true ? "0.5.
|
|
906
|
+
var CHROME_RELAY_VERSION = true ? "0.5.14" : "0.0.0-dev";
|
|
870
907
|
|
|
871
908
|
// src/commands/shared.ts
|
|
872
909
|
init_dist();
|
|
@@ -914,6 +951,11 @@ async function callTool(name, args) {
|
|
|
914
951
|
}
|
|
915
952
|
|
|
916
953
|
// src/commands/shared.ts
|
|
954
|
+
function makeWithBase(baseArgs) {
|
|
955
|
+
return function withBase(opts, extras) {
|
|
956
|
+
return { ...baseArgs(opts), ...extras ?? {} };
|
|
957
|
+
};
|
|
958
|
+
}
|
|
917
959
|
function tabOpt(cmd) {
|
|
918
960
|
return cmd.option("-t, --tab <id>", "target tab ID", (v) => Number(v)).option("--workspace <name>", "target the active tab in a named workspace window (see `chrome-relay workspace`)").option("--group <name>", "target the active tab in a named tab-group (see `chrome-relay group`)");
|
|
919
961
|
}
|
|
@@ -1100,6 +1142,15 @@ async function runDoctor() {
|
|
|
1100
1142
|
|
|
1101
1143
|
// src/release-notes.ts
|
|
1102
1144
|
var RELEASE_NOTES = {
|
|
1145
|
+
"0.5.14": [
|
|
1146
|
+
"Optional parser fields are now strict \u2014 passing a present-but-wrong-type value (e.g. `{newTab: 'yes'}` instead of `true`) throws RelayError(invalid_arguments) instead of silently being dropped. undefined/null still means 'caller omitted the field' and the handler uses its default.",
|
|
1147
|
+
"parseTargetArgs is strict too: `{tabId: '5'}` rejects (numeric tabId required). Navigate is the one exception \u2014 it coerces string tabId for back-compat since it's used as a 'reference window' rather than a strict target.",
|
|
1148
|
+
"CLI tab-group --tabs arg now forwarded as the raw comma-separated string. The protocol parser (parseChromeGroupArgs) does the strict per-element parsing so `--tabs 1,foo,3` errors instead of silently becoming [1,3].",
|
|
1149
|
+
"Deleted the dead duplicate parser module at apps/extension/src/browser/parsers.ts and its test file. The protocol parsers replace them; behavior identical.",
|
|
1150
|
+
"HAR creator.version now reads from chrome.runtime.getManifest() instead of the hardcoded stale '0.2.x'. Drift-proof.",
|
|
1151
|
+
"New CLI helper `withBase(opts, extras)` collapses the `const args = {}; Object.assign(args, baseArgs(opts)); args.foo = bar` pattern into one expression. All four command modules migrated \u2014 `Object.assign(args, baseArgs(opts))` is gone from the codebase.",
|
|
1152
|
+
"Tests: +2 strict-optional cases. Total 407 (was 433 \u2014 28 dropped were duplicates of protocol parsers)."
|
|
1153
|
+
],
|
|
1103
1154
|
"0.5.13": [
|
|
1104
1155
|
"Protocol arg-parser coverage complete (code-quality-hardening Risk 1 \u2014 finished). Every one of the 22 tools now has an executable parser in @chrome-relay/protocol that returns a typed args object with `code:'invalid_arguments'` errors on malformed input.",
|
|
1105
1156
|
"New parsers in this release: parseChrome{ReadPage,Click,Fill,Keyboard,Type,Evaluate,SwitchTab,CloseTabs,Ax,ClickAx,Screenshot,Viewport,Console,Workspace,Group,Screencast}Args, plus parseGetWindowsAndTabsArgs and parseChromeSelfReloadArgs.",
|
|
@@ -1299,7 +1350,7 @@ function registerInstallUpdate(program) {
|
|
|
1299
1350
|
|
|
1300
1351
|
// src/commands/navigation.ts
|
|
1301
1352
|
function registerNavigation(ctx) {
|
|
1302
|
-
const { program,
|
|
1353
|
+
const { program, withBase, run } = ctx;
|
|
1303
1354
|
program.command("tabs [verb]").description("List open Chrome windows and tabs. (verb 'list' is accepted as alias)").action(async (verb) => {
|
|
1304
1355
|
if (verb && verb !== "list") {
|
|
1305
1356
|
process.stderr.write(`unknown tabs verb: ${verb}. Use 'tabs' or 'tabs list'.
|
|
@@ -1328,11 +1379,10 @@ Use "chrome-relay switch ${url}" to activate that tab, or "chrome-relay navigate
|
|
|
1328
1379
|
);
|
|
1329
1380
|
process.exit(1);
|
|
1330
1381
|
}
|
|
1331
|
-
const
|
|
1332
|
-
|
|
1333
|
-
if (opts.
|
|
1334
|
-
|
|
1335
|
-
await run("chrome_navigate", args);
|
|
1382
|
+
const extras = { url };
|
|
1383
|
+
if (opts.new) extras.newTab = true;
|
|
1384
|
+
if (opts.inactive) extras.active = false;
|
|
1385
|
+
await run("chrome_navigate", withBase(opts, extras));
|
|
1336
1386
|
});
|
|
1337
1387
|
program.command("switch <tabId>").description("Activate a tab by ID.").action(async (tabId) => {
|
|
1338
1388
|
await run("chrome_switch_tab", { tabId: Number(tabId) });
|
|
@@ -1348,20 +1398,16 @@ Use "chrome-relay switch ${url}" to activate that tab, or "chrome-relay navigate
|
|
|
1348
1398
|
|
|
1349
1399
|
// src/commands/input.ts
|
|
1350
1400
|
function registerInput(ctx) {
|
|
1351
|
-
const { program,
|
|
1401
|
+
const { program, withBase, run } = ctx;
|
|
1352
1402
|
tabOpt(
|
|
1353
1403
|
program.command("click <selector>").description("Click an element by CSS selector.")
|
|
1354
1404
|
).action(async (selector, opts) => {
|
|
1355
|
-
|
|
1356
|
-
Object.assign(args, baseArgs(opts));
|
|
1357
|
-
await run("chrome_click_element", args);
|
|
1405
|
+
await run("chrome_click_element", withBase(opts, { selector }));
|
|
1358
1406
|
});
|
|
1359
1407
|
tabOpt(
|
|
1360
1408
|
program.command("fill <selector> <value>").description("Fill an input or textarea.")
|
|
1361
1409
|
).action(async (selector, value, opts) => {
|
|
1362
|
-
|
|
1363
|
-
Object.assign(args, baseArgs(opts));
|
|
1364
|
-
await run("chrome_fill_or_select", args);
|
|
1410
|
+
await run("chrome_fill_or_select", withBase(opts, { selector, value }));
|
|
1365
1411
|
});
|
|
1366
1412
|
tabOpt(
|
|
1367
1413
|
program.command("keys <keys>").description("Press a single key or chord via trusted CDP input (e.g. Enter, Cmd+K).").addHelpText(
|
|
@@ -1378,9 +1424,7 @@ For typing text into a field, use \`chrome-relay type\` instead.
|
|
|
1378
1424
|
`
|
|
1379
1425
|
)
|
|
1380
1426
|
).action(async (keys, opts) => {
|
|
1381
|
-
|
|
1382
|
-
Object.assign(args, baseArgs(opts));
|
|
1383
|
-
await run("chrome_keyboard", args);
|
|
1427
|
+
await run("chrome_keyboard", withBase(opts, { keys }));
|
|
1384
1428
|
});
|
|
1385
1429
|
tabOpt(
|
|
1386
1430
|
program.command("type <text>").description("Insert text via trusted CDP input. Works in contenteditable / Draft.js / Lexical.").option("-s, --selector <selector>", "focus this element first").addHelpText(
|
|
@@ -1399,10 +1443,9 @@ When to pick which:
|
|
|
1399
1443
|
`
|
|
1400
1444
|
)
|
|
1401
1445
|
).action(async (text, opts) => {
|
|
1402
|
-
const
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
await run("chrome_type", args);
|
|
1446
|
+
const extras = { text };
|
|
1447
|
+
if (opts.selector) extras.selector = opts.selector;
|
|
1448
|
+
await run("chrome_type", withBase(opts, extras));
|
|
1406
1449
|
});
|
|
1407
1450
|
tabOpt(
|
|
1408
1451
|
program.command("js <code>").description("Evaluate JavaScript in the page MAIN world. Use `return` for the value.").option("--timeout-ms <ms>", "execution timeout in milliseconds (default 15000)", (v) => Number(v)).addHelpText(
|
|
@@ -1421,10 +1464,9 @@ Notes:
|
|
|
1421
1464
|
`
|
|
1422
1465
|
)
|
|
1423
1466
|
).action(async (code, opts) => {
|
|
1424
|
-
const
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
await run("chrome_evaluate", args);
|
|
1467
|
+
const extras = { code };
|
|
1468
|
+
if (typeof opts.timeoutMs === "number") extras.timeoutMs = opts.timeoutMs;
|
|
1469
|
+
await run("chrome_evaluate", withBase(opts, extras));
|
|
1428
1470
|
});
|
|
1429
1471
|
tabOpt(
|
|
1430
1472
|
program.command("hover [selector]").description("Move the pointer over an element or coordinates. Fires :hover styles.").option("--x <px>", "explicit x coordinate (CSS pixels)", (v) => Number(v)).option("--y <px>", "explicit y coordinate (CSS pixels)", (v) => Number(v)).addHelpText(
|
|
@@ -1440,21 +1482,20 @@ tooltip appearance, etc.) that a bare click would skip past too quickly.
|
|
|
1440
1482
|
`
|
|
1441
1483
|
)
|
|
1442
1484
|
).action(async (selector, opts) => {
|
|
1443
|
-
const
|
|
1444
|
-
|
|
1445
|
-
if (selector) args.selector = selector;
|
|
1485
|
+
const extras = {};
|
|
1486
|
+
if (selector) extras.selector = selector;
|
|
1446
1487
|
if (typeof opts.x === "number" && typeof opts.y === "number") {
|
|
1447
|
-
|
|
1448
|
-
|
|
1488
|
+
extras.x = opts.x;
|
|
1489
|
+
extras.y = opts.y;
|
|
1449
1490
|
}
|
|
1450
|
-
await run("chrome_hover",
|
|
1491
|
+
await run("chrome_hover", withBase(opts, extras));
|
|
1451
1492
|
});
|
|
1452
1493
|
}
|
|
1453
1494
|
|
|
1454
1495
|
// src/commands/capture.ts
|
|
1455
1496
|
import { writeFileSync } from "fs";
|
|
1456
1497
|
function registerCapture(ctx) {
|
|
1457
|
-
const { program,
|
|
1498
|
+
const { program, withBase, run } = ctx;
|
|
1458
1499
|
tabOpt(
|
|
1459
1500
|
program.command("screenshot").description("Capture a screenshot of any tab without activating it.").option("--full", "capture beyond the viewport (full page)").option("--bbox <rect>", "capture a region: 'x,y,width,height' (pixels)").option("--selector <css>", "capture the bounding box of a CSS selector").option("--padding <px>", "pixels of padding around --selector region", (v) => Number(v)).option("--max-edge <px>", "downscale so longer edge \u2264 this many pixels (no default; opt-in)", (v) => Number(v)).option("-o, --out <path>", "save image to path (base64 PNG decoded)").addHelpText(
|
|
1460
1501
|
"after",
|
|
@@ -1473,13 +1514,13 @@ full-tab screenshot when an agent only needs to see one component.
|
|
|
1473
1514
|
`
|
|
1474
1515
|
)
|
|
1475
1516
|
).action(async (opts) => {
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1478
|
-
if (opts.
|
|
1479
|
-
if (opts.
|
|
1480
|
-
if (opts.
|
|
1481
|
-
if (typeof opts.
|
|
1482
|
-
|
|
1517
|
+
const extras = {};
|
|
1518
|
+
if (opts.full) extras.fullPage = true;
|
|
1519
|
+
if (opts.bbox) extras.bbox = opts.bbox;
|
|
1520
|
+
if (opts.selector) extras.selector = opts.selector;
|
|
1521
|
+
if (typeof opts.padding === "number") extras.padding = opts.padding;
|
|
1522
|
+
if (typeof opts.maxEdge === "number") extras.maxEdge = opts.maxEdge;
|
|
1523
|
+
const args = withBase(opts, extras);
|
|
1483
1524
|
try {
|
|
1484
1525
|
const result = await callTool("chrome_screenshot", args);
|
|
1485
1526
|
if (opts.out && result && typeof result === "object") {
|
|
@@ -1503,10 +1544,9 @@ full-tab screenshot when an agent only needs to see one component.
|
|
|
1503
1544
|
tabOpt(
|
|
1504
1545
|
program.command("read").description("Extract page structure and interactive elements.").option("-i, --interactive", "return only interactive elements")
|
|
1505
1546
|
).action(async (opts) => {
|
|
1506
|
-
const
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
await run("chrome_read_page", args);
|
|
1547
|
+
const extras = {};
|
|
1548
|
+
if (opts.interactive) extras.interactiveOnly = true;
|
|
1549
|
+
await run("chrome_read_page", withBase(opts, extras));
|
|
1510
1550
|
});
|
|
1511
1551
|
tabOpt(
|
|
1512
1552
|
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(
|
|
@@ -1524,11 +1564,11 @@ Notes:
|
|
|
1524
1564
|
`
|
|
1525
1565
|
)
|
|
1526
1566
|
).action(async (opts) => {
|
|
1527
|
-
const
|
|
1528
|
-
if (opts.interactiveOnly)
|
|
1529
|
-
if (opts.root)
|
|
1530
|
-
if (opts.includeSubframes)
|
|
1531
|
-
await run("chrome_ax",
|
|
1567
|
+
const extras = {};
|
|
1568
|
+
if (opts.interactiveOnly) extras.interactiveOnly = true;
|
|
1569
|
+
if (opts.root) extras.rootRole = opts.root;
|
|
1570
|
+
if (opts.includeSubframes) extras.includeSubframes = true;
|
|
1571
|
+
await run("chrome_ax", withBase(opts, extras));
|
|
1532
1572
|
});
|
|
1533
1573
|
tabOpt(
|
|
1534
1574
|
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(
|
|
@@ -1544,9 +1584,7 @@ Notes:
|
|
|
1544
1584
|
`
|
|
1545
1585
|
)
|
|
1546
1586
|
).action(async (opts) => {
|
|
1547
|
-
|
|
1548
|
-
args.node = opts.node;
|
|
1549
|
-
await run("chrome_click_ax", args);
|
|
1587
|
+
await run("chrome_click_ax", withBase(opts, { node: opts.node }));
|
|
1550
1588
|
});
|
|
1551
1589
|
const screencast = program.command("screencast").description("Record a tab via CDP (paint-driven). Requires an active tab.").addHelpText(
|
|
1552
1590
|
"after",
|
|
@@ -1571,20 +1609,18 @@ Notes:
|
|
|
1571
1609
|
tabOpt(
|
|
1572
1610
|
screencast.command("start").description("Begin screencast capture on a tab.").option("--format <fmt>", "jpeg | png (default jpeg)").option("--quality <n>", "jpeg quality 0-100 (default 80)", (v) => Number(v)).option("--max-width <px>", "downscale; aspect preserved", (v) => Number(v)).option("--max-height <px>", "downscale; aspect preserved", (v) => Number(v)).option("--every-nth <n>", "throttle: keep 1 in N frames (default 1)", (v) => Number(v))
|
|
1573
1611
|
).action(async (opts) => {
|
|
1574
|
-
const
|
|
1575
|
-
|
|
1576
|
-
if (opts.
|
|
1577
|
-
if (typeof opts.
|
|
1578
|
-
if (typeof opts.
|
|
1579
|
-
if (typeof opts.
|
|
1580
|
-
|
|
1581
|
-
await run("chrome_screencast", args);
|
|
1612
|
+
const extras = { action: "start" };
|
|
1613
|
+
if (opts.format) extras.format = opts.format;
|
|
1614
|
+
if (typeof opts.quality === "number") extras.quality = opts.quality;
|
|
1615
|
+
if (typeof opts.maxWidth === "number") extras.maxWidth = opts.maxWidth;
|
|
1616
|
+
if (typeof opts.maxHeight === "number") extras.maxHeight = opts.maxHeight;
|
|
1617
|
+
if (typeof opts.everyNth === "number") extras.everyNthFrame = opts.everyNth;
|
|
1618
|
+
await run("chrome_screencast", withBase(opts, extras));
|
|
1582
1619
|
});
|
|
1583
1620
|
tabOpt(
|
|
1584
1621
|
screencast.command("stop").description("Stop the screencast and emit frames (or write to disk).").option("-o, --out <dir>", "write frames as JPEGs into this directory (created if missing)").option("--gif", "after writing frames, ffmpeg them into <dir>.gif (requires ffmpeg on PATH)").option("--mp4", "after writing frames, ffmpeg them into <dir>.mp4 (requires ffmpeg on PATH)").option("--fps <n>", "assumed framerate when invoking ffmpeg (default 15)", (v) => Number(v)).option("--no-dedupe", "keep raw frames; default collapses consecutive identical frames via SHA-256").option("--allow-missing-ffmpeg", "with --gif/--mp4: skip ffmpeg step (and emit a warning) if ffmpeg isn't on PATH, instead of failing with external_dependency_missing")
|
|
1585
1622
|
).action(async (opts) => {
|
|
1586
|
-
const args = { action: "stop" };
|
|
1587
|
-
Object.assign(args, baseArgs(opts));
|
|
1623
|
+
const args = withBase(opts, { action: "stop" });
|
|
1588
1624
|
try {
|
|
1589
1625
|
const result = await callTool("chrome_screencast", args);
|
|
1590
1626
|
if (!opts.out) {
|
|
@@ -1719,7 +1755,7 @@ function netFilterArgs(opts) {
|
|
|
1719
1755
|
return a;
|
|
1720
1756
|
}
|
|
1721
1757
|
function registerSessions(ctx) {
|
|
1722
|
-
const { program,
|
|
1758
|
+
const { program, withBase, run } = ctx;
|
|
1723
1759
|
const viewport = program.command("viewport").description("Emulate device viewport, DPR, mobile flag, touch, and user agent.").addHelpText(
|
|
1724
1760
|
"after",
|
|
1725
1761
|
`
|
|
@@ -1740,27 +1776,22 @@ Notes:
|
|
|
1740
1776
|
tabOpt(
|
|
1741
1777
|
viewport.command("set").description("Apply explicit viewport dimensions.").requiredOption("--width <px>", "viewport width in CSS pixels", (v) => Number(v)).requiredOption("--height <px>", "viewport height in CSS pixels", (v) => Number(v)).option("--dpr <ratio>", "device pixel ratio (1, 2, 3...)", (v) => Number(v)).option("--mobile", "set the mobile flag (affects meta viewport interpretation)").option("--touch", "enable touch event emulation").option("--user-agent <ua>", "override the User-Agent header")
|
|
1742
1778
|
).action(async (opts) => {
|
|
1743
|
-
const
|
|
1744
|
-
|
|
1745
|
-
if (opts.
|
|
1746
|
-
if (opts.
|
|
1747
|
-
if (opts.
|
|
1748
|
-
|
|
1749
|
-
await run("chrome_viewport", args);
|
|
1779
|
+
const extras = { action: "set", width: opts.width, height: opts.height };
|
|
1780
|
+
if (opts.dpr !== void 0) extras.dpr = opts.dpr;
|
|
1781
|
+
if (opts.mobile) extras.mobile = true;
|
|
1782
|
+
if (opts.touch) extras.hasTouch = true;
|
|
1783
|
+
if (opts.userAgent) extras.userAgent = opts.userAgent;
|
|
1784
|
+
await run("chrome_viewport", withBase(opts, extras));
|
|
1750
1785
|
});
|
|
1751
1786
|
tabOpt(
|
|
1752
1787
|
viewport.command("preset <name>").description("Apply a named device preset (iphone-14, pixel-7, desktop-1440, etc).")
|
|
1753
1788
|
).action(async (name, opts) => {
|
|
1754
|
-
|
|
1755
|
-
Object.assign(args, baseArgs(opts));
|
|
1756
|
-
await run("chrome_viewport", args);
|
|
1789
|
+
await run("chrome_viewport", withBase(opts, { action: "preset", name }));
|
|
1757
1790
|
});
|
|
1758
1791
|
tabOpt(
|
|
1759
1792
|
viewport.command("clear").description("Drop the viewport override and return the tab to its native size.")
|
|
1760
1793
|
).action(async (opts) => {
|
|
1761
|
-
|
|
1762
|
-
Object.assign(args, baseArgs(opts));
|
|
1763
|
-
await run("chrome_viewport", args);
|
|
1794
|
+
await run("chrome_viewport", withBase(opts, { action: "clear" }));
|
|
1764
1795
|
});
|
|
1765
1796
|
viewport.command("list").description("List available presets.").action(async () => {
|
|
1766
1797
|
await run("chrome_viewport", { action: "list" });
|
|
@@ -1821,8 +1852,7 @@ Notes:
|
|
|
1821
1852
|
`
|
|
1822
1853
|
);
|
|
1823
1854
|
group.command("create <name>").description("Group existing tabs into a new tab-group bound to <name>.").requiredOption("--tabs <ids>", "comma-separated tab IDs to group, e.g. 123,456,789").option("--color <color>", "grey | blue | red | yellow | green | pink | purple | cyan | orange").option("--collapsed", "create the group in its collapsed state").action(async (name, opts) => {
|
|
1824
|
-
const args = { action: "create", name };
|
|
1825
|
-
args.tabIds = String(opts.tabs).split(",").map((s) => Number(s.trim())).filter(Number.isFinite);
|
|
1855
|
+
const args = { action: "create", name, tabIds: String(opts.tabs) };
|
|
1826
1856
|
if (opts.color) args.color = opts.color;
|
|
1827
1857
|
if (opts.collapsed) args.collapsed = true;
|
|
1828
1858
|
await run("chrome_group", args);
|
|
@@ -1834,12 +1864,10 @@ Notes:
|
|
|
1834
1864
|
await run("chrome_group", { action: "close", name });
|
|
1835
1865
|
});
|
|
1836
1866
|
group.command("add <name>").description("Add existing tabs to an existing tab-group.").requiredOption("--tabs <ids>", "comma-separated tab IDs to add").action(async (name, opts) => {
|
|
1837
|
-
|
|
1838
|
-
await run("chrome_group", { action: "add", name, tabIds });
|
|
1867
|
+
await run("chrome_group", { action: "add", name, tabIds: String(opts.tabs) });
|
|
1839
1868
|
});
|
|
1840
1869
|
group.command("remove").description("Ungroup specific tabs (they remain open, just outside any tab-group).").requiredOption("--tabs <ids>", "comma-separated tab IDs to ungroup").action(async (opts) => {
|
|
1841
|
-
|
|
1842
|
-
await run("chrome_group", { action: "remove", tabIds });
|
|
1870
|
+
await run("chrome_group", { action: "remove", tabIds: String(opts.tabs) });
|
|
1843
1871
|
});
|
|
1844
1872
|
const network = tabOpt(netFilterOpts(
|
|
1845
1873
|
program.command("network").description("Capture HTTP request/response metadata. Ring buffer, last 200 per tab.")
|
|
@@ -1869,41 +1897,38 @@ Notes:
|
|
|
1869
1897
|
WebSocket frames and SSE streams are out of scope.
|
|
1870
1898
|
`
|
|
1871
1899
|
).action(async (opts) => {
|
|
1872
|
-
|
|
1873
|
-
await run("chrome_network", args);
|
|
1900
|
+
await run("chrome_network", withBase(opts, netFilterArgs(opts)));
|
|
1874
1901
|
});
|
|
1875
1902
|
tabOpt(netFilterOpts(
|
|
1876
1903
|
network.command("read").description("(alias) list captured network entries.")
|
|
1877
1904
|
)).action(async (opts) => {
|
|
1878
|
-
|
|
1879
|
-
await run("chrome_network", args);
|
|
1905
|
+
await run("chrome_network", withBase(opts, netFilterArgs(opts)));
|
|
1880
1906
|
});
|
|
1881
1907
|
tabOpt(
|
|
1882
1908
|
network.command("body <requestId>").description("Fetch the response body for one request (lazy; may fail if GC'd).").option("--head <bytes>", "truncate to first N bytes", (v) => Number(v)).option("--full", "return the full body \u2014 default truncates to 8 KB")
|
|
1883
1909
|
).action(async (requestId, opts) => {
|
|
1884
|
-
const
|
|
1885
|
-
if (opts.full)
|
|
1886
|
-
if (typeof opts.head === "number")
|
|
1887
|
-
await run("chrome_network",
|
|
1910
|
+
const extras = { action: "body", requestId };
|
|
1911
|
+
if (opts.full) extras.full = true;
|
|
1912
|
+
if (typeof opts.head === "number") extras.head = opts.head;
|
|
1913
|
+
await run("chrome_network", withBase(opts, extras));
|
|
1888
1914
|
});
|
|
1889
1915
|
tabOpt(netFilterOpts(
|
|
1890
1916
|
network.command("har").description("Emit HAR-compatible JSON for the captured entries.").option("--with-bodies", "fetch response bodies before emitting; strict by default \u2014 fails if any body cannot be fetched").option("--best-effort-bodies", "with --with-bodies: keep the HAR even when some bodies are missing/errored (legacy behavior); per-entry _chrome_relay.bodyState/bodyError records what failed")
|
|
1891
1917
|
)).action(async (opts) => {
|
|
1892
|
-
const
|
|
1893
|
-
if (opts.withBodies)
|
|
1894
|
-
if (opts.bestEffortBodies)
|
|
1918
|
+
const extras = { ...netFilterArgs(opts), action: "har" };
|
|
1919
|
+
if (opts.withBodies) extras.withBodies = true;
|
|
1920
|
+
if (opts.bestEffortBodies) extras.bestEffortBodies = true;
|
|
1895
1921
|
if (!opts.withBodies) {
|
|
1896
1922
|
process.stderr.write(
|
|
1897
1923
|
"[chrome-relay] HAR exported WITHOUT response bodies. Pass --with-bodies to include them (strict by default; add --best-effort-bodies to allow per-entry misses).\n"
|
|
1898
1924
|
);
|
|
1899
1925
|
}
|
|
1900
|
-
await run("chrome_network",
|
|
1926
|
+
await run("chrome_network", withBase(opts, extras));
|
|
1901
1927
|
});
|
|
1902
1928
|
tabOpt(
|
|
1903
1929
|
network.command("clear").description("Wipe the network buffer for this tab.")
|
|
1904
1930
|
).action(async (opts) => {
|
|
1905
|
-
|
|
1906
|
-
await run("chrome_network", args);
|
|
1931
|
+
await run("chrome_network", withBase(opts, { action: "clear" }));
|
|
1907
1932
|
});
|
|
1908
1933
|
tabOpt(
|
|
1909
1934
|
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(
|
|
@@ -1923,12 +1948,12 @@ Notes:
|
|
|
1923
1948
|
`
|
|
1924
1949
|
)
|
|
1925
1950
|
).action(async (opts) => {
|
|
1926
|
-
const
|
|
1927
|
-
if (opts.clear)
|
|
1928
|
-
if (opts.level)
|
|
1929
|
-
if (typeof opts.since === "number")
|
|
1930
|
-
if (typeof opts.limit === "number")
|
|
1931
|
-
await run("chrome_console",
|
|
1951
|
+
const extras = {};
|
|
1952
|
+
if (opts.clear) extras.action = "clear";
|
|
1953
|
+
if (opts.level) extras.levels = opts.level;
|
|
1954
|
+
if (typeof opts.since === "number") extras.since = opts.since;
|
|
1955
|
+
if (typeof opts.limit === "number") extras.limit = opts.limit;
|
|
1956
|
+
await run("chrome_console", withBase(opts, extras));
|
|
1932
1957
|
});
|
|
1933
1958
|
}
|
|
1934
1959
|
|
|
@@ -1955,9 +1980,11 @@ Notes:
|
|
|
1955
1980
|
Tools attach via CDP and run on backgrounded tabs without stealing focus.
|
|
1956
1981
|
`
|
|
1957
1982
|
);
|
|
1983
|
+
const baseArgs = makeBaseArgs(program);
|
|
1958
1984
|
const ctx = {
|
|
1959
1985
|
program,
|
|
1960
|
-
baseArgs
|
|
1986
|
+
baseArgs,
|
|
1987
|
+
withBase: makeWithBase(baseArgs),
|
|
1961
1988
|
run: runTool
|
|
1962
1989
|
};
|
|
1963
1990
|
registerInstallUpdate(program);
|
package/dist/index.js
CHANGED
package/dist/native-host.js
CHANGED
|
@@ -48,7 +48,7 @@ function toBridgeError(unknownErr, fallbackTool) {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
// src/index.ts
|
|
51
|
-
var CHROME_RELAY_VERSION = true ? "0.5.
|
|
51
|
+
var CHROME_RELAY_VERSION = true ? "0.5.14" : "0.0.0-dev";
|
|
52
52
|
|
|
53
53
|
// src/release-notes.ts
|
|
54
54
|
function compareSemver(a, b) {
|