@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
@@ -10,13 +10,13 @@ import * as fs from 'fs';
10
10
  import * as path from 'path';
11
11
  import * as os from 'os';
12
12
  import * as yaml from 'yaml';
13
- import { normalizeAliases } from './resource-aliases.js';
14
- import { AGENTS, ensureSkillsDir, agentConfigDirName } from './agents.js';
15
- import { capableAgents, isCapable } from './capabilities.js';
16
- import { getUserSkillsDir, getSkillsDir as getSystemSkillsDir, getProjectAgentsDir, getEnabledExtraRepos, getTrashSkillsDir } from './state.js';
17
- import { getEffectiveHome, getVersionHomePath, listInstalledVersions } from './versions.js';
18
- import { listCommandSkillsInVersion } from './command-skills.js';
19
- import { emit } from './events.js';
13
+ import { normalizeAliases } from '../resource-aliases.js';
14
+ import { AGENTS, ensureSkillsDir, agentConfigDirName } from '../agents.js';
15
+ import { capableAgents, isCapable } from '../capabilities.js';
16
+ import { getUserSkillsDir, getSkillsDir as getSystemSkillsDir, getProjectAgentsDir, getEnabledExtraRepos, getTrashSkillsDir } from '../state.js';
17
+ import { getEffectiveHome, getVersionHomePath, listInstalledVersions } from '../installations/versions.js';
18
+ import { listCommandSkillsInVersion } from '../command-skills.js';
19
+ import { emit } from '../feed/events.js';
20
20
  const HOME = os.homedir();
21
21
  /** User-scoped skills dir (~/.agents/skills/). Used for installs. */
22
22
  export function getSkillsDir() {
@@ -18,7 +18,7 @@ export declare function costOfUsage(u: TokenUsage): number;
18
18
  * USD cost of one usage record priced as if caching were OFF: cache-read and
19
19
  * cache-write tokens are billed at the model's full INPUT rate rather than their
20
20
  * discounted cache rates. This is the "what would this have cost with no prompt
21
- * caching?" scenario that `agents output --pricing no-cache` models. Output and
21
+ * caching?" scenario that `agents insights output --pricing no-cache` models. Output and
22
22
  * uncached input are unchanged — only the cache tokens are repriced. Returns 0
23
23
  * for a missing/unpriced model, exactly like {@link costOfUsage}.
24
24
  */
@@ -35,7 +35,7 @@ export function costOfUsage(u) {
35
35
  * USD cost of one usage record priced as if caching were OFF: cache-read and
36
36
  * cache-write tokens are billed at the model's full INPUT rate rather than their
37
37
  * discounted cache rates. This is the "what would this have cost with no prompt
38
- * caching?" scenario that `agents output --pricing no-cache` models. Output and
38
+ * caching?" scenario that `agents insights output --pricing no-cache` models. Output and
39
39
  * uncached input are unchanged — only the cache tokens are repriced. Returns 0
40
40
  * for a missing/unpriced model, exactly like {@link costOfUsage}.
41
41
  */
@@ -2,7 +2,7 @@
2
2
  * Built-in profile presets for popular model providers.
3
3
  *
4
4
  * Each preset bundles a host CLI, API base URL, default model, and provider
5
- * name so users can `agents profiles add kimi` without manual configuration.
5
+ * name so users can `agents harness add kimi` without manual configuration.
6
6
  */
7
7
  import type { AgentId } from './types.js';
8
8
  export interface PresetVar {
@@ -2,7 +2,7 @@
2
2
  * Built-in profile presets for popular model providers.
3
3
  *
4
4
  * Each preset bundles a host CLI, API base URL, default model, and provider
5
- * name so users can `agents profiles add kimi` without manual configuration.
5
+ * name so users can `agents harness add kimi` without manual configuration.
6
6
  */
7
7
  // Model IDs verified against openrouter.ai/api/v1/models on 2026-07-31
8
8
  // (spark presets corrected from the never-served meta/claude-spark-1.1 to the
@@ -129,7 +129,7 @@ export function listProfiles() {
129
129
  profiles.push(readProfile(name));
130
130
  }
131
131
  catch {
132
- // Skip malformed profile files; surfacing via `agents profiles view <name>`.
132
+ // Skip malformed profile files; surfacing via `agents harness view <name>`.
133
133
  }
134
134
  }
135
135
  return profiles.sort((a, b) => a.name.localeCompare(b.name));
@@ -318,7 +318,7 @@ export function profileFromPreset(profileName, preset, version) {
318
318
  }
319
319
  /**
320
320
  * Env var each host CLI reads to override its model. Mirror of the read-side
321
- * `MODEL_ENV_KEYS` above, keyed by host so a one-shot `--host <agent> --model
321
+ * `MODEL_ENV_KEYS` above, keyed by agent so a one-shot `--device <agent> --model
322
322
  * <id>` writes the model onto the var that host actually honors.
323
323
  */
324
324
  const MODEL_ENV_KEY_BY_HOST = {
@@ -68,7 +68,10 @@ export function buildLinearImportCandidates(projects, existing, deps, opts) {
68
68
  continue;
69
69
  }
70
70
  seen.add(name);
71
- const def = { ...prior, name, linear: { projectId: p.id } };
71
+ // The Linear display name is recorded verbatim (`p.name`), not the
72
+ // slugified def name: `agi` is the local id, "AGI" is what the board calls
73
+ // it, and agents read the latter when they name the project.
74
+ const def = { ...prior, name, linear: { projectId: p.id, name: p.name } };
72
75
  if (p.url)
73
76
  def.linear.url = p.url;
74
77
  const dir = matchLocalCheckoutExact(p.name, deps.localDirs);
@@ -55,6 +55,13 @@ export function repoRootForCwd(dir, home = os.homedir()) {
55
55
  for (;;) {
56
56
  if (fs.existsSync(path.join(current, '.git')))
57
57
  return current === stop ? undefined : current;
58
+ // Never climb past $HOME: an ancestor of home (/tmp, /, ...) is not part of
59
+ // any project this cwd belongs to, and treating one as the repo root would
60
+ // let unrelated host state (e.g. a stray /tmp/.git) swallow every loose
61
+ // directory under home. Mirrors the shim's own home boundary — see
62
+ // shims.ts shimExecTail.
63
+ if (current === stop)
64
+ return undefined;
58
65
  const parent = path.dirname(current);
59
66
  if (parent === current)
60
67
  return undefined;
@@ -35,7 +35,7 @@
35
35
  * change path.
36
36
  */
37
37
  import type { AgentId } from './types.js';
38
- import { pluginInstallDir } from './plugin-marketplace.js';
38
+ import { pluginInstallDir } from './plugins/plugin-marketplace.js';
39
39
  export interface LaunchSyncOptions {
40
40
  agent: AgentId;
41
41
  version: string;
@@ -40,12 +40,12 @@ import * as os from 'os';
40
40
  import * as path from 'path';
41
41
  import { supports } from './capabilities.js';
42
42
  import { getEnabledExtraRepos, getExtraPluginsDir, getPluginsDir, getProjectAgentsDir, getProjectPluginsDir, getSystemPluginsDir, } from './state.js';
43
- import { getVersionHomePath } from './versions.js';
43
+ import { getVersionHomePath } from './installations/versions.js';
44
44
  import { toPortableKey } from './platform/index.js';
45
45
  import { compileRulesForProject } from './rules/compile.js';
46
46
  import { syncProjectResourcesToAgent } from './project-resources.js';
47
- import { discoverPluginsInDir, hasPluginExecSurfaces, inspectPluginCapabilities } from './plugins.js';
48
- import { MARKETPLACE_NAME, PROJECT_MARKETPLACE_NAME, SYSTEM_MARKETPLACE_NAME, addPluginToSettings, copyPluginToMarketplace, marketplaceNameFor, marketplaceRoot, pluginInstallDir, registerMarketplace, removePluginFromSettings, syncMarketplaceManifest, } from './plugin-marketplace.js';
47
+ import { discoverPluginsInDir, hasPluginExecSurfaces, inspectPluginCapabilities } from './plugins/plugins.js';
48
+ import { MARKETPLACE_NAME, PROJECT_MARKETPLACE_NAME, SYSTEM_MARKETPLACE_NAME, addPluginToSettings, copyPluginToMarketplace, marketplaceNameFor, marketplaceRoot, pluginInstallDir, registerMarketplace, removePluginFromSettings, syncMarketplaceManifest, } from './plugins/plugin-marketplace.js';
49
49
  /**
50
50
  * Run the launch-time project compile. Safe to call on every agent launch:
51
51
  * each step is idempotent and skips when its inputs are missing.
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Project pull — fast-forward every fleet checkout of a named project.
3
+ *
4
+ * Safety contract (binding for all paths in this module):
5
+ * - Dirty trees are blocked immediately (no fetch attempted).
6
+ * - Only checkouts on the remote's default branch are fast-forwarded.
7
+ * - Local commits ahead of upstream block the pull.
8
+ * - Fast-forward ONLY — no rebase, no reset, no history rewrite.
9
+ * - Missing checkouts are reported, never cloned.
10
+ * - Malformed or partial peer envelopes fail CLOSED and LOUD (the parse
11
+ * returns `valid: false`, so the peer lands in `parseFailed` and drives a
12
+ * non-zero exit — never an empty result set that reads as "nothing to do").
13
+ * - Git hook symlinks are NEVER installed during a pull.
14
+ *
15
+ * The fan-out extends the `projects status` seam: the same
16
+ * `projectRepoTargetsForDef` expansion drives both commands, so they
17
+ * always operate on exactly the same set of directories.
18
+ */
19
+ import { type ProjectRepoTarget } from './projects.js';
20
+ import { type RemoteAgentsJsonParseResult } from './remote-agents-json.js';
21
+ export type ProjectPullStatus = 'updated' | 'current' | 'missing' | 'blocked' | 'failed';
22
+ export interface ProjectPullResult {
23
+ /** Machine that ran the pull (from {@link machineId}). */
24
+ host: string;
25
+ /** Home-relative path — re-roots on each fleet device. */
26
+ path: string;
27
+ /** Expected GitHub slug, echoed from the target for reference. */
28
+ expectedSlug?: string;
29
+ status: ProjectPullStatus;
30
+ branch?: string;
31
+ upstream?: string;
32
+ /** Short commit hash HEAD was at before the pull. */
33
+ before?: string;
34
+ /** Short commit hash HEAD is at after the pull. */
35
+ after?: string;
36
+ /** Human-readable status or error; present when status is not `current`. */
37
+ message?: string;
38
+ }
39
+ /** The wire format this machine emits for the fleet fan-out. Fail-closed on parse. */
40
+ export interface ProjectPullEnvelope {
41
+ schemaVersion: 1;
42
+ kind: 'project-pull';
43
+ /** The emitting machine's {@link machineId} — verified by the caller. */
44
+ machine: string;
45
+ /** Hash of the target list the caller sent — verified before trusting results. */
46
+ targetFingerprint: string;
47
+ results: ProjectPullResult[];
48
+ }
49
+ /**
50
+ * Deterministic hash of a target list so the orchestrator can verify the peer
51
+ * pulled the same set of directories it intended to request. Computed from the
52
+ * canonical `path\0expectedSlug` representation, sorted so insertion order does
53
+ * not matter.
54
+ */
55
+ export declare function fingerprintTargets(targets: ProjectRepoTarget[]): string;
56
+ /**
57
+ * Encode a target list as the single `--targets` argument the orchestrating
58
+ * `pull` hands to a peer's hidden `pull-local`.
59
+ *
60
+ * A target is a `{ path, expectedSlug }` PAIR, and both halves must survive the
61
+ * hop: `expectedSlug` is what makes the peer refuse to fast-forward a directory
62
+ * whose `origin` is a different repo, and it is also hashed into
63
+ * {@link fingerprintTargets}. Sending bare paths therefore broke the fan-out
64
+ * twice over — slug verification silently became a no-op on every peer, and the
65
+ * peer's slug-less fingerprint could never match the caller's, so
66
+ * {@link parseProjectPullEnvelope} discarded the peer's whole result set. JSON
67
+ * keeps the pair intact; both transports quote it as one argument
68
+ * (`shellQuote` on bash, `powershellQuote` inside a base64 `-EncodedCommand`
69
+ * on Windows), so no escaping is owed here.
70
+ */
71
+ export declare function encodePullTargets(targets: ProjectRepoTarget[]): string;
72
+ /**
73
+ * The exact `agents …` argv the fleet fan-out runs on each peer. Owned here,
74
+ * beside the decoder, so the two halves of the hop cannot drift apart and a
75
+ * test can exercise the real caller-side arguments rather than a retyped copy.
76
+ */
77
+ export declare function pullLocalArgs(targets: ProjectRepoTarget[]): string[];
78
+ /**
79
+ * Decode the `--targets` argument back into targets on the peer. THROWS on any
80
+ * malformed input rather than returning a partial list: a peer that cannot tell
81
+ * exactly which directories it was asked to pull must fail loudly, not
82
+ * fast-forward a guessed subset.
83
+ */
84
+ export declare function decodePullTargets(raw: string): ProjectRepoTarget[];
85
+ /**
86
+ * Pull each target on THIS machine, sequentially (concurrent worktree writes
87
+ * can corrupt the git index). Returns one result per target.
88
+ *
89
+ * Missing checkouts (no directory or no `.git`) are reported as `missing` and
90
+ * skipped — never cloned. A slug mismatch (the directory's `origin` remote does
91
+ * not match the expected slug) is reported as `blocked`.
92
+ */
93
+ export declare function pullProjectTargets(targets: ProjectRepoTarget[], host?: string): Promise<ProjectPullResult[]>;
94
+ export declare function buildPullEnvelope(results: ProjectPullResult[], targets: ProjectRepoTarget[]): ProjectPullEnvelope;
95
+ /**
96
+ * Parse a peer's `projects pull-local --json` stdout. Fails CLOSED **and
97
+ * LOUD**: any structural anomaly (wrong schema version, wrong kind, machine
98
+ * mismatch, fingerprint mismatch, non-array results, malformed rows) returns
99
+ * `{ items: [], valid: false }` rather than silently accepting a partial or
100
+ * spoofed payload.
101
+ *
102
+ * `valid: false` is what makes the failure visible. A bare `[]` normalizes to
103
+ * `{ items: [], valid: true }` in `normalizeRemoteAgentsJsonParse`, so the peer
104
+ * would be recorded as having answered with nothing to report — indistinguishable
105
+ * from a device that genuinely had no work, even though it had already run
106
+ * `git fetch` + `merge --ff-only` or hit a real `blocked`/`failed`. Returning
107
+ * the result shape instead lands the peer in `parseFailed`, which the caller
108
+ * both prints and treats as a non-zero exit.
109
+ */
110
+ export declare function parseProjectPullEnvelope(stdout: string, machine: string, opts?: {
111
+ expectedFingerprint?: string;
112
+ }): RemoteAgentsJsonParseResult<ProjectPullResult>;
113
+ /**
114
+ * Returns `true` when every pull completed successfully (`updated` or
115
+ * `current`). `missing` paths are skipped by design — their absence does not
116
+ * count as a failure. `blocked` and `failed` paths drive a non-zero exit.
117
+ */
118
+ export declare function projectPullComplete(results: ProjectPullResult[]): boolean;
119
+ /**
120
+ * Print a human-readable per-host / per-path pull summary to stdout, followed
121
+ * by a one-line counts footer. Does not exit — the caller controls the exit code.
122
+ *
123
+ * `unavailableDevices` never answered (offline, no CLI, timed out).
124
+ * `unverifiedDevices` DID answer but their envelope failed verification, so
125
+ * their real outcome is unknown — a strictly worse state than silence, and the
126
+ * one the caller turns into a non-zero exit.
127
+ */
128
+ export declare function printProjectPullSummary(projectName: string, results: ProjectPullResult[], unavailableDevices: string[], unverifiedDevices?: string[]): void;
@@ -0,0 +1,384 @@
1
+ /**
2
+ * Project pull — fast-forward every fleet checkout of a named project.
3
+ *
4
+ * Safety contract (binding for all paths in this module):
5
+ * - Dirty trees are blocked immediately (no fetch attempted).
6
+ * - Only checkouts on the remote's default branch are fast-forwarded.
7
+ * - Local commits ahead of upstream block the pull.
8
+ * - Fast-forward ONLY — no rebase, no reset, no history rewrite.
9
+ * - Missing checkouts are reported, never cloned.
10
+ * - Malformed or partial peer envelopes fail CLOSED and LOUD (the parse
11
+ * returns `valid: false`, so the peer lands in `parseFailed` and drives a
12
+ * non-zero exit — never an empty result set that reads as "nothing to do").
13
+ * - Git hook symlinks are NEVER installed during a pull.
14
+ *
15
+ * The fan-out extends the `projects status` seam: the same
16
+ * `projectRepoTargetsForDef` expansion drives both commands, so they
17
+ * always operate on exactly the same set of directories.
18
+ */
19
+ import * as crypto from 'crypto';
20
+ import * as fs from 'fs';
21
+ import * as path from 'path';
22
+ import chalk from 'chalk';
23
+ import { expandLocalHome } from './project-root.js';
24
+ import { getRepoCommit, getRemoteUrl, pullRepo } from './git.js';
25
+ import { parseOwnerRepoFromRemote } from './registry.js';
26
+ import { machineId } from './machine-id.js';
27
+ // ---------------------------------------------------------------------------
28
+ // Target fingerprint
29
+ // ---------------------------------------------------------------------------
30
+ /**
31
+ * Deterministic hash of a target list so the orchestrator can verify the peer
32
+ * pulled the same set of directories it intended to request. Computed from the
33
+ * canonical `path\0expectedSlug` representation, sorted so insertion order does
34
+ * not matter.
35
+ */
36
+ export function fingerprintTargets(targets) {
37
+ const lines = targets
38
+ .map((t) => `${t.path}\0${t.expectedSlug ?? ''}`)
39
+ .sort();
40
+ return crypto.createHash('sha256').update(lines.join('\n')).digest('hex').slice(0, 16);
41
+ }
42
+ // ---------------------------------------------------------------------------
43
+ // Target wire encoding (the `pull` → `pull-local` CLI-arg boundary)
44
+ // ---------------------------------------------------------------------------
45
+ /**
46
+ * Encode a target list as the single `--targets` argument the orchestrating
47
+ * `pull` hands to a peer's hidden `pull-local`.
48
+ *
49
+ * A target is a `{ path, expectedSlug }` PAIR, and both halves must survive the
50
+ * hop: `expectedSlug` is what makes the peer refuse to fast-forward a directory
51
+ * whose `origin` is a different repo, and it is also hashed into
52
+ * {@link fingerprintTargets}. Sending bare paths therefore broke the fan-out
53
+ * twice over — slug verification silently became a no-op on every peer, and the
54
+ * peer's slug-less fingerprint could never match the caller's, so
55
+ * {@link parseProjectPullEnvelope} discarded the peer's whole result set. JSON
56
+ * keeps the pair intact; both transports quote it as one argument
57
+ * (`shellQuote` on bash, `powershellQuote` inside a base64 `-EncodedCommand`
58
+ * on Windows), so no escaping is owed here.
59
+ */
60
+ export function encodePullTargets(targets) {
61
+ return JSON.stringify(targets.map((t) => (t.expectedSlug === undefined ? { path: t.path } : { path: t.path, expectedSlug: t.expectedSlug })));
62
+ }
63
+ /**
64
+ * The exact `agents …` argv the fleet fan-out runs on each peer. Owned here,
65
+ * beside the decoder, so the two halves of the hop cannot drift apart and a
66
+ * test can exercise the real caller-side arguments rather than a retyped copy.
67
+ */
68
+ export function pullLocalArgs(targets) {
69
+ return ['projects', 'pull-local', '--json', '--targets', encodePullTargets(targets)];
70
+ }
71
+ /**
72
+ * Decode the `--targets` argument back into targets on the peer. THROWS on any
73
+ * malformed input rather than returning a partial list: a peer that cannot tell
74
+ * exactly which directories it was asked to pull must fail loudly, not
75
+ * fast-forward a guessed subset.
76
+ */
77
+ export function decodePullTargets(raw) {
78
+ let parsed;
79
+ try {
80
+ parsed = JSON.parse(raw);
81
+ }
82
+ catch {
83
+ throw new Error('not valid JSON');
84
+ }
85
+ if (!Array.isArray(parsed))
86
+ throw new Error('expected a JSON array of targets');
87
+ return parsed.map((x, i) => {
88
+ if (!x || typeof x !== 'object' || Array.isArray(x))
89
+ throw new Error(`target ${i} is not an object`);
90
+ const o = x;
91
+ if (typeof o.path !== 'string' || o.path.length === 0)
92
+ throw new Error(`target ${i} has no "path"`);
93
+ if (o.expectedSlug !== undefined && typeof o.expectedSlug !== 'string') {
94
+ throw new Error(`target ${i} has a non-string "expectedSlug"`);
95
+ }
96
+ return o.expectedSlug === undefined
97
+ ? { path: o.path }
98
+ : { path: o.path, expectedSlug: o.expectedSlug };
99
+ });
100
+ }
101
+ // ---------------------------------------------------------------------------
102
+ // Local pull
103
+ // ---------------------------------------------------------------------------
104
+ /**
105
+ * Pull each target on THIS machine, sequentially (concurrent worktree writes
106
+ * can corrupt the git index). Returns one result per target.
107
+ *
108
+ * Missing checkouts (no directory or no `.git`) are reported as `missing` and
109
+ * skipped — never cloned. A slug mismatch (the directory's `origin` remote does
110
+ * not match the expected slug) is reported as `blocked`.
111
+ */
112
+ export async function pullProjectTargets(targets, host = machineId()) {
113
+ const results = [];
114
+ for (const target of targets) {
115
+ const absPath = expandLocalHome(target.path);
116
+ // Missing directory or not a git repo: report and skip.
117
+ if (!fs.existsSync(absPath) || !fs.existsSync(path.join(absPath, '.git'))) {
118
+ results.push({
119
+ host,
120
+ path: target.path,
121
+ expectedSlug: target.expectedSlug,
122
+ status: 'missing',
123
+ message: 'Checkout not present on this device',
124
+ });
125
+ continue;
126
+ }
127
+ // Slug verification: if the project def declares a slug, the directory's
128
+ // origin must match it. A mismatch means the path hosts a different repo
129
+ // and we must not fast-forward it under a wrong project's command.
130
+ // An unreadable remote URL or an unrecognised remote shape is also blocked —
131
+ // we cannot confirm the checkout is the right repo, so we fail closed.
132
+ if (target.expectedSlug) {
133
+ const remoteUrl = await getRemoteUrl(absPath);
134
+ if (remoteUrl === null) {
135
+ results.push({
136
+ host,
137
+ path: target.path,
138
+ expectedSlug: target.expectedSlug,
139
+ status: 'blocked',
140
+ message: `Slug verification failed: no origin remote found (expected ${target.expectedSlug})`,
141
+ });
142
+ continue;
143
+ }
144
+ const actualSlug = parseOwnerRepoFromRemote(remoteUrl);
145
+ if (actualSlug === null) {
146
+ results.push({
147
+ host,
148
+ path: target.path,
149
+ expectedSlug: target.expectedSlug,
150
+ status: 'blocked',
151
+ message: `Slug verification failed: cannot parse remote URL "${remoteUrl}" (expected ${target.expectedSlug})`,
152
+ });
153
+ continue;
154
+ }
155
+ if (actualSlug.toLowerCase() !== target.expectedSlug.toLowerCase()) {
156
+ results.push({
157
+ host,
158
+ path: target.path,
159
+ expectedSlug: target.expectedSlug,
160
+ status: 'blocked',
161
+ message: `Slug mismatch: expected ${target.expectedSlug}, found ${actualSlug}`,
162
+ });
163
+ continue;
164
+ }
165
+ }
166
+ // Capture HEAD before the pull for the before→after diff in output.
167
+ // getRepoCommit already returns an 8-char short hash and never throws.
168
+ const beforeRaw = await getRepoCommit(absPath);
169
+ const before = beforeRaw === 'unknown' ? undefined : beforeRaw;
170
+ // Fast-forward in strict mode. The option contract is documented on PullRepoOptions.
171
+ let pull;
172
+ try {
173
+ pull = await pullRepo(absPath, { mode: 'default-branch-fast-forward' });
174
+ }
175
+ catch (err) {
176
+ results.push({
177
+ host,
178
+ path: target.path,
179
+ expectedSlug: target.expectedSlug,
180
+ status: 'failed',
181
+ before,
182
+ message: err.message,
183
+ });
184
+ continue;
185
+ }
186
+ if (!pull.success) {
187
+ results.push({
188
+ host,
189
+ path: target.path,
190
+ expectedSlug: target.expectedSlug,
191
+ status: 'blocked',
192
+ branch: pull.branch,
193
+ before,
194
+ message: pull.error,
195
+ });
196
+ continue;
197
+ }
198
+ const after = pull.commit;
199
+ // Both are 8-char short hashes; equal → already up-to-date.
200
+ const status = before !== undefined && before !== after ? 'updated' : 'current';
201
+ results.push({
202
+ host,
203
+ path: target.path,
204
+ expectedSlug: target.expectedSlug,
205
+ status,
206
+ branch: pull.branch,
207
+ before,
208
+ after,
209
+ });
210
+ }
211
+ return results;
212
+ }
213
+ // ---------------------------------------------------------------------------
214
+ // Envelope builder (used by the hidden `pull-local` peer command)
215
+ // ---------------------------------------------------------------------------
216
+ export function buildPullEnvelope(results, targets) {
217
+ return {
218
+ schemaVersion: 1,
219
+ kind: 'project-pull',
220
+ machine: machineId(),
221
+ targetFingerprint: fingerprintTargets(targets),
222
+ results,
223
+ };
224
+ }
225
+ // ---------------------------------------------------------------------------
226
+ // Envelope parser (fail-closed — every validation failure returns [])
227
+ // ---------------------------------------------------------------------------
228
+ /**
229
+ * Parse a peer's `projects pull-local --json` stdout. Fails CLOSED **and
230
+ * LOUD**: any structural anomaly (wrong schema version, wrong kind, machine
231
+ * mismatch, fingerprint mismatch, non-array results, malformed rows) returns
232
+ * `{ items: [], valid: false }` rather than silently accepting a partial or
233
+ * spoofed payload.
234
+ *
235
+ * `valid: false` is what makes the failure visible. A bare `[]` normalizes to
236
+ * `{ items: [], valid: true }` in `normalizeRemoteAgentsJsonParse`, so the peer
237
+ * would be recorded as having answered with nothing to report — indistinguishable
238
+ * from a device that genuinely had no work, even though it had already run
239
+ * `git fetch` + `merge --ff-only` or hit a real `blocked`/`failed`. Returning
240
+ * the result shape instead lands the peer in `parseFailed`, which the caller
241
+ * both prints and treats as a non-zero exit.
242
+ */
243
+ export function parseProjectPullEnvelope(stdout, machine, opts = {}) {
244
+ const rejected = { items: [], valid: false };
245
+ let parsed;
246
+ try {
247
+ parsed = JSON.parse(stdout);
248
+ }
249
+ catch {
250
+ return rejected;
251
+ }
252
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
253
+ return rejected;
254
+ const env = parsed;
255
+ // Every field is load-bearing — a partial envelope is not trusted.
256
+ if (env.schemaVersion !== 1)
257
+ return rejected;
258
+ if (env.kind !== 'project-pull')
259
+ return rejected;
260
+ if (typeof env.machine !== 'string' || env.machine !== machine)
261
+ return rejected;
262
+ if (opts.expectedFingerprint !== undefined && env.targetFingerprint !== opts.expectedFingerprint)
263
+ return rejected;
264
+ if (!Array.isArray(env.results))
265
+ return rejected;
266
+ const validStatuses = new Set(['updated', 'current', 'missing', 'blocked', 'failed']);
267
+ const items = [];
268
+ for (const x of env.results) {
269
+ // A malformed ROW is the same class of failure as a malformed envelope:
270
+ // dropping it would hide one directory's real outcome inside an otherwise
271
+ // healthy-looking answer.
272
+ if (!x || typeof x !== 'object' || Array.isArray(x))
273
+ return rejected;
274
+ const r = x;
275
+ if (typeof r.path !== 'string')
276
+ return rejected;
277
+ if (typeof r.status !== 'string' || !validStatuses.has(r.status))
278
+ return rejected;
279
+ const result = {
280
+ host: machine,
281
+ path: r.path,
282
+ status: r.status,
283
+ };
284
+ if (typeof r.expectedSlug === 'string')
285
+ result.expectedSlug = r.expectedSlug;
286
+ if (typeof r.branch === 'string')
287
+ result.branch = r.branch;
288
+ if (typeof r.upstream === 'string')
289
+ result.upstream = r.upstream;
290
+ if (typeof r.before === 'string')
291
+ result.before = r.before;
292
+ if (typeof r.after === 'string')
293
+ result.after = r.after;
294
+ if (typeof r.message === 'string')
295
+ result.message = r.message;
296
+ items.push(result);
297
+ }
298
+ return { items, valid: true };
299
+ }
300
+ // ---------------------------------------------------------------------------
301
+ // Exit-code predicate
302
+ // ---------------------------------------------------------------------------
303
+ /**
304
+ * Returns `true` when every pull completed successfully (`updated` or
305
+ * `current`). `missing` paths are skipped by design — their absence does not
306
+ * count as a failure. `blocked` and `failed` paths drive a non-zero exit.
307
+ */
308
+ export function projectPullComplete(results) {
309
+ return results.every((r) => r.status !== 'blocked' && r.status !== 'failed');
310
+ }
311
+ // ---------------------------------------------------------------------------
312
+ // Human-readable output
313
+ // ---------------------------------------------------------------------------
314
+ function statusIcon(s) {
315
+ switch (s) {
316
+ case 'updated': return chalk.green('✓');
317
+ case 'current': return chalk.dim('·');
318
+ case 'missing': return chalk.yellow('?');
319
+ case 'blocked': return chalk.red('✗');
320
+ case 'failed': return chalk.red('✗');
321
+ }
322
+ }
323
+ function statusLabel(r) {
324
+ switch (r.status) {
325
+ case 'updated': {
326
+ const diff = r.before && r.after ? chalk.dim(` ${r.before}→${r.after}`) : '';
327
+ const br = r.branch ? ` (${r.branch})` : '';
328
+ return `${chalk.green('updated')}${br}${diff}`;
329
+ }
330
+ case 'current': {
331
+ const br = r.branch ? chalk.dim(` (${r.branch})`) : '';
332
+ return `${chalk.dim('already current')}${br}`;
333
+ }
334
+ case 'missing':
335
+ return chalk.yellow('missing — skipped');
336
+ case 'blocked':
337
+ return `${chalk.red('blocked')}${r.message ? chalk.dim(` — ${r.message}`) : ''}`;
338
+ case 'failed':
339
+ return `${chalk.red('failed')}${r.message ? chalk.dim(` — ${r.message}`) : ''}`;
340
+ }
341
+ }
342
+ /**
343
+ * Print a human-readable per-host / per-path pull summary to stdout, followed
344
+ * by a one-line counts footer. Does not exit — the caller controls the exit code.
345
+ *
346
+ * `unavailableDevices` never answered (offline, no CLI, timed out).
347
+ * `unverifiedDevices` DID answer but their envelope failed verification, so
348
+ * their real outcome is unknown — a strictly worse state than silence, and the
349
+ * one the caller turns into a non-zero exit.
350
+ */
351
+ export function printProjectPullSummary(projectName, results, unavailableDevices, unverifiedDevices = []) {
352
+ const counts = { updated: 0, current: 0, missing: 0, blocked: 0, failed: 0 };
353
+ for (const r of results)
354
+ counts[r.status]++;
355
+ console.log(chalk.bold(`${projectName}`));
356
+ const hosts = [...new Set(results.map((r) => r.host))].sort();
357
+ for (const host of hosts) {
358
+ const hostResults = results.filter((r) => r.host === host);
359
+ const hasProblems = hostResults.some((r) => r.status === 'blocked' || r.status === 'failed');
360
+ const hostLabel = hasProblems ? chalk.red(host) : chalk.cyan(host);
361
+ console.log(` ${hostLabel}`);
362
+ for (const r of hostResults) {
363
+ console.log(` ${statusIcon(r.status)} ${chalk.dim(r.path)} ${statusLabel(r)}`);
364
+ }
365
+ }
366
+ if (unavailableDevices.length > 0) {
367
+ console.log(chalk.gray(` unavailable: ${unavailableDevices.join(', ')}`));
368
+ }
369
+ if (unverifiedDevices.length > 0) {
370
+ console.log(chalk.red(` unverified: ${unverifiedDevices.join(', ')} — answered, but the result could not be verified; their checkouts may have changed`));
371
+ }
372
+ const parts = [];
373
+ if (counts.updated)
374
+ parts.push(chalk.green(`${counts.updated} updated`));
375
+ if (counts.current)
376
+ parts.push(chalk.dim(`${counts.current} current`));
377
+ if (counts.missing)
378
+ parts.push(chalk.yellow(`${counts.missing} missing`));
379
+ if (counts.blocked)
380
+ parts.push(chalk.red(`${counts.blocked} blocked`));
381
+ if (counts.failed)
382
+ parts.push(chalk.red(`${counts.failed} failed`));
383
+ console.log(` ${parts.join(chalk.dim(' · '))}`);
384
+ }