@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
@@ -0,0 +1,231 @@
1
+ /**
2
+ * `agents cp <src> <dst>` — first-class fleet file transfer.
3
+ *
4
+ * Accepts `host:path` on either side (local = bare absolute path). Resolves
5
+ * `~` and `$HOME` remote paths ON the remote before transfer so a `$HOME` in
6
+ * the remote spec can never silently expand to the local user's home directory
7
+ * — the documented silent-failure class in `remote-dispatch-mutation-safety`.
8
+ *
9
+ * Reuses the same SSH fabric (SSH_OPTS, deviceIdentityArgs, resolveDeviceTarget)
10
+ * that `agents ssh` and `--host` dispatch use.
11
+ */
12
+ import { spawnSync } from 'child_process';
13
+ import chalk from 'chalk';
14
+ import { setHelpSections } from '../lib/help.js';
15
+ import { resolveDeviceTarget } from '../lib/devices/resolve-target.js';
16
+ import { sshTargetFor, deviceIdentityArgs } from '../lib/devices/connect.js';
17
+ import { sshExec, SSH_OPTS } from '../lib/ssh-exec.js';
18
+ /**
19
+ * Parse one CLI token into an {@link Endpoint}.
20
+ *
21
+ * Grammar: `host:path` is remote; anything else is local. The host part must
22
+ * not contain a `/` (that would be a local absolute path with a colon in it —
23
+ * unusual but valid; the colon test covers the common fleet pattern and rejects
24
+ * the almost-never-seen edge case in favour of local-path semantics).
25
+ *
26
+ * Windows drive letters (`C:path`) are excluded by requiring the host part to
27
+ * be at least two characters and not a single capital letter.
28
+ */
29
+ export function parseEndpoint(token) {
30
+ const colonIdx = token.indexOf(':');
31
+ if (colonIdx > 1 && !token.startsWith('/')) {
32
+ const host = token.slice(0, colonIdx);
33
+ const path = token.slice(colonIdx + 1);
34
+ // A bare single uppercase letter before `:` looks like a Windows drive
35
+ // letter — treat as local to avoid a confusing error.
36
+ if (!/^[A-Z]$/.test(host) && !host.includes('/')) {
37
+ return { isRemote: true, host, path };
38
+ }
39
+ }
40
+ return { isRemote: false, path: token };
41
+ }
42
+ // ---------------------------------------------------------------------------
43
+ // Remote path resolution — the core safeguard
44
+ // ---------------------------------------------------------------------------
45
+ /**
46
+ * Resolve the remote HOME directory in one SSH round-trip. Returns the
47
+ * absolute path (always starts with `/`), or throws on failure.
48
+ */
49
+ export async function resolveRemoteHome(target, extraSshArgs) {
50
+ const res = sshExec(target, 'printf "%s" "$HOME"', {
51
+ extraSshArgs,
52
+ timeoutMs: 10_000,
53
+ });
54
+ if (res.code !== 0) {
55
+ throw new Error(`Cannot resolve remote $HOME on ${target}: ${res.stderr.trim() || 'ssh exited ' + String(res.code)}`);
56
+ }
57
+ const home = res.stdout.trim();
58
+ if (!home || !home.startsWith('/')) {
59
+ throw new Error(`Remote $HOME on ${target} is not an absolute path: ${JSON.stringify(home)}`);
60
+ }
61
+ return home;
62
+ }
63
+ /**
64
+ * Pure home-prefix expansion. Given a remote HOME (already resolved on the
65
+ * remote) and a raw path token, substitute a leading `~` or `$HOME`. Paths
66
+ * without those prefixes are returned unchanged. Never consults
67
+ * `process.env.HOME` — the local home must not leak into a remote path.
68
+ */
69
+ export function expandRemoteHomePrefix(rawPath, remoteHome) {
70
+ if (rawPath === '~' || rawPath.startsWith('~/')) {
71
+ return rawPath === '~' ? remoteHome : remoteHome + rawPath.slice(1);
72
+ }
73
+ if (rawPath === '$HOME' || rawPath.startsWith('$HOME/')) {
74
+ return rawPath === '$HOME' ? remoteHome : remoteHome + rawPath.slice(5);
75
+ }
76
+ return rawPath;
77
+ }
78
+ /**
79
+ * Expand a remote path's leading `~` or literal `$HOME` to an absolute path
80
+ * resolved **on the remote**, not locally. A path that already starts with `/`
81
+ * is returned unchanged (no round-trip).
82
+ *
83
+ * This is the safeguard against the silent-failure class: a `$HOME` or `~`
84
+ * that would expand to the **local** user's home if left in a shell command.
85
+ */
86
+ export async function resolveRemotePath(rawPath, target, extraSshArgs) {
87
+ if (rawPath === '~' ||
88
+ rawPath.startsWith('~/') ||
89
+ rawPath === '$HOME' ||
90
+ rawPath.startsWith('$HOME/')) {
91
+ const home = await resolveRemoteHome(target, extraSshArgs);
92
+ return expandRemoteHomePrefix(rawPath, home);
93
+ }
94
+ return rawPath;
95
+ }
96
+ // ---------------------------------------------------------------------------
97
+ // scp invocation
98
+ // ---------------------------------------------------------------------------
99
+ /**
100
+ * Build the argv for the `scp` invocation. Pure — no spawning — so it is
101
+ * unit-testable.
102
+ *
103
+ * `scp` accepts ssh `-o` options, so `SSH_OPTS` flows through unchanged.
104
+ * Identity args (`-i key -o IdentitiesOnly=yes`) are included only when a
105
+ * single remote device is involved; for two-remote transfers the identity
106
+ * applies to both connections, which works when the fleet uses a shared key.
107
+ *
108
+ * Remote endpoints are formatted as `user@host:path` (or bare `host:path`),
109
+ * local endpoints as their absolute path. Paths have already been resolved
110
+ * (no `~` or `$HOME`) by the time this is called.
111
+ */
112
+ export function buildScpArgv({ srcSpec, dstSpec, identityArgs, recursive, }) {
113
+ const opts = [...SSH_OPTS, ...identityArgs];
114
+ if (recursive)
115
+ opts.push('-r');
116
+ // Two-remote transfers route through the local machine (-3) to avoid
117
+ // requiring direct SSH access between the two fleet boxes.
118
+ if (srcSpec.includes(':') && dstSpec.includes(':'))
119
+ opts.push('-3');
120
+ return [...opts, srcSpec, dstSpec];
121
+ }
122
+ async function resolveEndpoint(ep) {
123
+ if (!ep.isRemote) {
124
+ return { spec: ep.path, identityArgs: [] };
125
+ }
126
+ const device = await resolveDeviceTarget(ep.host);
127
+ if (!device) {
128
+ throw new Error(`Unknown device ${JSON.stringify(ep.host)}. Register it with \`agents devices add\` or check \`agents devices\`.`);
129
+ }
130
+ const target = sshTargetFor(device);
131
+ const idArgs = deviceIdentityArgs(device);
132
+ const resolvedPath = await resolveRemotePath(ep.path, target, idArgs);
133
+ if (!resolvedPath.startsWith('/')) {
134
+ throw new Error(`Remote path on ${ep.host} must be absolute after expansion, got: ${JSON.stringify(resolvedPath)}. Use an absolute path or start with ~/`);
135
+ }
136
+ return { spec: `${target}:${resolvedPath}`, identityArgs: idArgs };
137
+ }
138
+ // ---------------------------------------------------------------------------
139
+ // Validation
140
+ // ---------------------------------------------------------------------------
141
+ /** Validate a local path: must be absolute. */
142
+ function validateLocalPath(raw) {
143
+ if (!raw.startsWith('/')) {
144
+ throw new Error(`Local path must be absolute, got: ${JSON.stringify(raw)}`);
145
+ }
146
+ }
147
+ /** Validate a remote path: non-empty. */
148
+ function validateRemotePath(raw, host) {
149
+ if (!raw) {
150
+ throw new Error(`Remote path for ${JSON.stringify(host)} is empty. Expected: ${host}:/abs/path`);
151
+ }
152
+ }
153
+ // ---------------------------------------------------------------------------
154
+ // Command registration
155
+ // ---------------------------------------------------------------------------
156
+ export function registerCpCommand(program) {
157
+ const cmd = program
158
+ .command('cp <src> <dst>')
159
+ .description('Copy a file or directory between fleet hosts. Either endpoint may be host:path (remote) or an absolute local path.')
160
+ .option('-r, --recursive', 'Copy directories recursively')
161
+ .action(async (srcToken, dstToken, opts) => {
162
+ const recursive = opts.recursive ?? false;
163
+ const srcEp = parseEndpoint(srcToken);
164
+ const dstEp = parseEndpoint(dstToken);
165
+ if (!srcEp.isRemote && !dstEp.isRemote) {
166
+ process.stderr.write(chalk.red('error:') +
167
+ ' At least one endpoint must be remote (host:path). For local-only copies use `cp`.\n');
168
+ process.exit(1);
169
+ }
170
+ let srcResolved;
171
+ let dstResolved;
172
+ try {
173
+ // Validate shapes before any network calls.
174
+ if (!srcEp.isRemote)
175
+ validateLocalPath(srcEp.path);
176
+ if (srcEp.isRemote)
177
+ validateRemotePath(srcEp.path, srcEp.host);
178
+ if (!dstEp.isRemote)
179
+ validateLocalPath(dstEp.path);
180
+ if (dstEp.isRemote)
181
+ validateRemotePath(dstEp.path, dstEp.host);
182
+ [srcResolved, dstResolved] = await Promise.all([
183
+ resolveEndpoint(srcEp),
184
+ resolveEndpoint(dstEp),
185
+ ]);
186
+ }
187
+ catch (err) {
188
+ process.stderr.write(chalk.red('error:') + ' ' + err.message + '\n');
189
+ process.exit(1);
190
+ }
191
+ // Merge identity args: for two-remote transfers, omit per-device identity
192
+ // args (they apply to both connections via -3; a conflict would need
193
+ // separate -i per connection which scp doesn't support). A shared fleet
194
+ // key works without explicit -i.
195
+ const identityArgs = srcResolved.identityArgs.length > 0 && dstResolved.identityArgs.length === 0
196
+ ? srcResolved.identityArgs
197
+ : dstResolved.identityArgs.length > 0 && srcResolved.identityArgs.length === 0
198
+ ? dstResolved.identityArgs
199
+ : srcResolved.identityArgs.join(',') === dstResolved.identityArgs.join(',')
200
+ ? srcResolved.identityArgs
201
+ : []; // two remotes with different keys — skip per-device flag, use agent's default key
202
+ const argv = buildScpArgv({
203
+ srcSpec: srcResolved.spec,
204
+ dstSpec: dstResolved.spec,
205
+ identityArgs,
206
+ recursive,
207
+ });
208
+ const res = spawnSync('scp', argv, { stdio: 'inherit' });
209
+ const code = typeof res.status === 'number' ? res.status : 1;
210
+ if (code !== 0) {
211
+ // scp already printed a diagnostic; just exit non-zero.
212
+ process.exit(code);
213
+ }
214
+ });
215
+ setHelpSections(cmd, {
216
+ examples: `
217
+ agents cp yosemite-s0:/abs/data.json /tmp/data.json # download a file from a fleet host
218
+ agents cp /tmp/build.tar.gz mac-mini:/abs/deploy/ # upload a local file to a fleet host
219
+ agents cp -r yosemite-s0:/abs/src/ mac-mini:/abs/dst/ # copy a directory between two fleet hosts
220
+ agents cp yosemite-s0:~/logs/run.log /tmp/run.log # ~ is resolved on the remote host
221
+ `,
222
+ notes: `
223
+ Local paths must be absolute (use /abs/path, not ./relative).
224
+ Remote paths must be absolute after ~ / $HOME expansion.
225
+ Remote $HOME is resolved on the remote host — never in the local shell.
226
+ Both local-to-remote and remote-to-local transfers are supported.
227
+ Remote-to-remote transfers route through the local machine (-3).
228
+ Devices must be registered: 'agents devices' to list, 'agents devices add' to enroll.
229
+ `,
230
+ });
231
+ }
@@ -19,14 +19,105 @@ import { execFileSync } from 'child_process';
19
19
  import * as fs from 'fs';
20
20
  import * as path from 'path';
21
21
  import { setHelpSections } from '../lib/help.js';
22
- import { getDaemonStatus, isDaemonRunning, isDaemonWedged, readDaemonLog, startDaemon, stopDaemon, signalDaemonReload, findSurvivingStateDirDaemons, } from '../lib/daemon.js';
22
+ import { getDaemonStatus, isDaemonRunning, isDaemonWedged, readDaemonLog, startDaemon, stopDaemon, signalDaemonReload, findSurvivingStateDirDaemons, getDaemonLogPath, isDaemonAutostartCircuitOpen, } from '../lib/daemon.js';
23
23
  import { getConfigValue, setConfigValue, isDaemonEnabled } from '../lib/device-config.js';
24
- import { readSubsystemHealth, SUBSYSTEM_SECRETS_BROKER, SUBSYSTEM_BROWSER_IPC, } from '../lib/daemon-health.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';
25
26
  import { listJobs, getLatestRun } from '../lib/routines.js';
26
27
  import { JobScheduler } from '../lib/scheduler.js';
27
- import { getDaemonDir } from '../lib/state.js';
28
28
  import { followFile } from '../lib/log-follow.js';
29
29
  import { parseDuration } from '../lib/hooks/cache.js';
30
+ import { registerFunnelCommand } from './funnel.js';
31
+ /**
32
+ * Provably absent, or null when unknowable.
33
+ *
34
+ * `fs.existsSync` cannot express the difference: it returns false for ANY failed
35
+ * stat, so `EACCES` on a parent directory is indistinguishable from deletion.
36
+ * The feeding scan is box-wide `ps` with no uid filter, so that gap is reachable
37
+ * on any shared box — `/root` is mode 700, and a stat of a root-owned daemon's
38
+ * entry from an ordinary uid returns false while the file is perfectly present.
39
+ * Reporting that would tell the user to `kill` a healthy daemon they do not own,
40
+ * with a command that would `EPERM` anyway.
41
+ *
42
+ * `statSync(p, { throwIfNoEntry: false })` returns `undefined` only for `ENOENT`
43
+ * and throws for everything else, which is exactly the distinction needed.
44
+ */
45
+ function entryAbsent(p) {
46
+ try {
47
+ return fs.statSync(p, { throwIfNoEntry: false }) === undefined;
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ }
53
+ /**
54
+ * A daemon whose launch entry is provably gone from disk (RUSH-2493).
55
+ *
56
+ * This predicate answers only "is this one's code gone?". WHO may be told about
57
+ * it, and whether that report is actionable, is {@link staleDaemons}'s job — a
58
+ * separation reached the hard way: an early revision reported every box-wide
59
+ * `ps` match as actionable (re-opening RUSH-2368), and the correction then
60
+ * over-swung to registry-only, which silently excluded the very incident below.
61
+ *
62
+ * Observed 2026-08-10 on yosemite-s0: a daemon ran for 4h14m from
63
+ * `.agents/worktrees/rush-2431-binary-shadow/apps/cli/dist/index.js` after that
64
+ * worktree was deleted. `systemctl --user is-active` said `active`,
65
+ * `agents daemon status` reported healthy, and nothing anywhere named it —
66
+ * while it held a second routine scheduler, the double-fire class the
67
+ * one-scheduler-one-executor rule exists to prevent. A restart would also have
68
+ * failed, since the manifest pointed at the same missing path.
69
+ *
70
+ * ABSOLUTE PATHS ONLY. `entryFromTokens` returns the second-to-last argv token,
71
+ * which is the entry for a real daemon (`getDaemonLaunch` always spawns an
72
+ * absolute one) but is arbitrary text for anything else — `node -e '<code>'
73
+ * __daemon-run` yields the code blob, which of course does not exist on disk.
74
+ * Requiring absoluteness keeps a non-path token from being reported as deleted
75
+ * code, the same false-positive class RUSH-2368 had to correct for duplicates.
76
+ *
77
+ * That guard also covers an entry path containing SPACES, which `ps` renders
78
+ * unquoted and the tokenizer therefore splits: `/tmp/ghost space/sub/index.js`
79
+ * yields `space/sub/index.js`, not absolute, so a healthy daemon on such a path
80
+ * is never accused (verified live). The cost is a false NEGATIVE — a genuinely
81
+ * deleted entry containing a space is not reported either. That is the safe
82
+ * direction to fail: missing one detection is a silence we already lived with,
83
+ * whereas telling someone to `kill` a healthy shared daemon is a new harm.
84
+ */
85
+ function entryIsGone(p) {
86
+ return p.entry !== null && path.isAbsolute(p.entry) && p.entryMissing === true;
87
+ }
88
+ /**
89
+ * Stale daemons split into two tiers, because DETECTION and ACCUSATION are
90
+ * different acts with different blast radii.
91
+ *
92
+ * RUSH-2368's harm was the accusation, not the sighting: a leaked fixture "was
93
+ * reported as a stray to `kill`". So the narrow registry scope is what gates
94
+ * anything actionable — a `doctor` problem, a non-zero exit, a `kill` — while a
95
+ * same-uid sighting is merely shown.
96
+ *
97
+ * That split is not academic. The incident this feature exists to catch was
98
+ * neither the tracked pid nor in the registry: it ran under an ephemeral `/tmp`
99
+ * cwd from a deleted worktree, and `lib/daemon.ts` documents that such a process
100
+ * "registers under its own state dir and is invisible here" BY DESIGN. Gating
101
+ * the display on the registry too would have made this command silent on the
102
+ * exact 4h14m ghost that motivated it.
103
+ *
104
+ * `actionable` — this device's daemon, or this install's registry. Drives
105
+ * `doctor` problems and the `kill`/`restart` remediation.
106
+ * `visible` — additionally any daemon running as THIS uid. Shown in
107
+ * `status`, never exits non-zero, never told to kill.
108
+ *
109
+ * A different uid is never named at all: we cannot reliably stat its entry
110
+ * ({@link entryAbsent}), and we could not signal it if we tried.
111
+ */
112
+ function staleDaemons(processes, ownerPid, registered) {
113
+ const isOurs = (p) => p.pid === ownerPid || registered.has(p.pid);
114
+ const myUid = typeof process.getuid === 'function' ? process.getuid() : null;
115
+ const gone = processes.filter(entryIsGone);
116
+ return {
117
+ actionable: gone.filter(isOurs),
118
+ visible: gone.filter((p) => isOurs(p) || (myUid !== null && p.uid === myUid)),
119
+ };
120
+ }
30
121
  /**
31
122
  * Resolve the working directory a live process was started from, or null if
32
123
  * unavailable. Linux only (`/proc/<pid>/cwd`) — there is no equivalent
@@ -118,26 +209,31 @@ function scanDaemonProcesses() {
118
209
  return [];
119
210
  let out;
120
211
  try {
121
- out = execFileSync('ps', ['-eo', 'pid=,args='], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] });
212
+ out = execFileSync('ps', ['-eo', 'pid=,uid=,args='], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] });
122
213
  }
123
214
  catch {
124
215
  return [];
125
216
  }
126
217
  const found = [];
127
218
  for (const line of out.split('\n')) {
128
- const m = line.trim().match(/^(\d+)\s+(.*)$/);
219
+ const m = line.trim().match(/^(\d+)\s+(\d+)\s+(.*)$/);
129
220
  if (!m)
130
221
  continue;
131
- const args = m[2].trim();
222
+ const args = m[3].trim();
132
223
  const tokens = args.split(/\s+/);
133
224
  if (tokens.length === 0 || tokens[tokens.length - 1] !== '__daemon-run')
134
225
  continue;
135
226
  const pid = parseInt(m[1], 10);
136
227
  if (isNaN(pid))
137
228
  continue;
229
+ const uidParsed = parseInt(m[2], 10);
230
+ const uid = isNaN(uidParsed) ? null : uidParsed;
138
231
  const entry = entryFromTokens(tokens);
139
232
  const version = entry ? resolveVersionNear(entry, pid) : null;
140
- found.push({ pid, entry, version });
233
+ // Asks whether the code is on disk for the NEXT launch -- what a restart and
234
+ // every other reader will see -- not what this pid currently has mapped.
235
+ const entryMissing = entry ? entryAbsent(entry) : false;
236
+ found.push({ pid, entry, version, entryMissing, uid });
141
237
  }
142
238
  return found;
143
239
  }
@@ -255,6 +351,17 @@ async function runStatus(opts) {
255
351
  const processes = scanDaemonProcesses();
256
352
  const owner = pid ? processes.find((p) => p.pid === pid) : undefined;
257
353
  const duplicates = registryScopedDuplicates(processes, pid ?? null);
354
+ // Every daemon whose code is gone from disk, including this device's own if
355
+ // it is one. Not filtered by the duplicate scope — see entryIsGone.
356
+ // Deliberately its own read, NOT shared with registryScopedDuplicates above.
357
+ // That one passes `exclude` INTO findSurvivingStateDirDaemons to drop the
358
+ // owner, so handing it a shared empty-exclude snapshot would put ownerPid back
359
+ // in the set and report this device's daemon as its own duplicate. The saving
360
+ // would be one `ps` spawn per registered pid, at the 1-3 that actually run.
361
+ const registeredPids = new Set(findSurvivingStateDirDaemons(new Set()));
362
+ const staleTiers = staleDaemons(processes, pid ?? null, registeredPids);
363
+ const stale = staleTiers.visible;
364
+ const ownerEntryGone = owner ? entryIsGone(owner) : false;
258
365
  const [secrets, browserIpc] = await Promise.all([probeSecretsBroker(), probeBrowserIPC()]);
259
366
  const scheduler = schedulerSummary();
260
367
  if (opts.json) {
@@ -266,7 +373,18 @@ async function runStatus(opts) {
266
373
  logPath: status.logPath,
267
374
  binaryPath: owner?.entry ?? status.binaryPath,
268
375
  binaryVersion: owner?.version ?? null,
376
+ binaryMissing: ownerEntryGone,
269
377
  duplicates: duplicates.map((d) => ({ pid: d.pid, entry: d.entry, version: d.version })),
378
+ // `actionable` is part of the contract, not decoration: a routine or
379
+ // monitor reading this must be able to tell a ghost it may act on from one
380
+ // that is merely visible. Without it the machine surface re-opens exactly
381
+ // what the two-tier split closed for the text surface.
382
+ staleBinaries: stale.map((d) => ({
383
+ pid: d.pid,
384
+ entry: d.entry,
385
+ version: d.version,
386
+ actionable: staleTiers.actionable.some((a) => a.pid === d.pid),
387
+ })),
270
388
  daemonEnabled: enabled,
271
389
  services: {
272
390
  secretsBroker: {
@@ -304,11 +422,36 @@ async function runStatus(opts) {
304
422
  console.log(` Uptime: ${humanDuration(uptime)}`);
305
423
  if (heartbeatAgeMs !== null)
306
424
  console.log(` Heartbeat: ${Math.round(heartbeatAgeMs / 1000)}s ago`);
307
- console.log(` Binary: ${chalk.gray(owner?.entry ?? status.binaryPath ?? 'unknown')}`);
425
+ const binaryLabel = owner?.entry ?? status.binaryPath ?? 'unknown';
426
+ console.log(` Binary: ${ownerEntryGone ? chalk.red(`${binaryLabel} (MISSING from disk)`) : chalk.gray(binaryLabel)}`);
308
427
  console.log(` Version: ${chalk.gray(owner?.version ?? 'unknown')}`);
309
428
  console.log(` Log: ${chalk.gray(status.logPath)}`);
310
429
  if (!enabled)
311
430
  console.log(chalk.yellow(` daemon.enabled is false — nothing auto-starts it. Explicit start: agents daemon start`));
431
+ if (stale.length > 0) {
432
+ console.log(chalk.red(`\nStale code (${stale.length})\n`));
433
+ const actionablePids = new Set(staleTiers.actionable.map((d) => d.pid));
434
+ for (const d of stale) {
435
+ const mine = pid !== null && d.pid === pid ? ' — this is the daemon above' : '';
436
+ const note = mine || (actionablePids.has(d.pid) ? '' : ' — not this install; shown for visibility');
437
+ console.log(` PID ${d.pid} ${chalk.gray(d.entry ?? 'unknown entry')}${chalk.red(' (deleted)')}${chalk.gray(note)}`);
438
+ }
439
+ const advice = [
440
+ '\n These run code that no longer exists on disk, so a restart fails and their',
441
+ ' behaviour is whatever was loaded when the file was deleted.',
442
+ ];
443
+ // Only offer a remedy when a row here is one we may act on. Printing
444
+ // `kill <pid>` under a section whose only rows are visibility-tier is
445
+ // RUSH-2368's harm re-entering through the render layer after the data
446
+ // layer stopped producing it.
447
+ if (staleTiers.actionable.length > 0) {
448
+ advice.push(` Yours: ${chalk.white('agents daemon restart')} A stray this install owns: ${chalk.white('kill <pid>')}`);
449
+ }
450
+ else {
451
+ advice.push(' None belong to this install — nothing for you to stop here.');
452
+ }
453
+ console.log(chalk.gray(advice.join('\n')));
454
+ }
312
455
  if (duplicates.length > 0) {
313
456
  console.log(chalk.red(`\nDuplicates (${duplicates.length})\n`));
314
457
  for (const d of duplicates) {
@@ -377,7 +520,7 @@ async function runLogs(opts) {
377
520
  const sinceMs = opts.since ? Date.now() - (parseDuration(opts.since) ?? 0) * 1000 : undefined;
378
521
  const lineCount = opts.lines ? parseInt(opts.lines, 10) : 50;
379
522
  if (opts.follow) {
380
- const logPath = path.join(getDaemonDir(), 'logs.jsonl');
523
+ const logPath = getDaemonLogPath();
381
524
  for (const entry of parseLogLines(readDaemonLog(lineCount)).filter((e) => passesFilters(e, opts.level, sinceMs))) {
382
525
  if (opts.json)
383
526
  console.log(JSON.stringify(entry));
@@ -419,10 +562,37 @@ async function runDoctor(opts) {
419
562
  problems.push('Daemon is not running. Start it: agents daemon start');
420
563
  if (status.running && isDaemonWedged())
421
564
  problems.push('Daemon is wedged (heartbeat stale). Restart: agents daemon restart');
422
- const duplicates = registryScopedDuplicates(scanDaemonProcesses(), status.pid);
565
+ // RUSH-2418: an open auto-start circuit breaker is the FIRST thing to report
566
+ // for a stopped daemon — otherwise the only advice is "agents daemon start",
567
+ // which is the exact action the breaker just refused, with no hint that a
568
+ // breaker exists or what the underlying failure was. This is where the
569
+ // breaker's own message sends the operator, so it has to answer them.
570
+ const startHealth = readSubsystemHealth(SUBSYSTEM_DAEMON_START);
571
+ if (isDaemonAutostartCircuitOpen()) {
572
+ problems.push(`Daemon auto-start is disabled after ${startHealth?.consecutiveFailures ?? 0} consecutive starts that never reported healthy: ` +
573
+ `${startHealth?.lastError ?? 'no reason recorded'}. Fix the cause, then retry with: agents daemon start`);
574
+ }
575
+ else if (!status.running && startHealth && startHealth.consecutiveFailures > 0) {
576
+ // Only while the daemon is DOWN. A start is marked as failed the moment it
577
+ // is issued and cleared once the daemon finishes booting, so a running
578
+ // daemon with a non-zero streak is just the boot window (or a daemon
579
+ // launched outside startDaemon) — reporting it there is a false alarm that
580
+ // clears itself a second later.
581
+ problems.push(`Daemon start has ${startHealth.consecutiveFailures} consecutive failure(s): ${startHealth.lastError}`);
582
+ }
583
+ const healthProcesses = scanDaemonProcesses();
584
+ const duplicates = registryScopedDuplicates(healthProcesses, status.pid);
423
585
  if (duplicates.length > 0) {
424
586
  problems.push(`${duplicates.length} duplicate daemon process(es) running: ${duplicates.map((d) => d.pid).join(', ')}. Stop the stray(s).`);
425
587
  }
588
+ // A daemon whose entry is gone from disk is a problem even when it answers
589
+ // every probe: it cannot restart, and it is running whatever was loaded
590
+ // before the file was deleted (RUSH-2493).
591
+ for (const p of staleDaemons(healthProcesses, status.pid, new Set(findSurvivingStateDirDaemons(new Set()))).actionable) {
592
+ const own = status.pid !== null && p.pid === status.pid;
593
+ problems.push(`Daemon pid ${p.pid} runs code deleted from disk (${p.entry}). ` +
594
+ (own ? 'Restart it: agents daemon restart' : 'Stray from a removed install/worktree. Stop it: kill ' + p.pid));
595
+ }
426
596
  const secrets = await probeSecretsBroker();
427
597
  if (!secrets.reachable)
428
598
  problems.push('Secrets broker is unreachable.');
@@ -468,7 +638,7 @@ export function registerDaemonCommand(program) {
468
638
  # Identity, duplicates, and per-service health in one view
469
639
  agents daemon status
470
640
 
471
- # Machine-readable status (for scripts / Factory)
641
+ # Machine-readable status (for scripts / AGI EXT)
472
642
  agents daemon status --json
473
643
 
474
644
  # Start / stop / restart the daemon process
@@ -486,6 +656,10 @@ export function registerDaemonCommand(program) {
486
656
  # Just the two hosted services (secrets broker, browser IPC)
487
657
  agents daemon services
488
658
 
659
+ # Manage public ingress for daemon-world webhook receivers
660
+ agents daemon funnel status yosemite-s0
661
+ agents daemon funnel up yosemite-s0 --local-port 8787 --port 443
662
+
489
663
  # Tail the daemon's own log, warnings and up, from the last hour
490
664
  agents daemon logs -f --level warn --since 1h
491
665
 
@@ -504,7 +678,7 @@ export function registerDaemonCommand(program) {
504
678
  `,
505
679
  });
506
680
  cmd.command('status')
507
- .description('Identity (state/pid/uptime/binary), duplicate daemon processes, and per-service health.')
681
+ .description('Identity (state/pid/uptime/binary), duplicate daemons, daemons running deleted code, and per-service health.')
508
682
  .option('--json', 'Emit as JSON')
509
683
  .action(async (opts, command) => {
510
684
  await runStatus({ json: command.optsWithGlobals().json === true });
@@ -595,12 +769,67 @@ export function registerDaemonCommand(program) {
595
769
  const ok = signalDaemonReload();
596
770
  console.log(ok ? chalk.green('Daemon reloaded') : chalk.yellow('Reload signal not delivered (unsupported on this platform, or the daemon just exited)'));
597
771
  });
598
- cmd.command('services')
599
- .description('The two hosted services (secrets broker, browser IPC): bound state, socket path, and health.')
772
+ const servicesCmd = cmd
773
+ .command('services')
774
+ .description('The hosted services: health, bound state, socket path, and per-service toggles.')
600
775
  .option('--json', 'Emit as JSON')
601
776
  .action(async (opts, command) => {
602
777
  await runServices({ json: command.optsWithGlobals().json === true });
603
778
  });
779
+ servicesCmd
780
+ .command('list')
781
+ .description('List every daemon service and whether it is enabled.')
782
+ .option('--json', 'Emit as JSON')
783
+ .action(async (opts, command) => {
784
+ const json = command.optsWithGlobals().json === true;
785
+ const states = listDaemonServiceStates();
786
+ if (json) {
787
+ console.log(JSON.stringify(states.map((s) => ({
788
+ id: s.id,
789
+ title: s.title,
790
+ enabled: s.enabled,
791
+ description: s.description,
792
+ })), null, 2));
793
+ return;
794
+ }
795
+ console.log(chalk.bold('Daemon services'));
796
+ for (const s of states) {
797
+ const state = s.enabled ? chalk.green('enabled') : chalk.gray('disabled');
798
+ console.log(` ${s.id.padEnd(18)} ${state}`);
799
+ console.log(` ${chalk.gray(s.description)}`);
800
+ }
801
+ console.log(chalk.gray(`\nConfig: ${getDaemonServicesConfigPath()}`));
802
+ console.log(chalk.gray('Changes take effect on the next daemon reload or restart.'));
803
+ });
804
+ servicesCmd
805
+ .command('enable <service>')
806
+ .description('Enable a daemon service.')
807
+ .action((service) => {
808
+ if (!DAEMON_SERVICE_IDS.includes(service)) {
809
+ console.error(chalk.red(`Unknown service '${service}'. Run 'agents daemon services list' for valid services.`));
810
+ process.exit(1);
811
+ }
812
+ setDaemonServiceEnabled(service, true);
813
+ console.log(chalk.green(`Enabled '${service}'.`));
814
+ if (isDaemonRunning()) {
815
+ console.log(chalk.gray('Run `agents daemon reload` (or restart) to apply.'));
816
+ }
817
+ });
818
+ servicesCmd
819
+ .command('disable <service>')
820
+ .description('Disable a daemon service.')
821
+ .action((service) => {
822
+ if (!DAEMON_SERVICE_IDS.includes(service)) {
823
+ console.error(chalk.red(`Unknown service '${service}'. Run 'agents daemon services list' for valid services.`));
824
+ process.exit(1);
825
+ }
826
+ setDaemonServiceEnabled(service, false);
827
+ console.log(chalk.green(`Disabled '${service}'.`));
828
+ if (isDaemonRunning()) {
829
+ console.log(chalk.gray('Run `agents daemon reload` (or restart) to apply.'));
830
+ }
831
+ });
832
+ registerFunnelCommand(cmd);
604
833
  cmd.command('logs')
605
834
  .description('Read the daemon\'s own log (lifecycle + subsystem errors — not routine run output).')
606
835
  .option('-n, --lines <number>', 'Show this many recent lines', '50')
@@ -3,7 +3,7 @@
3
3
  * resolver. Kept import-light (only a type import) so it is unit-tested without
4
4
  * loading the live-session discovery graph.
5
5
  */
6
- import type { ActiveSession } from '../lib/session/active.js';
6
+ import { type ActiveSession } from '../lib/session/active.js';
7
7
  /**
8
8
  * The prompt the backgrounded run resumes with. Its whole job is to stop a
9
9
  * now-unwatched agent from stalling on a confirmation nobody will answer: it
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Pure core of `agents sessions detach` — the nudge, the resumed-run argv, and the id
3
+ * resolver. Kept import-light (only a type import) so it is unit-tested without
4
+ * loading the live-session discovery graph.
5
+ */
6
+ import { sessionProcessHost } from '../lib/session/active.js';
1
7
  /**
2
8
  * The prompt the backgrounded run resumes with. Its whole job is to stop a
3
9
  * now-unwatched agent from stalling on a confirmation nobody will answer: it
@@ -40,8 +46,12 @@ export function resolveDetachTarget(s, self) {
40
46
  if (!sessionId) {
41
47
  return { kind: 'refuse', reason: 'That session has no id to resume, so it cannot be detached.' };
42
48
  }
43
- if (s.machine && s.machine !== self) {
44
- return { kind: 'remote', machine: s.machine, sessionId };
49
+ // The box to hop to is where the PROCESS is (a shim lives on its dispatcher),
50
+ // which is not always `machine`. `sessionProcessHost` returns undefined when
51
+ // the process is here, so a value means a genuine remote takeover.
52
+ const processHost = sessionProcessHost(s, self);
53
+ if (processHost) {
54
+ return { kind: 'remote', machine: processHost, sessionId };
45
55
  }
46
56
  return { kind: 'local', sessionId };
47
57
  }
@@ -166,6 +166,6 @@ export async function detachAction(id, opts = {}) {
166
166
  detachedAtMs: Date.now(),
167
167
  });
168
168
  console.log(chalk.green(`◒ Backgrounded ${agent} ${short}`) +
169
- chalk.gray(` — running headless (pid ${pid}). Bring it back: agents sessions attach ${short}`));
169
+ chalk.gray(` — running headless (pid ${pid}). Bring it back: agents sessions resume ${short}`));
170
170
  console.log(chalk.gray(` logs: ${logFile}`));
171
171
  }