@phnx-labs/agents-cli 1.22.35 → 1.22.38

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 (500) hide show
  1. package/CHANGELOG.md +708 -2
  2. package/README.md +83 -43
  3. package/dist/bin/agents +0 -0
  4. package/dist/bootstrap.d.ts +15 -0
  5. package/dist/bootstrap.js +1212 -0
  6. package/dist/commands/accounts.d.ts +4 -11
  7. package/dist/commands/accounts.js +159 -27
  8. package/dist/commands/artifacts-setup.d.ts +53 -0
  9. package/dist/commands/artifacts-setup.js +161 -0
  10. package/dist/commands/artifacts.d.ts +18 -0
  11. package/dist/commands/artifacts.js +58 -0
  12. package/dist/commands/attach.js +17 -4
  13. package/dist/commands/audit.d.ts +8 -8
  14. package/dist/commands/audit.js +57 -47
  15. package/dist/commands/browser.js +17 -11
  16. package/dist/commands/cloud.js +1 -1
  17. package/dist/commands/commands.js +0 -11
  18. package/dist/commands/computer-actions.js +33 -0
  19. package/dist/commands/computer-sessions-picker.d.ts +21 -0
  20. package/dist/commands/computer-sessions-picker.js +141 -0
  21. package/dist/commands/computer.d.ts +16 -0
  22. package/dist/commands/computer.js +37 -0
  23. package/dist/commands/config.d.ts +17 -0
  24. package/dist/commands/config.js +401 -0
  25. package/dist/commands/cp.d.ts +84 -0
  26. package/dist/commands/cp.js +231 -0
  27. package/dist/commands/daemon.js +243 -14
  28. package/dist/commands/detach-core.d.ts +1 -1
  29. package/dist/commands/detach-core.js +12 -2
  30. package/dist/commands/detach.js +1 -1
  31. package/dist/commands/doctor.js +64 -5
  32. package/dist/commands/events.d.ts +24 -12
  33. package/dist/commands/events.js +144 -28
  34. package/dist/commands/exec.js +86 -42
  35. package/dist/commands/feed.d.ts +1 -1
  36. package/dist/commands/feed.js +16 -8
  37. package/dist/commands/focus.d.ts +33 -1
  38. package/dist/commands/focus.js +177 -17
  39. package/dist/commands/funnel.d.ts +1 -1
  40. package/dist/commands/go.js +9 -8
  41. package/dist/commands/harness-wizard.d.ts +1 -0
  42. package/dist/commands/harness-wizard.js +18 -20
  43. package/dist/commands/harness.d.ts +2 -0
  44. package/dist/commands/harness.js +59 -26
  45. package/dist/commands/hooks.js +0 -11
  46. package/dist/commands/inspect.d.ts +94 -1
  47. package/dist/commands/inspect.js +866 -40
  48. package/dist/commands/lease.d.ts +4 -4
  49. package/dist/commands/lease.js +6 -6
  50. package/dist/commands/login.js +0 -13
  51. package/dist/commands/logs.d.ts +2 -2
  52. package/dist/commands/logs.js +2 -2
  53. package/dist/commands/mcp.js +7 -2
  54. package/dist/commands/memory.js +7 -2
  55. package/dist/commands/message.d.ts +1 -1
  56. package/dist/commands/message.js +37 -6
  57. package/dist/commands/models.js +71 -0
  58. package/dist/commands/monitors.d.ts +0 -1
  59. package/dist/commands/monitors.js +199 -16
  60. package/dist/commands/open.d.ts +18 -0
  61. package/dist/commands/open.js +57 -0
  62. package/dist/commands/packages.js +2 -107
  63. package/dist/commands/plugins.js +6 -3
  64. package/dist/commands/profiles.d.ts +1 -0
  65. package/dist/commands/profiles.js +16 -160
  66. package/dist/commands/projects.d.ts +28 -0
  67. package/dist/commands/projects.js +125 -5
  68. package/dist/commands/reconnect.js +9 -3
  69. package/dist/commands/repo.js +42 -1
  70. package/dist/commands/resource-view.d.ts +17 -0
  71. package/dist/commands/resource-view.js +36 -14
  72. package/dist/commands/resume.js +9 -2
  73. package/dist/commands/routines.d.ts +0 -2
  74. package/dist/commands/routines.js +473 -289
  75. package/dist/commands/secrets.js +52 -9
  76. package/dist/commands/sessions-export.d.ts +47 -0
  77. package/dist/commands/sessions-export.js +116 -6
  78. package/dist/commands/sessions-import.d.ts +17 -0
  79. package/dist/commands/sessions-import.js +114 -6
  80. package/dist/commands/sessions-migrate.d.ts +27 -0
  81. package/dist/commands/sessions-migrate.js +50 -9
  82. package/dist/commands/sessions-picker.js +23 -4
  83. package/dist/commands/sessions-resume.d.ts +5 -3
  84. package/dist/commands/sessions-resume.js +30 -8
  85. package/dist/commands/sessions-watch.d.ts +2 -0
  86. package/dist/commands/sessions-watch.js +53 -0
  87. package/dist/commands/sessions.d.ts +66 -3
  88. package/dist/commands/sessions.js +298 -47
  89. package/dist/commands/setup-browser.js +2 -2
  90. package/dist/commands/setup-mine.js +0 -1
  91. package/dist/commands/setup-preferences.js +1 -1
  92. package/dist/commands/setup.d.ts +5 -0
  93. package/dist/commands/setup.js +31 -5
  94. package/dist/commands/share.d.ts +87 -7
  95. package/dist/commands/share.js +320 -58
  96. package/dist/commands/skills.js +0 -11
  97. package/dist/commands/snapshot.js +1 -1
  98. package/dist/commands/ssh.d.ts +18 -0
  99. package/dist/commands/ssh.js +549 -210
  100. package/dist/commands/sync.d.ts +5 -0
  101. package/dist/commands/sync.js +212 -32
  102. package/dist/commands/teams.d.ts +13 -0
  103. package/dist/commands/teams.js +187 -30
  104. package/dist/commands/tickets.d.ts +2 -0
  105. package/dist/commands/tickets.js +43 -0
  106. package/dist/commands/update.js +2 -4
  107. package/dist/commands/usage.js +5 -3
  108. package/dist/commands/versions.js +11 -5
  109. package/dist/commands/view.js +15 -7
  110. package/dist/commands/webhook.d.ts +2 -2
  111. package/dist/commands/webhook.js +7 -7
  112. package/dist/index.d.ts +18 -2
  113. package/dist/index.js +53 -1264
  114. package/dist/lib/account-catalog.d.ts +18 -0
  115. package/dist/lib/account-catalog.js +38 -0
  116. package/dist/lib/account-provider-registry.d.ts +18 -0
  117. package/dist/lib/account-provider-registry.js +59 -0
  118. package/dist/lib/account-registry.d.ts +40 -0
  119. package/dist/lib/account-registry.js +239 -0
  120. package/dist/lib/account-schema.d.ts +44 -0
  121. package/dist/lib/account-schema.js +91 -0
  122. package/dist/lib/account-state-service.d.ts +21 -0
  123. package/dist/lib/account-state-service.js +60 -0
  124. package/dist/lib/activity.d.ts +1 -1
  125. package/dist/lib/activity.js +8 -1
  126. package/dist/lib/add-dir.d.ts +80 -0
  127. package/dist/lib/add-dir.js +241 -0
  128. package/dist/lib/agent-cli-commands.d.ts +11 -0
  129. package/dist/lib/agent-cli-commands.js +30 -0
  130. package/dist/lib/agents.js +44 -36
  131. package/dist/lib/answer-router.js +1 -1
  132. package/dist/lib/audit/log.d.ts +10 -27
  133. package/dist/lib/audit/log.js +20 -33
  134. package/dist/lib/auth-health.d.ts +9 -2
  135. package/dist/lib/auth-health.js +42 -9
  136. package/dist/lib/auto-pull-worker.js +7 -3
  137. package/dist/lib/auto-pull.d.ts +44 -3
  138. package/dist/lib/auto-pull.js +88 -5
  139. package/dist/lib/binary-shadow.d.ts +28 -0
  140. package/dist/lib/binary-shadow.js +121 -0
  141. package/dist/lib/brand.js +9 -3
  142. package/dist/lib/browser/hygiene.d.ts +90 -0
  143. package/dist/lib/browser/hygiene.js +146 -0
  144. package/dist/lib/browser/ipc.d.ts +30 -0
  145. package/dist/lib/browser/ipc.js +93 -4
  146. package/dist/lib/browser/profiles.d.ts +3 -2
  147. package/dist/lib/browser/profiles.js +65 -22
  148. package/dist/lib/browser/service.d.ts +79 -1
  149. package/dist/lib/browser/service.js +231 -14
  150. package/dist/lib/browser/sessions-list.d.ts +22 -2
  151. package/dist/lib/browser/sessions-list.js +54 -6
  152. package/dist/lib/browser/stream.d.ts +2 -0
  153. package/dist/lib/browser/stream.js +1 -0
  154. package/dist/lib/browser/types.d.ts +63 -1
  155. package/dist/lib/byok-usage.d.ts +9 -2
  156. package/dist/lib/byok-usage.js +133 -31
  157. package/dist/lib/cli-entry.d.ts +11 -0
  158. package/dist/lib/cli-entry.js +46 -0
  159. package/dist/lib/cloud/host.d.ts +1 -1
  160. package/dist/lib/cloud/host.js +2 -2
  161. package/dist/lib/cloud/registry.js +1 -1
  162. package/dist/lib/cloud/types.d.ts +1 -1
  163. package/dist/lib/commands.d.ts +2 -0
  164. package/dist/lib/commands.js +2 -0
  165. package/dist/lib/computer/sessions-list.d.ts +150 -0
  166. package/dist/lib/computer/sessions-list.js +363 -0
  167. package/dist/lib/config-keys.d.ts +86 -0
  168. package/dist/lib/config-keys.js +193 -0
  169. package/dist/lib/config-machine-keys.d.ts +12 -0
  170. package/dist/lib/config-machine-keys.js +17 -0
  171. package/dist/lib/config-transfer.js +3 -4
  172. package/dist/lib/crabbox/cli.d.ts +2 -2
  173. package/dist/lib/crabbox/cli.js +3 -3
  174. package/dist/lib/daemon-health.d.ts +22 -0
  175. package/dist/lib/daemon-health.js +33 -3
  176. package/dist/lib/daemon-services.d.ts +50 -0
  177. package/dist/lib/daemon-services.js +147 -0
  178. package/dist/lib/daemon-ticks.d.ts +35 -66
  179. package/dist/lib/daemon-ticks.js +77 -159
  180. package/dist/lib/daemon.d.ts +72 -4
  181. package/dist/lib/daemon.js +792 -202
  182. package/dist/lib/deeplink/register.d.ts +49 -0
  183. package/dist/lib/deeplink/register.js +253 -0
  184. package/dist/lib/deeplink/url.d.ts +34 -0
  185. package/dist/lib/deeplink/url.js +78 -0
  186. package/dist/lib/device-config.d.ts +124 -29
  187. package/dist/lib/device-config.js +346 -141
  188. package/dist/lib/devices/config-migration.d.ts +42 -0
  189. package/dist/lib/devices/config-migration.js +203 -0
  190. package/dist/lib/devices/connect.js +14 -7
  191. package/dist/lib/devices/doctor-findings.d.ts +4 -1
  192. package/dist/lib/devices/doctor-findings.js +20 -1
  193. package/dist/lib/devices/harness-inventory.d.ts +11 -2
  194. package/dist/lib/devices/harness-inventory.js +0 -0
  195. package/dist/lib/devices/health.d.ts +1 -1
  196. package/dist/lib/devices/health.js +1 -1
  197. package/dist/lib/devices/pending.d.ts +22 -1
  198. package/dist/lib/devices/pending.js +67 -2
  199. package/dist/lib/devices/registry.d.ts +0 -25
  200. package/dist/lib/devices/registry.js +2 -83
  201. package/dist/lib/devices/resolve-profile.d.ts +20 -0
  202. package/dist/lib/devices/resolve-profile.js +44 -0
  203. package/dist/lib/devices/rollout-verify.d.ts +101 -0
  204. package/dist/lib/devices/rollout-verify.js +193 -0
  205. package/dist/lib/devices/ssh-config.js +10 -6
  206. package/dist/lib/event-families.d.ts +28 -0
  207. package/dist/lib/event-families.js +159 -0
  208. package/dist/lib/event-stream.d.ts +9 -1
  209. package/dist/lib/event-stream.js +26 -9
  210. package/dist/lib/events.d.ts +9 -1
  211. package/dist/lib/events.js +21 -6
  212. package/dist/lib/exec.d.ts +30 -0
  213. package/dist/lib/exec.js +219 -121
  214. package/dist/lib/feed-broadcast.d.ts +25 -0
  215. package/dist/lib/feed-broadcast.js +33 -0
  216. package/dist/lib/feed.d.ts +1 -1
  217. package/dist/lib/feed.js +12 -2
  218. package/dist/lib/fleet/capture.js +15 -0
  219. package/dist/lib/fleet/manifest.js +9 -0
  220. package/dist/lib/fleet/types.d.ts +9 -0
  221. package/dist/lib/fleet-status.js +10 -8
  222. package/dist/lib/git.d.ts +21 -7
  223. package/dist/lib/git.js +122 -18
  224. package/dist/lib/hosts/dispatch.d.ts +8 -3
  225. package/dist/lib/hosts/dispatch.js +29 -3
  226. package/dist/lib/hosts/logs.d.ts +2 -2
  227. package/dist/lib/hosts/logs.js +2 -2
  228. package/dist/lib/hosts/option.js +1 -1
  229. package/dist/lib/hosts/passthrough.d.ts +2 -3
  230. package/dist/lib/hosts/passthrough.js +10 -24
  231. package/dist/lib/hosts/progress.js +2 -2
  232. package/dist/lib/hosts/providers/devices.d.ts +3 -3
  233. package/dist/lib/hosts/providers/devices.js +18 -12
  234. package/dist/lib/hosts/ready.d.ts +5 -1
  235. package/dist/lib/hosts/ready.js +9 -3
  236. package/dist/lib/hosts/reconcile.d.ts +2 -2
  237. package/dist/lib/hosts/reconcile.js +2 -2
  238. package/dist/lib/hosts/registry.js +14 -9
  239. package/dist/lib/hosts/remote-cmd.js +1 -1
  240. package/dist/lib/hosts/remote-os.d.ts +9 -6
  241. package/dist/lib/hosts/remote-os.js +13 -6
  242. package/dist/lib/hosts/routing-flag.d.ts +23 -0
  243. package/dist/lib/hosts/routing-flag.js +41 -0
  244. package/dist/lib/hosts/run-target.js +1 -1
  245. package/dist/lib/hosts/session-index.js +1 -1
  246. package/dist/lib/hosts/tasks.d.ts +6 -6
  247. package/dist/lib/hosts/tasks.js +4 -4
  248. package/dist/lib/hosts/types.js +1 -2
  249. package/dist/lib/installations/resolve.js +2 -8
  250. package/dist/lib/isolation-boundary-report.js +0 -1
  251. package/dist/lib/linear-cache.d.ts +7 -0
  252. package/dist/lib/linear-cache.js +31 -4
  253. package/dist/lib/linear-project-counts.d.ts +1 -1
  254. package/dist/lib/linear-project-counts.js +2 -3
  255. package/dist/lib/linear-projects.js +1 -1
  256. package/dist/lib/loop.d.ts +2 -2
  257. package/dist/lib/loop.js +1 -1
  258. package/dist/lib/mailbox-target.d.ts +1 -1
  259. package/dist/lib/mailbox-target.js +1 -1
  260. package/dist/lib/manifest.js +7 -4
  261. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  262. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  263. package/dist/lib/menubar/snapshot.d.ts +3 -2
  264. package/dist/lib/menubar/snapshot.js +6 -2
  265. package/dist/lib/migrate-fold.d.ts +33 -0
  266. package/dist/lib/migrate-fold.js +130 -0
  267. package/dist/lib/migrate.d.ts +34 -34
  268. package/dist/lib/migrate.js +106 -128
  269. package/dist/lib/model-tier-overrides.d.ts +1 -1
  270. package/dist/lib/model-tier-overrides.js +15 -2
  271. package/dist/lib/monitors/config.d.ts +17 -3
  272. package/dist/lib/monitors/config.js +52 -18
  273. package/dist/lib/monitors/engine.d.ts +32 -1
  274. package/dist/lib/monitors/engine.js +94 -11
  275. package/dist/lib/monitors/fingerprint.d.ts +46 -0
  276. package/dist/lib/monitors/fingerprint.js +100 -0
  277. package/dist/lib/monitors/remote.d.ts +47 -0
  278. package/dist/lib/monitors/remote.js +81 -0
  279. package/dist/lib/monitors/state.d.ts +39 -0
  280. package/dist/lib/monitors/state.js +56 -0
  281. package/dist/lib/onepassword.d.ts +17 -0
  282. package/dist/lib/onepassword.js +52 -22
  283. package/dist/lib/open-url.js +1 -1
  284. package/dist/lib/picker.d.ts +14 -2
  285. package/dist/lib/picker.js +35 -13
  286. package/dist/lib/placement.d.ts +1 -1
  287. package/dist/lib/placement.js +1 -1
  288. package/dist/lib/pr-land-detach.d.ts +32 -0
  289. package/dist/lib/pr-land-detach.js +79 -0
  290. package/dist/lib/profiles-presets.js +13 -0
  291. package/dist/lib/profiles.d.ts +2 -0
  292. package/dist/lib/profiles.js +44 -1
  293. package/dist/lib/project-focus.d.ts +1 -1
  294. package/dist/lib/project-focus.js +1 -1
  295. package/dist/lib/project-probe.d.ts +4 -1
  296. package/dist/lib/project-probe.js +5 -4
  297. package/dist/lib/project-resources.js +2 -18
  298. package/dist/lib/project-root.d.ts +16 -0
  299. package/dist/lib/project-root.js +20 -2
  300. package/dist/lib/projects.d.ts +20 -1
  301. package/dist/lib/projects.js +78 -1
  302. package/dist/lib/refresh-coordinator.d.ts +24 -0
  303. package/dist/lib/refresh-coordinator.js +52 -0
  304. package/dist/lib/registry.d.ts +2 -2
  305. package/dist/lib/registry.js +1 -1
  306. package/dist/lib/resource-aliases.d.ts +13 -0
  307. package/dist/lib/resource-aliases.js +26 -0
  308. package/dist/lib/resources/types.d.ts +1 -1
  309. package/dist/lib/resources.d.ts +16 -0
  310. package/dist/lib/resources.js +66 -10
  311. package/dist/lib/rotate.d.ts +3 -4
  312. package/dist/lib/rotate.js +4 -5
  313. package/dist/lib/routine-activation.d.ts +22 -2
  314. package/dist/lib/routine-activation.js +63 -16
  315. package/dist/lib/routine-context.d.ts +9 -2
  316. package/dist/lib/routine-context.js +21 -6
  317. package/dist/lib/routines-project.d.ts +58 -48
  318. package/dist/lib/routines-project.js +190 -159
  319. package/dist/lib/routines.d.ts +44 -7
  320. package/dist/lib/routines.js +92 -34
  321. package/dist/lib/run-defaults.d.ts +5 -0
  322. package/dist/lib/run-defaults.js +11 -1
  323. package/dist/lib/runner.d.ts +10 -8
  324. package/dist/lib/runner.js +122 -39
  325. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  326. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  327. package/dist/lib/secrets/agent.d.ts +10 -3
  328. package/dist/lib/secrets/agent.js +43 -8
  329. package/dist/lib/secrets/bundles.d.ts +1 -0
  330. package/dist/lib/secrets/bundles.js +92 -80
  331. package/dist/lib/secrets/index.js +32 -9
  332. package/dist/lib/secrets/install-helper.d.ts +4 -4
  333. package/dist/lib/secrets/install-helper.js +4 -4
  334. package/dist/lib/secrets/push.d.ts +40 -6
  335. package/dist/lib/secrets/push.js +58 -11
  336. package/dist/lib/secrets/read-backoff.d.ts +1 -1
  337. package/dist/lib/secrets/read-backoff.js +1 -1
  338. package/dist/lib/secrets/remote.d.ts +24 -0
  339. package/dist/lib/secrets/remote.js +54 -4
  340. package/dist/lib/self-update.d.ts +108 -0
  341. package/dist/lib/self-update.js +276 -1
  342. package/dist/lib/session/active.d.ts +89 -5
  343. package/dist/lib/session/active.js +165 -10
  344. package/dist/lib/session/actor-sidecar.d.ts +1 -1
  345. package/dist/lib/session/actor-sidecar.js +2 -1
  346. package/dist/lib/session/db.d.ts +176 -2
  347. package/dist/lib/session/db.js +484 -17
  348. package/dist/lib/session/discover.js +27 -4
  349. package/dist/lib/session/hook-sessions.js +2 -2
  350. package/dist/lib/session/host-link.d.ts +2 -2
  351. package/dist/lib/session/host-link.js +2 -2
  352. package/dist/lib/session/pid-registry.d.ts +21 -0
  353. package/dist/lib/session/pid-registry.js +57 -0
  354. package/dist/lib/session/remote-active.js +15 -1
  355. package/dist/lib/session/remote.js +2 -2
  356. package/dist/lib/session/session-cache.d.ts +14 -7
  357. package/dist/lib/session/session-cache.js +51 -9
  358. package/dist/lib/session/state.d.ts +2 -2
  359. package/dist/lib/session/state.js +1 -1
  360. package/dist/lib/session/sync/config.d.ts +10 -10
  361. package/dist/lib/session/sync/config.js +10 -10
  362. package/dist/lib/session/sync/r2.d.ts +34 -0
  363. package/dist/lib/session/sync/r2.js +123 -0
  364. package/dist/lib/session/throughput.d.ts +1 -1
  365. package/dist/lib/session/throughput.js +1 -1
  366. package/dist/lib/session/types.d.ts +23 -1
  367. package/dist/lib/session/types.js +15 -0
  368. package/dist/lib/session/viewing-in.d.ts +1 -1
  369. package/dist/lib/session/viewing-in.js +1 -1
  370. package/dist/lib/session/watch.d.ts +101 -0
  371. package/dist/lib/session/watch.js +242 -0
  372. package/dist/lib/share/analytics.js +1 -1
  373. package/dist/lib/share/capture.d.ts +1 -1
  374. package/dist/lib/share/capture.js +3 -3
  375. package/dist/lib/share/config.d.ts +6 -2
  376. package/dist/lib/share/config.js +6 -5
  377. package/dist/lib/share/delete.js +3 -3
  378. package/dist/lib/share/provision.d.ts +38 -0
  379. package/dist/lib/share/provision.js +51 -1
  380. package/dist/lib/share/publish.d.ts +40 -1
  381. package/dist/lib/share/publish.js +119 -6
  382. package/dist/lib/share/worker-template.d.ts +12 -1
  383. package/dist/lib/share/worker-template.js +83 -7
  384. package/dist/lib/shims.d.ts +17 -3
  385. package/dist/lib/shims.js +98 -26
  386. package/dist/lib/signin-badge.js +3 -2
  387. package/dist/lib/skills.js +2 -0
  388. package/dist/lib/smart-launch.d.ts +8 -8
  389. package/dist/lib/smart-launch.js +80 -59
  390. package/dist/lib/ssh-exec.d.ts +1 -1
  391. package/dist/lib/ssh-exec.js +1 -1
  392. package/dist/lib/staleness/prune.d.ts +88 -0
  393. package/dist/lib/staleness/prune.js +69 -0
  394. package/dist/lib/staleness/registry.d.ts +1 -1
  395. package/dist/lib/staleness/writers/commands.js +34 -2
  396. package/dist/lib/staleness/writers/hooks.js +16 -0
  397. package/dist/lib/staleness/writers/kinds.d.ts +6 -0
  398. package/dist/lib/staleness/writers/skills.js +19 -0
  399. package/dist/lib/staleness/writers/subagents.d.ts +2 -3
  400. package/dist/lib/staleness/writers/subagents.js +0 -4
  401. package/dist/lib/staleness/writers/types.d.ts +32 -0
  402. package/dist/lib/startup/command-registry.d.ts +16 -14
  403. package/dist/lib/startup/command-registry.js +26 -37
  404. package/dist/lib/startup/dev-build.d.ts +10 -0
  405. package/dist/lib/startup/dev-build.js +13 -1
  406. package/dist/lib/startup/spellcheck.d.ts +18 -0
  407. package/dist/lib/startup/spellcheck.js +45 -0
  408. package/dist/lib/state.d.ts +36 -9
  409. package/dist/lib/state.js +96 -46
  410. package/dist/lib/subagents-registry.d.ts +0 -7
  411. package/dist/lib/subagents-registry.js +38 -65
  412. package/dist/lib/subagents.d.ts +0 -33
  413. package/dist/lib/subagents.js +0 -75
  414. package/dist/lib/sync-umbrella.js +1 -1
  415. package/dist/lib/teams/agents.d.ts +41 -1
  416. package/dist/lib/teams/agents.js +187 -9
  417. package/dist/lib/teams/api.d.ts +11 -1
  418. package/dist/lib/teams/api.js +11 -2
  419. package/dist/lib/teams/delivery.d.ts +41 -0
  420. package/dist/lib/teams/delivery.js +60 -0
  421. package/dist/lib/teams/registry.d.ts +14 -0
  422. package/dist/lib/teams/registry.js +51 -1
  423. package/dist/lib/teams/remoteWorktree.d.ts +19 -0
  424. package/dist/lib/teams/remoteWorktree.js +29 -0
  425. package/dist/lib/teams/scheduler.d.ts +1 -1
  426. package/dist/lib/teams/scheduler.js +1 -1
  427. package/dist/lib/teams/worktree.d.ts +24 -0
  428. package/dist/lib/teams/worktree.js +42 -0
  429. package/dist/lib/terminal/backends/vscodium-agent.d.ts +10 -1
  430. package/dist/lib/terminal/backends/vscodium-agent.js +16 -5
  431. package/dist/lib/terminal/engine.d.ts +4 -0
  432. package/dist/lib/terminal/engine.js +8 -2
  433. package/dist/lib/terminal/types.d.ts +21 -2
  434. package/dist/lib/testdata/refresh-coordinator-worker.d.ts +1 -0
  435. package/dist/lib/testdata/refresh-coordinator-worker.js +23 -0
  436. package/dist/lib/tickets/list.d.ts +53 -0
  437. package/dist/lib/tickets/list.js +153 -0
  438. package/dist/lib/tmux/orphan-reap.d.ts +316 -0
  439. package/dist/lib/tmux/orphan-reap.js +638 -0
  440. package/dist/lib/tmux/session.d.ts +68 -3
  441. package/dist/lib/tmux/session.js +96 -5
  442. package/dist/lib/triggers/handlers.d.ts +20 -0
  443. package/dist/lib/triggers/handlers.js +16 -1
  444. package/dist/lib/triggers/webhook.js +10 -0
  445. package/dist/lib/types.d.ts +47 -35
  446. package/dist/lib/usage-fleet.d.ts +32 -0
  447. package/dist/lib/usage-fleet.js +125 -0
  448. package/dist/lib/usage-refresh.d.ts +3 -3
  449. package/dist/lib/usage-refresh.js +19 -16
  450. package/dist/lib/usage.d.ts +51 -47
  451. package/dist/lib/usage.js +145 -153
  452. package/dist/lib/version-duplicates.js +2 -2
  453. package/dist/lib/versions.d.ts +56 -13
  454. package/dist/lib/versions.js +195 -45
  455. package/dist/lib/watchdog/log.d.ts +1 -1
  456. package/dist/lib/watchdog/log.js +3 -3
  457. package/dist/lib/watchdog/rotate.d.ts +6 -6
  458. package/dist/lib/watchdog/rotate.js +6 -6
  459. package/dist/lib/workflows.d.ts +1 -1
  460. package/dist/lib/yaml-io.d.ts +47 -0
  461. package/dist/lib/yaml-io.js +55 -0
  462. package/package.json +2 -1
  463. package/scripts/install-helper.js +2 -2
  464. package/scripts/postinstall.js +23 -20
  465. package/dist/commands/defaults.d.ts +0 -7
  466. package/dist/commands/defaults.js +0 -107
  467. package/dist/commands/export.d.ts +0 -11
  468. package/dist/commands/export.js +0 -215
  469. package/dist/commands/helper.d.ts +0 -12
  470. package/dist/commands/helper.js +0 -87
  471. package/dist/commands/hosts.d.ts +0 -11
  472. package/dist/commands/hosts.js +0 -330
  473. package/dist/commands/lock.d.ts +0 -12
  474. package/dist/commands/lock.js +0 -70
  475. package/dist/commands/pull.d.ts +0 -17
  476. package/dist/commands/pull.js +0 -39
  477. package/dist/commands/push.d.ts +0 -14
  478. package/dist/commands/push.js +0 -30
  479. package/dist/commands/set.d.ts +0 -15
  480. package/dist/commands/set.js +0 -79
  481. package/dist/commands/setup-share.d.ts +0 -17
  482. package/dist/commands/setup-share.js +0 -85
  483. package/dist/commands/wallet.d.ts +0 -20
  484. package/dist/commands/wallet.js +0 -216
  485. package/dist/commands/worktree.d.ts +0 -19
  486. package/dist/commands/worktree.js +0 -272
  487. package/dist/lib/account-labels.d.ts +0 -24
  488. package/dist/lib/account-labels.js +0 -72
  489. package/dist/lib/auto-dispatch-linear.d.ts +0 -18
  490. package/dist/lib/auto-dispatch-linear.js +0 -107
  491. package/dist/lib/auto-dispatch-provider.d.ts +0 -10
  492. package/dist/lib/auto-dispatch-provider.js +0 -30
  493. package/dist/lib/auto-dispatch.d.ts +0 -93
  494. package/dist/lib/auto-dispatch.js +0 -128
  495. package/dist/lib/export.d.ts +0 -72
  496. package/dist/lib/export.js +0 -269
  497. package/dist/lib/lock.d.ts +0 -93
  498. package/dist/lib/lock.js +0 -207
  499. package/dist/lib/wallet/index.d.ts +0 -78
  500. package/dist/lib/wallet/index.js +0 -253
package/dist/index.js CHANGED
@@ -2,32 +2,31 @@
2
2
  /**
3
3
  * CLI entry point for agents-cli.
4
4
  *
5
- * Registers all commands, handles update checks, auto-corrects typos,
6
- * and launches the first-run interactive setup when appropriate.
5
+ * Slim shell (RUSH-2335): only the leaf `lib/secrets/sync-commands.js` is a
6
+ * static import, so the argv fast paths below can answer without evaluating the
7
+ * commander + self-update + command-registry graph that used to be hoisted
8
+ * above every intercept (~140ms of cold bootstrap per synchronous secrets-
9
+ * broker read). The full CLI loads via `await import('./bootstrap.js')` once
10
+ * none of the fast paths match.
11
+ *
12
+ * Fast paths (must stay above the bootstrap import — ESM does not hoist dynamic
13
+ * `import()`, but any static import here would still evaluate first):
14
+ * - `__vault-age-helper`
15
+ * - `__secrets-get` / `__secrets-ping` / `__secrets-lock` (SYNC_* tokens)
16
+ * - `__shim`
17
+ * - `__daemon-run`
18
+ *
19
+ * The tokens are imported from the leaf module sync-commands.ts — the SAME
20
+ * bindings the clients spawn with, so this dispatch and those spawns cannot
21
+ * drift apart. It is a leaf precisely so binding them here is free: importing
22
+ * agent.js would pull the whole secrets graph into every invocation.
7
23
  */
8
- import { Command } from 'commander';
9
- import chalk from 'chalk';
10
- import * as fs from 'fs';
11
- import * as path from 'path';
12
- import { fileURLToPath } from 'url';
13
- import { detectDevBuild } from './lib/startup/dev-build.js';
14
- import { configureRootCommand } from './lib/startup/root-command.js';
15
- // `ora`, `@inquirer/prompts`, `./commands/utils.js`, and the agents/versions/shims
16
- // modules are imported dynamically at their use sites: they are needed only on
17
- // interactive / update / shim-repair paths, never for fast commands like
18
- // `--version`, `--help`, or `view`. Keeping them off the module-eval path is
19
- // what gets cold starts under the target.
24
+ import { SYNC_GET_CMD, SYNC_PING_CMD, SYNC_LOCK_CMD } from './lib/secrets/sync-commands.js';
20
25
  // Force exit on Ctrl+C when no interactive prompt is handling it.
21
26
  process.on('SIGINT', () => process.exit(130));
22
27
  // Ignore SIGPIPE — prevents exit code 13 crashes in piped environments
23
28
  // (e.g. `agents sessions | head`, or when stdout is captured by another process).
24
29
  process.on('SIGPIPE', () => { });
25
- // Get version from package.json
26
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
27
- const packageJsonPath = path.join(__dirname, '..', 'package.json');
28
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
29
- const VERSION = packageJson.version;
30
- import { SYNC_GET_CMD, SYNC_PING_CMD, SYNC_LOCK_CMD } from './lib/secrets/sync-commands.js';
31
30
  if (process.argv[2] === '__vault-age-helper') {
32
31
  const { runVaultAgeHelperCli } = await import('./lib/secrets/vault-age-helper.js');
33
32
  await runVaultAgeHelperCli();
@@ -38,28 +37,14 @@ if (process.argv[2] === '__vault-age-helper') {
38
37
  // it can't await a socket round-trip — it spawns one of these and reads the
39
38
  // exit code (0 = hit/alive, 3 = miss/down).
40
39
  //
41
- // Intercepted HERE, before commander and before the startup STATEMENTS, for the
42
- // same reason as __daemon-run and __vault-age-helper above. Everything below
43
- // this point runs `checkForUpdates()` and `spawnDetachedSync()` on every
44
- // non-help invocation so registering these as ordinary hidden subcommands
45
- // would fire an update check and fork a detached background sync on *every
46
- // cache hit*, which is both a fork storm on the hot path and a source of stdout
47
- // writes that could corrupt the JSON payload. Keep them above the line;
48
- // agent.test.ts asserts this ordering so the block can't drift downward.
49
- //
50
- // The tokens are imported from the leaf module sync-commands.ts — the SAME
51
- // bindings the clients spawn with, so this dispatch and those spawns cannot
52
- // drift apart. It is a leaf precisely so binding them here is free: importing
53
- // agent.js would pull the whole secrets graph into every invocation. Matching
54
- // string literals were the earlier design; a text-scanning test could not
55
- // reliably catch their divergence, and the divergence is silent — the CLI says
56
- // "unknown command", the client reads that as "broker down", and every read
57
- // falls back to a Touch ID prompt.
58
- //
59
- // "Before the startup statements", not "before all code": ESM hoists the
60
- // imports below, so those module bodies evaluate first. They are side-effect
61
- // free today (no top-level stdout write or spawn) — which is what makes this
62
- // safe, and worth preserving.
40
+ // Intercepted HERE, before bootstrap, for the same reason as __daemon-run and
41
+ // __vault-age-helper. Everything in bootstrap runs `checkForUpdates()` and
42
+ // `spawnDetachedSync()` on every non-help invocation — so registering these as
43
+ // ordinary hidden subcommands would fire an update check and fork a detached
44
+ // background sync on *every cache hit*, which is both a fork storm on the hot
45
+ // path and a source of stdout writes that could corrupt the JSON payload. Keep
46
+ // them above the line; agent.test.ts asserts this ordering so the block can't
47
+ // drift into bootstrap.
63
48
  if (process.argv[2] === SYNC_GET_CMD ||
64
49
  process.argv[2] === SYNC_PING_CMD ||
65
50
  process.argv[2] === SYNC_LOCK_CMD) {
@@ -71,40 +56,8 @@ if (process.argv[2] === SYNC_GET_CMD ||
71
56
  : await runAgentLockSync(name);
72
57
  process.exit(code);
73
58
  }
74
- import { NPM_PACKAGE_NAME, deriveGlobalPrefix, detectPackageManager, installPackageIntoPrefix, installPackageWithBun, verifyInstalledVersion, refreshAliasShims, downloadVerifiedTarball, } from './lib/self-update.js';
75
- import { registerUpgradeCommand } from './commands/upgrade.js';
76
- // Detect dev/working-tree builds and default the noisy startup steps off.
77
- // Three cases trip this:
78
- // 1. Dev install (scripts/install.sh) — package.json version stamped 0.0.0-dev.<sha>
79
- // 2. Running `node dist/index.js` from a working tree — repo root has .git/
80
- // 3. Running tsx/ts-node from src/ — also has .git/ at the repo root
81
- // For all three: skip auto-pull (no network noise + no surprise FF on the
82
- // system repo while iterating), skip migration (a buggy in-progress migration
83
- // must not scribble on the user's real ~/.agents/), and skip the update prompt
84
- // (the "0.0.0-dev -> 1.x.y" message is misleading). Each individual env var
85
- // can still be set explicitly to override (set to '0' to re-enable).
86
- const IS_DEV_BUILD = detectDevBuild(process.argv[1] || '', VERSION);
87
- if (IS_DEV_BUILD) {
88
- if (process.env.AGENTS_NO_AUTOPULL === undefined)
89
- process.env.AGENTS_NO_AUTOPULL = '1';
90
- if (process.env.AGENTS_SKIP_MIGRATION === undefined)
91
- process.env.AGENTS_SKIP_MIGRATION = '1';
92
- if (process.env.AGENTS_CLI_DISABLE_AUTO_UPDATE === undefined)
93
- process.env.AGENTS_CLI_DISABLE_AUTO_UPDATE = '1';
94
- }
95
- // Command registration is lazy: instead of statically importing every command
96
- // module on each invocation (which loaded the whole ~50-module tree before the
97
- // first byte of output), the registry maps a command name to a thunk that
98
- // imports only what that command needs. See src/lib/startup/command-registry.ts.
99
- import { COMMAND_LOADERS, LAZY_COMMAND_NAMES, loadView, loadInspect, loadFeedback, loadCommands, loadHooks, loadSkills, loadRules, loadMemory, loadPermissions, loadMcp, loadCli, loadSubagents, loadPlugins, loadWorkflows, loadWorktree, loadVersions, loadUpdate, loadImport, loadExport, loadPackages, loadRoutines, loadDaemon, loadMonitors, loadProjects, loadRun, loadFork, loadDefaults, loadSet, loadModels, loadModes, loadPrune, loadTrash, loadRestore, loadDoctor, loadApply, loadStatus, loadSnapshot, loadProfiles, loadHarness, loadSecrets, loadLogin, loadWallet, loadHelper, loadMenubar, loadBeta, loadSync, loadLock, loadRefreshRules, loadFactory, loadUsage, loadCost, loadInsights, loadPerf, loadTrends, loadOutput, loadBudget, loadAlias, loadMine, loadPty, loadTmux, loadWatchdog, loadBrowser, loadComputer, loadHosts, loadLogs, loadEvents, loadAudit, loadWebhook, loadFunnel, loadHumans, loadAccounts, loadSsh, loadPull, loadPush, loadRepo, loadSetup, loadUninstall, loadBench, loadShare, loadSend, loadFeed, loadMailboxes, } from './lib/startup/command-registry.js';
100
- import { applyGlobalHelpConventions } from './lib/help.js';
101
- import { renderWhatsNew } from './lib/whats-new.js';
102
- import { getCliLaunch } from './lib/cli-entry.js';
103
- import { emit, emitFriction, redactArgs } from './lib/events.js';
104
- import { stampProvenance } from './lib/event-provenance.js';
105
- import { die } from './lib/format.js';
106
59
  // Transparent shim delegate: the generated Windows `.cmd` shims invoke
107
- // `agents __shim <agent>[@version] <raw args>`. Intercept here, before commander
60
+ // `agents __shim <agent>[@version] <raw args>`. Intercept here, before bootstrap
108
61
  // parses anything, so the agent's own flags (`--help`, `--version`, etc.) pass
109
62
  // through completely untouched and we skip registering the full command tree.
110
63
  if (process.argv[2] === '__shim') {
@@ -118,1199 +71,35 @@ if (process.argv[2] === '__shim') {
118
71
  process.exit(code);
119
72
  }
120
73
  if (process.argv[2] === '__daemon-run') {
121
- const { runDaemon } = await import('./lib/daemon.js');
122
- await runDaemon();
123
- process.exit(process.exitCode ?? 0);
124
- }
125
- // One-shot invocation of a migrated daemon housekeeping tick (RUSH-2353). The
126
- // shipped system routines (`watchdog`, `device-probe`, `fleet-cache-warm`, ...)
127
- // run this as their `command:` instead of the daemon holding a setInterval —
128
- // same tick body, now scheduled/tracked/pinnable through the routines system.
129
- if (process.argv[2] === '__daemon-tick') {
130
- const name = process.argv[3] || '';
131
- const { runDaemonTick } = await import('./lib/daemon-ticks.js');
132
- try {
133
- await runDaemonTick(name);
134
- process.exit(0);
135
- }
136
- catch (err) {
137
- process.stderr.write(`[agents] daemon tick '${name}' failed: ${err.message}\n`);
138
- process.exit(1);
139
- }
140
- }
141
- // White-label: the shim for a brand (e.g. `jack`) exports AGENTS_BRAND, so the
142
- // CLI presents its own name/help/errors as the brand. Unbranded (AGENTS_BRAND
143
- // unset) resolves to 'agents' and everything below is byte-identical to before.
144
- const BRAND = resolveBrandName();
145
- const program = configureRootCommand(new Command(), BRAND, VERSION);
146
- // ─── Audit backbone ────────────────────────────────────────────────────────────
147
- // One choke point logs every `agents <module> <cmd>` invocation to the structured
148
- // event log — so team create/disband, agent run, secrets access, and everything
149
- // else is captured generically (with SSH/remote-user attribution added in emit()),
150
- // no per-command wiring. `agents events` reads it back. Attached to the root
151
- // program, so it's inherited by every subcommand regardless of lazy registration.
152
- /** Command path from the acting command up to (but excluding) the `agents` root. */
153
- function auditCommandPath(cmd) {
154
- const parts = [];
155
- let c = cmd;
156
- while (c && c.name() && c.name() !== BRAND) {
157
- parts.unshift(c.name());
158
- c = c.parent;
159
- }
160
- return parts;
161
- }
162
- const auditStarts = new WeakMap();
163
- /**
164
- * Commands that WRITE the event stream, so recording their own invocation would
165
- * add records to the log they are writing into. `events emit` is batched — one
166
- * flush every few seconds per open editor window — so auditing it would bury the
167
- * real events under two `command.*` records per flush. `_internal friction`
168
- * exists for the same reason (shell guards fire before any `agents` process
169
- * exists, so they cannot emit in-process) and had the same self-logging bug.
170
- */
171
- const AUDIT_EXEMPT_COMMANDS = new Set([
172
- 'events emit',
173
- '_internal friction',
174
- ]);
175
- program.hook('preAction', (_thisCommand, actionCommand) => {
176
- try {
177
- const parts = auditCommandPath(actionCommand);
178
- if (parts.length === 0)
179
- return;
180
- if (AUDIT_EXEMPT_COMMANDS.has(parts.join(' ')))
181
- return;
182
- auditStarts.set(actionCommand, Date.now());
183
- emit('command.start', {
184
- module: parts[0],
185
- command: parts.join(' '),
186
- // Commander exposes positional operands in actionCommand.args but omits
187
- // parsed option values. Audit the real argv so sensitive flags are seen
188
- // and redacted instead of silently bypassing the policy.
189
- args: redactArgs(process.argv.slice(2, 22)),
190
- cwd: process.cwd(),
191
- });
192
- }
193
- catch {
194
- // Audit logging must never break command dispatch.
195
- }
196
- });
197
- program.hook('postAction', (_thisCommand, actionCommand) => {
198
- try {
199
- const parts = auditCommandPath(actionCommand);
200
- if (parts.length === 0)
201
- return;
202
- if (AUDIT_EXEMPT_COMMANDS.has(parts.join(' ')))
203
- return;
204
- const started = auditStarts.get(actionCommand);
205
- const durationMs = started !== undefined ? Date.now() - started : undefined;
206
- const command = parts.join(' ');
207
- emit('command.end', {
208
- module: parts[0],
209
- command,
210
- ...(durationMs !== undefined ? { durationMs } : {}),
211
- });
212
- if (parts[0] === 'run') {
213
- const agentName = actionCommand.args?.[0] ? String(actionCommand.args[0]).split('@')[0] : 'run';
214
- void import('./lib/analytics/usage-db.js').then(({ recordUsage }) => {
215
- recordUsage({
216
- kind: 'agent',
217
- name: agentName || 'run',
218
- event: 'invoke',
219
- source: 'cli',
220
- meta: durationMs !== undefined ? { durationMs } : undefined,
221
- });
222
- }).catch(() => { });
223
- }
224
- // Disposable perf warehouse — fail-soft spool append (no SQLite on this path).
225
- if (durationMs !== undefined && parts[0] !== 'perf') {
226
- // sessionId/agent are resolvable here the same way emit() resolves them
227
- // for command.start/command.end above (the shared provenance floor,
228
- // event-provenance.ts) — without this, every command.end perf sample
229
- // was anonymous, unlike the audit log record right next to it.
230
- const { sessionId, agent } = stampProvenance();
231
- void import('./lib/perf/spool.js').then(({ recordSample }) => {
232
- recordSample({
233
- kind: 'command.end',
234
- label: command,
235
- durationMs,
236
- cwd: process.cwd(),
237
- sessionId,
238
- agent,
239
- });
240
- }).catch(() => { });
241
- }
242
- }
243
- catch {
244
- // Best-effort completion record; the start line is the durable audit fact.
245
- }
246
- });
247
- /**
248
- * Skin the static root help for a brand: rewrite the visible `agents` command
249
- * examples to the brand name and drop lines for commands this brand disabled.
250
- * A no-op for the unbranded `agents` CLI with nothing disabled.
251
- */
252
- function brandRootHelp(raw) {
253
- let text = raw;
254
- if (BRAND !== 'agents') {
255
- text = text
256
- .replace(/Usage: agents /g, `Usage: ${BRAND} `)
257
- .replace(/^ {2}agents /gm, ` ${BRAND} `)
258
- .replace(/Run 'agents /g, `Run '${BRAND} `);
259
- }
260
- const disabled = disabledCommandsForActiveBrand();
261
- if (disabled.size > 0) {
262
- text = text
263
- .split('\n')
264
- .filter((line) => {
265
- const m = line.match(/^ {2}([a-z][\w-]*)/);
266
- return !(m && disabled.has(m[1]));
267
- })
268
- .join('\n');
269
- }
270
- return text;
271
- }
272
- // Custom help for the main program only
273
- const originalHelpInformation = program.helpInformation.bind(program);
274
- program.helpInformation = function () {
275
- if (this.name() === BRAND && !this.parent) {
276
- return brandRootHelp(`Usage: agents [command] [options]
277
-
278
- Install, configure, run, and dispatch AI coding agents from one place.
279
- Works with Claude, Codex, Antigravity, Cursor, OpenCode, OpenClaw, and Droid.
280
-
281
- Quick start:
282
- agents setup First-time setup (interactive)
283
- agents view See what's installed
284
- agents run <agent> ["prompt"] Run an agent (interactive without prompt, headless with)
285
- agents sessions Browse past sessions across all agents
286
-
287
- Agent versions:
288
- add <agent>[@version] Install an agent CLI (e.g. agents add grok or agents add codex)
289
- import <agent> Adopt an existing global install (npm/homebrew) into agents-cli
290
- update <agent>[@version] Move an installed agent to a new release, keeping its name (agents-cli itself is 'agents upgrade')
291
- prune <agent>[@version] Uninstall a version
292
- remove <agent>[@version] Alias for prune
293
- use <agent>@<version> Set the default version
294
- prune cleanup [target] Remove orphan resources and older duplicate version installs
295
- trash Inspect and restore soft-deleted version directories
296
- view [agent[@version]] List versions, inspect one in detail, or --merged for the cross-layer resource surface
297
- inspect <target> Deep details for one agent+version, or a DotAgents repo (user|system|project|alias|path)
298
-
299
- Agent configuration (synced across versions):
300
- rules Instructions given to agents (CLAUDE.md, etc.)
301
- commands Slash commands (/commit, /test, etc.)
302
- skills Knowledge packs (SKILL.md + supporting files)
303
- mcp MCP servers (stdio or HTTP)
304
- permissions Allow/deny rules for tool calls
305
- hooks Shell scripts that run on agent events (hooks.yaml in agents.yaml)
306
- subagents Named sub-agent definitions
307
- plugins Bundles of skills, hooks, and scripts
308
-
309
- Packages:
310
- search <query> Find MCP servers and skills in registries
311
- install <pkg> Install from registry (mcp:name, skill:user/repo)
312
-
313
- Run and dispatch:
314
- run <agent|profile> [prompt] Run an agent. Omit prompt for interactive mode.
315
- defaults Configure run defaults by agent/version selector
316
- teams Coordinate multiple agents on shared work
317
- routines Run agents on a cron schedule (scheduler auto-starts)
318
- daemon Runtime status/control for the always-on daemon (secrets broker, browser IPC, scheduler)
319
- webhook Receive signed GitHub/Linear webhooks for trigger routines
320
- funnel Expose a webhook receiver through Tailscale Funnel
321
- sessions Browse, search, and replay past runs (live-search in TTY; grouped by workspace)
322
- logs [id] Show a run's log — host-dispatch task or session; -f to follow
323
- browser Automate a browser — navigate, click, screenshot, console, network
324
- pty Drive interactive terminal programs (REPLs, TUIs) via a persistent PTY session
325
-
326
- Observe (read the fleet — no store merge; aliases point at the real readers):
327
- feed / inbox Needs-you inbox (open blocks waiting on you)
328
- timeline Agent progress stream (= feed --filter updates)
329
- roster Live agents (= sessions --active)
330
- events Unified ops + activity event trail
331
- audit Tamper-evident run-dispatch log (not events)
332
- snapshot One-process inventory + active sessions poll
333
- status Sync/drift only (not the live fleet snapshot)
334
-
335
- Credentials and profiles:
336
- profile Activate resource profiles across skills, MCP, permissions, and secrets
337
- profiles Bundles of (host CLI, endpoint, model, auth)
338
- secrets Keychain-backed env bundles; use 'secrets exec <bundle> -- <cmd>' to inject into a subprocess
339
-
340
- Diagnostics:
341
- doctor [agent[@version]] Diagnose CLI availability, sync status, and resource divergence; --check for the CI drift gate
342
- usage [agent] Show rate-limit and quota usage per agent
343
- insights How work looks — behaviour (default) or counter mix (insights mix)
344
- perf Latency rollups (hooks, commands, runs) from the disposable perf warehouse
345
-
346
- Config sync:
347
- repo pull [alias] Git pull a repo (system | user | <extra>)
348
- sync [agent] Re-materialize installed version homes; --local to skip fetching
349
- repo init --path <dir> Scaffold your own editable repo from a template
350
- repo add <path|gh:user/repo> Merge an extra repo after the system repo
351
- lock [--frozen] Write/verify agents.lock (SHA-256 of resolved resources); --frozen fails on drift
352
-
353
- Beta features:
354
- beta Enable preview features (factory and more)
355
-
356
- Automation tips:
357
- Pass explicit names/IDs Avoid pickers: agents sessions <id> --markdown
358
- Use --yes for defaults Auto-accept sync/default prompts on add/use/pull
359
- Use --names for central items e.g. agents commands add --names review-pr,debug
360
- Use agent@version targets e.g. --agents grok@0.1.218,claude@2.1.79,codex@default
361
- Non-TTY shells apply defaults Omitted required selections fail with a plain hint
362
-
363
- Options:
364
- -V, --version Show version number
365
- -h, --help Show help
366
- --verbose Show startup self-heal details on stderr
367
-
368
- System config lives in ~/.agents/.system/. Run 'agents <command> --help' for details.
369
- `);
370
- }
371
- return originalHelpInformation();
372
- };
373
- /** Compare two semver version strings. Returns 1 if a > b, -1 if a < b, 0 if equal. */
374
- function compareVersions(a, b) {
375
- const partsA = a.split('.').map(Number);
376
- const partsB = b.split('.').map(Number);
377
- for (let i = 0; i < 3; i++) {
378
- if (partsA[i] > partsB[i])
379
- return 1;
380
- if (partsA[i] < partsB[i])
381
- return -1;
382
- }
383
- return 0;
384
- }
385
- /** Fetch and display changelog entries between two versions from unpkg. */
386
- async function showWhatsNew(fromVersion, toVersion) {
387
- try {
388
- const response = await fetch(`https://unpkg.com/@phnx-labs/agents-cli@${toVersion}/CHANGELOG.md`);
389
- if (!response.ok)
390
- return;
391
- const relevantChanges = renderWhatsNew(await response.text(), fromVersion, toVersion);
392
- if (relevantChanges.length > 0) {
393
- console.log(chalk.bold("\nWhat's new:\n"));
394
- for (const line of relevantChanges) {
395
- console.log(line);
396
- }
397
- console.log(chalk.gray('\nFull notes: https://github.com/phnx-labs/agents-cli/blob/main/CHANGELOG.md'));
398
- console.log();
399
- }
400
- }
401
- catch {
402
- // Silently ignore changelog fetch errors
403
- }
404
- }
405
- const UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
406
- import { getUpdateCheckPath, getMigratedSentinelPath, getUserAgentsDir, getRuntimeStateDir } from './lib/state.js';
407
- import { resolveBrandName, disabledCommandsForActiveBrand } from './lib/brand.js';
408
- import { readUpdateCache, saveUpdateCheck, dismissUpdateVersion, shouldPromptUpgrade, buildMultiInstallInventory, findAgentsCliInstalls, resolveRunningPackageRoot, } from './lib/self-update.js';
409
- const UPDATE_CHECK_FILE = getUpdateCheckPath();
410
- /**
411
- * Warn once when this machine contains a different agents-cli install than the
412
- * copy that is currently running (or several). Divergent installs
413
- * are how self-updates "succeed" without changing the command the user types.
414
- * The warning re-fires only when the set of install roots or their helper-copy
415
- * safety changes. Dev builds are included because old dev copies can still
416
- * overwrite the shared macOS helper bundle non-atomically.
417
- */
418
- function maybeWarnMultiInstall() {
419
- const sentinel = path.join(getRuntimeStateDir(), 'multi-install-warned');
420
- let runningRoot;
421
- try {
422
- runningRoot = resolveRunningPackageRoot(__dirname);
423
- }
424
- catch {
425
- // Without a real root for the running copy there is nothing to compare
426
- // against, and a guess here is exactly what produced the phantom
427
- // "/$bunfs" install. This warning is advisory — stay silent instead.
428
- return;
429
- }
430
- const inventory = buildMultiInstallInventory(runningRoot, VERSION, findAgentsCliInstalls(process.env.PATH || ''));
431
- if (inventory.length < 2) {
432
- try {
433
- fs.unlinkSync(sentinel);
434
- }
435
- catch { /* nothing recorded */ }
436
- return;
437
- }
438
- const key = inventory
439
- .map((info) => `${info.packageRoot}\t${info.version}\t${info.note}`)
440
- .sort()
441
- .join('\n');
442
- try {
443
- if (fs.readFileSync(sentinel, 'utf-8') === key)
444
- return;
445
- }
446
- catch { /* not warned for this set yet */ }
447
- console.error(chalk.yellow('Multiple agents-cli installs detected:'));
448
- for (const info of inventory) {
449
- console.error(chalk.gray(` ${info.packageRoot} ${info.version} (${info.note})`));
450
- }
451
- console.error(chalk.gray('Upgrades apply to the running copy. Remove a stale copy with: npm uninstall -g --prefix <prefix> @phnx-labs/agents-cli'));
452
- try {
453
- fs.mkdirSync(path.dirname(sentinel), { recursive: true });
454
- fs.writeFileSync(sentinel, key);
455
- }
456
- catch { /* best-effort; worst case the warning repeats */ }
457
- }
458
- /** Determine whether enough time has elapsed since the last registry fetch. */
459
- function shouldFetchLatest(cache) {
460
- if (!cache)
461
- return true;
462
- return Date.now() - cache.lastCheck > UPDATE_CHECK_INTERVAL_MS;
463
- }
464
- /** Fetch the exact latest npm version plus its registry integrity hash. */
465
- async function fetchNpmPackageMetadata(versionOrTag = 'latest', timeoutMs = 5000) {
466
- const response = await fetch(`https://registry.npmjs.org/${NPM_PACKAGE_NAME}/${versionOrTag}`, {
467
- signal: AbortSignal.timeout(timeoutMs),
468
- });
469
- if (!response.ok) {
470
- if (response.status === 404) {
471
- throw new Error(`${NPM_PACKAGE_NAME}@${versionOrTag} not found on npm`);
472
- }
473
- throw new Error('Could not reach npm registry');
474
- }
475
- const data = await response.json();
476
- if (typeof data.version !== 'string' ||
477
- typeof data.dist?.integrity !== 'string' ||
478
- typeof data.dist?.tarball !== 'string') {
479
- throw new Error('npm registry response did not include version, integrity, and tarball');
480
- }
481
- return { version: data.version, integrity: data.dist.integrity, tarball: data.dist.tarball };
482
- }
483
- function printResolvedPackage(metadata) {
484
- console.log(chalk.gray(`Resolved: ${NPM_PACKAGE_NAME}@${metadata.version}`));
485
- console.log(chalk.gray(`Integrity: ${metadata.integrity}`));
486
- }
487
- async function installResolvedPackage(metadata) {
488
- const packageRoot = resolveRunningPackageRoot(__dirname);
489
- // Download the published tarball and prove its bytes match the registry
490
- // integrity BEFORE installing anything. A `name@version` spec would let the
491
- // package manager fetch and install whatever the registry serves with no
492
- // hash check on our side; instead we verify here and install the LOCAL, now
493
- // trusted .tgz. A mismatch throws and nothing below runs — fail closed.
494
- const tarball = await downloadVerifiedTarball(metadata.tarball, metadata.integrity);
495
- try {
496
- // Upgrade with the package manager that owns this install. A bun global
497
- // install lives at <bunGlobalDir>/node_modules/... (no `lib` segment), so an
498
- // `npm install --prefix` would write to <bunGlobalDir>/lib/node_modules and
499
- // never touch the running copy — npm exits 0, the verify below fails.
500
- if (detectPackageManager(packageRoot) === 'bun') {
501
- await installPackageWithBun(tarball);
502
- }
503
- else {
504
- await installPackageIntoPrefix(tarball, deriveGlobalPrefix(packageRoot));
505
- }
506
- }
507
- finally {
508
- // Best-effort cleanup of the verified tarball and its temp dir.
509
- try {
510
- fs.rmSync(path.dirname(tarball), { recursive: true, force: true });
511
- }
512
- catch {
513
- /* leave it for the OS temp sweep */
514
- }
515
- }
516
- verifyInstalledVersion(packageRoot, metadata.version);
517
- refreshAliasShims(packageRoot);
518
- // The npm install above runs with --ignore-scripts, so the postinstall that
519
- // installs the macOS Keychain helper never fires on upgrade. Force-refresh the
520
- // helper here so a user upgrading FROM a broken build (e.g. the entitlement-less
521
- // 1.20.4 helper that fails SecItemAdd with -34018) gets the fixed, signed bundle
522
- // immediately — instead of waiting for the lazy staleness check in
523
- // getKeychainHelperPath() to repair it on their next secret operation. The new
524
- // package is already on disk, so the dynamic import resolves the freshly-installed
525
- // helper module + bundle. Best-effort: an upgrade must never fail because the
526
- // helper could not be reinstalled (`agents helper install --force` stays available).
527
- if (process.platform === 'darwin') {
528
- try {
529
- const { ensureKeychainHelperInstalled } = await import('./lib/secrets/install-helper.js');
530
- ensureKeychainHelperInstalled({ forceReinstall: true });
531
- }
532
- catch {
533
- // Non-fatal.
534
- }
535
- }
536
- }
537
- /** Present an interactive upgrade prompt (TTY) or a one-line hint (non-TTY). */
538
- async function promptUpgrade(latestVersion) {
539
- const { default: ora } = await import('ora');
540
- const { confirm, select } = await import('@inquirer/prompts');
541
- const { isInteractiveTerminal, isPromptCancelled } = await import('./commands/utils.js');
542
- if (!isInteractiveTerminal()) {
543
- console.error(chalk.yellow(`Update available: ${VERSION} -> ${latestVersion}. Run: agents upgrade --yes`));
544
- return;
545
- }
546
- const answer = await select({
547
- message: `Update available: ${VERSION} -> ${latestVersion}`,
548
- choices: [
549
- { value: 'now', name: 'Upgrade now' },
550
- { value: 'later', name: 'Later' },
551
- { value: 'dismiss', name: `Skip ${latestVersion}` },
552
- ],
553
- });
554
- if (answer === 'dismiss') {
555
- dismissUpdateVersion(UPDATE_CHECK_FILE, latestVersion);
556
- return;
557
- }
558
- if (answer === 'now') {
559
- const { spawnSync } = await import('child_process');
560
- let spinner = ora('Resolving package metadata...').start();
561
- try {
562
- const metadata = await fetchNpmPackageMetadata();
563
- // The prompt showed the cached latest, which can lag the registry (the
564
- // 24h window) — sync the cache to what was actually resolved so later
565
- // prompts and the install agree on the same version.
566
- saveUpdateCheck(UPDATE_CHECK_FILE, metadata.version);
567
- spinner.succeed(`Resolved ${NPM_PACKAGE_NAME}@${metadata.version}`);
568
- printResolvedPackage(metadata);
569
- const approved = await confirm({
570
- message: `Install ${NPM_PACKAGE_NAME}@${metadata.version}?`,
571
- default: false,
572
- });
573
- if (!approved) {
574
- console.log(chalk.gray('Upgrade cancelled'));
575
- return;
576
- }
577
- spinner = ora('Upgrading...').start();
578
- await installResolvedPackage(metadata);
579
- spinner.succeed(`Upgraded to ${metadata.version}`);
580
- await showWhatsNew(VERSION, metadata.version);
581
- console.log();
582
- // Re-exec the verified install's entrypoint and exit. PATH lookup of
583
- // `agents` could resolve a different copy (dev build, another prefix)
584
- // than the one that was just upgraded. getCliLaunch resolves the JS-vs-
585
- // standalone shape — never hand-roll `[process.execPath, entrypoint]`,
586
- // which hands the bun virtual entry to a compiled binary as a bogus arg.
587
- const { command, args } = getCliLaunch(process.argv.slice(2));
588
- const result = spawnSync(command, args, {
589
- stdio: 'inherit',
590
- shell: false,
591
- });
592
- process.exit(result.status ?? 0);
593
- }
594
- catch (err) {
595
- if (isPromptCancelled(err))
596
- return;
597
- spinner.fail(`Upgrade failed: ${err instanceof Error ? err.message : String(err)}`);
598
- console.log(chalk.gray('Run manually: agents upgrade --yes'));
599
- }
600
- console.log();
601
- }
602
- }
603
- /**
604
- * Background update check — fires once per 24h cache window.
605
- * Network: GET registry.npmjs.org/@phnx-labs/agents-cli/latest.
606
- * Disable: set AGENTS_CLI_DISABLE_AUTO_UPDATE=1 in shell rc.
607
- *
608
- * Fire-and-forget; never blocks the CLI's foreground operation.
609
- */
610
- function refreshUpdateCacheInBackground() {
611
- fetch('https://registry.npmjs.org/@phnx-labs/agents-cli/latest', {
612
- signal: AbortSignal.timeout(2000),
613
- })
614
- .then((response) => (response.ok ? response.json() : null))
615
- .then((data) => {
616
- if (data && typeof data.version === 'string') {
617
- saveUpdateCheck(UPDATE_CHECK_FILE, data.version);
618
- }
619
- })
620
- .catch(() => {
621
- /* network error, try again next invocation */
622
- });
623
- }
624
- /** Check for available updates using the local cache. Triggers a background refresh if stale. */
625
- async function checkForUpdates() {
626
- if (process.env.AGENTS_CLI_DISABLE_AUTO_UPDATE)
627
- return;
628
- maybeWarnMultiInstall();
629
- const cache = readUpdateCache(UPDATE_CHECK_FILE);
630
- // Kick off network refresh in background if stale. Does not block.
631
- if (shouldFetchLatest(cache)) {
632
- refreshUpdateCacheInBackground();
633
- }
634
- // Prompt based on current cache (may be from a previous run's background refresh).
635
- // Skip if the user dismissed this exact version — they'll be prompted again when
636
- // a newer version appears.
637
- if (shouldPromptUpgrade(cache, VERSION)) {
74
+ const { runDaemon, log: daemonLog } = await import('./lib/daemon.js');
75
+ // RUSH-2418: the daemon is the one always-on process here, and it ran with no
76
+ // top-level handler of any kind — an uncaught throw or a rejected promise from
77
+ // any of its background ticks died on Node's default handler, printing a raw
78
+ // stack to whatever the service manager had wired to stdout and never reaching
79
+ // the daemon's own structured log. Route both into log() so the failure is in
80
+ // logs.jsonl where `agents daemon logs` reads it, then exit non-zero and
81
+ // DELIBERATELY let the supervisor restart us — now paced by the plist's
82
+ // ThrottleInterval / the unit's StartLimitBurst. Swallowing here would be the
83
+ // worse failure: a daemon left alive with a dead subsystem.
84
+ const crash = (kind) => (err) => {
85
+ const detail = err instanceof Error ? (err.stack ?? err.message) : String(err);
638
86
  try {
639
- await promptUpgrade(cache.latestVersion);
87
+ daemonLog('ERROR', `${kind}: ${detail}`);
640
88
  }
641
- catch (err) {
642
- const { isPromptCancelled } = await import('./commands/utils.js');
643
- if (isPromptCancelled(err))
644
- return;
645
- /* prompt error, ignore */
646
- }
647
- }
648
- }
649
- async function maybeBootstrapShimIntegration(requestedCommand, helpOrVersionRequested, verboseStartup) {
650
- if (!verboseStartup && (!process.stdin.isTTY || !process.stdout.isTTY)) {
651
- return;
652
- }
653
- // Pure documentation paths must never trigger interactive repair — mirrors
654
- // the helpOrVersionRequested gate around ensureInitialized below. Covers
655
- // both bare `agents --version` (requestedCommand === undefined) and
656
- // `agents <subcommand> --help` (requestedCommand === subcommand name).
657
- if (helpOrVersionRequested) {
658
- return;
659
- }
660
- if (requestedCommand === 'sync' || requestedCommand === 'refresh-rules') {
661
- return;
662
- }
663
- // Past the documentation/non-TTY guards: heal the shim/shadow/PATH conditions
664
- // through the unified self-heal registry — the SAME checks the daemon runs, but
665
- // driven silently on this interactive invocation so a user who never starts the
666
- // daemon still gets healed. Regenerating stale shims, adopting symlink launchers,
667
- // and adding the shims dir to PATH now happen without any output. The only thing
668
- // that ever prints is a ONE-TIME notice for what a machine can't silently fix
669
- // (a real native binary shadowing the shim) or is worth saying once (a PATH entry
670
- // just added). Suppression is persistent and keyed to the condition — a new
671
- // terminal no longer re-nags (the old per-PPID sentinel did, every shell).
672
- const { runInteractiveShimHeal } = await import('./lib/shim-heal.js');
673
- const { summarizeSelfHeal } = await import('./lib/self-heal/registry.js');
674
- const { noticeLines, report } = await runInteractiveShimHeal();
675
- if (verboseStartup) {
676
- process.stderr.write(`[agents] startup self-heal: ${summarizeSelfHeal(report)}\n`);
677
- }
678
- if (noticeLines) {
679
- for (const line of noticeLines)
680
- console.log(chalk.gray(line));
681
- }
682
- }
683
- // --- Inline command registrars ----------------------------------------------
684
- // These commands are defined here rather than in a command module because they
685
- // close over entry-point-local state (program re-parsing, VERSION, the npm
686
- // upgrade helpers). The lazy registrar and the all-commands fallback below both
687
- // call them, so the behavior is identical to the old eager registration.
688
- // memory is a first-class resource command (see commands/memory.ts via
689
- // COMMAND_LOADERS). The old memory→rules tombstone was removed in RUSH-1330.
690
- /** Deprecated `perms` alias — re-parses as `permissions`. */
691
- function registerPermsAliasCommand(p) {
692
- p.command('perms', { hidden: true })
693
- .allowUnknownOption()
694
- .allowExcessArguments()
695
- .action(async () => {
696
- console.log(chalk.yellow('Deprecated: Use "agents permissions" instead of "agents perms"\n'));
697
- // Re-parse with 'permissions' command
698
- const args = process.argv.slice(2);
699
- args[0] = 'permissions';
700
- await program.parseAsync(['node', 'agents', ...args]);
701
- });
702
- }
703
- /** Deprecated `exec` alias — re-parses as `run`. */
704
- function registerExecAliasCommand(p) {
705
- p.command('exec', { hidden: true })
706
- .allowUnknownOption()
707
- .allowExcessArguments()
708
- .action(async () => {
709
- console.log(chalk.yellow('Deprecated: Use "agents run" instead of "agents exec"\n'));
710
- const args = process.argv.slice(2);
711
- args[0] = 'run';
712
- await program.parseAsync(['node', 'agents', ...args]);
713
- });
714
- }
715
- /** Deprecated `jobs` / `cron` aliases — re-parse as `routines`. */
716
- function registerJobsCronAliasCommand(p, alias) {
717
- p.command(alias, { hidden: true })
718
- .allowUnknownOption()
719
- .allowExcessArguments()
720
- .action(async () => {
721
- console.log(chalk.yellow(`Deprecated: Use "agents routines" instead of "agents ${alias}"\n`));
722
- const args = process.argv.slice(2);
723
- args[0] = 'routines';
724
- await program.parseAsync(['node', 'agents', ...args]);
725
- });
726
- }
727
- /**
728
- * Removed `check` command (RUSH-1234) — re-parses as `doctor --check`, forwarding
729
- * any remaining flags so `check --quiet` / `check --json` / `check --devices` keep
730
- * working and the drift-gate exit code survives the rename. The notice goes to
731
- * stderr so `--json` stdout stays clean for CI parsers.
732
- */
733
- function registerCheckTombstoneCommand(p) {
734
- p.command('check', { hidden: true })
735
- .allowUnknownOption()
736
- .allowExcessArguments()
737
- .action(async () => {
738
- console.error(chalk.yellow('Deprecated: "agents check" is now "agents doctor --check". Running that for you.\n'));
739
- const args = process.argv.slice(2);
740
- args[0] = 'doctor';
741
- args.splice(1, 0, '--check');
742
- await program.parseAsync(['node', 'agents', ...args]);
743
- });
744
- }
745
- /**
746
- * Removed `resources` command (RUSH-1234) — re-parses as `view --merged` (the
747
- * cross-layer, first-wins resource table now lives there; `agents inspect <target>`
748
- * covers per-agent / per-repo detail). Forwards remaining flags like `--json`.
749
- */
750
- function registerResourcesTombstoneCommand(p) {
751
- p.command('resources', { hidden: true })
752
- .allowUnknownOption()
753
- .allowExcessArguments()
754
- .action(async () => {
755
- console.error(chalk.yellow('Deprecated: "agents resources" is now "agents view --merged" (use "agents inspect <target>" for per-agent/per-repo detail). Running that for you.\n'));
756
- const args = process.argv.slice(2);
757
- args[0] = 'view';
758
- args.splice(1, 0, '--merged');
759
- await program.parseAsync(['node', 'agents', ...args]);
760
- });
761
- }
762
- /**
763
- * Removed `hq` command — the JSON bridge for the interactive Agents HQ floor
764
- * (`agents hq floor --json`). No UI ever consumed it (apps/factory has zero
765
- * references) and it had no external users, so it is gone with no replacement.
766
- * Kept as a hidden tombstone so a stale invocation gets a clear message and a
767
- * non-zero exit instead of commander's raw "unknown command".
768
- */
769
- function registerHqTombstoneCommand(p) {
770
- p.command('hq', { hidden: true })
771
- .allowUnknownOption()
772
- .allowExcessArguments()
773
- .action(() => {
774
- die('"agents hq" was removed (internal Agents HQ floor bridge, no longer used).');
775
- });
776
- }
777
- /**
778
- * Hidden `agents _internal <sub>` namespace for machine-to-machine calls that
779
- * are not user-facing. The first subcommand is `friction`, used by shell guard
780
- * hooks (git-guard, rm-guard, …) to self-report a block into the event log
781
- * before they exit 2 — they run before any `agents` process exists, so they
782
- * cannot emit in-process.
783
- */
784
- function registerInternalCommand(p) {
785
- const internal = p.command('_internal', { hidden: true });
786
- internal
787
- .command('friction')
788
- .option('--surface <surface>', 'Subsystem that hit the failure (e.g. guard, teams)')
789
- .option('--id <failureId>', 'Stable failure slug (e.g. git.reset-hard)')
790
- .option('--error <message>', 'Human-readable failure reason')
791
- .option('--command <command>', 'The command that was blocked')
792
- .action((opts) => {
793
- if (!opts.surface || !opts.id) {
794
- process.exit(0); // fail-open: never break the caller
795
- }
796
- emitFriction(opts.surface, opts.id, {
797
- ...(opts.error ? { error: opts.error } : {}),
798
- ...(opts.command ? { command: opts.command } : {}),
799
- });
800
- process.exit(0);
801
- });
802
- }
803
- /** Runtime action for the shared `agents upgrade [version]` command definition. */
804
- async function runUpgrade(version, options) {
805
- const { default: ora } = await import('ora');
806
- const { confirm } = await import('@inquirer/prompts');
807
- const { isInteractiveTerminal, isPromptCancelled } = await import('./commands/utils.js');
808
- const target = version ?? 'latest';
809
- let spinner = ora(version ? `Resolving ${NPM_PACKAGE_NAME}@${target}...` : 'Checking for updates...').start();
810
- try {
811
- const metadata = await fetchNpmPackageMetadata(target);
812
- const resolvedVersion = metadata.version;
813
- if (resolvedVersion === VERSION) {
814
- spinner.succeed(`Already on ${VERSION}`);
815
- return;
816
- }
817
- // For `latest` (no explicit version) skip when already ahead. When a
818
- // version is named explicitly, honor it even if it's a downgrade.
819
- if (!version && compareVersions(resolvedVersion, VERSION) <= 0) {
820
- spinner.succeed(`Already ahead of latest (${VERSION} >= ${resolvedVersion})`);
821
- return;
822
- }
823
- const direction = compareVersions(resolvedVersion, VERSION) < 0 ? 'Downgrade' : 'Upgrade';
824
- spinner.succeed(`Resolved ${NPM_PACKAGE_NAME}@${resolvedVersion}`);
825
- printResolvedPackage(metadata);
826
- if (isInteractiveTerminal() && !options.yes) {
827
- const approved = await confirm({
828
- message: `Install ${NPM_PACKAGE_NAME}@${resolvedVersion}?`,
829
- default: false,
830
- });
831
- if (!approved) {
832
- console.log(chalk.gray('Upgrade cancelled'));
833
- return;
834
- }
835
- }
836
- spinner = ora(`${direction === 'Downgrade' ? 'Downgrading' : 'Upgrading'} ${VERSION} -> ${resolvedVersion}...`).start();
837
- await installResolvedPackage(metadata);
838
- spinner.succeed(`${direction}d to ${resolvedVersion}`);
839
- // Only show the changelog for a genuine upgrade range.
840
- if (compareVersions(resolvedVersion, VERSION) > 0) {
841
- await showWhatsNew(VERSION, resolvedVersion);
842
- }
843
- }
844
- catch (err) {
845
- if (isPromptCancelled(err))
846
- return;
847
- spinner.fail(`Upgrade failed: ${err instanceof Error ? err.message : String(err)}`);
848
- console.log(chalk.gray(`Run manually: agents upgrade ${version ? version + ' ' : ''}--yes`));
849
- }
850
- }
851
- function registerUpgradeRuntimeCommand(p) {
852
- registerUpgradeCommand(p, runUpgrade);
853
- }
854
- // --- Lazy registration orchestration -----------------------------------------
855
- /** Import a command module via its loader and register it on the program. */
856
- async function reg(loader) {
857
- (await loader())(program);
858
- }
859
- /**
860
- * Register exactly the command(s) the requested top-level name needs.
861
- * Returns false when the name maps to no known command (typo / unknown) so the
862
- * caller can fall back to registering everything for spellcheck.
863
- *
864
- * Lazy commands (sessions/teams/cloud) are intentionally NOT handled here — they
865
- * must register after applyGlobalHelpConventions to match main's ordering.
866
- */
867
- async function registerEagerForRequest(name) {
868
- switch (name) {
869
- case 'perms':
870
- // The action re-parses as `permissions`, so that target must exist too.
871
- registerPermsAliasCommand(program);
872
- await reg(loadPermissions);
873
- return true;
874
- case 'exec':
875
- registerExecAliasCommand(program);
876
- await reg(loadRun);
877
- return true;
878
- case 'jobs':
879
- case 'cron':
880
- registerJobsCronAliasCommand(program, name);
881
- await reg(loadRoutines);
882
- return true;
883
- case 'check':
884
- // The action re-parses as `doctor --check`, so doctor must exist too.
885
- registerCheckTombstoneCommand(program);
886
- await reg(loadDoctor);
887
- return true;
888
- case 'resources':
889
- // The action re-parses as `view --merged`, so view must exist too.
890
- registerResourcesTombstoneCommand(program);
891
- await reg(loadView);
892
- return true;
893
- case 'hq':
894
- registerHqTombstoneCommand(program);
895
- return true;
896
- case '_internal':
897
- registerInternalCommand(program);
898
- return true;
899
- case 'upgrade':
900
- registerUpgradeRuntimeCommand(program);
901
- return true;
902
- }
903
- const loaders = COMMAND_LOADERS[name];
904
- if (!loaders)
905
- return false;
906
- for (const loader of loaders)
907
- await reg(loader);
908
- return true;
909
- }
910
- /**
911
- * Register every command in the EXACT order main does (old src/index.ts lines
912
- * 691-844), including the inline deprecated aliases. Used only on the slow paths
913
- * (unknown command spellcheck, "did you mean" auto-correct) where the full set
914
- * of names — and their registration order, which breaks ties in the suggestion
915
- * picker — must match main byte-for-byte.
916
- */
917
- async function registerAllEagerCommands() {
918
- await reg(loadView);
919
- registerResourcesTombstoneCommand(program);
920
- await reg(loadShare);
921
- await reg(loadSend);
922
- await reg(loadInspect);
923
- await reg(loadFeedback);
924
- await reg(loadCommands);
925
- await reg(loadHooks);
926
- await reg(loadSkills);
927
- await reg(loadRules);
928
- await reg(loadMemory);
929
- await reg(loadPermissions);
930
- registerPermsAliasCommand(program);
931
- await reg(loadMcp);
932
- await reg(loadCli);
933
- await reg(loadSubagents);
934
- await reg(loadPlugins);
935
- await reg(loadWorkflows);
936
- await reg(loadWorktree);
937
- await reg(loadVersions);
938
- await reg(loadUpdate);
939
- await reg(loadImport);
940
- await reg(loadExport);
941
- await reg(loadPackages);
942
- await reg(loadRoutines);
943
- await reg(loadDaemon);
944
- await reg(loadMonitors);
945
- await reg(loadProjects);
946
- await reg(loadRun);
947
- await reg(loadFork);
948
- await reg(loadDefaults);
949
- await reg(loadSet);
950
- await reg(loadModels);
951
- await reg(loadModes);
952
- await reg(loadPrune);
953
- await reg(loadTrash);
954
- await reg(loadRestore);
955
- await reg(loadDoctor);
956
- registerCheckTombstoneCommand(program);
957
- await reg(loadApply);
958
- await reg(loadStatus);
959
- await reg(loadSnapshot);
960
- registerExecAliasCommand(program);
961
- await reg(loadProfiles);
962
- await reg(loadHarness);
963
- await reg(loadSecrets);
964
- await reg(loadLogin);
965
- await reg(loadWallet);
966
- await reg(loadHelper);
967
- await reg(loadMenubar);
968
- await reg(loadBeta);
969
- await reg(loadSync);
970
- await reg(loadLock);
971
- await reg(loadRefreshRules);
972
- await reg(loadFactory);
973
- await reg(loadUsage);
974
- await reg(loadCost);
975
- await reg(loadInsights);
976
- await reg(loadPerf);
977
- await reg(loadBench);
978
- await reg(loadTrends);
979
- await reg(loadOutput);
980
- await reg(loadBudget);
981
- await reg(loadAlias);
982
- await reg(loadMine);
983
- await reg(loadPty);
984
- await reg(loadTmux);
985
- await reg(loadWatchdog);
986
- await reg(loadBrowser);
987
- await reg(loadComputer);
988
- await reg(loadHosts);
989
- await reg(loadLogs);
990
- await reg(loadEvents);
991
- await reg(loadAudit);
992
- await reg(loadWebhook);
993
- await reg(loadFunnel);
994
- await reg(loadHumans);
995
- await reg(loadAccounts);
996
- registerHqTombstoneCommand(program);
997
- await reg(loadFeed);
998
- await reg(loadMailboxes);
999
- await reg(loadSsh);
1000
- registerJobsCronAliasCommand(program, 'jobs');
1001
- registerJobsCronAliasCommand(program, 'cron');
1002
- registerInternalCommand(program);
1003
- registerUpgradeRuntimeCommand(program);
1004
- await reg(loadPull);
1005
- await reg(loadPush);
1006
- await reg(loadRepo);
1007
- await reg(loadSetup);
1008
- await reg(loadUninstall);
1009
- }
1010
- /** Calculate the Levenshtein edit distance between two strings. */
1011
- function levenshtein(a, b) {
1012
- const m = a.length;
1013
- const n = b.length;
1014
- const dp = Array(m + 1)
1015
- .fill(null)
1016
- .map(() => Array(n + 1).fill(0));
1017
- for (let i = 0; i <= m; i++)
1018
- dp[i][0] = i;
1019
- for (let j = 0; j <= n; j++)
1020
- dp[0][j] = j;
1021
- for (let i = 1; i <= m; i++) {
1022
- for (let j = 1; j <= n; j++) {
1023
- dp[i][j] =
1024
- a[i - 1] === b[j - 1]
1025
- ? dp[i - 1][j - 1]
1026
- : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
1027
- }
1028
- }
1029
- return dp[m][n];
1030
- }
1031
- // Auto-correct typos with edit distance 1
1032
- program.on('command:*', (operands) => {
1033
- const unknown = operands[0];
1034
- const allCommands = program.commands.map((c) => c.name());
1035
- let closest = null;
1036
- let minDist = Infinity;
1037
- for (const cmd of allCommands) {
1038
- const dist = levenshtein(unknown, cmd);
1039
- if (dist < minDist) {
1040
- minDist = dist;
1041
- closest = cmd;
1042
- }
1043
- }
1044
- if (minDist === 1 && closest) {
1045
- const args = process.argv.slice(2);
1046
- args[0] = closest;
1047
- // The typo'd name was unknown, so the top-level --host router (which ran
1048
- // before commander parsing, against the ORIGINAL name) could not have
1049
- // routed it - it correctly fell through to reach this handler at all
1050
- // (that fallthrough is this ticket's own fix). But falling through to a
1051
- // plain local re-parse means a routing flag on a corrected REAL
1052
- // host-routable command (e.g. `docto --host box`, corrected to `doctor`)
1053
- // silently ran LOCALLY instead of remotely, with no error - worse than
1054
- // the loud "does not support --host" this ticket replaced. Re-run the
1055
- // router with the CORRECTED name before falling through to local parse;
1056
- // it already no-ops when no routing flag is present. RUSH-2022 review r2.
1057
- void (async () => {
1058
- const { maybeRunOnHost } = await import('./lib/hosts/passthrough.js');
1059
- if (await maybeRunOnHost(closest, args)) {
1060
- process.exit(process.exitCode ?? 0);
1061
- }
1062
- program.parse(['node', 'agents', ...args]);
1063
- })();
1064
- return;
1065
- }
1066
- console.error(`error: unknown command '${unknown}'`);
1067
- if (closest && minDist <= 3) {
1068
- console.error(`(Did you mean ${closest}?)`);
1069
- }
1070
- process.exit(1);
1071
- });
1072
- // Parse the invocation shape up front: the first non-flag token is the command,
1073
- // and the doc flags (--version/--help/-h) drive both the registration strategy
1074
- // and whether the update check + background sync run at all.
1075
- const passedArgs = process.argv.slice(2);
1076
- // Commander owns `--version` on the root command and otherwise intercepts it
1077
- // even after `sessions`, before the subcommand can parse its version filter.
1078
- // Rewrite only that value-taking nested form; bare `agents --version` and every
1079
- // other command retain the root documentation flag unchanged.
1080
- if (passedArgs[0] === 'sessions') {
1081
- const nestedVersionIndex = passedArgs.indexOf('--version', 1);
1082
- if (nestedVersionIndex >= 0) {
1083
- const nestedVersion = passedArgs[nestedVersionIndex + 1];
1084
- if (!nestedVersion || nestedVersion.startsWith('-')) {
1085
- console.error("error: option '--version <version>' argument missing");
1086
- process.exit(1);
1087
- }
1088
- passedArgs[nestedVersionIndex] = '--session-version';
1089
- process.argv[nestedVersionIndex + 2] = '--session-version';
1090
- }
1091
- }
1092
- const requestedCommand = passedArgs.find((arg) => !arg.startsWith('-'));
1093
- const verboseStartup = passedArgs.includes('--verbose');
1094
- // Help and version output are pure documentation — they must never gate on
1095
- // setup, otherwise `agents <cmd> --help` becomes useless on a fresh box.
1096
- const helpOrVersionRequested = passedArgs.some((arg) => arg === '--help' || arg === '-h' || arg === '--version' || arg === '-V');
1097
- // White-label: a brand can hide built-in top-level commands. A hidden command
1098
- // must behave as if it doesn't exist under this brand (unknown-command +
1099
- // spellcheck), while `agents` itself is unaffected. `brandDisabled` is empty for
1100
- // the unbranded CLI, so all of this is a no-op there.
1101
- const brandDisabled = disabledCommandsForActiveBrand();
1102
- const requestedIsDisabled = requestedCommand !== undefined && brandDisabled.has(requestedCommand);
1103
- // `--host` passthrough: run this invocation on a remote machine over SSH instead
1104
- // of locally. Handled before any local command registration / update check /
1105
- // background sync — a remote run needs none of that. Only the allowlisted
1106
- // read-only + config + teams commands route here; `run`/`sessions` are absent
1107
- // from the table and fall through to their own richer `--host` handling below.
1108
- // `--help`/`--version` stay local (docs must work without a reachable host).
1109
- if (requestedCommand !== undefined && !helpOrVersionRequested && !requestedIsDisabled) {
1110
- const { maybeRunOnHost } = await import('./lib/hosts/passthrough.js');
1111
- if (await maybeRunOnHost(requestedCommand, passedArgs)) {
1112
- process.exit(process.exitCode ?? 0);
1113
- }
1114
- }
1115
- // Register only the command(s) this invocation actually uses. Lazy commands
1116
- // (sessions/teams/cloud) are handled after applyGlobalHelpConventions below.
1117
- const isLazyRequest = requestedCommand !== undefined && LAZY_COMMAND_NAMES.has(requestedCommand);
1118
- // Set when the requested name maps to no command: the lazy tree is then also
1119
- // registered below so the spellcheck can suggest `sessions`/`teams`/`cloud`.
1120
- let requestedIsUnknown = false;
1121
- if (requestedIsDisabled) {
1122
- // The brand turned this command off: register the full tree so the "did you
1123
- // mean" picker still works, then strip the disabled commands below so the
1124
- // request resolves as unknown.
1125
- await registerAllEagerCommands();
1126
- }
1127
- else if (requestedCommand !== undefined && !isLazyRequest) {
1128
- const known = await registerEagerForRequest(requestedCommand);
1129
- if (!known) {
1130
- // Unknown top-level command: register the full tree so the "did you mean"
1131
- // spellcheck and edit-distance-1 auto-correct (the command:* handler above)
1132
- // see the same candidate set — and ordering — as main.
1133
- await registerAllEagerCommands();
1134
- requestedIsUnknown = true;
1135
- }
1136
- }
1137
- // When requestedCommand is undefined (bare invocation, --version, --help, -h) no
1138
- // command modules are needed: --version is built in and the root help text is a
1139
- // static string.
1140
- // Mirror main: help conventions are applied after the eager command tree and
1141
- // before the lazy commands, so the latter inherit the root's custom help
1142
- // formatter instead of getting the per-command recursive pass.
1143
- applyGlobalHelpConventions(program);
1144
- // Lazy commands pull in the SQLite-backed session/cloud stack; register them
1145
- // only when explicitly requested, keeping lightweight commands off that path.
1146
- if (isLazyRequest && !requestedIsDisabled) {
1147
- for (const loader of COMMAND_LOADERS[requestedCommand])
1148
- await reg(loader);
1149
- }
1150
- else if (requestedIsUnknown) {
1151
- // Unknown command: the lazy names must be candidates too, or `agents session`
1152
- // (a typo for the very much lazy `sessions`) gets no "did you mean" at all —
1153
- // the miss the RUSH-2022 report walked into. Registration order still mirrors
1154
- // main: lazy commands come after applyGlobalHelpConventions.
1155
- const seen = new Set();
1156
- for (const name of LAZY_COMMAND_NAMES) {
1157
- for (const loader of COMMAND_LOADERS[name] ?? []) {
1158
- if (seen.has(loader))
1159
- continue;
1160
- seen.add(loader);
1161
- await reg(loader);
1162
- }
1163
- }
1164
- }
1165
- // White-label: remove any commands this brand disabled so they resolve as
1166
- // unknown (the command:* handler then reports "unknown command"). Unbranded or
1167
- // nothing-disabled → no-op. Done after all registration paths above.
1168
- if (brandDisabled.size > 0) {
1169
- const kept = program.commands.filter((c) => !brandDisabled.has(c.name()));
1170
- if (kept.length !== program.commands.length) {
1171
- program.commands = kept;
1172
- }
1173
- }
1174
- // Pure documentation paths (--version / --help / -h) return immediately: skip
1175
- // the update check (PATH scan + cache read) and the detached background sync
1176
- // (spawns a child process) that every other invocation runs.
1177
- if (!helpOrVersionRequested) {
1178
- // Run update check before parsing so the upgrade notice/prompt precedes output.
1179
- await checkForUpdates();
1180
- // Fire-and-forget the background sync. System repo gets a real fast-forward
1181
- // pull (read-only locally, safe). User repo and extras get fetch-only + a
1182
- // status marker that `agents doctor` surfaces as a repo-behind warning.
1183
- const { spawnDetachedSync } = await import('./lib/auto-pull.js');
1184
- spawnDetachedSync();
1185
- }
1186
- // First-run experience: no args + no config yet + TTY -> launch interactive setup.
1187
- // Skipped when stdin/stdout isn't a terminal (CI, pipes) or when user passes any args.
1188
- const metaFilePath = path.join(getUserAgentsDir(), 'agents.yaml');
1189
- const firstRun = passedArgs.length === 0 &&
1190
- !fs.existsSync(metaFilePath) &&
1191
- process.stdin.isTTY &&
1192
- process.stdout.isTTY;
1193
- if (firstRun) {
89
+ catch { /* log path unwritable — stderr below still carries it */ }
90
+ process.stderr.write(`[agents] daemon ${kind}: ${detail}\n`);
91
+ process.exit(1);
92
+ };
93
+ process.on('uncaughtException', crash('uncaughtException'));
94
+ process.on('unhandledRejection', crash('unhandledRejection'));
1194
95
  try {
1195
- const { runSetup } = await import('./commands/setup.js');
1196
- await runSetup(program);
96
+ await runDaemon();
1197
97
  }
1198
98
  catch (err) {
1199
- if (!(err instanceof Error && err.name === 'ExitPromptError')) {
1200
- throw err;
1201
- }
99
+ crash('startup failure')(err);
1202
100
  }
1203
- process.exit(0);
1204
- }
1205
- // Every command requires the system repo to be cloned first. `setup` is the
1206
- // command that does the cloning; `uninstall` is its reverse and must run even
1207
- // from a broken/half-setup state (that is exactly when you want to tear down).
1208
- const SETUP_EXEMPT_COMMANDS = new Set(['setup', 'help', 'uninstall']);
1209
- // Fold legacy ~/.agents-system/ into ~/.agents/.system/ BEFORE ensureInitialized
1210
- // runs. ensureInitialized checks for .git inside the new path; if the user is
1211
- // upgrading from a layout where .git lives under the legacy path, the check
1212
- // would fail and exit before the migrator ever runs. Also runs outside the
1213
- // sentinel guard below because the sentinel was set by pre-fold releases and
1214
- // would otherwise skip this step on every existing install. Idempotent —
1215
- // no-ops when legacy is missing or already a symlink.
1216
- if (process.env.AGENTS_SKIP_MIGRATION !== '1') {
1217
- try {
1218
- const { foldLegacySystemRepo } = await import('./lib/migrate.js');
1219
- foldLegacySystemRepo();
1220
- }
1221
- catch { /* must never block CLI startup */ }
1222
- }
1223
- if (!firstRun &&
1224
- requestedCommand &&
1225
- !SETUP_EXEMPT_COMMANDS.has(requestedCommand) &&
1226
- !helpOrVersionRequested) {
1227
- const { ensureInitialized } = await import('./commands/setup.js');
1228
- await ensureInitialized(program);
1229
- }
1230
- // One-shot idempotent migrations (split-layout, legacy file moves).
1231
- // Each step is internally guarded by existence checks so it's safe to run
1232
- // every invocation. A sentinel file in the system dir short-circuits the
1233
- // scan once a migration version has run, so the hot path stays cheap.
1234
- // AGENTS_SKIP_MIGRATION=1 disables the bootstrap-time run for tests and
1235
- // scripted invocations that prepare their own legacy fixtures.
1236
- if (process.env.AGENTS_SKIP_MIGRATION !== '1') {
1237
- try {
1238
- const { runMigration } = await import('./lib/migrate.js');
1239
- const sentinel = getMigratedSentinelPath();
1240
- // Sentinel is keyed to the migration SCHEMA version, not the binary version.
1241
- // Bumping the suffix re-runs migrations for every user; binary releases that
1242
- // don't change the schema must NOT re-run (they would destroy user content
1243
- // when migration steps overlap with user-authored paths). See issue #20.
1244
- const sentinelValue = 'v18';
1245
- let needRun = true;
1246
- try {
1247
- if (fs.existsSync(sentinel) && fs.readFileSync(sentinel, 'utf-8').trim() === sentinelValue) {
1248
- needRun = false;
1249
- }
1250
- }
1251
- catch { /* best-effort — fall through to run */ }
1252
- if (needRun) {
1253
- await runMigration();
1254
- try {
1255
- fs.mkdirSync(path.dirname(sentinel), { recursive: true });
1256
- fs.writeFileSync(sentinel, sentinelValue);
1257
- }
1258
- catch { /* best-effort */ }
1259
- }
1260
- }
1261
- catch { /* migration must never block CLI startup */ }
1262
- }
1263
- // Auto-enable the macOS menu-bar helper once, for every user. Best-effort and
1264
- // idempotent: installMenubarLaunchAgentOnUpgrade() no-ops when not on darwin,
1265
- // when the user ran `agents menubar disable` (sticky opt-out), when the service
1266
- // is already installed, or when no helper bundle ships with this build. This is
1267
- // a lightweight startup self-heal (two existsSync checks then return) rather
1268
- // than a migration-sentinel bump, so it covers fresh installs AND upgrades
1269
- // without re-running the full migration for the whole user base (issue #20).
1270
- if (process.platform === 'darwin' && process.env.AGENTS_SKIP_MIGRATION !== '1') {
1271
- try {
1272
- const { installMenubarLaunchAgentOnUpgrade } = await import('./lib/menubar/install-menubar.js');
1273
- installMenubarLaunchAgentOnUpgrade();
1274
- }
1275
- catch { /* never block CLI startup on the menu bar */ }
1276
- }
1277
- // Bare invocation prints the root help. Commander only auto-displays help on
1278
- // an empty parse when subcommands are registered, and the lazy-startup path
1279
- // registers none for a bare call — without this branch, `agents` exits
1280
- // silently. Runs after first-run setup and migrations so those still fire;
1281
- // exits 0 to match `agents --help` (and the pre-fix exit code).
1282
- if (passedArgs.length === 0) {
1283
- program.outputHelp();
1284
- process.exit(0);
1285
- }
1286
- try {
1287
- await maybeBootstrapShimIntegration(requestedCommand, helpOrVersionRequested, verboseStartup);
1288
- await program.parseAsync();
1289
- }
1290
- catch (err) {
1291
- if (err instanceof Error && err.name === 'ExitPromptError') {
1292
- process.exit(130);
1293
- }
1294
- // Browser-daemon-not-running and CDP-not-reachable surface as typed errors
1295
- // from src/lib/browser/. Don't dump a Node stacktrace for these — they are
1296
- // user-actionable, not engineering bugs. See issues #41 and #43.
1297
- if (err instanceof Error) {
1298
- const isBrowserDaemonNotRunning = err.name === 'BrowserDaemonNotRunningError';
1299
- const isBrowserCdpUnreachable = err.name === 'BrowserCdpConnectionError';
1300
- const isBrowserIpcDown = err.message.startsWith('IPC error:') &&
1301
- (err.message.includes('ECONNREFUSED') || err.message.includes('ENOENT'));
1302
- if (isBrowserDaemonNotRunning || isBrowserCdpUnreachable || isBrowserIpcDown) {
1303
- console.error(err.message);
1304
- process.exit(1);
1305
- }
1306
- // A --host targeting a password-auth device throws this from resolveHost.
1307
- // It carries an actionable message (switch to key auth / enroll as a host);
1308
- // handling it here covers every resolveHost caller (run, hosts check/rm,
1309
- // secrets --host) at the source instead of a catch at each call site.
1310
- if (err.name === 'DeviceOffloadUnsupportedError') {
1311
- console.error(err.message);
1312
- process.exit(1);
1313
- }
1314
- }
1315
- throw err;
101
+ process.exit(process.exitCode ?? 0);
1316
102
  }
103
+ // Full CLI: commander tree, update checks, migrations, parse. Static imports
104
+ // inside bootstrap.js evaluate only when we reach this line.
105
+ await import('./bootstrap.js');