@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,90 @@
1
+ import { type PidSessionEntry } from '../session/pid-registry.js';
2
+ import type { ReapResult, Task } from './types.js';
3
+ /** Default idle window before an untouched task is reaped. */
4
+ export declare const DEFAULT_IDLE_MS: number;
5
+ /**
6
+ * The slice of `BrowserService` the reaper needs. Structural, so a test drives
7
+ * it with a stub and `BrowserService` satisfies it without an import — which
8
+ * also keeps this module off the service's import cycle.
9
+ */
10
+ export interface ReapableService {
11
+ listTasks(): Array<{
12
+ profile: string;
13
+ task: Task;
14
+ }>;
15
+ recordStatus(taskName: string): Promise<{
16
+ recording: boolean;
17
+ }>;
18
+ stop(taskName: string): Promise<{
19
+ ok: boolean;
20
+ profile?: string;
21
+ }>;
22
+ }
23
+ /**
24
+ * Injectable liveness sources. Same shape as `feed-post.ts`
25
+ * (`input.listEntries ?? listPidSessionEntries`, feed-post.ts:119) — the
26
+ * defaults are the real registry and the real process table, so a test can
27
+ * substitute them without mocking a module.
28
+ */
29
+ export interface ReapDeps {
30
+ listEntries?: () => PidSessionEntry[];
31
+ pidAlive?: (pid: number, startedAtMs?: number) => boolean;
32
+ sessionIdOfPid?: (pid: number) => string | undefined;
33
+ sessionLiveOnProcessTable?: (sessionId: string) => Promise<boolean>;
34
+ }
35
+ export interface ReapOptions {
36
+ /** Idle window in ms. Default {@link DEFAULT_IDLE_MS}. */
37
+ idleMs?: number;
38
+ /** Clock, injectable for tests. Default `Date.now()`. */
39
+ now?: number;
40
+ /** Report what would be closed without closing anything. */
41
+ dryRun?: boolean;
42
+ deps?: ReapDeps;
43
+ }
44
+ export interface LiveIdentities {
45
+ sessions: Set<string>;
46
+ launches: Set<string>;
47
+ }
48
+ /**
49
+ * Session and launch ids belonging to a process that is alive right now.
50
+ *
51
+ * Built from the per-pid launch registry, filtered to live pids —
52
+ * `isPidAlive(pid, startedAtMs)` rather than a bare existence check, so a pid
53
+ * the OS recycled onto an unrelated process does not read as a live agent.
54
+ * An entry with no recorded `sessionId` still contributes one when the live
55
+ * process carries `--session-id` on its argv, which is the RUSH-2384 recovery
56
+ * path the registry itself documents (pid-registry.ts:102-107).
57
+ */
58
+ export declare function resolveLiveIdentities(deps?: ReapDeps): LiveIdentities;
59
+ /**
60
+ * True when the task's owner is PROVABLY gone. The bar is proof, not absence of
61
+ * evidence, because being wrong here closes a working agent's tabs.
62
+ *
63
+ * Only a `sessionId` can carry that proof. It has two independent sources — the
64
+ * per-pid launch registry, and a live process carrying `--session-id <id>` in
65
+ * its argv — so a session the registry missed is still caught by the process
66
+ * table. The registry misses constantly: a wrapper pid exits, a prune sweeps
67
+ * the entry, or the agent was never launched via `agents run`
68
+ * (pid-registry.ts:102-107, RUSH-2384).
69
+ *
70
+ * A `launchId` has NO second source — the registry is its only witness, and
71
+ * that witness is the unreliable one. So a task carrying only a `launchId` is
72
+ * never session-reaped, exactly like a task carrying no identity at all; both
73
+ * fall through to the idle rule. This is not a corner case: `launchId` is
74
+ * minted for every run (`exec.ts` `resolveLaunchId`) while `AGENT_SESSION_ID`
75
+ * is Claude-only and skipped on resume, so treating a missing registry entry as
76
+ * proof of death would close the tabs of every live codex/droid/grok run whose
77
+ * launch pid had already exited.
78
+ *
79
+ * A live `launchId` still RESCUES a task whose `sessionId` looks dead — proof of
80
+ * life needs only one witness, unlike proof of death.
81
+ */
82
+ export declare function taskOwnerIsGone(task: Task, live: LiveIdentities, deps?: ReapDeps): Promise<boolean>;
83
+ /**
84
+ * Stop every task whose owner is gone or that has sat untouched past `idleMs`.
85
+ *
86
+ * Returns what it closed and how many it left alone. A task that is mid-
87
+ * recording is always left alone: reaping it would truncate a capture the user
88
+ * asked for, and an in-flight recording is itself proof the task is in use.
89
+ */
90
+ export declare function reapAbandonedTasks(service: ReapableService, opts?: ReapOptions): Promise<ReapResult>;
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Abandoned browser-task reaper (RUSH-2622).
3
+ *
4
+ * `agents browser done` / `stop` already close a task's tabs, but agents
5
+ * routinely never call them — the run ends, the process exits, and the task's
6
+ * tabs stay open in the profile window forever. Over a day of fleet activity
7
+ * that is dozens of leftover tabs in Comet/Chrome.
8
+ *
9
+ * This closes the loop from the other end: a periodic pass that finds tasks
10
+ * nobody is driving any more and stops them.
11
+ *
12
+ * Two independent reasons, both conservative:
13
+ *
14
+ * - `session-dead` — the task recorded WHICH agent session (`sessionId`) or
15
+ * WHICH run (`launchId`) started it, and neither is alive on this host.
16
+ * That is the honest end of the task: the thing that would have called
17
+ * `done` no longer exists.
18
+ * - `idle` — nothing has touched the task for `idleMs` (default 30 minutes).
19
+ * The catch-all for a task with no recorded identity (a human running
20
+ * `agents browser start` by hand) or an agent that stalled without exiting.
21
+ *
22
+ * Closing always goes through `BrowserService.stop`, never a direct
23
+ * `Target.closeTarget`, so history, the session cache, the target cache, and
24
+ * forked-profile teardown all stay on the one code path that already handles
25
+ * them. Two things this deliberately never does: touch a tab that is not in
26
+ * `task.tabs` (a tab the user opened themselves is not ours to close), and kill
27
+ * the profile window or the browser process because one task went idle.
28
+ */
29
+ import { isPidAlive, isSessionIdLiveOnProcessTable } from '../session/active.js';
30
+ import { listPidSessionEntries, sessionIdFromLivePid } from '../session/pid-registry.js';
31
+ /** Default idle window before an untouched task is reaped. */
32
+ export const DEFAULT_IDLE_MS = 30 * 60_000;
33
+ /**
34
+ * Session and launch ids belonging to a process that is alive right now.
35
+ *
36
+ * Built from the per-pid launch registry, filtered to live pids —
37
+ * `isPidAlive(pid, startedAtMs)` rather than a bare existence check, so a pid
38
+ * the OS recycled onto an unrelated process does not read as a live agent.
39
+ * An entry with no recorded `sessionId` still contributes one when the live
40
+ * process carries `--session-id` on its argv, which is the RUSH-2384 recovery
41
+ * path the registry itself documents (pid-registry.ts:102-107).
42
+ */
43
+ export function resolveLiveIdentities(deps = {}) {
44
+ const listEntries = deps.listEntries ?? listPidSessionEntries;
45
+ const alive = deps.pidAlive ?? isPidAlive;
46
+ const sessionIdOf = deps.sessionIdOfPid ?? sessionIdFromLivePid;
47
+ const sessions = new Set();
48
+ const launches = new Set();
49
+ for (const entry of listEntries()) {
50
+ if (!alive(entry.pid, entry.startedAtMs))
51
+ continue;
52
+ const sessionId = entry.sessionId ?? sessionIdOf(entry.pid);
53
+ if (sessionId)
54
+ sessions.add(sessionId);
55
+ if (entry.launchId)
56
+ launches.add(entry.launchId);
57
+ }
58
+ return { sessions, launches };
59
+ }
60
+ /**
61
+ * True when the task's owner is PROVABLY gone. The bar is proof, not absence of
62
+ * evidence, because being wrong here closes a working agent's tabs.
63
+ *
64
+ * Only a `sessionId` can carry that proof. It has two independent sources — the
65
+ * per-pid launch registry, and a live process carrying `--session-id <id>` in
66
+ * its argv — so a session the registry missed is still caught by the process
67
+ * table. The registry misses constantly: a wrapper pid exits, a prune sweeps
68
+ * the entry, or the agent was never launched via `agents run`
69
+ * (pid-registry.ts:102-107, RUSH-2384).
70
+ *
71
+ * A `launchId` has NO second source — the registry is its only witness, and
72
+ * that witness is the unreliable one. So a task carrying only a `launchId` is
73
+ * never session-reaped, exactly like a task carrying no identity at all; both
74
+ * fall through to the idle rule. This is not a corner case: `launchId` is
75
+ * minted for every run (`exec.ts` `resolveLaunchId`) while `AGENT_SESSION_ID`
76
+ * is Claude-only and skipped on resume, so treating a missing registry entry as
77
+ * proof of death would close the tabs of every live codex/droid/grok run whose
78
+ * launch pid had already exited.
79
+ *
80
+ * A live `launchId` still RESCUES a task whose `sessionId` looks dead — proof of
81
+ * life needs only one witness, unlike proof of death.
82
+ */
83
+ export async function taskOwnerIsGone(task, live, deps = {}) {
84
+ if (!task.sessionId)
85
+ return false;
86
+ if (task.launchId && live.launches.has(task.launchId))
87
+ return false;
88
+ if (live.sessions.has(task.sessionId))
89
+ return false;
90
+ const onProcessTable = deps.sessionLiveOnProcessTable ?? ((id) => isSessionIdLiveOnProcessTable(id));
91
+ return !(await onProcessTable(task.sessionId));
92
+ }
93
+ /**
94
+ * Stop every task whose owner is gone or that has sat untouched past `idleMs`.
95
+ *
96
+ * Returns what it closed and how many it left alone. A task that is mid-
97
+ * recording is always left alone: reaping it would truncate a capture the user
98
+ * asked for, and an in-flight recording is itself proof the task is in use.
99
+ */
100
+ export async function reapAbandonedTasks(service, opts = {}) {
101
+ const idleMs = opts.idleMs ?? DEFAULT_IDLE_MS;
102
+ // Fail loud rather than reap everything. A caller-supplied `0` survives `??`
103
+ // and would close every task including one created a millisecond ago; a
104
+ // non-numeric value makes every `>=` comparison false and silently disables
105
+ // idle reaping. Both are worse than an error.
106
+ if (!Number.isFinite(idleMs) || idleMs <= 0) {
107
+ throw new Error(`idleMs must be a positive number of milliseconds, got ${String(idleMs)}`);
108
+ }
109
+ const now = opts.now ?? Date.now();
110
+ const deps = opts.deps ?? {};
111
+ const live = resolveLiveIdentities(deps);
112
+ const closed = [];
113
+ let skipped = 0;
114
+ for (const { profile, task } of service.listTasks()) {
115
+ if ((await service.recordStatus(task.name)).recording) {
116
+ skipped++;
117
+ continue;
118
+ }
119
+ let reason;
120
+ if (await taskOwnerIsGone(task, live, deps)) {
121
+ reason = 'session-dead';
122
+ }
123
+ else if (now - (task.lastActionAt ?? task.createdAt) >= idleMs) {
124
+ reason = 'idle';
125
+ }
126
+ if (!reason) {
127
+ skipped++;
128
+ continue;
129
+ }
130
+ if (opts.dryRun) {
131
+ closed.push({ task: task.name, profile, reason });
132
+ continue;
133
+ }
134
+ const result = await service.stop(task.name);
135
+ if (result.ok) {
136
+ closed.push({ task: task.name, profile: result.profile ?? profile, reason });
137
+ }
138
+ else {
139
+ // `stop` reports not-found only when the task already left the map — a
140
+ // concurrent `done`, or a profile torn down mid-pass. Nothing was closed,
141
+ // so it is not reported as closed.
142
+ skipped++;
143
+ }
144
+ }
145
+ return { closed, skipped };
146
+ }
@@ -34,9 +34,39 @@ export declare class BrowserIPCConnection {
34
34
  export declare class BrowserIPCServer {
35
35
  private server;
36
36
  private service;
37
+ /** Live client connections, so {@link stop} can end them rather than wait. */
38
+ private connections;
39
+ /** In-flight stop, so a second SIGTERM awaits the same close, never skips it. */
40
+ private stopping;
37
41
  constructor(service: BrowserService);
38
42
  start(): Promise<void>;
43
+ /**
44
+ * Stop accepting, release the binding, and only then resolve.
45
+ *
46
+ * RUSH-2421: this used to call `server.close()` and move on. `close()` is
47
+ * asynchronous — it stops accepting immediately but the binding is not
48
+ * actually released until the `'close'` event fires — so `stop()` resolved
49
+ * while the socket (or named pipe) was still held. The daemon's
50
+ * `handleShutdown` awaits this before it exits, and a successor's
51
+ * `claimDaemonInstance` waits for that exit as its proof the predecessor's
52
+ * resources are free (SING-11). Resolving early made that proof false: the
53
+ * newcomer could bind before the incumbent's binding was gone, which is the
54
+ * two-servers-on-one-socket orphan the eviction protocol exists to prevent.
55
+ *
56
+ * The first version of this waited out a timeout instead, and that made every
57
+ * graceful daemon stop escalate to `killTree`: `close()` does not complete
58
+ * while a connection is open, browser clients hold one open on purpose, and
59
+ * the timeout was set equal to the daemon's own SIGTERM grace window
60
+ * (`STOP_GRACE_MS`) — so `handleShutdown` was still inside `stop()` when
61
+ * `stopDaemon` gave up waiting and killed it. So we END the connections
62
+ * rather than wait for them: `close()` stops accepting, destroying the
63
+ * tracked sockets lets it complete, and the timeout is only a backstop for a
64
+ * socket that will not die, deliberately far below `STOP_GRACE_MS`.
65
+ */
39
66
  stop(): Promise<void>;
67
+ private doStop;
68
+ /** Release the listening binding, resolving only once it is genuinely gone. */
69
+ private closeServer;
40
70
  private handleRequest;
41
71
  }
42
72
  /**
@@ -6,6 +6,23 @@ import { getHelpersDir } from '../state.js';
6
6
  import { startDaemon, stopDaemon } from '../daemon.js';
7
7
  import { getCliVersion } from '../version.js';
8
8
  const SOCKET_NAME = 'browser.sock';
9
+ /**
10
+ * Backstop for {@link BrowserIPCServer.stop} — how long to wait for the socket
11
+ * to be released before unlinking it anyway (RUSH-2421).
12
+ *
13
+ * This MUST stay well under the daemon's SIGTERM grace window
14
+ * (`STOP_GRACE_MS`, daemon.ts). `handleShutdown` awaits `stop()`, so a close
15
+ * that takes as long as the grace window means `stopDaemon` gives up waiting
16
+ * and escalates to `killTree` — turning every graceful stop into a kill. The
17
+ * first version of this constant was set to exactly 5000, the same value as
18
+ * `STOP_GRACE_MS`, and did precisely that whenever a browser client held its
19
+ * (deliberately) long-lived connection open.
20
+ *
21
+ * With connections now ended explicitly in `closeServer`, reaching this
22
+ * timeout at all means a socket refused to die; 1.5s bounds that without
23
+ * coming near the grace window.
24
+ */
25
+ const IPC_CLOSE_TIMEOUT_MS = 1_500;
9
26
  export class BrowserDaemonNotRunningError extends Error {
10
27
  constructor() {
11
28
  super(formatBrowserDaemonNotRunningError());
@@ -153,6 +170,10 @@ export class BrowserIPCConnection {
153
170
  export class BrowserIPCServer {
154
171
  server = null;
155
172
  service;
173
+ /** Live client connections, so {@link stop} can end them rather than wait. */
174
+ connections = new Set();
175
+ /** In-flight stop, so a second SIGTERM awaits the same close, never skips it. */
176
+ stopping = null;
156
177
  constructor(service) {
157
178
  this.service = service;
158
179
  }
@@ -170,6 +191,12 @@ export class BrowserIPCServer {
170
191
  }
171
192
  }
172
193
  this.server = net.createServer((socket) => {
194
+ // `net.Server.close()` stops accepting but does not complete until every
195
+ // EXISTING connection ends, and clients here hold one open on purpose
196
+ // ("the process and socket stay warm between actions"). Tracking them is
197
+ // what lets stop() end them deliberately instead of waiting out a timeout.
198
+ this.connections.add(socket);
199
+ socket.on('close', () => this.connections.delete(socket));
173
200
  let buffer = '';
174
201
  socket.on('data', async (data) => {
175
202
  buffer += data.toString();
@@ -229,11 +256,38 @@ export class BrowserIPCServer {
229
256
  });
230
257
  });
231
258
  }
259
+ /**
260
+ * Stop accepting, release the binding, and only then resolve.
261
+ *
262
+ * RUSH-2421: this used to call `server.close()` and move on. `close()` is
263
+ * asynchronous — it stops accepting immediately but the binding is not
264
+ * actually released until the `'close'` event fires — so `stop()` resolved
265
+ * while the socket (or named pipe) was still held. The daemon's
266
+ * `handleShutdown` awaits this before it exits, and a successor's
267
+ * `claimDaemonInstance` waits for that exit as its proof the predecessor's
268
+ * resources are free (SING-11). Resolving early made that proof false: the
269
+ * newcomer could bind before the incumbent's binding was gone, which is the
270
+ * two-servers-on-one-socket orphan the eviction protocol exists to prevent.
271
+ *
272
+ * The first version of this waited out a timeout instead, and that made every
273
+ * graceful daemon stop escalate to `killTree`: `close()` does not complete
274
+ * while a connection is open, browser clients hold one open on purpose, and
275
+ * the timeout was set equal to the daemon's own SIGTERM grace window
276
+ * (`STOP_GRACE_MS`) — so `handleShutdown` was still inside `stop()` when
277
+ * `stopDaemon` gave up waiting and killed it. So we END the connections
278
+ * rather than wait for them: `close()` stops accepting, destroying the
279
+ * tracked sockets lets it complete, and the timeout is only a backstop for a
280
+ * socket that will not die, deliberately far below `STOP_GRACE_MS`.
281
+ */
232
282
  async stop() {
233
- if (this.server) {
234
- this.server.close();
235
- this.server = null;
236
- }
283
+ // A second SIGTERM must await the SAME stop, not skip past it because
284
+ // `this.server` was already nulled by the first.
285
+ if (!this.stopping)
286
+ this.stopping = this.doStop();
287
+ return this.stopping;
288
+ }
289
+ async doStop() {
290
+ await this.closeServer();
237
291
  if (!IS_WINDOWS) {
238
292
  const socketPath = getSocketPath();
239
293
  if (fs.existsSync(socketPath)) {
@@ -242,6 +296,28 @@ export class BrowserIPCServer {
242
296
  }
243
297
  await this.service.shutdown();
244
298
  }
299
+ /** Release the listening binding, resolving only once it is genuinely gone. */
300
+ closeServer() {
301
+ const server = this.server;
302
+ this.server = null;
303
+ if (!server)
304
+ return Promise.resolve();
305
+ return new Promise((resolve) => {
306
+ const done = () => { clearTimeout(timer); resolve(); };
307
+ // Backstop only, for a socket that refuses to die. Far below the daemon's
308
+ // SIGTERM grace window so a slow close can never be what gets the daemon
309
+ // killed — the whole point is that the graceful path stays graceful.
310
+ const timer = setTimeout(done, IPC_CLOSE_TIMEOUT_MS);
311
+ // `close()` reports an error when the server was never listening —
312
+ // nothing to release, so that is a completed close, not a failure.
313
+ server.close(() => done());
314
+ // `close()` waits for existing connections; ending them is what lets it
315
+ // finish now instead of at the timeout.
316
+ for (const socket of this.connections)
317
+ socket.destroy();
318
+ this.connections.clear();
319
+ });
320
+ }
245
321
  async handleRequest(request) {
246
322
  if (request.action === 'upload-stage') {
247
323
  const source = request.source;
@@ -264,8 +340,10 @@ export class BrowserIPCServer {
264
340
  url: request.url,
265
341
  endpointName: request.endpoint,
266
342
  skipDomainSkill: request.skipDomainSkill,
343
+ fresh: request.fresh,
267
344
  actor: request.actor,
268
345
  launchId: request.launchId,
346
+ sessionId: request.sessionId,
269
347
  });
270
348
  return {
271
349
  ok: true,
@@ -275,6 +353,17 @@ export class BrowserIPCServer {
275
353
  skill: result.skill,
276
354
  };
277
355
  }
356
+ // The out-of-process seam onto the abandoned-task reaper: the daemon
357
+ // owns the live BrowserService, so a CLI verb (`agents browser gc`) can
358
+ // only reach `reapAbandoned` through IPC. The daemon's own periodic tick
359
+ // calls the service method directly.
360
+ case 'gc': {
361
+ const reaped = await this.service.reapAbandoned({
362
+ idleMs: request.idleMinutes !== undefined ? request.idleMinutes * 60_000 : undefined,
363
+ dryRun: request.dryRun,
364
+ });
365
+ return { ok: true, reaped };
366
+ }
278
367
  case 'done': {
279
368
  if (!request.task) {
280
369
  return { ok: false, error: 'Task required' };
@@ -5,8 +5,9 @@ export declare const DEFAULT_BROWSER_PROFILE_NAME = "default";
5
5
  * The device-local configured default profile name (set via
6
6
  * `agents browser profiles set-default`), or undefined when unset. When set, it
7
7
  * is the profile `agents browser start` resolves to for BOTH the no-`--profile`
8
- * path and an explicit `--profile default`. Stored per-machine see
9
- * `Meta.defaultBrowserProfile`.
8
+ * path and an explicit `--profile default`. Stored as this machine's
9
+ * `browser.profile` device-config key (central `fleet.devices.<name>.config`
10
+ * block — see lib/device-config.ts).
10
11
  */
11
12
  export declare function getConfiguredDefaultProfileName(): string | undefined;
12
13
  export declare function getBrowserRuntimeDir(): string;
@@ -1,17 +1,33 @@
1
1
  import * as path from 'path';
2
- import { getBrowserRuntimeDir as getBrowserRuntimeDirRoot, readMeta, writeMeta, } from '../state.js';
2
+ import { getBrowserRuntimeDir as getBrowserRuntimeDirRoot, readMeta, updateMeta, } from '../state.js';
3
+ import { getConfigValue } from '../device-config.js';
3
4
  import { findBrowserPath, findFirstInstalledBrowser, isPortInUse } from './chrome.js';
4
5
  import { DEFAULT_VIEWPORT } from './devices.js';
5
6
  export const DEFAULT_BROWSER_PROFILE_NAME = 'default';
7
+ /**
8
+ * True for a profile whose contents only make sense on THIS machine, so it must
9
+ * live in the per-machine doc rather than the fleet-synced agents.yaml.
10
+ *
11
+ * Only the auto-detected `default` qualifies: it is generated by probing local
12
+ * browser installs and a free local port, so its `binary:` is an absolute path
13
+ * for one OS. Shared fleet-wide it caused a permanent rewrite loop — a macOS box
14
+ * wrote `/Applications/...`, a Linux box rewrote `/usr/bin/chromium-browser`,
15
+ * each on every `agents browser start`. Named profiles a user created are real
16
+ * fleet config and keep syncing.
17
+ */
18
+ function isMachineLocalProfile(name) {
19
+ return name === DEFAULT_BROWSER_PROFILE_NAME;
20
+ }
6
21
  /**
7
22
  * The device-local configured default profile name (set via
8
23
  * `agents browser profiles set-default`), or undefined when unset. When set, it
9
24
  * is the profile `agents browser start` resolves to for BOTH the no-`--profile`
10
- * path and an explicit `--profile default`. Stored per-machine see
11
- * `Meta.defaultBrowserProfile`.
25
+ * path and an explicit `--profile default`. Stored as this machine's
26
+ * `browser.profile` device-config key (central `fleet.devices.<name>.config`
27
+ * block — see lib/device-config.ts).
12
28
  */
13
29
  export function getConfiguredDefaultProfileName() {
14
- return readMeta().defaultBrowserProfile || undefined;
30
+ return getConfigValue('browser.profile').value || undefined;
15
31
  }
16
32
  export function getBrowserRuntimeDir() {
17
33
  return getBrowserRuntimeDirRoot();
@@ -82,15 +98,22 @@ function profileToConfig(profile) {
82
98
  config.logHost = profile.logHost;
83
99
  return config;
84
100
  }
85
- export async function listProfiles() {
101
+ /**
102
+ * Every profile visible on this machine: the fleet-shared ones plus this
103
+ * machine's own. A machine-local entry WINS on a name collision — it was written
104
+ * by this box for this box, whereas the shared copy may carry another OS's
105
+ * binary path (that mismatch is exactly why `default` moved local).
106
+ */
107
+ function allProfileConfigs() {
86
108
  const meta = readMeta();
87
- if (!meta.browser)
88
- return [];
89
- return Object.entries(meta.browser).map(([name, config]) => configToProfile(name, config));
109
+ return { ...meta.browser, ...meta.deviceBrowser };
110
+ }
111
+ export async function listProfiles() {
112
+ const configs = allProfileConfigs();
113
+ return Object.entries(configs).map(([name, config]) => configToProfile(name, config));
90
114
  }
91
115
  export async function getProfile(name) {
92
- const meta = readMeta();
93
- const config = meta.browser?.[name];
116
+ const config = allProfileConfigs()[name];
94
117
  if (!config)
95
118
  return null;
96
119
  return configToProfile(name, config);
@@ -271,7 +294,8 @@ function hasSshEndpoint(endpoints) {
271
294
  }
272
295
  export async function createProfile(profile) {
273
296
  const meta = readMeta();
274
- if (meta.browser?.[profile.name]) {
297
+ const existingConfigs = allProfileConfigs();
298
+ if (existingConfigs[profile.name]) {
275
299
  throw new Error(`Profile "${profile.name}" already exists`);
276
300
  }
277
301
  // Collision check. Every CDP/SSH profile ends up listening on (or
@@ -280,8 +304,8 @@ export async function createProfile(profile) {
280
304
  // longer need to scope by host. Two profiles that would need the same
281
305
  // local port can't both run at the same time.
282
306
  const newLocal = effectiveLocalPort(profile);
283
- if (newLocal !== undefined && meta.browser) {
284
- for (const [existingName, existingConfig] of Object.entries(meta.browser)) {
307
+ if (newLocal !== undefined) {
308
+ for (const [existingName, existingConfig] of Object.entries(existingConfigs)) {
285
309
  const existingProfile = configToProfile(existingName, existingConfig);
286
310
  const existingLocal = effectiveLocalPort(existingProfile);
287
311
  if (existingLocal === newLocal) {
@@ -302,25 +326,44 @@ export async function createProfile(profile) {
302
326
  if (!hasSshEndpoint(profile.endpoints)) {
303
327
  findBrowserPath(profile.browser, profile.binary);
304
328
  }
305
- meta.browser = meta.browser ?? {};
306
- meta.browser[profile.name] = profileToConfig(profile);
307
- writeMeta(meta);
329
+ const config = profileToConfig(profile);
330
+ if (isMachineLocalProfile(profile.name)) {
331
+ updateMeta((m) => ({ ...m, deviceBrowser: { ...m.deviceBrowser, [profile.name]: config } }));
332
+ return;
333
+ }
334
+ updateMeta((m) => ({ ...m, browser: { ...m.browser, [profile.name]: config } }));
308
335
  }
309
336
  export async function updateProfile(profile) {
310
337
  const meta = readMeta();
311
- if (!meta.browser?.[profile.name]) {
338
+ const local = meta.deviceBrowser?.[profile.name];
339
+ if (!local && !meta.browser?.[profile.name]) {
312
340
  throw new Error(`Profile "${profile.name}" does not exist`);
313
341
  }
314
- meta.browser[profile.name] = profileToConfig(profile);
315
- writeMeta(meta);
342
+ const config = profileToConfig(profile);
343
+ // A profile that already lives machine-locally stays there; so does the auto
344
+ // `default`, whose binary path and port are specific to this machine.
345
+ if (local || isMachineLocalProfile(profile.name)) {
346
+ updateMeta((m) => ({ ...m, deviceBrowser: { ...m.deviceBrowser, [profile.name]: config } }));
347
+ return;
348
+ }
349
+ updateMeta((m) => ({ ...m, browser: { ...m.browser, [profile.name]: config } }));
316
350
  }
317
351
  export async function deleteProfile(name) {
318
352
  const meta = readMeta();
319
- if (!meta.browser?.[name]) {
353
+ const isLocal = !!meta.deviceBrowser?.[name];
354
+ if (!isLocal && !meta.browser?.[name]) {
320
355
  throw new Error(`Profile "${name}" does not exist`);
321
356
  }
322
- delete meta.browser[name];
323
- writeMeta(meta);
357
+ updateMeta((m) => {
358
+ if (isLocal) {
359
+ const next = { ...m.deviceBrowser };
360
+ delete next[name];
361
+ return { ...m, deviceBrowser: Object.keys(next).length > 0 ? next : undefined };
362
+ }
363
+ const next = { ...m.browser };
364
+ delete next[name];
365
+ return { ...m, browser: Object.keys(next).length > 0 ? next : undefined };
366
+ });
324
367
  }
325
368
  /**
326
369
  * Resolve a profile's endpoint presets into a normalized map regardless of