@phnx-labs/agents-cli 1.22.34 → 1.22.37

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 (493) hide show
  1. package/CHANGELOG.md +672 -2
  2. package/README.md +82 -35
  3. package/dist/bin/agents +0 -0
  4. package/dist/bootstrap.d.ts +15 -0
  5. package/dist/bootstrap.js +1211 -0
  6. package/dist/commands/accounts.d.ts +4 -11
  7. package/dist/commands/accounts.js +159 -27
  8. package/dist/commands/attach.js +17 -4
  9. package/dist/commands/audit.d.ts +8 -8
  10. package/dist/commands/audit.js +57 -47
  11. package/dist/commands/browser.js +15 -11
  12. package/dist/commands/cloud.js +1 -1
  13. package/dist/commands/commands.js +0 -11
  14. package/dist/commands/computer-actions.js +33 -0
  15. package/dist/commands/computer-sessions-picker.d.ts +21 -0
  16. package/dist/commands/computer-sessions-picker.js +141 -0
  17. package/dist/commands/computer.d.ts +16 -0
  18. package/dist/commands/computer.js +37 -0
  19. package/dist/commands/config.d.ts +17 -0
  20. package/dist/commands/config.js +401 -0
  21. package/dist/commands/cp.d.ts +84 -0
  22. package/dist/commands/cp.js +231 -0
  23. package/dist/commands/daemon.js +243 -14
  24. package/dist/commands/detach-core.d.ts +1 -1
  25. package/dist/commands/detach-core.js +12 -2
  26. package/dist/commands/detach.js +1 -1
  27. package/dist/commands/doctor.js +64 -5
  28. package/dist/commands/events.d.ts +24 -12
  29. package/dist/commands/events.js +144 -28
  30. package/dist/commands/exec.js +85 -41
  31. package/dist/commands/feed.d.ts +1 -1
  32. package/dist/commands/feed.js +16 -8
  33. package/dist/commands/focus.d.ts +33 -1
  34. package/dist/commands/focus.js +177 -17
  35. package/dist/commands/funnel.d.ts +1 -1
  36. package/dist/commands/go.js +9 -8
  37. package/dist/commands/harness-wizard.d.ts +1 -0
  38. package/dist/commands/harness-wizard.js +18 -20
  39. package/dist/commands/harness.d.ts +2 -0
  40. package/dist/commands/harness.js +59 -26
  41. package/dist/commands/hooks.js +0 -11
  42. package/dist/commands/inspect.d.ts +94 -1
  43. package/dist/commands/inspect.js +866 -40
  44. package/dist/commands/lease.d.ts +4 -4
  45. package/dist/commands/lease.js +6 -6
  46. package/dist/commands/login.js +0 -13
  47. package/dist/commands/logs.d.ts +2 -2
  48. package/dist/commands/logs.js +2 -2
  49. package/dist/commands/mcp.js +7 -2
  50. package/dist/commands/memory.js +7 -2
  51. package/dist/commands/message.d.ts +1 -1
  52. package/dist/commands/message.js +37 -6
  53. package/dist/commands/models.js +4 -0
  54. package/dist/commands/monitors.d.ts +0 -1
  55. package/dist/commands/monitors.js +199 -16
  56. package/dist/commands/open.d.ts +18 -0
  57. package/dist/commands/open.js +57 -0
  58. package/dist/commands/packages.js +2 -107
  59. package/dist/commands/plugins.js +6 -3
  60. package/dist/commands/profiles.d.ts +1 -0
  61. package/dist/commands/profiles.js +16 -160
  62. package/dist/commands/projects.d.ts +28 -0
  63. package/dist/commands/projects.js +125 -5
  64. package/dist/commands/reconnect.js +9 -3
  65. package/dist/commands/repo.js +42 -1
  66. package/dist/commands/resource-view.d.ts +17 -0
  67. package/dist/commands/resource-view.js +36 -14
  68. package/dist/commands/resume.js +9 -2
  69. package/dist/commands/routines.d.ts +0 -2
  70. package/dist/commands/routines.js +473 -289
  71. package/dist/commands/secrets.js +52 -9
  72. package/dist/commands/sessions-export.d.ts +47 -0
  73. package/dist/commands/sessions-export.js +116 -6
  74. package/dist/commands/sessions-import.d.ts +17 -0
  75. package/dist/commands/sessions-import.js +114 -6
  76. package/dist/commands/sessions-migrate.d.ts +27 -0
  77. package/dist/commands/sessions-migrate.js +50 -9
  78. package/dist/commands/sessions-picker.js +23 -4
  79. package/dist/commands/sessions-resume.d.ts +5 -3
  80. package/dist/commands/sessions-resume.js +30 -8
  81. package/dist/commands/sessions-watch.d.ts +2 -0
  82. package/dist/commands/sessions-watch.js +53 -0
  83. package/dist/commands/sessions.d.ts +66 -3
  84. package/dist/commands/sessions.js +298 -47
  85. package/dist/commands/set.d.ts +1 -1
  86. package/dist/commands/set.js +2 -2
  87. package/dist/commands/setup-browser.js +2 -2
  88. package/dist/commands/setup-mine.js +0 -1
  89. package/dist/commands/setup-preferences.js +1 -1
  90. package/dist/commands/setup-share.js +34 -4
  91. package/dist/commands/setup.d.ts +5 -0
  92. package/dist/commands/setup.js +26 -0
  93. package/dist/commands/share.d.ts +85 -0
  94. package/dist/commands/share.js +379 -8
  95. package/dist/commands/skills.js +0 -11
  96. package/dist/commands/snapshot.js +1 -1
  97. package/dist/commands/ssh.d.ts +18 -0
  98. package/dist/commands/ssh.js +549 -210
  99. package/dist/commands/sync.d.ts +5 -0
  100. package/dist/commands/sync.js +212 -32
  101. package/dist/commands/teams.d.ts +13 -0
  102. package/dist/commands/teams.js +187 -30
  103. package/dist/commands/tickets.d.ts +2 -0
  104. package/dist/commands/tickets.js +43 -0
  105. package/dist/commands/update.js +2 -4
  106. package/dist/commands/usage.js +5 -3
  107. package/dist/commands/versions.js +11 -5
  108. package/dist/commands/view.js +15 -7
  109. package/dist/commands/webhook.d.ts +2 -2
  110. package/dist/commands/webhook.js +7 -7
  111. package/dist/index.d.ts +18 -2
  112. package/dist/index.js +53 -1264
  113. package/dist/lib/account-catalog.d.ts +18 -0
  114. package/dist/lib/account-catalog.js +38 -0
  115. package/dist/lib/account-provider-registry.d.ts +18 -0
  116. package/dist/lib/account-provider-registry.js +59 -0
  117. package/dist/lib/account-registry.d.ts +40 -0
  118. package/dist/lib/account-registry.js +239 -0
  119. package/dist/lib/account-schema.d.ts +44 -0
  120. package/dist/lib/account-schema.js +91 -0
  121. package/dist/lib/account-state-service.d.ts +21 -0
  122. package/dist/lib/account-state-service.js +60 -0
  123. package/dist/lib/activity.d.ts +1 -1
  124. package/dist/lib/activity.js +8 -1
  125. package/dist/lib/add-dir.d.ts +80 -0
  126. package/dist/lib/add-dir.js +241 -0
  127. package/dist/lib/agent-cli-commands.d.ts +11 -0
  128. package/dist/lib/agent-cli-commands.js +30 -0
  129. package/dist/lib/agents.js +44 -36
  130. package/dist/lib/answer-router.js +1 -1
  131. package/dist/lib/audit/log.d.ts +10 -27
  132. package/dist/lib/audit/log.js +20 -33
  133. package/dist/lib/auth-health.d.ts +9 -2
  134. package/dist/lib/auth-health.js +42 -9
  135. package/dist/lib/auto-pull-worker.js +7 -3
  136. package/dist/lib/auto-pull.d.ts +44 -3
  137. package/dist/lib/auto-pull.js +88 -5
  138. package/dist/lib/binary-shadow.d.ts +28 -0
  139. package/dist/lib/binary-shadow.js +121 -0
  140. package/dist/lib/brand.js +9 -3
  141. package/dist/lib/browser/ipc.d.ts +30 -0
  142. package/dist/lib/browser/ipc.js +81 -4
  143. package/dist/lib/browser/profiles.d.ts +3 -2
  144. package/dist/lib/browser/profiles.js +65 -22
  145. package/dist/lib/browser/service.d.ts +4 -0
  146. package/dist/lib/browser/service.js +30 -3
  147. package/dist/lib/browser/sessions-list.d.ts +22 -2
  148. package/dist/lib/browser/sessions-list.js +54 -6
  149. package/dist/lib/browser/stream.d.ts +2 -0
  150. package/dist/lib/browser/stream.js +1 -0
  151. package/dist/lib/browser/types.d.ts +19 -0
  152. package/dist/lib/byok-usage.d.ts +9 -2
  153. package/dist/lib/byok-usage.js +133 -31
  154. package/dist/lib/cli-entry.d.ts +11 -0
  155. package/dist/lib/cli-entry.js +46 -0
  156. package/dist/lib/cloud/host.d.ts +1 -1
  157. package/dist/lib/cloud/host.js +2 -2
  158. package/dist/lib/cloud/registry.js +1 -1
  159. package/dist/lib/cloud/types.d.ts +1 -1
  160. package/dist/lib/codex-policy.d.ts +9 -1
  161. package/dist/lib/codex-policy.js +17 -2
  162. package/dist/lib/commands.d.ts +2 -0
  163. package/dist/lib/commands.js +2 -0
  164. package/dist/lib/computer/sessions-list.d.ts +150 -0
  165. package/dist/lib/computer/sessions-list.js +363 -0
  166. package/dist/lib/config-keys.d.ts +86 -0
  167. package/dist/lib/config-keys.js +193 -0
  168. package/dist/lib/config-machine-keys.d.ts +12 -0
  169. package/dist/lib/config-machine-keys.js +17 -0
  170. package/dist/lib/config-transfer.js +3 -4
  171. package/dist/lib/crabbox/cli.d.ts +2 -2
  172. package/dist/lib/crabbox/cli.js +3 -3
  173. package/dist/lib/daemon-health.d.ts +22 -0
  174. package/dist/lib/daemon-health.js +33 -3
  175. package/dist/lib/daemon-services.d.ts +50 -0
  176. package/dist/lib/daemon-services.js +147 -0
  177. package/dist/lib/daemon-ticks.d.ts +35 -66
  178. package/dist/lib/daemon-ticks.js +77 -159
  179. package/dist/lib/daemon.d.ts +72 -4
  180. package/dist/lib/daemon.js +792 -202
  181. package/dist/lib/deeplink/register.d.ts +49 -0
  182. package/dist/lib/deeplink/register.js +253 -0
  183. package/dist/lib/deeplink/url.d.ts +34 -0
  184. package/dist/lib/deeplink/url.js +78 -0
  185. package/dist/lib/device-config.d.ts +124 -29
  186. package/dist/lib/device-config.js +346 -141
  187. package/dist/lib/devices/config-migration.d.ts +42 -0
  188. package/dist/lib/devices/config-migration.js +203 -0
  189. package/dist/lib/devices/connect.js +14 -7
  190. package/dist/lib/devices/doctor-findings.d.ts +4 -1
  191. package/dist/lib/devices/doctor-findings.js +20 -1
  192. package/dist/lib/devices/harness-inventory.d.ts +11 -2
  193. package/dist/lib/devices/harness-inventory.js +0 -0
  194. package/dist/lib/devices/health.d.ts +1 -1
  195. package/dist/lib/devices/health.js +1 -1
  196. package/dist/lib/devices/pending.d.ts +22 -1
  197. package/dist/lib/devices/pending.js +67 -2
  198. package/dist/lib/devices/registry.d.ts +0 -25
  199. package/dist/lib/devices/registry.js +2 -83
  200. package/dist/lib/devices/resolve-profile.d.ts +20 -0
  201. package/dist/lib/devices/resolve-profile.js +44 -0
  202. package/dist/lib/devices/rollout-verify.d.ts +101 -0
  203. package/dist/lib/devices/rollout-verify.js +193 -0
  204. package/dist/lib/devices/ssh-config.js +10 -6
  205. package/dist/lib/event-families.d.ts +28 -0
  206. package/dist/lib/event-families.js +159 -0
  207. package/dist/lib/event-stream.d.ts +9 -1
  208. package/dist/lib/event-stream.js +26 -9
  209. package/dist/lib/events.d.ts +9 -1
  210. package/dist/lib/events.js +21 -6
  211. package/dist/lib/exec.d.ts +30 -0
  212. package/dist/lib/exec.js +220 -122
  213. package/dist/lib/feed-broadcast.d.ts +25 -0
  214. package/dist/lib/feed-broadcast.js +33 -0
  215. package/dist/lib/feed.d.ts +1 -1
  216. package/dist/lib/feed.js +12 -2
  217. package/dist/lib/fleet/capture.js +15 -0
  218. package/dist/lib/fleet/manifest.js +9 -0
  219. package/dist/lib/fleet/types.d.ts +9 -0
  220. package/dist/lib/fleet-status.js +10 -8
  221. package/dist/lib/git.d.ts +20 -6
  222. package/dist/lib/git.js +121 -17
  223. package/dist/lib/hosts/dispatch.d.ts +8 -3
  224. package/dist/lib/hosts/dispatch.js +29 -3
  225. package/dist/lib/hosts/logs.d.ts +2 -2
  226. package/dist/lib/hosts/logs.js +2 -2
  227. package/dist/lib/hosts/option.js +1 -1
  228. package/dist/lib/hosts/passthrough.d.ts +2 -3
  229. package/dist/lib/hosts/passthrough.js +10 -24
  230. package/dist/lib/hosts/progress.js +2 -2
  231. package/dist/lib/hosts/providers/devices.d.ts +3 -3
  232. package/dist/lib/hosts/providers/devices.js +18 -12
  233. package/dist/lib/hosts/ready.d.ts +5 -1
  234. package/dist/lib/hosts/ready.js +9 -3
  235. package/dist/lib/hosts/reconcile.d.ts +2 -2
  236. package/dist/lib/hosts/reconcile.js +2 -2
  237. package/dist/lib/hosts/registry.js +14 -9
  238. package/dist/lib/hosts/remote-cmd.js +1 -1
  239. package/dist/lib/hosts/remote-os.d.ts +9 -6
  240. package/dist/lib/hosts/remote-os.js +13 -6
  241. package/dist/lib/hosts/routing-flag.d.ts +23 -0
  242. package/dist/lib/hosts/routing-flag.js +41 -0
  243. package/dist/lib/hosts/run-target.js +1 -1
  244. package/dist/lib/hosts/session-index.js +1 -1
  245. package/dist/lib/hosts/tasks.d.ts +6 -6
  246. package/dist/lib/hosts/tasks.js +4 -4
  247. package/dist/lib/hosts/types.js +1 -2
  248. package/dist/lib/installations/resolve.js +2 -8
  249. package/dist/lib/isolation-boundary-report.js +0 -1
  250. package/dist/lib/linear-cache.d.ts +7 -0
  251. package/dist/lib/linear-cache.js +31 -4
  252. package/dist/lib/linear-project-counts.d.ts +1 -1
  253. package/dist/lib/linear-project-counts.js +2 -3
  254. package/dist/lib/linear-projects.js +1 -1
  255. package/dist/lib/loop.d.ts +2 -2
  256. package/dist/lib/loop.js +1 -1
  257. package/dist/lib/mailbox-target.d.ts +1 -1
  258. package/dist/lib/mailbox-target.js +1 -1
  259. package/dist/lib/manifest.js +7 -4
  260. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  261. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  262. package/dist/lib/menubar/snapshot.d.ts +3 -2
  263. package/dist/lib/menubar/snapshot.js +6 -2
  264. package/dist/lib/migrate-fold.d.ts +33 -0
  265. package/dist/lib/migrate-fold.js +130 -0
  266. package/dist/lib/migrate.d.ts +34 -34
  267. package/dist/lib/migrate.js +106 -128
  268. package/dist/lib/model-tier-overrides.d.ts +1 -1
  269. package/dist/lib/model-tier-overrides.js +15 -2
  270. package/dist/lib/monitors/config.d.ts +17 -3
  271. package/dist/lib/monitors/config.js +52 -18
  272. package/dist/lib/monitors/engine.d.ts +32 -1
  273. package/dist/lib/monitors/engine.js +94 -11
  274. package/dist/lib/monitors/fingerprint.d.ts +46 -0
  275. package/dist/lib/monitors/fingerprint.js +100 -0
  276. package/dist/lib/monitors/remote.d.ts +47 -0
  277. package/dist/lib/monitors/remote.js +81 -0
  278. package/dist/lib/monitors/state.d.ts +39 -0
  279. package/dist/lib/monitors/state.js +56 -0
  280. package/dist/lib/onepassword.d.ts +17 -0
  281. package/dist/lib/onepassword.js +52 -22
  282. package/dist/lib/open-url.js +1 -1
  283. package/dist/lib/picker.d.ts +14 -2
  284. package/dist/lib/picker.js +35 -13
  285. package/dist/lib/placement.d.ts +1 -1
  286. package/dist/lib/placement.js +1 -1
  287. package/dist/lib/pr-land-detach.d.ts +32 -0
  288. package/dist/lib/pr-land-detach.js +79 -0
  289. package/dist/lib/profiles-presets.js +13 -0
  290. package/dist/lib/profiles.d.ts +2 -0
  291. package/dist/lib/profiles.js +44 -1
  292. package/dist/lib/project-focus.d.ts +1 -1
  293. package/dist/lib/project-focus.js +1 -1
  294. package/dist/lib/project-key.d.ts +17 -0
  295. package/dist/lib/project-key.js +26 -0
  296. package/dist/lib/project-probe.d.ts +4 -1
  297. package/dist/lib/project-probe.js +5 -4
  298. package/dist/lib/project-resources.js +2 -18
  299. package/dist/lib/project-root.d.ts +16 -0
  300. package/dist/lib/project-root.js +20 -2
  301. package/dist/lib/projects.d.ts +20 -1
  302. package/dist/lib/projects.js +78 -1
  303. package/dist/lib/refresh-coordinator.d.ts +24 -0
  304. package/dist/lib/refresh-coordinator.js +52 -0
  305. package/dist/lib/registry.d.ts +2 -2
  306. package/dist/lib/registry.js +1 -1
  307. package/dist/lib/resource-aliases.d.ts +13 -0
  308. package/dist/lib/resource-aliases.js +26 -0
  309. package/dist/lib/resources/types.d.ts +1 -1
  310. package/dist/lib/resources.d.ts +16 -0
  311. package/dist/lib/resources.js +66 -10
  312. package/dist/lib/rotate.d.ts +3 -4
  313. package/dist/lib/rotate.js +4 -5
  314. package/dist/lib/routine-activation.d.ts +22 -2
  315. package/dist/lib/routine-activation.js +63 -16
  316. package/dist/lib/routine-context.d.ts +9 -2
  317. package/dist/lib/routine-context.js +21 -6
  318. package/dist/lib/routines-project.d.ts +58 -48
  319. package/dist/lib/routines-project.js +190 -159
  320. package/dist/lib/routines.d.ts +41 -6
  321. package/dist/lib/routines.js +92 -34
  322. package/dist/lib/run-defaults.d.ts +5 -0
  323. package/dist/lib/run-defaults.js +11 -1
  324. package/dist/lib/runner.d.ts +10 -8
  325. package/dist/lib/runner.js +122 -39
  326. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  327. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  328. package/dist/lib/secrets/agent.d.ts +10 -3
  329. package/dist/lib/secrets/agent.js +43 -8
  330. package/dist/lib/secrets/bundles.d.ts +1 -0
  331. package/dist/lib/secrets/bundles.js +92 -80
  332. package/dist/lib/secrets/index.js +32 -9
  333. package/dist/lib/secrets/install-helper.d.ts +4 -4
  334. package/dist/lib/secrets/install-helper.js +4 -4
  335. package/dist/lib/secrets/push.d.ts +40 -6
  336. package/dist/lib/secrets/push.js +58 -11
  337. package/dist/lib/secrets/read-backoff.d.ts +1 -1
  338. package/dist/lib/secrets/read-backoff.js +1 -1
  339. package/dist/lib/secrets/remote.d.ts +24 -0
  340. package/dist/lib/secrets/remote.js +54 -4
  341. package/dist/lib/self-update.d.ts +108 -0
  342. package/dist/lib/self-update.js +276 -1
  343. package/dist/lib/session/active.d.ts +89 -5
  344. package/dist/lib/session/active.js +165 -10
  345. package/dist/lib/session/actor-sidecar.d.ts +1 -1
  346. package/dist/lib/session/actor-sidecar.js +2 -1
  347. package/dist/lib/session/db.d.ts +176 -2
  348. package/dist/lib/session/db.js +484 -17
  349. package/dist/lib/session/discover.js +27 -4
  350. package/dist/lib/session/hook-sessions.js +2 -2
  351. package/dist/lib/session/host-link.d.ts +2 -2
  352. package/dist/lib/session/host-link.js +2 -2
  353. package/dist/lib/session/pid-registry.d.ts +21 -0
  354. package/dist/lib/session/pid-registry.js +57 -0
  355. package/dist/lib/session/remote-active.js +15 -1
  356. package/dist/lib/session/remote.js +2 -2
  357. package/dist/lib/session/session-cache.d.ts +14 -7
  358. package/dist/lib/session/session-cache.js +51 -9
  359. package/dist/lib/session/state.d.ts +2 -2
  360. package/dist/lib/session/state.js +1 -1
  361. package/dist/lib/session/sync/config.d.ts +10 -10
  362. package/dist/lib/session/sync/config.js +10 -10
  363. package/dist/lib/session/sync/r2.d.ts +34 -0
  364. package/dist/lib/session/sync/r2.js +123 -0
  365. package/dist/lib/session/throughput.d.ts +1 -1
  366. package/dist/lib/session/throughput.js +1 -1
  367. package/dist/lib/session/types.d.ts +23 -1
  368. package/dist/lib/session/types.js +15 -0
  369. package/dist/lib/session/viewing-in.d.ts +1 -1
  370. package/dist/lib/session/viewing-in.js +1 -1
  371. package/dist/lib/session/watch.d.ts +101 -0
  372. package/dist/lib/session/watch.js +242 -0
  373. package/dist/lib/share/config.d.ts +4 -0
  374. package/dist/lib/share/config.js +1 -0
  375. package/dist/lib/share/delete.d.ts +93 -0
  376. package/dist/lib/share/delete.js +127 -0
  377. package/dist/lib/share/provision.d.ts +38 -0
  378. package/dist/lib/share/provision.js +50 -0
  379. package/dist/lib/share/publish.d.ts +40 -1
  380. package/dist/lib/share/publish.js +116 -3
  381. package/dist/lib/share/worker-template.js +71 -6
  382. package/dist/lib/shims.d.ts +17 -3
  383. package/dist/lib/shims.js +136 -27
  384. package/dist/lib/signin-badge.js +3 -2
  385. package/dist/lib/skills.js +2 -0
  386. package/dist/lib/smart-launch.d.ts +8 -8
  387. package/dist/lib/smart-launch.js +80 -59
  388. package/dist/lib/ssh-exec.d.ts +1 -1
  389. package/dist/lib/ssh-exec.js +1 -1
  390. package/dist/lib/staleness/prune.d.ts +88 -0
  391. package/dist/lib/staleness/prune.js +69 -0
  392. package/dist/lib/staleness/registry.d.ts +1 -1
  393. package/dist/lib/staleness/writers/commands.js +34 -2
  394. package/dist/lib/staleness/writers/hooks.js +16 -0
  395. package/dist/lib/staleness/writers/kinds.d.ts +6 -0
  396. package/dist/lib/staleness/writers/skills.js +19 -0
  397. package/dist/lib/staleness/writers/subagents.d.ts +2 -3
  398. package/dist/lib/staleness/writers/subagents.js +0 -4
  399. package/dist/lib/staleness/writers/types.d.ts +32 -0
  400. package/dist/lib/startup/command-registry.d.ts +6 -12
  401. package/dist/lib/startup/command-registry.js +14 -31
  402. package/dist/lib/startup/dev-build.d.ts +10 -0
  403. package/dist/lib/startup/dev-build.js +13 -1
  404. package/dist/lib/startup/spellcheck.d.ts +18 -0
  405. package/dist/lib/startup/spellcheck.js +45 -0
  406. package/dist/lib/state.d.ts +36 -9
  407. package/dist/lib/state.js +96 -46
  408. package/dist/lib/subagents-registry.d.ts +0 -7
  409. package/dist/lib/subagents-registry.js +38 -65
  410. package/dist/lib/subagents.d.ts +0 -33
  411. package/dist/lib/subagents.js +0 -75
  412. package/dist/lib/sync-umbrella.js +1 -1
  413. package/dist/lib/teams/agents.d.ts +41 -1
  414. package/dist/lib/teams/agents.js +187 -9
  415. package/dist/lib/teams/api.d.ts +11 -1
  416. package/dist/lib/teams/api.js +11 -2
  417. package/dist/lib/teams/delivery.d.ts +41 -0
  418. package/dist/lib/teams/delivery.js +60 -0
  419. package/dist/lib/teams/registry.d.ts +14 -0
  420. package/dist/lib/teams/registry.js +51 -1
  421. package/dist/lib/teams/remoteWorktree.d.ts +19 -0
  422. package/dist/lib/teams/remoteWorktree.js +29 -0
  423. package/dist/lib/teams/scheduler.d.ts +1 -1
  424. package/dist/lib/teams/scheduler.js +1 -1
  425. package/dist/lib/teams/worktree.d.ts +24 -0
  426. package/dist/lib/teams/worktree.js +42 -0
  427. package/dist/lib/terminal/backends/vscodium-agent.d.ts +10 -1
  428. package/dist/lib/terminal/backends/vscodium-agent.js +16 -5
  429. package/dist/lib/terminal/engine.d.ts +4 -0
  430. package/dist/lib/terminal/engine.js +8 -2
  431. package/dist/lib/terminal/types.d.ts +21 -2
  432. package/dist/lib/testdata/refresh-coordinator-worker.d.ts +1 -0
  433. package/dist/lib/testdata/refresh-coordinator-worker.js +23 -0
  434. package/dist/lib/tickets/list.d.ts +53 -0
  435. package/dist/lib/tickets/list.js +153 -0
  436. package/dist/lib/tmux/orphan-reap.d.ts +320 -0
  437. package/dist/lib/tmux/orphan-reap.js +644 -0
  438. package/dist/lib/tmux/session.d.ts +68 -3
  439. package/dist/lib/tmux/session.js +95 -5
  440. package/dist/lib/triggers/handlers.d.ts +20 -0
  441. package/dist/lib/triggers/handlers.js +6 -1
  442. package/dist/lib/types.d.ts +46 -34
  443. package/dist/lib/usage-fleet.d.ts +32 -0
  444. package/dist/lib/usage-fleet.js +125 -0
  445. package/dist/lib/usage-refresh.d.ts +3 -3
  446. package/dist/lib/usage-refresh.js +19 -16
  447. package/dist/lib/usage.d.ts +51 -47
  448. package/dist/lib/usage.js +145 -153
  449. package/dist/lib/version-duplicates.js +2 -2
  450. package/dist/lib/versions.d.ts +56 -13
  451. package/dist/lib/versions.js +195 -45
  452. package/dist/lib/watchdog/log.d.ts +1 -1
  453. package/dist/lib/watchdog/log.js +3 -3
  454. package/dist/lib/watchdog/rotate.d.ts +6 -6
  455. package/dist/lib/watchdog/rotate.js +6 -6
  456. package/dist/lib/workflows.d.ts +1 -1
  457. package/dist/lib/yaml-io.d.ts +47 -0
  458. package/dist/lib/yaml-io.js +55 -0
  459. package/package.json +2 -1
  460. package/scripts/install-helper.js +2 -2
  461. package/scripts/postinstall.js +23 -20
  462. package/dist/commands/defaults.d.ts +0 -7
  463. package/dist/commands/defaults.js +0 -107
  464. package/dist/commands/export.d.ts +0 -11
  465. package/dist/commands/export.js +0 -215
  466. package/dist/commands/helper.d.ts +0 -12
  467. package/dist/commands/helper.js +0 -87
  468. package/dist/commands/hosts.d.ts +0 -11
  469. package/dist/commands/hosts.js +0 -330
  470. package/dist/commands/lock.d.ts +0 -12
  471. package/dist/commands/lock.js +0 -70
  472. package/dist/commands/pull.d.ts +0 -17
  473. package/dist/commands/pull.js +0 -39
  474. package/dist/commands/push.d.ts +0 -14
  475. package/dist/commands/push.js +0 -30
  476. package/dist/commands/wallet.d.ts +0 -20
  477. package/dist/commands/wallet.js +0 -216
  478. package/dist/commands/worktree.d.ts +0 -19
  479. package/dist/commands/worktree.js +0 -272
  480. package/dist/lib/account-labels.d.ts +0 -24
  481. package/dist/lib/account-labels.js +0 -72
  482. package/dist/lib/auto-dispatch-linear.d.ts +0 -18
  483. package/dist/lib/auto-dispatch-linear.js +0 -107
  484. package/dist/lib/auto-dispatch-provider.d.ts +0 -10
  485. package/dist/lib/auto-dispatch-provider.js +0 -30
  486. package/dist/lib/auto-dispatch.d.ts +0 -93
  487. package/dist/lib/auto-dispatch.js +0 -128
  488. package/dist/lib/export.d.ts +0 -72
  489. package/dist/lib/export.js +0 -269
  490. package/dist/lib/lock.d.ts +0 -93
  491. package/dist/lib/lock.js +0 -207
  492. package/dist/lib/wallet/index.d.ts +0 -78
  493. package/dist/lib/wallet/index.js +0 -253
@@ -13,6 +13,21 @@ export const SESSION_AGENTS = ['claude', 'codex', 'gemini', 'antigravity', 'open
13
13
  * of {@link SESSION_AGENTS}). The single predicate every session-index writer
14
14
  * gates on, so "is this a trackable agent?" is decided in exactly one place.
15
15
  */
16
+ /**
17
+ * The tmux session name the CLI mints for an agent run: `ag-<agent>-<shortid>`.
18
+ *
19
+ * ONE definition. This shape was independently re-written as a regex literal in
20
+ * five places (focus's selector test, sessions-resume's direct-selector test,
21
+ * active's name parsers, actor-sidecar, and the deeplink parser); they drifted —
22
+ * focus accepted a 6+ hex suffix while the others required exactly 8 — which is
23
+ * how the same alias could be an "identity" to one code path and a keyword query
24
+ * to the next.
25
+ */
26
+ export const AG_TMUX_NAME_RE = /^ag-([a-z][a-z0-9-]*?)-([0-9a-f]{8})$/i;
27
+ /** Whether `name` is an `ag-<agent>-<shortid>` tmux session name. */
28
+ export function isAgentTmuxAlias(name) {
29
+ return AG_TMUX_NAME_RE.test(name);
30
+ }
16
31
  export function isSessionTrackedAgent(agent) {
17
32
  return SESSION_AGENTS.includes(agent);
18
33
  }
@@ -41,7 +41,7 @@ export interface ViewingIn {
41
41
  * BOTH "no client attached" and "could not locate the pane", so the pane's resolved
42
42
  * `tmuxTarget` is what separates them: without it there is no evidence of absence,
43
43
  * only absence of evidence. Claiming `'detached'` there would invent an orphaned
44
- * session — and a consumer acts on that claim (Factory's picker pre-ticks every
44
+ * session — and a consumer acts on that claim (the ext's picker pre-ticks every
45
45
  * detached row for rescue), so the wrong answer resumes a session nobody asked for.
46
46
  *
47
47
  * Shared by the `--active` row renderer and the `--json` serializer so a machine
@@ -40,7 +40,7 @@ const execFileAsync = promisify(execFile);
40
40
  * BOTH "no client attached" and "could not locate the pane", so the pane's resolved
41
41
  * `tmuxTarget` is what separates them: without it there is no evidence of absence,
42
42
  * only absence of evidence. Claiming `'detached'` there would invent an orphaned
43
- * session — and a consumer acts on that claim (Factory's picker pre-ticks every
43
+ * session — and a consumer acts on that claim (the ext's picker pre-ticks every
44
44
  * detached row for rescue), so the wrong answer resumes a session nobody asked for.
45
45
  *
46
46
  * Shared by the `--active` row renderer and the `--json` serializer so a machine
@@ -0,0 +1,101 @@
1
+ import type { ActiveSession } from './active.js';
2
+ import { readActiveSessionsCache } from './session-cache.js';
3
+ export declare const SESSION_WATCH_VERSION: 1;
4
+ export declare const SESSION_WATCH_HEARTBEAT_MS = 15000;
5
+ export type SessionWatchScopeStatus = 'available' | 'unavailable';
6
+ export type SessionWatchEnvelope = {
7
+ version: 1;
8
+ type: 'reset';
9
+ streamId: string;
10
+ sequence: number;
11
+ capturedAt: number;
12
+ scope: string;
13
+ rows: SessionWatchRow[];
14
+ } | {
15
+ version: 1;
16
+ type: 'upsert';
17
+ streamId: string;
18
+ sequence: number;
19
+ capturedAt: number;
20
+ scope: string;
21
+ rowKey: string;
22
+ row: SessionWatchRow;
23
+ } | {
24
+ version: 1;
25
+ type: 'remove';
26
+ streamId: string;
27
+ sequence: number;
28
+ capturedAt: number;
29
+ scope: string;
30
+ rowKey: string;
31
+ } | {
32
+ version: 1;
33
+ type: 'scope';
34
+ streamId: string;
35
+ sequence: number;
36
+ capturedAt: number;
37
+ scope: string;
38
+ status: SessionWatchScopeStatus;
39
+ reason?: string;
40
+ } | {
41
+ version: 1;
42
+ type: 'heartbeat';
43
+ streamId: string;
44
+ sequence: number;
45
+ scope: string;
46
+ capturedAt: number;
47
+ };
48
+ export interface SessionWatchRow extends Omit<ActiveSession, 'viewingIn'> {
49
+ rowKey: string;
50
+ sourceDevice: string;
51
+ resumable: boolean;
52
+ unwatched: boolean;
53
+ viewingIn: string | null;
54
+ recovery: {
55
+ command: 'agents';
56
+ args: string[];
57
+ cwd?: string;
58
+ } | null;
59
+ }
60
+ /** Stable, opaque identity for one row within one device scope. */
61
+ export declare function sessionWatchRowKey(scope: string, row: ActiveSession): string;
62
+ export declare function toSessionWatchRow(scope: string, row: ActiveSession): SessionWatchRow;
63
+ /** Stream-local sequencer and convergent row diff. */
64
+ export declare class SessionWatchState {
65
+ readonly streamId: string;
66
+ private sequence;
67
+ private readonly rows;
68
+ constructor(streamId?: string);
69
+ private base;
70
+ reset(scope: string, sourceRows: ActiveSession[]): SessionWatchEnvelope;
71
+ update(scope: string, sourceRows: ActiveSession[]): SessionWatchEnvelope[];
72
+ patch(scope: string, upserts: ActiveSession[], removes: string[]): SessionWatchEnvelope[];
73
+ scope(scope: string, status: SessionWatchScopeStatus, reason?: string): SessionWatchEnvelope;
74
+ heartbeat(scope: string, capturedAt?: number): SessionWatchEnvelope;
75
+ }
76
+ export interface WatchLocalOptions {
77
+ scope: string;
78
+ signal: AbortSignal;
79
+ emit: (event: SessionWatchEnvelope) => void;
80
+ refreshMs?: number;
81
+ heartbeatMs?: number;
82
+ readCache?: typeof readActiveSessionsCache;
83
+ journalPath?: string;
84
+ journalPollMs?: number;
85
+ }
86
+ /**
87
+ * Keep one local subscription alive. Startup reads one canonical reset snapshot;
88
+ * steady state tails the canonical writer journal and never invokes a gather.
89
+ */
90
+ export declare function watchLocalSessions(options: WatchLocalOptions): Promise<void>;
91
+ export interface WatchFleetOptions {
92
+ signal: AbortSignal;
93
+ emit: (event: SessionWatchEnvelope) => void;
94
+ reconnectMs?: number;
95
+ }
96
+ /**
97
+ * Subscribe to every dialable compute device with one persistent SSH process.
98
+ * A peer disconnect emits `scope: unavailable` but no removes; reconnecting
99
+ * peer resets only its own scope. There is no recurring fleet list command.
100
+ */
101
+ export declare function watchFleetSessions(options: WatchFleetOptions): Promise<void>;
@@ -0,0 +1,242 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { spawn } from 'node:child_process';
3
+ import { createInterface } from 'node:readline';
4
+ import * as fs from 'node:fs';
5
+ import * as path from 'node:path';
6
+ import { loadDevices, isControlDevice, isDialableDevice } from '../devices/registry.js';
7
+ import { deviceIdentityArgs, sshTargetFor } from '../devices/connect.js';
8
+ import { machineId, normalizeHost } from '../machine-id.js';
9
+ import { SSH_OPTS, controlOpts, shellQuote } from '../ssh-exec.js';
10
+ import { buildWindowsAgentsCommand, remoteShellFor } from '../hosts/remote-cmd.js';
11
+ import { activeSessionsJournalPath, activeSessionJournalIdentity, readActiveSessionsCache, } from './session-cache.js';
12
+ export const SESSION_WATCH_VERSION = 1;
13
+ export const SESSION_WATCH_HEARTBEAT_MS = 15_000;
14
+ /** Stable, opaque identity for one row within one device scope. */
15
+ export function sessionWatchRowKey(scope, row) {
16
+ const identity = activeSessionJournalIdentity(row);
17
+ return createHash('sha256').update(`${scope}\0${identity}`).digest('base64url').slice(0, 22);
18
+ }
19
+ export function toSessionWatchRow(scope, row) {
20
+ const rowKey = sessionWatchRowKey(scope, row);
21
+ const resumable = Boolean(row.sessionId);
22
+ const viewingIn = row.viewingIn
23
+ ? [row.viewingIn.app, row.viewingIn.tab ? `tab ${row.viewingIn.tab}` : undefined].filter(Boolean).join(' ')
24
+ : null;
25
+ return {
26
+ ...row,
27
+ rowKey,
28
+ sourceDevice: scope,
29
+ resumable,
30
+ unwatched: !viewingIn,
31
+ viewingIn,
32
+ recovery: resumable
33
+ ? { command: 'agents', args: ['sessions', 'resume', row.sessionId, '--device', scope], ...(row.cwd ? { cwd: row.cwd } : {}) }
34
+ : null,
35
+ };
36
+ }
37
+ function sameRow(a, b) {
38
+ return JSON.stringify(a) === JSON.stringify(b);
39
+ }
40
+ /** Stream-local sequencer and convergent row diff. */
41
+ export class SessionWatchState {
42
+ streamId;
43
+ sequence = 0;
44
+ rows = new Map();
45
+ constructor(streamId = randomUUID()) { this.streamId = streamId; }
46
+ base(type) {
47
+ return { version: SESSION_WATCH_VERSION, type, streamId: this.streamId, sequence: ++this.sequence, capturedAt: Date.now() };
48
+ }
49
+ reset(scope, sourceRows) {
50
+ const rows = sourceRows.map((row) => toSessionWatchRow(scope, row));
51
+ this.rows.set(scope, new Map(rows.map((row) => [row.rowKey, row])));
52
+ return { ...this.base('reset'), scope, rows };
53
+ }
54
+ update(scope, sourceRows) {
55
+ const previous = this.rows.get(scope) ?? new Map();
56
+ const nextRows = sourceRows.map((row) => toSessionWatchRow(scope, row));
57
+ const next = new Map(nextRows.map((row) => [row.rowKey, row]));
58
+ const events = [];
59
+ for (const [rowKey, row] of next) {
60
+ if (!previous.has(rowKey) || !sameRow(previous.get(rowKey), row)) {
61
+ events.push({ ...this.base('upsert'), scope, rowKey, row });
62
+ }
63
+ }
64
+ for (const rowKey of previous.keys()) {
65
+ if (!next.has(rowKey))
66
+ events.push({ ...this.base('remove'), scope, rowKey });
67
+ }
68
+ this.rows.set(scope, next);
69
+ return events;
70
+ }
71
+ patch(scope, upserts, removes) {
72
+ const current = new Map(this.rows.get(scope) ?? []);
73
+ const events = [];
74
+ for (const source of upserts) {
75
+ const row = toSessionWatchRow(scope, source);
76
+ if (!current.has(row.rowKey) || !sameRow(current.get(row.rowKey), row)) {
77
+ current.set(row.rowKey, row);
78
+ events.push({ ...this.base('upsert'), scope, rowKey: row.rowKey, row });
79
+ }
80
+ }
81
+ for (const identity of removes) {
82
+ const rowKey = createHash('sha256').update(`${scope}\0${identity}`).digest('base64url').slice(0, 22);
83
+ if (current.delete(rowKey))
84
+ events.push({ ...this.base('remove'), scope, rowKey });
85
+ }
86
+ this.rows.set(scope, current);
87
+ return events;
88
+ }
89
+ scope(scope, status, reason) {
90
+ // Deliberately retain rows while unavailable. A reconnecting scope replaces
91
+ // them with a reset; transient fleet loss must not look like session death.
92
+ return { ...this.base('scope'), scope, status, ...(reason ? { reason } : {}) };
93
+ }
94
+ heartbeat(scope, capturedAt = Date.now()) {
95
+ return { ...this.base('heartbeat'), scope, capturedAt };
96
+ }
97
+ }
98
+ /**
99
+ * Keep one local subscription alive. Startup reads one canonical reset snapshot;
100
+ * steady state tails the canonical writer journal and never invokes a gather.
101
+ */
102
+ export async function watchLocalSessions(options) {
103
+ const state = new SessionWatchState();
104
+ const readCache = options.readCache ?? readActiveSessionsCache;
105
+ const journal = options.journalPath ?? activeSessionsJournalPath();
106
+ const heartbeatMs = options.heartbeatMs ?? SESSION_WATCH_HEARTBEAT_MS;
107
+ let offset = 0;
108
+ try {
109
+ offset = fs.statSync(journal).size;
110
+ }
111
+ catch { /* first publication */ }
112
+ const initial = readCache('local');
113
+ options.emit(state.reset(options.scope, initial?.sessions ?? []));
114
+ options.emit(state.scope(options.scope, initial ? 'available' : 'unavailable', initial ? undefined : 'awaiting publisher'));
115
+ if (options.signal.aborted)
116
+ return;
117
+ await new Promise((resolve) => {
118
+ let partial = '';
119
+ let reading = false;
120
+ const readAppended = () => {
121
+ if (reading || options.signal.aborted)
122
+ return;
123
+ reading = true;
124
+ try {
125
+ const size = fs.statSync(journal).size;
126
+ if (size < offset) {
127
+ offset = 0;
128
+ partial = '';
129
+ }
130
+ if (size === offset)
131
+ return;
132
+ const fd = fs.openSync(journal, 'r');
133
+ try {
134
+ const buffer = Buffer.alloc(size - offset);
135
+ fs.readSync(fd, buffer, 0, buffer.length, offset);
136
+ offset = size;
137
+ const lines = (partial + buffer.toString('utf8')).split('\n');
138
+ partial = lines.pop() ?? '';
139
+ for (const line of lines) {
140
+ if (!line)
141
+ continue;
142
+ try {
143
+ const record = JSON.parse(line);
144
+ if (record.version !== 1 || record.scope !== 'local' || !Array.isArray(record.upserts) || !Array.isArray(record.removes))
145
+ continue;
146
+ for (const event of state.patch(options.scope, record.upserts, record.removes))
147
+ options.emit(event);
148
+ options.emit(state.scope(options.scope, 'available'));
149
+ }
150
+ catch { /* partial/corrupt journal lines are not state */ }
151
+ }
152
+ }
153
+ finally {
154
+ fs.closeSync(fd);
155
+ }
156
+ }
157
+ catch { /* journal may not exist until the first publisher write */ }
158
+ finally {
159
+ reading = false;
160
+ }
161
+ };
162
+ fs.mkdirSync(path.dirname(journal), { recursive: true });
163
+ const journalListener = () => readAppended();
164
+ fs.watchFile(journal, { interval: options.journalPollMs ?? 250 }, journalListener);
165
+ const heartbeatTimer = setInterval(() => options.emit(state.heartbeat(options.scope)), heartbeatMs);
166
+ const stop = () => { fs.unwatchFile(journal, journalListener); clearInterval(heartbeatTimer); resolve(); };
167
+ options.signal.addEventListener('abort', stop, { once: true });
168
+ // Close the offset/read/watch handoff: a publisher may append after the
169
+ // startup offset is captured but before watchFile is registered.
170
+ readAppended();
171
+ });
172
+ }
173
+ function remoteWatchCommand(os) {
174
+ const args = ['sessions', 'watch', '--json', '--local'];
175
+ return remoteShellFor(os) === 'powershell'
176
+ ? buildWindowsAgentsCommand({ args })
177
+ : `bash -lc ${shellQuote(`agents ${args.map(shellQuote).join(' ')}`)}`;
178
+ }
179
+ /**
180
+ * Subscribe to every dialable compute device with one persistent SSH process.
181
+ * A peer disconnect emits `scope: unavailable` but no removes; reconnecting
182
+ * peer resets only its own scope. There is no recurring fleet list command.
183
+ */
184
+ export async function watchFleetSessions(options) {
185
+ const local = watchLocalSessions({ scope: machineId(), signal: options.signal, emit: options.emit });
186
+ let devices;
187
+ try {
188
+ devices = await loadDevices();
189
+ }
190
+ catch {
191
+ await local;
192
+ return;
193
+ }
194
+ const self = machineId();
195
+ const peers = Object.values(devices).filter((device) => isDialableDevice(device)
196
+ && !isControlDevice(device)
197
+ && normalizeHost(device.name) !== self
198
+ && ['windows', 'linux', 'macos'].includes(device.platform));
199
+ const reconnectMs = options.reconnectMs ?? 2_000;
200
+ const peerTasks = peers.map(async (device) => {
201
+ const scope = normalizeHost(device.name);
202
+ const state = new SessionWatchState();
203
+ while (!options.signal.aborted) {
204
+ let target;
205
+ try {
206
+ target = sshTargetFor(device);
207
+ }
208
+ catch (error) {
209
+ options.emit(state.scope(scope, 'unavailable', error instanceof Error ? error.message : String(error)));
210
+ break;
211
+ }
212
+ const child = spawn('ssh', [
213
+ ...SSH_OPTS, ...controlOpts(), ...deviceIdentityArgs(device), target, remoteWatchCommand(device.platform),
214
+ ], { stdio: ['ignore', 'pipe', 'ignore'] });
215
+ const stop = () => child.kill('SIGTERM');
216
+ options.signal.addEventListener('abort', stop, { once: true });
217
+ const reader = createInterface({ input: child.stdout });
218
+ reader.on('line', (line) => {
219
+ try {
220
+ const event = JSON.parse(line);
221
+ if (event.version === SESSION_WATCH_VERSION && typeof event.sequence === 'number')
222
+ options.emit(event);
223
+ }
224
+ catch { /* incomplete/non-protocol peer output is not state */ }
225
+ });
226
+ const code = await new Promise((resolve) => {
227
+ child.once('error', () => resolve(null));
228
+ child.once('close', resolve);
229
+ });
230
+ reader.close();
231
+ options.signal.removeEventListener('abort', stop);
232
+ if (options.signal.aborted)
233
+ break;
234
+ options.emit(state.scope(scope, 'unavailable', code === null ? 'ssh failed' : `ssh exited ${code}`));
235
+ await new Promise((resolve) => {
236
+ const timer = setTimeout(resolve, reconnectMs);
237
+ options.signal.addEventListener('abort', () => { clearTimeout(timer); resolve(); }, { once: true });
238
+ });
239
+ }
240
+ });
241
+ await Promise.all([local, ...peerTasks]);
242
+ }
@@ -8,6 +8,10 @@ export interface ShareConfig {
8
8
  domain?: string;
9
9
  /** Cloudflare Web Analytics token injected into published HTML pages. */
10
10
  analyticsToken?: string;
11
+ /** sha256 of the Worker script deployed at the last provision/update. Absent
12
+ * on any config written before this field existed — treat that as "unknown",
13
+ * never as "outdated" (there is nothing to compare against). */
14
+ templateHash?: string;
11
15
  }
12
16
  export declare const SHARE_BUNDLE = "share";
13
17
  export declare const SHARE_TOKEN_KEY = "WRITE_TOKEN";
@@ -35,6 +35,7 @@ export function readShareConfig() {
35
35
  bucketName: s.bucketName,
36
36
  domain: s.domain,
37
37
  analyticsToken: s.analyticsToken,
38
+ templateHash: s.templateHash,
38
39
  };
39
40
  }
40
41
  /** Persist the endpoint config to `agents.yaml` (syncs across the fleet). */
@@ -0,0 +1,93 @@
1
+ import { type ShareConfig } from './config.js';
2
+ /** DI seam for tests — override the real HTTP DELETE. */
3
+ export type DeleteFn = (url: string, headers: Record<string, string>) => Promise<{
4
+ ok: boolean;
5
+ status: number;
6
+ }>;
7
+ /** DI seam for tests — override the real HTTP existence check (HEAD). */
8
+ export type CheckFn = (url: string) => Promise<{
9
+ status: number;
10
+ }>;
11
+ export interface DeleteEndpoint {
12
+ baseUrl: string;
13
+ token: string;
14
+ }
15
+ export interface ResolvedShareTarget {
16
+ /** R2 object key for the page, `<user>/<slug>`. */
17
+ key: string;
18
+ /** R2 object key for the sibling OG cover, `<user>/<slug>.png`. */
19
+ coverKey: string;
20
+ }
21
+ /**
22
+ * Normalize any of the three accepted target forms to the R2 key that publish
23
+ * would have written:
24
+ * - a full share URL: `https://share.agents-cli.sh/<user>/<slug>`
25
+ * - `<user>/<slug>`
26
+ * - a bare `<slug>` — resolved against the caller's own namespace exactly as
27
+ * `publishToEndpoint` resolves it at publish time (resolveShareUsername +
28
+ * buildShareKey), so a bare slug always targets *your* published page.
29
+ *
30
+ * The URL and `<user>/<slug>` forms are taken as already the exact key a prior
31
+ * publish produced (no re-sanitizing) — only the bare-slug form runs through
32
+ * `buildShareKey`, because that's the one case where the slug hasn't already
33
+ * been normalized by a publish.
34
+ */
35
+ export declare function resolveDeleteTarget(target: string, opts?: {
36
+ githubUser?: string;
37
+ }): Promise<ResolvedShareTarget>;
38
+ export interface DeleteObjectResult {
39
+ key: string;
40
+ url: string;
41
+ /** Whether the object resolved (non-404) before the DELETE was issued. */
42
+ existedBefore: boolean;
43
+ /** Whether the Worker's DELETE call itself reported ok. */
44
+ deleted: boolean;
45
+ /** The postcondition: a follow-up check resolved 404 after the DELETE. */
46
+ verified404: boolean;
47
+ }
48
+ /**
49
+ * Delete one R2 object behind the share Worker and assert the postcondition.
50
+ * `{"ok":true}` from the Worker is not evidence a page came down — R2 delete is
51
+ * idempotent, so a DELETE on a key that was never there also returns `ok:true`.
52
+ * This checks existence before (so callers can tell "deleted" from "was never
53
+ * there") and re-checks after (so callers can tell "deleted" from "still public").
54
+ */
55
+ export declare function deleteObject(endpoint: DeleteEndpoint, key: string, opts?: {
56
+ deleter?: DeleteFn;
57
+ checker?: CheckFn;
58
+ }): Promise<DeleteObjectResult>;
59
+ export interface DeleteShareOptions {
60
+ /** Skip deleting the sibling `<slug>.png` OG cover (default: delete it too). */
61
+ keepCover?: boolean;
62
+ /** Treat an already-missing target as a no-op success instead of an error
63
+ * (mirrors SQL's `DROP ... IF EXISTS`). Default: missing target is an error. */
64
+ ifExists?: boolean;
65
+ /** Override the GitHub username used to resolve a bare-slug target. */
66
+ githubUser?: string;
67
+ /** DI seam for tests — override the persisted share endpoint config. */
68
+ config?: ShareConfig;
69
+ /** DI seam for tests — override the keychain-backed write token. */
70
+ writeToken?: string;
71
+ /** DI seam for tests — override the real HTTP DELETE. */
72
+ deleter?: DeleteFn;
73
+ /** DI seam for tests — override the real HTTP existence check. */
74
+ checker?: CheckFn;
75
+ }
76
+ export interface DeleteShareResult {
77
+ key: string;
78
+ url: string;
79
+ existedBefore: boolean;
80
+ verified404: boolean;
81
+ /** True when `ifExists` was set and the target was already gone — nothing ran. */
82
+ skipped?: boolean;
83
+ cover?: {
84
+ key: string;
85
+ url: string;
86
+ existedBefore: boolean;
87
+ verified404: boolean;
88
+ };
89
+ }
90
+ /** Delete one share target (page + by default its OG cover) and verify both
91
+ * are gone. Throws on an unverified takedown — never reports success for an
92
+ * object that still resolves. */
93
+ export declare function deleteShare(target: string, opts?: DeleteShareOptions): Promise<DeleteShareResult>;
@@ -0,0 +1,127 @@
1
+ // The delete path for `agents share delete` / `agents unshare` — an authed DELETE
2
+ // to the Worker, which already implements it (worker-template.ts). Mirrors
3
+ // publish.ts: pure target-resolution logic is exported for tests, the network
4
+ // calls (a status check + a delete) sit behind an injectable DI seam.
5
+ //
6
+ // The Worker's R2 delete is idempotent — DELETE on a key that never existed still
7
+ // returns `{"ok":true}` — so `{"ok":true}` alone is never proof of a takedown.
8
+ // Every delete here is followed by a status check that must observe 404 before
9
+ // the operation is reported as successful.
10
+ import { readShareConfig, readWriteToken } from './config.js';
11
+ import { buildShareKey, resolveShareUsername } from './publish.js';
12
+ /**
13
+ * Normalize any of the three accepted target forms to the R2 key that publish
14
+ * would have written:
15
+ * - a full share URL: `https://share.agents-cli.sh/<user>/<slug>`
16
+ * - `<user>/<slug>`
17
+ * - a bare `<slug>` — resolved against the caller's own namespace exactly as
18
+ * `publishToEndpoint` resolves it at publish time (resolveShareUsername +
19
+ * buildShareKey), so a bare slug always targets *your* published page.
20
+ *
21
+ * The URL and `<user>/<slug>` forms are taken as already the exact key a prior
22
+ * publish produced (no re-sanitizing) — only the bare-slug form runs through
23
+ * `buildShareKey`, because that's the one case where the slug hasn't already
24
+ * been normalized by a publish.
25
+ */
26
+ export async function resolveDeleteTarget(target, opts = {}) {
27
+ const trimmed = target.trim();
28
+ if (!trimmed)
29
+ throw new Error('Share target is empty.');
30
+ let key;
31
+ if (/^https?:\/\//i.test(trimmed)) {
32
+ const url = new URL(trimmed);
33
+ const segments = url.pathname
34
+ .replace(/^\/+|\/+$/g, '')
35
+ .split('/')
36
+ .filter(Boolean)
37
+ .map(decodeURIComponent);
38
+ if (segments.length < 2) {
39
+ throw new Error(`Not a share page URL (expected .../<user>/<slug>): ${trimmed}`);
40
+ }
41
+ key = segments.slice(0, 2).join('/');
42
+ }
43
+ else if (trimmed.includes('/')) {
44
+ const segments = trimmed.replace(/^\/+|\/+$/g, '').split('/').filter(Boolean);
45
+ if (segments.length !== 2) {
46
+ throw new Error(`Expected <user>/<slug>, got: ${trimmed}`);
47
+ }
48
+ key = segments.join('/');
49
+ }
50
+ else {
51
+ const username = await resolveShareUsername(opts);
52
+ key = buildShareKey(username, trimmed);
53
+ }
54
+ return { key, coverKey: `${key}.png` };
55
+ }
56
+ async function defaultCheck(url) {
57
+ const res = await fetch(url, { method: 'HEAD' });
58
+ return { status: res.status };
59
+ }
60
+ async function defaultDelete(url, headers) {
61
+ const res = await fetch(url, { method: 'DELETE', headers });
62
+ return { ok: res.ok, status: res.status };
63
+ }
64
+ /**
65
+ * Delete one R2 object behind the share Worker and assert the postcondition.
66
+ * `{"ok":true}` from the Worker is not evidence a page came down — R2 delete is
67
+ * idempotent, so a DELETE on a key that was never there also returns `ok:true`.
68
+ * This checks existence before (so callers can tell "deleted" from "was never
69
+ * there") and re-checks after (so callers can tell "deleted" from "still public").
70
+ */
71
+ export async function deleteObject(endpoint, key, opts = {}) {
72
+ const url = `${endpoint.baseUrl.replace(/\/+$/, '')}/${key}`;
73
+ const check = opts.checker ?? defaultCheck;
74
+ const del = opts.deleter ?? defaultDelete;
75
+ const before = await check(url);
76
+ const existedBefore = before.status !== 404;
77
+ const r = await del(url, { authorization: `Bearer ${endpoint.token}` });
78
+ if (!r.ok) {
79
+ throw new Error(`Delete failed (${r.status}) for ${url}. Check the write token, or that 'agents share setup' completed.`);
80
+ }
81
+ const after = await check(url);
82
+ const verified404 = after.status === 404;
83
+ return { key, url, existedBefore, deleted: r.ok, verified404 };
84
+ }
85
+ /** Delete one share target (page + by default its OG cover) and verify both
86
+ * are gone. Throws on an unverified takedown — never reports success for an
87
+ * object that still resolves. */
88
+ export async function deleteShare(target, opts = {}) {
89
+ const cfg = opts.config ?? readShareConfig();
90
+ if (!cfg) {
91
+ throw new Error("Not set up yet. Run 'agents share setup' (provision your own endpoint) or 'agents share join' (use an existing one).");
92
+ }
93
+ const token = opts.writeToken ?? readWriteToken();
94
+ const resolved = await resolveDeleteTarget(target, { githubUser: opts.githubUser });
95
+ const endpoint = { baseUrl: cfg.baseUrl, token };
96
+ const page = await deleteObject(endpoint, resolved.key, { deleter: opts.deleter, checker: opts.checker });
97
+ if (!page.existedBefore) {
98
+ if (opts.ifExists) {
99
+ return { key: page.key, url: page.url, existedBefore: false, verified404: page.verified404, skipped: true };
100
+ }
101
+ throw new Error(`Nothing to delete — ${page.url} was already not found. Pass --if-exists to treat this as a no-op instead of an error.`);
102
+ }
103
+ if (!page.verified404) {
104
+ throw new Error(`Delete reported success but ${page.url} still resolves — takedown NOT verified. Retry, or investigate the Worker/R2 directly.`);
105
+ }
106
+ const result = {
107
+ key: page.key,
108
+ url: page.url,
109
+ existedBefore: page.existedBefore,
110
+ verified404: page.verified404,
111
+ };
112
+ if (!opts.keepCover) {
113
+ const cover = await deleteObject(endpoint, resolved.coverKey, { deleter: opts.deleter, checker: opts.checker });
114
+ result.cover = {
115
+ key: cover.key,
116
+ url: cover.url,
117
+ existedBefore: cover.existedBefore,
118
+ verified404: cover.verified404,
119
+ };
120
+ // A missing cover is normal (non-HTML publishes, or --no-cover at publish
121
+ // time never made one) — only a cover that existed and is still up is a bug.
122
+ if (cover.existedBefore && !cover.verified404) {
123
+ throw new Error(`Cover delete reported success but ${cover.url} still resolves — takedown NOT verified. Retry, or pass --keep-cover and delete it manually.`);
124
+ }
125
+ }
126
+ return result;
127
+ }
@@ -51,6 +51,44 @@ export declare function mergeShareLifecycleRule(existing?: R2LifecycleRule[], ru
51
51
  export declare function configureBucketLifecycle(apiToken: string, accountId: string, bucketName: string, opts?: ProvisionOptions): Promise<void>;
52
52
  /** Upload the module Worker with an R2 binding (`BUCKET`). Secrets are set via the Workers Secrets API. */
53
53
  export declare function deployWorker(apiToken: string, accountId: string, workerName: string, script: string, bucketName: string, opts?: ProvisionOptions): Promise<void>;
54
+ /** sha256 of the rendered Worker script, so a deployed endpoint can be compared
55
+ * against the current `worker-template.ts` without redeploying to find out. */
56
+ export declare function hashWorkerScript(script: string): string;
57
+ export interface UpdateWorkerResult {
58
+ /** sha256 of `script` (the hash the endpoint now matches, whether or not this
59
+ * call actually redeployed). */
60
+ templateHash: string;
61
+ /** True when the upload was skipped because `previousHash` already matched. */
62
+ skipped: boolean;
63
+ }
64
+ /**
65
+ * Re-deploy the Worker script against an ALREADY-provisioned endpoint (same
66
+ * account/worker/bucket `deployWorker` was first called with) and idempotently
67
+ * no-op when the template hasn't changed.
68
+ *
69
+ * Cloudflare's script-upload endpoint (`deployWorker`, above) replaces the
70
+ * Worker's bindings/secrets wholesale on every call — there is no documented,
71
+ * reliable way to tell it "keep the existing secret" (the `keep_bindings`
72
+ * metadata field some third-party guides mention is absent from Cloudflare's
73
+ * current Multipart upload metadata reference, and the community has reported
74
+ * it not preventing binding loss even when present:
75
+ * https://developers.cloudflare.com/workers/configuration/multipart-upload-metadata/,
76
+ * https://community.cloudflare.com/t/upload-worker-module-endpoint-removes-existing-bindings-despite-keep-bindings/766447).
77
+ * So instead this immediately re-applies WRITE_TOKEN via the documented Secrets
78
+ * API right after the upload — the same two-call sequence first-time
79
+ * provisioning already uses (`deployWorker` then `setWorkerSecret`), except
80
+ * `writeToken` here is the caller's EXISTING token, never a freshly generated
81
+ * one. Cloudflare's own docs describe exactly this "secrets survive a
82
+ * subsequent write" contract for the sibling `wrangler versions upload
83
+ * --secrets-file` flow ("Secrets not included in the file are preserved from
84
+ * the previous version" —
85
+ * https://developers.cloudflare.com/workers/configuration/secrets/#upload-secrets-alongside-code);
86
+ * re-setting the secret to its own value after every deploy gets the same
87
+ * outcome without depending on an unverified upload-time flag.
88
+ */
89
+ export declare function updateWorker(apiToken: string, accountId: string, workerName: string, bucketName: string, script: string, writeToken: string, previousHash: string | undefined, opts?: ProvisionOptions & {
90
+ force?: boolean;
91
+ }): Promise<UpdateWorkerResult>;
54
92
  /** Add/update the WRITE_TOKEN binding using Cloudflare's Workers Secrets API. */
55
93
  export declare function setWorkerSecret(apiToken: string, accountId: string, workerName: string, writeToken: string, opts?: ProvisionOptions): Promise<void>;
56
94
  /** Enable the free `*.workers.dev` route for the script, and return the account subdomain. */