chrome-relay 0.5.11 → 0.5.13

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 CHANGED
@@ -9,6 +9,714 @@ var __export = (target, all) => {
9
9
  __defProp(target, name, { get: all[name], enumerable: true });
10
10
  };
11
11
 
12
+ // ../protocol/dist/args/shared.js
13
+ function asObject(input, tool) {
14
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
15
+ throw new RelayError({
16
+ code: "invalid_arguments",
17
+ message: `${tool}: arguments must be a JSON object.`,
18
+ tool,
19
+ phase: "parse_arguments",
20
+ details: { received: input },
21
+ retryable: false
22
+ });
23
+ }
24
+ return input;
25
+ }
26
+ function requireString(obj, key, tool) {
27
+ const v = obj[key];
28
+ if (typeof v !== "string" || !v) {
29
+ throw new RelayError({
30
+ code: "invalid_arguments",
31
+ message: `${tool}: \`${key}\` is required and must be a non-empty string.`,
32
+ tool,
33
+ phase: "parse_arguments",
34
+ details: { field: key, received: v },
35
+ retryable: false
36
+ });
37
+ }
38
+ return v;
39
+ }
40
+ function optString(obj, key) {
41
+ const v = obj[key];
42
+ return typeof v === "string" && v ? v : void 0;
43
+ }
44
+ function optNumber(obj, key) {
45
+ const v = obj[key];
46
+ return typeof v === "number" && Number.isFinite(v) ? v : void 0;
47
+ }
48
+ function optBool(obj, key) {
49
+ const v = obj[key];
50
+ return typeof v === "boolean" ? v : void 0;
51
+ }
52
+ function parseTargetArgs(obj) {
53
+ const out = {};
54
+ if (typeof obj.tabId === "number")
55
+ out.tabId = obj.tabId;
56
+ if (typeof obj.workspaceName === "string" && obj.workspaceName)
57
+ out.workspaceName = obj.workspaceName;
58
+ if (typeof obj.groupName === "string" && obj.groupName)
59
+ out.groupName = obj.groupName;
60
+ return out;
61
+ }
62
+ var init_shared = __esm({
63
+ "../protocol/dist/args/shared.js"() {
64
+ "use strict";
65
+ init_dist();
66
+ }
67
+ });
68
+
69
+ // ../protocol/dist/args/navigate.js
70
+ function parseChromeNavigateArgs(input) {
71
+ const obj = asObject(input, TOOL_NAMES.NAVIGATE);
72
+ const out = {
73
+ url: requireString(obj, "url", TOOL_NAMES.NAVIGATE),
74
+ ...parseTargetArgs(obj)
75
+ };
76
+ if (typeof obj.tabId === "string" && obj.tabId) {
77
+ const n = Number(obj.tabId);
78
+ if (!Number.isFinite(n)) {
79
+ throw new RelayError({
80
+ code: "invalid_arguments",
81
+ message: `chrome_navigate: invalid tabId ${JSON.stringify(obj.tabId)}. Expected a number.`,
82
+ tool: TOOL_NAMES.NAVIGATE,
83
+ phase: "parse_arguments",
84
+ details: { field: "tabId", received: obj.tabId },
85
+ retryable: false
86
+ });
87
+ }
88
+ out.tabId = n;
89
+ } else {
90
+ const n = optNumber(obj, "tabId");
91
+ if (n !== void 0)
92
+ out.tabId = n;
93
+ }
94
+ const newTab = optBool(obj, "newTab");
95
+ if (newTab !== void 0)
96
+ out.newTab = newTab;
97
+ const active = optBool(obj, "active");
98
+ if (active !== void 0)
99
+ out.active = active;
100
+ const allowPartial = optBool(obj, "allowPartial");
101
+ if (allowPartial !== void 0)
102
+ out.allowPartial = allowPartial;
103
+ void optString;
104
+ return out;
105
+ }
106
+ var init_navigate = __esm({
107
+ "../protocol/dist/args/navigate.js"() {
108
+ "use strict";
109
+ init_dist();
110
+ init_shared();
111
+ }
112
+ });
113
+
114
+ // ../protocol/dist/args/hover.js
115
+ function parseChromeHoverArgs(input) {
116
+ const obj = asObject(input, TOOL_NAMES.HOVER);
117
+ const target = parseTargetArgs(obj);
118
+ const x = optNumber(obj, "x");
119
+ const y = optNumber(obj, "y");
120
+ if (x !== void 0 && y !== void 0) {
121
+ return { ...target, kind: "coords", x, y };
122
+ }
123
+ const selector = optString(obj, "selector");
124
+ if (selector) {
125
+ return { ...target, kind: "selector", selector };
126
+ }
127
+ throw new RelayError({
128
+ code: "invalid_arguments",
129
+ message: "chrome_hover requires either a selector or x AND y.",
130
+ tool: TOOL_NAMES.HOVER,
131
+ phase: "parse_arguments",
132
+ details: { received: { selector: obj.selector, x: obj.x, y: obj.y } },
133
+ retryable: false
134
+ });
135
+ }
136
+ var init_hover = __esm({
137
+ "../protocol/dist/args/hover.js"() {
138
+ "use strict";
139
+ init_dist();
140
+ init_shared();
141
+ }
142
+ });
143
+
144
+ // ../protocol/dist/args/network.js
145
+ function parseFilter(obj) {
146
+ const out = {};
147
+ const filter = optString(obj, "filter");
148
+ if (filter)
149
+ out.filter = filter;
150
+ const method = optString(obj, "method");
151
+ if (method)
152
+ out.method = method;
153
+ const limit = optNumber(obj, "limit");
154
+ if (limit !== void 0)
155
+ out.limit = limit;
156
+ const status = obj.status;
157
+ if (status !== void 0 && status !== null) {
158
+ if (typeof status !== "string" || !VALID_STATUSES.includes(status)) {
159
+ throw new RelayError({
160
+ code: "invalid_arguments",
161
+ message: `chrome_network: invalid status ${JSON.stringify(status)}. Expected one of: ${VALID_STATUSES.join(", ")}.`,
162
+ tool: TOOL_NAMES.NETWORK,
163
+ phase: "parse_status",
164
+ details: { received: status, validChoices: VALID_STATUSES },
165
+ retryable: false
166
+ });
167
+ }
168
+ out.status = status;
169
+ }
170
+ return out;
171
+ }
172
+ function parseChromeNetworkArgs(input) {
173
+ const obj = asObject(input, TOOL_NAMES.NETWORK);
174
+ const target = parseTargetArgs(obj);
175
+ const rawAction = obj.action;
176
+ const action = typeof rawAction === "string" ? rawAction : "read";
177
+ if (action === "clear") {
178
+ return { ...target, action: "clear" };
179
+ }
180
+ if (action === "body") {
181
+ const requestId = optString(obj, "requestId");
182
+ if (!requestId) {
183
+ throw new RelayError({
184
+ code: "invalid_arguments",
185
+ message: "chrome_network body requires `requestId` (a non-empty string).",
186
+ tool: TOOL_NAMES.NETWORK,
187
+ phase: "parse_arguments",
188
+ details: { field: "requestId", received: obj.requestId },
189
+ retryable: false
190
+ });
191
+ }
192
+ const out = {
193
+ ...target,
194
+ action: "body",
195
+ requestId
196
+ };
197
+ const full = optBool(obj, "full");
198
+ if (full !== void 0)
199
+ out.full = full;
200
+ const head = optNumber(obj, "head");
201
+ if (head !== void 0)
202
+ out.head = head;
203
+ return out;
204
+ }
205
+ if (action === "har") {
206
+ const withBodies = optBool(obj, "withBodies");
207
+ const bestEffortBodies = optBool(obj, "bestEffortBodies");
208
+ return {
209
+ ...target,
210
+ action: "har",
211
+ ...withBodies !== void 0 ? { withBodies } : {},
212
+ ...bestEffortBodies !== void 0 ? { bestEffortBodies } : {},
213
+ ...parseFilter(obj)
214
+ };
215
+ }
216
+ if (action === "read") {
217
+ return { ...target, action: "read", ...parseFilter(obj) };
218
+ }
219
+ throw new RelayError({
220
+ code: "invalid_arguments",
221
+ message: `chrome_network: unknown action "${action}". Expected read | clear | har | body.`,
222
+ tool: TOOL_NAMES.NETWORK,
223
+ phase: "parse_action",
224
+ details: { received: action, validChoices: ["read", "clear", "har", "body"] },
225
+ retryable: false
226
+ });
227
+ }
228
+ var VALID_STATUSES;
229
+ var init_network = __esm({
230
+ "../protocol/dist/args/network.js"() {
231
+ "use strict";
232
+ init_dist();
233
+ init_shared();
234
+ VALID_STATUSES = ["ok", "redirect", "client_error", "server_error", "failed"];
235
+ }
236
+ });
237
+
238
+ // ../protocol/dist/args/simple.js
239
+ function parseGetWindowsAndTabsArgs(_input) {
240
+ return {};
241
+ }
242
+ function parseChromeSelfReloadArgs(_input) {
243
+ return {};
244
+ }
245
+ function parseChromeReadPageArgs(input) {
246
+ const obj = asObject(input, TOOL_NAMES.READ_PAGE);
247
+ const out = { ...parseTargetArgs(obj) };
248
+ const io = optBool(obj, "interactiveOnly");
249
+ if (io !== void 0)
250
+ out.interactiveOnly = io;
251
+ return out;
252
+ }
253
+ function parseChromeClickArgs(input) {
254
+ const obj = asObject(input, TOOL_NAMES.CLICK);
255
+ return {
256
+ selector: requireString(obj, "selector", TOOL_NAMES.CLICK),
257
+ ...parseTargetArgs(obj)
258
+ };
259
+ }
260
+ function parseChromeFillArgs(input) {
261
+ const obj = asObject(input, TOOL_NAMES.FILL);
262
+ if (typeof obj.value !== "string") {
263
+ throw new RelayError({
264
+ code: "invalid_arguments",
265
+ message: `${TOOL_NAMES.FILL}: \`value\` is required and must be a string (empty string allowed).`,
266
+ tool: TOOL_NAMES.FILL,
267
+ phase: "parse_arguments",
268
+ details: { field: "value", received: obj.value },
269
+ retryable: false
270
+ });
271
+ }
272
+ return {
273
+ selector: requireString(obj, "selector", TOOL_NAMES.FILL),
274
+ value: obj.value,
275
+ ...parseTargetArgs(obj)
276
+ };
277
+ }
278
+ function parseChromeKeyboardArgs(input) {
279
+ const obj = asObject(input, TOOL_NAMES.KEYBOARD);
280
+ return {
281
+ keys: requireString(obj, "keys", TOOL_NAMES.KEYBOARD),
282
+ ...parseTargetArgs(obj)
283
+ };
284
+ }
285
+ function parseChromeTypeArgs(input) {
286
+ const obj = asObject(input, TOOL_NAMES.TYPE);
287
+ const out = {
288
+ text: requireString(obj, "text", TOOL_NAMES.TYPE),
289
+ ...parseTargetArgs(obj)
290
+ };
291
+ const selector = optString(obj, "selector");
292
+ if (selector)
293
+ out.selector = selector;
294
+ return out;
295
+ }
296
+ function parseChromeEvaluateArgs(input) {
297
+ const obj = asObject(input, TOOL_NAMES.EVALUATE);
298
+ const out = {
299
+ code: requireString(obj, "code", TOOL_NAMES.EVALUATE),
300
+ ...parseTargetArgs(obj)
301
+ };
302
+ const t = optNumber(obj, "timeoutMs");
303
+ if (t !== void 0)
304
+ out.timeoutMs = t;
305
+ return out;
306
+ }
307
+ function parseChromeSwitchTabArgs(input) {
308
+ const obj = asObject(input, TOOL_NAMES.SWITCH_TAB);
309
+ const tabId = Number(obj.tabId);
310
+ if (!Number.isFinite(tabId)) {
311
+ throw new RelayError({
312
+ code: "invalid_arguments",
313
+ message: `${TOOL_NAMES.SWITCH_TAB} requires a numeric tabId.`,
314
+ tool: TOOL_NAMES.SWITCH_TAB,
315
+ phase: "parse_arguments",
316
+ details: { received: obj.tabId },
317
+ retryable: false
318
+ });
319
+ }
320
+ return { tabId };
321
+ }
322
+ function parseChromeCloseTabsArgs(input) {
323
+ const obj = asObject(input, TOOL_NAMES.CLOSE_TABS);
324
+ if (!Array.isArray(obj.tabIds)) {
325
+ throw new RelayError({
326
+ code: "invalid_arguments",
327
+ message: `${TOOL_NAMES.CLOSE_TABS} requires a numeric tabIds array.`,
328
+ tool: TOOL_NAMES.CLOSE_TABS,
329
+ phase: "parse_arguments",
330
+ details: { field: "tabIds", received: obj.tabIds },
331
+ retryable: false
332
+ });
333
+ }
334
+ const tabIds = obj.tabIds.map((v) => Number(v));
335
+ if (tabIds.length === 0 || tabIds.some((n) => !Number.isFinite(n))) {
336
+ throw new RelayError({
337
+ code: "invalid_arguments",
338
+ message: `${TOOL_NAMES.CLOSE_TABS} requires a non-empty array of numeric tab IDs.`,
339
+ tool: TOOL_NAMES.CLOSE_TABS,
340
+ phase: "parse_arguments",
341
+ details: { received: obj.tabIds },
342
+ retryable: false
343
+ });
344
+ }
345
+ return { tabIds };
346
+ }
347
+ function parseChromeAxArgs(input) {
348
+ const obj = asObject(input, TOOL_NAMES.AX);
349
+ const out = { ...parseTargetArgs(obj) };
350
+ const io = optBool(obj, "interactiveOnly");
351
+ if (io !== void 0)
352
+ out.interactiveOnly = io;
353
+ const root = optString(obj, "rootRole");
354
+ if (root)
355
+ out.rootRole = root;
356
+ const is = optBool(obj, "includeSubframes");
357
+ if (is !== void 0)
358
+ out.includeSubframes = is;
359
+ return out;
360
+ }
361
+ function parseChromeClickAxArgs(input) {
362
+ const obj = asObject(input, TOOL_NAMES.CLICK_AX);
363
+ const node = Number(obj.node ?? obj.id);
364
+ if (!Number.isFinite(node) || node <= 0) {
365
+ throw new RelayError({
366
+ code: "invalid_arguments",
367
+ message: `${TOOL_NAMES.CLICK_AX} requires \`node\` (a positive backendDOMNodeId from chrome_ax).`,
368
+ tool: TOOL_NAMES.CLICK_AX,
369
+ phase: "parse_arguments",
370
+ details: { received: obj.node ?? obj.id },
371
+ retryable: false
372
+ });
373
+ }
374
+ return { node, ...parseTargetArgs(obj) };
375
+ }
376
+ function parseChromeScreenshotArgs(input) {
377
+ const obj = asObject(input, TOOL_NAMES.SCREENSHOT);
378
+ const out = { ...parseTargetArgs(obj) };
379
+ const fp = optBool(obj, "fullPage");
380
+ if (fp !== void 0)
381
+ out.fullPage = fp;
382
+ const bbox = optString(obj, "bbox");
383
+ if (bbox)
384
+ out.bbox = bbox;
385
+ const sel = optString(obj, "selector");
386
+ if (sel)
387
+ out.selector = sel;
388
+ const pad = optNumber(obj, "padding");
389
+ if (pad !== void 0)
390
+ out.padding = pad;
391
+ const me = optNumber(obj, "maxEdge");
392
+ if (me !== void 0 && me > 0)
393
+ out.maxEdge = me;
394
+ return out;
395
+ }
396
+ var init_simple = __esm({
397
+ "../protocol/dist/args/simple.js"() {
398
+ "use strict";
399
+ init_dist();
400
+ init_shared();
401
+ }
402
+ });
403
+
404
+ // ../protocol/dist/args/multi.js
405
+ function invalidAction(tool, received, expected) {
406
+ throw new RelayError({
407
+ code: "invalid_arguments",
408
+ message: `${tool}: unknown action ${JSON.stringify(received)}. Expected ${expected.join(" | ")}.`,
409
+ tool,
410
+ phase: "parse_action",
411
+ details: { received, validChoices: expected },
412
+ retryable: false
413
+ });
414
+ }
415
+ function parseChromeViewportArgs(input) {
416
+ const obj = asObject(input, TOOL_NAMES.VIEWPORT);
417
+ const action = typeof obj.action === "string" ? obj.action : "set";
418
+ if (!VALID_VIEWPORT_ACTIONS.includes(action)) {
419
+ invalidAction(TOOL_NAMES.VIEWPORT, action, VALID_VIEWPORT_ACTIONS);
420
+ }
421
+ const target = parseTargetArgs(obj);
422
+ if (action === "list")
423
+ return { action: "list" };
424
+ if (action === "clear")
425
+ return { ...target, action: "clear" };
426
+ if (action === "preset") {
427
+ return { ...target, action: "preset", name: requireString(obj, "name", TOOL_NAMES.VIEWPORT) };
428
+ }
429
+ const width = Number(obj.width);
430
+ const height = Number(obj.height);
431
+ if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
432
+ throw new RelayError({
433
+ code: "invalid_arguments",
434
+ message: `${TOOL_NAMES.VIEWPORT} set requires positive numeric width and height.`,
435
+ tool: TOOL_NAMES.VIEWPORT,
436
+ phase: "parse_dimensions",
437
+ details: { received: { width: obj.width, height: obj.height } },
438
+ retryable: false
439
+ });
440
+ }
441
+ const out = {
442
+ ...target,
443
+ action: "set",
444
+ width,
445
+ height
446
+ };
447
+ const dpr = optNumber(obj, "dpr");
448
+ if (dpr !== void 0)
449
+ out.dpr = dpr;
450
+ const mobile = optBool(obj, "mobile");
451
+ if (mobile !== void 0)
452
+ out.mobile = mobile;
453
+ const hasTouch = optBool(obj, "hasTouch");
454
+ if (hasTouch !== void 0)
455
+ out.hasTouch = hasTouch;
456
+ const userAgent = optString(obj, "userAgent");
457
+ if (userAgent)
458
+ out.userAgent = userAgent;
459
+ return out;
460
+ }
461
+ function parseLevels(input) {
462
+ if (input === void 0 || input === null)
463
+ return void 0;
464
+ const valid = new Set(VALID_CONSOLE_LEVELS);
465
+ const verify = (s) => {
466
+ if (typeof s !== "string" || !valid.has(s)) {
467
+ throw new RelayError({
468
+ code: "invalid_arguments",
469
+ message: `${TOOL_NAMES.CONSOLE}: invalid level ${JSON.stringify(s)}. Expected one of: ${VALID_CONSOLE_LEVELS.join(", ")}.`,
470
+ tool: TOOL_NAMES.CONSOLE,
471
+ phase: "parse_levels",
472
+ details: { received: s, validChoices: VALID_CONSOLE_LEVELS },
473
+ retryable: false
474
+ });
475
+ }
476
+ return s;
477
+ };
478
+ if (typeof input === "string")
479
+ return input.split(",").map((s) => verify(s.trim()));
480
+ if (Array.isArray(input))
481
+ return input.map(verify);
482
+ throw new RelayError({
483
+ code: "invalid_arguments",
484
+ message: `${TOOL_NAMES.CONSOLE}: invalid levels argument ${JSON.stringify(input)}. Expected a comma-separated string or an array of strings.`,
485
+ tool: TOOL_NAMES.CONSOLE,
486
+ phase: "parse_levels",
487
+ details: { received: input },
488
+ retryable: false
489
+ });
490
+ }
491
+ function parseChromeConsoleArgs(input) {
492
+ const obj = asObject(input, TOOL_NAMES.CONSOLE);
493
+ const target = parseTargetArgs(obj);
494
+ const action = typeof obj.action === "string" ? obj.action : "read";
495
+ if (!VALID_CONSOLE_ACTIONS.includes(action)) {
496
+ invalidAction(TOOL_NAMES.CONSOLE, action, VALID_CONSOLE_ACTIONS);
497
+ }
498
+ if (action === "clear")
499
+ return { ...target, action: "clear" };
500
+ const out = { ...target, action: "read" };
501
+ const levels = parseLevels(obj.levels);
502
+ if (levels)
503
+ out.levels = levels;
504
+ const since = optNumber(obj, "since");
505
+ if (since !== void 0)
506
+ out.since = since;
507
+ const limit = optNumber(obj, "limit");
508
+ if (limit !== void 0)
509
+ out.limit = limit;
510
+ return out;
511
+ }
512
+ function parseChromeWorkspaceArgs(input) {
513
+ const obj = asObject(input, TOOL_NAMES.WORKSPACE);
514
+ const action = typeof obj.action === "string" ? obj.action : "list";
515
+ if (!VALID_WORKSPACE_ACTIONS.includes(action)) {
516
+ invalidAction(TOOL_NAMES.WORKSPACE, action, VALID_WORKSPACE_ACTIONS);
517
+ }
518
+ if (action === "list")
519
+ return { action: "list" };
520
+ if (action === "close")
521
+ return { action: "close", name: requireString(obj, "name", TOOL_NAMES.WORKSPACE) };
522
+ const out = {
523
+ action: "create",
524
+ name: requireString(obj, "name", TOOL_NAMES.WORKSPACE)
525
+ };
526
+ const url = optString(obj, "url");
527
+ if (url)
528
+ out.url = url;
529
+ const label = optString(obj, "label");
530
+ if (label)
531
+ out.label = label;
532
+ return out;
533
+ }
534
+ 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
+ if (Array.isArray(raw))
552
+ return raw.map(coerce);
553
+ if (typeof raw === "string")
554
+ return raw.split(",").map(coerce);
555
+ if (typeof raw === "number")
556
+ return [raw];
557
+ return [];
558
+ }
559
+ function parseColor(raw) {
560
+ if (raw === void 0 || raw === null)
561
+ return void 0;
562
+ if (typeof raw !== "string") {
563
+ throw new RelayError({
564
+ code: "invalid_arguments",
565
+ message: `${TOOL_NAMES.GROUP}: invalid color ${JSON.stringify(raw)}. Expected one of: ${VALID_GROUP_COLORS.join(", ")}.`,
566
+ tool: TOOL_NAMES.GROUP,
567
+ phase: "parse_color",
568
+ details: { received: raw, validChoices: VALID_GROUP_COLORS },
569
+ retryable: false
570
+ });
571
+ }
572
+ const c = raw.toLowerCase();
573
+ if (!VALID_GROUP_COLORS.includes(c)) {
574
+ throw new RelayError({
575
+ code: "invalid_arguments",
576
+ message: `${TOOL_NAMES.GROUP}: invalid color "${raw}". Expected one of: ${VALID_GROUP_COLORS.join(", ")}.`,
577
+ tool: TOOL_NAMES.GROUP,
578
+ phase: "parse_color",
579
+ details: { received: raw, validChoices: VALID_GROUP_COLORS },
580
+ retryable: false
581
+ });
582
+ }
583
+ return c;
584
+ }
585
+ function parseChromeGroupArgs(input) {
586
+ const obj = asObject(input, TOOL_NAMES.GROUP);
587
+ const action = typeof obj.action === "string" ? obj.action : "list";
588
+ if (!VALID_GROUP_ACTIONS.includes(action)) {
589
+ invalidAction(TOOL_NAMES.GROUP, action, VALID_GROUP_ACTIONS);
590
+ }
591
+ if (action === "list")
592
+ return { action: "list" };
593
+ if (action === "close")
594
+ return { action: "close", name: requireString(obj, "name", TOOL_NAMES.GROUP) };
595
+ if (action === "remove") {
596
+ const tabIds2 = parseTabIds(obj.tabIds);
597
+ if (tabIds2.length === 0) {
598
+ throw new RelayError({
599
+ code: "invalid_arguments",
600
+ message: `${TOOL_NAMES.GROUP} remove requires tabIds.`,
601
+ tool: TOOL_NAMES.GROUP,
602
+ phase: "parse_arguments",
603
+ details: { field: "tabIds" },
604
+ retryable: false
605
+ });
606
+ }
607
+ return { action: "remove", tabIds: tabIds2 };
608
+ }
609
+ if (action === "add") {
610
+ const tabIds2 = parseTabIds(obj.tabIds);
611
+ if (tabIds2.length === 0) {
612
+ throw new RelayError({
613
+ code: "invalid_arguments",
614
+ message: `${TOOL_NAMES.GROUP} add requires tabIds.`,
615
+ tool: TOOL_NAMES.GROUP,
616
+ phase: "parse_arguments",
617
+ details: { field: "tabIds" },
618
+ retryable: false
619
+ });
620
+ }
621
+ return { action: "add", name: requireString(obj, "name", TOOL_NAMES.GROUP), tabIds: tabIds2 };
622
+ }
623
+ const tabIds = parseTabIds(obj.tabIds);
624
+ if (tabIds.length === 0) {
625
+ throw new RelayError({
626
+ code: "invalid_arguments",
627
+ message: `${TOOL_NAMES.GROUP} create requires at least one tabId.`,
628
+ tool: TOOL_NAMES.GROUP,
629
+ phase: "parse_arguments",
630
+ details: { field: "tabIds" },
631
+ retryable: false
632
+ });
633
+ }
634
+ const out = {
635
+ action: "create",
636
+ name: requireString(obj, "name", TOOL_NAMES.GROUP),
637
+ tabIds
638
+ };
639
+ const color = parseColor(obj.color);
640
+ if (color)
641
+ out.color = color;
642
+ const collapsed = optBool(obj, "collapsed");
643
+ if (collapsed !== void 0)
644
+ out.collapsed = collapsed;
645
+ const windowId = optNumber(obj, "windowId");
646
+ if (windowId !== void 0)
647
+ out.windowId = windowId;
648
+ return out;
649
+ }
650
+ function parseChromeScreencastArgs(input) {
651
+ const obj = asObject(input, TOOL_NAMES.SCREENCAST);
652
+ const target = parseTargetArgs(obj);
653
+ const action = typeof obj.action === "string" ? obj.action : "start";
654
+ if (!VALID_SCREENCAST_ACTIONS.includes(action)) {
655
+ invalidAction(TOOL_NAMES.SCREENCAST, action, VALID_SCREENCAST_ACTIONS);
656
+ }
657
+ if (action === "stop")
658
+ return { ...target, action: "stop" };
659
+ const out = {
660
+ ...target,
661
+ action: "start"
662
+ };
663
+ if (obj.format !== void 0 && obj.format !== null) {
664
+ if (obj.format !== "jpeg" && obj.format !== "png") {
665
+ throw new RelayError({
666
+ code: "invalid_arguments",
667
+ message: `${TOOL_NAMES.SCREENCAST}: invalid format ${JSON.stringify(obj.format)}. Expected "jpeg" or "png".`,
668
+ tool: TOOL_NAMES.SCREENCAST,
669
+ phase: "parse_format",
670
+ details: { received: obj.format, validChoices: VALID_SCREENCAST_FORMATS },
671
+ retryable: false
672
+ });
673
+ }
674
+ out.format = obj.format;
675
+ }
676
+ const q = optNumber(obj, "quality");
677
+ if (q !== void 0)
678
+ out.quality = q;
679
+ const mw = optNumber(obj, "maxWidth");
680
+ if (mw !== void 0)
681
+ out.maxWidth = mw;
682
+ const mh = optNumber(obj, "maxHeight");
683
+ if (mh !== void 0)
684
+ out.maxHeight = mh;
685
+ const en = optNumber(obj, "everyNthFrame");
686
+ if (en !== void 0)
687
+ out.everyNthFrame = en;
688
+ return out;
689
+ }
690
+ 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;
691
+ var init_multi = __esm({
692
+ "../protocol/dist/args/multi.js"() {
693
+ "use strict";
694
+ init_dist();
695
+ init_shared();
696
+ VALID_VIEWPORT_ACTIONS = ["set", "preset", "clear", "list"];
697
+ VALID_CONSOLE_ACTIONS = ["read", "clear"];
698
+ VALID_CONSOLE_LEVELS = ["log", "info", "warn", "error", "debug", "exception"];
699
+ VALID_WORKSPACE_ACTIONS = ["create", "list", "close"];
700
+ VALID_GROUP_ACTIONS = ["create", "list", "close", "add", "remove"];
701
+ VALID_GROUP_COLORS = ["grey", "blue", "red", "yellow", "green", "pink", "purple", "cyan", "orange"];
702
+ VALID_SCREENCAST_ACTIONS = ["start", "stop"];
703
+ VALID_SCREENCAST_FORMATS = ["jpeg", "png"];
704
+ }
705
+ });
706
+
707
+ // ../protocol/dist/args/index.js
708
+ var init_args = __esm({
709
+ "../protocol/dist/args/index.js"() {
710
+ "use strict";
711
+ init_shared();
712
+ init_navigate();
713
+ init_hover();
714
+ init_network();
715
+ init_simple();
716
+ init_multi();
717
+ }
718
+ });
719
+
12
720
  // ../protocol/dist/index.js
13
721
  var dist_exports = {};
14
722
  __export(dist_exports, {
@@ -21,6 +729,33 @@ __export(dist_exports, {
21
729
  NATIVE_HOST_NAME: () => NATIVE_HOST_NAME,
22
730
  RelayError: () => RelayError,
23
731
  TOOL_NAMES: () => TOOL_NAMES,
732
+ asObject: () => asObject,
733
+ optBool: () => optBool,
734
+ optNumber: () => optNumber,
735
+ optString: () => optString,
736
+ parseChromeAxArgs: () => parseChromeAxArgs,
737
+ parseChromeClickArgs: () => parseChromeClickArgs,
738
+ parseChromeClickAxArgs: () => parseChromeClickAxArgs,
739
+ parseChromeCloseTabsArgs: () => parseChromeCloseTabsArgs,
740
+ parseChromeConsoleArgs: () => parseChromeConsoleArgs,
741
+ parseChromeEvaluateArgs: () => parseChromeEvaluateArgs,
742
+ parseChromeFillArgs: () => parseChromeFillArgs,
743
+ parseChromeGroupArgs: () => parseChromeGroupArgs,
744
+ parseChromeHoverArgs: () => parseChromeHoverArgs,
745
+ parseChromeKeyboardArgs: () => parseChromeKeyboardArgs,
746
+ parseChromeNavigateArgs: () => parseChromeNavigateArgs,
747
+ parseChromeNetworkArgs: () => parseChromeNetworkArgs,
748
+ parseChromeReadPageArgs: () => parseChromeReadPageArgs,
749
+ parseChromeScreencastArgs: () => parseChromeScreencastArgs,
750
+ parseChromeScreenshotArgs: () => parseChromeScreenshotArgs,
751
+ parseChromeSelfReloadArgs: () => parseChromeSelfReloadArgs,
752
+ parseChromeSwitchTabArgs: () => parseChromeSwitchTabArgs,
753
+ parseChromeTypeArgs: () => parseChromeTypeArgs,
754
+ parseChromeViewportArgs: () => parseChromeViewportArgs,
755
+ parseChromeWorkspaceArgs: () => parseChromeWorkspaceArgs,
756
+ parseGetWindowsAndTabsArgs: () => parseGetWindowsAndTabsArgs,
757
+ parseTargetArgs: () => parseTargetArgs,
758
+ requireString: () => requireString,
24
759
  toBridgeError: () => toBridgeError
25
760
  });
26
761
  function toBridgeError(unknownErr, fallbackTool) {
@@ -39,6 +774,7 @@ var NATIVE_HOST_NAME, DEFAULT_HTTP_PORT, CHROME_WEB_STORE_EXTENSION_ID, LEGACY_D
39
774
  var init_dist = __esm({
40
775
  "../protocol/dist/index.js"() {
41
776
  "use strict";
777
+ init_args();
42
778
  NATIVE_HOST_NAME = "dev.chrome_relay.native_host";
43
779
  DEFAULT_HTTP_PORT = 12122;
44
780
  CHROME_WEB_STORE_EXTENSION_ID = "cpdiapbifblhlcpnmlmfpgfjlacebokb";
@@ -130,7 +866,7 @@ var init_dist = __esm({
130
866
  import { Command } from "commander";
131
867
 
132
868
  // src/index.ts
133
- var CHROME_RELAY_VERSION = true ? "0.5.11" : "0.0.0-dev";
869
+ var CHROME_RELAY_VERSION = true ? "0.5.13" : "0.0.0-dev";
134
870
 
135
871
  // src/commands/shared.ts
136
872
  init_dist();
@@ -364,6 +1100,19 @@ async function runDoctor() {
364
1100
 
365
1101
  // src/release-notes.ts
366
1102
  var RELEASE_NOTES = {
1103
+ "0.5.13": [
1104
+ "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
+ "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.",
1106
+ "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.",
1107
+ "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.",
1108
+ "36 new tests in packages/protocol/test/args-all.test.ts. Total now 433 (was 397)."
1109
+ ],
1110
+ "0.5.12": [
1111
+ "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.",
1112
+ "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.",
1113
+ "chrome_hover handler refactored to use the new parser end-to-end. Hover args now collapse into a discriminated union (`{kind:'selector', selector}` | `{kind:'coords', x, y}`) so the handler branches without re-doing the typeof dance.",
1114
+ "19 new tests in packages/protocol/test/args.test.ts. Total now 397."
1115
+ ],
367
1116
  "0.5.11": [
368
1117
  "Tests-only: 6 new edge-case tests for `chrome-relay update`. Covers --dry-run, install failure, install-success-but-binary-version-unchanged (PATH mismatch / stale shim), install-success-but-which-fails, install-success-but-release-notes-parse-fails, and the happy path. Locks in the structured-metadata contract from 0.5.7.",
369
1118
  "Total tests now 378 (was 372)."
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- var CHROME_RELAY_VERSION = true ? "0.5.11" : "0.0.0-dev";
2
+ var CHROME_RELAY_VERSION = true ? "0.5.13" : "0.0.0-dev";
3
3
  export {
4
4
  CHROME_RELAY_VERSION
5
5
  };
@@ -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.11" : "0.0.0-dev";
51
+ var CHROME_RELAY_VERSION = true ? "0.5.13" : "0.0.0-dev";
52
52
 
53
53
  // src/release-notes.ts
54
54
  function compareSemver(a, b) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chrome-relay",
3
- "version": "0.5.11",
3
+ "version": "0.5.13",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",