@phnx-labs/agents-cli 1.22.41 → 1.22.43

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 (253) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +10 -4
  3. package/dist/bin/agents +0 -0
  4. package/dist/bootstrap.js +25 -7
  5. package/dist/cli/command-registry.d.ts +2 -3
  6. package/dist/cli/command-registry.js +4 -8
  7. package/dist/commands/accounts.d.ts +29 -0
  8. package/dist/commands/accounts.js +220 -29
  9. package/dist/commands/alias.d.ts +7 -4
  10. package/dist/commands/alias.js +34 -9
  11. package/dist/commands/auth.d.ts +9 -0
  12. package/dist/commands/auth.js +108 -0
  13. package/dist/commands/browser-sessions-picker.js +42 -62
  14. package/dist/commands/browser.js +30 -38
  15. package/dist/commands/computer-actions.d.ts +1 -1
  16. package/dist/commands/computer-actions.js +1 -1
  17. package/dist/commands/computer-sessions-picker.js +32 -55
  18. package/dist/commands/computer.d.ts +1 -1
  19. package/dist/commands/computer.js +22 -8
  20. package/dist/commands/daemon.js +1 -1
  21. package/dist/commands/doctor.d.ts +5 -3
  22. package/dist/commands/doctor.js +23 -8
  23. package/dist/commands/exec.js +3 -2
  24. package/dist/commands/feed.js +2 -31
  25. package/dist/commands/harness.js +3 -3
  26. package/dist/commands/hooks.js +1 -1
  27. package/dist/commands/import.js +1 -1
  28. package/dist/commands/insights.js +163 -85
  29. package/dist/commands/inspect.js +2 -2
  30. package/dist/commands/mine.js +1 -1
  31. package/dist/commands/monitors.js +1 -1
  32. package/dist/commands/org.d.ts +6 -0
  33. package/dist/commands/org.js +175 -0
  34. package/dist/commands/packages.js +1 -1
  35. package/dist/commands/profiles.js +2 -2
  36. package/dist/commands/prune.js +1 -1
  37. package/dist/commands/routines.js +44 -26
  38. package/dist/commands/run-account-picker.d.ts +19 -0
  39. package/dist/commands/run-account-picker.js +81 -0
  40. package/dist/commands/sessions-picker-factory.d.ts +40 -0
  41. package/dist/commands/sessions-picker-factory.js +67 -0
  42. package/dist/commands/sessions-picker.js +124 -122
  43. package/dist/commands/setup-browser.js +4 -4
  44. package/dist/commands/setup-mine.js +1 -1
  45. package/dist/commands/setup-preferences.js +2 -2
  46. package/dist/commands/setup.js +10 -4
  47. package/dist/commands/snapshot.d.ts +1 -1
  48. package/dist/commands/snapshot.js +4 -4
  49. package/dist/commands/status.d.ts +4 -2
  50. package/dist/commands/status.js +10 -7
  51. package/dist/commands/sync.d.ts +1 -0
  52. package/dist/commands/sync.js +30 -3
  53. package/dist/commands/versions.js +1 -1
  54. package/dist/commands/view.js +7 -1
  55. package/dist/commands/webhook.js +1 -1
  56. package/dist/lib/account-capabilities.d.ts +10 -0
  57. package/dist/lib/account-capabilities.js +34 -0
  58. package/dist/lib/accounting/rotate.d.ts +5 -3
  59. package/dist/lib/accounting/rotate.js +36 -7
  60. package/dist/lib/accounting/usage.d.ts +18 -4
  61. package/dist/lib/accounting/usage.js +138 -27
  62. package/dist/lib/agent-spec/agents.d.ts +584 -0
  63. package/dist/lib/agent-spec/agents.js +3127 -0
  64. package/dist/lib/agent-spec/provider.js +1 -1
  65. package/dist/lib/agents.d.ts +2 -584
  66. package/dist/lib/agents.js +2 -3127
  67. package/dist/lib/browser/drivers/local.d.ts +8 -2
  68. package/dist/lib/browser/drivers/local.js +8 -2
  69. package/dist/lib/browser/drivers/ssh.d.ts +8 -2
  70. package/dist/lib/browser/drivers/ssh.js +10 -4
  71. package/dist/lib/browser/hygiene.d.ts +1 -1
  72. package/dist/lib/browser/hygiene.js +5 -1
  73. package/dist/lib/browser/profiles.d.ts +74 -1
  74. package/dist/lib/browser/profiles.js +114 -5
  75. package/dist/lib/browser/runtime-state.d.ts +11 -5
  76. package/dist/lib/browser/runtime-state.js +13 -9
  77. package/dist/lib/browser/service.d.ts +68 -30
  78. package/dist/lib/browser/service.js +305 -194
  79. package/dist/lib/browser/types.d.ts +95 -3
  80. package/dist/lib/browser/types.js +51 -0
  81. package/dist/lib/catchup.js +1 -1
  82. package/dist/lib/cloud/rush.d.ts +0 -15
  83. package/dist/lib/cloud/rush.js +0 -35
  84. package/dist/lib/computer/actions.d.ts +1 -1
  85. package/dist/lib/computer/actions.js +1 -1
  86. package/dist/lib/{computer-rpc.js → computer/computer-rpc.js} +5 -5
  87. package/dist/lib/computer/dispatch.d.ts +1 -1
  88. package/dist/lib/computer/download.d.ts +1 -1
  89. package/dist/lib/computer/download.js +3 -3
  90. package/dist/lib/{ssh-tunnel.d.ts → computer/ssh-tunnel.d.ts} +1 -1
  91. package/dist/lib/{ssh-tunnel.js → computer/ssh-tunnel.js} +12 -12
  92. package/dist/lib/crabbox/runtimes.d.ts +6 -2
  93. package/dist/lib/crabbox/runtimes.js +21 -5
  94. package/dist/lib/daemon/daemon.js +87 -59
  95. package/dist/lib/{runner.d.ts → daemon/runner.d.ts} +28 -11
  96. package/dist/lib/{runner.js → daemon/runner.js} +143 -123
  97. package/dist/lib/devices/doctor-findings.d.ts +1 -1
  98. package/dist/lib/devices/doctor-findings.js +4 -4
  99. package/dist/lib/devices/fleet-inventory.js +1 -1
  100. package/dist/lib/devices/harness-inventory.js +1 -1
  101. package/dist/lib/devices/health-report.js +1 -1
  102. package/dist/lib/devices/registry.js +4 -22
  103. package/dist/lib/doctor-diff.d.ts +1 -1
  104. package/dist/lib/doctor-diff.js +1 -1
  105. package/dist/lib/drift-sync.d.ts +1 -1
  106. package/dist/lib/drift-sync.js +2 -2
  107. package/dist/lib/drift.d.ts +1 -1
  108. package/dist/lib/drift.js +1 -1
  109. package/dist/lib/entitlement.d.ts +31 -0
  110. package/dist/lib/entitlement.js +137 -0
  111. package/dist/lib/exec.js +52 -205
  112. package/dist/lib/feed/feed.js +2 -8
  113. package/dist/lib/format.d.ts +9 -0
  114. package/dist/lib/format.js +16 -0
  115. package/dist/lib/fs-atomic.d.ts +7 -0
  116. package/dist/lib/fs-atomic.js +9 -0
  117. package/dist/lib/github/pr-mergeable.d.ts +49 -0
  118. package/dist/lib/github/pr-mergeable.js +157 -0
  119. package/dist/lib/github/pr-verdict.d.ts +67 -0
  120. package/dist/lib/github/pr-verdict.js +72 -0
  121. package/dist/lib/harness/adapter.d.ts +161 -0
  122. package/dist/lib/harness/adapter.js +47 -0
  123. package/dist/lib/harness/adapters/claude.d.ts +2 -0
  124. package/dist/lib/harness/adapters/claude.js +110 -0
  125. package/dist/lib/harness/adapters/codex.d.ts +2 -0
  126. package/dist/lib/harness/adapters/codex.js +83 -0
  127. package/dist/lib/harness/adapters/copilot.d.ts +2 -0
  128. package/dist/lib/harness/adapters/copilot.js +23 -0
  129. package/dist/lib/harness/adapters/cursor.d.ts +2 -0
  130. package/dist/lib/harness/adapters/cursor.js +38 -0
  131. package/dist/lib/harness/adapters/droid.d.ts +2 -0
  132. package/dist/lib/harness/adapters/droid.js +17 -0
  133. package/dist/lib/harness/adapters/grok.d.ts +2 -0
  134. package/dist/lib/harness/adapters/grok.js +14 -0
  135. package/dist/lib/harness/adapters/kimi.d.ts +2 -0
  136. package/dist/lib/harness/adapters/kimi.js +49 -0
  137. package/dist/lib/harness/adapters/muse.d.ts +2 -0
  138. package/dist/lib/harness/adapters/muse.js +44 -0
  139. package/dist/lib/harness/adapters/opencode.d.ts +2 -0
  140. package/dist/lib/harness/adapters/opencode.js +14 -0
  141. package/dist/lib/harness/exec-config-version.d.ts +8 -0
  142. package/dist/lib/harness/exec-config-version.js +22 -0
  143. package/dist/lib/harness/index.d.ts +1 -0
  144. package/dist/lib/harness/index.js +34 -0
  145. package/dist/lib/{hooks.d.ts → hooks/install.d.ts} +1 -1
  146. package/dist/lib/{hooks.js → hooks/install.js} +11 -11
  147. package/dist/lib/hosts/passthrough.d.ts +10 -0
  148. package/dist/lib/hosts/passthrough.js +28 -6
  149. package/dist/lib/hosts/session-index.js +1 -1
  150. package/dist/lib/import.js +1 -1
  151. package/dist/lib/installations/index.d.ts +4 -4
  152. package/dist/lib/installations/index.js +3 -4
  153. package/dist/lib/installations/migrate.js +6 -6
  154. package/dist/lib/{shims.d.ts → installations/shims.d.ts} +2 -2
  155. package/dist/lib/{shims.js → installations/shims.js} +13 -115
  156. package/dist/lib/installations/store.d.ts +141 -3
  157. package/dist/lib/installations/store.js +475 -5
  158. package/dist/lib/installations/versions.d.ts +40 -125
  159. package/dist/lib/installations/versions.js +57 -462
  160. package/dist/lib/isolation-boundary-report.d.ts +1 -1
  161. package/dist/lib/isolation-boundary-report.js +1 -1
  162. package/dist/lib/mailbox-gc.js +2 -3
  163. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  164. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  165. package/dist/lib/menubar/install-menubar.js +19 -8
  166. package/dist/lib/monitors/config.d.ts +2 -2
  167. package/dist/lib/monitors/config.js +3 -2
  168. package/dist/lib/monitors/dispatch.d.ts +1 -1
  169. package/dist/lib/monitors/dispatch.js +2 -2
  170. package/dist/lib/monitors/state.d.ts +1 -1
  171. package/dist/lib/origin-machine.d.ts +18 -0
  172. package/dist/lib/origin-machine.js +34 -0
  173. package/dist/lib/perf/db.js +1 -1
  174. package/dist/lib/perf/spool.js +1 -1
  175. package/dist/lib/plugins/plugins.js +1 -1
  176. package/dist/lib/plugins/skills.js +1 -1
  177. package/dist/lib/prix-account.d.ts +158 -0
  178. package/dist/lib/prix-account.js +214 -0
  179. package/dist/lib/profiles.d.ts +7 -0
  180. package/dist/lib/profiles.js +26 -1
  181. package/dist/lib/project-key.js +2 -2
  182. package/dist/lib/project-launch.js +1 -1
  183. package/dist/lib/refresh.js +2 -2
  184. package/dist/lib/resource-inventory.d.ts +1 -1
  185. package/dist/lib/resource-inventory.js +1 -1
  186. package/dist/lib/rules/rules.js +1 -1
  187. package/dist/lib/sandbox.d.ts +24 -0
  188. package/dist/lib/sandbox.js +101 -0
  189. package/dist/lib/scheduling/routines.d.ts +18 -9
  190. package/dist/lib/scheduling/routines.js +17 -9
  191. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  192. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  193. package/dist/lib/secrets/agent.js +9 -1
  194. package/dist/lib/secrets/index.js +1 -1
  195. package/dist/lib/secrets/list-filter.js +1 -1
  196. package/dist/lib/self-heal/checks/hook-runtime.js +1 -1
  197. package/dist/lib/self-heal/checks/path.js +1 -1
  198. package/dist/lib/self-heal/checks/shadowing.js +1 -1
  199. package/dist/lib/self-heal/checks/shims.js +1 -1
  200. package/dist/lib/service-manifest.d.ts +20 -0
  201. package/dist/lib/service-manifest.js +34 -0
  202. package/dist/lib/session/cloud.js +1 -1
  203. package/dist/lib/session/db.js +1 -1
  204. package/dist/lib/session/discover.d.ts +39 -0
  205. package/dist/lib/session/discover.js +3 -3
  206. package/dist/lib/session/fork.js +1 -1
  207. package/dist/lib/session/live-metadata.js +1 -1
  208. package/dist/lib/session/origin-machine.d.ts +1 -18
  209. package/dist/lib/session/origin-machine.js +4 -34
  210. package/dist/lib/session/parse.js +24 -42
  211. package/dist/lib/session/recovery.js +1 -1
  212. package/dist/lib/session/relative-time.d.ts +1 -44
  213. package/dist/lib/session/relative-time.js +4 -120
  214. package/dist/lib/session/remote-list.js +1 -1
  215. package/dist/lib/session/remote.js +2 -2
  216. package/dist/lib/session/short-id.d.ts +1 -17
  217. package/dist/lib/session/short-id.js +4 -20
  218. package/dist/lib/session/width.d.ts +1 -29
  219. package/dist/lib/session/width.js +4 -101
  220. package/dist/lib/snapshot.d.ts +2 -2
  221. package/dist/lib/snapshot.js +1 -1
  222. package/dist/lib/staleness/prune.d.ts +1 -1
  223. package/dist/lib/staleness/prune.js +1 -1
  224. package/dist/lib/staleness/writers/hooks.js +1 -1
  225. package/dist/lib/startup/command-registry.d.ts +4 -1
  226. package/dist/lib/startup/command-registry.js +12 -5
  227. package/dist/lib/teams/agents.js +2 -19
  228. package/dist/lib/teams/parsers.js +20 -26
  229. package/dist/lib/teams/placement-probe.js +1 -1
  230. package/dist/lib/teams/registry.js +2 -21
  231. package/dist/lib/teams/scheduler.js +1 -1
  232. package/dist/lib/text/relative-time.d.ts +44 -0
  233. package/dist/lib/text/relative-time.js +120 -0
  234. package/dist/lib/text/short-id.d.ts +17 -0
  235. package/dist/lib/text/short-id.js +20 -0
  236. package/dist/lib/text/width.d.ts +29 -0
  237. package/dist/lib/text/width.js +101 -0
  238. package/dist/lib/triggers/handlers.d.ts +7 -0
  239. package/dist/lib/triggers/handlers.js +9 -1
  240. package/dist/lib/triggers/webhook.d.ts +1 -0
  241. package/dist/lib/triggers/webhook.js +3 -2
  242. package/dist/lib/uninstall.js +1 -1
  243. package/dist/lib/version.d.ts +9 -2
  244. package/dist/lib/version.js +11 -9
  245. package/dist/lib/view-types.d.ts +4 -0
  246. package/package.json +1 -1
  247. package/dist/commands/tickets.d.ts +0 -2
  248. package/dist/commands/tickets.js +0 -43
  249. package/dist/lib/observe-aliases.d.ts +0 -30
  250. package/dist/lib/observe-aliases.js +0 -38
  251. package/dist/lib/tickets/list.d.ts +0 -53
  252. package/dist/lib/tickets/list.js +0 -153
  253. /package/dist/lib/{computer-rpc.d.ts → computer/computer-rpc.d.ts} +0 -0
@@ -13,6 +13,7 @@ import * as yaml from 'yaml';
13
13
  import { isDaemonRunning, signalDaemonReload, startDaemon, stopDaemon, readDaemonLog, getDaemonStatus, getDaemonLogPath, } from '../lib/daemon/daemon.js';
14
14
  import { assertSchedulerEnabled, assertDaemonEnabled, isDaemonEnabled } from '../lib/device-config.js';
15
15
  import { parseAgentVersionSpec, isAgentHardDeprecated, hardDeprecationError, ROUTINE_AGENT_IDS } from '../lib/agents.js';
16
+ import { isCustomHarnessName } from '../lib/profiles.js';
16
17
  import { RUN_STRATEGIES, normalizeRunStrategy } from '../lib/accounting/rotate.js';
17
18
  import { formatRepoLink, REPO_DISPLAY_MAX } from '../lib/routines-format.js';
18
19
  import { listJobs as listAllJobs, deleteJob, readJob, validateJob, writeJob, setJobEnabled, listRuns, routineStats, getLatestRun, getRunDir, getJobPath, isCanonicalRoutineSource, parseAtTime, hasCompletedOneShotRun, isOneShotLikeSchedule, isOneShotRoutine, jobRunsOnThisDevice, checkJobDeviceEligibility, normalizeTriggerEvent, parseHostStrategy, resolveHostStrategy, HOST_STRATEGIES, computeProjectGroupKind, projectGroupKey, projectGroupTitle, projectGroupOrder, normalizeProjects, buildRoutineListJson, fireConditionLabel, nextRunForDisplay, nextRunLabel, localLatestRun, listJobsForDisplay, } from '../lib/scheduling/routines.js';
@@ -24,7 +25,7 @@ import { fireWebhookJobs, matchJobsToWebhook } from '../lib/triggers/webhook.js'
24
25
  import { getRoutinesDir } from '../lib/state.js';
25
26
  import { IS_WINDOWS } from '../lib/platform/index.js';
26
27
  import { safeJoin } from '../lib/paths.js';
27
- import { executeJob, monitorRunningJobs } from '../lib/runner.js';
28
+ import { executeJob, monitorRunningJobs } from '../lib/daemon/runner.js';
28
29
  import { JobScheduler } from '../lib/scheduler.js';
29
30
  import { detectOverdueJobs } from '../lib/overdue.js';
30
31
  import { runCatchup } from '../lib/catchup.js';
@@ -897,7 +898,7 @@ export function registerRoutinesCommands(program) {
897
898
  .command('add [nameOrPath]')
898
899
  .description('Create a new routine from a YAML file or inline flags. Starts the scheduler automatically if it is not already running.')
899
900
  .option('-s, --schedule <cron>', 'Cron schedule in standard format (5 fields: minute hour day month weekday)')
900
- .option('-a, --agent <agent[@version]>', `Which agent runs this routine: ${ROUTINE_AGENT_IDS.join(', ')}. An @version pins that exact installed version (same syntax as agents run), persisted as separate agent + version fields.`)
901
+ .option('-a, --agent <agent[@version]>', `Which agent runs this routine: ${ROUTINE_AGENT_IDS.join(', ')}, or a custom harness name (agents harness list). An @version pins that exact installed version (same syntax as agents run), persisted as separate agent + version fields; a custom harness pins its own host version.`)
901
902
  .option('--strategy <strategy>', `Version/account selection strategy for this routine: ${RUN_STRATEGIES.join(' | ')}. Overrides run.<agent>.strategy from the firing device's config. Conflicts with an @version pin.`)
902
903
  .option('--balanced', 'Shortcut for --strategy balanced.')
903
904
  .option('--workflow <name>', 'Run an installed workflow (~/.agents/workflows/<name>) via `agents run`. Mutually exclusive with --agent.')
@@ -984,17 +985,26 @@ export function registerRoutinesCommands(program) {
984
985
  // silently missed a hard-deprecated harness pinned with @version.
985
986
  let parsedAgent;
986
987
  if (options.agent) {
987
- const parsed = parseAgentVersionSpec(options.agent);
988
- if ('error' in parsed) {
989
- console.error(chalk.red(parsed.error));
990
- process.exit(1);
988
+ // A custom harness (agents harness list) is a valid routine agent:
989
+ // the runner delegates it to `agents run <name>`. It pins its own
990
+ // host version, so no @version suffix applies (a suffixed name falls
991
+ // through to the parse error below).
992
+ if (isCustomHarnessName(options.agent)) {
993
+ parsedAgent = { agent: options.agent };
991
994
  }
992
- parsedAgent = parsed;
993
- // Hard-deprecated harnesses cannot be scheduled — refuse at create time
994
- // so no new recurring job silently fails against a retired backend.
995
- if (isAgentHardDeprecated(parsed.agent)) {
996
- console.error(chalk.red(hardDeprecationError(parsed.agent)));
997
- process.exit(1);
995
+ else {
996
+ const parsed = parseAgentVersionSpec(options.agent);
997
+ if ('error' in parsed) {
998
+ console.error(chalk.red(parsed.error));
999
+ process.exit(1);
1000
+ }
1001
+ parsedAgent = parsed;
1002
+ // Hard-deprecated harnesses cannot be scheduled — refuse at create time
1003
+ // so no new recurring job silently fails against a retired backend.
1004
+ if (isAgentHardDeprecated(parsed.agent)) {
1005
+ console.error(chalk.red(hardDeprecationError(parsed.agent)));
1006
+ process.exit(1);
1007
+ }
998
1008
  }
999
1009
  }
1000
1010
  // --strategy / --balanced: per-routine selection policy, same vocabulary
@@ -1377,22 +1387,30 @@ export function registerRoutinesCommands(program) {
1377
1387
  if (options.projectAnchor !== undefined)
1378
1388
  existing.project = options.projectAnchor;
1379
1389
  if (options.agent !== undefined) {
1380
- const parsed = parseAgentVersionSpec(options.agent);
1381
- if ('error' in parsed) {
1382
- console.error(chalk.red(parsed.error));
1383
- process.exit(1);
1390
+ if (isCustomHarnessName(options.agent)) {
1391
+ // Custom harness: delegated to `agents run <name>`; the profile
1392
+ // pins its own host version, so any prior pin is cleared.
1393
+ existing.agent = options.agent;
1394
+ delete existing.version;
1384
1395
  }
1385
- if (isAgentHardDeprecated(parsed.agent)) {
1386
- console.error(chalk.red(hardDeprecationError(parsed.agent)));
1387
- process.exit(1);
1396
+ else {
1397
+ const parsed = parseAgentVersionSpec(options.agent);
1398
+ if ('error' in parsed) {
1399
+ console.error(chalk.red(parsed.error));
1400
+ process.exit(1);
1401
+ }
1402
+ if (isAgentHardDeprecated(parsed.agent)) {
1403
+ console.error(chalk.red(hardDeprecationError(parsed.agent)));
1404
+ process.exit(1);
1405
+ }
1406
+ existing.agent = parsed.agent;
1407
+ // An @version pin replaces any previous pin; a bare agent clears it so
1408
+ // the routine returns to strategy/balanced selection.
1409
+ if (parsed.version)
1410
+ existing.version = parsed.version;
1411
+ else
1412
+ delete existing.version;
1388
1413
  }
1389
- existing.agent = parsed.agent;
1390
- // An @version pin replaces any previous pin; a bare agent clears it so
1391
- // the routine returns to strategy/balanced selection.
1392
- if (parsed.version)
1393
- existing.version = parsed.version;
1394
- else
1395
- delete existing.version;
1396
1414
  }
1397
1415
  if (options.strategy !== undefined) {
1398
1416
  const normalized = normalizeRunStrategy(options.strategy);
@@ -12,10 +12,29 @@ export interface RunAccountChoice {
12
12
  */
13
13
  signInRequired: boolean;
14
14
  }
15
+ /** One named account row for `agents accounts switch` (reuses this picker's layout). */
16
+ export interface SwitchAccountRow {
17
+ accountName: string;
18
+ kind: 'provider' | 'native';
19
+ detail: string;
20
+ current: boolean;
21
+ candidate: RotateCandidate | null;
22
+ }
15
23
  /** Human-readable remaining capacity for every window the provider exposes. */
16
24
  export declare function formatAccountLimits(candidate: RotateCandidate): string;
17
25
  /** Build aligned picker rows with usable accounts first and unsafe rows disabled. */
18
26
  export declare function buildRunAccountChoices(candidates: RotateCandidate[], globalDefault: string | null): RunAccountChoice[];
27
+ /**
28
+ * Aligned picker rows for `accounts switch`. Same columns as the run picker
29
+ * (identity, status, limits) but the value is the named account to set-default.
30
+ * Rows stay selectable: setting a default is not a launch.
31
+ */
32
+ export declare function buildSwitchAccountChoices(rows: SwitchAccountRow[]): RunAccountChoice[];
33
+ /**
34
+ * Prompt for the named account that becomes this harness's default.
35
+ * A cancelled picker writes nothing.
36
+ */
37
+ export declare function pickSwitchAccount(agent: AgentId, rows: SwitchAccountRow[]): Promise<string | null>;
19
38
  /**
20
39
  * Whether a zero-healthy run may recover by launching for a login, or must keep
21
40
  * failing loud. Three inputs, all of which have to hold:
@@ -124,6 +124,87 @@ export function buildRunAccountChoices(candidates, globalDefault) {
124
124
  signInRequired: row.signInRequired,
125
125
  }));
126
126
  }
127
+ function switchRowStatus(row) {
128
+ if (!row.candidate) {
129
+ return {
130
+ status: row.kind === 'provider' ? 'credential' : 'named',
131
+ limits: 'limits unavailable',
132
+ ready: true,
133
+ };
134
+ }
135
+ const readiness = readinessFromCandidate(row.candidate);
136
+ const authReason = readiness.ready ? null : readiness.reason;
137
+ const status = authReason === 'revoked'
138
+ ? 'needs re-login'
139
+ : authReason === 'signed_out'
140
+ ? 'logged out'
141
+ : authReason === 'rate_limited'
142
+ ? 'rate limited'
143
+ : authReason === 'out_of_credits'
144
+ ? 'out of credits'
145
+ : 'logged in';
146
+ const limits = authReason === 'revoked'
147
+ ? 'needs re-authentication'
148
+ : authReason === 'signed_out'
149
+ ? 'signed out'
150
+ : formatAccountLimits(row.candidate);
151
+ return { status, limits, ready: readiness.ready };
152
+ }
153
+ /**
154
+ * Aligned picker rows for `accounts switch`. Same columns as the run picker
155
+ * (identity, status, limits) but the value is the named account to set-default.
156
+ * Rows stay selectable: setting a default is not a launch.
157
+ */
158
+ export function buildSwitchAccountChoices(rows) {
159
+ const rendered = rows.map((row) => {
160
+ const { status, limits, ready } = switchRowStatus(row);
161
+ const account = row.current ? `${row.accountName} (default)` : row.accountName;
162
+ const kind = row.kind === 'provider' ? `provider · ${row.detail}` : `native · ${row.detail}`;
163
+ return { account, kind, status, limits, ready, value: row.accountName };
164
+ });
165
+ const accountWidth = Math.max(0, ...rendered.map((row) => row.account.length));
166
+ const kindWidth = Math.max(0, ...rendered.map((row) => row.kind.length));
167
+ const statusWidth = Math.max(0, ...rendered.map((row) => row.status.length));
168
+ return rendered.map((row) => ({
169
+ name: [
170
+ row.account.padEnd(accountWidth),
171
+ row.kind.padEnd(kindWidth),
172
+ row.status.padEnd(statusWidth),
173
+ row.limits,
174
+ ].join(' '),
175
+ value: row.value,
176
+ ready: row.ready,
177
+ signInRequired: false,
178
+ }));
179
+ }
180
+ /**
181
+ * Prompt for the named account that becomes this harness's default.
182
+ * A cancelled picker writes nothing.
183
+ */
184
+ export async function pickSwitchAccount(agent, rows) {
185
+ if (!isInteractiveTerminal()) {
186
+ requireInteractiveSelection(`Selecting a ${agentLabel(agent)} account`, [
187
+ `agents accounts switch ${agent} <account>`,
188
+ `agents accounts set-default ${agent} <account>`,
189
+ ]);
190
+ }
191
+ if (rows.length === 0) {
192
+ throw new Error(`No named accounts for ${agent}. Add one with 'agents accounts add <name> --provider <p>' or 'agents accounts name ${agent}@<version> <name>'.`);
193
+ }
194
+ const choices = buildSwitchAccountChoices(rows).map(({ ready: _ready, signInRequired: _signInRequired, ...choice }) => choice);
195
+ try {
196
+ return await select({
197
+ message: `Select the default ${agentLabel(agent)} account:`,
198
+ choices,
199
+ loop: false,
200
+ });
201
+ }
202
+ catch (err) {
203
+ if (isPromptCancelled(err))
204
+ return null;
205
+ throw err;
206
+ }
207
+ }
127
208
  /**
128
209
  * Whether a zero-healthy run may recover by launching for a login, or must keep
129
210
  * failing loud. Three inputs, all of which have to hold:
@@ -0,0 +1,40 @@
1
+ /** Flag subset both twins share for the interactive-routing gate. */
2
+ export interface SessionsPickerGateOpts {
3
+ interactive?: boolean;
4
+ json?: boolean;
5
+ /** Present only on the browser twin; computer never sets it. */
6
+ open?: string | boolean;
7
+ }
8
+ export interface SessionsPickerCommandSpec<TRow, TOpts extends SessionsPickerGateOpts> {
9
+ /**
10
+ * Browser requires `opts.open === undefined` so `--open` (bare or with a
11
+ * selector) falls through to the flat printer. Computer has no `--open`.
12
+ */
13
+ requireOpenUndefined?: boolean;
14
+ runFlat: (opts: TOpts) => void;
15
+ buildRows: (opts: TOpts) => TRow[];
16
+ emptyMessage: (opts: TOpts) => string;
17
+ message: string;
18
+ matches: (row: TRow, query: string) => boolean;
19
+ labelFor: (row: TRow) => string;
20
+ buildPreview: (row: TRow) => string;
21
+ emptyFilterMessage: string;
22
+ enterHint: string;
23
+ onOpen: (row: TRow) => void | Promise<void>;
24
+ }
25
+ export interface SessionsPickerCommand<TOpts extends SessionsPickerGateOpts> {
26
+ shouldOpen: (opts: TOpts, isTTY: boolean) => boolean;
27
+ run: (opts: TOpts) => Promise<void>;
28
+ }
29
+ export declare function shouldOpenInteractiveSessions(opts: SessionsPickerGateOpts, isTTY: boolean, requireOpenUndefined?: boolean): boolean;
30
+ export declare function browseSessionsUntilQuit<TRow>(spec: {
31
+ message: string;
32
+ rows: TRow[];
33
+ matches: (row: TRow, query: string) => boolean;
34
+ labelFor: (row: TRow) => string;
35
+ buildPreview: (row: TRow) => string;
36
+ emptyFilterMessage: string;
37
+ enterHint: string;
38
+ onOpen: (row: TRow) => void | Promise<void>;
39
+ }): Promise<void>;
40
+ export declare function createSessionsPickerCommand<TRow, TOpts extends SessionsPickerGateOpts>(spec: SessionsPickerCommandSpec<TRow, TOpts>): SessionsPickerCommand<TOpts>;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Shared interactive-routing + browse loop for the task-first session
3
+ * pickers (`browser-sessions-picker.ts`, `computer-sessions-picker.ts`).
4
+ *
5
+ * Each twin keeps its own row formatter, matcher, and enter-handler; this
6
+ * factory owns the TTY/`--json`/`--no-interactive` (and optional `--open`)
7
+ * gate, the empty-list message, and the cancel-aware `itemPicker` loop.
8
+ */
9
+ import { itemPicker } from '../lib/picker.js';
10
+ import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
11
+ export function shouldOpenInteractiveSessions(opts, isTTY, requireOpenUndefined = false) {
12
+ return (opts.interactive !== false &&
13
+ !opts.json &&
14
+ (!requireOpenUndefined || opts.open === undefined) &&
15
+ isTTY);
16
+ }
17
+ export async function browseSessionsUntilQuit(spec) {
18
+ for (;;) {
19
+ let picked;
20
+ try {
21
+ picked = await itemPicker({
22
+ message: spec.message,
23
+ items: spec.rows,
24
+ filter: (query) => (query.trim() ? spec.rows.filter((r) => spec.matches(r, query)) : spec.rows),
25
+ labelFor: spec.labelFor,
26
+ buildPreview: spec.buildPreview,
27
+ emptyMessage: spec.emptyFilterMessage,
28
+ enterHint: spec.enterHint,
29
+ });
30
+ }
31
+ catch (err) {
32
+ if (isPromptCancelled(err))
33
+ return;
34
+ throw err;
35
+ }
36
+ if (!picked)
37
+ return;
38
+ await spec.onOpen(picked.item);
39
+ }
40
+ }
41
+ export function createSessionsPickerCommand(spec) {
42
+ const shouldOpen = (opts, isTTY) => shouldOpenInteractiveSessions(opts, isTTY, spec.requireOpenUndefined === true);
43
+ return {
44
+ shouldOpen,
45
+ run: async (opts) => {
46
+ if (!shouldOpen(opts, isInteractiveTerminal())) {
47
+ spec.runFlat(opts);
48
+ return;
49
+ }
50
+ const rows = spec.buildRows(opts);
51
+ if (rows.length === 0) {
52
+ console.log(spec.emptyMessage(opts));
53
+ return;
54
+ }
55
+ await browseSessionsUntilQuit({
56
+ message: spec.message,
57
+ rows,
58
+ matches: spec.matches,
59
+ labelFor: spec.labelFor,
60
+ buildPreview: spec.buildPreview,
61
+ emptyFilterMessage: spec.emptyFilterMessage,
62
+ enterHint: spec.enterHint,
63
+ onOpen: spec.onOpen,
64
+ });
65
+ },
66
+ };
67
+ }