@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
@@ -14,11 +14,11 @@ import * as os from 'os';
14
14
  import * as path from 'path';
15
15
  import { SSH_TARGET_RE, assertValidSshTarget } from '../lib/ssh-exec.js';
16
16
  import { quoteWin32ExecArg, composeWin32CommandLine } from '../lib/platform/index.js';
17
- import { ensureDaemonStarted, isDaemonRunning } from '../lib/daemon.js';
17
+ import { ensureDaemonStarted, isDaemonRunning } from '../lib/daemon/daemon.js';
18
18
  import { parseHostsOption, remoteResolveEnv, remoteSecretsRaw, remoteSecretsStream, resolveHostSshTarget, } from '../lib/secrets/remote.js';
19
19
  import { resolveBundleForPush, pushResolvedBundleToHost, bundleEnvToDotenv } from '../lib/secrets/push.js';
20
20
  export { bundleEnvToDotenv };
21
- import { bundleBackend, bundleExists, bundleItemStore, bundlePolicy, deleteBundle, describeBundle, keychainItemsForBundle, keychainRef, listBundles, healKeychainBundleMetadataAclOnce, isHeadlessSecretsContext, migrateLegacyBundles, parseDotenv, readAndResolveBundleEnv, readBundle, readBundleIfDecryptable, reAclBundleItems, renameBundle, rotateBundleSecret, sanitizeProcessEnv, validateBundleName, validateEnvKey, validateExpiresFutureDated, validateSecretType, writeBundle, writeBundleWithItems, SECRET_TYPES, } from '../lib/secrets/bundles.js';
21
+ import { bundleBackend, bundleExists, bundleItemStore, bundlePolicy, deleteBundle, describeBundle, keychainItemsForBundle, keychainRef, listBundles, healKeychainBundleMetadataAclOnce, isHeadlessSecretsContext, isAgentInvocationContext, migrateLegacyBundles, parseDotenv, readAndResolveBundleEnv, readBundle, readBundleIfDecryptable, reAclBundleItems, renameBundle, rotateBundleSecret, sanitizeProcessEnv, validateBundleName, validateEnvKey, validateExpiresFutureDated, validateSecretType, writeBundle, writeBundleWithItems, SECRET_TYPES, } from '../lib/secrets/bundles.js';
22
22
  import { parseListFilters, bundleMatchesFilter, bundleExpiry, filterIsActive, describeFilter, parseSortField, sortBundles, SORT_FIELDS, REF_KINDS, DEFAULT_EXPIRING_DAYS, } from '../lib/secrets/list-filter.js';
23
23
  import { encryptForFallback, decryptForFallback } from '../lib/secrets/filestore.js';
24
24
  import { getKeychainToken, hasKeychainToken, secretsKeychainItem, setKeychainToken, maybeAutoRekey, } from '../lib/secrets/index.js';
@@ -30,7 +30,7 @@ import { saveSession, deleteBundleSessions, deleteAllSessions } from '../lib/sec
30
30
  import { getCliVersionFresh } from '../lib/version.js';
31
31
  import { readMeta } from '../lib/state.js';
32
32
  import { parseDuration } from '../lib/hooks/cache.js';
33
- import { emit, query } from '../lib/events.js';
33
+ import { emit, query } from '../lib/feed/events.js';
34
34
  import { emitSecretAudit } from '../lib/secrets/audit.js';
35
35
  import { SYNC_PASSPHRASE_ENV, resolveSyncPassphraseFromEnv } from '../lib/secrets/sync-passphrase.js';
36
36
  import { getBundleUsage, getAllBundleUsage, getUsageHistory, SECRET_USAGE_EVENTS, } from '../lib/secrets/usage-db.js';
@@ -38,11 +38,11 @@ import { frequentlyPromptedBundles } from '../lib/secrets/unlock-hints.js';
38
38
  import { registerCommandGroups, setHelpSections } from '../lib/help.js';
39
39
  import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
40
40
  import { discoverSyncedBundles, importSyncedBundle, } from '../lib/secrets/icloud-import.js';
41
- import { getVaultSession, vaultExists } from '../lib/secrets/vault.js';
42
41
  import { registerSecretsSyncCommands } from './secrets-sync.js';
43
42
  import { registerSecretsMigrateAclCommand } from './secrets-migrate.js';
44
43
  import { registerSecretsImportKeyringCommand } from './secrets-import.js';
45
44
  import { registerSecretsRotatePassphraseCommand } from './secrets-rotate-passphrase.js';
45
+ import { registerSecretsVaultCommands } from './secrets-vault.js';
46
46
  /** Prompt the user for a secret value with masked input. Requires an interactive TTY. */
47
47
  async function promptForSecret(message) {
48
48
  if (!isInteractiveTerminal()) {
@@ -144,7 +144,7 @@ async function resolveVault(vaultOpt) {
144
144
  /** Read all available data from stdin synchronously, trimmed. */
145
145
  /**
146
146
  * Read the raw `.env` text for `import --from <path|->`. A `-` reads the .env
147
- * from stdin (the SSH push path: `export --host` pipes the resolved dotenv over
147
+ * from stdin (the SSH push path: `export --device` pipes the resolved dotenv over
148
148
  * ssh stdin, which has no `/dev/stdin` on a Windows remote); any other value is
149
149
  * a filesystem path.
150
150
  */
@@ -262,7 +262,7 @@ function maybePrintSyncedHint(name, stillPresent) {
262
262
  }
263
263
  }
264
264
  /**
265
- * Build the remote `agents secrets unlock` argv for `unlock --host`. `--all`
265
+ * Build the remote `agents secrets unlock` argv for `unlock --device`. `--all`
266
266
  * forwards verbatim; otherwise the explicit bundle names. A `--ttl` is passed
267
267
  * through as-is so the REMOTE parses its own duration (its platform rules, its
268
268
  * defaults). Shared with the command action so the wiring is unit-testable
@@ -323,10 +323,10 @@ export function buildRemoteUnlockArgs(names, opts) {
323
323
  ];
324
324
  }
325
325
  /**
326
- * Build the remote `agents secrets list` argv for `list --host`. Every filter
326
+ * Build the remote `agents secrets list` argv for `list --device`. Every filter
327
327
  * must be forwarded: `browseRemote` sends this argv verbatim, so a flag left out
328
328
  * here is not an error — the remote just lists everything, and
329
- * `secrets list --host zion --expired` reports every bundle on zion as expired.
329
+ * `secrets list --device zion --expired` reports every bundle on zion as expired.
330
330
  * Silent and wrong beats loud and wrong only for the person who wrote the bug.
331
331
  *
332
332
  * Values pass through unparsed so the REMOTE validates them under its own rules,
@@ -923,14 +923,17 @@ export function registerSecretsCommands(program) {
923
923
  # Stop a noisy automation bundle from prompting every run: ask once a week
924
924
  agents secrets policy prod hold
925
925
 
926
- # Eval the bundle into your current shell
927
- eval "$(agents secrets export prod --plaintext)"
926
+ # Run a one-off command with secrets injected (values ride the child env, never stdout)
927
+ agents secrets exec prod -- ./deploy.sh
928
928
 
929
- # Push the bundle to remote machine(s) over SSH (lands as a native bundle there)
930
- agents secrets export prod --host yosemite-s0 --host yosemite-s1 --force
929
+ # Need one value inside a script? Read it under injection, not with a printer
930
+ agents secrets exec prod -- printenv STRIPE_API_KEY
931
931
 
932
- # Run a one-off command with secrets injected
933
- agents secrets exec prod -- ./deploy.sh
932
+ # Deliberately reveal values at your terminal (interactive only)
933
+ agents secrets view prod --reveal
934
+
935
+ # Push the bundle to remote machine(s) over SSH (lands as a native bundle there)
936
+ agents secrets export prod --device yosemite-s0 --device yosemite-s1 --force
934
937
  `,
935
938
  notes: `
936
939
  Bundles are containers; secrets are the variables inside them. Keychain values
@@ -983,15 +986,15 @@ export function registerSecretsCommands(program) {
983
986
  { title: 'Raw item commands', names: ['get', 'set'] },
984
987
  { title: 'Sync commands', names: ['push', 'pull', 'remote-list'] },
985
988
  { title: 'Utilities', names: ['exec', 'mcp', 'generate', 'migrate-acl'] },
989
+ { title: 'Synced vault', names: ['vault'] },
986
990
  ]);
991
+ registerSecretsVaultCommands(cmd);
987
992
  const listCmd = cmd
988
993
  .command('list [query]')
989
994
  .alias('ls')
990
- .description('List configured secrets bundles, optionally filtered (use --host/--hosts for other machines over SSH)')
991
- .option('--host <target>', 'List bundles on a remote host over SSH (enrolled `agents hosts` name, ssh-config alias, or user@host)')
992
- .option('--hosts <list>', 'Comma-separated hosts to list in one shot, e.g. yosemite-s0,yosemite-s1')
993
- .option('--device <target>', 'Alias for --host')
994
- .option('--devices <list>', 'Alias for --hosts')
995
+ .description('List configured secrets bundles, optionally filtered (use --device/--devices for other machines over SSH)')
996
+ .option('--device <target>', 'List bundles on a remote device over SSH (enrolled `agents hosts` name, ssh-config alias, or user@host)')
997
+ .option('--devices <list>', 'Comma-separated devices to list in one shot, e.g. yosemite-s0,yosemite-s1')
995
998
  .option('--json', 'Emit machine-readable JSON (bundle metadata only — never secret values) instead of the table')
996
999
  .option('--policy <list>', "Only these prompt policies (comma-separated): hold, always, never. '--policy never' is the audit for bundles that read with no Touch ID at all")
997
1000
  .option('--backend <list>', 'Only these backends (comma-separated): keychain, file, vault')
@@ -1005,7 +1008,7 @@ export function registerSecretsCommands(program) {
1005
1008
  .addOption(new Option('--sort <field>', 'Sort by: name (default), used (most recently used), uses (most frequently accessed), created, updated, expiry').choices([...SORT_FIELDS]))
1006
1009
  .option('-n, --limit <n>', 'Show at most this many bundles (after filtering and sorting)')
1007
1010
  .action(async (query, opts) => {
1008
- const targets = parseHostsOption(opts);
1011
+ const targets = parseHostsOption({ device: opts.device, devices: opts.devices });
1009
1012
  if (targets.length > 0) {
1010
1013
  // Forward the filters — browseRemote sends this argv verbatim, so a
1011
1014
  // dropped flag would make the remote silently list everything.
@@ -1028,7 +1031,7 @@ export function registerSecretsCommands(program) {
1028
1031
  // hold-state filter would answer from no data — every bundle would look
1029
1032
  // not-held. Refuse rather than return a confidently wrong list.
1030
1033
  if (filter.held !== undefined && process.platform !== 'darwin') {
1031
- throw new Error('--held/--not-held need the secrets-agent, which is macOS-only. Run it on a Mac, or with --host <mac>.');
1034
+ throw new Error('--held/--not-held need the secrets-agent, which is macOS-only. Run it on a Mac, or with --device <mac>.');
1032
1035
  }
1033
1036
  }
1034
1037
  catch (err) {
@@ -1160,16 +1163,16 @@ export function registerSecretsCommands(program) {
1160
1163
  agents secrets list --policy hold --backend file --expiring
1161
1164
 
1162
1165
  # Same filters, on another machine, machine-readable
1163
- agents secrets list --expired --host mac-mini --json
1166
+ agents secrets list --expired --device mac-mini --json
1164
1167
  `,
1165
1168
  notes: `
1166
1169
  - Filters compose: every flag you add narrows the list further.
1167
1170
  - An unknown value is an error, not an empty list — '--policy hodl' names the valid set.
1168
- - --held/--not-held read live broker state, so they need macOS. Use --host <mac> from elsewhere.
1171
+ - --held/--not-held read live broker state, so they need macOS. Use --device <mac> from elsewhere.
1169
1172
  - --expired and --expiring are different questions: already lapsed vs coming due. The EXPIRING column counts both and turns red once anything has lapsed.
1170
1173
  - --unused matches bundles never read at all, not just old ones.
1171
1174
  - Filters apply before --json, so the JSON is the exact twin of the table.
1172
- - Filters are forwarded over --host, so a remote list narrows the same way.
1175
+ - Filters are forwarded over --device, so a remote list narrows the same way.
1173
1176
  - --sort used|uses read the value-free usage read-model (~/.agents/secrets/secrets.db); see 'agents secrets activity'.
1174
1177
  `,
1175
1178
  });
@@ -1221,16 +1224,23 @@ export function registerSecretsCommands(program) {
1221
1224
  .command('view [name]')
1222
1225
  .alias('show')
1223
1226
  .description('Show a bundle. Keychain values are masked by default — pass --reveal to see them.')
1224
- .option('--reveal', 'Print keychain-backed values in the clear (TTY only unless --plaintext)')
1225
- .option('--plaintext', 'Allow --reveal in non-interactive shells (use with care)')
1227
+ .option('--reveal', 'Print keychain-backed values in the clear (interactive terminal only)')
1226
1228
  .option('--json', 'Emit machine-readable JSON (values masked unless --reveal) instead of the human view')
1227
- .option('--host <target>', 'Show a bundle on a remote host over SSH (enrolled `agents hosts` name, ssh-config alias, or user@host)')
1228
- .option('--hosts <list>', 'Comma-separated hosts to show in one shot, e.g. yosemite-s0,yosemite-s1')
1229
- .option('--device <target>', 'Alias for --host')
1230
- .option('--devices <list>', 'Alias for --hosts')
1229
+ .option('--device <target>', 'Show a bundle on a remote device over SSH (enrolled `agents hosts` name, ssh-config alias, or user@host)')
1230
+ .option('--devices <list>', 'Comma-separated devices to show in one shot, e.g. yosemite-s0,yosemite-s1')
1231
1231
  .action(async (name, opts) => {
1232
1232
  try {
1233
- const targets = parseHostsOption(opts);
1233
+ // `--reveal` is a deliberate HUMAN reveal: interactive terminal only, and
1234
+ // never inside an agent session (an agent under tmux has a TTY, so the
1235
+ // env markers are the load-bearing check). The old `--plaintext` escape
1236
+ // that allowed a piped/non-TTY reveal is removed (RUSH-2774) — printed
1237
+ // values land in an agent's context and session transcript.
1238
+ if (opts.reveal && (isAgentInvocationContext() || !isInteractiveTerminal())) {
1239
+ console.error(chalk.red('--reveal prints values in the clear and needs a real interactive terminal outside an agent session.'));
1240
+ console.error(chalk.dim('Run the consuming command under injection instead: agents secrets exec <bundle> -- <cmd>'));
1241
+ process.exit(1);
1242
+ }
1243
+ const targets = parseHostsOption({ device: opts.device, devices: opts.devices });
1234
1244
  if (targets.length > 0) {
1235
1245
  if (!name) {
1236
1246
  console.error(chalk.red('A bundle name is required when viewing a remote host (interactive pick needs a local terminal).'));
@@ -1239,14 +1249,13 @@ export function registerSecretsCommands(program) {
1239
1249
  const args = ['view', name];
1240
1250
  if (opts.reveal)
1241
1251
  args.push('--reveal');
1242
- if (opts.plaintext)
1243
- args.push('--plaintext');
1244
1252
  if (opts.json)
1245
1253
  args.push('--json');
1246
1254
  // With --reveal, force a TTY so the remote keychain prompt can surface
1247
- // (and the remote's "--reveal in a non-TTY needs --plaintext" gate is
1248
- // satisfied) only when this side is itself interactive.
1249
- const tty = Boolean(opts.reveal) && Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY);
1255
+ // and the remote's own interactive-terminal gate is satisfied. The
1256
+ // early gate above already proved this side is an interactive,
1257
+ // non-agent terminal.
1258
+ const tty = Boolean(opts.reveal);
1250
1259
  // `--reveal` streams the plaintext value back over ssh stdout — a
1251
1260
  // secret-bearing read that must pin the host key and not multiplex,
1252
1261
  // whether it runs on the interactive (tty) or `--plaintext` non-tty path.
@@ -1289,14 +1298,10 @@ export function registerSecretsCommands(program) {
1289
1298
  emitSecretAudit({ event: 'secrets.view', bundle: bundle.name, operation: opts.json ? 'view --json' : 'view', source: 'view', status: 'success' });
1290
1299
  if (opts.json) {
1291
1300
  // Machine-readable discovery for agents. Values are null unless --reveal
1292
- // (which still routes through the same non-TTY/plaintext gate + audit
1293
- // event as the human path). Gated on the explicit flag, not stdout.isTTY,
1294
- // so a piped human view never silently swaps formats.
1301
+ // (already gated above: interactive terminal, outside an agent session).
1302
+ // Gated on the explicit flag, not stdout.isTTY, so a piped human view
1303
+ // never silently swaps formats.
1295
1304
  const reveal = Boolean(opts.reveal);
1296
- if (reveal && !isInteractiveTerminal() && !opts.plaintext) {
1297
- console.error(chalk.red('--reveal in a non-TTY requires --plaintext.'));
1298
- process.exit(1);
1299
- }
1300
1305
  const revealed = new Map();
1301
1306
  if (reveal) {
1302
1307
  const { env } = readAndResolveBundleEnv(bundle.name, {
@@ -1382,7 +1387,7 @@ export function registerSecretsCommands(program) {
1382
1387
  if (bundle.backend === 'file')
1383
1388
  console.log(chalk.gray('backend: file (encrypted at rest; headless reads via a machine-local key, or AGENTS_SECRETS_PASSPHRASE if set — no Touch ID)'));
1384
1389
  if (bundle.backend === 'vault')
1385
- console.log(chalk.gray('storage: synced (age-encrypted ~/.agents/vault.age; needs agents login)'));
1390
+ console.log(chalk.gray('storage: synced (age-encrypted ~/.agents/vault.age; needs `agents secrets vault unlock`)'));
1386
1391
  if (bundlePolicy(bundle) === 'never') {
1387
1392
  console.log(chalk.red.bold('policy: never — NO biometry ACL; reads are silent (no Touch ID, no user-presence check). Automation-only.'));
1388
1393
  }
@@ -1416,10 +1421,6 @@ export function registerSecretsCommands(program) {
1416
1421
  return;
1417
1422
  }
1418
1423
  const reveal = Boolean(opts.reveal);
1419
- if (reveal && !isInteractiveTerminal() && !opts.plaintext) {
1420
- console.error(chalk.red('--reveal in a non-TTY requires --plaintext.'));
1421
- process.exit(1);
1422
- }
1423
1424
  // An explicit human `view --reveal` at a terminal is a deliberate value
1424
1425
  // access: resolve interactively (one Touch ID) so a locked bundle shows
1425
1426
  // its values. Under an agent (AGENTS_RUNTIME) or headless (no TTY) it
@@ -1492,50 +1493,38 @@ export function registerSecretsCommands(program) {
1492
1493
  });
1493
1494
  cmd
1494
1495
  .command('get <item> [key]')
1495
- .description('Print one secret value for shell hooks/automation. One arg = a raw keychain item by name; two args = one KEY out of a bundle (`get <bundle> <KEY>`). Cross-platform.')
1496
+ .description('Print one raw keychain item by name, for shell hooks/automation. Cross-platform. Bundle values are never printed run commands under `agents secrets exec` instead.')
1496
1497
  .action((item, key) => {
1497
- if (key === undefined) {
1498
- // Raw keychain item path unchanged.
1499
- try {
1500
- // Routes through the platform keychain layer: macOS reads bare items
1501
- // via /usr/bin/security (no Touch ID), Linux via secret-tool with the
1502
- // encrypted-file fallback. The value goes to stdout (newline-terminated
1503
- // so `$(agents secrets get NAME)` captures it cleanly); diagnostics go
1504
- // to stderr so they never pollute the captured value.
1505
- const value = getKeychainToken(item);
1506
- // Raw item reads bypass readAndResolveBundleEnv, so audit here too.
1507
- // `item` is the keychain service name, never the value.
1508
- emitSecretAudit({ event: 'secrets.get', item, source: 'raw-item', status: 'success' });
1509
- process.stdout.write(value.endsWith('\n') ? value : `${value}\n`);
1510
- }
1511
- catch {
1512
- // Missing item is a normal, quiet outcome for a hook probe: exit 1,
1513
- // print nothing to stdout. Callers test the exit code / empty capture.
1514
- process.exit(1);
1515
- }
1516
- return;
1498
+ if (key !== undefined) {
1499
+ // The bundle-key form (`get <bundle> <KEY>`) is REMOVED (RUSH-2774): a
1500
+ // one-liner that prints a bundle credential is the exfiltration path
1501
+ // this change closes, for every caller. The injection replacement is
1502
+ // just as short for a human script and never touches this stdout.
1503
+ console.error(chalk.red(`'secrets get <bundle> <KEY>' has been removed — printing a bundle value to stdout is the exfiltration path this blocks.`));
1504
+ console.error(chalk.dim(`Use: agents secrets exec ${item} -- printenv ${key} (or run the consuming command directly under exec)`));
1505
+ process.exit(1);
1517
1506
  }
1518
- // Bundle-key path: `get <bundle> <KEY>` prints exactly one resolved value.
1519
- // Ungated like the raw path (it IS the automation primitive); the
1520
- // `secrets.get` audit event is emitted inside readAndResolveBundleEnv.
1507
+ // Raw keychain item path a single ad-hoc token, not a credential bundle.
1508
+ // Deliberately NOT agent-gated (unlike the removed bundle-key form): shell
1509
+ // hooks that run inside agent sessions inherit the session env markers and
1510
+ // legitimately capture a raw item into their own variables (e.g. the
1511
+ // posthog analytics hook), where the value never reaches the transcript.
1512
+ // A single ad-hoc token is the accepted narrower residual (RUSH-2774).
1521
1513
  try {
1522
- if (!bundleExists(item) && !(vaultExists() && !getVaultSession().loggedIn)) {
1523
- console.error(chalk.red(`Secrets bundle '${item}' not found.`));
1524
- process.exit(1);
1525
- }
1526
- // `secrets get` is the scriptable automation primitive ($(agents secrets
1527
- // get bundle KEY)). Every read is broker-only; a locked bundle points at
1528
- // the explicit unlock command instead of raising Touch ID.
1529
- const { env } = readAndResolveBundleEnv(item, { caller: 'secrets get', keys: [key], keyMode: 'storage', agentOnly: true });
1530
- if (!(key in env)) {
1531
- console.error(chalk.red(`Key '${key}' not in bundle '${item}'.`));
1532
- process.exit(1);
1533
- }
1534
- const value = env[key];
1514
+ // Routes through the platform keychain layer: macOS reads bare items
1515
+ // via /usr/bin/security (no Touch ID), Linux via secret-tool with the
1516
+ // encrypted-file fallback. The value goes to stdout (newline-terminated
1517
+ // so `$(agents secrets get NAME)` captures it cleanly); diagnostics go
1518
+ // to stderr so they never pollute the captured value.
1519
+ const value = getKeychainToken(item);
1520
+ // Raw item reads bypass readAndResolveBundleEnv, so audit here too.
1521
+ // `item` is the keychain service name, never the value.
1522
+ emitSecretAudit({ event: 'secrets.get', item, source: 'raw-item', status: 'success' });
1535
1523
  process.stdout.write(value.endsWith('\n') ? value : `${value}\n`);
1536
1524
  }
1537
- catch (err) {
1538
- console.error(chalk.red(err.message));
1525
+ catch {
1526
+ // Missing item is a normal, quiet outcome for a hook probe: exit 1,
1527
+ // print nothing to stdout. Callers test the exit code / empty capture.
1539
1528
  process.exit(1);
1540
1529
  }
1541
1530
  });
@@ -2019,12 +2008,28 @@ Examples:
2019
2008
  .option('--backend <backend>', 'When creating the bundle: keychain or file (defaults to agents.yaml secrets.backend)')
2020
2009
  .option('--synced', 'When creating the bundle, store it in the age-encrypted synced secrets file')
2021
2010
  .option('--force', 'Overwrite an existing key in the bundle')
2011
+ .option('--policy <policy>', 'Set the bundle prompt policy as part of this import (always, hold, or never)')
2012
+ .option('--i-understand', 'Confirm an import with policy never (no biometry ACL)')
2022
2013
  .option('--purge', 'With --from icloud: delete the iCloud copies after a successful import (iCloud propagates the deletion to your other devices)')
2023
2014
  .option('--from-file <path>', `Import from an AES-256-GCM encrypted offline bundle file (needs ${SYNC_PASSPHRASE_ENV}; symmetric counterpart of export --to-file)`)
2024
- .option('--from-ssh', 'Pull the bundle from a fleet peer over SSH and import it locally (requires --host)')
2025
- .option('--host <peer>', 'SSH peer to pull from when using --from-ssh (host alias or user@host)')
2015
+ .option('--from-ssh', 'Pull the bundle from a fleet peer over SSH and import it locally (requires --device)')
2016
+ .option('--device <peer>', 'SSH peer to pull from when using --from-ssh (device alias or user@host)')
2026
2017
  .action(async (bundleName, opts) => {
2027
2018
  try {
2019
+ const importPolicy = opts.policy ? parsePolicyOpt(opts.policy) : undefined;
2020
+ const policyAck = assertNeverPolicyAcknowledged(importPolicy, {
2021
+ iUnderstand: opts.iUnderstand,
2022
+ interactive: isInteractiveTerminal(),
2023
+ });
2024
+ if (policyAck === 'prompt' && !(await confirmNeverPolicyInteractive(bundleName ?? 'imported bundle'))) {
2025
+ console.error(chalk.yellow('Aborted.'));
2026
+ return;
2027
+ }
2028
+ const applyImportPolicy = (bundle) => {
2029
+ if (importPolicy)
2030
+ bundle.policy = importPolicy;
2031
+ return bundle;
2032
+ };
2028
2033
  // A single import can name only one source. --from-file / --from-ssh are
2029
2034
  // early-return paths, so guard them against each other and the --from /
2030
2035
  // --from-1password pair (which parseImportSource guards on its own).
@@ -2043,24 +2048,24 @@ Examples:
2043
2048
  }
2044
2049
  const env = importBundleFromFile(opts.fromFile, passphrase);
2045
2050
  const resolvedBundleName = bundleName ?? (await pickBundleName('import into'));
2046
- const bundle = resolveImportBundle(resolvedBundleName, opts.backend, opts.synced, opts.force);
2051
+ const bundle = applyImportPolicy(resolveImportBundle(resolvedBundleName, opts.backend, opts.synced, opts.force));
2047
2052
  const { added, skipped } = applyEnvToBundle(bundle, env, opts);
2048
2053
  emitSecretAudit({ event: 'secrets.import', bundle: bundle.name, operation: 'import --from-file', source: 'file', status: 'success', keyCount: added });
2049
2054
  console.log(chalk.green(`Imported ${added} key(s) from file${skipped ? `, skipped ${skipped} (already set, pass --force)` : ''}.`));
2050
2055
  return;
2051
2056
  }
2052
2057
  if (opts.fromSsh) {
2053
- if (!opts.host) {
2054
- throw new Error('--from-ssh requires --host <peer>.');
2058
+ if (!opts.device) {
2059
+ throw new Error('--from-ssh requires --device <peer>.');
2055
2060
  }
2056
- assertValidSshTarget(opts.host);
2061
+ assertValidSshTarget(opts.device);
2057
2062
  const resolvedBundleName = bundleName ?? (await pickBundleName('import into'));
2058
- const target = await resolveHostSshTarget(opts.host);
2059
- const env = await remoteResolveEnv(target, resolvedBundleName, { osLookupName: opts.host });
2060
- const bundle = resolveImportBundle(resolvedBundleName, opts.backend, opts.synced, opts.force);
2063
+ const target = await resolveHostSshTarget(opts.device);
2064
+ const env = await remoteResolveEnv(target, resolvedBundleName, { osLookupName: opts.device });
2065
+ const bundle = applyImportPolicy(resolveImportBundle(resolvedBundleName, opts.backend, opts.synced, opts.force));
2061
2066
  const { added, skipped } = applyEnvToBundle(bundle, env, opts);
2062
- emitSecretAudit({ event: 'secrets.import', bundle: bundle.name, operation: 'import --from-ssh', source: 'ssh', host: opts.host, status: 'success', keyCount: added });
2063
- console.log(chalk.green(`Imported ${added} key(s) from ${opts.host}${skipped ? `, skipped ${skipped} (already set, pass --force)` : ''}.`));
2067
+ emitSecretAudit({ event: 'secrets.import', bundle: bundle.name, operation: 'import --from-ssh', source: 'ssh', host: opts.device, status: 'success', keyCount: added });
2068
+ console.log(chalk.green(`Imported ${added} key(s) from ${opts.device}${skipped ? `, skipped ${skipped} (already set, pass --force)` : ''}.`));
2064
2069
  return;
2065
2070
  }
2066
2071
  const source = parseImportSource(opts);
@@ -2083,9 +2088,9 @@ Examples:
2083
2088
  const resolvedBundleName = bundleName ?? (await pickBundleName('import into'));
2084
2089
  // resolveImportBundle inherits an existing bundle's backend (and refuses
2085
2090
  // to downgrade keychain -> file) or creates it with the requested backend
2086
- // so a single `import --backend file` works (what `export --host ...
2091
+ // so a single `import --backend file` works (what `export --device ...
2087
2092
  // --remote-backend file` drives on the remote).
2088
- const bundle = resolveImportBundle(resolvedBundleName, opts.backend, opts.synced, opts.force);
2093
+ const bundle = applyImportPolicy(resolveImportBundle(resolvedBundleName, opts.backend, opts.synced, opts.force));
2089
2094
  if (source.kind === '1password') {
2090
2095
  assertOpAvailable();
2091
2096
  const vault = await resolveVault(source.vault);
@@ -2118,24 +2123,23 @@ Examples:
2118
2123
  });
2119
2124
  cmd
2120
2125
  .command('export [bundle]')
2121
- .description('Resolve a bundle and print KEY=VALUE lines, push it to a 1Password vault with --to-1password, or push it to remote machine(s) over SSH with --host.')
2122
- .option('--plaintext', 'Acknowledge that the resolved values will be printed in the clear (shell export mode)')
2126
+ .description('Move a bundle without exposing it: push to remote machine(s) over SSH with --device, to a 1Password vault with --to-1password, or to an encrypted file with --to-file.')
2127
+ // Both transport flags are hidden, not documented: the ONLY caller is the
2128
+ // machine-to-machine SSH resolve (remoteResolveEnv / verifyRemoteKeychainPush),
2129
+ // which sets AGENTS_SECRETS_REMOTE_TRANSPORT=1 on the remote invocation. The
2130
+ // old public shell-eval mode (`eval "$(agents secrets export … --plaintext)"`)
2131
+ // was the top agent-exfiltration path and is removed (RUSH-2774).
2132
+ .addOption(new Option('--plaintext', 'internal remote-resolve transport').hideHelp())
2133
+ .addOption(new Option('--format <json>', 'internal remote-resolve transport').hideHelp())
2123
2134
  .option('--to-1password', 'Push every key in the bundle as a PASSWORD item in a 1Password vault')
2124
2135
  .option('--vault <name>', '1Password vault name (used with --to-1password)')
2125
- .option('--host <target...>', 'Push the bundle over SSH to this target (host alias or user@host); repeatable for multiple machines')
2126
- .option('--device <target...>', 'Alias for --host; repeatable')
2127
- .option('--remote-backend <backend>', 'Backend for the bundle on the remote (with --host): keychain (default) or file. file is headless-readable via the remote\'s machine-local key; it forwards AGENTS_SECRETS_PASSPHRASE over stdin only if set (opt-in).', 'keychain')
2128
- .option('--force', 'Overwrite existing keys/items on the target (used with --to-1password and --host)')
2129
- .option('--format <shell|json>', 'Output for --plaintext export: shell (default) or json (lossless, machine-readable; used by remote resolve)', 'shell')
2136
+ .option('--device <target...>', 'Push the bundle over SSH to this device (host alias or user@host); repeatable for multiple machines')
2137
+ .option('--remote-backend <backend>', 'Backend for the bundle on the remote device (with --device): keychain (default) or file. file is headless-readable via the remote\'s machine-local key; it forwards AGENTS_SECRETS_PASSPHRASE over stdin only if set (opt-in).', 'keychain')
2138
+ .option('--force', 'Overwrite existing keys/items on the target (used with --to-1password and --device)')
2130
2139
  .option('--to-file <path>', `Write the bundle as an AES-256-GCM encrypted offline file (needs ${SYNC_PASSPHRASE_ENV}; symmetric counterpart of import --from-file)`)
2131
2140
  .action(async (bundleName, opts) => {
2132
2141
  try {
2133
- // `--device` is an alias for `--host` (fleet vocabulary parity with
2134
- // `agents run --device`); fold it into the host list up front so
2135
- // every downstream branch sees one resolved target list.
2136
- if (opts.device?.length)
2137
- opts.host = [...(opts.host ?? []), ...opts.device];
2138
- const { readAndResolveBundleEnv, bundleToEnvPrefix, isReservedEnvName } = await import('../lib/secrets/bundles.js');
2142
+ const { readAndResolveBundleEnv } = await import('../lib/secrets/bundles.js');
2139
2143
  const resolvedBundleName = bundleName ?? (await pickBundleName('export'));
2140
2144
  if (opts.toFile) {
2141
2145
  // Transport, not the local store's master key — see lib/secrets/sync-passphrase.ts.
@@ -2150,10 +2154,10 @@ Examples:
2150
2154
  console.log(chalk.green(`Exported ${Object.keys(env).length} key(s) to ${opts.toFile}`));
2151
2155
  return;
2152
2156
  }
2153
- // The presence of --host selects SSH push: --host is the destination
2157
+ // The presence of --device selects SSH push: --device is the destination
2154
2158
  // and carries the mode (no separate --to-ssh needed — it would be
2155
- // strictly redundant since SSH always requires at least one host).
2156
- const hosts = opts.host ?? [];
2159
+ // strictly redundant since SSH always requires at least one device).
2160
+ const hosts = opts.device ?? [];
2157
2161
  if (hosts.length > 0) {
2158
2162
  for (const h of hosts)
2159
2163
  assertValidSshTarget(h);
@@ -2203,7 +2207,7 @@ Examples:
2203
2207
  }
2204
2208
  console.log(chalk.green(`${host} -> '${resolvedBundleName}': ${out.message}`));
2205
2209
  }
2206
- emitSecretAudit({ event: 'secrets.export', bundle: resolvedBundleName, operation: `export --host ${hosts.join(',')}`, source: 'ssh', host: hosts.join(','), status: failures > 0 ? 'error' : 'success', keyCount });
2210
+ emitSecretAudit({ event: 'secrets.export', bundle: resolvedBundleName, operation: `export --device ${hosts.join(',')}`, source: 'ssh', host: hosts.join(','), status: failures > 0 ? 'error' : 'success', keyCount });
2207
2211
  if (failures > 0)
2208
2212
  process.exit(1);
2209
2213
  return;
@@ -2242,41 +2246,36 @@ Examples:
2242
2246
  console.log(chalk.green(`Exported to 1Password vault '${vault}': ${parts.join(', ')}.`));
2243
2247
  return;
2244
2248
  }
2245
- if (opts.format && opts.format !== 'shell' && opts.format !== 'json') {
2246
- console.error(chalk.red(`Invalid --format ${JSON.stringify(opts.format)}. Expected 'shell' or 'json'.`));
2247
- process.exit(1);
2248
- }
2249
- if (!opts.plaintext) {
2250
- console.error(chalk.red('export prints secrets in the clear and requires --plaintext (works for TTY and pipes alike).'));
2249
+ // The shell-eval print mode (`eval "$(agents secrets export … --plaintext)"`)
2250
+ // is REMOVED (RUSH-2774): a whole bundle printed to stdout lands in a
2251
+ // coding agent's context and session transcript, and it was the single
2252
+ // most-copied exfiltration one-liner on the fleet. The sole surviving
2253
+ // stdout emitter is the machine-to-machine SSH resolve transport, which
2254
+ // requires the remote-invocation marker AND a non-agent context both
2255
+ // set only by remoteResolveEnv / verifyRemoteKeychainPush building the
2256
+ // remote command. Everything else gets the refusal with the paved paths.
2257
+ const isTransportCall = process.env.AGENTS_SECRETS_REMOTE_TRANSPORT === '1' &&
2258
+ Boolean(opts.plaintext) &&
2259
+ opts.format === 'json' &&
2260
+ !isAgentInvocationContext();
2261
+ if (!isTransportCall) {
2262
+ console.error(chalk.red('export no longer prints values. Pick a destination:'));
2263
+ console.error(chalk.red(' --device <host> | --to-1password | --to-file <path>'));
2264
+ console.error(chalk.dim(`Run a command with the values injected instead: agents secrets exec ${resolvedBundleName} -- <cmd>`));
2265
+ console.error(chalk.dim(`Reveal one value at your terminal: agents secrets view ${resolvedBundleName} --reveal`));
2251
2266
  process.exit(1);
2252
2267
  }
2253
- // `agents secrets export --plaintext` is what release/CI scripts eval.
2254
- // Every caller is broker-only, including a plain shell, so export never
2255
- // raises Touch ID on the interactive user's screen.
2268
+ // Machine-readable form consumed by `remoteResolveEnv` over SSH: single
2269
+ // object of injected env KEY -> value; values verbatim. Broker-only, so a
2270
+ // headless remote read fails fast rather than raising Touch ID. The
2271
+ // resolve emits its own `secrets.get` audit on the remote; no export
2272
+ // event here — that would tally every peer pull as an export.
2256
2273
  const { env } = readAndResolveBundleEnv(resolvedBundleName, {
2257
- caller: `export to shell`,
2274
+ caller: 'remote resolve transport',
2258
2275
  keyMode: 'process',
2259
2276
  agentOnly: true,
2260
2277
  });
2261
- if (opts.format === 'json') {
2262
- // Machine-readable form consumed by `remoteResolveEnv` over SSH.
2263
- // Single object of injected env KEY -> value; values verbatim.
2264
- process.stdout.write(JSON.stringify(env));
2265
- return;
2266
- }
2267
- const prefix = bundleToEnvPrefix(resolvedBundleName);
2268
- for (const [k, v] of Object.entries(env)) {
2269
- const exportKey = isReservedEnvName(k) ? `${prefix}_${k}` : k;
2270
- const needsQuotes = /[\s$`"'\\|&;<>(){}[\]!#~]/.test(v);
2271
- const output = needsQuotes ? `'${v.replace(/'/g, `'\\''`)}'` : v;
2272
- process.stdout.write(`export ${exportKey}=${output}\n`);
2273
- }
2274
- // Record the shell export (the `eval "$(...)"` path). The `--format json`
2275
- // branch above is the machine-to-machine remote-resolve transport (already
2276
- // counted as an access on this machine by the resolve above), so it is
2277
- // deliberately not counted here as an export — that would tally every peer
2278
- // pull as an export.
2279
- emitSecretAudit({ event: 'secrets.export', bundle: resolvedBundleName, operation: 'export --plaintext', source: 'shell', status: 'success', keyCount: Object.keys(env).length });
2278
+ process.stdout.write(JSON.stringify(env));
2280
2279
  }
2281
2280
  catch (err) {
2282
2281
  if (isPromptCancelled(err))
@@ -2287,8 +2286,8 @@ Examples:
2287
2286
  });
2288
2287
  cmd
2289
2288
  .command('exec <bundle> [command...]')
2290
- .description('Run a command with the bundle\'s secrets injected into the environment (use --host to resolve the bundle from a remote machine, ephemerally)')
2291
- .option('--host <target>', 'Resolve <bundle> on a remote host over SSH and inject it (ephemeral — never stored on this machine)')
2289
+ .description('Run a command with the bundle\'s secrets injected into the environment (use --device to resolve the bundle from a remote machine, ephemerally)')
2290
+ .option('--device <target>', 'Resolve <bundle> on a remote device over SSH and inject it (ephemeral — never stored on this machine)')
2292
2291
  .option('--keys <keys>', 'Inject only this comma-separated subset of keys (e.g. KEY1,KEY2). Missing keys are an error.')
2293
2292
  .option('--allow-expired', 'Inject keys even if their expiry date has passed (overrides the pre-run expiry abort).')
2294
2293
  .allowUnknownOption()
@@ -2303,14 +2302,14 @@ Examples:
2303
2302
  ? execOpts.keys.split(',').map((k) => k.trim()).filter(Boolean)
2304
2303
  : undefined;
2305
2304
  let secretEnv;
2306
- if (execOpts.host) {
2305
+ if (execOpts.device) {
2307
2306
  // Least-privilege flags do not yet cross the SSH resolver —
2308
2307
  // silently applying them would inject the full remote env or an
2309
2308
  // expired key. Fail loud so the user can drop the flag or run
2310
2309
  // locally instead.
2311
2310
  const { assertRemoteBundleFlagsUnsupported } = await import('../lib/secrets/bundles.js');
2312
- assertRemoteBundleFlagsUnsupported(bundleName, execOpts.host, { keys: keysSubset, allowExpired: execOpts.allowExpired }, { keysFlag: '--keys', allowExpiredFlag: '--allow-expired' });
2313
- secretEnv = await remoteResolveEnv(await resolveHostSshTarget(execOpts.host), bundleName, { osLookupName: execOpts.host });
2311
+ assertRemoteBundleFlagsUnsupported(bundleName, execOpts.device, { keys: keysSubset, allowExpired: execOpts.allowExpired }, { keysFlag: '--keys', allowExpiredFlag: '--allow-expired' });
2312
+ secretEnv = await remoteResolveEnv(await resolveHostSshTarget(execOpts.device), bundleName, { osLookupName: execOpts.device });
2314
2313
  }
2315
2314
  else {
2316
2315
  const { readAndResolveBundleEnv } = await import('../lib/secrets/bundles.js');
@@ -2497,14 +2496,14 @@ Examples:
2497
2496
  });
2498
2497
  cmd
2499
2498
  .command('unlock [names...]')
2500
- .description('Hold a bundle in the secrets-agent after one Touch ID, so concurrent runs read it without re-prompting (macOS). With --host, unlock FILE-backed bundle(s) on a remote (the passphrase prompt surfaces over the SSH TTY); keychain/biometry bundles are GUI-only and can\'t be remote-unlocked.')
2499
+ .description('Hold a bundle in the secrets-agent after one Touch ID, so concurrent runs read it without re-prompting (macOS). With --device, unlock FILE-backed bundle(s) on a remote (the passphrase prompt surfaces over the SSH TTY); keychain/biometry bundles are GUI-only and can\'t be remote-unlocked.')
2501
2500
  .option('--ttl <duration>', 'How long to hold it (e.g. 30m, 8h, 3d). Default 7d.')
2502
2501
  .option('--until <date>', 'Hold until this absolute date or timestamp (for example 2026-08-06T12:00:00Z). Mutually exclusive with --ttl.')
2503
2502
  .option('--durable', 'Keep the unlock across sleep + reboot too (default: survives upgrade/restart but re-locks on sleep). Set secrets.agent.durable in agents.yaml to make this the default.')
2504
2503
  .option('--agent <agent>', 'Narrow the unlock to ONE harness type (for example claude, codex, or kimi). Default: the grant is global — every harness and a plain shell can read it, so one Touch ID covers them all.')
2505
2504
  .option('--keys <keys>', 'Hold ONLY this comma-separated subset of the bundle\'s keys instead of the whole bundle. Scopes exactly one bundle; fails closed on an unknown key. `secrets status` shows which keys a scoped hold covers.')
2506
2505
  .option('--all', 'Unlock every configured bundle')
2507
- .option('--host <target>', 'Unlock the bundle(s) on this remote machine over SSH instead of locally (file-backed bundles only — the remote\'s passphrase prompt surfaces on your terminal over a -tt session). Single-valued (NOT variadic) so it never swallows the bundle name: `unlock <name> --host <machine>`.')
2506
+ .option('--device <target>', 'Unlock the bundle(s) on this remote device over SSH instead of locally (file-backed bundles only — the remote\'s passphrase prompt surfaces on your terminal over a -tt session). Single-valued (NOT variadic) so it never swallows the bundle name: `unlock <name> --device <machine>`.')
2508
2507
  .action(async (names, opts) => {
2509
2508
  if (opts.ttl && opts.until) {
2510
2509
  console.error(chalk.red('--ttl and --until are mutually exclusive.'));
@@ -2512,10 +2511,10 @@ Examples:
2512
2511
  }
2513
2512
  if (opts.keys !== undefined) {
2514
2513
  // --keys scopes ONE bundle to an explicit subset. It is local-only (a
2515
- // remote --host unlock holds the whole file-backed bundle) and cannot
2514
+ // remote --device unlock holds the whole file-backed bundle) and cannot
2516
2515
  // combine with --all, which would hold every bundle whole.
2517
- if (opts.host) {
2518
- console.error(chalk.red('--keys is local-only; a remote (--host) unlock holds the whole file-backed bundle.'));
2516
+ if (opts.device) {
2517
+ console.error(chalk.red('--keys is local-only; a remote (--device) unlock holds the whole file-backed bundle.'));
2519
2518
  process.exit(1);
2520
2519
  }
2521
2520
  if (opts.all || !names || names.length !== 1) {
@@ -2523,10 +2522,10 @@ Examples:
2523
2522
  process.exit(1);
2524
2523
  }
2525
2524
  }
2526
- // Single-valued (not variadic): a variadic --host greedily consumes the
2527
- // positional bundle name (`unlock --host mac wztest` -> host=[mac,wztest],
2525
+ // Single-valued (not variadic): a variadic --device greedily consumes the
2526
+ // positional bundle name (`unlock --device mac wztest` -> device=[mac,wztest],
2528
2527
  // names=[]). Unlock targets one remote at a time anyway.
2529
- const hosts = opts.host ? [opts.host] : [];
2528
+ const hosts = opts.device ? [opts.device] : [];
2530
2529
  if (hosts.length > 0) {
2531
2530
  // Remote unlock: the REMOTE enforces its own platform rules, so the
2532
2531
  // local darwin-only guard below does NOT apply. Only file-backed
@@ -204,7 +204,7 @@ export function registerSessionsBackfillCommand(sessionsCmd) {
204
204
  tools.action(async (_options, command) => {
205
205
  const options = command.optsWithGlobals();
206
206
  if (options.local && (options.fleet || mergeHosts(options).length > 0)) {
207
- console.error(chalk.red('--local cannot be combined with --fleet, --host, or --device.'));
207
+ console.error(chalk.red('--local cannot be combined with --fleet or --device.'));
208
208
  process.exitCode = 1;
209
209
  return;
210
210
  }