@phnx-labs/agents-cli 1.22.43 → 1.22.44

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.
Files changed (164) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/README.md +14 -12
  3. package/dist/bootstrap.js +8 -6
  4. package/dist/cli/command-registry.d.ts +0 -5
  5. package/dist/cli/command-registry.js +1 -17
  6. package/dist/commands/apply.d.ts +7 -10
  7. package/dist/commands/apply.js +15 -35
  8. package/dist/commands/artifacts.js +4 -5
  9. package/dist/commands/audit.d.ts +6 -6
  10. package/dist/commands/audit.js +12 -12
  11. package/dist/commands/auth.js +7 -3
  12. package/dist/commands/beta.d.ts +7 -1
  13. package/dist/commands/beta.js +16 -9
  14. package/dist/commands/commands.js +1 -1
  15. package/dist/commands/doctor.js +1 -4
  16. package/dist/commands/events.d.ts +2 -2
  17. package/dist/commands/events.js +6 -5
  18. package/dist/commands/exec.d.ts +1 -1
  19. package/dist/commands/exec.js +8 -4
  20. package/dist/commands/factory.d.ts +1 -1
  21. package/dist/commands/fleet-capture.d.ts +1 -1
  22. package/dist/commands/fleet-capture.js +4 -5
  23. package/dist/commands/harness.js +1 -49
  24. package/dist/commands/hooks.js +1 -1
  25. package/dist/commands/insights.d.ts +1 -1
  26. package/dist/commands/insights.js +3 -2
  27. package/dist/commands/menubar.js +58 -1
  28. package/dist/commands/org.d.ts +5 -0
  29. package/dist/commands/org.js +82 -29
  30. package/dist/commands/output.js +2 -2
  31. package/dist/commands/prune.js +5 -4
  32. package/dist/commands/routines.test-fixture.d.ts +86 -0
  33. package/dist/commands/routines.test-fixture.js +345 -0
  34. package/dist/commands/sessions.test-fixture.d.ts +24 -0
  35. package/dist/commands/sessions.test-fixture.js +224 -0
  36. package/dist/commands/setup.js +5 -1
  37. package/dist/commands/share.d.ts +9 -10
  38. package/dist/commands/share.js +68 -50
  39. package/dist/commands/skills.js +1 -1
  40. package/dist/commands/ssh.js +15 -62
  41. package/dist/commands/subagents.js +1 -1
  42. package/dist/commands/view.js +5 -2
  43. package/dist/lib/analytics/mix-commands.d.ts +6 -28
  44. package/dist/lib/analytics/mix-commands.js +14 -40
  45. package/dist/lib/audit/log.d.ts +2 -2
  46. package/dist/lib/audit/log.js +2 -2
  47. package/dist/lib/beta.js +1 -1
  48. package/dist/lib/browser/domain-skills.d.ts +26 -6
  49. package/dist/lib/browser/domain-skills.js +81 -43
  50. package/dist/lib/daemon/daemon.js +11 -0
  51. package/dist/lib/daemon/daemon.test-fixture.d.ts +24 -0
  52. package/dist/lib/daemon/daemon.test-fixture.js +71 -0
  53. package/dist/lib/daemon-ticks.d.ts +38 -1
  54. package/dist/lib/daemon-ticks.js +52 -5
  55. package/dist/lib/device-config.js +1 -2
  56. package/dist/lib/devices/discovery-policy.d.ts +3 -3
  57. package/dist/lib/devices/discovery-policy.js +3 -3
  58. package/dist/lib/devices/fleet.d.ts +4 -8
  59. package/dist/lib/devices/fleet.js +3 -15
  60. package/dist/lib/devices/health-report.js +3 -3
  61. package/dist/lib/devices/pool.d.ts +0 -6
  62. package/dist/lib/devices/pool.js +0 -6
  63. package/dist/lib/devices/registry.d.ts +0 -15
  64. package/dist/lib/devices/registry.js +0 -9
  65. package/dist/lib/devices/stats-cache.d.ts +12 -0
  66. package/dist/lib/devices/stats-cache.js +24 -5
  67. package/dist/lib/fleet/capture.d.ts +1 -1
  68. package/dist/lib/fleet/manifest.js +1 -1
  69. package/dist/lib/fleet/remote-login.d.ts +2 -2
  70. package/dist/lib/fleet/remote-login.js +3 -4
  71. package/dist/lib/hosts/dispatch.d.ts +2 -0
  72. package/dist/lib/hosts/dispatch.js +8 -1
  73. package/dist/lib/hosts/passthrough.js +0 -1
  74. package/dist/lib/hosts/providers/devices.js +1 -13
  75. package/dist/lib/hosts/ready.d.ts +12 -3
  76. package/dist/lib/hosts/ready.js +27 -12
  77. package/dist/lib/hosts/registry.d.ts +4 -5
  78. package/dist/lib/hosts/registry.js +3 -7
  79. package/dist/lib/installations/versions.js +21 -2
  80. package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +1 -5
  81. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  82. package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +2 -15
  83. package/dist/lib/menubar/install-menubar.d.ts +109 -0
  84. package/dist/lib/menubar/install-menubar.js +199 -4
  85. package/dist/lib/prix-account.d.ts +2 -1
  86. package/dist/lib/prix-account.js +2 -1
  87. package/dist/lib/remote-agents-json.js +1 -7
  88. package/dist/lib/routines-placement.d.ts +1 -1
  89. package/dist/lib/routines-placement.js +1 -1
  90. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  91. package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -2
  92. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  93. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -13
  94. package/dist/lib/session/active.d.ts +4 -16
  95. package/dist/lib/session/active.js +2 -6
  96. package/dist/lib/session/db.d.ts +13 -98
  97. package/dist/lib/session/db.js +4 -11
  98. package/dist/lib/session/discover.d.ts +5 -81
  99. package/dist/lib/session/discover.js +5 -5
  100. package/dist/lib/session/remote/remote-bundle.d.ts +12 -0
  101. package/dist/lib/session/remote/remote-bundle.js +61 -0
  102. package/dist/lib/session/remote/remote-list.d.ts +148 -0
  103. package/dist/lib/session/remote/remote-list.js +607 -0
  104. package/dist/lib/session/remote/remote.d.ts +132 -0
  105. package/dist/lib/session/remote/remote.js +314 -0
  106. package/dist/lib/session/remote/watch.d.ts +101 -0
  107. package/dist/lib/session/remote/watch.js +241 -0
  108. package/dist/lib/session/remote-bundle.d.ts +1 -12
  109. package/dist/lib/session/remote-bundle.js +3 -61
  110. package/dist/lib/session/remote-list.d.ts +1 -148
  111. package/dist/lib/session/remote-list.js +3 -607
  112. package/dist/lib/session/remote.d.ts +1 -132
  113. package/dist/lib/session/remote.js +3 -314
  114. package/dist/lib/session/watch.d.ts +1 -101
  115. package/dist/lib/session/watch.js +3 -242
  116. package/dist/lib/share/config.d.ts +12 -2
  117. package/dist/lib/share/config.js +47 -11
  118. package/dist/lib/share/delete.js +1 -1
  119. package/dist/lib/share/publish.js +1 -1
  120. package/dist/lib/share/worker-template.js +2 -2
  121. package/dist/lib/smart-launch.d.ts +4 -4
  122. package/dist/lib/smart-launch.js +8 -18
  123. package/dist/lib/staleness/index.d.ts +3 -1
  124. package/dist/lib/staleness/index.js +21 -1
  125. package/dist/lib/staleness/types.d.ts +11 -0
  126. package/dist/lib/staleness/writers/commands.js +13 -4
  127. package/dist/lib/staleness/writers/hooks.js +3 -1
  128. package/dist/lib/staleness/writers/mcp.d.ts +0 -7
  129. package/dist/lib/staleness/writers/mcp.js +16 -1
  130. package/dist/lib/staleness/writers/rules.js +1 -1
  131. package/dist/lib/staleness/writers/skills.js +3 -1
  132. package/dist/lib/staleness/writers/subagents.d.ts +0 -9
  133. package/dist/lib/staleness/writers/subagents.js +18 -1
  134. package/dist/lib/staleness/writers/types.d.ts +10 -0
  135. package/dist/lib/startup/command-registry.d.ts +7 -1
  136. package/dist/lib/startup/command-registry.js +17 -5
  137. package/dist/lib/startup/root-command.d.ts +18 -1
  138. package/dist/lib/startup/root-command.js +18 -1
  139. package/dist/lib/state.js +7 -0
  140. package/dist/lib/teams/placement-probe.js +27 -12
  141. package/dist/lib/teams/scheduler.d.ts +3 -1
  142. package/dist/lib/types.d.ts +1 -1
  143. package/dist/lib/view-types.d.ts +3 -0
  144. package/package.json +2 -3
  145. package/dist/bin/agents +0 -0
  146. package/dist/commands/serve.d.ts +0 -10
  147. package/dist/commands/serve.js +0 -68
  148. package/dist/commands/trends.d.ts +0 -10
  149. package/dist/commands/trends.js +0 -12
  150. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  151. package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
  152. package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
  153. package/dist/lib/serve/control.d.ts +0 -95
  154. package/dist/lib/serve/control.js +0 -260
  155. package/dist/lib/serve/data.d.ts +0 -81
  156. package/dist/lib/serve/data.js +0 -91
  157. package/dist/lib/serve/page.d.ts +0 -7
  158. package/dist/lib/serve/page.js +0 -140
  159. package/dist/lib/serve/server.d.ts +0 -80
  160. package/dist/lib/serve/server.js +0 -145
  161. package/dist/lib/serve/stream.d.ts +0 -43
  162. package/dist/lib/serve/stream.js +0 -116
  163. package/dist/lib/serve/token.d.ts +0 -35
  164. package/dist/lib/serve/token.js +0 -85
@@ -26,6 +26,7 @@ import { registerSetupSecretsCommand } from './setup-secrets.js';
26
26
  import { registerSetupFleetCommand } from './setup-fleet.js';
27
27
  import { registerSetupWatchdogCommand, runWatchdogSetupWizard } from './setup-watchdog.js';
28
28
  import { registerAliasCommand } from './alias.js';
29
+ import { registerBetaCommands } from './beta.js';
29
30
  import { runPreferencesStep } from './setup-preferences.js';
30
31
  import { getConfiguredDefaultProfileName, getProfile, getAutoDetectedProfile, isProfileLaunchableHere } from '../lib/browser/profiles.js';
31
32
  import { listInstalledBrowsers } from '../lib/browser/chrome.js';
@@ -363,7 +364,7 @@ export function registerSetupCommand(program) {
363
364
  .description('Set up agents-cli, or re-open the capability onboarding hub.')
364
365
  .option('-f, --force', 'Re-run setup even if ~/.agents/.system/ already exists (use with caution)')
365
366
  .option('--no-system-repo', 'Skip cloning the system repo (you must populate ~/.agents/.system/ yourself)');
366
- // Capability subcommands: `agents setup browser|computer|mine|secrets|fleet|alias`.
367
+ // Capability subcommands: `agents setup browser|computer|mine|secrets|fleet|alias|beta`.
367
368
  // Share/artifact publishing is set up by `agents artifacts setup` (RUSH-2580);
368
369
  // the hub below still offers it as a phase via runShareWizard.
369
370
  registerSetupBrowserCommand(setupCmd);
@@ -373,6 +374,7 @@ export function registerSetupCommand(program) {
373
374
  registerSetupFleetCommand(setupCmd);
374
375
  registerSetupWatchdogCommand(setupCmd);
375
376
  registerAliasCommand(setupCmd);
377
+ registerBetaCommands(setupCmd);
376
378
  setupCmd.command('status')
377
379
  .description('Show setup readiness for core, browser, computer, secrets, fleet, share, watchdog, and preferences.')
378
380
  .option('--json', 'print machine-readable JSON')
@@ -400,6 +402,7 @@ export function registerSetupCommand(program) {
400
402
  agents setup fleet
401
403
  agents setup watchdog
402
404
  agents setup alias add teams
405
+ agents setup beta enable factory
403
406
  `,
404
407
  notes: `
405
408
  What it does:
@@ -416,6 +419,7 @@ export function registerSetupCommand(program) {
416
419
  agents setup fleet # discover Tailscale devices + configure SSH access
417
420
  agents setup watchdog # choose which devices run the daemon watchdog pass
418
421
  agents setup alias add teams # PATH shorthand: teams runs agents teams
422
+ agents setup beta enable factory # opt into preview features
419
423
 
420
424
  To install CLIs from agents.yaml and sync resources into version homes:
421
425
  agents sync --local -y
@@ -105,7 +105,7 @@ export interface ShareRevisionsResult {
105
105
  * outdated-template hint as {@link parseShareListing}) on an unexpected shape. */
106
106
  export declare function parseShareRevisions(key: string, body: string): ShareRevisionsResult;
107
107
  /** Fetch and parse the retained prior versions of one published slug. `target`
108
- * accepts the same three forms as `agents unshare` (a full URL, `<user>/<slug>`,
108
+ * accepts the same three forms as `agents artifacts unshare` (a full URL, `<user>/<slug>`,
109
109
  * or a bare slug resolved against the caller's own namespace) — reuses
110
110
  * {@link resolveDeleteTarget} rather than re-deriving the key. */
111
111
  export declare function runShareRevisions(target: string, opts?: {
@@ -119,11 +119,11 @@ interface ShareDeleteCliOpts {
119
119
  keepCover?: boolean;
120
120
  keepRevisions?: boolean;
121
121
  ifExists?: boolean;
122
- githubUser?: string;
123
- json?: boolean;
122
+ forUser?: string;
123
+ deleteJson?: boolean;
124
124
  }
125
125
  /** Shared handler for `agents artifacts share delete <targets...>` and the
126
- * top-level `agents unshare <targets...>` alias. Deletes each target independently and
126
+ * nested `agents artifacts unshare <targets...>` alias. Deletes each target independently and
127
127
  * continues past a failed one (rm-style), reporting all results and exiting
128
128
  * non-zero if any target failed to verify as gone.
129
129
  *
@@ -133,18 +133,17 @@ interface ShareDeleteCliOpts {
133
133
  export declare function runShareDelete(targets: string[], opts: ShareDeleteCliOpts, deleteFn?: typeof deleteShare): Promise<void>;
134
134
  /**
135
135
  * Register the `share` subtree under its parent group — `agents artifacts share`
136
- * (see commands/artifacts.ts). The top-level `agents unshare` alias is a sibling
136
+ * (see commands/artifacts.ts). `agents artifacts unshare` is the nested alias
137
+ * registered via {@link registerUnshareCommand} on the artifacts group.
137
138
  * registration on the ROOT program, so it is {@link registerUnshareCommand}, not
138
139
  * part of this subtree.
139
140
  */
140
141
  export declare function registerShareCommands(artifactsCmd: Command): void;
141
142
  /**
142
- * Register the top-level `agents unshare <targets...>` alias of
143
- * `agents artifacts share delete`. It takes the ROOT program (not the artifacts
144
- * group) because it is deliberately a top-level convenience verb — taking a page
145
- * down is the one artifact action typed often enough to keep at the root.
143
+ * Register `agents artifacts unshare <targets...>` as the nested alias of
144
+ * `agents artifacts share delete`. Top-level `agents unshare` is retired.
146
145
  */
147
- export declare function registerUnshareCommand(program: Command): void;
146
+ export declare function registerUnshareCommand(artifactsCmd: Command): void;
148
147
  /** Provision a fresh R2 bucket + Worker on the user's Cloudflare and persist the
149
148
  * endpoint config + write token. Shared by both modes of `agents artifacts setup`
150
149
  * (the flag-driven provision and the interactive wizard). */
@@ -235,7 +235,7 @@ export function parseShareRevisions(key, body) {
235
235
  return { key, count: revisions.length, revisions };
236
236
  }
237
237
  /** Fetch and parse the retained prior versions of one published slug. `target`
238
- * accepts the same three forms as `agents unshare` (a full URL, `<user>/<slug>`,
238
+ * accepts the same three forms as `agents artifacts unshare` (a full URL, `<user>/<slug>`,
239
239
  * or a bare slug resolved against the caller's own namespace) — reuses
240
240
  * {@link resolveDeleteTarget} rather than re-deriving the key. */
241
241
  export async function runShareRevisions(target, opts = {}) {
@@ -296,7 +296,7 @@ export function formatShareDeleteResult(result, json = false) {
296
296
  return lines.join('\n');
297
297
  }
298
298
  /** Shared handler for `agents artifacts share delete <targets...>` and the
299
- * top-level `agents unshare <targets...>` alias. Deletes each target independently and
299
+ * nested `agents artifacts unshare <targets...>` alias. Deletes each target independently and
300
300
  * continues past a failed one (rm-style), reporting all results and exiting
301
301
  * non-zero if any target failed to verify as gone.
302
302
  *
@@ -311,19 +311,19 @@ export async function runShareDelete(targets, opts, deleteFn = deleteShare) {
311
311
  keepCover: opts.keepCover === true,
312
312
  keepRevisions: opts.keepRevisions === true,
313
313
  ifExists: opts.ifExists === true,
314
- githubUser: opts.githubUser,
314
+ githubUser: opts.forUser,
315
315
  });
316
316
  results.push({ target, result });
317
- if (!opts.json)
317
+ if (!opts.deleteJson)
318
318
  console.log(formatShareDeleteResult(result));
319
319
  }
320
320
  catch (e) {
321
321
  results.push({ target, error: e.message });
322
- if (!opts.json)
322
+ if (!opts.deleteJson)
323
323
  console.error(chalk.red(`${target}: ${e.message}`));
324
324
  }
325
325
  }
326
- if (opts.json) {
326
+ if (opts.deleteJson) {
327
327
  console.log(JSON.stringify(results, null, 2));
328
328
  }
329
329
  if (results.some((r) => r.error)) {
@@ -335,8 +335,17 @@ function registerShareDeleteOptions(cmd) {
335
335
  .option('--keep-cover', 'leave the sibling <slug>.png OG cover in place (default: delete it too)')
336
336
  .option('--keep-revisions', 'leave retained revisions (<slug>/rev-*) in place (default: delete them too)')
337
337
  .option('--if-exists', 'treat an already-missing target as a no-op success instead of an error')
338
- .option('--github-user <user>', 'GitHub username for resolving a bare-slug target (default: resolved from gh/git config)')
339
- .option('--json', 'emit machine-readable results');
338
+ // Named --for-user / --delete-json, not --github-user / --json: `share
339
+ // <file>` (the parent of the nested `share delete`) already owns both
340
+ // those long names, and commander resolves an option's long name against
341
+ // the WHOLE ancestor chain — a same-named child option is silently
342
+ // dropped at parse time even when it parses alone (RUSH-2687; verified
343
+ // with a real program.parseAsync(), see share.test.ts). Matches the
344
+ // `revisions` precedent (--for-user/--revisions-json) below, and applies
345
+ // equally to the top-level `unshare` alias so both spellings stay
346
+ // identical even though `unshare` itself has no ancestor collision.
347
+ .option('--for-user <user>', 'GitHub username for resolving a bare-slug target (default: resolved from gh/git config)')
348
+ .option('--delete-json', 'emit machine-readable results');
340
349
  }
341
350
  const SHARE_DELETE_EXAMPLES = `
342
351
  # Delete by full URL — also takes down the sibling OG cover and any retained revisions
@@ -344,19 +353,19 @@ const SHARE_DELETE_EXAMPLES = `
344
353
 
345
354
  # Delete by <user>/<slug>, or a bare slug in your own namespace
346
355
  agents artifacts share delete octocat/my-plan-a1b2
347
- agents unshare my-plan-a1b2
356
+ agents artifacts unshare my-plan-a1b2
348
357
 
349
358
  # Several at once
350
- agents unshare my-plan-a1b2 old-report-9f3c
359
+ agents artifacts unshare my-plan-a1b2 old-report-9f3c
351
360
 
352
361
  # Keep the cover image up (rare — you usually want both gone)
353
- agents unshare my-plan-a1b2 --keep-cover
362
+ agents artifacts unshare my-plan-a1b2 --keep-cover
354
363
 
355
364
  # Keep retained revisions up too (rare — they're world-readable by URL like the page itself)
356
- agents unshare my-plan-a1b2 --keep-revisions
365
+ agents artifacts unshare my-plan-a1b2 --keep-revisions
357
366
 
358
367
  # Don't error if it's already gone
359
- agents unshare my-plan-a1b2 --if-exists
368
+ agents artifacts unshare my-plan-a1b2 --if-exists
360
369
  `;
361
370
  const SHARE_DELETE_NOTES = `
362
371
  A follow-up GET is required to resolve 404 before this reports success — the
@@ -369,11 +378,12 @@ const SHARE_DELETE_NOTES = `
369
378
  down. Pass --keep-revisions to leave them (they still expire via the bucket's
370
379
  lifecycle rule on their own schedule either way).
371
380
 
372
- agents artifacts share delete === agents unshare (same command, different name).
381
+ agents artifacts share delete === agents artifacts unshare (same command, different name).
373
382
  `;
374
383
  /**
375
384
  * Register the `share` subtree under its parent group — `agents artifacts share`
376
- * (see commands/artifacts.ts). The top-level `agents unshare` alias is a sibling
385
+ * (see commands/artifacts.ts). `agents artifacts unshare` is the nested alias
386
+ * registered via {@link registerUnshareCommand} on the artifacts group.
377
387
  * registration on the ROOT program, so it is {@link registerUnshareCommand}, not
378
388
  * part of this subtree.
379
389
  */
@@ -471,7 +481,7 @@ ${SHARE_DELETE_NOTES}
471
481
  });
472
482
  const shareDeleteCmd = registerShareDeleteOptions(shareCmd
473
483
  .command('delete <targets...>')
474
- .description('Take down a published page (and by default its OG cover). Verifies the page 404s before reporting success. Top-level alias: agents unshare.'));
484
+ .description('Take down a published page (and by default its OG cover). Verifies the page 404s before reporting success. Nested alias: agents artifacts unshare.'));
475
485
  setHelpSections(shareDeleteCmd, { examples: SHARE_DELETE_EXAMPLES, notes: SHARE_DELETE_NOTES });
476
486
  shareDeleteCmd.action(async (targets, opts) => {
477
487
  await runShareDelete(targets, opts);
@@ -497,11 +507,15 @@ ${SHARE_DELETE_NOTES}
497
507
  .option('--account <id>', 'Cloudflare account id override (default: the configured endpoint\'s account)')
498
508
  .option('--token <t>', 'Cloudflare API token (else read from --bundle)')
499
509
  .option('--force', 're-deploy even if the deployed template already matches')
500
- .option('--json', 'emit a machine-readable result')
510
+ // Named --update-json, not --json: `share <file>` (the parent) already
511
+ // owns --json for its own publish-time result. Commander resolves an
512
+ // option's long name against the WHOLE ancestor chain, so a same-named
513
+ // child option is silently dropped at parse time (RUSH-2687).
514
+ .option('--update-json', 'emit a machine-readable result')
501
515
  .action(async (opts) => {
502
516
  try {
503
517
  const result = await runShareUpdate(opts);
504
- if (opts.json) {
518
+ if (opts.updateJson) {
505
519
  console.log(JSON.stringify(result, null, 2));
506
520
  return;
507
521
  }
@@ -541,7 +555,10 @@ ${SHARE_DELETE_NOTES}
541
555
  return;
542
556
  }
543
557
  console.log(`${chalk.bold('endpoint')} ${chalk.green(cfg.baseUrl)}`);
544
- console.log(chalk.dim(`worker ${cfg.workerName} · bucket ${cfg.bucketName} · account ${cfg.accountId}`));
558
+ console.log(chalk.dim(`worker ${cfg.workerName} · bucket ${cfg.bucketName} · account ${cfg.accountId || 'missing'}`));
559
+ if (!cfg.accountId) {
560
+ console.log(chalk.dim('account id empty — publish still uses baseUrl + WRITE_TOKEN; `agents artifacts share update` needs --account or join'));
561
+ }
545
562
  const user = await resolveGitHubUsername();
546
563
  console.log(`${chalk.bold('namespace')} ${user ? chalk.cyan(`${cfg.baseUrl}/${user}`) : chalk.yellow('unknown — set gh auth or github.user')}`);
547
564
  console.log(`${chalk.bold('analytics')} ${analyticsEnabled(cfg) ? chalk.green('enabled') : chalk.dim('not configured')}`);
@@ -555,28 +572,31 @@ ${SHARE_DELETE_NOTES}
555
572
  });
556
573
  const shareListCmd = shareCmd
557
574
  .command('list')
558
- .description("List the pages you've published to your share namespace (human table; --json for scripts).")
559
- .option('--github-user <user>', 'GitHub username whose namespace to list (default: resolved from gh/git config)')
575
+ .description("List the pages you've published to your share namespace (human table; --list-json for scripts).")
576
+ // Named --for-user / --list-json, not --github-user / --json: `share
577
+ // <file>` (the parent) already owns both those long names, and commander
578
+ // resolves an option's long name against the WHOLE ancestor chain — a
579
+ // same-named child option is silently dropped at parse time even when it
580
+ // parses alone (RUSH-2687; verified with a real program.parseAsync(), see
581
+ // share.test.ts). Matches the `revisions` precedent (--for-user/
582
+ // --revisions-json) and the `delete`/`unshare` precedent (--for-user/
583
+ // --delete-json) below.
584
+ .option('--for-user <user>', 'GitHub username whose namespace to list (default: resolved from gh/git config)')
560
585
  .option('--agent <name>', 'filter to shares published by this agent/harness (case-insensitive)')
561
586
  .option('--session <id>', 'filter to shares published from this session id')
562
587
  // Named --label-contains, not --label: `share <file>` (the parent) already owns
563
- // `--label`/`--title`, and commander's argv scanner resolves an option name
564
- // against the WHOLE ancestor chain, not per-command — a same-named child option
565
- // is silently dropped (verified; see RUSH-2683 PR notes). Fixing that properly
566
- // needs `enablePositionalOptions()` on the root program, a CLI-wide change out
567
- // of scope here (a pre-existing instance of it already affects --json/
568
- // --github-user on list/update/delete — tracked as a follow-up).
588
+ // `--label`/`--title`, same collision class as --for-user/--list-json above.
569
589
  .option('--label-contains <substr>', 'filter to shares whose label contains this text (case-insensitive)')
570
- .option('--json', 'emit the machine-readable listing (slug, url, size, contentType, publishedAt, expiresAt, label, agent, session, host, repo, revisionCount, meta)')
590
+ .option('--list-json', 'emit the machine-readable listing (slug, url, size, contentType, publishedAt, expiresAt, label, agent, session, host, repo, revisionCount, meta)')
571
591
  .action(async (opts) => {
572
592
  try {
573
593
  const result = await runShareList({
574
- githubUser: opts.githubUser,
594
+ githubUser: opts.forUser,
575
595
  agent: opts.agent,
576
596
  session: opts.session,
577
597
  label: opts.labelContains,
578
598
  });
579
- console.log(formatShareList(result, Boolean(opts.json)));
599
+ console.log(formatShareList(result, Boolean(opts.listJson)));
580
600
  }
581
601
  catch (e) {
582
602
  console.error(chalk.red(e.message));
@@ -589,10 +609,10 @@ ${SHARE_DELETE_NOTES}
589
609
  agents artifacts share list
590
610
 
591
611
  # Machine-readable — e.g. pull every still-public URL with jq
592
- agents artifacts share list --json | jq -r '.objects[].url'
612
+ agents artifacts share list --list-json | jq -r '.objects[].url'
593
613
 
594
614
  # List another namespace
595
- agents artifacts share list --github-user octocat
615
+ agents artifacts share list --for-user octocat
596
616
 
597
617
  # Narrow by who/what published it
598
618
  agents artifacts share list --agent claude
@@ -606,22 +626,19 @@ ${SHARE_DELETE_NOTES}
606
626
  artifacts share update' (RUSH-2449) rather than returning a wrong or empty result
607
627
  — see 'agents artifacts share status' for whether an update is due.
608
628
 
609
- --agent/--session/--label-contains filter the fetched listing client-side; --json's
610
- count reflects the filtered set.
629
+ --agent/--session/--label-contains filter the fetched listing client-side;
630
+ --list-json's count reflects the filtered set.
611
631
  `,
612
632
  });
613
633
  const shareRevisionsCmd = shareCmd
614
634
  .command('revisions <target>')
615
635
  .description('Show the retained prior versions of a published slug, newest first (human table; --revisions-json for scripts).')
616
636
  // Named --for-user / --revisions-json, not --github-user / --json: `share
617
- // <file>` (the parent) already owns both those long names, and commander's
618
- // argv scanner resolves an option name against the WHOLE ancestor chain —
619
- // a same-named child option is silently dropped even when it parses alone
620
- // (verified with a real program.parseAsync(), see share.test.ts). Unlike
621
- // `list`'s pre-existing --json/--github-user collision (RUSH-2687, left
622
- // as a tracked follow-up because it predates this diff), `revisions` is
623
- // brand-new here, so it gets non-colliding names from the start instead
624
- // of shipping a silently-broken flag.
637
+ // <file>` (the parent) already owns both those long names, and commander
638
+ // resolves an option's long name against the WHOLE ancestor chain — a
639
+ // same-named child option is silently dropped at parse time even when it
640
+ // parses alone (RUSH-2687; verified with a real program.parseAsync(), see
641
+ // share.test.ts). Same collision class as `list`/`update`/`delete` above.
625
642
  .option('--for-user <user>', 'GitHub username for resolving a bare-slug target (default: resolved from gh/git config)')
626
643
  .option('--revisions-json', 'emit the machine-readable revision list')
627
644
  .action(async (target, opts) => {
@@ -639,7 +656,7 @@ ${SHARE_DELETE_NOTES}
639
656
  # Prior versions kept under this slug (bare slug — your own namespace)
640
657
  agents artifacts share revisions q3-report
641
658
 
642
- # By <user>/<slug> or full URL, same target forms as 'agents unshare'
659
+ # By <user>/<slug> or full URL, same target forms as 'agents artifacts unshare'
643
660
  agents artifacts share revisions octocat/q3-report
644
661
  agents artifacts share revisions https://share.agents-cli.sh/octocat/q3-report
645
662
 
@@ -682,13 +699,11 @@ ${SHARE_DELETE_NOTES}
682
699
  });
683
700
  }
684
701
  /**
685
- * Register the top-level `agents unshare <targets...>` alias of
686
- * `agents artifacts share delete`. It takes the ROOT program (not the artifacts
687
- * group) because it is deliberately a top-level convenience verb — taking a page
688
- * down is the one artifact action typed often enough to keep at the root.
702
+ * Register `agents artifacts unshare <targets...>` as the nested alias of
703
+ * `agents artifacts share delete`. Top-level `agents unshare` is retired.
689
704
  */
690
- export function registerUnshareCommand(program) {
691
- const unshareCmd = registerShareDeleteOptions(program
705
+ export function registerUnshareCommand(artifactsCmd) {
706
+ const unshareCmd = registerShareDeleteOptions(artifactsCmd
692
707
  .command('unshare <targets...>')
693
708
  .description('Alias of `agents artifacts share delete` — take down a published page (and by default its OG cover).'));
694
709
  setHelpSections(unshareCmd, { examples: SHARE_DELETE_EXAMPLES, notes: SHARE_DELETE_NOTES });
@@ -778,12 +793,15 @@ export async function runShareUpdate(opts = {}) {
778
793
  accountId: opts.account,
779
794
  });
780
795
  const accountId = opts.account || acctFromBundle || cfg.accountId;
796
+ if (!accountId) {
797
+ throw new Error("Share endpoint has no Cloudflare account id — `agents artifacts share update` cannot call the API. Pass --account <id>, or re-run 'agents artifacts share join'.");
798
+ }
781
799
  const writeToken = readWriteToken();
782
800
  const script = renderWorkerScript();
783
801
  const provisionOpts = { ...(opts.request ? { request: opts.request } : {}), force: opts.force };
784
802
  const result = await updateWorker(apiToken, accountId, cfg.workerName, cfg.bucketName, script, writeToken, cfg.templateHash, provisionOpts);
785
803
  if (!result.skipped) {
786
- writeShareConfig({ ...cfg, templateHash: result.templateHash });
804
+ writeShareConfig({ ...cfg, accountId, templateHash: result.templateHash });
787
805
  }
788
806
  return { updated: !result.skipped, templateHash: result.templateHash, baseUrl: cfg.baseUrl, workerName: cfg.workerName };
789
807
  }
@@ -403,7 +403,7 @@ Examples:
403
403
  }
404
404
  else {
405
405
  console.log(chalk.green(`\nRemoved ${removed} skill(s) from version homes.`));
406
- console.log(chalk.gray('Central source unchanged. Skills will re-sync on next agent launch.'));
406
+ console.log(chalk.gray("Central source unchanged. Restore any target with 'agents sync <agent>@<version> --yes'."));
407
407
  }
408
408
  });
409
409
  // `skills prune` moved to the top-level `agents prune cleanup` command.
@@ -24,8 +24,6 @@ import { registerFleetApplyAlias } from './apply.js';
24
24
  import { addIgnored, getDevice, loadDevices, loadIgnored, removeDevice, removeIgnored, upsertDevice, writeReachability, } from '../lib/devices/registry.js';
25
25
  import { resolveDeviceProfile } from '../lib/devices/resolve-profile.js';
26
26
  import { collectReachabilityWriteBacks, deviceOnlineState } from '../lib/devices/reachability.js';
27
- import { addControlToken } from '../lib/serve/token.js';
28
- import { DEFAULT_SERVE_PORT } from '../lib/serve/server.js';
29
27
  import { nodeToDeviceInput, parseTailscaleStatus, tailscaleStatusJson, } from '../lib/devices/tailscale.js';
30
28
  import { defaultPickerChecked, localLoginUser, planDeviceReconciliation, runDeviceSync, withDefaultUser } from '../lib/devices/sync.js';
31
29
  import { resolveDeviceTarget, splitUserHost } from '../lib/devices/resolve-target.js';
@@ -41,7 +39,7 @@ import { fanOutDevices, fleetHealthSkip, planFleetTargets, remoteFleetTargets, r
41
39
  import { isRolloutSuccess, verifyFleetRollout, } from '../lib/devices/rollout-verify.js';
42
40
  import { fleetCapacity, fmtBytes, headroom, } from '../lib/devices/health.js';
43
41
  import { buildFleetHealthReport, renderFleetMatrix, renderFleetSummary, renderFleetWarnings, } from '../lib/devices/health-report.js';
44
- import { loadFleetStats, readStatsCache } from '../lib/devices/stats-cache.js';
42
+ import { isFreshDeviceStats, loadFleetStats, readStatsCache } from '../lib/devices/stats-cache.js';
45
43
  import { collectLocalFleetInventory } from '../lib/devices/fleet-inventory.js';
46
44
  import { checkSyncStatus, countOrphans } from '../lib/drift.js';
47
45
  import { checkAllClis } from '../lib/teams/agents.js';
@@ -661,8 +659,11 @@ async function runFleetPing(opts) {
661
659
  const self = machineId();
662
660
  const { refreshLocalFleetAuthState } = await import('../lib/daemon-ticks.js');
663
661
  // --local: probe just this host. Used both directly and as the fan-out worker.
662
+ // force: this command promises "a real request for every account" (--strict
663
+ // gates on it), so it must never reuse the periodic tick's rate-limit-throttled
664
+ // cached verdict (RUSH-2998).
664
665
  if (opts.local) {
665
- const { authRows: rows } = await refreshLocalFleetAuthState();
666
+ const { authRows: rows } = await refreshLocalFleetAuthState({ force: true });
666
667
  if (opts.json) {
667
668
  console.log(JSON.stringify({ host: self, rows }));
668
669
  }
@@ -679,7 +680,12 @@ async function runFleetPing(opts) {
679
680
  const reg = await loadDevices();
680
681
  const planned = planFleetTargets(reg);
681
682
  const results = [];
682
- const { authRows: localRows } = await refreshLocalFleetAuthState();
683
+ // force: same as the --local worker below — this command promises a real
684
+ // request for every account and --strict gates on it, so the self row must
685
+ // never reuse the periodic tick's rate-limit-throttled cached verdict. Each
686
+ // remote peer is force-probed via its own `devices ping --local` worker (see
687
+ // probeRemoteAuth); the self row must match (RUSH-2998).
688
+ const { authRows: localRows } = await refreshLocalFleetAuthState({ force: true });
683
689
  results.push({ host: self, rows: localRows });
684
690
  const remoteTargets = remoteFleetTargets(planned, self).map((t) => ({
685
691
  name: t.device.name,
@@ -1000,7 +1006,7 @@ function registerDevicesCommands(program) {
1000
1006
  { title: 'Inspect', names: ['list', 'show', 'status', 'ping', 'harnesses', 'accounts', 'snapshot'] },
1001
1007
  { title: 'Disposable devices', names: ['lease'] },
1002
1008
  { title: 'Configure a device', names: ['config', 'render'] },
1003
- { title: 'Fleet operations', names: ['update', 'run', 'login', 'pair-ios', 'capture', 'apply'] },
1009
+ { title: 'Fleet operations', names: ['update', 'run', 'login', 'capture', 'apply'] },
1004
1010
  ]);
1005
1011
  devicesCmd
1006
1012
  .command('sync')
@@ -1026,8 +1032,7 @@ function registerDevicesCommands(program) {
1026
1032
  });
1027
1033
  // `agents fleet capture` — snapshot live state into agents.yaml fleet:.
1028
1034
  registerFleetCaptureCommand(devicesCmd);
1029
- // `agents fleet apply` — same reconcile engine as the top-level `agents apply`,
1030
- // surfaced under the fleet tree for discoverability.
1035
+ // `agents fleet apply` — canonical fleet reconcile (top-level apply is retired).
1031
1036
  registerFleetApplyAlias(devicesCmd);
1032
1037
  devicesCmd
1033
1038
  .command('register <name>')
@@ -1268,13 +1273,7 @@ function registerDevicesCommands(program) {
1268
1273
  *
1269
1274
  * A role written here lands in that device's tracked per-device doc
1270
1275
  * (`devices/<name>/agents.yaml` `config.role`) and syncs with repo
1271
- * push/pull. The vocabulary is deliberately `worker | personal` only: a
1272
- * paired cockpit's `control` role lives in the per-machine device registry,
1273
- * is written by `agents devices pair-ios`, and is what the existing
1274
- * dial-exclusion filters (`isControlDevice`) read. Accepting `control` here
1275
- * too would promise a fleet-wide dial exclusion this key cannot deliver —
1276
- * those filters read each box's own registry, so the mark would only hold
1277
- * on the machine that ran the command.
1276
+ * push/pull. The vocabulary is deliberately `worker | personal` only.
1278
1277
  */
1279
1278
  const runDevicesRole = async (name, role, opts) => {
1280
1279
  if (!name) {
@@ -1528,10 +1527,6 @@ function registerDevicesCommands(program) {
1528
1527
 
1529
1528
  Turn the allowlist off with 'agents config set auto.pool all'; a personal
1530
1529
  box stays excluded, since that is what the mark is for.
1531
-
1532
- A paired iPhone/iPad cockpit is a separate role: 'agents devices pair-ios'
1533
- marks it control in that box's device registry, and the fleet never dials
1534
- it — including for placement. This command does not set that role.
1535
1530
  `,
1536
1531
  });
1537
1532
  /** Deprecation notice for a retired subcommand — STDERR only, so a --json consumer's stdout stays parseable. */
@@ -1735,7 +1730,7 @@ function registerDevicesCommands(program) {
1735
1730
  .map((t) => t.device);
1736
1731
  // Only spin when we'll actually ssh (forced, or a cold/partial cache).
1737
1732
  const cache = readStatsCache();
1738
- const willSsh = forceRefresh || probeable.some((d) => d.name !== self && !cache[d.name]);
1733
+ const willSsh = forceRefresh || probeable.some((d) => d.name !== self && (!cache[d.name] || !isFreshDeviceStats(cache[d.name])));
1739
1734
  const spinner = willSsh && isInteractiveTerminal()
1740
1735
  ? ora(`Probing ${probeable.length} device${probeable.length === 1 ? '' : 's'}…`).start()
1741
1736
  : undefined;
@@ -1991,48 +1986,6 @@ email) into a single row. Use \`agents devices harnesses\` for the per-install v
1991
1986
  process.exit(1);
1992
1987
  }
1993
1988
  });
1994
- devicesCmd
1995
- .command('pair-ios [name]')
1996
- .description('Pair an iPhone/iPad cockpit (RUSH-1733): mint a control token for `agents serve --control` and mark the device control-only. The token is shown ONCE — enter it in the app. Run this on the anchor.')
1997
- .option('--port <n>', 'Anchor control port to advertise to the app', String(DEFAULT_SERVE_PORT))
1998
- .action(async (name, opts) => {
1999
- const label = (name || 'iphone').trim();
2000
- // Mint the bearer token — hash-only on disk, raw shown once (see serve/token.ts).
2001
- const { id, token } = addControlToken(label);
2002
- // If a device with this name is already registered (e.g. discovered over
2003
- // Tailscale as an `unknown`-platform node), mark it control-only so the
2004
- // fleet stops trying to dial it for sessions/placement.
2005
- let marked = false;
2006
- let unknownName = false;
2007
- if (name) {
2008
- const existing = await getDevice(name);
2009
- if (existing) {
2010
- await upsertDevice(name, { role: 'control' });
2011
- setDeviceDiscoveryStatus(name, 'approved');
2012
- marked = true;
2013
- }
2014
- else {
2015
- unknownName = true;
2016
- }
2017
- }
2018
- const port = parseInt(opts.port ?? '', 10) || DEFAULT_SERVE_PORT;
2019
- console.log(chalk.green(`Paired cockpit '${label}'`) + chalk.gray(` (token id ${id})`));
2020
- if (marked)
2021
- console.log(chalk.gray(`Marked device '${name}' role=control — the fleet won't dial it.`));
2022
- if (unknownName) {
2023
- console.log(chalk.yellow(`Note: no registered device named '${name}' — token minted, but no device was marked role=control.`) +
2024
- chalk.gray(` Run \`agents devices sync\` first if this phone should appear in the fleet.`));
2025
- }
2026
- console.log();
2027
- console.log(chalk.bold('Control token (shown once — enter it in the app):'));
2028
- console.log(' ' + chalk.cyan(token));
2029
- console.log();
2030
- console.log('On the anchor, expose the control server on your tailnet:');
2031
- console.log(chalk.gray(` agents serve --control --bind <anchor-tailnet-ip> --port ${port}`));
2032
- console.log('Then point the app at:');
2033
- console.log(chalk.gray(` http://<anchor-tailnet-ip>:${port} (Bearer: the token above)`));
2034
- console.log(chalk.yellow('Keep the control server on the tailnet — never public Funnel.'));
2035
- });
2036
1989
  devicesCmd
2037
1990
  .command('rm <name>')
2038
1991
  .alias('remove')
@@ -304,7 +304,7 @@ Examples:
304
304
  }
305
305
  else {
306
306
  console.log(chalk.green(`\nRemoved ${removed} subagent(s) from version homes.`));
307
- console.log(chalk.gray('Central source unchanged. Subagents will re-sync on next agent launch.'));
307
+ console.log(chalk.gray("Central source unchanged. Restore any target with 'agents sync <agent>@<version> --yes'."));
308
308
  }
309
309
  });
310
310
  }
@@ -1241,6 +1241,8 @@ export function parseResourceSections(options, jsonMode) {
1241
1241
  */
1242
1242
  export async function collectAgentsJson(filterAgentId, resourceSections) {
1243
1243
  const agentsToShow = filterAgentId ? [filterAgentId] : ALL_AGENT_IDS;
1244
+ const authCache = readAuthHealthCache();
1245
+ const host = machineId();
1244
1246
  const wantResources = !!resourceSections && resourceSections.size > 0;
1245
1247
  // Only pay for the git-status + resource scans when resources were requested.
1246
1248
  const resourceSync = wantResources ? await loadResourceSyncData() : null;
@@ -1293,6 +1295,7 @@ export async function collectAgentsJson(filterAgentId, resourceSections) {
1293
1295
  isolated: isVersionIsolated(agentId, version),
1294
1296
  isIsolatedDefault: getIsolatedDefault(agentId) === version,
1295
1297
  signedIn: info.signedIn,
1298
+ authVerdict: authCache[authCacheKey(host, agentId, version)]?.verdict ?? null,
1296
1299
  email: info.email,
1297
1300
  accountId: info.accountId,
1298
1301
  organizationType: info.organizationType ?? null,
@@ -1740,8 +1743,8 @@ Output:
1740
1743
  - With agent name: versions for that agent, showing which is the default
1741
1744
  - With a custom harness name: that harness's host, model, provider, and auth
1742
1745
  - With agent@version: detailed breakdown of resources synced to that version
1743
- - With --json: structured JSON with version, isDefault, signedIn, email, plan,
1744
- usageStatus, per-window usedPercent, lastActive, and path
1746
+ - With --json: structured JSON with version, isDefault, signedIn, authVerdict,
1747
+ email, plan, usageStatus, per-window usedPercent, lastActive, and path
1745
1748
  - With --prune: plan of which older versions will be removed, then confirm
1746
1749
  - With --prune --dry-run: preview only, no deletions
1747
1750
  `)
@@ -7,9 +7,8 @@
7
7
  * usage.db) still exists — it is now `agents insights mix` and the recipe
8
8
  * subcommands below. Latency stays on `agents perf`; quota on `agents usage`.
9
9
  *
10
- * `agents trends` remains a thin deprecated alias registered from
11
- * `commands/trends.ts` that shares this same registration no second copy of
12
- * the recipe tree.
10
+ * Former top-level `agents trends` is gone. The nested spelling
11
+ * `agents insights trends` is an alias of `agents insights mix`.
13
12
  */
14
13
  import type { Command } from 'commander';
15
14
  export declare function parseMixDays(raw: string | undefined): number;
@@ -19,35 +18,14 @@ export declare function printMixSection(section: {
19
18
  rows: Array<Record<string, string | number | null>>;
20
19
  }): void;
21
20
  export declare function renderMixDashboard(days: number, asJson: boolean, bannerLabel?: string): void;
22
- /** One-line deprecation for the retired top-level `agents trends` spelling. */
23
- export declare function printTrendsDeprecation(): void;
24
- export interface RegisterMixCommandsOptions {
25
- /**
26
- * When true, every action prints the trends deprecation line first. Used only
27
- * by the thin top-level `agents trends` alias — the real tree under insights
28
- * never sets this.
29
- */
30
- deprecated?: boolean;
31
- /**
32
- * Banner label for the multi-recipe dashboard (default: agents insights mix).
33
- * The deprecated alias passes `agents insights mix` still, so callers learn
34
- * the new name even when they typed trends.
35
- */
36
- dashboardBanner?: string;
37
- }
38
21
  /**
39
- * Attach counter-mix subcommands to a parent (typically `insights` or the
40
- * deprecated `trends` alias).
22
+ * Attach counter-mix subcommands to a parent (typically `insights`).
41
23
  *
42
24
  * Layout:
43
- * <parent> mix multi-recipe board (default for trends alias)
25
+ * <parent> mix multi-recipe board
26
+ * <parent> trends alias of mix (former top-level `agents trends`)
44
27
  * <parent> recipes list recipe ids
45
28
  * <parent> query raw usage.db rows
46
29
  * <parent> harness-mix|… one baked recipe
47
30
  */
48
- export declare function registerMixCommands(parent: Command, opts?: RegisterMixCommandsOptions): void;
49
- /**
50
- * Thin top-level `agents trends` alias: same mix tree, deprecation on every path,
51
- * default action = mix board (old `agents trends` with no subcommand).
52
- */
53
- export declare function registerDeprecatedTrendsAlias(program: Command): void;
31
+ export declare function registerMixCommands(parent: Command): void;