@phnx-labs/agents-cli 1.22.39 → 1.22.41

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 (569) hide show
  1. package/CHANGELOG.md +316 -0
  2. package/README.md +116 -70
  3. package/dist/bin/agents +0 -0
  4. package/dist/bootstrap.js +45 -27
  5. package/dist/browser.js +2 -2
  6. package/dist/cli/command-registry.d.ts +135 -0
  7. package/dist/cli/command-registry.js +259 -0
  8. package/dist/commands/accounts.d.ts +20 -0
  9. package/dist/commands/accounts.js +186 -25
  10. package/dist/commands/apply.d.ts +3 -3
  11. package/dist/commands/apply.js +20 -63
  12. package/dist/commands/browser.d.ts +15 -0
  13. package/dist/commands/browser.js +318 -131
  14. package/dist/commands/budget.d.ts +7 -6
  15. package/dist/commands/budget.js +8 -7
  16. package/dist/commands/cloud.js +17 -20
  17. package/dist/commands/commands.js +1 -1
  18. package/dist/commands/computer-actions.d.ts +1 -35
  19. package/dist/commands/computer-actions.js +13 -181
  20. package/dist/commands/computer.d.ts +28 -3
  21. package/dist/commands/computer.js +93 -65
  22. package/dist/commands/config.js +16 -3
  23. package/dist/commands/cost.d.ts +2 -1
  24. package/dist/commands/cost.js +7 -6
  25. package/dist/commands/daemon.js +151 -4
  26. package/dist/commands/detach.js +1 -1
  27. package/dist/commands/doctor.js +19 -11
  28. package/dist/commands/events.js +1 -1
  29. package/dist/commands/exec.d.ts +2 -50
  30. package/dist/commands/exec.js +160 -215
  31. package/dist/commands/factory.js +1 -1
  32. package/dist/commands/feed.d.ts +2 -2
  33. package/dist/commands/feed.js +12 -23
  34. package/dist/commands/fleet-capture.js +25 -27
  35. package/dist/commands/focus.d.ts +2 -3
  36. package/dist/commands/focus.js +34 -9
  37. package/dist/commands/go.js +15 -5
  38. package/dist/commands/harness.d.ts +3 -3
  39. package/dist/commands/harness.js +53 -5
  40. package/dist/commands/hooks.js +1 -1
  41. package/dist/commands/import.js +2 -2
  42. package/dist/commands/insights.d.ts +5 -5
  43. package/dist/commands/insights.js +15 -9
  44. package/dist/commands/inspect.d.ts +2 -3
  45. package/dist/commands/inspect.js +4 -16
  46. package/dist/commands/logs.d.ts +2 -2
  47. package/dist/commands/logs.js +9 -9
  48. package/dist/commands/mcp.js +2 -2
  49. package/dist/commands/memory.js +1 -1
  50. package/dist/commands/message.d.ts +1 -1
  51. package/dist/commands/message.js +7 -7
  52. package/dist/commands/mine.d.ts +11 -8
  53. package/dist/commands/mine.js +11 -10
  54. package/dist/commands/models.js +1 -1
  55. package/dist/commands/modes.js +1 -1
  56. package/dist/commands/monitors.js +23 -6
  57. package/dist/commands/output.d.ts +7 -6
  58. package/dist/commands/output.js +11 -10
  59. package/dist/commands/packages.js +4 -4
  60. package/dist/commands/perf.js +1 -1
  61. package/dist/commands/permissions.js +49 -32
  62. package/dist/commands/plugins.d.ts +1 -1
  63. package/dist/commands/plugins.js +3 -3
  64. package/dist/commands/profiles.d.ts +6 -9
  65. package/dist/commands/profiles.js +8 -321
  66. package/dist/commands/projects.d.ts +9 -0
  67. package/dist/commands/projects.js +140 -8
  68. package/dist/commands/prune.js +5 -13
  69. package/dist/commands/refresh-rules.js +1 -1
  70. package/dist/commands/repo.d.ts +5 -7
  71. package/dist/commands/repo.js +34 -18
  72. package/dist/commands/resource-view.js +4 -0
  73. package/dist/commands/resume.d.ts +11 -7
  74. package/dist/commands/resume.js +82 -98
  75. package/dist/commands/route.d.ts +12 -0
  76. package/dist/commands/route.js +261 -0
  77. package/dist/commands/routines.d.ts +2 -3
  78. package/dist/commands/routines.js +120 -145
  79. package/dist/commands/rules.js +1 -1
  80. package/dist/commands/run-account-picker.d.ts +1 -1
  81. package/dist/commands/run-account-picker.js +2 -2
  82. package/dist/commands/run-broadcast.d.ts +21 -0
  83. package/dist/commands/run-broadcast.js +89 -0
  84. package/dist/commands/run-cloud.js +1 -1
  85. package/dist/commands/secrets-vault.d.ts +10 -0
  86. package/dist/commands/{login.js → secrets-vault.js} +34 -14
  87. package/dist/commands/secrets.d.ts +4 -4
  88. package/dist/commands/secrets.js +158 -159
  89. package/dist/commands/sessions-backfill.js +1 -1
  90. package/dist/commands/sessions-browser.d.ts +1 -1
  91. package/dist/commands/sessions-browser.js +9 -5
  92. package/dist/commands/sessions-export.js +2 -2
  93. package/dist/commands/sessions-inject.js +4 -5
  94. package/dist/commands/sessions-migrate.js +16 -27
  95. package/dist/commands/sessions-picker.d.ts +14 -0
  96. package/dist/commands/sessions-picker.js +209 -13
  97. package/dist/commands/sessions-resume.d.ts +7 -3
  98. package/dist/commands/sessions-resume.js +81 -33
  99. package/dist/commands/sessions-share.d.ts +25 -0
  100. package/dist/commands/sessions-share.js +166 -0
  101. package/dist/commands/sessions-stats.js +2 -2
  102. package/dist/commands/sessions.d.ts +75 -68
  103. package/dist/commands/sessions.js +144 -180
  104. package/dist/commands/setup-browser.js +1 -1
  105. package/dist/commands/setup-computer.js +1 -1
  106. package/dist/commands/setup-fleet.js +3 -0
  107. package/dist/commands/setup-mine.d.ts +7 -3
  108. package/dist/commands/setup-mine.js +13 -8
  109. package/dist/commands/setup-preferences.js +2 -2
  110. package/dist/commands/setup-secrets.js +2 -2
  111. package/dist/commands/setup-watchdog.js +2 -2
  112. package/dist/commands/setup.js +7 -2
  113. package/dist/commands/share.d.ts +65 -0
  114. package/dist/commands/share.js +253 -20
  115. package/dist/commands/skills.js +3 -3
  116. package/dist/commands/snapshot.d.ts +2 -2
  117. package/dist/commands/snapshot.js +9 -9
  118. package/dist/commands/ssh.d.ts +1 -1
  119. package/dist/commands/ssh.js +186 -64
  120. package/dist/commands/subagents.js +1 -1
  121. package/dist/commands/sync.js +24 -9
  122. package/dist/commands/teams.d.ts +2 -2
  123. package/dist/commands/teams.js +9 -18
  124. package/dist/commands/tmux.js +5 -1
  125. package/dist/commands/trash.js +1 -1
  126. package/dist/commands/usage.d.ts +1 -1
  127. package/dist/commands/usage.js +2 -2
  128. package/dist/commands/utils.d.ts +4 -14
  129. package/dist/commands/utils.js +6 -27
  130. package/dist/commands/versions.js +2 -2
  131. package/dist/commands/view.d.ts +9 -77
  132. package/dist/commands/view.js +23 -8
  133. package/dist/commands/watchdog.js +1 -1
  134. package/dist/commands/webhook.js +25 -34
  135. package/dist/commands/workflows.js +1 -1
  136. package/dist/index.js +1 -1
  137. package/dist/lib/account-capabilities.d.ts +39 -0
  138. package/dist/lib/account-capabilities.js +59 -0
  139. package/dist/lib/account-catalog.js +1 -1
  140. package/dist/lib/account-registry.d.ts +66 -0
  141. package/dist/lib/account-registry.js +151 -5
  142. package/dist/lib/{rotate.d.ts → accounting/rotate.d.ts} +4 -4
  143. package/dist/lib/{rotate.js → accounting/rotate.js} +8 -8
  144. package/dist/lib/{usage.d.ts → accounting/usage.d.ts} +4 -4
  145. package/dist/lib/{usage.js → accounting/usage.js} +11 -11
  146. package/dist/lib/agent-spec/provider.js +1 -1
  147. package/dist/lib/agents.d.ts +72 -4
  148. package/dist/lib/agents.js +231 -199
  149. package/dist/lib/answer-router.d.ts +2 -2
  150. package/dist/lib/ask-classifier.d.ts +1 -1
  151. package/dist/lib/ask-classifier.js +1 -1
  152. package/dist/lib/audit/log.js +1 -1
  153. package/dist/lib/auth-health.d.ts +1 -1
  154. package/dist/lib/auth-health.js +2 -2
  155. package/dist/lib/brand.js +1 -1
  156. package/dist/lib/browser/caller-identity.d.ts +28 -0
  157. package/dist/lib/browser/caller-identity.js +156 -0
  158. package/dist/lib/browser/cdp.js +5 -1
  159. package/dist/lib/browser/chrome.js +12 -1
  160. package/dist/lib/browser/drivers/ssh.d.ts +1 -1
  161. package/dist/lib/browser/drivers/ssh.js +3 -2
  162. package/dist/lib/browser/hygiene.d.ts +10 -2
  163. package/dist/lib/browser/hygiene.js +7 -4
  164. package/dist/lib/browser/ipc.d.ts +22 -2
  165. package/dist/lib/browser/ipc.js +185 -21
  166. package/dist/lib/browser/profiles.d.ts +69 -5
  167. package/dist/lib/browser/profiles.js +135 -13
  168. package/dist/lib/browser/remote-control.d.ts +2 -2
  169. package/dist/lib/browser/remote-control.js +3 -3
  170. package/dist/lib/browser/runtime-state.d.ts +79 -0
  171. package/dist/lib/browser/runtime-state.js +119 -1
  172. package/dist/lib/browser/service.d.ts +137 -2
  173. package/dist/lib/browser/service.js +492 -54
  174. package/dist/lib/browser/sessions-list.d.ts +2 -1
  175. package/dist/lib/browser/sessions-list.js +4 -13
  176. package/dist/lib/browser/types.d.ts +19 -1
  177. package/dist/lib/budget/config.js +1 -1
  178. package/dist/lib/budget/ledger.js +1 -1
  179. package/dist/lib/byok-usage.js +1 -1
  180. package/dist/lib/capabilities.js +3 -15
  181. package/dist/lib/catchup.d.ts +1 -1
  182. package/dist/lib/catchup.js +1 -1
  183. package/dist/lib/cloud/dispatch.js +1 -1
  184. package/dist/lib/cloud/host.d.ts +3 -3
  185. package/dist/lib/cloud/host.js +5 -5
  186. package/dist/lib/cloud/rush.d.ts +9 -21
  187. package/dist/lib/cloud/rush.js +33 -120
  188. package/dist/lib/cloud/types.d.ts +2 -2
  189. package/dist/lib/commands.js +3 -13
  190. package/dist/lib/computer/actions.d.ts +36 -0
  191. package/dist/lib/computer/actions.js +162 -0
  192. package/dist/lib/computer/dispatch.d.ts +2 -2
  193. package/dist/lib/computer/dispatch.js +2 -2
  194. package/dist/lib/computer/sessions-list.d.ts +3 -3
  195. package/dist/lib/computer/sessions-list.js +2 -2
  196. package/dist/lib/config-keys.d.ts +2 -1
  197. package/dist/lib/config-keys.js +8 -4
  198. package/dist/lib/config-machine-keys.d.ts +4 -2
  199. package/dist/lib/config-machine-keys.js +5 -2
  200. package/dist/lib/crabbox/lease.js +9 -1
  201. package/dist/lib/crabbox/runtimes.d.ts +32 -7
  202. package/dist/lib/crabbox/runtimes.js +72 -30
  203. package/dist/lib/{daemon.d.ts → daemon/daemon.d.ts} +59 -1
  204. package/dist/lib/{daemon.js → daemon/daemon.js} +237 -49
  205. package/dist/lib/daemon-services.d.ts +1 -1
  206. package/dist/lib/daemon-services.js +5 -0
  207. package/dist/lib/daemon-ticks.d.ts +25 -0
  208. package/dist/lib/daemon-ticks.js +31 -1
  209. package/dist/lib/daemon-webhooks.d.ts +104 -0
  210. package/dist/lib/daemon-webhooks.js +215 -0
  211. package/dist/lib/device-config.d.ts +85 -75
  212. package/dist/lib/device-config.js +280 -142
  213. package/dist/lib/devices/config-migration.d.ts +29 -34
  214. package/dist/lib/devices/config-migration.js +209 -130
  215. package/dist/lib/devices/connect.d.ts +2 -2
  216. package/dist/lib/devices/connect.js +2 -2
  217. package/dist/lib/devices/discovery-policy.d.ts +30 -0
  218. package/dist/lib/devices/discovery-policy.js +101 -0
  219. package/dist/lib/devices/fleet-inventory.js +8 -3
  220. package/dist/lib/devices/harness-inventory.d.ts +1 -1
  221. package/dist/lib/devices/harness-inventory.js +12 -3
  222. package/dist/lib/devices/known-hosts.js +1 -1
  223. package/dist/lib/devices/pool.d.ts +10 -5
  224. package/dist/lib/devices/pool.js +5 -7
  225. package/dist/lib/devices/registry.d.ts +1 -1
  226. package/dist/lib/devices/registry.js +1 -1
  227. package/dist/lib/devices/resolve-profile.d.ts +2 -2
  228. package/dist/lib/devices/resolve-profile.js +2 -2
  229. package/dist/lib/devices/resolve-target.d.ts +1 -1
  230. package/dist/lib/devices/resolve-target.js +4 -4
  231. package/dist/lib/devices/self-host.d.ts +1 -1
  232. package/dist/lib/devices/self-host.js +3 -3
  233. package/dist/lib/devices/ssh-config.d.ts +1 -1
  234. package/dist/lib/devices/stats-cache.js +1 -1
  235. package/dist/lib/devices/sync.d.ts +3 -1
  236. package/dist/lib/devices/sync.js +3 -3
  237. package/dist/lib/doctor-diff.js +4 -4
  238. package/dist/lib/drift-sync.js +2 -2
  239. package/dist/lib/drift.js +2 -2
  240. package/dist/lib/event-families.d.ts +1 -1
  241. package/dist/lib/event-stream.d.ts +1 -1
  242. package/dist/lib/event-stream.js +2 -2
  243. package/dist/lib/events-ingest.js +2 -2
  244. package/dist/lib/exec.d.ts +4 -4
  245. package/dist/lib/exec.js +11 -11
  246. package/dist/lib/factory/snapshot.d.ts +1 -1
  247. package/dist/lib/factory/snapshot.js +1 -1
  248. package/dist/lib/{activity.d.ts → feed/activity.d.ts} +1 -1
  249. package/dist/lib/{activity.js → feed/activity.js} +7 -7
  250. package/dist/lib/{events.d.ts → feed/events.d.ts} +2 -2
  251. package/dist/lib/{events.js → feed/events.js} +5 -5
  252. package/dist/lib/{feed.js → feed/feed.js} +4 -4
  253. package/dist/lib/feed-outcome.d.ts +1 -1
  254. package/dist/lib/feed-policy.d.ts +1 -1
  255. package/dist/lib/feed-policy.js +1 -1
  256. package/dist/lib/feed-post.d.ts +1 -1
  257. package/dist/lib/feed-post.js +1 -1
  258. package/dist/lib/feed-ranking.d.ts +1 -1
  259. package/dist/lib/fleet/apply.d.ts +0 -6
  260. package/dist/lib/fleet/apply.js +18 -45
  261. package/dist/lib/fleet/auth-sync.d.ts +16 -35
  262. package/dist/lib/fleet/auth-sync.js +16 -75
  263. package/dist/lib/fleet/capture.js +8 -6
  264. package/dist/lib/fleet/manifest.js +23 -11
  265. package/dist/lib/fleet/types.d.ts +19 -7
  266. package/dist/lib/fleet-cache.d.ts +1 -1
  267. package/dist/lib/fleet-cache.js +1 -1
  268. package/dist/lib/format.d.ts +19 -0
  269. package/dist/lib/format.js +44 -1
  270. package/dist/lib/friction-heuristics.d.ts +1 -1
  271. package/dist/lib/git.d.ts +24 -1
  272. package/dist/lib/git.js +82 -38
  273. package/dist/lib/heal.js +3 -3
  274. package/dist/lib/hooks.d.ts +18 -0
  275. package/dist/lib/hooks.js +113 -1
  276. package/dist/lib/hosts/credentials.d.ts +30 -10
  277. package/dist/lib/hosts/credentials.js +38 -26
  278. package/dist/lib/hosts/dispatch.d.ts +3 -3
  279. package/dist/lib/hosts/dispatch.js +3 -3
  280. package/dist/lib/hosts/option.d.ts +3 -3
  281. package/dist/lib/hosts/option.js +7 -8
  282. package/dist/lib/hosts/passthrough.d.ts +17 -17
  283. package/dist/lib/hosts/passthrough.js +67 -50
  284. package/dist/lib/hosts/providers/devices.d.ts +1 -1
  285. package/dist/lib/hosts/providers/devices.js +1 -1
  286. package/dist/lib/hosts/reconcile.d.ts +1 -1
  287. package/dist/lib/hosts/reconcile.js +1 -1
  288. package/dist/lib/hosts/reconnect.d.ts +1 -1
  289. package/dist/lib/hosts/reconnect.js +2 -2
  290. package/dist/lib/hosts/registry.d.ts +5 -5
  291. package/dist/lib/hosts/registry.js +5 -5
  292. package/dist/lib/hosts/remote-cmd.d.ts +10 -10
  293. package/dist/lib/hosts/remote-cmd.js +25 -18
  294. package/dist/lib/hosts/remote-os.d.ts +4 -4
  295. package/dist/lib/hosts/remote-os.js +4 -4
  296. package/dist/lib/hosts/remote-session-id.d.ts +2 -2
  297. package/dist/lib/hosts/remote-session-id.js +2 -2
  298. package/dist/lib/hosts/routing-flag.d.ts +3 -3
  299. package/dist/lib/hosts/routing-flag.js +4 -5
  300. package/dist/lib/hosts/run-target.d.ts +4 -4
  301. package/dist/lib/hosts/run-target.js +4 -4
  302. package/dist/lib/hosts/session-index.d.ts +2 -2
  303. package/dist/lib/hosts/session-index.js +1 -1
  304. package/dist/lib/hosts/session-marker.d.ts +1 -1
  305. package/dist/lib/hosts/session-marker.js +1 -1
  306. package/dist/lib/hosts/types.d.ts +2 -2
  307. package/dist/lib/hosts/types.js +2 -2
  308. package/dist/lib/import.js +1 -1
  309. package/dist/lib/{migrate.d.ts → installations/migrate.d.ts} +13 -1
  310. package/dist/lib/{migrate.js → installations/migrate.js} +160 -40
  311. package/dist/lib/installations/resolve.js +1 -1
  312. package/dist/lib/installations/strategies.js +1 -1
  313. package/dist/lib/installations/update.js +2 -2
  314. package/dist/lib/{versions.d.ts → installations/versions.d.ts} +12 -3
  315. package/dist/lib/{versions.js → installations/versions.js} +51 -31
  316. package/dist/lib/isolation-boundary-report.js +1 -1
  317. package/dist/lib/linear-projects.d.ts +28 -0
  318. package/dist/lib/linear-projects.js +30 -0
  319. package/dist/lib/loop.js +1 -1
  320. package/dist/lib/mailbox-gc.js +1 -1
  321. package/dist/lib/mailbox.js +1 -1
  322. package/dist/lib/mcp-registry.d.ts +62 -0
  323. package/dist/lib/mcp-registry.js +177 -0
  324. package/dist/lib/mcp.d.ts +5 -0
  325. package/dist/lib/mcp.js +124 -307
  326. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  327. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  328. package/dist/lib/menubar/install-menubar.d.ts +7 -0
  329. package/dist/lib/menubar/install-menubar.js +24 -6
  330. package/dist/lib/menubar/snapshot.d.ts +7 -0
  331. package/dist/lib/menubar/snapshot.js +8 -6
  332. package/dist/lib/models.js +1 -1
  333. package/dist/lib/monitors/config.d.ts +12 -0
  334. package/dist/lib/monitors/config.js +3 -0
  335. package/dist/lib/monitors/dispatch.js +26 -5
  336. package/dist/lib/monitors/engine.js +10 -0
  337. package/dist/lib/monitors/sources/webhook.d.ts +1 -1
  338. package/dist/lib/monitors/state.d.ts +42 -0
  339. package/dist/lib/monitors/state.js +30 -0
  340. package/dist/lib/notify.d.ts +1 -1
  341. package/dist/lib/observe-aliases.d.ts +3 -3
  342. package/dist/lib/observe-aliases.js +3 -21
  343. package/dist/lib/output/git-output.js +2 -2
  344. package/dist/lib/overdue.d.ts +1 -1
  345. package/dist/lib/overdue.js +1 -1
  346. package/dist/lib/permissions-registry.d.ts +65 -0
  347. package/dist/lib/permissions-registry.js +599 -0
  348. package/dist/lib/permissions.d.ts +24 -13
  349. package/dist/lib/permissions.js +52 -271
  350. package/dist/lib/picker.d.ts +16 -0
  351. package/dist/lib/picker.js +33 -3
  352. package/dist/lib/placement.d.ts +1 -1
  353. package/dist/lib/placement.js +4 -4
  354. package/dist/lib/platform/paths.d.ts +8 -0
  355. package/dist/lib/platform/paths.js +11 -0
  356. package/dist/lib/platform/posixpath.js +3 -3
  357. package/dist/lib/{plugin-marketplace.d.ts → plugins/plugin-marketplace.d.ts} +1 -1
  358. package/dist/lib/{plugin-marketplace.js → plugins/plugin-marketplace.js} +2 -2
  359. package/dist/lib/{plugins.d.ts → plugins/plugins.d.ts} +1 -1
  360. package/dist/lib/{plugins.js → plugins/plugins.js} +7 -7
  361. package/dist/lib/{skills.d.ts → plugins/skills.d.ts} +1 -1
  362. package/dist/lib/{skills.js → plugins/skills.js} +7 -7
  363. package/dist/lib/pricing/cost.d.ts +1 -1
  364. package/dist/lib/pricing/cost.js +1 -1
  365. package/dist/lib/profiles-presets.d.ts +1 -1
  366. package/dist/lib/profiles-presets.js +1 -1
  367. package/dist/lib/profiles.js +2 -2
  368. package/dist/lib/project-import.js +4 -1
  369. package/dist/lib/project-key.js +7 -0
  370. package/dist/lib/project-launch.d.ts +1 -1
  371. package/dist/lib/project-launch.js +3 -3
  372. package/dist/lib/project-pull.d.ts +128 -0
  373. package/dist/lib/project-pull.js +384 -0
  374. package/dist/lib/project-root.d.ts +2 -2
  375. package/dist/lib/project-root.js +2 -2
  376. package/dist/lib/project-status.js +1 -1
  377. package/dist/lib/projects.d.ts +23 -0
  378. package/dist/lib/projects.js +29 -6
  379. package/dist/lib/refresh.d.ts +12 -1
  380. package/dist/lib/refresh.js +16 -9
  381. package/dist/lib/registry.d.ts +9 -2
  382. package/dist/lib/registry.js +11 -4
  383. package/dist/lib/resources.d.ts +2 -2
  384. package/dist/lib/resources.js +2 -2
  385. package/dist/lib/routers.d.ts +81 -0
  386. package/dist/lib/routers.js +153 -0
  387. package/dist/lib/routine-context.d.ts +1 -0
  388. package/dist/lib/routine-context.js +7 -2
  389. package/dist/lib/routine-notify-owner.d.ts +1 -1
  390. package/dist/lib/routine-notify.d.ts +1 -1
  391. package/dist/lib/routine-notify.js +1 -1
  392. package/dist/lib/routine-process-cleanup.d.ts +1 -1
  393. package/dist/lib/routine-readiness.d.ts +1 -1
  394. package/dist/lib/routine-readiness.js +11 -4
  395. package/dist/lib/routines-placement.d.ts +12 -2
  396. package/dist/lib/routines-placement.js +20 -3
  397. package/dist/lib/routines-project.d.ts +1 -1
  398. package/dist/lib/routines-project.js +1 -1
  399. package/dist/lib/rules/compile.d.ts +1 -0
  400. package/dist/lib/rules/compile.js +10 -2
  401. package/dist/lib/rules/rules.js +1 -1
  402. package/dist/lib/run-notify.js +1 -1
  403. package/dist/lib/runner.d.ts +80 -2
  404. package/dist/lib/runner.js +261 -67
  405. package/dist/lib/sandbox.d.ts +1 -1
  406. package/dist/lib/scheduler.d.ts +1 -1
  407. package/dist/lib/scheduler.js +1 -1
  408. package/dist/lib/{routines.d.ts → scheduling/routines.d.ts} +54 -9
  409. package/dist/lib/{routines.js → scheduling/routines.js} +173 -14
  410. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  411. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  412. package/dist/lib/secrets/agent.js +2 -2
  413. package/dist/lib/secrets/audit.js +1 -1
  414. package/dist/lib/secrets/bundles.d.ts +4 -4
  415. package/dist/lib/secrets/bundles.js +8 -8
  416. package/dist/lib/secrets/headless.d.ts +16 -0
  417. package/dist/lib/secrets/headless.js +21 -0
  418. package/dist/lib/secrets/index.js +1 -1
  419. package/dist/lib/secrets/list-filter.d.ts +1 -1
  420. package/dist/lib/secrets/list-filter.js +2 -2
  421. package/dist/lib/secrets/push.d.ts +1 -1
  422. package/dist/lib/secrets/push.js +10 -12
  423. package/dist/lib/secrets/remote.d.ts +21 -17
  424. package/dist/lib/secrets/remote.js +39 -23
  425. package/dist/lib/secrets/vault.js +2 -2
  426. package/dist/lib/self-heal/checks/shadowing.js +1 -1
  427. package/dist/lib/self-heal/checks/shims.js +1 -1
  428. package/dist/lib/self-update.d.ts +39 -4
  429. package/dist/lib/self-update.js +78 -25
  430. package/dist/lib/serve/control.d.ts +1 -1
  431. package/dist/lib/serve/control.js +4 -4
  432. package/dist/lib/serve/data.d.ts +1 -1
  433. package/dist/lib/serve/data.js +1 -1
  434. package/dist/lib/serve/stream.js +1 -1
  435. package/dist/lib/service-manifest.d.ts +29 -0
  436. package/dist/lib/service-manifest.js +80 -0
  437. package/dist/lib/session/active.d.ts +59 -7
  438. package/dist/lib/session/active.js +158 -17
  439. package/dist/lib/session/bash-command.js +1 -1
  440. package/dist/lib/session/claude-accounts.d.ts +1 -1
  441. package/dist/lib/session/claude-accounts.js +1 -1
  442. package/dist/lib/session/db.d.ts +10 -2
  443. package/dist/lib/session/db.js +25 -6
  444. package/dist/lib/session/discover.d.ts +77 -0
  445. package/dist/lib/session/discover.js +130 -25
  446. package/dist/lib/session/fork.js +1 -1
  447. package/dist/lib/session/inject.d.ts +1 -1
  448. package/dist/lib/session/insights.d.ts +1 -1
  449. package/dist/lib/session/insights.js +1 -1
  450. package/dist/lib/session/live-metadata.d.ts +15 -0
  451. package/dist/lib/session/live-metadata.js +76 -0
  452. package/dist/lib/session/migrate-targets.d.ts +1 -1
  453. package/dist/lib/session/recovery.d.ts +1 -1
  454. package/dist/lib/session/recovery.js +2 -2
  455. package/dist/lib/session/remote-active.d.ts +10 -1
  456. package/dist/lib/session/remote-active.js +8 -3
  457. package/dist/lib/session/remote-bundle.d.ts +1 -1
  458. package/dist/lib/session/remote-bundle.js +2 -2
  459. package/dist/lib/session/remote-list.d.ts +15 -4
  460. package/dist/lib/session/remote-list.js +40 -3
  461. package/dist/lib/session/remote.d.ts +5 -5
  462. package/dist/lib/session/remote.js +13 -17
  463. package/dist/lib/session/resume-command.d.ts +3 -2
  464. package/dist/lib/session/resume-command.js +4 -3
  465. package/dist/lib/session/resume-owner.d.ts +3 -3
  466. package/dist/lib/session/resume-owner.js +3 -3
  467. package/dist/lib/session/session-cache.d.ts +12 -0
  468. package/dist/lib/session/session-cache.js +2 -0
  469. package/dist/lib/session/share-html.d.ts +55 -0
  470. package/dist/lib/session/share-html.js +319 -0
  471. package/dist/lib/session/shell-programs.js +1 -1
  472. package/dist/lib/session/state.js +5 -5
  473. package/dist/lib/session/team-filter.d.ts +1 -1
  474. package/dist/lib/session/team-filter.js +1 -1
  475. package/dist/lib/session/types.d.ts +3 -3
  476. package/dist/lib/session/width.d.ts +2 -2
  477. package/dist/lib/session/width.js +2 -2
  478. package/dist/lib/settings-manifest.d.ts +2 -0
  479. package/dist/lib/settings-manifest.js +81 -3
  480. package/dist/lib/share/delete.d.ts +21 -0
  481. package/dist/lib/share/delete.js +42 -0
  482. package/dist/lib/share/publish.d.ts +120 -0
  483. package/dist/lib/share/publish.js +243 -18
  484. package/dist/lib/share/worker-template.js +172 -9
  485. package/dist/lib/shims.js +35 -7
  486. package/dist/lib/smart-launch.d.ts +4 -4
  487. package/dist/lib/smart-launch.js +6 -4
  488. package/dist/lib/snapshot.d.ts +4 -4
  489. package/dist/lib/snapshot.js +2 -2
  490. package/dist/lib/ssh-exec.d.ts +1 -1
  491. package/dist/lib/ssh-exec.js +2 -2
  492. package/dist/lib/ssh-tunnel.d.ts +10 -10
  493. package/dist/lib/ssh-tunnel.js +12 -12
  494. package/dist/lib/staleness/checkers/hooks.d.ts +1 -1
  495. package/dist/lib/staleness/checkers/hooks.js +1 -1
  496. package/dist/lib/staleness/detectors/permissions.js +0 -23
  497. package/dist/lib/staleness/detectors/plugins.js +1 -1
  498. package/dist/lib/staleness/fingerprint.js +1 -1
  499. package/dist/lib/staleness/layers.d.ts +1 -1
  500. package/dist/lib/staleness/layers.js +1 -1
  501. package/dist/lib/staleness/writers/mcp.js +3 -1
  502. package/dist/lib/staleness/writers/plugins.js +1 -1
  503. package/dist/lib/staleness/writers/types.d.ts +8 -0
  504. package/dist/lib/startup/command-registry.d.ts +6 -147
  505. package/dist/lib/startup/command-registry.js +40 -303
  506. package/dist/lib/startup/dev-build.d.ts +0 -21
  507. package/dist/lib/startup/dev-build.js +29 -8
  508. package/dist/lib/state.d.ts +19 -1
  509. package/dist/lib/state.js +120 -51
  510. package/dist/lib/subagents.js +1 -1
  511. package/dist/lib/sync-status.js +1 -1
  512. package/dist/lib/sync-umbrella.d.ts +6 -1
  513. package/dist/lib/sync-umbrella.js +9 -4
  514. package/dist/lib/teams/agents.js +2 -2
  515. package/dist/lib/teams/registry.js +1 -1
  516. package/dist/lib/terminal/backends/vscodium-agent.js +1 -1
  517. package/dist/lib/terminal/engine.js +1 -1
  518. package/dist/lib/terminal/index.d.ts +4 -0
  519. package/dist/lib/terminal/index.js +3 -0
  520. package/dist/lib/terminal/inject.d.ts +2 -2
  521. package/dist/lib/terminal/inject.js +2 -2
  522. package/dist/lib/terminal/transport.js +1 -1
  523. package/dist/lib/tmux/index.d.ts +1 -1
  524. package/dist/lib/tmux/index.js +1 -1
  525. package/dist/lib/tmux/session.d.ts +21 -5
  526. package/dist/lib/tmux/session.js +76 -22
  527. package/dist/lib/triggers/handlers.d.ts +38 -3
  528. package/dist/lib/triggers/handlers.js +58 -7
  529. package/dist/lib/triggers/webhook.d.ts +113 -5
  530. package/dist/lib/triggers/webhook.js +223 -44
  531. package/dist/lib/types.d.ts +19 -7
  532. package/dist/lib/usage-fleet.d.ts +1 -1
  533. package/dist/lib/usage-fleet.js +1 -1
  534. package/dist/lib/usage-refresh.d.ts +1 -1
  535. package/dist/lib/usage-refresh.js +3 -3
  536. package/dist/lib/version-duplicates.js +2 -2
  537. package/dist/lib/view-types.d.ts +56 -0
  538. package/dist/lib/view-types.js +1 -0
  539. package/dist/lib/watchdog/rotate.d.ts +2 -2
  540. package/dist/lib/watchdog/rotate.js +3 -3
  541. package/dist/lib/watchdog/runner.d.ts +2 -3
  542. package/dist/lib/watchdog/runner.js +3 -4
  543. package/dist/lib/workflows.js +1 -1
  544. package/dist/lib/wrap.d.ts +33 -0
  545. package/dist/lib/wrap.js +70 -0
  546. package/package.json +2 -1
  547. package/scripts/postinstall.js +2 -2
  548. package/dist/commands/bench.d.ts +0 -2
  549. package/dist/commands/bench.js +0 -101
  550. package/dist/commands/cp.d.ts +0 -84
  551. package/dist/commands/cp.js +0 -231
  552. package/dist/commands/login.d.ts +0 -2
  553. package/dist/lib/resources/commands.d.ts +0 -46
  554. package/dist/lib/resources/commands.js +0 -211
  555. package/dist/lib/resources/hooks.d.ts +0 -16
  556. package/dist/lib/resources/hooks.js +0 -153
  557. package/dist/lib/resources/mcp.d.ts +0 -34
  558. package/dist/lib/resources/mcp.js +0 -250
  559. package/dist/lib/resources/memory.d.ts +0 -15
  560. package/dist/lib/resources/memory.js +0 -46
  561. package/dist/lib/resources/permissions.d.ts +0 -13
  562. package/dist/lib/resources/permissions.js +0 -202
  563. package/dist/lib/resources/rules.d.ts +0 -46
  564. package/dist/lib/resources/rules.js +0 -179
  565. package/dist/lib/resources/skills.d.ts +0 -37
  566. package/dist/lib/resources/skills.js +0 -247
  567. package/dist/lib/resources/subagents.d.ts +0 -46
  568. package/dist/lib/resources/subagents.js +0 -202
  569. /package/dist/lib/{feed.d.ts → feed/feed.d.ts} +0 -0
@@ -1,42 +1,37 @@
1
1
  /**
2
- * One-time migration: fold per-device config from the legacy stores into the
3
- * central `fleet.devices.<name>.config` block of `~/.agents/agents.yaml`.
2
+ * One-time migration to the current device-config + pins layout:
4
3
  *
5
- * Only SHARED-visibility keys move — the ones a peer reads. Machine-visibility
6
- * keys (see lib/config-machine-keys.ts) stay in the per-device doc, which is
7
- * exactly where the machine-local read path now looks for them.
4
+ * (a) central `fleet.devices.<name>.config` (the short-lived #2458 store)
5
+ * folds into each per-device doc's `config:` block central wins
6
+ * (newest intent) and is stripped from central;
7
+ * (b) legacy `.history/devices/auto-launch.json` flags fold into the doc
8
+ * `config:` too (oldest store — only fills keys not already set);
9
+ * (c) a top-level `defaultBrowserProfile:` in a device doc folds into that
10
+ * doc's `config:`;
11
+ * (d) agent pins (`agents:` / `isolatedAgents:`) leave the TRACKED
12
+ * per-device docs: THIS machine's pins move to the untracked
13
+ * `.history/devices/pins-<host>.json` (pins file wins on conflict — it
14
+ * is the destination); peers' pins are simply dropped from the tracked
15
+ * file (each peer owns/rewrites its own pins locally).
8
16
  *
9
- * Legacy sources (both pre-unification):
10
- * - `~/.agents/devices/<name>/agents.yaml` the `config:` map and the
11
- * top-level `defaultBrowserProfile:` field. Agent pins (`agents:`,
12
- * `isolatedAgents:`), `routines:`, and machine-visibility config STAY in the
13
- * per-device doc — only shared operator config moves.
14
- * - `~/.agents/.history/devices/auto-launch.json` — AGI EXT auto-launch
15
- * enabled/preferred flags, becoming `autoLaunchEnabled` /
16
- * `autoLaunchPreferred` config keys.
17
+ * What stays put: a device doc's existing `config:` (already the right home)
18
+ * and its `routines:` list (operator-owned, read cross-device by
19
+ * routine-activation.ts).
17
20
  *
18
- * The fold is ADDITIVE: the central block is written and the legacy stores are
19
- * left exactly as they were, so a box still on the previous CLI keeps reading
20
- * what it always read. The central-wins merge makes a re-run a byte no-op.
21
+ * Order is crash-safe: destination writes (pins file, device doc) land BEFORE
22
+ * the source strip (central), so a crash mid-fold re-folds on the next run and
23
+ * the destination-wins merges make that a no-op. Idempotent — after a
24
+ * successful run none of the legacy locations hold data, so re-running is a
25
+ * cheap existence check. A doc that fails to parse is LOUDLY skipped (left
26
+ * untouched) so the next run retries — never silently emptied, same corruption
27
+ * contract the device registry keeps.
21
28
  *
22
- * Invoked only from a lifecycle entry point `runMigration()` (fresh installs)
23
- * and daemon boot. It must NEVER hang off a config read or write: agents.yaml is
24
- * tracked and shared by the whole fleet, so a migration on the read path lets an
25
- * ordinary `agents config get` dirty it on every machine.
29
+ * Invoked from three places so every install converges regardless of entry
30
+ * point: `runMigration()` (fresh / sentinel-less installs), daemon boot, and
31
+ * the first `lib/device-config.ts` read/write in a process.
26
32
  */
27
- import type { FleetDeviceOverride, FleetManifest } from '../fleet/types.js';
28
33
  /**
29
- * The `fleet.devices` map a config write should edit. A missing block starts
30
- * empty; the literal `'all'` upgrades to an explicit map of the
31
- * currently-registered roster so a config write never silently narrows what
32
- * `agents apply` targets. (The dynamic "new devices join automatically"
33
- * semantics of `'all'` do not survive the upgrade — per-device config is
34
- * inherently a map.)
34
+ * Fold every legacy device-config/pins location into the current layout. Safe
35
+ * to call on every boot / config access: cheap no-op once folded.
35
36
  */
36
- export declare function fleetDevicesMapForWrite(fleet: FleetManifest | undefined): Record<string, FleetDeviceOverride>;
37
- /**
38
- * Fold the legacy per-device config stores into the central
39
- * `fleet.devices.<name>.config` block. Safe to call on every boot / config
40
- * access: cheap no-op once the legacy stores are stripped.
41
- */
42
- export declare function migrateDeviceConfigToCentral(): void;
37
+ export declare function migrateDeviceConfigStores(): void;
@@ -1,73 +1,50 @@
1
1
  /**
2
- * One-time migration: fold per-device config from the legacy stores into the
3
- * central `fleet.devices.<name>.config` block of `~/.agents/agents.yaml`.
2
+ * One-time migration to the current device-config + pins layout:
4
3
  *
5
- * Only SHARED-visibility keys move — the ones a peer reads. Machine-visibility
6
- * keys (see lib/config-machine-keys.ts) stay in the per-device doc, which is
7
- * exactly where the machine-local read path now looks for them.
4
+ * (a) central `fleet.devices.<name>.config` (the short-lived #2458 store)
5
+ * folds into each per-device doc's `config:` block central wins
6
+ * (newest intent) and is stripped from central;
7
+ * (b) legacy `.history/devices/auto-launch.json` flags fold into the doc
8
+ * `config:` too (oldest store — only fills keys not already set);
9
+ * (c) a top-level `defaultBrowserProfile:` in a device doc folds into that
10
+ * doc's `config:`;
11
+ * (d) agent pins (`agents:` / `isolatedAgents:`) leave the TRACKED
12
+ * per-device docs: THIS machine's pins move to the untracked
13
+ * `.history/devices/pins-<host>.json` (pins file wins on conflict — it
14
+ * is the destination); peers' pins are simply dropped from the tracked
15
+ * file (each peer owns/rewrites its own pins locally).
8
16
  *
9
- * Legacy sources (both pre-unification):
10
- * - `~/.agents/devices/<name>/agents.yaml` the `config:` map and the
11
- * top-level `defaultBrowserProfile:` field. Agent pins (`agents:`,
12
- * `isolatedAgents:`), `routines:`, and machine-visibility config STAY in the
13
- * per-device doc — only shared operator config moves.
14
- * - `~/.agents/.history/devices/auto-launch.json` — AGI EXT auto-launch
15
- * enabled/preferred flags, becoming `autoLaunchEnabled` /
16
- * `autoLaunchPreferred` config keys.
17
+ * What stays put: a device doc's existing `config:` (already the right home)
18
+ * and its `routines:` list (operator-owned, read cross-device by
19
+ * routine-activation.ts).
17
20
  *
18
- * The fold is ADDITIVE: the central block is written and the legacy stores are
19
- * left exactly as they were, so a box still on the previous CLI keeps reading
20
- * what it always read. The central-wins merge makes a re-run a byte no-op.
21
+ * Order is crash-safe: destination writes (pins file, device doc) land BEFORE
22
+ * the source strip (central), so a crash mid-fold re-folds on the next run and
23
+ * the destination-wins merges make that a no-op. Idempotent — after a
24
+ * successful run none of the legacy locations hold data, so re-running is a
25
+ * cheap existence check. A doc that fails to parse is LOUDLY skipped (left
26
+ * untouched) so the next run retries — never silently emptied, same corruption
27
+ * contract the device registry keeps.
21
28
  *
22
- * Invoked only from a lifecycle entry point `runMigration()` (fresh installs)
23
- * and daemon boot. It must NEVER hang off a config read or write: agents.yaml is
24
- * tracked and shared by the whole fleet, so a migration on the read path lets an
25
- * ordinary `agents config get` dirty it on every machine.
29
+ * Invoked from three places so every install converges regardless of entry
30
+ * point: `runMigration()` (fresh / sentinel-less installs), daemon boot, and
31
+ * the first `lib/device-config.ts` read/write in a process.
26
32
  */
27
33
  import * as fs from 'fs';
28
34
  import * as path from 'path';
29
35
  import * as yaml from 'yaml';
30
- import { getDevicesAutoLaunchPath, getUserAgentsDir, updateMeta, } from '../state.js';
31
- import { loadDevicesSync } from './registry.js';
32
- import { MACHINE_LOCAL_YAML_KEYS } from '../config-machine-keys.js';
33
- /**
34
- * The `fleet.devices` map a config write should edit. A missing block starts
35
- * empty; the literal `'all'` upgrades to an explicit map of the
36
- * currently-registered roster so a config write never silently narrows what
37
- * `agents apply` targets. (The dynamic "new devices join automatically"
38
- * semantics of `'all'` do not survive the upgrade — per-device config is
39
- * inherently a map.)
40
- */
41
- export function fleetDevicesMapForWrite(fleet) {
42
- if (fleet && fleet.devices !== 'all')
43
- return { ...fleet.devices };
44
- if (fleet?.devices !== 'all')
45
- return {};
46
- // devices === 'all': expand to the registered roster.
47
- const roster = loadDevicesSync();
48
- const devices = {};
49
- for (const name of Object.keys(roster))
50
- devices[name] = {};
51
- return devices;
52
- }
53
- function mergeFold(folds, device, values) {
54
- folds[device] = { ...folds[device], ...values };
55
- }
56
- /**
57
- * Read every legacy device doc, returning the config to fold per device.
58
- * A doc that fails to parse is LOUDLY skipped (and left untouched) so the next
59
- * run retries — never silently emptied, same corruption contract the device
60
- * registry keeps.
61
- */
62
- function collectDeviceDocFolds(devicesRoot) {
63
- const folds = {};
36
+ import { META_HEADER, getDevicesAutoLaunchPath, getDevicePinsPath, getUserAgentsDir, readMeta, updateMeta, withMetaLock, } from '../state.js';
37
+ import { atomicWriteFileSync } from '../fs-atomic.js';
38
+ import { machineId } from '../machine-id.js';
39
+ /** Read every device doc. A doc that fails to parse is loudly skipped. */
40
+ function readDeviceDocs(devicesRoot) {
64
41
  const docs = [];
65
42
  let entries;
66
43
  try {
67
44
  entries = fs.readdirSync(devicesRoot, { withFileTypes: true });
68
45
  }
69
46
  catch {
70
- return { folds, docs }; // no devices/ tree — nothing to fold
47
+ return docs; // no devices/ tree — nothing to fold
71
48
  }
72
49
  for (const entry of entries) {
73
50
  if (!entry.isDirectory())
@@ -89,48 +66,38 @@ function collectDeviceDocFolds(devicesRoot) {
89
66
  console.error(`device config migration: ${docPath} is not a YAML map; leaving it for manual repair`);
90
67
  continue;
91
68
  }
92
- const doc = parsed;
93
- const config = {};
94
- if (doc.config !== undefined) {
95
- if (typeof doc.config !== 'object' || doc.config === null || Array.isArray(doc.config)) {
96
- console.error(`device config migration: ${docPath} has a non-map config: block; leaving it for manual repair`);
97
- continue;
69
+ docs.push({ name: entry.name, path: docPath, doc: parsed });
70
+ }
71
+ return docs;
72
+ }
73
+ /** Write a device doc, or remove it (and its dir) when nothing remains. */
74
+ function writeDeviceDoc(docPath, doc) {
75
+ try {
76
+ if (Object.keys(doc).length === 0) {
77
+ fs.rmSync(docPath, { force: true });
78
+ try {
79
+ fs.rmdirSync(path.dirname(docPath));
98
80
  }
99
- Object.assign(config, doc.config);
100
- }
101
- if (typeof doc.defaultBrowserProfile === 'string') {
102
- config.defaultBrowserProfile = doc.defaultBrowserProfile;
81
+ catch { /* not empty — other files live in the device dir */ }
103
82
  }
104
- // Machine-visibility keys are NOT folded. Two reasons, both load-bearing:
105
- //
106
- // 1. They already live exactly where they belong. `config:` in this doc is
107
- // the machine-local store the new read path uses (state.ts overlays it
108
- // as `deviceConfig`), so folding would only copy them into the shared
109
- // file this whole change exists to keep clean.
110
- // 2. Copying a PEER's would be a leak. `browserRemoteControl` is a consent
111
- // flag gating whether other machines may drive that box's browser;
112
- // hoisting one box's opt-in into the synced file spreads it to the fleet.
113
- // Each machine keeps its own, in its own doc.
114
- for (const key of Object.keys(config)) {
115
- if (MACHINE_LOCAL_YAML_KEYS.has(key))
116
- delete config[key];
83
+ else {
84
+ fs.mkdirSync(path.dirname(docPath), { recursive: true });
85
+ atomicWriteFileSync(docPath, META_HEADER + yaml.stringify(doc));
117
86
  }
118
- if (Object.keys(config).length === 0)
119
- continue;
120
- mergeFold(folds, entry.name, config);
121
- docs.push({ name: entry.name, path: docPath, doc });
122
87
  }
123
- return { folds, docs };
88
+ catch (err) {
89
+ console.error(`device config migration: could not rewrite ${docPath} (${err.message}); a later run retries`);
90
+ }
124
91
  }
125
- /** Read the legacy auto-launch.json, returning the flags to fold per device. */
126
- function collectAutoLaunchFolds(autoLaunchPath) {
127
- const folds = {};
92
+ /** The legacy auto-launch.json flags, keyed by device name ({} when absent). */
93
+ function readAutoLaunchFlags(autoLaunchPath) {
94
+ const out = {};
128
95
  let raw;
129
96
  try {
130
97
  raw = fs.readFileSync(autoLaunchPath, 'utf-8');
131
98
  }
132
99
  catch {
133
- return folds; // absent — nothing to fold
100
+ return out; // absent — nothing to fold
134
101
  }
135
102
  let parsed;
136
103
  try {
@@ -138,7 +105,7 @@ function collectAutoLaunchFolds(autoLaunchPath) {
138
105
  }
139
106
  catch (err) {
140
107
  console.error(`device config migration: could not parse ${autoLaunchPath} (${err.message}); leaving it for a later retry`);
141
- return folds;
108
+ return out;
142
109
  }
143
110
  const devices = parsed?.devices;
144
111
  if (devices && typeof devices === 'object' && !Array.isArray(devices)) {
@@ -149,55 +116,167 @@ function collectAutoLaunchFolds(autoLaunchPath) {
149
116
  if (pref?.preferred === true)
150
117
  config.autoLaunchPreferred = true;
151
118
  if (Object.keys(config).length > 0)
152
- mergeFold(folds, name, config);
119
+ out[name] = config;
153
120
  }
154
121
  }
155
- return folds;
122
+ return out;
123
+ }
124
+ function isConfigMap(v) {
125
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
156
126
  }
157
127
  /**
158
- * Fold the legacy per-device config stores into the central
159
- * `fleet.devices.<name>.config` block. Safe to call on every boot / config
160
- * access: cheap no-op once the legacy stores are stripped.
128
+ * Fold every legacy device-config/pins location into the current layout. Safe
129
+ * to call on every boot / config access: cheap no-op once folded.
161
130
  */
162
- export function migrateDeviceConfigToCentral() {
131
+ export function migrateDeviceConfigStores() {
163
132
  const devicesRoot = path.join(getUserAgentsDir(), 'devices');
164
133
  const autoLaunchPath = getDevicesAutoLaunchPath();
165
- const { folds: docFolds, docs } = collectDeviceDocFolds(devicesRoot);
166
- const autoFolds = collectAutoLaunchFolds(autoLaunchPath);
167
- const folds = {};
168
- for (const [name, config] of Object.entries(docFolds))
169
- mergeFold(folds, name, config);
170
- for (const [name, config] of Object.entries(autoFolds))
171
- mergeFold(folds, name, config);
172
- if (Object.keys(folds).length === 0)
134
+ const self = machineId();
135
+ // ── 1. Gather ────────────────────────────────────────────────────────────
136
+ // Central per-device config (the #2458 store).
137
+ const fleet = readMeta().fleet;
138
+ const centralDevices = fleet && fleet.devices !== 'all' ? fleet.devices : {};
139
+ const centralHasConfig = Object.values(centralDevices).some((ov) => ov?.config && Object.keys(ov.config).length > 0);
140
+ const autoLaunchFlags = readAutoLaunchFlags(autoLaunchPath);
141
+ const docs = readDeviceDocs(devicesRoot);
142
+ const pinsPath = getDevicePinsPath();
143
+ let selfPins = {};
144
+ try {
145
+ selfPins = JSON.parse(fs.readFileSync(pinsPath, 'utf-8')) || {};
146
+ }
147
+ catch { /* absent or malformed — the fold below recreates it */ }
148
+ // ── 1b. Plan (pure) — so a converged install never takes the meta lock or
149
+ // rewrites a byte-identical doc on every boot.
150
+ const plans = [];
151
+ for (const { name, path: docPath, doc } of docs) {
152
+ const plan = planDeviceDocFold(name, docPath, doc, centralDevices[name]?.config, autoLaunchFlags[name]);
153
+ if (plan)
154
+ plans.push(plan);
155
+ }
156
+ const docNames = new Set(docs.map((d) => d.name));
157
+ const newDocs = [];
158
+ for (const [name, ov] of Object.entries(centralDevices)) {
159
+ if (docNames.has(name) || !isConfigMap(ov?.config))
160
+ continue;
161
+ newDocs.push({ path: path.join(devicesRoot, name, 'agents.yaml'), doc: { config: { ...autoLaunchFlags[name], ...ov.config } } });
162
+ }
163
+ for (const [name, flags] of Object.entries(autoLaunchFlags)) {
164
+ if (docNames.has(name) || isConfigMap(centralDevices[name]?.config))
165
+ continue;
166
+ newDocs.push({ path: path.join(devicesRoot, name, 'agents.yaml'), doc: { config: { ...flags } } });
167
+ }
168
+ const selfDoc = docs.find((d) => d.name === self);
169
+ const docAgents = isConfigMap(selfDoc?.doc.agents) ? selfDoc.doc.agents : undefined;
170
+ const docIsolated = isConfigMap(selfDoc?.doc.isolatedAgents)
171
+ ? selfDoc.doc.isolatedAgents
172
+ : undefined;
173
+ const hasDestinationWork = plans.length > 0 || newDocs.length > 0 || docAgents !== undefined || docIsolated !== undefined;
174
+ const autoLaunchPending = fs.existsSync(autoLaunchPath);
175
+ if (!centralHasConfig && !hasDestinationWork && !autoLaunchPending)
173
176
  return;
174
- // 1. Central write FIRST. A `fleet.devices: all` declaration upgrades to an
175
- // explicit map of the currently-registered roster so `agents apply` keeps
176
- // targeting the same devices. A key already present centrally WINS over
177
- // the legacy value a re-fold after a mid-migration crash is then a
178
- // no-op, and a value written by a newer CLI is never clobbered.
179
- updateMeta((m) => {
180
- const devices = fleetDevicesMapForWrite(m.fleet);
181
- for (const [name, config] of Object.entries(folds)) {
182
- const prev = devices[name] ?? {};
183
- devices[name] = { ...prev, config: { ...config, ...prev.config } };
177
+ // ── 2. Destination writes FIRST (crash-safe), under the meta lock so they
178
+ // serialize against writeMetaUnlocked's own read-merge-write of the doc.
179
+ // Only taken when there IS a write a converged install never locks
180
+ // (taking it would create a default central agents.yaml as a side effect).
181
+ if (hasDestinationWork) {
182
+ withMetaLock(() => {
183
+ // 2a. THIS machine's pins: doc pins merge INTO the pins file (pins file
184
+ // wins it is the destination, and a re-fold after a crash must not
185
+ // clobber pins the new CLI already wrote).
186
+ if (docAgents || docIsolated) {
187
+ const pins = { ...selfPins };
188
+ if (docAgents)
189
+ pins.agents = { ...docAgents, ...selfPins.agents };
190
+ if (docIsolated)
191
+ pins.isolatedAgents = { ...docIsolated, ...selfPins.isolatedAgents };
192
+ try {
193
+ fs.mkdirSync(path.dirname(pinsPath), { recursive: true });
194
+ atomicWriteFileSync(pinsPath, JSON.stringify(pins, null, 2) + '\n');
195
+ }
196
+ catch (err) {
197
+ console.error(`device config migration: could not write ${pinsPath} (${err.message}); a later run retries`);
198
+ }
199
+ }
200
+ // 2b/2c. The planned doc rewrites + creations.
201
+ for (const plan of plans)
202
+ writeDeviceDoc(plan.path, plan.next);
203
+ for (const nd of newDocs)
204
+ writeDeviceDoc(nd.path, nd.doc);
205
+ });
206
+ }
207
+ // ── 3. Source strips LAST ────────────────────────────────────────────────
208
+ // 3a. Central: drop every devices.<name>.config block (now folded into the
209
+ // device docs). Overrides left with no other fields are dropped; an
210
+ // emptied fleet block (no defaults/secrets/routines) goes away entirely.
211
+ if (centralHasConfig) {
212
+ updateMeta((m) => {
213
+ const devices = m.fleet?.devices;
214
+ if (!devices || devices === 'all')
215
+ return m;
216
+ const nextDevices = {};
217
+ for (const [name, ov] of Object.entries(devices)) {
218
+ const rest = { ...ov };
219
+ delete rest.config;
220
+ if (Object.keys(rest).length > 0)
221
+ nextDevices[name] = rest;
222
+ }
223
+ const fleet = { ...m.fleet, devices: nextDevices };
224
+ if (Object.keys(nextDevices).length === 0 && !fleet.defaults && !fleet.secrets && !fleet.routines) {
225
+ const { fleet: _, ...rest } = m;
226
+ void _;
227
+ return rest;
228
+ }
229
+ return { ...m, fleet };
230
+ });
231
+ }
232
+ // 3b. The legacy auto-launch.json.
233
+ if (autoLaunchPending) {
234
+ try {
235
+ fs.rmSync(autoLaunchPath, { force: true });
184
236
  }
185
- const fleet = { ...m.fleet, devices };
186
- return { ...m, fleet };
187
- });
188
- // 2. The legacy stores are deliberately LEFT IN PLACE.
189
- //
190
- // This migration used to delete them `fs.rmSync` on the device doc and
191
- // `fs.rmdirSync` on its directory. Deleting is what made the fold unsafe
192
- // to run anywhere: it had to win a race against every other machine's copy
193
- // of the same shared file, and a box on an older CLI that re-created the
194
- // doc would be stripped again on the next command.
195
- //
196
- // An additive fold has neither problem. The central block is written; the
197
- // source is untouched; a box still running the previous CLI keeps reading
198
- // what it always read. The now-redundant legacy copy is pruned in a later
199
- // release by one explicit operator command, not by 13 machines each
200
- // deciding to delete on their own schedule.
201
- void docs;
202
- void autoLaunchPath;
237
+ catch (err) {
238
+ console.error(`device config migration: could not remove ${autoLaunchPath} (${err.message}); a later run retries`);
239
+ }
240
+ }
241
+ }
242
+ /** Shallow-set equality for folded doc content (values compared deep via JSON). */
243
+ function sameDocContent(a, b) {
244
+ const ak = Object.keys(a);
245
+ const bk = Object.keys(b);
246
+ if (ak.length !== bk.length)
247
+ return false;
248
+ return ak.every((k) => k in b && JSON.stringify(a[k]) === JSON.stringify(b[k]));
249
+ }
250
+ /**
251
+ * Compute a device doc's post-fold content: merge the config layers (oldest
252
+ * auto-launch.json flags < the doc's own config: < the central #2458 block —
253
+ * newest wins), fold a top-level defaultBrowserProfile into config:, and strip
254
+ * pins from the tracked file. Returns null when the doc is corrupt (loudly —
255
+ * left for manual repair) or the fold would not change its content.
256
+ */
257
+ function planDeviceDocFold(name, docPath, doc, centralConfig, autoFlags) {
258
+ const config = {};
259
+ Object.assign(config, autoFlags);
260
+ if (doc.config !== undefined) {
261
+ if (!isConfigMap(doc.config)) {
262
+ console.error(`device config migration: ${docPath} has a non-map config: block; leaving it for manual repair`);
263
+ return null;
264
+ }
265
+ Object.assign(config, doc.config);
266
+ }
267
+ if (typeof doc.defaultBrowserProfile === 'string' && config.defaultBrowserProfile === undefined) {
268
+ config.defaultBrowserProfile = doc.defaultBrowserProfile;
269
+ }
270
+ if (isConfigMap(centralConfig))
271
+ Object.assign(config, centralConfig);
272
+ const next = {};
273
+ // Preserve fields the migration does not own (routines:, anything else).
274
+ for (const [k, v] of Object.entries(doc)) {
275
+ if (k === 'config' || k === 'defaultBrowserProfile' || k === 'agents' || k === 'isolatedAgents')
276
+ continue;
277
+ next[k] = v;
278
+ }
279
+ if (Object.keys(config).length > 0)
280
+ next.config = config;
281
+ return sameDocContent(next, doc) ? null : { name, path: docPath, next };
203
282
  }
@@ -44,7 +44,7 @@ export declare function wrapRemoteCommand(device: DeviceProfile, cmd: string[]):
44
44
  * (`mirrorCwd` is undefined, or resolves to the home root itself) so the caller
45
45
  * keeps the plain no-command interactive login.
46
46
  *
47
- * This is the interactive analogue of `agents run --host`'s cwd mirroring, and
47
+ * This is the interactive analogue of `agents run --device`'s cwd mirroring, and
48
48
  * it reuses the SAME machinery so there is no second resolver: the portable
49
49
  * `mirrorCwd` comes from `deriveMirroredCwd`, and — for POSIX — the best-effort
50
50
  * `cd` comes from `remoteCdPrefix({ mirror: true })`, whose `|| cd "$HOME"`
@@ -94,7 +94,7 @@ export declare function deviceIdentityArgs(device: DeviceProfile): string[];
94
94
  * interactive login (no `cmd`) — from `deriveMirroredCwd(process.cwd())`. When
95
95
  * it names a real home-relative sub-path, the login starts there via
96
96
  * {@link buildInteractiveShellCommand} (best-effort — a missing dir falls back
97
- * to the remote home), matching `agents run --host`. It is ignored when a `cmd`
97
+ * to the remote home), matching `agents run --device`. It is ignored when a `cmd`
98
98
  * is given: an explicit command keeps its current cwd (the remote home) and its
99
99
  * behavior unchanged (RUSH-2412).
100
100
  */
@@ -93,7 +93,7 @@ export function wrapRemoteCommand(device, cmd) {
93
93
  * (`mirrorCwd` is undefined, or resolves to the home root itself) so the caller
94
94
  * keeps the plain no-command interactive login.
95
95
  *
96
- * This is the interactive analogue of `agents run --host`'s cwd mirroring, and
96
+ * This is the interactive analogue of `agents run --device`'s cwd mirroring, and
97
97
  * it reuses the SAME machinery so there is no second resolver: the portable
98
98
  * `mirrorCwd` comes from `deriveMirroredCwd`, and — for POSIX — the best-effort
99
99
  * `cd` comes from `remoteCdPrefix({ mirror: true })`, whose `|| cd "$HOME"`
@@ -153,7 +153,7 @@ export function deviceIdentityArgs(device) {
153
153
  * interactive login (no `cmd`) — from `deriveMirroredCwd(process.cwd())`. When
154
154
  * it names a real home-relative sub-path, the login starts there via
155
155
  * {@link buildInteractiveShellCommand} (best-effort — a missing dir falls back
156
- * to the remote home), matching `agents run --host`. It is ignored when a `cmd`
156
+ * to the remote home), matching `agents run --device`. It is ignored when a `cmd`
157
157
  * is given: an explicit command keeps its current cwd (the remote home) and its
158
158
  * behavior unchanged (RUSH-2412).
159
159
  */
@@ -0,0 +1,30 @@
1
+ export type DeviceDiscoveryStatus = 'approved' | 'ignored';
2
+ export interface DeviceDiscoveryReconcileResult {
3
+ approved: string[];
4
+ ignored: string[];
5
+ registered: string[];
6
+ unresolved: string[];
7
+ }
8
+ /** Read one portable decision. Absence means pending. */
9
+ export declare function getDeviceDiscoveryStatus(name: string): DeviceDiscoveryStatus | undefined;
10
+ /** Persist one portable decision in the central fleet manifest. */
11
+ export declare function setDeviceDiscoveryStatus(name: string, status: DeviceDiscoveryStatus | undefined): void;
12
+ /** Load every explicit decision from the synced central fleet manifest. */
13
+ export declare function loadDeviceDiscoveryPolicies(): Map<string, DeviceDiscoveryStatus>;
14
+ /**
15
+ * Apply synced intent to this machine's local registry. Approval resolves live
16
+ * Tailscale metadata; ignore never needs the network. Missing approved peers are
17
+ * reported as unresolved rather than written with invented connection details.
18
+ *
19
+ * Only devices with an EXPLICIT entry in the synced policy are touched. A
20
+ * device absent from the map is left alone, whether or not the map itself is
21
+ * defined — the map is not treated as authoritative-by-omission, because not
22
+ * every local registration path writes a policy entry (pair-ios control
23
+ * devices, daemon/umbrella auto-refresh, and any device registered before this
24
+ * feature shipped). Wiping those on the next `agents repo pull user` would be
25
+ * silent, fleet-wide data loss for a decision (e.g. "ignore this one iPad")
26
+ * that never meant to touch them.
27
+ */
28
+ export declare function reconcileDeviceDiscoveryPolicies(): Promise<DeviceDiscoveryReconcileResult>;
29
+ /** Complete reconciliation from real `tailscale status --json` output. */
30
+ export declare function registerApprovedDevicesFromTailscale(json: string, approved: string[], ignored: string[], missing: string[]): Promise<DeviceDiscoveryReconcileResult>;
@@ -0,0 +1,101 @@
1
+ /** Synced device approval/ignore policy and local registry reconciliation. */
2
+ import { readMeta, updateMeta } from '../state.js';
3
+ import { addIgnored, assertValidDeviceName, loadDevices, removeDevice, removeIgnored, upsertDevice, } from './registry.js';
4
+ import { localLoginUser, withDefaultUser } from './sync.js';
5
+ import { nodeToDeviceInput, parseTailscaleStatus, tailscaleStatusJson } from './tailscale.js';
6
+ /** Read one portable decision. Absence means pending. */
7
+ export function getDeviceDiscoveryStatus(name) {
8
+ assertValidDeviceName(name);
9
+ return loadDeviceDiscoveryPolicies().get(name);
10
+ }
11
+ /** Persist one portable decision in the central fleet manifest. */
12
+ export function setDeviceDiscoveryStatus(name, status) {
13
+ assertValidDeviceName(name);
14
+ updateMeta((meta) => {
15
+ const discovery = { ...meta.fleet?.discovery };
16
+ if (status)
17
+ discovery[name] = status;
18
+ else
19
+ delete discovery[name];
20
+ const fleet = {
21
+ ...meta.fleet,
22
+ devices: meta.fleet?.devices ?? {},
23
+ // Keep an empty map as the synced tombstone: it distinguishes "policy is
24
+ // authoritative and every device is pending" from an older config that
25
+ // has never opted into portable discovery decisions.
26
+ discovery,
27
+ };
28
+ return { ...meta, fleet };
29
+ });
30
+ }
31
+ /** Load every explicit decision from the synced central fleet manifest. */
32
+ export function loadDeviceDiscoveryPolicies() {
33
+ const policies = new Map();
34
+ for (const [name, status] of Object.entries(readMeta().fleet?.discovery ?? {})) {
35
+ assertValidDeviceName(name);
36
+ if (status !== 'approved' && status !== 'ignored') {
37
+ throw new Error(`Device discovery policy for '${name}' must be approved or ignored.`);
38
+ }
39
+ policies.set(name, status);
40
+ }
41
+ return policies;
42
+ }
43
+ /**
44
+ * Apply synced intent to this machine's local registry. Approval resolves live
45
+ * Tailscale metadata; ignore never needs the network. Missing approved peers are
46
+ * reported as unresolved rather than written with invented connection details.
47
+ *
48
+ * Only devices with an EXPLICIT entry in the synced policy are touched. A
49
+ * device absent from the map is left alone, whether or not the map itself is
50
+ * defined — the map is not treated as authoritative-by-omission, because not
51
+ * every local registration path writes a policy entry (pair-ios control
52
+ * devices, daemon/umbrella auto-refresh, and any device registered before this
53
+ * feature shipped). Wiping those on the next `agents repo pull user` would be
54
+ * silent, fleet-wide data loss for a decision (e.g. "ignore this one iPad")
55
+ * that never meant to touch them.
56
+ */
57
+ export async function reconcileDeviceDiscoveryPolicies() {
58
+ const configured = readMeta().fleet?.discovery;
59
+ if (configured === undefined) {
60
+ return { approved: [], ignored: [], registered: [], unresolved: [] };
61
+ }
62
+ const policies = loadDeviceDiscoveryPolicies();
63
+ const approved = [...policies].filter(([, s]) => s === 'approved').map(([n]) => n).sort();
64
+ const ignored = [...policies].filter(([, s]) => s === 'ignored').map(([n]) => n).sort();
65
+ for (const name of ignored) {
66
+ await removeDevice(name);
67
+ await addIgnored(name);
68
+ }
69
+ for (const name of approved)
70
+ await removeIgnored(name);
71
+ const currentRegistry = await loadDevices();
72
+ const missing = approved.filter((name) => !currentRegistry[name]);
73
+ if (missing.length === 0)
74
+ return { approved, ignored, registered: [], unresolved: [] };
75
+ let json;
76
+ try {
77
+ json = tailscaleStatusJson();
78
+ }
79
+ catch {
80
+ return { approved, ignored, registered: [], unresolved: missing };
81
+ }
82
+ return registerApprovedDevicesFromTailscale(json, approved, ignored, missing);
83
+ }
84
+ /** Complete reconciliation from real `tailscale status --json` output. */
85
+ export async function registerApprovedDevicesFromTailscale(json, approved, ignored, missing) {
86
+ const nodes = parseTailscaleStatus(json);
87
+ const byName = new Map(nodes.map((node) => [node.name, node]));
88
+ const registered = [];
89
+ const unresolved = [];
90
+ const user = localLoginUser();
91
+ for (const name of missing) {
92
+ const node = byName.get(name);
93
+ if (!node) {
94
+ unresolved.push(name);
95
+ continue;
96
+ }
97
+ await upsertDevice(name, withDefaultUser(nodeToDeviceInput(node), undefined, user));
98
+ registered.push(name);
99
+ }
100
+ return { approved, ignored, registered, unresolved };
101
+ }