chrome-relay 0.5.6 → 0.5.8

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
@@ -4,6 +4,15 @@
4
4
  import { Command } from "commander";
5
5
  import { writeFileSync } from "fs";
6
6
 
7
+ // src/index.ts
8
+ var CHROME_RELAY_VERSION = true ? "0.5.8" : "0.0.0-dev";
9
+
10
+ // src/install/install.ts
11
+ import os from "os";
12
+ import path from "path";
13
+ import { chmod, mkdir, readFile, stat, writeFile } from "fs/promises";
14
+ import { fileURLToPath } from "url";
15
+
7
16
  // ../protocol/dist/index.js
8
17
  var NATIVE_HOST_NAME = "dev.chrome_relay.native_host";
9
18
  var DEFAULT_HTTP_PORT = 12122;
@@ -42,14 +51,7 @@ var RelayError = class extends Error {
42
51
  }
43
52
  };
44
53
 
45
- // src/index.ts
46
- var CHROME_RELAY_VERSION = true ? "0.5.6" : "0.0.0-dev";
47
-
48
54
  // src/install/install.ts
49
- import os from "os";
50
- import path from "path";
51
- import { chmod, mkdir, readFile, stat, writeFile } from "fs/promises";
52
- import { fileURLToPath } from "url";
53
55
  var APP_DIR = path.join(os.homedir(), ".chrome-relay");
54
56
  var KNOWN_EXTENSION_IDS = [
55
57
  ["Chrome Web Store", CHROME_WEB_STORE_EXTENSION_ID],
@@ -196,6 +198,16 @@ async function callTool(name, args) {
196
198
 
197
199
  // src/release-notes.ts
198
200
  var RELEASE_NOTES = {
201
+ "0.5.8": [
202
+ "Internal refactor (code-quality-hardening PR 6, first cut): shared CLI helpers moved out of program.ts into packages/cli/src/commands/shared.ts.",
203
+ "tabOpt(), makeBaseArgs(program), and runTool() are now importable from `./commands/shared.js`. program.ts dropped ~100 lines.",
204
+ "No behavior change \u2014 all 355 tests still pass. Future PRs can split per-domain command groups (navigation, input, capture, sessions) into their own modules without churning helpers."
205
+ ],
206
+ "0.5.7": [
207
+ "`chrome-relay update` returns structured verification metadata (code-quality-hardening PR 5). Output now has `install: { attempted, packageManager, status, command }`, `binary: { path, reexeced }`, `releaseNotes: { source: 'current_process' | 'updated_binary', changes }`, and a `warnings[]` array.",
208
+ "Surfaces the 'install said success but binary didn't change' failure mode (PATH mismatch, stale shim, cross-package-manager confusion) as `warnings[].code === 'update_not_verified'`. Agents can branch on it.",
209
+ "Falls back gracefully: when the re-exec can't be proven, release notes are read from the current (pre-update) process and marked `source: 'current_process'`. The agent knows the bullets may be stale."
210
+ ],
199
211
  "0.5.6": [
200
212
  "BEHAVIOR CHANGE \u2014 `chrome-relay network har --with-bodies` is now strict by default (code-quality-hardening PR 4). When ANY body fails to fetch, the call throws `partial_success_disallowed` with details about which entries failed.",
201
213
  "New `--best-effort-bodies` flag restores the legacy behavior: HAR still emits, missing/errored bodies are recorded per-entry in `_chrome_relay.bodyState` and `_chrome_relay.bodyError` (with code, message, and phase).",
@@ -261,6 +273,81 @@ function listReleaseNotesSince(since) {
261
273
  return Object.keys(RELEASE_NOTES).filter((v) => compareSemver(v, since) > 0).sort((a, b) => compareSemver(a, b)).map((version) => ({ version, bullets: RELEASE_NOTES[version] }));
262
274
  }
263
275
 
276
+ // src/commands/shared.ts
277
+ function tabOpt(cmd) {
278
+ 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`)");
279
+ }
280
+ function makeBaseArgs(program) {
281
+ return function baseArgs(opts) {
282
+ const parentOpts = program.opts();
283
+ rejectIntraScopeConflict("subcommand", {
284
+ tab: opts.tab,
285
+ workspace: opts.workspace,
286
+ group: opts.group
287
+ });
288
+ rejectIntraScopeConflict("program-level", {
289
+ workspace: parentOpts.workspace,
290
+ group: parentOpts.group
291
+ });
292
+ if (opts.workspace && parentOpts.workspace && opts.workspace !== parentOpts.workspace) {
293
+ emitTargetOverride("workspace", parentOpts.workspace, opts.workspace);
294
+ }
295
+ if (opts.group && parentOpts.group && opts.group !== parentOpts.group) {
296
+ emitTargetOverride("group", parentOpts.group, opts.group);
297
+ }
298
+ if (opts.tab !== void 0 && (parentOpts.workspace || parentOpts.group)) {
299
+ const prior = parentOpts.workspace ? `workspace=${parentOpts.workspace}` : `group=${parentOpts.group}`;
300
+ emitTargetOverride("tab", prior, String(opts.tab));
301
+ }
302
+ const args = {};
303
+ if (opts.tab !== void 0) args.tabId = opts.tab;
304
+ const effectiveWorkspace = opts.workspace ?? parentOpts.workspace;
305
+ const effectiveGroup = opts.group ?? parentOpts.group;
306
+ if (opts.tab === void 0 && effectiveWorkspace) args.workspaceName = effectiveWorkspace;
307
+ if (opts.tab === void 0 && effectiveGroup) args.groupName = effectiveGroup;
308
+ return args;
309
+ };
310
+ }
311
+ function rejectIntraScopeConflict(scope, fields) {
312
+ const present = [];
313
+ if (fields.tab !== void 0) present.push("--tab");
314
+ if (fields.workspace) present.push("--workspace");
315
+ if (fields.group) present.push("--group");
316
+ if (present.length > 1) {
317
+ process.stderr.write(
318
+ `[chrome-relay] target_conflict: ${scope} flags ${present.join(" + ")} are mutually exclusive. Pass exactly one of --tab, --workspace, or --group on the same ${scope}.
319
+ `
320
+ );
321
+ process.exit(2);
322
+ }
323
+ }
324
+ function emitTargetOverride(kind, from, to) {
325
+ process.stderr.write(
326
+ `[chrome-relay] target_overridden: ${kind} ${from} \u2192 ${to} (subcommand-level overrides program-level)
327
+ `
328
+ );
329
+ }
330
+ async function runTool(name, args) {
331
+ try {
332
+ const result = await callTool(name, args);
333
+ if (typeof result === "string") {
334
+ process.stdout.write(result + "\n");
335
+ } else {
336
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
337
+ }
338
+ } catch (error) {
339
+ if (error instanceof RelayError) {
340
+ process.stderr.write(error.message + "\n");
341
+ process.stderr.write(JSON.stringify({ relayError: error.toBridgeError() }, null, 2) + "\n");
342
+ } else {
343
+ process.stderr.write(
344
+ (error instanceof Error ? error.message : String(error)) + "\n"
345
+ );
346
+ }
347
+ process.exit(1);
348
+ }
349
+ }
350
+
264
351
  // src/program.ts
265
352
  function buildProgram() {
266
353
  const program = new Command();
@@ -294,31 +381,75 @@ Notes:
294
381
  program.command("update").description("Update chrome-relay CLI to the latest version and print what changed (agent-readable JSON).").option("--dry-run", "skip the install; just show what changed since the current version").action(async (opts) => {
295
382
  const fromVersion = CHROME_RELAY_VERSION;
296
383
  const { spawnSync } = await import("child_process");
384
+ const out = {
385
+ updatedFrom: fromVersion,
386
+ updatedTo: fromVersion,
387
+ install: { attempted: false },
388
+ binary: { path: process.argv[1] ?? "", reexeced: false },
389
+ releaseNotes: { source: "current_process", changes: [] },
390
+ warnings: []
391
+ };
297
392
  if (!opts.dryRun) {
298
393
  const argv0 = process.argv[1] ?? "";
299
394
  const pm = /[\\/](pnpm|\.pnpm)[\\/]/.test(argv0) ? "pnpm" : /[\\/]bun[\\/]/.test(argv0) ? "bun" : "npm";
300
395
  const cmd = pm === "pnpm" ? ["pnpm", ["add", "-g", "chrome-relay@latest"]] : pm === "bun" ? ["bun", ["add", "-g", "chrome-relay@latest"]] : ["npm", ["install", "-g", "chrome-relay@latest"]];
396
+ out.install = {
397
+ attempted: true,
398
+ packageManager: pm,
399
+ command: `${cmd[0]} ${cmd[1].join(" ")}`
400
+ };
301
401
  process.stderr.write(`[chrome-relay] updating from ${fromVersion} via ${pm}...
302
402
  `);
303
403
  const install = spawnSync(cmd[0], cmd[1], { stdio: "inherit" });
404
+ out.install.status = install.status;
304
405
  if (install.status !== 0) {
305
- process.stderr.write(`[chrome-relay] install failed (${pm} exited ${install.status}). Try manually: ${pm} ${cmd[1].join(" ")}
406
+ process.stderr.write(`[chrome-relay] install failed (${pm} exited ${install.status}). Try manually: ${cmd[0]} ${cmd[1].join(" ")}
306
407
  `);
408
+ out.warnings.push({
409
+ code: "update_install_failed",
410
+ message: `Package-manager exit ${install.status}. Active binary unchanged.`
411
+ });
412
+ process.stdout.write(JSON.stringify(out, null, 2) + "\n");
307
413
  process.exit(1);
308
414
  }
309
415
  const which = spawnSync("which", ["chrome-relay"]);
310
416
  const newBin = which.stdout?.toString().trim();
311
- if (which.status === 0 && newBin && newBin !== argv0) {
312
- spawnSync(newBin, ["release-notes", "--since", fromVersion], { stdio: "inherit" });
313
- return;
417
+ if (which.status === 0 && newBin) {
418
+ const versionOut = spawnSync(newBin, ["--version"]);
419
+ const newVersion = (versionOut.stdout?.toString() ?? "").trim();
420
+ out.binary.path = newBin;
421
+ if (newVersion && newVersion !== fromVersion) {
422
+ out.updatedTo = newVersion;
423
+ const rn = spawnSync(newBin, ["release-notes", "--since", fromVersion]);
424
+ try {
425
+ const parsed = JSON.parse(rn.stdout?.toString() ?? "");
426
+ if (Array.isArray(parsed.changes)) {
427
+ out.releaseNotes = { source: "updated_binary", changes: parsed.changes };
428
+ }
429
+ } catch {
430
+ out.warnings.push({
431
+ code: "release_notes_parse_failed",
432
+ message: `Could not parse output of "${newBin} release-notes --since ${fromVersion}".`
433
+ });
434
+ }
435
+ out.binary.reexeced = true;
436
+ } else {
437
+ out.warnings.push({
438
+ code: "update_not_verified",
439
+ message: `Install completed but \`${newBin} --version\` still reports ${newVersion || "unknown"}. The active binary may not have changed \u2014 check your PATH or run "${cmd[0]} ${cmd[1].join(" ")}" manually and verify.`
440
+ });
441
+ }
442
+ } else {
443
+ out.warnings.push({
444
+ code: "update_not_verified",
445
+ message: `Install completed but \`which chrome-relay\` did not return a path. Could not verify the active binary changed.`
446
+ });
314
447
  }
315
448
  }
316
- const changes = listReleaseNotesSince(fromVersion);
317
- process.stdout.write(JSON.stringify({
318
- updatedFrom: fromVersion,
319
- updatedTo: CHROME_RELAY_VERSION,
320
- changes
321
- }, null, 2) + "\n");
449
+ if (out.releaseNotes.source === "current_process") {
450
+ out.releaseNotes.changes = listReleaseNotesSince(fromVersion);
451
+ }
452
+ process.stdout.write(JSON.stringify(out, null, 2) + "\n");
322
453
  });
323
454
  program.command("release-notes").description("Print release notes since a version (no install). JSON output for agents.").option("--since <version>", "show release notes for versions newer than this", "0.0.0").action((opts) => {
324
455
  const changes = listReleaseNotesSince(opts.since);
@@ -328,77 +459,8 @@ Notes:
328
459
  changes
329
460
  }, null, 2) + "\n");
330
461
  });
331
- async function run(name, args) {
332
- try {
333
- const result = await callTool(name, args);
334
- if (typeof result === "string") {
335
- process.stdout.write(result + "\n");
336
- } else {
337
- process.stdout.write(JSON.stringify(result, null, 2) + "\n");
338
- }
339
- } catch (error) {
340
- if (error instanceof RelayError) {
341
- process.stderr.write(error.message + "\n");
342
- process.stderr.write(JSON.stringify({ relayError: error.toBridgeError() }, null, 2) + "\n");
343
- } else {
344
- process.stderr.write(
345
- (error instanceof Error ? error.message : String(error)) + "\n"
346
- );
347
- }
348
- process.exit(1);
349
- }
350
- }
351
- function tabOpt(cmd) {
352
- 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`)");
353
- }
354
- function baseArgs(opts) {
355
- const parentOpts = program.opts();
356
- rejectIntraScopeConflict("subcommand", {
357
- tab: opts.tab,
358
- workspace: opts.workspace,
359
- group: opts.group
360
- });
361
- rejectIntraScopeConflict("program-level", {
362
- workspace: parentOpts.workspace,
363
- group: parentOpts.group
364
- });
365
- if (opts.workspace && parentOpts.workspace && opts.workspace !== parentOpts.workspace) {
366
- emitTargetOverride("workspace", parentOpts.workspace, opts.workspace);
367
- }
368
- if (opts.group && parentOpts.group && opts.group !== parentOpts.group) {
369
- emitTargetOverride("group", parentOpts.group, opts.group);
370
- }
371
- if (opts.tab !== void 0 && (parentOpts.workspace || parentOpts.group)) {
372
- const prior = parentOpts.workspace ? `workspace=${parentOpts.workspace}` : `group=${parentOpts.group}`;
373
- emitTargetOverride("tab", prior, String(opts.tab));
374
- }
375
- const args = {};
376
- if (opts.tab !== void 0) args.tabId = opts.tab;
377
- const effectiveWorkspace = opts.workspace ?? parentOpts.workspace;
378
- const effectiveGroup = opts.group ?? parentOpts.group;
379
- if (opts.tab === void 0 && effectiveWorkspace) args.workspaceName = effectiveWorkspace;
380
- if (opts.tab === void 0 && effectiveGroup) args.groupName = effectiveGroup;
381
- return args;
382
- }
383
- function rejectIntraScopeConflict(scope, fields) {
384
- const present = [];
385
- if (fields.tab !== void 0) present.push("--tab");
386
- if (fields.workspace) present.push("--workspace");
387
- if (fields.group) present.push("--group");
388
- if (present.length > 1) {
389
- process.stderr.write(
390
- `[chrome-relay] target_conflict: ${scope} flags ${present.join(" + ")} are mutually exclusive. Pass exactly one of --tab, --workspace, or --group on the same ${scope}.
391
- `
392
- );
393
- process.exit(2);
394
- }
395
- }
396
- function emitTargetOverride(kind, from, to) {
397
- process.stderr.write(
398
- `[chrome-relay] target_overridden: ${kind} ${from} \u2192 ${to} (subcommand-level overrides program-level)
399
- `
400
- );
401
- }
462
+ const run = runTool;
463
+ const baseArgs = makeBaseArgs(program);
402
464
  program.command("tabs [verb]").description("List open Chrome windows and tabs. (verb 'list' is accepted as alias)").action(async (verb) => {
403
465
  if (verb && verb !== "list") {
404
466
  process.stderr.write(`unknown tabs verb: ${verb}. Use 'tabs' or 'tabs list'.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- var CHROME_RELAY_VERSION = true ? "0.5.6" : "0.0.0-dev";
2
+ var CHROME_RELAY_VERSION = true ? "0.5.8" : "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.6" : "0.0.0-dev";
51
+ var CHROME_RELAY_VERSION = true ? "0.5.8" : "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.6",
3
+ "version": "0.5.8",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",