chrome-relay 0.5.13 → 0.5.15
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 +311 -170
- package/dist/index.js +1 -1
- package/dist/native-host.js +12 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -37,26 +37,140 @@ 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;
|
|
65
|
+
}
|
|
66
|
+
function optPositiveNumber(obj, key, tool) {
|
|
67
|
+
const v = optNumber(obj, key, tool);
|
|
68
|
+
if (v === void 0)
|
|
69
|
+
return void 0;
|
|
70
|
+
if (v <= 0) {
|
|
71
|
+
throw new RelayError({
|
|
72
|
+
code: "invalid_arguments",
|
|
73
|
+
message: `${tool ?? "<unknown tool>"}: \`${key}\` must be > 0 (got ${v}).`,
|
|
74
|
+
tool,
|
|
75
|
+
phase: "parse_arguments",
|
|
76
|
+
details: { field: key, expected: "> 0", received: v },
|
|
77
|
+
retryable: false
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return v;
|
|
81
|
+
}
|
|
82
|
+
function optNonNegativeNumber(obj, key, tool) {
|
|
83
|
+
const v = optNumber(obj, key, tool);
|
|
84
|
+
if (v === void 0)
|
|
85
|
+
return void 0;
|
|
86
|
+
if (v < 0) {
|
|
87
|
+
throw new RelayError({
|
|
88
|
+
code: "invalid_arguments",
|
|
89
|
+
message: `${tool ?? "<unknown tool>"}: \`${key}\` must be >= 0 (got ${v}).`,
|
|
90
|
+
tool,
|
|
91
|
+
phase: "parse_arguments",
|
|
92
|
+
details: { field: key, expected: ">= 0", received: v },
|
|
93
|
+
retryable: false
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return v;
|
|
97
|
+
}
|
|
98
|
+
function coerceTabId(raw, tool) {
|
|
99
|
+
if (typeof raw === "number") {
|
|
100
|
+
if (!Number.isFinite(raw)) {
|
|
101
|
+
throw new RelayError({
|
|
102
|
+
code: "invalid_arguments",
|
|
103
|
+
message: `${tool}: invalid tabId ${JSON.stringify(raw)}. Expected a finite number.`,
|
|
104
|
+
tool,
|
|
105
|
+
phase: "parse_arguments",
|
|
106
|
+
details: { received: raw },
|
|
107
|
+
retryable: false
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return raw;
|
|
111
|
+
}
|
|
112
|
+
if (typeof raw === "string") {
|
|
113
|
+
const trimmed = raw.trim();
|
|
114
|
+
if (!trimmed) {
|
|
115
|
+
throw new RelayError({
|
|
116
|
+
code: "invalid_arguments",
|
|
117
|
+
message: `${tool}: invalid tabId ${JSON.stringify(raw)}. Expected a number; got a blank string.`,
|
|
118
|
+
tool,
|
|
119
|
+
phase: "parse_arguments",
|
|
120
|
+
details: { received: raw },
|
|
121
|
+
retryable: false
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
const n = Number(trimmed);
|
|
125
|
+
if (!Number.isFinite(n)) {
|
|
126
|
+
throw new RelayError({
|
|
127
|
+
code: "invalid_arguments",
|
|
128
|
+
message: `${tool}: invalid tabId ${JSON.stringify(raw)}. Expected a numeric string.`,
|
|
129
|
+
tool,
|
|
130
|
+
phase: "parse_arguments",
|
|
131
|
+
details: { received: raw },
|
|
132
|
+
retryable: false
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return n;
|
|
136
|
+
}
|
|
137
|
+
throw new RelayError({
|
|
138
|
+
code: "invalid_arguments",
|
|
139
|
+
message: `${tool}: invalid tabId ${JSON.stringify(raw)}. Expected a number or numeric string.`,
|
|
140
|
+
tool,
|
|
141
|
+
phase: "parse_arguments",
|
|
142
|
+
details: { received: raw },
|
|
143
|
+
retryable: false
|
|
144
|
+
});
|
|
47
145
|
}
|
|
48
|
-
function optBool(obj, key) {
|
|
146
|
+
function optBool(obj, key, tool) {
|
|
49
147
|
const v = obj[key];
|
|
50
|
-
|
|
148
|
+
if (v === void 0 || v === null)
|
|
149
|
+
return void 0;
|
|
150
|
+
if (typeof v !== "boolean")
|
|
151
|
+
rejectWrongType(obj, key, "a boolean", tool);
|
|
152
|
+
return v;
|
|
51
153
|
}
|
|
52
|
-
function parseTargetArgs(obj) {
|
|
154
|
+
function parseTargetArgs(obj, tool) {
|
|
53
155
|
const out = {};
|
|
54
|
-
if (
|
|
156
|
+
if (obj.tabId !== void 0 && obj.tabId !== null) {
|
|
157
|
+
if (typeof obj.tabId !== "number" || !Number.isFinite(obj.tabId)) {
|
|
158
|
+
rejectWrongType(obj, "tabId", "a finite number", tool);
|
|
159
|
+
}
|
|
55
160
|
out.tabId = obj.tabId;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
161
|
+
}
|
|
162
|
+
if (obj.workspaceName !== void 0 && obj.workspaceName !== null) {
|
|
163
|
+
if (typeof obj.workspaceName !== "string")
|
|
164
|
+
rejectWrongType(obj, "workspaceName", "a string", tool);
|
|
165
|
+
if (obj.workspaceName)
|
|
166
|
+
out.workspaceName = obj.workspaceName;
|
|
167
|
+
}
|
|
168
|
+
if (obj.groupName !== void 0 && obj.groupName !== null) {
|
|
169
|
+
if (typeof obj.groupName !== "string")
|
|
170
|
+
rejectWrongType(obj, "groupName", "a string", tool);
|
|
171
|
+
if (obj.groupName)
|
|
172
|
+
out.groupName = obj.groupName;
|
|
173
|
+
}
|
|
60
174
|
return out;
|
|
61
175
|
}
|
|
62
176
|
var init_shared = __esm({
|
|
@@ -69,10 +183,7 @@ var init_shared = __esm({
|
|
|
69
183
|
// ../protocol/dist/args/navigate.js
|
|
70
184
|
function parseChromeNavigateArgs(input) {
|
|
71
185
|
const obj = asObject(input, TOOL_NAMES.NAVIGATE);
|
|
72
|
-
const out = {
|
|
73
|
-
url: requireString(obj, "url", TOOL_NAMES.NAVIGATE),
|
|
74
|
-
...parseTargetArgs(obj)
|
|
75
|
-
};
|
|
186
|
+
const out = { url: requireString(obj, "url", TOOL_NAMES.NAVIGATE) };
|
|
76
187
|
if (typeof obj.tabId === "string" && obj.tabId) {
|
|
77
188
|
const n = Number(obj.tabId);
|
|
78
189
|
if (!Number.isFinite(n)) {
|
|
@@ -87,17 +198,23 @@ function parseChromeNavigateArgs(input) {
|
|
|
87
198
|
}
|
|
88
199
|
out.tabId = n;
|
|
89
200
|
} else {
|
|
90
|
-
const n = optNumber(obj, "tabId");
|
|
201
|
+
const n = optNumber(obj, "tabId", TOOL_NAMES.NAVIGATE);
|
|
91
202
|
if (n !== void 0)
|
|
92
203
|
out.tabId = n;
|
|
93
204
|
}
|
|
94
|
-
const
|
|
205
|
+
const { tabId: _, ...rest } = obj;
|
|
206
|
+
const target = parseTargetArgs(rest, TOOL_NAMES.NAVIGATE);
|
|
207
|
+
if (target.workspaceName)
|
|
208
|
+
out.workspaceName = target.workspaceName;
|
|
209
|
+
if (target.groupName)
|
|
210
|
+
out.groupName = target.groupName;
|
|
211
|
+
const newTab = optBool(obj, "newTab", TOOL_NAMES.NAVIGATE);
|
|
95
212
|
if (newTab !== void 0)
|
|
96
213
|
out.newTab = newTab;
|
|
97
|
-
const active = optBool(obj, "active");
|
|
214
|
+
const active = optBool(obj, "active", TOOL_NAMES.NAVIGATE);
|
|
98
215
|
if (active !== void 0)
|
|
99
216
|
out.active = active;
|
|
100
|
-
const allowPartial = optBool(obj, "allowPartial");
|
|
217
|
+
const allowPartial = optBool(obj, "allowPartial", TOOL_NAMES.NAVIGATE);
|
|
101
218
|
if (allowPartial !== void 0)
|
|
102
219
|
out.allowPartial = allowPartial;
|
|
103
220
|
void optString;
|
|
@@ -114,13 +231,13 @@ var init_navigate = __esm({
|
|
|
114
231
|
// ../protocol/dist/args/hover.js
|
|
115
232
|
function parseChromeHoverArgs(input) {
|
|
116
233
|
const obj = asObject(input, TOOL_NAMES.HOVER);
|
|
117
|
-
const target = parseTargetArgs(obj);
|
|
118
|
-
const x = optNumber(obj, "x");
|
|
119
|
-
const y = optNumber(obj, "y");
|
|
234
|
+
const target = parseTargetArgs(obj, TOOL_NAMES.HOVER);
|
|
235
|
+
const x = optNumber(obj, "x", TOOL_NAMES.HOVER);
|
|
236
|
+
const y = optNumber(obj, "y", TOOL_NAMES.HOVER);
|
|
120
237
|
if (x !== void 0 && y !== void 0) {
|
|
121
238
|
return { ...target, kind: "coords", x, y };
|
|
122
239
|
}
|
|
123
|
-
const selector = optString(obj, "selector");
|
|
240
|
+
const selector = optString(obj, "selector", TOOL_NAMES.HOVER);
|
|
124
241
|
if (selector) {
|
|
125
242
|
return { ...target, kind: "selector", selector };
|
|
126
243
|
}
|
|
@@ -197,7 +314,7 @@ function parseChromeNetworkArgs(input) {
|
|
|
197
314
|
const full = optBool(obj, "full");
|
|
198
315
|
if (full !== void 0)
|
|
199
316
|
out.full = full;
|
|
200
|
-
const head =
|
|
317
|
+
const head = optPositiveNumber(obj, "head", TOOL_NAMES.NETWORK);
|
|
201
318
|
if (head !== void 0)
|
|
202
319
|
out.head = head;
|
|
203
320
|
return out;
|
|
@@ -236,10 +353,14 @@ var init_network = __esm({
|
|
|
236
353
|
});
|
|
237
354
|
|
|
238
355
|
// ../protocol/dist/args/simple.js
|
|
239
|
-
function parseGetWindowsAndTabsArgs(
|
|
356
|
+
function parseGetWindowsAndTabsArgs(input) {
|
|
357
|
+
if (input !== void 0 && input !== null)
|
|
358
|
+
asObject(input, TOOL_NAMES.GET_WINDOWS_AND_TABS);
|
|
240
359
|
return {};
|
|
241
360
|
}
|
|
242
|
-
function parseChromeSelfReloadArgs(
|
|
361
|
+
function parseChromeSelfReloadArgs(input) {
|
|
362
|
+
if (input !== void 0 && input !== null)
|
|
363
|
+
asObject(input, TOOL_NAMES.SELF_RELOAD);
|
|
243
364
|
return {};
|
|
244
365
|
}
|
|
245
366
|
function parseChromeReadPageArgs(input) {
|
|
@@ -297,27 +418,25 @@ function parseChromeEvaluateArgs(input) {
|
|
|
297
418
|
const obj = asObject(input, TOOL_NAMES.EVALUATE);
|
|
298
419
|
const out = {
|
|
299
420
|
code: requireString(obj, "code", TOOL_NAMES.EVALUATE),
|
|
300
|
-
...parseTargetArgs(obj)
|
|
421
|
+
...parseTargetArgs(obj, TOOL_NAMES.EVALUATE)
|
|
301
422
|
};
|
|
302
|
-
const t =
|
|
423
|
+
const t = optPositiveNumber(obj, "timeoutMs", TOOL_NAMES.EVALUATE);
|
|
303
424
|
if (t !== void 0)
|
|
304
425
|
out.timeoutMs = t;
|
|
305
426
|
return out;
|
|
306
427
|
}
|
|
307
428
|
function parseChromeSwitchTabArgs(input) {
|
|
308
429
|
const obj = asObject(input, TOOL_NAMES.SWITCH_TAB);
|
|
309
|
-
|
|
310
|
-
if (!Number.isFinite(tabId)) {
|
|
430
|
+
if (obj.tabId === void 0 || obj.tabId === null) {
|
|
311
431
|
throw new RelayError({
|
|
312
432
|
code: "invalid_arguments",
|
|
313
433
|
message: `${TOOL_NAMES.SWITCH_TAB} requires a numeric tabId.`,
|
|
314
434
|
tool: TOOL_NAMES.SWITCH_TAB,
|
|
315
435
|
phase: "parse_arguments",
|
|
316
|
-
details: { received: obj.tabId },
|
|
317
436
|
retryable: false
|
|
318
437
|
});
|
|
319
438
|
}
|
|
320
|
-
return { tabId };
|
|
439
|
+
return { tabId: coerceTabId(obj.tabId, TOOL_NAMES.SWITCH_TAB) };
|
|
321
440
|
}
|
|
322
441
|
function parseChromeCloseTabsArgs(input) {
|
|
323
442
|
const obj = asObject(input, TOOL_NAMES.CLOSE_TABS);
|
|
@@ -331,18 +450,17 @@ function parseChromeCloseTabsArgs(input) {
|
|
|
331
450
|
retryable: false
|
|
332
451
|
});
|
|
333
452
|
}
|
|
334
|
-
|
|
335
|
-
if (tabIds.length === 0 || tabIds.some((n) => !Number.isFinite(n))) {
|
|
453
|
+
if (obj.tabIds.length === 0) {
|
|
336
454
|
throw new RelayError({
|
|
337
455
|
code: "invalid_arguments",
|
|
338
|
-
message: `${TOOL_NAMES.CLOSE_TABS} requires a non-empty array of
|
|
456
|
+
message: `${TOOL_NAMES.CLOSE_TABS} requires a non-empty array of tab IDs.`,
|
|
339
457
|
tool: TOOL_NAMES.CLOSE_TABS,
|
|
340
458
|
phase: "parse_arguments",
|
|
341
459
|
details: { received: obj.tabIds },
|
|
342
460
|
retryable: false
|
|
343
461
|
});
|
|
344
462
|
}
|
|
345
|
-
return { tabIds };
|
|
463
|
+
return { tabIds: obj.tabIds.map((v) => coerceTabId(v, TOOL_NAMES.CLOSE_TABS)) };
|
|
346
464
|
}
|
|
347
465
|
function parseChromeAxArgs(input) {
|
|
348
466
|
const obj = asObject(input, TOOL_NAMES.AX);
|
|
@@ -375,21 +493,21 @@ function parseChromeClickAxArgs(input) {
|
|
|
375
493
|
}
|
|
376
494
|
function parseChromeScreenshotArgs(input) {
|
|
377
495
|
const obj = asObject(input, TOOL_NAMES.SCREENSHOT);
|
|
378
|
-
const out = { ...parseTargetArgs(obj) };
|
|
379
|
-
const fp = optBool(obj, "fullPage");
|
|
496
|
+
const out = { ...parseTargetArgs(obj, TOOL_NAMES.SCREENSHOT) };
|
|
497
|
+
const fp = optBool(obj, "fullPage", TOOL_NAMES.SCREENSHOT);
|
|
380
498
|
if (fp !== void 0)
|
|
381
499
|
out.fullPage = fp;
|
|
382
|
-
const bbox = optString(obj, "bbox");
|
|
500
|
+
const bbox = optString(obj, "bbox", TOOL_NAMES.SCREENSHOT);
|
|
383
501
|
if (bbox)
|
|
384
502
|
out.bbox = bbox;
|
|
385
|
-
const sel = optString(obj, "selector");
|
|
503
|
+
const sel = optString(obj, "selector", TOOL_NAMES.SCREENSHOT);
|
|
386
504
|
if (sel)
|
|
387
505
|
out.selector = sel;
|
|
388
|
-
const pad =
|
|
506
|
+
const pad = optNonNegativeNumber(obj, "padding", TOOL_NAMES.SCREENSHOT);
|
|
389
507
|
if (pad !== void 0)
|
|
390
508
|
out.padding = pad;
|
|
391
|
-
const me =
|
|
392
|
-
if (me !== void 0
|
|
509
|
+
const me = optPositiveNumber(obj, "maxEdge", TOOL_NAMES.SCREENSHOT);
|
|
510
|
+
if (me !== void 0)
|
|
393
511
|
out.maxEdge = me;
|
|
394
512
|
return out;
|
|
395
513
|
}
|
|
@@ -532,28 +650,12 @@ function parseChromeWorkspaceArgs(input) {
|
|
|
532
650
|
return out;
|
|
533
651
|
}
|
|
534
652
|
function parseTabIds(raw) {
|
|
535
|
-
const reject = (bad) => {
|
|
536
|
-
throw new RelayError({
|
|
537
|
-
code: "invalid_arguments",
|
|
538
|
-
message: `${TOOL_NAMES.GROUP}: invalid tabId ${JSON.stringify(bad)}. Expected a number or a comma-separated list of numbers.`,
|
|
539
|
-
tool: TOOL_NAMES.GROUP,
|
|
540
|
-
phase: "parse_tab_ids",
|
|
541
|
-
details: { received: bad },
|
|
542
|
-
retryable: false
|
|
543
|
-
});
|
|
544
|
-
};
|
|
545
|
-
const coerce = (v) => {
|
|
546
|
-
const n = Number(typeof v === "string" ? v.trim() : v);
|
|
547
|
-
if (!Number.isFinite(n))
|
|
548
|
-
reject(v);
|
|
549
|
-
return n;
|
|
550
|
-
};
|
|
551
653
|
if (Array.isArray(raw))
|
|
552
|
-
return raw.map(
|
|
654
|
+
return raw.map((v) => coerceTabId(v, TOOL_NAMES.GROUP));
|
|
553
655
|
if (typeof raw === "string")
|
|
554
|
-
return raw.split(",").map(
|
|
656
|
+
return raw.split(",").map((s) => coerceTabId(s, TOOL_NAMES.GROUP));
|
|
555
657
|
if (typeof raw === "number")
|
|
556
|
-
return [raw];
|
|
658
|
+
return [coerceTabId(raw, TOOL_NAMES.GROUP)];
|
|
557
659
|
return [];
|
|
558
660
|
}
|
|
559
661
|
function parseColor(raw) {
|
|
@@ -673,16 +775,27 @@ function parseChromeScreencastArgs(input) {
|
|
|
673
775
|
}
|
|
674
776
|
out.format = obj.format;
|
|
675
777
|
}
|
|
676
|
-
const q =
|
|
677
|
-
if (q !== void 0)
|
|
778
|
+
const q = optNonNegativeNumber(obj, "quality", TOOL_NAMES.SCREENCAST);
|
|
779
|
+
if (q !== void 0) {
|
|
780
|
+
if (q > 100) {
|
|
781
|
+
throw new RelayError({
|
|
782
|
+
code: "invalid_arguments",
|
|
783
|
+
message: `${TOOL_NAMES.SCREENCAST}: quality must be 0-100 (got ${q}).`,
|
|
784
|
+
tool: TOOL_NAMES.SCREENCAST,
|
|
785
|
+
phase: "parse_arguments",
|
|
786
|
+
details: { field: "quality", received: q, range: [0, 100] },
|
|
787
|
+
retryable: false
|
|
788
|
+
});
|
|
789
|
+
}
|
|
678
790
|
out.quality = q;
|
|
679
|
-
|
|
791
|
+
}
|
|
792
|
+
const mw = optPositiveNumber(obj, "maxWidth", TOOL_NAMES.SCREENCAST);
|
|
680
793
|
if (mw !== void 0)
|
|
681
794
|
out.maxWidth = mw;
|
|
682
|
-
const mh =
|
|
795
|
+
const mh = optPositiveNumber(obj, "maxHeight", TOOL_NAMES.SCREENCAST);
|
|
683
796
|
if (mh !== void 0)
|
|
684
797
|
out.maxHeight = mh;
|
|
685
|
-
const en =
|
|
798
|
+
const en = optPositiveNumber(obj, "everyNthFrame", TOOL_NAMES.SCREENCAST);
|
|
686
799
|
if (en !== void 0)
|
|
687
800
|
out.everyNthFrame = en;
|
|
688
801
|
return out;
|
|
@@ -717,21 +830,50 @@ var init_args = __esm({
|
|
|
717
830
|
}
|
|
718
831
|
});
|
|
719
832
|
|
|
833
|
+
// ../protocol/dist/limits.js
|
|
834
|
+
var DEFAULT_TOOL_CALL_TIMEOUT_MS, DEFAULT_PING_TIMEOUT_MS, DEFAULT_READY_TIMEOUT_MS, DEFAULT_EVAL_TIMEOUT_MS, DEFAULT_BODY_PREVIEW_BYTES, NETWORK_BUFFER_MAX_ENTRIES, NETWORK_BUFFER_MAX_BYTES, CONSOLE_BUFFER_MAX_ENTRIES, CONSOLE_BUFFER_MAX_BYTES;
|
|
835
|
+
var init_limits = __esm({
|
|
836
|
+
"../protocol/dist/limits.js"() {
|
|
837
|
+
"use strict";
|
|
838
|
+
DEFAULT_TOOL_CALL_TIMEOUT_MS = 3e4;
|
|
839
|
+
DEFAULT_PING_TIMEOUT_MS = 2e3;
|
|
840
|
+
DEFAULT_READY_TIMEOUT_MS = 15e3;
|
|
841
|
+
DEFAULT_EVAL_TIMEOUT_MS = 15e3;
|
|
842
|
+
DEFAULT_BODY_PREVIEW_BYTES = 8 * 1024;
|
|
843
|
+
NETWORK_BUFFER_MAX_ENTRIES = 200;
|
|
844
|
+
NETWORK_BUFFER_MAX_BYTES = 512 * 1024;
|
|
845
|
+
CONSOLE_BUFFER_MAX_ENTRIES = 200;
|
|
846
|
+
CONSOLE_BUFFER_MAX_BYTES = 256 * 1024;
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
|
|
720
850
|
// ../protocol/dist/index.js
|
|
721
851
|
var dist_exports = {};
|
|
722
852
|
__export(dist_exports, {
|
|
723
853
|
CHROME_WEB_STORE_EXTENSION_ID: () => CHROME_WEB_STORE_EXTENSION_ID,
|
|
854
|
+
CONSOLE_BUFFER_MAX_BYTES: () => CONSOLE_BUFFER_MAX_BYTES,
|
|
855
|
+
CONSOLE_BUFFER_MAX_ENTRIES: () => CONSOLE_BUFFER_MAX_ENTRIES,
|
|
856
|
+
DEFAULT_BODY_PREVIEW_BYTES: () => DEFAULT_BODY_PREVIEW_BYTES,
|
|
857
|
+
DEFAULT_EVAL_TIMEOUT_MS: () => DEFAULT_EVAL_TIMEOUT_MS,
|
|
724
858
|
DEFAULT_EXTENSION_ID: () => DEFAULT_EXTENSION_ID,
|
|
725
859
|
DEFAULT_EXTENSION_IDS: () => DEFAULT_EXTENSION_IDS,
|
|
726
860
|
DEFAULT_HTTP_PORT: () => DEFAULT_HTTP_PORT,
|
|
861
|
+
DEFAULT_PING_TIMEOUT_MS: () => DEFAULT_PING_TIMEOUT_MS,
|
|
862
|
+
DEFAULT_READY_TIMEOUT_MS: () => DEFAULT_READY_TIMEOUT_MS,
|
|
863
|
+
DEFAULT_TOOL_CALL_TIMEOUT_MS: () => DEFAULT_TOOL_CALL_TIMEOUT_MS,
|
|
727
864
|
LEGACY_DEV_EXTENSION_ID: () => LEGACY_DEV_EXTENSION_ID,
|
|
728
865
|
LOCAL_UNPACKED_EXTENSION_ID: () => LOCAL_UNPACKED_EXTENSION_ID,
|
|
729
866
|
NATIVE_HOST_NAME: () => NATIVE_HOST_NAME,
|
|
867
|
+
NETWORK_BUFFER_MAX_BYTES: () => NETWORK_BUFFER_MAX_BYTES,
|
|
868
|
+
NETWORK_BUFFER_MAX_ENTRIES: () => NETWORK_BUFFER_MAX_ENTRIES,
|
|
730
869
|
RelayError: () => RelayError,
|
|
731
870
|
TOOL_NAMES: () => TOOL_NAMES,
|
|
732
871
|
asObject: () => asObject,
|
|
872
|
+
coerceTabId: () => coerceTabId,
|
|
733
873
|
optBool: () => optBool,
|
|
874
|
+
optNonNegativeNumber: () => optNonNegativeNumber,
|
|
734
875
|
optNumber: () => optNumber,
|
|
876
|
+
optPositiveNumber: () => optPositiveNumber,
|
|
735
877
|
optString: () => optString,
|
|
736
878
|
parseChromeAxArgs: () => parseChromeAxArgs,
|
|
737
879
|
parseChromeClickArgs: () => parseChromeClickArgs,
|
|
@@ -775,6 +917,7 @@ var init_dist = __esm({
|
|
|
775
917
|
"../protocol/dist/index.js"() {
|
|
776
918
|
"use strict";
|
|
777
919
|
init_args();
|
|
920
|
+
init_limits();
|
|
778
921
|
NATIVE_HOST_NAME = "dev.chrome_relay.native_host";
|
|
779
922
|
DEFAULT_HTTP_PORT = 12122;
|
|
780
923
|
CHROME_WEB_STORE_EXTENSION_ID = "cpdiapbifblhlcpnmlmfpgfjlacebokb";
|
|
@@ -866,7 +1009,7 @@ var init_dist = __esm({
|
|
|
866
1009
|
import { Command } from "commander";
|
|
867
1010
|
|
|
868
1011
|
// src/index.ts
|
|
869
|
-
var CHROME_RELAY_VERSION = true ? "0.5.
|
|
1012
|
+
var CHROME_RELAY_VERSION = true ? "0.5.15" : "0.0.0-dev";
|
|
870
1013
|
|
|
871
1014
|
// src/commands/shared.ts
|
|
872
1015
|
init_dist();
|
|
@@ -914,6 +1057,11 @@ async function callTool(name, args) {
|
|
|
914
1057
|
}
|
|
915
1058
|
|
|
916
1059
|
// src/commands/shared.ts
|
|
1060
|
+
function makeWithBase(baseArgs) {
|
|
1061
|
+
return function withBase(opts, extras) {
|
|
1062
|
+
return { ...baseArgs(opts), ...extras ?? {} };
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
917
1065
|
function tabOpt(cmd) {
|
|
918
1066
|
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
1067
|
}
|
|
@@ -1100,6 +1248,23 @@ async function runDoctor() {
|
|
|
1100
1248
|
|
|
1101
1249
|
// src/release-notes.ts
|
|
1102
1250
|
var RELEASE_NOTES = {
|
|
1251
|
+
"0.5.15": [
|
|
1252
|
+
"Tab-id coercion is now strict against blank strings. `--tabs '1,,3'` (or `[\"\"]`, or `[' ']`) used to silently become `[1, 0, 3]` because Number('') === 0 \u2014 and tab 0 is a real Chrome target. Now throws RelayError(invalid_arguments). Affects chrome_group create/add/remove, chrome_switch_tab, chrome_close_tabs.",
|
|
1253
|
+
"Numeric range validation added across the parsers. `optPositiveNumber` (> 0) and `optNonNegativeNumber` (>= 0) helpers reject out-of-range values at the parser boundary instead of letting nonsense through to CDP / handler logic. Affects: chrome_evaluate timeoutMs, chrome_screenshot maxEdge + padding, chrome_screencast quality/maxWidth/maxHeight/everyNthFrame, chrome_network body head.",
|
|
1254
|
+
"chrome_screenshot maxEdge <= 0 now throws instead of being silently ignored (was the one remaining `if (x > 0)` silent-drop in the parsers).",
|
|
1255
|
+
"No-args parser comment + behavior aligned. parseGetWindowsAndTabsArgs / parseChromeSelfReloadArgs validate the input is at least an object (rejects strings/arrays) but accept any extra fields. Both handlers now call their parser at the top for consistency with every other tool.",
|
|
1256
|
+
"New @chrome-relay/protocol exports: shared numeric limits in `packages/protocol/src/limits.ts`. Constants (DEFAULT_TOOL_CALL_TIMEOUT_MS, DEFAULT_EVAL_TIMEOUT_MS, DEFAULT_BODY_PREVIEW_BYTES, NETWORK_BUFFER_MAX_ENTRIES/BYTES, CONSOLE_BUFFER_MAX_ENTRIES/BYTES, etc.) now flow from one source \u2014 bridge.ts, console-buffer.ts, network-buffer.ts, and the network/evaluate handlers all import. Future doc/help drift is structurally prevented.",
|
|
1257
|
+
"Tests: +9 in args-all.test.ts covering blank-string rejection, range validation across screenshot/evaluate/screencast/network. Total 416 (was 407)."
|
|
1258
|
+
],
|
|
1259
|
+
"0.5.14": [
|
|
1260
|
+
"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.",
|
|
1261
|
+
"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.",
|
|
1262
|
+
"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].",
|
|
1263
|
+
"Deleted the dead duplicate parser module at apps/extension/src/browser/parsers.ts and its test file. The protocol parsers replace them; behavior identical.",
|
|
1264
|
+
"HAR creator.version now reads from chrome.runtime.getManifest() instead of the hardcoded stale '0.2.x'. Drift-proof.",
|
|
1265
|
+
"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.",
|
|
1266
|
+
"Tests: +2 strict-optional cases. Total 407 (was 433 \u2014 28 dropped were duplicates of protocol parsers)."
|
|
1267
|
+
],
|
|
1103
1268
|
"0.5.13": [
|
|
1104
1269
|
"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
1270
|
"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 +1464,7 @@ function registerInstallUpdate(program) {
|
|
|
1299
1464
|
|
|
1300
1465
|
// src/commands/navigation.ts
|
|
1301
1466
|
function registerNavigation(ctx) {
|
|
1302
|
-
const { program,
|
|
1467
|
+
const { program, withBase, run } = ctx;
|
|
1303
1468
|
program.command("tabs [verb]").description("List open Chrome windows and tabs. (verb 'list' is accepted as alias)").action(async (verb) => {
|
|
1304
1469
|
if (verb && verb !== "list") {
|
|
1305
1470
|
process.stderr.write(`unknown tabs verb: ${verb}. Use 'tabs' or 'tabs list'.
|
|
@@ -1328,11 +1493,10 @@ Use "chrome-relay switch ${url}" to activate that tab, or "chrome-relay navigate
|
|
|
1328
1493
|
);
|
|
1329
1494
|
process.exit(1);
|
|
1330
1495
|
}
|
|
1331
|
-
const
|
|
1332
|
-
|
|
1333
|
-
if (opts.
|
|
1334
|
-
|
|
1335
|
-
await run("chrome_navigate", args);
|
|
1496
|
+
const extras = { url };
|
|
1497
|
+
if (opts.new) extras.newTab = true;
|
|
1498
|
+
if (opts.inactive) extras.active = false;
|
|
1499
|
+
await run("chrome_navigate", withBase(opts, extras));
|
|
1336
1500
|
});
|
|
1337
1501
|
program.command("switch <tabId>").description("Activate a tab by ID.").action(async (tabId) => {
|
|
1338
1502
|
await run("chrome_switch_tab", { tabId: Number(tabId) });
|
|
@@ -1348,20 +1512,16 @@ Use "chrome-relay switch ${url}" to activate that tab, or "chrome-relay navigate
|
|
|
1348
1512
|
|
|
1349
1513
|
// src/commands/input.ts
|
|
1350
1514
|
function registerInput(ctx) {
|
|
1351
|
-
const { program,
|
|
1515
|
+
const { program, withBase, run } = ctx;
|
|
1352
1516
|
tabOpt(
|
|
1353
1517
|
program.command("click <selector>").description("Click an element by CSS selector.")
|
|
1354
1518
|
).action(async (selector, opts) => {
|
|
1355
|
-
|
|
1356
|
-
Object.assign(args, baseArgs(opts));
|
|
1357
|
-
await run("chrome_click_element", args);
|
|
1519
|
+
await run("chrome_click_element", withBase(opts, { selector }));
|
|
1358
1520
|
});
|
|
1359
1521
|
tabOpt(
|
|
1360
1522
|
program.command("fill <selector> <value>").description("Fill an input or textarea.")
|
|
1361
1523
|
).action(async (selector, value, opts) => {
|
|
1362
|
-
|
|
1363
|
-
Object.assign(args, baseArgs(opts));
|
|
1364
|
-
await run("chrome_fill_or_select", args);
|
|
1524
|
+
await run("chrome_fill_or_select", withBase(opts, { selector, value }));
|
|
1365
1525
|
});
|
|
1366
1526
|
tabOpt(
|
|
1367
1527
|
program.command("keys <keys>").description("Press a single key or chord via trusted CDP input (e.g. Enter, Cmd+K).").addHelpText(
|
|
@@ -1378,9 +1538,7 @@ For typing text into a field, use \`chrome-relay type\` instead.
|
|
|
1378
1538
|
`
|
|
1379
1539
|
)
|
|
1380
1540
|
).action(async (keys, opts) => {
|
|
1381
|
-
|
|
1382
|
-
Object.assign(args, baseArgs(opts));
|
|
1383
|
-
await run("chrome_keyboard", args);
|
|
1541
|
+
await run("chrome_keyboard", withBase(opts, { keys }));
|
|
1384
1542
|
});
|
|
1385
1543
|
tabOpt(
|
|
1386
1544
|
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 +1557,9 @@ When to pick which:
|
|
|
1399
1557
|
`
|
|
1400
1558
|
)
|
|
1401
1559
|
).action(async (text, opts) => {
|
|
1402
|
-
const
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
await run("chrome_type", args);
|
|
1560
|
+
const extras = { text };
|
|
1561
|
+
if (opts.selector) extras.selector = opts.selector;
|
|
1562
|
+
await run("chrome_type", withBase(opts, extras));
|
|
1406
1563
|
});
|
|
1407
1564
|
tabOpt(
|
|
1408
1565
|
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 +1578,9 @@ Notes:
|
|
|
1421
1578
|
`
|
|
1422
1579
|
)
|
|
1423
1580
|
).action(async (code, opts) => {
|
|
1424
|
-
const
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
await run("chrome_evaluate", args);
|
|
1581
|
+
const extras = { code };
|
|
1582
|
+
if (typeof opts.timeoutMs === "number") extras.timeoutMs = opts.timeoutMs;
|
|
1583
|
+
await run("chrome_evaluate", withBase(opts, extras));
|
|
1428
1584
|
});
|
|
1429
1585
|
tabOpt(
|
|
1430
1586
|
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 +1596,20 @@ tooltip appearance, etc.) that a bare click would skip past too quickly.
|
|
|
1440
1596
|
`
|
|
1441
1597
|
)
|
|
1442
1598
|
).action(async (selector, opts) => {
|
|
1443
|
-
const
|
|
1444
|
-
|
|
1445
|
-
if (selector) args.selector = selector;
|
|
1599
|
+
const extras = {};
|
|
1600
|
+
if (selector) extras.selector = selector;
|
|
1446
1601
|
if (typeof opts.x === "number" && typeof opts.y === "number") {
|
|
1447
|
-
|
|
1448
|
-
|
|
1602
|
+
extras.x = opts.x;
|
|
1603
|
+
extras.y = opts.y;
|
|
1449
1604
|
}
|
|
1450
|
-
await run("chrome_hover",
|
|
1605
|
+
await run("chrome_hover", withBase(opts, extras));
|
|
1451
1606
|
});
|
|
1452
1607
|
}
|
|
1453
1608
|
|
|
1454
1609
|
// src/commands/capture.ts
|
|
1455
1610
|
import { writeFileSync } from "fs";
|
|
1456
1611
|
function registerCapture(ctx) {
|
|
1457
|
-
const { program,
|
|
1612
|
+
const { program, withBase, run } = ctx;
|
|
1458
1613
|
tabOpt(
|
|
1459
1614
|
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
1615
|
"after",
|
|
@@ -1473,13 +1628,13 @@ full-tab screenshot when an agent only needs to see one component.
|
|
|
1473
1628
|
`
|
|
1474
1629
|
)
|
|
1475
1630
|
).action(async (opts) => {
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1478
|
-
if (opts.
|
|
1479
|
-
if (opts.
|
|
1480
|
-
if (opts.
|
|
1481
|
-
if (typeof opts.
|
|
1482
|
-
|
|
1631
|
+
const extras = {};
|
|
1632
|
+
if (opts.full) extras.fullPage = true;
|
|
1633
|
+
if (opts.bbox) extras.bbox = opts.bbox;
|
|
1634
|
+
if (opts.selector) extras.selector = opts.selector;
|
|
1635
|
+
if (typeof opts.padding === "number") extras.padding = opts.padding;
|
|
1636
|
+
if (typeof opts.maxEdge === "number") extras.maxEdge = opts.maxEdge;
|
|
1637
|
+
const args = withBase(opts, extras);
|
|
1483
1638
|
try {
|
|
1484
1639
|
const result = await callTool("chrome_screenshot", args);
|
|
1485
1640
|
if (opts.out && result && typeof result === "object") {
|
|
@@ -1503,10 +1658,9 @@ full-tab screenshot when an agent only needs to see one component.
|
|
|
1503
1658
|
tabOpt(
|
|
1504
1659
|
program.command("read").description("Extract page structure and interactive elements.").option("-i, --interactive", "return only interactive elements")
|
|
1505
1660
|
).action(async (opts) => {
|
|
1506
|
-
const
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
await run("chrome_read_page", args);
|
|
1661
|
+
const extras = {};
|
|
1662
|
+
if (opts.interactive) extras.interactiveOnly = true;
|
|
1663
|
+
await run("chrome_read_page", withBase(opts, extras));
|
|
1510
1664
|
});
|
|
1511
1665
|
tabOpt(
|
|
1512
1666
|
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 +1678,11 @@ Notes:
|
|
|
1524
1678
|
`
|
|
1525
1679
|
)
|
|
1526
1680
|
).action(async (opts) => {
|
|
1527
|
-
const
|
|
1528
|
-
if (opts.interactiveOnly)
|
|
1529
|
-
if (opts.root)
|
|
1530
|
-
if (opts.includeSubframes)
|
|
1531
|
-
await run("chrome_ax",
|
|
1681
|
+
const extras = {};
|
|
1682
|
+
if (opts.interactiveOnly) extras.interactiveOnly = true;
|
|
1683
|
+
if (opts.root) extras.rootRole = opts.root;
|
|
1684
|
+
if (opts.includeSubframes) extras.includeSubframes = true;
|
|
1685
|
+
await run("chrome_ax", withBase(opts, extras));
|
|
1532
1686
|
});
|
|
1533
1687
|
tabOpt(
|
|
1534
1688
|
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 +1698,7 @@ Notes:
|
|
|
1544
1698
|
`
|
|
1545
1699
|
)
|
|
1546
1700
|
).action(async (opts) => {
|
|
1547
|
-
|
|
1548
|
-
args.node = opts.node;
|
|
1549
|
-
await run("chrome_click_ax", args);
|
|
1701
|
+
await run("chrome_click_ax", withBase(opts, { node: opts.node }));
|
|
1550
1702
|
});
|
|
1551
1703
|
const screencast = program.command("screencast").description("Record a tab via CDP (paint-driven). Requires an active tab.").addHelpText(
|
|
1552
1704
|
"after",
|
|
@@ -1571,20 +1723,18 @@ Notes:
|
|
|
1571
1723
|
tabOpt(
|
|
1572
1724
|
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
1725
|
).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);
|
|
1726
|
+
const extras = { action: "start" };
|
|
1727
|
+
if (opts.format) extras.format = opts.format;
|
|
1728
|
+
if (typeof opts.quality === "number") extras.quality = opts.quality;
|
|
1729
|
+
if (typeof opts.maxWidth === "number") extras.maxWidth = opts.maxWidth;
|
|
1730
|
+
if (typeof opts.maxHeight === "number") extras.maxHeight = opts.maxHeight;
|
|
1731
|
+
if (typeof opts.everyNth === "number") extras.everyNthFrame = opts.everyNth;
|
|
1732
|
+
await run("chrome_screencast", withBase(opts, extras));
|
|
1582
1733
|
});
|
|
1583
1734
|
tabOpt(
|
|
1584
1735
|
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
1736
|
).action(async (opts) => {
|
|
1586
|
-
const args = { action: "stop" };
|
|
1587
|
-
Object.assign(args, baseArgs(opts));
|
|
1737
|
+
const args = withBase(opts, { action: "stop" });
|
|
1588
1738
|
try {
|
|
1589
1739
|
const result = await callTool("chrome_screencast", args);
|
|
1590
1740
|
if (!opts.out) {
|
|
@@ -1719,7 +1869,7 @@ function netFilterArgs(opts) {
|
|
|
1719
1869
|
return a;
|
|
1720
1870
|
}
|
|
1721
1871
|
function registerSessions(ctx) {
|
|
1722
|
-
const { program,
|
|
1872
|
+
const { program, withBase, run } = ctx;
|
|
1723
1873
|
const viewport = program.command("viewport").description("Emulate device viewport, DPR, mobile flag, touch, and user agent.").addHelpText(
|
|
1724
1874
|
"after",
|
|
1725
1875
|
`
|
|
@@ -1740,27 +1890,22 @@ Notes:
|
|
|
1740
1890
|
tabOpt(
|
|
1741
1891
|
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
1892
|
).action(async (opts) => {
|
|
1743
|
-
const
|
|
1744
|
-
|
|
1745
|
-
if (opts.
|
|
1746
|
-
if (opts.
|
|
1747
|
-
if (opts.
|
|
1748
|
-
|
|
1749
|
-
await run("chrome_viewport", args);
|
|
1893
|
+
const extras = { action: "set", width: opts.width, height: opts.height };
|
|
1894
|
+
if (opts.dpr !== void 0) extras.dpr = opts.dpr;
|
|
1895
|
+
if (opts.mobile) extras.mobile = true;
|
|
1896
|
+
if (opts.touch) extras.hasTouch = true;
|
|
1897
|
+
if (opts.userAgent) extras.userAgent = opts.userAgent;
|
|
1898
|
+
await run("chrome_viewport", withBase(opts, extras));
|
|
1750
1899
|
});
|
|
1751
1900
|
tabOpt(
|
|
1752
1901
|
viewport.command("preset <name>").description("Apply a named device preset (iphone-14, pixel-7, desktop-1440, etc).")
|
|
1753
1902
|
).action(async (name, opts) => {
|
|
1754
|
-
|
|
1755
|
-
Object.assign(args, baseArgs(opts));
|
|
1756
|
-
await run("chrome_viewport", args);
|
|
1903
|
+
await run("chrome_viewport", withBase(opts, { action: "preset", name }));
|
|
1757
1904
|
});
|
|
1758
1905
|
tabOpt(
|
|
1759
1906
|
viewport.command("clear").description("Drop the viewport override and return the tab to its native size.")
|
|
1760
1907
|
).action(async (opts) => {
|
|
1761
|
-
|
|
1762
|
-
Object.assign(args, baseArgs(opts));
|
|
1763
|
-
await run("chrome_viewport", args);
|
|
1908
|
+
await run("chrome_viewport", withBase(opts, { action: "clear" }));
|
|
1764
1909
|
});
|
|
1765
1910
|
viewport.command("list").description("List available presets.").action(async () => {
|
|
1766
1911
|
await run("chrome_viewport", { action: "list" });
|
|
@@ -1821,8 +1966,7 @@ Notes:
|
|
|
1821
1966
|
`
|
|
1822
1967
|
);
|
|
1823
1968
|
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);
|
|
1969
|
+
const args = { action: "create", name, tabIds: String(opts.tabs) };
|
|
1826
1970
|
if (opts.color) args.color = opts.color;
|
|
1827
1971
|
if (opts.collapsed) args.collapsed = true;
|
|
1828
1972
|
await run("chrome_group", args);
|
|
@@ -1834,12 +1978,10 @@ Notes:
|
|
|
1834
1978
|
await run("chrome_group", { action: "close", name });
|
|
1835
1979
|
});
|
|
1836
1980
|
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 });
|
|
1981
|
+
await run("chrome_group", { action: "add", name, tabIds: String(opts.tabs) });
|
|
1839
1982
|
});
|
|
1840
1983
|
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 });
|
|
1984
|
+
await run("chrome_group", { action: "remove", tabIds: String(opts.tabs) });
|
|
1843
1985
|
});
|
|
1844
1986
|
const network = tabOpt(netFilterOpts(
|
|
1845
1987
|
program.command("network").description("Capture HTTP request/response metadata. Ring buffer, last 200 per tab.")
|
|
@@ -1869,41 +2011,38 @@ Notes:
|
|
|
1869
2011
|
WebSocket frames and SSE streams are out of scope.
|
|
1870
2012
|
`
|
|
1871
2013
|
).action(async (opts) => {
|
|
1872
|
-
|
|
1873
|
-
await run("chrome_network", args);
|
|
2014
|
+
await run("chrome_network", withBase(opts, netFilterArgs(opts)));
|
|
1874
2015
|
});
|
|
1875
2016
|
tabOpt(netFilterOpts(
|
|
1876
2017
|
network.command("read").description("(alias) list captured network entries.")
|
|
1877
2018
|
)).action(async (opts) => {
|
|
1878
|
-
|
|
1879
|
-
await run("chrome_network", args);
|
|
2019
|
+
await run("chrome_network", withBase(opts, netFilterArgs(opts)));
|
|
1880
2020
|
});
|
|
1881
2021
|
tabOpt(
|
|
1882
2022
|
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
2023
|
).action(async (requestId, opts) => {
|
|
1884
|
-
const
|
|
1885
|
-
if (opts.full)
|
|
1886
|
-
if (typeof opts.head === "number")
|
|
1887
|
-
await run("chrome_network",
|
|
2024
|
+
const extras = { action: "body", requestId };
|
|
2025
|
+
if (opts.full) extras.full = true;
|
|
2026
|
+
if (typeof opts.head === "number") extras.head = opts.head;
|
|
2027
|
+
await run("chrome_network", withBase(opts, extras));
|
|
1888
2028
|
});
|
|
1889
2029
|
tabOpt(netFilterOpts(
|
|
1890
2030
|
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
2031
|
)).action(async (opts) => {
|
|
1892
|
-
const
|
|
1893
|
-
if (opts.withBodies)
|
|
1894
|
-
if (opts.bestEffortBodies)
|
|
2032
|
+
const extras = { ...netFilterArgs(opts), action: "har" };
|
|
2033
|
+
if (opts.withBodies) extras.withBodies = true;
|
|
2034
|
+
if (opts.bestEffortBodies) extras.bestEffortBodies = true;
|
|
1895
2035
|
if (!opts.withBodies) {
|
|
1896
2036
|
process.stderr.write(
|
|
1897
2037
|
"[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
2038
|
);
|
|
1899
2039
|
}
|
|
1900
|
-
await run("chrome_network",
|
|
2040
|
+
await run("chrome_network", withBase(opts, extras));
|
|
1901
2041
|
});
|
|
1902
2042
|
tabOpt(
|
|
1903
2043
|
network.command("clear").description("Wipe the network buffer for this tab.")
|
|
1904
2044
|
).action(async (opts) => {
|
|
1905
|
-
|
|
1906
|
-
await run("chrome_network", args);
|
|
2045
|
+
await run("chrome_network", withBase(opts, { action: "clear" }));
|
|
1907
2046
|
});
|
|
1908
2047
|
tabOpt(
|
|
1909
2048
|
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 +2062,12 @@ Notes:
|
|
|
1923
2062
|
`
|
|
1924
2063
|
)
|
|
1925
2064
|
).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",
|
|
2065
|
+
const extras = {};
|
|
2066
|
+
if (opts.clear) extras.action = "clear";
|
|
2067
|
+
if (opts.level) extras.levels = opts.level;
|
|
2068
|
+
if (typeof opts.since === "number") extras.since = opts.since;
|
|
2069
|
+
if (typeof opts.limit === "number") extras.limit = opts.limit;
|
|
2070
|
+
await run("chrome_console", withBase(opts, extras));
|
|
1932
2071
|
});
|
|
1933
2072
|
}
|
|
1934
2073
|
|
|
@@ -1955,9 +2094,11 @@ Notes:
|
|
|
1955
2094
|
Tools attach via CDP and run on backgrounded tabs without stealing focus.
|
|
1956
2095
|
`
|
|
1957
2096
|
);
|
|
2097
|
+
const baseArgs = makeBaseArgs(program);
|
|
1958
2098
|
const ctx = {
|
|
1959
2099
|
program,
|
|
1960
|
-
baseArgs
|
|
2100
|
+
baseArgs,
|
|
2101
|
+
withBase: makeWithBase(baseArgs),
|
|
1961
2102
|
run: runTool
|
|
1962
2103
|
};
|
|
1963
2104
|
registerInstallUpdate(program);
|
package/dist/index.js
CHANGED
package/dist/native-host.js
CHANGED
|
@@ -6,6 +6,14 @@ import process from "process";
|
|
|
6
6
|
// src/http/server.ts
|
|
7
7
|
import Fastify from "fastify";
|
|
8
8
|
|
|
9
|
+
// ../protocol/dist/limits.js
|
|
10
|
+
var DEFAULT_TOOL_CALL_TIMEOUT_MS = 3e4;
|
|
11
|
+
var DEFAULT_PING_TIMEOUT_MS = 2e3;
|
|
12
|
+
var DEFAULT_READY_TIMEOUT_MS = 15e3;
|
|
13
|
+
var DEFAULT_BODY_PREVIEW_BYTES = 8 * 1024;
|
|
14
|
+
var NETWORK_BUFFER_MAX_BYTES = 512 * 1024;
|
|
15
|
+
var CONSOLE_BUFFER_MAX_BYTES = 256 * 1024;
|
|
16
|
+
|
|
9
17
|
// ../protocol/dist/index.js
|
|
10
18
|
var DEFAULT_HTTP_PORT = 12122;
|
|
11
19
|
var RelayError = class extends Error {
|
|
@@ -48,7 +56,7 @@ function toBridgeError(unknownErr, fallbackTool) {
|
|
|
48
56
|
}
|
|
49
57
|
|
|
50
58
|
// src/index.ts
|
|
51
|
-
var CHROME_RELAY_VERSION = true ? "0.5.
|
|
59
|
+
var CHROME_RELAY_VERSION = true ? "0.5.15" : "0.0.0-dev";
|
|
52
60
|
|
|
53
61
|
// src/release-notes.ts
|
|
54
62
|
function compareSemver(a, b) {
|
|
@@ -197,7 +205,7 @@ var ExtensionBridge = class {
|
|
|
197
205
|
pending.reject(new Error(message.payload.error));
|
|
198
206
|
}
|
|
199
207
|
}
|
|
200
|
-
async waitUntilReady(timeoutMs =
|
|
208
|
+
async waitUntilReady(timeoutMs = DEFAULT_READY_TIMEOUT_MS) {
|
|
201
209
|
if (this.ready) {
|
|
202
210
|
return;
|
|
203
211
|
}
|
|
@@ -213,7 +221,7 @@ var ExtensionBridge = class {
|
|
|
213
221
|
this.readyWaiters.add(onReady);
|
|
214
222
|
});
|
|
215
223
|
}
|
|
216
|
-
async ping(timeoutMs =
|
|
224
|
+
async ping(timeoutMs = DEFAULT_PING_TIMEOUT_MS) {
|
|
217
225
|
const id = randomUUID();
|
|
218
226
|
const message = { type: "bridge.ping", id };
|
|
219
227
|
return new Promise((resolve) => {
|
|
@@ -229,7 +237,7 @@ var ExtensionBridge = class {
|
|
|
229
237
|
this.send(message);
|
|
230
238
|
});
|
|
231
239
|
}
|
|
232
|
-
async callTool(name, args, timeoutMs =
|
|
240
|
+
async callTool(name, args, timeoutMs = DEFAULT_TOOL_CALL_TIMEOUT_MS) {
|
|
233
241
|
await this.waitUntilReady();
|
|
234
242
|
const id = randomUUID();
|
|
235
243
|
return new Promise((resolve, reject) => {
|