chrome-relay 0.5.12 → 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 +660 -128
- 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,26 +103,38 @@ 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
|
-
if (Number.isFinite(n))
|
|
79
|
-
|
|
109
|
+
if (!Number.isFinite(n)) {
|
|
110
|
+
throw new RelayError({
|
|
111
|
+
code: "invalid_arguments",
|
|
112
|
+
message: `chrome_navigate: invalid tabId ${JSON.stringify(obj.tabId)}. Expected a number.`,
|
|
113
|
+
tool: TOOL_NAMES.NAVIGATE,
|
|
114
|
+
phase: "parse_arguments",
|
|
115
|
+
details: { field: "tabId", received: obj.tabId },
|
|
116
|
+
retryable: false
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
out.tabId = n;
|
|
80
120
|
} else {
|
|
81
|
-
const n = optNumber(obj, "tabId");
|
|
121
|
+
const n = optNumber(obj, "tabId", TOOL_NAMES.NAVIGATE);
|
|
82
122
|
if (n !== void 0)
|
|
83
123
|
out.tabId = n;
|
|
84
124
|
}
|
|
85
|
-
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);
|
|
86
132
|
if (newTab !== void 0)
|
|
87
133
|
out.newTab = newTab;
|
|
88
|
-
const active = optBool(obj, "active");
|
|
134
|
+
const active = optBool(obj, "active", TOOL_NAMES.NAVIGATE);
|
|
89
135
|
if (active !== void 0)
|
|
90
136
|
out.active = active;
|
|
91
|
-
const allowPartial = optBool(obj, "allowPartial");
|
|
137
|
+
const allowPartial = optBool(obj, "allowPartial", TOOL_NAMES.NAVIGATE);
|
|
92
138
|
if (allowPartial !== void 0)
|
|
93
139
|
out.allowPartial = allowPartial;
|
|
94
140
|
void optString;
|
|
@@ -105,13 +151,13 @@ var init_navigate = __esm({
|
|
|
105
151
|
// ../protocol/dist/args/hover.js
|
|
106
152
|
function parseChromeHoverArgs(input) {
|
|
107
153
|
const obj = asObject(input, TOOL_NAMES.HOVER);
|
|
108
|
-
const target = parseTargetArgs(obj);
|
|
109
|
-
const x = optNumber(obj, "x");
|
|
110
|
-
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);
|
|
111
157
|
if (x !== void 0 && y !== void 0) {
|
|
112
158
|
return { ...target, kind: "coords", x, y };
|
|
113
159
|
}
|
|
114
|
-
const selector = optString(obj, "selector");
|
|
160
|
+
const selector = optString(obj, "selector", TOOL_NAMES.HOVER);
|
|
115
161
|
if (selector) {
|
|
116
162
|
return { ...target, kind: "selector", selector };
|
|
117
163
|
}
|
|
@@ -226,6 +272,475 @@ var init_network = __esm({
|
|
|
226
272
|
}
|
|
227
273
|
});
|
|
228
274
|
|
|
275
|
+
// ../protocol/dist/args/simple.js
|
|
276
|
+
function parseGetWindowsAndTabsArgs(_input) {
|
|
277
|
+
return {};
|
|
278
|
+
}
|
|
279
|
+
function parseChromeSelfReloadArgs(_input) {
|
|
280
|
+
return {};
|
|
281
|
+
}
|
|
282
|
+
function parseChromeReadPageArgs(input) {
|
|
283
|
+
const obj = asObject(input, TOOL_NAMES.READ_PAGE);
|
|
284
|
+
const out = { ...parseTargetArgs(obj) };
|
|
285
|
+
const io = optBool(obj, "interactiveOnly");
|
|
286
|
+
if (io !== void 0)
|
|
287
|
+
out.interactiveOnly = io;
|
|
288
|
+
return out;
|
|
289
|
+
}
|
|
290
|
+
function parseChromeClickArgs(input) {
|
|
291
|
+
const obj = asObject(input, TOOL_NAMES.CLICK);
|
|
292
|
+
return {
|
|
293
|
+
selector: requireString(obj, "selector", TOOL_NAMES.CLICK),
|
|
294
|
+
...parseTargetArgs(obj)
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
function parseChromeFillArgs(input) {
|
|
298
|
+
const obj = asObject(input, TOOL_NAMES.FILL);
|
|
299
|
+
if (typeof obj.value !== "string") {
|
|
300
|
+
throw new RelayError({
|
|
301
|
+
code: "invalid_arguments",
|
|
302
|
+
message: `${TOOL_NAMES.FILL}: \`value\` is required and must be a string (empty string allowed).`,
|
|
303
|
+
tool: TOOL_NAMES.FILL,
|
|
304
|
+
phase: "parse_arguments",
|
|
305
|
+
details: { field: "value", received: obj.value },
|
|
306
|
+
retryable: false
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
selector: requireString(obj, "selector", TOOL_NAMES.FILL),
|
|
311
|
+
value: obj.value,
|
|
312
|
+
...parseTargetArgs(obj)
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
function parseChromeKeyboardArgs(input) {
|
|
316
|
+
const obj = asObject(input, TOOL_NAMES.KEYBOARD);
|
|
317
|
+
return {
|
|
318
|
+
keys: requireString(obj, "keys", TOOL_NAMES.KEYBOARD),
|
|
319
|
+
...parseTargetArgs(obj)
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
function parseChromeTypeArgs(input) {
|
|
323
|
+
const obj = asObject(input, TOOL_NAMES.TYPE);
|
|
324
|
+
const out = {
|
|
325
|
+
text: requireString(obj, "text", TOOL_NAMES.TYPE),
|
|
326
|
+
...parseTargetArgs(obj)
|
|
327
|
+
};
|
|
328
|
+
const selector = optString(obj, "selector");
|
|
329
|
+
if (selector)
|
|
330
|
+
out.selector = selector;
|
|
331
|
+
return out;
|
|
332
|
+
}
|
|
333
|
+
function parseChromeEvaluateArgs(input) {
|
|
334
|
+
const obj = asObject(input, TOOL_NAMES.EVALUATE);
|
|
335
|
+
const out = {
|
|
336
|
+
code: requireString(obj, "code", TOOL_NAMES.EVALUATE),
|
|
337
|
+
...parseTargetArgs(obj)
|
|
338
|
+
};
|
|
339
|
+
const t = optNumber(obj, "timeoutMs");
|
|
340
|
+
if (t !== void 0)
|
|
341
|
+
out.timeoutMs = t;
|
|
342
|
+
return out;
|
|
343
|
+
}
|
|
344
|
+
function parseChromeSwitchTabArgs(input) {
|
|
345
|
+
const obj = asObject(input, TOOL_NAMES.SWITCH_TAB);
|
|
346
|
+
const tabId = Number(obj.tabId);
|
|
347
|
+
if (!Number.isFinite(tabId)) {
|
|
348
|
+
throw new RelayError({
|
|
349
|
+
code: "invalid_arguments",
|
|
350
|
+
message: `${TOOL_NAMES.SWITCH_TAB} requires a numeric tabId.`,
|
|
351
|
+
tool: TOOL_NAMES.SWITCH_TAB,
|
|
352
|
+
phase: "parse_arguments",
|
|
353
|
+
details: { received: obj.tabId },
|
|
354
|
+
retryable: false
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
return { tabId };
|
|
358
|
+
}
|
|
359
|
+
function parseChromeCloseTabsArgs(input) {
|
|
360
|
+
const obj = asObject(input, TOOL_NAMES.CLOSE_TABS);
|
|
361
|
+
if (!Array.isArray(obj.tabIds)) {
|
|
362
|
+
throw new RelayError({
|
|
363
|
+
code: "invalid_arguments",
|
|
364
|
+
message: `${TOOL_NAMES.CLOSE_TABS} requires a numeric tabIds array.`,
|
|
365
|
+
tool: TOOL_NAMES.CLOSE_TABS,
|
|
366
|
+
phase: "parse_arguments",
|
|
367
|
+
details: { field: "tabIds", received: obj.tabIds },
|
|
368
|
+
retryable: false
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
const tabIds = obj.tabIds.map((v) => Number(v));
|
|
372
|
+
if (tabIds.length === 0 || tabIds.some((n) => !Number.isFinite(n))) {
|
|
373
|
+
throw new RelayError({
|
|
374
|
+
code: "invalid_arguments",
|
|
375
|
+
message: `${TOOL_NAMES.CLOSE_TABS} requires a non-empty array of numeric tab IDs.`,
|
|
376
|
+
tool: TOOL_NAMES.CLOSE_TABS,
|
|
377
|
+
phase: "parse_arguments",
|
|
378
|
+
details: { received: obj.tabIds },
|
|
379
|
+
retryable: false
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
return { tabIds };
|
|
383
|
+
}
|
|
384
|
+
function parseChromeAxArgs(input) {
|
|
385
|
+
const obj = asObject(input, TOOL_NAMES.AX);
|
|
386
|
+
const out = { ...parseTargetArgs(obj) };
|
|
387
|
+
const io = optBool(obj, "interactiveOnly");
|
|
388
|
+
if (io !== void 0)
|
|
389
|
+
out.interactiveOnly = io;
|
|
390
|
+
const root = optString(obj, "rootRole");
|
|
391
|
+
if (root)
|
|
392
|
+
out.rootRole = root;
|
|
393
|
+
const is = optBool(obj, "includeSubframes");
|
|
394
|
+
if (is !== void 0)
|
|
395
|
+
out.includeSubframes = is;
|
|
396
|
+
return out;
|
|
397
|
+
}
|
|
398
|
+
function parseChromeClickAxArgs(input) {
|
|
399
|
+
const obj = asObject(input, TOOL_NAMES.CLICK_AX);
|
|
400
|
+
const node = Number(obj.node ?? obj.id);
|
|
401
|
+
if (!Number.isFinite(node) || node <= 0) {
|
|
402
|
+
throw new RelayError({
|
|
403
|
+
code: "invalid_arguments",
|
|
404
|
+
message: `${TOOL_NAMES.CLICK_AX} requires \`node\` (a positive backendDOMNodeId from chrome_ax).`,
|
|
405
|
+
tool: TOOL_NAMES.CLICK_AX,
|
|
406
|
+
phase: "parse_arguments",
|
|
407
|
+
details: { received: obj.node ?? obj.id },
|
|
408
|
+
retryable: false
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
return { node, ...parseTargetArgs(obj) };
|
|
412
|
+
}
|
|
413
|
+
function parseChromeScreenshotArgs(input) {
|
|
414
|
+
const obj = asObject(input, TOOL_NAMES.SCREENSHOT);
|
|
415
|
+
const out = { ...parseTargetArgs(obj) };
|
|
416
|
+
const fp = optBool(obj, "fullPage");
|
|
417
|
+
if (fp !== void 0)
|
|
418
|
+
out.fullPage = fp;
|
|
419
|
+
const bbox = optString(obj, "bbox");
|
|
420
|
+
if (bbox)
|
|
421
|
+
out.bbox = bbox;
|
|
422
|
+
const sel = optString(obj, "selector");
|
|
423
|
+
if (sel)
|
|
424
|
+
out.selector = sel;
|
|
425
|
+
const pad = optNumber(obj, "padding");
|
|
426
|
+
if (pad !== void 0)
|
|
427
|
+
out.padding = pad;
|
|
428
|
+
const me = optNumber(obj, "maxEdge");
|
|
429
|
+
if (me !== void 0 && me > 0)
|
|
430
|
+
out.maxEdge = me;
|
|
431
|
+
return out;
|
|
432
|
+
}
|
|
433
|
+
var init_simple = __esm({
|
|
434
|
+
"../protocol/dist/args/simple.js"() {
|
|
435
|
+
"use strict";
|
|
436
|
+
init_dist();
|
|
437
|
+
init_shared();
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// ../protocol/dist/args/multi.js
|
|
442
|
+
function invalidAction(tool, received, expected) {
|
|
443
|
+
throw new RelayError({
|
|
444
|
+
code: "invalid_arguments",
|
|
445
|
+
message: `${tool}: unknown action ${JSON.stringify(received)}. Expected ${expected.join(" | ")}.`,
|
|
446
|
+
tool,
|
|
447
|
+
phase: "parse_action",
|
|
448
|
+
details: { received, validChoices: expected },
|
|
449
|
+
retryable: false
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
function parseChromeViewportArgs(input) {
|
|
453
|
+
const obj = asObject(input, TOOL_NAMES.VIEWPORT);
|
|
454
|
+
const action = typeof obj.action === "string" ? obj.action : "set";
|
|
455
|
+
if (!VALID_VIEWPORT_ACTIONS.includes(action)) {
|
|
456
|
+
invalidAction(TOOL_NAMES.VIEWPORT, action, VALID_VIEWPORT_ACTIONS);
|
|
457
|
+
}
|
|
458
|
+
const target = parseTargetArgs(obj);
|
|
459
|
+
if (action === "list")
|
|
460
|
+
return { action: "list" };
|
|
461
|
+
if (action === "clear")
|
|
462
|
+
return { ...target, action: "clear" };
|
|
463
|
+
if (action === "preset") {
|
|
464
|
+
return { ...target, action: "preset", name: requireString(obj, "name", TOOL_NAMES.VIEWPORT) };
|
|
465
|
+
}
|
|
466
|
+
const width = Number(obj.width);
|
|
467
|
+
const height = Number(obj.height);
|
|
468
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
|
|
469
|
+
throw new RelayError({
|
|
470
|
+
code: "invalid_arguments",
|
|
471
|
+
message: `${TOOL_NAMES.VIEWPORT} set requires positive numeric width and height.`,
|
|
472
|
+
tool: TOOL_NAMES.VIEWPORT,
|
|
473
|
+
phase: "parse_dimensions",
|
|
474
|
+
details: { received: { width: obj.width, height: obj.height } },
|
|
475
|
+
retryable: false
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
const out = {
|
|
479
|
+
...target,
|
|
480
|
+
action: "set",
|
|
481
|
+
width,
|
|
482
|
+
height
|
|
483
|
+
};
|
|
484
|
+
const dpr = optNumber(obj, "dpr");
|
|
485
|
+
if (dpr !== void 0)
|
|
486
|
+
out.dpr = dpr;
|
|
487
|
+
const mobile = optBool(obj, "mobile");
|
|
488
|
+
if (mobile !== void 0)
|
|
489
|
+
out.mobile = mobile;
|
|
490
|
+
const hasTouch = optBool(obj, "hasTouch");
|
|
491
|
+
if (hasTouch !== void 0)
|
|
492
|
+
out.hasTouch = hasTouch;
|
|
493
|
+
const userAgent = optString(obj, "userAgent");
|
|
494
|
+
if (userAgent)
|
|
495
|
+
out.userAgent = userAgent;
|
|
496
|
+
return out;
|
|
497
|
+
}
|
|
498
|
+
function parseLevels(input) {
|
|
499
|
+
if (input === void 0 || input === null)
|
|
500
|
+
return void 0;
|
|
501
|
+
const valid = new Set(VALID_CONSOLE_LEVELS);
|
|
502
|
+
const verify = (s) => {
|
|
503
|
+
if (typeof s !== "string" || !valid.has(s)) {
|
|
504
|
+
throw new RelayError({
|
|
505
|
+
code: "invalid_arguments",
|
|
506
|
+
message: `${TOOL_NAMES.CONSOLE}: invalid level ${JSON.stringify(s)}. Expected one of: ${VALID_CONSOLE_LEVELS.join(", ")}.`,
|
|
507
|
+
tool: TOOL_NAMES.CONSOLE,
|
|
508
|
+
phase: "parse_levels",
|
|
509
|
+
details: { received: s, validChoices: VALID_CONSOLE_LEVELS },
|
|
510
|
+
retryable: false
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
return s;
|
|
514
|
+
};
|
|
515
|
+
if (typeof input === "string")
|
|
516
|
+
return input.split(",").map((s) => verify(s.trim()));
|
|
517
|
+
if (Array.isArray(input))
|
|
518
|
+
return input.map(verify);
|
|
519
|
+
throw new RelayError({
|
|
520
|
+
code: "invalid_arguments",
|
|
521
|
+
message: `${TOOL_NAMES.CONSOLE}: invalid levels argument ${JSON.stringify(input)}. Expected a comma-separated string or an array of strings.`,
|
|
522
|
+
tool: TOOL_NAMES.CONSOLE,
|
|
523
|
+
phase: "parse_levels",
|
|
524
|
+
details: { received: input },
|
|
525
|
+
retryable: false
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
function parseChromeConsoleArgs(input) {
|
|
529
|
+
const obj = asObject(input, TOOL_NAMES.CONSOLE);
|
|
530
|
+
const target = parseTargetArgs(obj);
|
|
531
|
+
const action = typeof obj.action === "string" ? obj.action : "read";
|
|
532
|
+
if (!VALID_CONSOLE_ACTIONS.includes(action)) {
|
|
533
|
+
invalidAction(TOOL_NAMES.CONSOLE, action, VALID_CONSOLE_ACTIONS);
|
|
534
|
+
}
|
|
535
|
+
if (action === "clear")
|
|
536
|
+
return { ...target, action: "clear" };
|
|
537
|
+
const out = { ...target, action: "read" };
|
|
538
|
+
const levels = parseLevels(obj.levels);
|
|
539
|
+
if (levels)
|
|
540
|
+
out.levels = levels;
|
|
541
|
+
const since = optNumber(obj, "since");
|
|
542
|
+
if (since !== void 0)
|
|
543
|
+
out.since = since;
|
|
544
|
+
const limit = optNumber(obj, "limit");
|
|
545
|
+
if (limit !== void 0)
|
|
546
|
+
out.limit = limit;
|
|
547
|
+
return out;
|
|
548
|
+
}
|
|
549
|
+
function parseChromeWorkspaceArgs(input) {
|
|
550
|
+
const obj = asObject(input, TOOL_NAMES.WORKSPACE);
|
|
551
|
+
const action = typeof obj.action === "string" ? obj.action : "list";
|
|
552
|
+
if (!VALID_WORKSPACE_ACTIONS.includes(action)) {
|
|
553
|
+
invalidAction(TOOL_NAMES.WORKSPACE, action, VALID_WORKSPACE_ACTIONS);
|
|
554
|
+
}
|
|
555
|
+
if (action === "list")
|
|
556
|
+
return { action: "list" };
|
|
557
|
+
if (action === "close")
|
|
558
|
+
return { action: "close", name: requireString(obj, "name", TOOL_NAMES.WORKSPACE) };
|
|
559
|
+
const out = {
|
|
560
|
+
action: "create",
|
|
561
|
+
name: requireString(obj, "name", TOOL_NAMES.WORKSPACE)
|
|
562
|
+
};
|
|
563
|
+
const url = optString(obj, "url");
|
|
564
|
+
if (url)
|
|
565
|
+
out.url = url;
|
|
566
|
+
const label = optString(obj, "label");
|
|
567
|
+
if (label)
|
|
568
|
+
out.label = label;
|
|
569
|
+
return out;
|
|
570
|
+
}
|
|
571
|
+
function parseTabIds(raw) {
|
|
572
|
+
const reject = (bad) => {
|
|
573
|
+
throw new RelayError({
|
|
574
|
+
code: "invalid_arguments",
|
|
575
|
+
message: `${TOOL_NAMES.GROUP}: invalid tabId ${JSON.stringify(bad)}. Expected a number or a comma-separated list of numbers.`,
|
|
576
|
+
tool: TOOL_NAMES.GROUP,
|
|
577
|
+
phase: "parse_tab_ids",
|
|
578
|
+
details: { received: bad },
|
|
579
|
+
retryable: false
|
|
580
|
+
});
|
|
581
|
+
};
|
|
582
|
+
const coerce = (v) => {
|
|
583
|
+
const n = Number(typeof v === "string" ? v.trim() : v);
|
|
584
|
+
if (!Number.isFinite(n))
|
|
585
|
+
reject(v);
|
|
586
|
+
return n;
|
|
587
|
+
};
|
|
588
|
+
if (Array.isArray(raw))
|
|
589
|
+
return raw.map(coerce);
|
|
590
|
+
if (typeof raw === "string")
|
|
591
|
+
return raw.split(",").map(coerce);
|
|
592
|
+
if (typeof raw === "number")
|
|
593
|
+
return [raw];
|
|
594
|
+
return [];
|
|
595
|
+
}
|
|
596
|
+
function parseColor(raw) {
|
|
597
|
+
if (raw === void 0 || raw === null)
|
|
598
|
+
return void 0;
|
|
599
|
+
if (typeof raw !== "string") {
|
|
600
|
+
throw new RelayError({
|
|
601
|
+
code: "invalid_arguments",
|
|
602
|
+
message: `${TOOL_NAMES.GROUP}: invalid color ${JSON.stringify(raw)}. Expected one of: ${VALID_GROUP_COLORS.join(", ")}.`,
|
|
603
|
+
tool: TOOL_NAMES.GROUP,
|
|
604
|
+
phase: "parse_color",
|
|
605
|
+
details: { received: raw, validChoices: VALID_GROUP_COLORS },
|
|
606
|
+
retryable: false
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
const c = raw.toLowerCase();
|
|
610
|
+
if (!VALID_GROUP_COLORS.includes(c)) {
|
|
611
|
+
throw new RelayError({
|
|
612
|
+
code: "invalid_arguments",
|
|
613
|
+
message: `${TOOL_NAMES.GROUP}: invalid color "${raw}". Expected one of: ${VALID_GROUP_COLORS.join(", ")}.`,
|
|
614
|
+
tool: TOOL_NAMES.GROUP,
|
|
615
|
+
phase: "parse_color",
|
|
616
|
+
details: { received: raw, validChoices: VALID_GROUP_COLORS },
|
|
617
|
+
retryable: false
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
return c;
|
|
621
|
+
}
|
|
622
|
+
function parseChromeGroupArgs(input) {
|
|
623
|
+
const obj = asObject(input, TOOL_NAMES.GROUP);
|
|
624
|
+
const action = typeof obj.action === "string" ? obj.action : "list";
|
|
625
|
+
if (!VALID_GROUP_ACTIONS.includes(action)) {
|
|
626
|
+
invalidAction(TOOL_NAMES.GROUP, action, VALID_GROUP_ACTIONS);
|
|
627
|
+
}
|
|
628
|
+
if (action === "list")
|
|
629
|
+
return { action: "list" };
|
|
630
|
+
if (action === "close")
|
|
631
|
+
return { action: "close", name: requireString(obj, "name", TOOL_NAMES.GROUP) };
|
|
632
|
+
if (action === "remove") {
|
|
633
|
+
const tabIds2 = parseTabIds(obj.tabIds);
|
|
634
|
+
if (tabIds2.length === 0) {
|
|
635
|
+
throw new RelayError({
|
|
636
|
+
code: "invalid_arguments",
|
|
637
|
+
message: `${TOOL_NAMES.GROUP} remove requires tabIds.`,
|
|
638
|
+
tool: TOOL_NAMES.GROUP,
|
|
639
|
+
phase: "parse_arguments",
|
|
640
|
+
details: { field: "tabIds" },
|
|
641
|
+
retryable: false
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
return { action: "remove", tabIds: tabIds2 };
|
|
645
|
+
}
|
|
646
|
+
if (action === "add") {
|
|
647
|
+
const tabIds2 = parseTabIds(obj.tabIds);
|
|
648
|
+
if (tabIds2.length === 0) {
|
|
649
|
+
throw new RelayError({
|
|
650
|
+
code: "invalid_arguments",
|
|
651
|
+
message: `${TOOL_NAMES.GROUP} add requires tabIds.`,
|
|
652
|
+
tool: TOOL_NAMES.GROUP,
|
|
653
|
+
phase: "parse_arguments",
|
|
654
|
+
details: { field: "tabIds" },
|
|
655
|
+
retryable: false
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
return { action: "add", name: requireString(obj, "name", TOOL_NAMES.GROUP), tabIds: tabIds2 };
|
|
659
|
+
}
|
|
660
|
+
const tabIds = parseTabIds(obj.tabIds);
|
|
661
|
+
if (tabIds.length === 0) {
|
|
662
|
+
throw new RelayError({
|
|
663
|
+
code: "invalid_arguments",
|
|
664
|
+
message: `${TOOL_NAMES.GROUP} create requires at least one tabId.`,
|
|
665
|
+
tool: TOOL_NAMES.GROUP,
|
|
666
|
+
phase: "parse_arguments",
|
|
667
|
+
details: { field: "tabIds" },
|
|
668
|
+
retryable: false
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
const out = {
|
|
672
|
+
action: "create",
|
|
673
|
+
name: requireString(obj, "name", TOOL_NAMES.GROUP),
|
|
674
|
+
tabIds
|
|
675
|
+
};
|
|
676
|
+
const color = parseColor(obj.color);
|
|
677
|
+
if (color)
|
|
678
|
+
out.color = color;
|
|
679
|
+
const collapsed = optBool(obj, "collapsed");
|
|
680
|
+
if (collapsed !== void 0)
|
|
681
|
+
out.collapsed = collapsed;
|
|
682
|
+
const windowId = optNumber(obj, "windowId");
|
|
683
|
+
if (windowId !== void 0)
|
|
684
|
+
out.windowId = windowId;
|
|
685
|
+
return out;
|
|
686
|
+
}
|
|
687
|
+
function parseChromeScreencastArgs(input) {
|
|
688
|
+
const obj = asObject(input, TOOL_NAMES.SCREENCAST);
|
|
689
|
+
const target = parseTargetArgs(obj);
|
|
690
|
+
const action = typeof obj.action === "string" ? obj.action : "start";
|
|
691
|
+
if (!VALID_SCREENCAST_ACTIONS.includes(action)) {
|
|
692
|
+
invalidAction(TOOL_NAMES.SCREENCAST, action, VALID_SCREENCAST_ACTIONS);
|
|
693
|
+
}
|
|
694
|
+
if (action === "stop")
|
|
695
|
+
return { ...target, action: "stop" };
|
|
696
|
+
const out = {
|
|
697
|
+
...target,
|
|
698
|
+
action: "start"
|
|
699
|
+
};
|
|
700
|
+
if (obj.format !== void 0 && obj.format !== null) {
|
|
701
|
+
if (obj.format !== "jpeg" && obj.format !== "png") {
|
|
702
|
+
throw new RelayError({
|
|
703
|
+
code: "invalid_arguments",
|
|
704
|
+
message: `${TOOL_NAMES.SCREENCAST}: invalid format ${JSON.stringify(obj.format)}. Expected "jpeg" or "png".`,
|
|
705
|
+
tool: TOOL_NAMES.SCREENCAST,
|
|
706
|
+
phase: "parse_format",
|
|
707
|
+
details: { received: obj.format, validChoices: VALID_SCREENCAST_FORMATS },
|
|
708
|
+
retryable: false
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
out.format = obj.format;
|
|
712
|
+
}
|
|
713
|
+
const q = optNumber(obj, "quality");
|
|
714
|
+
if (q !== void 0)
|
|
715
|
+
out.quality = q;
|
|
716
|
+
const mw = optNumber(obj, "maxWidth");
|
|
717
|
+
if (mw !== void 0)
|
|
718
|
+
out.maxWidth = mw;
|
|
719
|
+
const mh = optNumber(obj, "maxHeight");
|
|
720
|
+
if (mh !== void 0)
|
|
721
|
+
out.maxHeight = mh;
|
|
722
|
+
const en = optNumber(obj, "everyNthFrame");
|
|
723
|
+
if (en !== void 0)
|
|
724
|
+
out.everyNthFrame = en;
|
|
725
|
+
return out;
|
|
726
|
+
}
|
|
727
|
+
var VALID_VIEWPORT_ACTIONS, VALID_CONSOLE_ACTIONS, VALID_CONSOLE_LEVELS, VALID_WORKSPACE_ACTIONS, VALID_GROUP_ACTIONS, VALID_GROUP_COLORS, VALID_SCREENCAST_ACTIONS, VALID_SCREENCAST_FORMATS;
|
|
728
|
+
var init_multi = __esm({
|
|
729
|
+
"../protocol/dist/args/multi.js"() {
|
|
730
|
+
"use strict";
|
|
731
|
+
init_dist();
|
|
732
|
+
init_shared();
|
|
733
|
+
VALID_VIEWPORT_ACTIONS = ["set", "preset", "clear", "list"];
|
|
734
|
+
VALID_CONSOLE_ACTIONS = ["read", "clear"];
|
|
735
|
+
VALID_CONSOLE_LEVELS = ["log", "info", "warn", "error", "debug", "exception"];
|
|
736
|
+
VALID_WORKSPACE_ACTIONS = ["create", "list", "close"];
|
|
737
|
+
VALID_GROUP_ACTIONS = ["create", "list", "close", "add", "remove"];
|
|
738
|
+
VALID_GROUP_COLORS = ["grey", "blue", "red", "yellow", "green", "pink", "purple", "cyan", "orange"];
|
|
739
|
+
VALID_SCREENCAST_ACTIONS = ["start", "stop"];
|
|
740
|
+
VALID_SCREENCAST_FORMATS = ["jpeg", "png"];
|
|
741
|
+
}
|
|
742
|
+
});
|
|
743
|
+
|
|
229
744
|
// ../protocol/dist/args/index.js
|
|
230
745
|
var init_args = __esm({
|
|
231
746
|
"../protocol/dist/args/index.js"() {
|
|
@@ -234,6 +749,8 @@ var init_args = __esm({
|
|
|
234
749
|
init_navigate();
|
|
235
750
|
init_hover();
|
|
236
751
|
init_network();
|
|
752
|
+
init_simple();
|
|
753
|
+
init_multi();
|
|
237
754
|
}
|
|
238
755
|
});
|
|
239
756
|
|
|
@@ -253,9 +770,27 @@ __export(dist_exports, {
|
|
|
253
770
|
optBool: () => optBool,
|
|
254
771
|
optNumber: () => optNumber,
|
|
255
772
|
optString: () => optString,
|
|
773
|
+
parseChromeAxArgs: () => parseChromeAxArgs,
|
|
774
|
+
parseChromeClickArgs: () => parseChromeClickArgs,
|
|
775
|
+
parseChromeClickAxArgs: () => parseChromeClickAxArgs,
|
|
776
|
+
parseChromeCloseTabsArgs: () => parseChromeCloseTabsArgs,
|
|
777
|
+
parseChromeConsoleArgs: () => parseChromeConsoleArgs,
|
|
778
|
+
parseChromeEvaluateArgs: () => parseChromeEvaluateArgs,
|
|
779
|
+
parseChromeFillArgs: () => parseChromeFillArgs,
|
|
780
|
+
parseChromeGroupArgs: () => parseChromeGroupArgs,
|
|
256
781
|
parseChromeHoverArgs: () => parseChromeHoverArgs,
|
|
782
|
+
parseChromeKeyboardArgs: () => parseChromeKeyboardArgs,
|
|
257
783
|
parseChromeNavigateArgs: () => parseChromeNavigateArgs,
|
|
258
784
|
parseChromeNetworkArgs: () => parseChromeNetworkArgs,
|
|
785
|
+
parseChromeReadPageArgs: () => parseChromeReadPageArgs,
|
|
786
|
+
parseChromeScreencastArgs: () => parseChromeScreencastArgs,
|
|
787
|
+
parseChromeScreenshotArgs: () => parseChromeScreenshotArgs,
|
|
788
|
+
parseChromeSelfReloadArgs: () => parseChromeSelfReloadArgs,
|
|
789
|
+
parseChromeSwitchTabArgs: () => parseChromeSwitchTabArgs,
|
|
790
|
+
parseChromeTypeArgs: () => parseChromeTypeArgs,
|
|
791
|
+
parseChromeViewportArgs: () => parseChromeViewportArgs,
|
|
792
|
+
parseChromeWorkspaceArgs: () => parseChromeWorkspaceArgs,
|
|
793
|
+
parseGetWindowsAndTabsArgs: () => parseGetWindowsAndTabsArgs,
|
|
259
794
|
parseTargetArgs: () => parseTargetArgs,
|
|
260
795
|
requireString: () => requireString,
|
|
261
796
|
toBridgeError: () => toBridgeError
|
|
@@ -368,7 +903,7 @@ var init_dist = __esm({
|
|
|
368
903
|
import { Command } from "commander";
|
|
369
904
|
|
|
370
905
|
// src/index.ts
|
|
371
|
-
var CHROME_RELAY_VERSION = true ? "0.5.
|
|
906
|
+
var CHROME_RELAY_VERSION = true ? "0.5.14" : "0.0.0-dev";
|
|
372
907
|
|
|
373
908
|
// src/commands/shared.ts
|
|
374
909
|
init_dist();
|
|
@@ -416,6 +951,11 @@ async function callTool(name, args) {
|
|
|
416
951
|
}
|
|
417
952
|
|
|
418
953
|
// src/commands/shared.ts
|
|
954
|
+
function makeWithBase(baseArgs) {
|
|
955
|
+
return function withBase(opts, extras) {
|
|
956
|
+
return { ...baseArgs(opts), ...extras ?? {} };
|
|
957
|
+
};
|
|
958
|
+
}
|
|
419
959
|
function tabOpt(cmd) {
|
|
420
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`)");
|
|
421
961
|
}
|
|
@@ -602,6 +1142,22 @@ async function runDoctor() {
|
|
|
602
1142
|
|
|
603
1143
|
// src/release-notes.ts
|
|
604
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
|
+
],
|
|
1154
|
+
"0.5.13": [
|
|
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.",
|
|
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.",
|
|
1157
|
+
"All extension handlers now consume their parser at the top of the handler body \u2014 silent shape drift between CLI and extension is structurally impossible.",
|
|
1158
|
+
"Multi-action tools (viewport, console, network, workspace, group, screencast) return discriminated unions so the handler branches with TypeScript narrowing instead of `typeof args.action === 'string'` boilerplate.",
|
|
1159
|
+
"36 new tests in packages/protocol/test/args-all.test.ts. Total now 433 (was 397)."
|
|
1160
|
+
],
|
|
605
1161
|
"0.5.12": [
|
|
606
1162
|
"Protocol-owned tool arg parsers (code-quality-hardening Risk 1). New @chrome-relay/protocol exports: `parseChromeNavigateArgs`, `parseChromeHoverArgs`, `parseChromeNetworkArgs`. Each is the single source of truth for what its tool accepts \u2014 CLI and extension consume the same parser so silent shape drift can't happen.",
|
|
607
1163
|
"Pattern established with 3 representative tools (navigate, hover, network \u2014 the doc-followup explicitly named these). Remaining 19 tools are mechanical follow-up (~20 lines + tests each). Each parser throws `RelayError(invalid_arguments)` with field/received/validChoices in details.",
|
|
@@ -794,7 +1350,7 @@ function registerInstallUpdate(program) {
|
|
|
794
1350
|
|
|
795
1351
|
// src/commands/navigation.ts
|
|
796
1352
|
function registerNavigation(ctx) {
|
|
797
|
-
const { program,
|
|
1353
|
+
const { program, withBase, run } = ctx;
|
|
798
1354
|
program.command("tabs [verb]").description("List open Chrome windows and tabs. (verb 'list' is accepted as alias)").action(async (verb) => {
|
|
799
1355
|
if (verb && verb !== "list") {
|
|
800
1356
|
process.stderr.write(`unknown tabs verb: ${verb}. Use 'tabs' or 'tabs list'.
|
|
@@ -823,11 +1379,10 @@ Use "chrome-relay switch ${url}" to activate that tab, or "chrome-relay navigate
|
|
|
823
1379
|
);
|
|
824
1380
|
process.exit(1);
|
|
825
1381
|
}
|
|
826
|
-
const
|
|
827
|
-
|
|
828
|
-
if (opts.
|
|
829
|
-
|
|
830
|
-
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));
|
|
831
1386
|
});
|
|
832
1387
|
program.command("switch <tabId>").description("Activate a tab by ID.").action(async (tabId) => {
|
|
833
1388
|
await run("chrome_switch_tab", { tabId: Number(tabId) });
|
|
@@ -843,20 +1398,16 @@ Use "chrome-relay switch ${url}" to activate that tab, or "chrome-relay navigate
|
|
|
843
1398
|
|
|
844
1399
|
// src/commands/input.ts
|
|
845
1400
|
function registerInput(ctx) {
|
|
846
|
-
const { program,
|
|
1401
|
+
const { program, withBase, run } = ctx;
|
|
847
1402
|
tabOpt(
|
|
848
1403
|
program.command("click <selector>").description("Click an element by CSS selector.")
|
|
849
1404
|
).action(async (selector, opts) => {
|
|
850
|
-
|
|
851
|
-
Object.assign(args, baseArgs(opts));
|
|
852
|
-
await run("chrome_click_element", args);
|
|
1405
|
+
await run("chrome_click_element", withBase(opts, { selector }));
|
|
853
1406
|
});
|
|
854
1407
|
tabOpt(
|
|
855
1408
|
program.command("fill <selector> <value>").description("Fill an input or textarea.")
|
|
856
1409
|
).action(async (selector, value, opts) => {
|
|
857
|
-
|
|
858
|
-
Object.assign(args, baseArgs(opts));
|
|
859
|
-
await run("chrome_fill_or_select", args);
|
|
1410
|
+
await run("chrome_fill_or_select", withBase(opts, { selector, value }));
|
|
860
1411
|
});
|
|
861
1412
|
tabOpt(
|
|
862
1413
|
program.command("keys <keys>").description("Press a single key or chord via trusted CDP input (e.g. Enter, Cmd+K).").addHelpText(
|
|
@@ -873,9 +1424,7 @@ For typing text into a field, use \`chrome-relay type\` instead.
|
|
|
873
1424
|
`
|
|
874
1425
|
)
|
|
875
1426
|
).action(async (keys, opts) => {
|
|
876
|
-
|
|
877
|
-
Object.assign(args, baseArgs(opts));
|
|
878
|
-
await run("chrome_keyboard", args);
|
|
1427
|
+
await run("chrome_keyboard", withBase(opts, { keys }));
|
|
879
1428
|
});
|
|
880
1429
|
tabOpt(
|
|
881
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(
|
|
@@ -894,10 +1443,9 @@ When to pick which:
|
|
|
894
1443
|
`
|
|
895
1444
|
)
|
|
896
1445
|
).action(async (text, opts) => {
|
|
897
|
-
const
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
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));
|
|
901
1449
|
});
|
|
902
1450
|
tabOpt(
|
|
903
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(
|
|
@@ -916,10 +1464,9 @@ Notes:
|
|
|
916
1464
|
`
|
|
917
1465
|
)
|
|
918
1466
|
).action(async (code, opts) => {
|
|
919
|
-
const
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
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));
|
|
923
1470
|
});
|
|
924
1471
|
tabOpt(
|
|
925
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(
|
|
@@ -935,21 +1482,20 @@ tooltip appearance, etc.) that a bare click would skip past too quickly.
|
|
|
935
1482
|
`
|
|
936
1483
|
)
|
|
937
1484
|
).action(async (selector, opts) => {
|
|
938
|
-
const
|
|
939
|
-
|
|
940
|
-
if (selector) args.selector = selector;
|
|
1485
|
+
const extras = {};
|
|
1486
|
+
if (selector) extras.selector = selector;
|
|
941
1487
|
if (typeof opts.x === "number" && typeof opts.y === "number") {
|
|
942
|
-
|
|
943
|
-
|
|
1488
|
+
extras.x = opts.x;
|
|
1489
|
+
extras.y = opts.y;
|
|
944
1490
|
}
|
|
945
|
-
await run("chrome_hover",
|
|
1491
|
+
await run("chrome_hover", withBase(opts, extras));
|
|
946
1492
|
});
|
|
947
1493
|
}
|
|
948
1494
|
|
|
949
1495
|
// src/commands/capture.ts
|
|
950
1496
|
import { writeFileSync } from "fs";
|
|
951
1497
|
function registerCapture(ctx) {
|
|
952
|
-
const { program,
|
|
1498
|
+
const { program, withBase, run } = ctx;
|
|
953
1499
|
tabOpt(
|
|
954
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(
|
|
955
1501
|
"after",
|
|
@@ -968,13 +1514,13 @@ full-tab screenshot when an agent only needs to see one component.
|
|
|
968
1514
|
`
|
|
969
1515
|
)
|
|
970
1516
|
).action(async (opts) => {
|
|
971
|
-
const
|
|
972
|
-
|
|
973
|
-
if (opts.
|
|
974
|
-
if (opts.
|
|
975
|
-
if (opts.
|
|
976
|
-
if (typeof opts.
|
|
977
|
-
|
|
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);
|
|
978
1524
|
try {
|
|
979
1525
|
const result = await callTool("chrome_screenshot", args);
|
|
980
1526
|
if (opts.out && result && typeof result === "object") {
|
|
@@ -998,10 +1544,9 @@ full-tab screenshot when an agent only needs to see one component.
|
|
|
998
1544
|
tabOpt(
|
|
999
1545
|
program.command("read").description("Extract page structure and interactive elements.").option("-i, --interactive", "return only interactive elements")
|
|
1000
1546
|
).action(async (opts) => {
|
|
1001
|
-
const
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
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));
|
|
1005
1550
|
});
|
|
1006
1551
|
tabOpt(
|
|
1007
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(
|
|
@@ -1019,11 +1564,11 @@ Notes:
|
|
|
1019
1564
|
`
|
|
1020
1565
|
)
|
|
1021
1566
|
).action(async (opts) => {
|
|
1022
|
-
const
|
|
1023
|
-
if (opts.interactiveOnly)
|
|
1024
|
-
if (opts.root)
|
|
1025
|
-
if (opts.includeSubframes)
|
|
1026
|
-
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));
|
|
1027
1572
|
});
|
|
1028
1573
|
tabOpt(
|
|
1029
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(
|
|
@@ -1039,9 +1584,7 @@ Notes:
|
|
|
1039
1584
|
`
|
|
1040
1585
|
)
|
|
1041
1586
|
).action(async (opts) => {
|
|
1042
|
-
|
|
1043
|
-
args.node = opts.node;
|
|
1044
|
-
await run("chrome_click_ax", args);
|
|
1587
|
+
await run("chrome_click_ax", withBase(opts, { node: opts.node }));
|
|
1045
1588
|
});
|
|
1046
1589
|
const screencast = program.command("screencast").description("Record a tab via CDP (paint-driven). Requires an active tab.").addHelpText(
|
|
1047
1590
|
"after",
|
|
@@ -1066,20 +1609,18 @@ Notes:
|
|
|
1066
1609
|
tabOpt(
|
|
1067
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))
|
|
1068
1611
|
).action(async (opts) => {
|
|
1069
|
-
const
|
|
1070
|
-
|
|
1071
|
-
if (opts.
|
|
1072
|
-
if (typeof opts.
|
|
1073
|
-
if (typeof opts.
|
|
1074
|
-
if (typeof opts.
|
|
1075
|
-
|
|
1076
|
-
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));
|
|
1077
1619
|
});
|
|
1078
1620
|
tabOpt(
|
|
1079
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")
|
|
1080
1622
|
).action(async (opts) => {
|
|
1081
|
-
const args = { action: "stop" };
|
|
1082
|
-
Object.assign(args, baseArgs(opts));
|
|
1623
|
+
const args = withBase(opts, { action: "stop" });
|
|
1083
1624
|
try {
|
|
1084
1625
|
const result = await callTool("chrome_screencast", args);
|
|
1085
1626
|
if (!opts.out) {
|
|
@@ -1214,7 +1755,7 @@ function netFilterArgs(opts) {
|
|
|
1214
1755
|
return a;
|
|
1215
1756
|
}
|
|
1216
1757
|
function registerSessions(ctx) {
|
|
1217
|
-
const { program,
|
|
1758
|
+
const { program, withBase, run } = ctx;
|
|
1218
1759
|
const viewport = program.command("viewport").description("Emulate device viewport, DPR, mobile flag, touch, and user agent.").addHelpText(
|
|
1219
1760
|
"after",
|
|
1220
1761
|
`
|
|
@@ -1235,27 +1776,22 @@ Notes:
|
|
|
1235
1776
|
tabOpt(
|
|
1236
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")
|
|
1237
1778
|
).action(async (opts) => {
|
|
1238
|
-
const
|
|
1239
|
-
|
|
1240
|
-
if (opts.
|
|
1241
|
-
if (opts.
|
|
1242
|
-
if (opts.
|
|
1243
|
-
|
|
1244
|
-
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));
|
|
1245
1785
|
});
|
|
1246
1786
|
tabOpt(
|
|
1247
1787
|
viewport.command("preset <name>").description("Apply a named device preset (iphone-14, pixel-7, desktop-1440, etc).")
|
|
1248
1788
|
).action(async (name, opts) => {
|
|
1249
|
-
|
|
1250
|
-
Object.assign(args, baseArgs(opts));
|
|
1251
|
-
await run("chrome_viewport", args);
|
|
1789
|
+
await run("chrome_viewport", withBase(opts, { action: "preset", name }));
|
|
1252
1790
|
});
|
|
1253
1791
|
tabOpt(
|
|
1254
1792
|
viewport.command("clear").description("Drop the viewport override and return the tab to its native size.")
|
|
1255
1793
|
).action(async (opts) => {
|
|
1256
|
-
|
|
1257
|
-
Object.assign(args, baseArgs(opts));
|
|
1258
|
-
await run("chrome_viewport", args);
|
|
1794
|
+
await run("chrome_viewport", withBase(opts, { action: "clear" }));
|
|
1259
1795
|
});
|
|
1260
1796
|
viewport.command("list").description("List available presets.").action(async () => {
|
|
1261
1797
|
await run("chrome_viewport", { action: "list" });
|
|
@@ -1316,8 +1852,7 @@ Notes:
|
|
|
1316
1852
|
`
|
|
1317
1853
|
);
|
|
1318
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) => {
|
|
1319
|
-
const args = { action: "create", name };
|
|
1320
|
-
args.tabIds = String(opts.tabs).split(",").map((s) => Number(s.trim())).filter(Number.isFinite);
|
|
1855
|
+
const args = { action: "create", name, tabIds: String(opts.tabs) };
|
|
1321
1856
|
if (opts.color) args.color = opts.color;
|
|
1322
1857
|
if (opts.collapsed) args.collapsed = true;
|
|
1323
1858
|
await run("chrome_group", args);
|
|
@@ -1329,12 +1864,10 @@ Notes:
|
|
|
1329
1864
|
await run("chrome_group", { action: "close", name });
|
|
1330
1865
|
});
|
|
1331
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) => {
|
|
1332
|
-
|
|
1333
|
-
await run("chrome_group", { action: "add", name, tabIds });
|
|
1867
|
+
await run("chrome_group", { action: "add", name, tabIds: String(opts.tabs) });
|
|
1334
1868
|
});
|
|
1335
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) => {
|
|
1336
|
-
|
|
1337
|
-
await run("chrome_group", { action: "remove", tabIds });
|
|
1870
|
+
await run("chrome_group", { action: "remove", tabIds: String(opts.tabs) });
|
|
1338
1871
|
});
|
|
1339
1872
|
const network = tabOpt(netFilterOpts(
|
|
1340
1873
|
program.command("network").description("Capture HTTP request/response metadata. Ring buffer, last 200 per tab.")
|
|
@@ -1364,41 +1897,38 @@ Notes:
|
|
|
1364
1897
|
WebSocket frames and SSE streams are out of scope.
|
|
1365
1898
|
`
|
|
1366
1899
|
).action(async (opts) => {
|
|
1367
|
-
|
|
1368
|
-
await run("chrome_network", args);
|
|
1900
|
+
await run("chrome_network", withBase(opts, netFilterArgs(opts)));
|
|
1369
1901
|
});
|
|
1370
1902
|
tabOpt(netFilterOpts(
|
|
1371
1903
|
network.command("read").description("(alias) list captured network entries.")
|
|
1372
1904
|
)).action(async (opts) => {
|
|
1373
|
-
|
|
1374
|
-
await run("chrome_network", args);
|
|
1905
|
+
await run("chrome_network", withBase(opts, netFilterArgs(opts)));
|
|
1375
1906
|
});
|
|
1376
1907
|
tabOpt(
|
|
1377
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")
|
|
1378
1909
|
).action(async (requestId, opts) => {
|
|
1379
|
-
const
|
|
1380
|
-
if (opts.full)
|
|
1381
|
-
if (typeof opts.head === "number")
|
|
1382
|
-
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));
|
|
1383
1914
|
});
|
|
1384
1915
|
tabOpt(netFilterOpts(
|
|
1385
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")
|
|
1386
1917
|
)).action(async (opts) => {
|
|
1387
|
-
const
|
|
1388
|
-
if (opts.withBodies)
|
|
1389
|
-
if (opts.bestEffortBodies)
|
|
1918
|
+
const extras = { ...netFilterArgs(opts), action: "har" };
|
|
1919
|
+
if (opts.withBodies) extras.withBodies = true;
|
|
1920
|
+
if (opts.bestEffortBodies) extras.bestEffortBodies = true;
|
|
1390
1921
|
if (!opts.withBodies) {
|
|
1391
1922
|
process.stderr.write(
|
|
1392
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"
|
|
1393
1924
|
);
|
|
1394
1925
|
}
|
|
1395
|
-
await run("chrome_network",
|
|
1926
|
+
await run("chrome_network", withBase(opts, extras));
|
|
1396
1927
|
});
|
|
1397
1928
|
tabOpt(
|
|
1398
1929
|
network.command("clear").description("Wipe the network buffer for this tab.")
|
|
1399
1930
|
).action(async (opts) => {
|
|
1400
|
-
|
|
1401
|
-
await run("chrome_network", args);
|
|
1931
|
+
await run("chrome_network", withBase(opts, { action: "clear" }));
|
|
1402
1932
|
});
|
|
1403
1933
|
tabOpt(
|
|
1404
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(
|
|
@@ -1418,12 +1948,12 @@ Notes:
|
|
|
1418
1948
|
`
|
|
1419
1949
|
)
|
|
1420
1950
|
).action(async (opts) => {
|
|
1421
|
-
const
|
|
1422
|
-
if (opts.clear)
|
|
1423
|
-
if (opts.level)
|
|
1424
|
-
if (typeof opts.since === "number")
|
|
1425
|
-
if (typeof opts.limit === "number")
|
|
1426
|
-
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));
|
|
1427
1957
|
});
|
|
1428
1958
|
}
|
|
1429
1959
|
|
|
@@ -1450,9 +1980,11 @@ Notes:
|
|
|
1450
1980
|
Tools attach via CDP and run on backgrounded tabs without stealing focus.
|
|
1451
1981
|
`
|
|
1452
1982
|
);
|
|
1983
|
+
const baseArgs = makeBaseArgs(program);
|
|
1453
1984
|
const ctx = {
|
|
1454
1985
|
program,
|
|
1455
|
-
baseArgs
|
|
1986
|
+
baseArgs,
|
|
1987
|
+
withBase: makeWithBase(baseArgs),
|
|
1456
1988
|
run: runTool
|
|
1457
1989
|
};
|
|
1458
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) {
|