@phnx-labs/agents-cli 1.22.46 → 1.22.49

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 (348) hide show
  1. package/CHANGELOG.md +663 -0
  2. package/LICENSE +102 -182
  3. package/README.md +96 -49
  4. package/dist/bootstrap.js +4 -2
  5. package/dist/cli/command-registry.d.ts +2 -1
  6. package/dist/cli/command-registry.js +6 -2
  7. package/dist/commands/accounts.d.ts +58 -0
  8. package/dist/commands/accounts.js +243 -25
  9. package/dist/commands/artifacts.js +7 -6
  10. package/dist/commands/attach.js +1 -1
  11. package/dist/commands/auth.js +7 -5
  12. package/dist/commands/browser-sessions-picker.js +9 -7
  13. package/dist/commands/browser.js +711 -62
  14. package/dist/commands/config.d.ts +0 -1
  15. package/dist/commands/config.js +26 -24
  16. package/dist/commands/cost.d.ts +1 -1
  17. package/dist/commands/daemon.d.ts +21 -0
  18. package/dist/commands/daemon.js +147 -12
  19. package/dist/commands/detach.d.ts +13 -0
  20. package/dist/commands/detach.js +11 -8
  21. package/dist/commands/exec.js +107 -53
  22. package/dist/commands/feed-watch.d.ts +2 -0
  23. package/dist/commands/feed-watch.js +28 -0
  24. package/dist/commands/feed.js +27 -0
  25. package/dist/commands/feedback.js +3 -19
  26. package/dist/commands/focus.d.ts +14 -0
  27. package/dist/commands/focus.js +48 -5
  28. package/dist/commands/go.d.ts +3 -0
  29. package/dist/commands/go.js +16 -3
  30. package/dist/commands/insights.d.ts +1 -1
  31. package/dist/commands/insights.js +2 -2
  32. package/dist/commands/inspect.js +2 -2
  33. package/dist/commands/lease.d.ts +1 -1
  34. package/dist/commands/lease.js +7 -6
  35. package/dist/commands/mailboxes.js +4 -3
  36. package/dist/commands/menubar.js +11 -4
  37. package/dist/commands/projects.d.ts +16 -1
  38. package/dist/commands/projects.js +58 -64
  39. package/dist/commands/resume.js +1 -1
  40. package/dist/commands/route.js +4 -4
  41. package/dist/commands/routines.js +4 -3
  42. package/dist/commands/run-broadcast.d.ts +1 -1
  43. package/dist/commands/run-broadcast.js +1 -1
  44. package/dist/commands/sessions-picker-factory.d.ts +2 -1
  45. package/dist/commands/sessions-picker-factory.js +1 -1
  46. package/dist/commands/sessions-picker.d.ts +22 -0
  47. package/dist/commands/sessions-picker.js +48 -9
  48. package/dist/commands/sessions-stop.d.ts +24 -0
  49. package/dist/commands/sessions-stop.js +66 -0
  50. package/dist/commands/sessions-trace.d.ts +82 -0
  51. package/dist/commands/sessions-trace.js +387 -0
  52. package/dist/commands/sessions.js +5 -0
  53. package/dist/commands/setup-secrets.js +23 -0
  54. package/dist/commands/share.d.ts +20 -5
  55. package/dist/commands/share.js +152 -52
  56. package/dist/commands/ssh.d.ts +15 -0
  57. package/dist/commands/ssh.js +243 -18
  58. package/dist/commands/teams.js +34 -2
  59. package/dist/commands/traces.d.ts +2 -0
  60. package/dist/commands/traces.js +184 -0
  61. package/dist/commands/view.d.ts +2 -0
  62. package/dist/commands/view.js +32 -62
  63. package/dist/commands/watchdog.js +9 -14
  64. package/dist/index.d.ts +1 -0
  65. package/dist/index.js +5 -0
  66. package/dist/lib/account-capabilities.d.ts +4 -0
  67. package/dist/lib/account-capabilities.js +13 -9
  68. package/dist/lib/account-registry.d.ts +2 -0
  69. package/dist/lib/account-registry.js +30 -2
  70. package/dist/lib/accounting/rotate.d.ts +9 -0
  71. package/dist/lib/accounting/rotate.js +12 -2
  72. package/dist/lib/accounting/usage.d.ts +84 -6
  73. package/dist/lib/accounting/usage.js +293 -75
  74. package/dist/lib/agent-modes.js +29 -1
  75. package/dist/lib/agent-spec/agents.d.ts +4 -3
  76. package/dist/lib/agent-spec/agents.js +25 -15
  77. package/dist/lib/analytics/mix-commands.d.ts +1 -1
  78. package/dist/lib/analytics/mix-commands.js +2 -2
  79. package/dist/lib/auth-health.d.ts +13 -3
  80. package/dist/lib/auth-health.js +50 -8
  81. package/dist/lib/browser/devices.d.ts +19 -0
  82. package/dist/lib/browser/devices.js +23 -0
  83. package/dist/lib/browser/drivers/ssh.d.ts +16 -2
  84. package/dist/lib/browser/drivers/ssh.js +36 -9
  85. package/dist/lib/browser/ipc.d.ts +1 -1
  86. package/dist/lib/browser/ipc.js +40 -9
  87. package/dist/lib/browser/profiles.d.ts +104 -65
  88. package/dist/lib/browser/profiles.js +277 -165
  89. package/dist/lib/browser/registry.d.ts +39 -0
  90. package/dist/lib/browser/registry.js +149 -0
  91. package/dist/lib/browser/remote-control.d.ts +28 -2
  92. package/dist/lib/browser/remote-control.js +35 -3
  93. package/dist/lib/browser/resolve-target.d.ts +68 -0
  94. package/dist/lib/browser/resolve-target.js +250 -0
  95. package/dist/lib/browser/runtime-state.d.ts +44 -11
  96. package/dist/lib/browser/runtime-state.js +98 -15
  97. package/dist/lib/browser/service.d.ts +65 -9
  98. package/dist/lib/browser/service.js +196 -61
  99. package/dist/lib/browser/sessions-list.d.ts +1 -3
  100. package/dist/lib/browser/sessions-list.js +3 -16
  101. package/dist/lib/browser/task-index.d.ts +70 -0
  102. package/dist/lib/browser/task-index.js +180 -0
  103. package/dist/lib/browser/types.d.ts +12 -1
  104. package/dist/lib/byok-usage.js +2 -2
  105. package/dist/lib/claude-statusline.d.ts +29 -0
  106. package/dist/lib/claude-statusline.js +139 -0
  107. package/dist/lib/cloud/host.d.ts +1 -1
  108. package/dist/lib/cloud/host.js +1 -1
  109. package/dist/lib/cloud/rush.js +1 -1
  110. package/dist/lib/codex-policy.d.ts +11 -1
  111. package/dist/lib/codex-policy.js +20 -5
  112. package/dist/lib/computer/computer-rpc.js +2 -2
  113. package/dist/lib/computer/download.d.ts +15 -15
  114. package/dist/lib/computer/download.js +35 -98
  115. package/dist/lib/computer/ssh-tunnel.d.ts +0 -3
  116. package/dist/lib/computer/ssh-tunnel.js +12 -22
  117. package/dist/lib/config-keys.d.ts +6 -11
  118. package/dist/lib/config-keys.js +18 -19
  119. package/dist/lib/crabbox/cli.d.ts +1 -1
  120. package/dist/lib/crabbox/cli.js +2 -2
  121. package/dist/lib/crabbox/lease.d.ts +9 -0
  122. package/dist/lib/crabbox/lease.js +70 -15
  123. package/dist/lib/crabbox/runtimes.js +1 -1
  124. package/dist/lib/daemon/account-state-daemon-service.d.ts +16 -0
  125. package/dist/lib/daemon/account-state-daemon-service.js +26 -0
  126. package/dist/lib/daemon/browser-ipc-service.d.ts +20 -0
  127. package/dist/lib/daemon/browser-ipc-service.js +46 -0
  128. package/dist/lib/daemon/daemon.d.ts +3 -0
  129. package/dist/lib/daemon/daemon.js +212 -444
  130. package/dist/lib/daemon/device-probe-service.d.ts +26 -0
  131. package/dist/lib/daemon/device-probe-service.js +47 -0
  132. package/dist/lib/daemon/keychain-reap-service.d.ts +17 -0
  133. package/dist/lib/daemon/keychain-reap-service.js +32 -0
  134. package/dist/lib/daemon/monitor-engine-service.d.ts +22 -0
  135. package/dist/lib/daemon/monitor-engine-service.js +29 -0
  136. package/dist/lib/daemon/runner.js +15 -2
  137. package/dist/lib/daemon/secrets-broker-service.d.ts +20 -0
  138. package/dist/lib/daemon/secrets-broker-service.js +66 -0
  139. package/dist/lib/daemon/self-heal-service.d.ts +27 -0
  140. package/dist/lib/daemon/self-heal-service.js +48 -0
  141. package/dist/lib/daemon/service.d.ts +102 -0
  142. package/dist/lib/daemon/service.js +98 -0
  143. package/dist/lib/daemon/session-index-service.d.ts +22 -0
  144. package/dist/lib/daemon/session-index-service.js +40 -0
  145. package/dist/lib/daemon/state-dir-check-service.d.ts +43 -0
  146. package/dist/lib/daemon/state-dir-check-service.js +63 -0
  147. package/dist/lib/daemon/supervisor.d.ts +87 -0
  148. package/dist/lib/daemon/supervisor.js +298 -0
  149. package/dist/lib/daemon/watchdog-service.d.ts +19 -0
  150. package/dist/lib/daemon/watchdog-service.js +40 -0
  151. package/dist/lib/daemon-health.d.ts +15 -0
  152. package/dist/lib/daemon-health.js +29 -5
  153. package/dist/lib/daemon-services.d.ts +8 -1
  154. package/dist/lib/daemon-services.js +55 -0
  155. package/dist/lib/daemon-ticks.d.ts +13 -2
  156. package/dist/lib/daemon-ticks.js +22 -20
  157. package/dist/lib/device-config.d.ts +25 -10
  158. package/dist/lib/device-config.js +90 -39
  159. package/dist/lib/devices/config-migration.d.ts +7 -0
  160. package/dist/lib/devices/config-migration.js +67 -3
  161. package/dist/lib/devices/health.d.ts +27 -2
  162. package/dist/lib/devices/health.js +39 -9
  163. package/dist/lib/devices/interactive-host.d.ts +15 -0
  164. package/dist/lib/devices/interactive-host.js +53 -0
  165. package/dist/lib/devices/registry.d.ts +61 -4
  166. package/dist/lib/devices/registry.js +94 -38
  167. package/dist/lib/devices/stats-cache.d.ts +26 -0
  168. package/dist/lib/devices/stats-cache.js +59 -7
  169. package/dist/lib/devices/worker-pick.d.ts +48 -0
  170. package/dist/lib/devices/worker-pick.js +113 -0
  171. package/dist/lib/exec.d.ts +70 -14
  172. package/dist/lib/exec.js +110 -38
  173. package/dist/lib/feed/answer.d.ts +29 -0
  174. package/dist/lib/feed/answer.js +145 -0
  175. package/dist/lib/feed/attention.d.ts +83 -0
  176. package/dist/lib/feed/attention.js +0 -0
  177. package/dist/lib/feed/feed.d.ts +109 -0
  178. package/dist/lib/feed/feed.js +154 -3
  179. package/dist/lib/feed/pr-status.d.ts +14 -0
  180. package/dist/lib/feed/pr-status.js +47 -0
  181. package/dist/lib/feed/watch.d.ts +59 -0
  182. package/dist/lib/feed/watch.js +180 -0
  183. package/dist/lib/fleet/capture.js +6 -0
  184. package/dist/lib/fleet/remote-login.js +8 -3
  185. package/dist/lib/fleet/types.d.ts +17 -0
  186. package/dist/lib/harness/adapter.d.ts +11 -0
  187. package/dist/lib/harness/adapters/claude.js +38 -24
  188. package/dist/lib/harness/adapters/codex.js +2 -4
  189. package/dist/lib/harness/adapters/cursor.js +8 -11
  190. package/dist/lib/helper-download.d.ts +108 -0
  191. package/dist/lib/helper-download.js +194 -0
  192. package/dist/lib/helper-versions.d.ts +52 -0
  193. package/dist/lib/helper-versions.js +58 -0
  194. package/dist/lib/hooks/cache.js +19 -0
  195. package/dist/lib/hooks/install.js +2 -2
  196. package/dist/lib/hooks/match.d.ts +3 -0
  197. package/dist/lib/hooks/match.js +25 -0
  198. package/dist/lib/hosts/credentials.d.ts +1 -1
  199. package/dist/lib/hosts/credentials.js +1 -1
  200. package/dist/lib/hosts/dispatch.d.ts +7 -1
  201. package/dist/lib/hosts/dispatch.js +24 -5
  202. package/dist/lib/hosts/passthrough.d.ts +8 -15
  203. package/dist/lib/hosts/passthrough.js +35 -16
  204. package/dist/lib/hosts/providers/devices.d.ts +1 -1
  205. package/dist/lib/hosts/providers/devices.js +1 -1
  206. package/dist/lib/hosts/reconnect.d.ts +179 -21
  207. package/dist/lib/hosts/reconnect.js +266 -45
  208. package/dist/lib/hosts/registry.js +7 -0
  209. package/dist/lib/installations/migrate.d.ts +4 -5
  210. package/dist/lib/installations/migrate.js +6 -7
  211. package/dist/lib/installations/shims.d.ts +1 -1
  212. package/dist/lib/installations/shims.js +23 -13
  213. package/dist/lib/installations/versions.js +25 -2
  214. package/dist/lib/linear-autoclose.d.ts +1 -1
  215. package/dist/lib/linear-autoclose.js +1 -1
  216. package/dist/lib/loop.d.ts +2 -2
  217. package/dist/lib/loop.js +1 -1
  218. package/dist/lib/menubar/download-menubar.d.ts +47 -0
  219. package/dist/lib/menubar/download-menubar.js +60 -0
  220. package/dist/lib/menubar/install-menubar.d.ts +44 -7
  221. package/dist/lib/menubar/install-menubar.js +105 -23
  222. package/dist/lib/menubar/notify-desktop.d.ts +3 -3
  223. package/dist/lib/menubar/notify-desktop.js +6 -6
  224. package/dist/lib/model-tiers.d.ts +1 -1
  225. package/dist/lib/open-url.d.ts +60 -2
  226. package/dist/lib/open-url.js +167 -12
  227. package/dist/lib/project-probe.d.ts +1 -1
  228. package/dist/lib/project-probe.js +1 -1
  229. package/dist/lib/project-pull.d.ts +1 -1
  230. package/dist/lib/project-pull.js +2 -2
  231. package/dist/lib/projects.d.ts +1 -1
  232. package/dist/lib/projects.js +1 -1
  233. package/dist/lib/refresh-coordinator.d.ts +1 -1
  234. package/dist/lib/refresh-coordinator.js +1 -1
  235. package/dist/lib/sandbox.js +6 -7
  236. package/dist/lib/secrets/download-keychain.d.ts +47 -0
  237. package/dist/lib/secrets/download-keychain.js +70 -0
  238. package/dist/lib/secrets/install-helper.d.ts +20 -1
  239. package/dist/lib/secrets/install-helper.js +44 -3
  240. package/dist/lib/session/active.d.ts +67 -0
  241. package/dist/lib/session/active.js +86 -2
  242. package/dist/lib/session/db.d.ts +11 -1
  243. package/dist/lib/session/db.js +142 -8
  244. package/dist/lib/session/discover.d.ts +23 -1
  245. package/dist/lib/session/discover.js +44 -10
  246. package/dist/lib/session/highlights.d.ts +49 -1
  247. package/dist/lib/session/highlights.js +95 -0
  248. package/dist/lib/session/host-link.d.ts +16 -2
  249. package/dist/lib/session/host-link.js +7 -1
  250. package/dist/lib/session/parse.js +33 -1
  251. package/dist/lib/session/prompt.d.ts +35 -0
  252. package/dist/lib/session/prompt.js +56 -0
  253. package/dist/lib/session/remote/remote-list.d.ts +9 -0
  254. package/dist/lib/session/remote/remote-list.js +19 -2
  255. package/dist/lib/session/remote/watch.d.ts +1 -1
  256. package/dist/lib/session/remote/watch.js +17 -3
  257. package/dist/lib/session/render.js +7 -3
  258. package/dist/lib/session/session-cache.d.ts +53 -0
  259. package/dist/lib/session/session-cache.js +93 -1
  260. package/dist/lib/session/state.js +1 -1
  261. package/dist/lib/session/tool-calls.d.ts +2 -0
  262. package/dist/lib/session/tool-calls.js +1 -1
  263. package/dist/lib/session/trajectory-compare.d.ts +47 -0
  264. package/dist/lib/session/trajectory-compare.js +116 -0
  265. package/dist/lib/session/trajectory-html.d.ts +19 -0
  266. package/dist/lib/session/trajectory-html.js +770 -0
  267. package/dist/lib/session/trajectory-lineage.d.ts +92 -0
  268. package/dist/lib/session/trajectory-lineage.js +195 -0
  269. package/dist/lib/session/trajectory-text.d.ts +37 -0
  270. package/dist/lib/session/trajectory-text.js +284 -0
  271. package/dist/lib/session/trajectory.d.ts +102 -0
  272. package/dist/lib/session/trajectory.js +384 -0
  273. package/dist/lib/session/types.d.ts +15 -0
  274. package/dist/lib/sha256-asset.d.ts +26 -0
  275. package/dist/lib/sha256-asset.js +41 -0
  276. package/dist/lib/share/backend.d.ts +95 -0
  277. package/dist/lib/share/backend.js +139 -0
  278. package/dist/lib/share/delete.d.ts +6 -2
  279. package/dist/lib/share/delete.js +8 -9
  280. package/dist/lib/share/html.d.ts +22 -0
  281. package/dist/lib/share/html.js +88 -0
  282. package/dist/lib/share/provision.d.ts +18 -4
  283. package/dist/lib/share/provision.js +38 -4
  284. package/dist/lib/share/publish.d.ts +39 -15
  285. package/dist/lib/share/publish.js +55 -26
  286. package/dist/lib/share/worker-template.js +171 -11
  287. package/dist/lib/sqlite.js +16 -3
  288. package/dist/lib/ssh-exec.d.ts +65 -0
  289. package/dist/lib/ssh-exec.js +120 -4
  290. package/dist/lib/startup/command-registry.d.ts +3 -1
  291. package/dist/lib/startup/command-registry.js +6 -3
  292. package/dist/lib/startup/dev-build.js +3 -3
  293. package/dist/lib/state.d.ts +2 -2
  294. package/dist/lib/state.js +11 -5
  295. package/dist/lib/subagents-registry.js +1 -1
  296. package/dist/lib/teams/agents.js +9 -0
  297. package/dist/lib/terminal/index.d.ts +1 -1
  298. package/dist/lib/terminal/inject.d.ts +19 -0
  299. package/dist/lib/terminal/inject.js +19 -9
  300. package/dist/lib/terminal/types.d.ts +1 -1
  301. package/dist/lib/terminal/types.js +1 -1
  302. package/dist/lib/tmux/orphan-reap.js +1 -1
  303. package/dist/lib/tmux/session.d.ts +46 -13
  304. package/dist/lib/tmux/session.js +204 -27
  305. package/dist/lib/traces/backend.d.ts +29 -0
  306. package/dist/lib/traces/backend.js +46 -0
  307. package/dist/lib/traces/classify.d.ts +52 -0
  308. package/dist/lib/traces/classify.js +72 -0
  309. package/dist/lib/traces/config.d.ts +10 -0
  310. package/dist/lib/traces/config.js +2 -0
  311. package/dist/lib/traces/provision.d.ts +19 -0
  312. package/dist/lib/traces/provision.js +30 -0
  313. package/dist/lib/traces/sync.d.ts +150 -0
  314. package/dist/lib/traces/sync.js +447 -0
  315. package/dist/lib/traces/worker-template.d.ts +4 -0
  316. package/dist/lib/traces/worker-template.js +159 -0
  317. package/dist/lib/types.d.ts +34 -19
  318. package/dist/lib/types.js +14 -0
  319. package/dist/lib/usage-backoff.d.ts +2 -1
  320. package/dist/lib/usage-backoff.js +60 -19
  321. package/dist/lib/usage-refresh.d.ts +18 -4
  322. package/dist/lib/usage-refresh.js +68 -19
  323. package/dist/lib/watchdog/log.d.ts +1 -1
  324. package/dist/lib/watchdog/log.js +1 -1
  325. package/dist/lib/watchdog/runner.d.ts +36 -45
  326. package/dist/lib/watchdog/runner.js +150 -119
  327. package/dist/lib/watchdog/service.d.ts +0 -1
  328. package/dist/lib/watchdog/service.js +0 -1
  329. package/dist/lib/watchdog/watchdog-agent.d.ts +38 -0
  330. package/dist/lib/watchdog/watchdog-agent.js +59 -0
  331. package/dist/lib/watchdog/watchdog.d.ts +11 -2
  332. package/dist/lib/watchdog/watchdog.js +37 -104
  333. package/dist/lib/workflows.d.ts +1 -1
  334. package/package.json +5 -7
  335. package/dist/commands/usage.d.ts +0 -27
  336. package/dist/commands/usage.js +0 -96
  337. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  338. package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +0 -24
  339. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  340. package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
  341. package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +0 -128
  342. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  343. package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -24
  344. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  345. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +0 -123
  346. package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
  347. package/dist/lib/usage-fleet.d.ts +0 -32
  348. package/dist/lib/usage-fleet.js +0 -125
@@ -5,7 +5,6 @@
5
5
  * - run defaults (model, mode, effort)
6
6
  * - tier overrides (folded into the run namespace)
7
7
  * - interactive host
8
- * - usage primary host
9
8
  * - browser profile
10
9
  * - local projects root
11
10
  * - per-device config keys
@@ -5,7 +5,6 @@
5
5
  * - run defaults (model, mode, effort)
6
6
  * - tier overrides (folded into the run namespace)
7
7
  * - interactive host
8
- * - usage primary host
9
8
  * - browser profile
10
9
  * - local projects root
11
10
  * - per-device config keys
@@ -40,8 +39,6 @@ function parseValue(key, parsed, raw) {
40
39
  return raw.trim();
41
40
  case 'interactive':
42
41
  return raw.trim();
43
- case 'usage':
44
- return raw.trim();
45
42
  case 'auto':
46
43
  return raw.trim();
47
44
  case 'browser':
@@ -72,6 +69,7 @@ function parseValue(key, parsed, raw) {
72
69
  case 'notes':
73
70
  return raw.trim();
74
71
  case 'browser.profile':
72
+ case 'browser.viewer':
75
73
  return raw.trim();
76
74
  }
77
75
  // A device property with no arm above used to fall out of the switch and
@@ -108,10 +106,6 @@ function setConfig(parsed, value) {
108
106
  setConfigValue('interactive.host', value);
109
107
  return;
110
108
  }
111
- case 'usage': {
112
- setConfigValue('usage.primary-host', value);
113
- return;
114
- }
115
109
  case 'auto': {
116
110
  setConfigValue('auto.pool', value);
117
111
  return;
@@ -121,7 +115,7 @@ function setConfig(parsed, value) {
121
115
  // (same store `agents devices config` / getConfigValue use). Bare
122
116
  // browser.profile targets this machine; devices.<name>.browser.profile
123
117
  // targets a peer.
124
- setConfigValue('browser.profile', value, parsed.device ? { device: parsed.device } : undefined);
118
+ setConfigValue(parsed.property === 'viewer' ? 'browser.viewer' : 'browser.profile', value, parsed.device ? { device: parsed.device } : undefined);
125
119
  return;
126
120
  }
127
121
  case 'project':
@@ -155,11 +149,6 @@ function unsetConfig(parsed) {
155
149
  unsetConfigValue('interactive.host');
156
150
  return had;
157
151
  }
158
- case 'usage': {
159
- const had = getConfigValue('usage.primary-host').value !== undefined;
160
- unsetConfigValue('usage.primary-host');
161
- return had;
162
- }
163
152
  case 'auto': {
164
153
  const had = getConfigValue('auto.pool').value !== undefined;
165
154
  unsetConfigValue('auto.pool');
@@ -167,8 +156,12 @@ function unsetConfig(parsed) {
167
156
  }
168
157
  case 'browser': {
169
158
  const target = parsed.device ? { device: parsed.device } : undefined;
170
- const had = getConfigValue('browser.profile', target).value !== undefined;
171
- unsetConfigValue('browser.profile', target);
159
+ // Must follow parsed.property. Hardcoding 'browser.profile' here meant
160
+ // `config unset browser.viewer` deleted the user's browser.profile while
161
+ // printing success, and left browserViewer in place.
162
+ const name = parsed.property === 'viewer' ? 'browser.viewer' : 'browser.profile';
163
+ const had = getConfigValue(name, target).value !== undefined;
164
+ unsetConfigValue(name, target);
172
165
  return had;
173
166
  }
174
167
  case 'project': {
@@ -199,12 +192,10 @@ function getConfig(parsed) {
199
192
  }
200
193
  case 'interactive':
201
194
  return getConfigValue('interactive.host').value;
202
- case 'usage':
203
- return getConfigValue('usage.primary-host').value;
204
195
  case 'auto':
205
196
  return getConfigValue('auto.pool').value;
206
197
  case 'browser': {
207
- return getConfigValue('browser.profile', parsed.device ? { device: parsed.device } : undefined).value;
198
+ return getConfigValue(parsed.property === 'viewer' ? 'browser.viewer' : 'browser.profile', parsed.device ? { device: parsed.device } : undefined).value;
208
199
  }
209
200
  case 'project':
210
201
  return getProjectRoot();
@@ -258,9 +249,6 @@ function* listCentralConfigEntries() {
258
249
  if (meta.config?.interactiveHost !== undefined) {
259
250
  yield { key: 'interactive.host', value: meta.config.interactiveHost, hint: 'config.interactiveHost' };
260
251
  }
261
- if (meta.config?.usagePrimaryHost !== undefined) {
262
- yield { key: 'usage.primary-host', value: meta.config.usagePrimaryHost, hint: 'config.usagePrimaryHost' };
263
- }
264
252
  if (meta.config?.autoPool !== undefined) {
265
253
  yield { key: 'auto.pool', value: meta.config.autoPool, hint: 'config.autoPool' };
266
254
  }
@@ -274,6 +262,11 @@ function* listCentralConfigEntries() {
274
262
  const key = 'browser.profile';
275
263
  yield { key, value: browserProfile, hint: configKeyStorageHint(parseConfigKey(key)) };
276
264
  }
265
+ const browserViewer = getConfigValue('browser.viewer').value;
266
+ if (browserViewer !== undefined) {
267
+ const key = 'browser.viewer';
268
+ yield { key, value: browserViewer, hint: configKeyStorageHint(parseConfigKey(key)) };
269
+ }
277
270
  }
278
271
  /** Collect device-scope config entries. */
279
272
  function* listDeviceConfigEntries(device) {
@@ -314,7 +307,19 @@ function* listDeviceConfigEntries(device) {
314
307
  continue;
315
308
  key = `${prefix}browser.profile`;
316
309
  break;
310
+ case 'browser.viewer':
311
+ // Same duplication rule as browser.profile above.
312
+ if (device === machineId())
313
+ continue;
314
+ key = `${prefix}browser.viewer`;
315
+ break;
317
316
  default:
317
+ // A `default: continue` here silently drops any device property with no
318
+ // arm — which is how browser.viewer was invisible to `config list` after
319
+ // being added everywhere else. This is the fourth switch enumerating
320
+ // DeviceConfigProperty; unlike parseValue's it cannot use a `never`
321
+ // binding (it must keep skipping properties that are deliberately not
322
+ // listed), so the completeness test in config.test.ts is the guard.
318
323
  continue;
319
324
  }
320
325
  yield { key, value: entry.value, hint: configKeyStorageHint(parseConfigKey(key)) };
@@ -331,14 +336,11 @@ export function registerConfigCommand(program) {
331
336
  agents config set run.claude@2.1.45.model claude-opus-4-8
332
337
  agents config set run.claude@*.mode auto
333
338
  agents config set interactive.host zion
334
- agents config set usage.primary-host zion
335
339
  agents config set browser.profile work
336
340
  agents config set project.root ~/src/github.com/<you>
337
341
  agents config set devices.mac-mini.max-agents 4
338
342
  agents config get run.claude@*.model
339
- agents config get usage.primary-host
340
343
  agents config unset run.claude@*.tier.best
341
- agents config unset usage.primary-host
342
344
  agents config list
343
345
  agents config budget
344
346
  agents config budget set per_day 50
@@ -5,7 +5,7 @@
5
5
  * This is the read side of issue #323. Cost and duration are computed and
6
6
  * persisted at scan time (see src/lib/session/discover.ts + db.ts); this
7
7
  * command only queries and renders them — a pure SQLite/CLI win, no server,
8
- * no telemetry. Distinct from `agents usage`, which reports live rate-limit /
8
+ * no telemetry. Distinct from `agents view`, which reports live rate-limit /
9
9
  * quota status per agent and is left untouched.
10
10
  */
11
11
  import type { Command } from 'commander';
@@ -15,4 +15,25 @@
15
15
  * duplicating it.
16
16
  */
17
17
  import type { Command } from 'commander';
18
+ import { type DaemonServiceId } from '../lib/daemon-services.js';
19
+ /** One row of the roster every registered daemon service — supervisor-managed or legacy — renders in `agents daemon services`. */
20
+ export interface DaemonServiceRow {
21
+ id: DaemonServiceId;
22
+ title: string;
23
+ description: string;
24
+ enabled: boolean;
25
+ /**
26
+ * `ServiceSupervisor`'s real lifecycle state when `supervised` is true
27
+ * (`idle`/`running`/`parked`/`stopped`, written cross-process via
28
+ * `recordSubsystemState`). A legacy `setInterval`-driven service has no such
29
+ * record, so its state is inferred from `enabled` + whether the daemon
30
+ * process is up — labelled distinctly so a reader can't mistake it for a
31
+ * measured value.
32
+ */
33
+ state: string;
34
+ supervised: boolean;
35
+ lastRunMs: number | null;
36
+ lastError: string | null;
37
+ consecutiveFailures: number;
38
+ }
18
39
  export declare function registerDaemonCommand(program: Command): void;
@@ -21,8 +21,8 @@ import * as path from 'path';
21
21
  import { setHelpSections } from '../lib/help.js';
22
22
  import { getDaemonStatus, isDaemonRunning, isDaemonWedged, readDaemonLog, startDaemon, stopDaemon, signalDaemonReload, findSurvivingStateDirDaemons, getDaemonLogPath, isDaemonAutostartCircuitOpen, } from '../lib/daemon/daemon.js';
23
23
  import { getConfigValue, setConfigValue, isDaemonEnabled } from '../lib/device-config.js';
24
- import { readSubsystemHealth, SUBSYSTEM_SECRETS_BROKER, SUBSYSTEM_BROWSER_IPC, SUBSYSTEM_DAEMON_START, } from '../lib/daemon-health.js';
25
- import { DAEMON_SERVICE_IDS, listDaemonServiceStates, setDaemonServiceEnabled, getDaemonServicesConfigPath, } from '../lib/daemon-services.js';
24
+ import { readSubsystemHealth, readAllSubsystemHealth, SUBSYSTEM_SECRETS_BROKER, SUBSYSTEM_BROWSER_IPC, SUBSYSTEM_DAEMON_START, } from '../lib/daemon-health.js';
25
+ import { DAEMON_SERVICE_IDS, listDaemonServiceStates, setDaemonServiceEnabled, getDaemonServicesConfigPath, queueDaemonServiceRestart, } from '../lib/daemon-services.js';
26
26
  import { listJobs, getLatestRun } from '../lib/scheduling/routines.js';
27
27
  import { JobScheduler } from '../lib/scheduler.js';
28
28
  import { followFile } from '../lib/log-follow.js';
@@ -474,19 +474,76 @@ async function runStatus(opts) {
474
474
  console.log(chalk.red('\nThe daemon is wedged (heartbeat stale). Restart: agents daemon restart'));
475
475
  }
476
476
  }
477
+ /**
478
+ * Combine the persisted enable/disable toggles with whatever health the
479
+ * `ServiceSupervisor` (or a legacy subsystem) has reported. Pure — reads two
480
+ * on-disk files, makes no probes — so it's cheap to call from both the
481
+ * non-interactive renderer and the interactive picker's refresh tick.
482
+ */
483
+ function buildServiceRows(daemonRunning) {
484
+ const states = listDaemonServiceStates();
485
+ const healthById = new Map(readAllSubsystemHealth().map((h) => [h.subsystem, h]));
486
+ return states.map((s) => {
487
+ const h = healthById.get(s.id);
488
+ const supervised = h?.state !== undefined;
489
+ // A persisted supervisor state (health.json) is only trustworthy while the
490
+ // daemon is actually up. If it is not running (crash / kill -9 / never
491
+ // started), every service is stopped no matter what the last-written record
492
+ // says — trusting a stale 'running'/'parked'/'idle' here would print a
493
+ // report that contradicts the live-probed socket rows below (RUSH-2368).
494
+ const state = daemonRunning
495
+ ? (h?.state ?? (s.enabled ? 'running (unsupervised)' : 'stopped'))
496
+ : 'stopped';
497
+ return {
498
+ id: s.id,
499
+ title: s.title,
500
+ description: s.description,
501
+ enabled: s.enabled,
502
+ state,
503
+ supervised,
504
+ lastRunMs: h?.lastOkAt ? Date.parse(h.lastOkAt) : null,
505
+ lastError: h?.lastError ?? null,
506
+ consecutiveFailures: h?.consecutiveFailures ?? 0,
507
+ };
508
+ });
509
+ }
510
+ function serviceStateLabel(state) {
511
+ if (state === 'running')
512
+ return chalk.green('running');
513
+ if (state === 'parked')
514
+ return chalk.red('parked');
515
+ if (state === 'stopped')
516
+ return chalk.gray('stopped');
517
+ if (state === 'idle')
518
+ return chalk.gray('idle');
519
+ return chalk.yellow(state); // 'running (unsupervised)' or any future label
520
+ }
477
521
  async function runServices(opts) {
478
522
  const [secrets, browserIpc] = await Promise.all([probeSecretsBroker(), probeBrowserIPC()]);
523
+ const rows = buildServiceRows(isDaemonRunning());
479
524
  if (opts.json) {
480
525
  console.log(JSON.stringify({
526
+ // Existing fields — unchanged shape, agents/CI consume these directly.
481
527
  secretsBroker: { reachable: secrets.reachable, socketPath: secrets.socketPath, heldBundles: secrets.heldBundles, health: secrets.record },
482
528
  browserIpc: { bound: browserIpc.bound, socketPath: browserIpc.socketPath, sessionCount: browserIpc.sessionCount, health: browserIpc.record },
529
+ // Additive: every registered service, supervised or legacy.
530
+ services: rows,
483
531
  }, null, 2));
484
532
  return;
485
533
  }
486
- console.log(chalk.bold('Hosted services\n'));
534
+ console.log(chalk.bold('Daemon services\n'));
535
+ for (const row of rows) {
536
+ const lastRun = row.lastRunMs ? new Date(row.lastRunMs).toLocaleString() : '-';
537
+ console.log(` ${row.id.padEnd(16)} ${serviceStateLabel(row.state).padEnd(20)} `
538
+ + `enabled=${row.enabled ? 'yes' : 'no '} fails=${row.consecutiveFailures} last-run=${lastRun}`);
539
+ if (row.lastError)
540
+ console.log(chalk.gray(` last-error: ${row.lastError}`));
541
+ }
542
+ console.log(chalk.bold('\nHosted sockets\n'));
487
543
  console.log(healthLine(`secrets broker ${secrets.reachable ? `(${secrets.socketPath}, ${secrets.heldBundles} bundle(s) held)` : '(unreachable)'}`, secrets.reachable, secrets.record));
488
544
  console.log(healthLine(`browser IPC ${browserIpc.bound ? `(${browserIpc.socketPath}, ${browserIpc.sessionCount} session(s))` : '(unbound)'}`, browserIpc.bound, browserIpc.record));
489
545
  console.log(chalk.gray('\nScheduled routines run through `agents routines` — see: agents routines stats'));
546
+ console.log(chalk.gray('agents daemon services enable|disable|restart <id> apply live for supervised services.'));
490
547
  }
491
548
  function parseLogLines(raw) {
492
549
  const out = [];
@@ -795,9 +852,17 @@ export function registerDaemonCommand(program) {
795
852
  # Reload config (SIGHUP) without restarting — picks up routine/scheduler-gate changes
796
853
  agents daemon reload
797
854
 
798
- # Every hosted service (secrets broker, browser IPC, webhook receiver, ...)
855
+ # Every registered service state, enabled, failures, last error
799
856
  agents daemon services
800
857
 
858
+ # Toggle or restart a service live — applies without a daemon restart
859
+ # for supervisor-managed services (secrets-broker, browser-ipc,
860
+ # account-state, session-index, monitors' off-transition, watchdog,
861
+ # device-probe, self-heal, keychain-reap, state-dir-check)
862
+ agents daemon services disable secrets-broker
863
+ agents daemon services enable secrets-broker
864
+ agents daemon services restart secrets-broker
865
+
801
866
  # Host a signed webhook receiver here, supervised and restarted on crash
802
867
  agents daemon webhooks add --secrets-bundle linear-webhook
803
868
  agents daemon webhooks list
@@ -821,6 +886,16 @@ export function registerDaemonCommand(program) {
821
886
  the daemon (daemon.enabled: false in ~/.agents/devices/<host>/agents.yaml).
822
887
  'agents daemon start' still starts it explicitly, same as
823
888
  'systemctl start' on a disabled unit.
889
+
890
+ 'agents daemon services enable|disable|restart <id>' applies live (no
891
+ daemon restart) for the 10 supervisor-managed services: secrets-broker,
892
+ browser-ipc, account-state, session-index, monitors (off only — turning
893
+ monitors back on still needs a restart), watchdog, device-probe,
894
+ self-heal, keychain-reap, and state-dir-check. The other 2 services
895
+ (scheduler, webhook-receiver) are still bare interval/socket setups with
896
+ no live start/stop hook, so a toggle there — and 'enable' on any service
897
+ that was disabled at daemon boot — still needs 'agents daemon restart'.
898
+ 'agents daemon services' names which case you're in per row.
824
899
  `,
825
900
  });
826
901
  cmd.command('status')
@@ -917,11 +992,38 @@ export function registerDaemonCommand(program) {
917
992
  });
918
993
  const servicesCmd = cmd
919
994
  .command('services')
920
- .description('The hosted services: health, bound state, socket path, and per-service toggles.')
995
+ .description('Every registered daemon service: live health, enabled state, and live enable/disable/restart.')
921
996
  .option('--json', 'Emit as JSON')
922
997
  .action(async (opts, command) => {
923
998
  await runServices({ json: command.optsWithGlobals().json === true });
924
999
  });
1000
+ setHelpSections(servicesCmd, {
1001
+ examples: `
1002
+ # State, enabled, consecutive failures, last error for every service
1003
+ agents daemon services
1004
+
1005
+ # Machine-readable — additive: also carries the pre-existing
1006
+ # secretsBroker/browserIpc hosted-socket fields
1007
+ agents daemon services --json
1008
+
1009
+ # Just the enable/disable metadata, no health probe
1010
+ agents daemon services list
1011
+
1012
+ # Toggle or restart live — no daemon restart for a supervisor-managed
1013
+ # service (secrets-broker, browser-ipc, account-state, session-index)
1014
+ agents daemon services disable secrets-broker
1015
+ agents daemon services enable secrets-broker
1016
+ agents daemon services restart secrets-broker
1017
+ `,
1018
+ notes: `
1019
+ A service disabled at daemon boot was never registered on the
1020
+ supervisor, so 'enable' on it — and any toggle/restart on the 2
1021
+ services the supervisor doesn't manage yet (scheduler,
1022
+ webhook-receiver) — falls back to 'restart the daemon to apply'. Each
1023
+ row in the plain-text view names which case it is; 'supervised:
1024
+ true/false' does the same in --json.
1025
+ `,
1026
+ });
925
1027
  servicesCmd
926
1028
  .command('list')
927
1029
  .description('List every daemon service and whether it is enabled.')
@@ -947,9 +1049,28 @@ export function registerDaemonCommand(program) {
947
1049
  console.log(chalk.gray(`\nConfig: ${getDaemonServicesConfigPath()}`));
948
1050
  console.log(chalk.gray('Changes take effect on the next daemon reload or restart.'));
949
1051
  });
1052
+ /**
1053
+ * Apply an enable/disable toggle live (RUSH-3193 P4): persist it, then signal
1054
+ * the running daemon to reload — its handler diffs the toggle and drives
1055
+ * `supervisor.start/stop(id)` for a supervised service, so no restart is
1056
+ * needed for the 10 supervisor-managed services (secrets-broker,
1057
+ * browser-ipc, account-state, session-index, monitors' off-transition,
1058
+ * watchdog, device-probe, self-heal, keychain-reap, state-dir-check). The 2
1059
+ * remaining `setInterval`/socket-driven services (scheduler,
1060
+ * webhook-receiver) and monitors' on-transition still need a restart — the
1061
+ * daemon's own reload log says so.
1062
+ */
1063
+ function applyServiceToggleLive(service) {
1064
+ if (!isDaemonRunning())
1065
+ return;
1066
+ const ok = signalDaemonReload();
1067
+ console.log(ok
1068
+ ? chalk.gray('Signalled the daemon to apply live. Confirm: agents daemon services')
1069
+ : chalk.yellow('Reload signal not delivered — restart the daemon to apply: agents daemon restart'));
1070
+ }
950
1071
  servicesCmd
951
1072
  .command('enable <service>')
952
- .description('Enable a daemon service.')
1073
+ .description('Enable a daemon service. Applies live for supervised services; a legacy service needs a restart.')
953
1074
  .action((service) => {
954
1075
  if (!DAEMON_SERVICE_IDS.includes(service)) {
955
1076
  console.error(chalk.red(`Unknown service '${service}'. Run 'agents daemon services list' for valid services.`));
@@ -957,13 +1078,11 @@ export function registerDaemonCommand(program) {
957
1078
  }
958
1079
  setDaemonServiceEnabled(service, true);
959
1080
  console.log(chalk.green(`Enabled '${service}'.`));
960
- if (isDaemonRunning()) {
961
- console.log(chalk.gray('Run `agents daemon reload` (or restart) to apply.'));
962
- }
1081
+ applyServiceToggleLive(service);
963
1082
  });
964
1083
  servicesCmd
965
1084
  .command('disable <service>')
966
- .description('Disable a daemon service.')
1085
+ .description('Disable a daemon service. Applies live for supervised services; a legacy service needs a restart.')
967
1086
  .action((service) => {
968
1087
  if (!DAEMON_SERVICE_IDS.includes(service)) {
969
1088
  console.error(chalk.red(`Unknown service '${service}'. Run 'agents daemon services list' for valid services.`));
@@ -971,9 +1090,25 @@ export function registerDaemonCommand(program) {
971
1090
  }
972
1091
  setDaemonServiceEnabled(service, false);
973
1092
  console.log(chalk.green(`Disabled '${service}'.`));
974
- if (isDaemonRunning()) {
975
- console.log(chalk.gray('Run `agents daemon reload` (or restart) to apply.'));
1093
+ applyServiceToggleLive(service);
1094
+ });
1095
+ servicesCmd
1096
+ .command('restart <service>')
1097
+ .description('Restart a supervised daemon service live, right now, outside its normal backoff schedule.')
1098
+ .action((service) => {
1099
+ if (!DAEMON_SERVICE_IDS.includes(service)) {
1100
+ console.error(chalk.red(`Unknown service '${service}'. Run 'agents daemon services list' for valid services.`));
1101
+ process.exit(1);
976
1102
  }
1103
+ if (!isDaemonRunning()) {
1104
+ console.log(chalk.yellow('Daemon is not running — nothing to restart. Start it: agents daemon start'));
1105
+ process.exit(1);
1106
+ }
1107
+ queueDaemonServiceRestart(service);
1108
+ const ok = signalDaemonReload();
1109
+ console.log(ok
1110
+ ? chalk.green(`Restart requested for '${service}'.`) + chalk.gray(' Confirm: agents daemon services')
1111
+ : chalk.yellow('Reload signal not delivered (unsupported on this platform, or the daemon just exited).'));
977
1112
  });
978
1113
  registerWebhooksSubcommand(cmd);
979
1114
  registerFunnelCommand(cmd);
@@ -1,5 +1,18 @@
1
1
  import type { Command } from 'commander';
2
+ import type { ActiveSession } from '../lib/session/active.js';
2
3
  export declare function registerDetachCommand(program: Command): void;
4
+ /**
5
+ * Stop a live agent's interactive process and tear down its mux. tmux-hosted
6
+ * sessions are killed by session (closes the pane, ends the agent, reaps its
7
+ * helper processes); a plain process is SIGTERM'd (then SIGKILLed) by pid. Either
8
+ * way we wait for the process to actually exit before returning.
9
+ *
10
+ * Shared by two callers: `detach` (stop the terminal so the headless resume it
11
+ * spawns next doesn't fight the old process over the same transcript) and `stop`
12
+ * (end the session outright — no resume). `socket` defaults to the shared agents
13
+ * socket; it is a parameter only so a test can drive an isolated tmux server.
14
+ */
15
+ export declare function stopInteractive(s: ActiveSession, socket?: string): Promise<void>;
3
16
  export declare function detachAction(id: string, opts?: {
4
17
  local?: boolean;
5
18
  }): Promise<void>;
@@ -34,17 +34,20 @@ export function registerDetachCommand(program) {
34
34
  });
35
35
  }
36
36
  /**
37
- * Stop the interactive process so it can't fight the headless resume over the
38
- * same transcript. tmux-hosted sessions are killed by session (closes the pane
39
- * and ends the agent); a plain process is SIGTERM'd by pid. Either way we then
40
- * wait for the process to actually exit before returning, so the headless
41
- * continuation never starts writing the transcript while the old process is
42
- * still flushing it.
37
+ * Stop a live agent's interactive process and tear down its mux. tmux-hosted
38
+ * sessions are killed by session (closes the pane, ends the agent, reaps its
39
+ * helper processes); a plain process is SIGTERM'd (then SIGKILLed) by pid. Either
40
+ * way we wait for the process to actually exit before returning.
41
+ *
42
+ * Shared by two callers: `detach` (stop the terminal so the headless resume it
43
+ * spawns next doesn't fight the old process over the same transcript) and `stop`
44
+ * (end the session outright — no resume). `socket` defaults to the shared agents
45
+ * socket; it is a parameter only so a test can drive an isolated tmux server.
43
46
  */
44
- async function stopInteractive(s) {
47
+ export async function stopInteractive(s, socket = getDefaultSocketPath()) {
45
48
  if (s.tmuxTarget) {
46
49
  const name = s.tmuxTarget.split(':')[0];
47
- await killSession(name, getDefaultSocketPath());
50
+ await killSession(name, socket);
48
51
  }
49
52
  else if (s.pid && s.pid > 0) {
50
53
  try {