harnery 0.40.0 → 0.42.0

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 (595) hide show
  1. package/README.md +1 -1
  2. package/dist/commander.js +4 -0
  3. package/dist/commands/agents.d.ts.map +1 -1
  4. package/dist/commands/agents.js +34 -20
  5. package/dist/commands/artifacts.d.ts.map +1 -1
  6. package/dist/commands/artifacts.js +113 -12
  7. package/dist/commands/browse-ai.d.ts.map +1 -1
  8. package/dist/commands/browse-ai.js +4 -0
  9. package/dist/commands/browse.d.ts +164 -0
  10. package/dist/commands/browse.d.ts.map +1 -1
  11. package/dist/commands/browse.js +388 -45
  12. package/dist/commands/checkpoint.js +8 -12
  13. package/dist/commands/claude-desktop.d.ts.map +1 -1
  14. package/dist/commands/claude-desktop.js +52 -0
  15. package/dist/commands/deinit.d.ts +1 -1
  16. package/dist/commands/deinit.d.ts.map +1 -1
  17. package/dist/commands/deinit.js +8 -4
  18. package/dist/commands/disk.d.ts +6 -0
  19. package/dist/commands/disk.d.ts.map +1 -0
  20. package/dist/commands/disk.js +80 -0
  21. package/dist/commands/docs.d.ts.map +1 -1
  22. package/dist/commands/docs.js +2 -0
  23. package/dist/commands/doctor.d.ts +8 -0
  24. package/dist/commands/doctor.d.ts.map +1 -1
  25. package/dist/commands/doctor.js +58 -3
  26. package/dist/commands/fetch.d.ts.map +1 -1
  27. package/dist/commands/fetch.js +10 -0
  28. package/dist/commands/files.d.ts +16 -0
  29. package/dist/commands/files.d.ts.map +1 -1
  30. package/dist/commands/files.js +54 -2
  31. package/dist/commands/init.d.ts +21 -5
  32. package/dist/commands/init.d.ts.map +1 -1
  33. package/dist/commands/init.js +271 -109
  34. package/dist/commands/instructions.js +5 -4
  35. package/dist/commands/ledger-v3.d.ts.map +1 -1
  36. package/dist/commands/ledger-v3.js +2 -1
  37. package/dist/commands/rm.d.ts +4 -0
  38. package/dist/commands/rm.d.ts.map +1 -0
  39. package/dist/commands/rm.js +87 -0
  40. package/dist/commands/servers.d.ts +7 -0
  41. package/dist/commands/servers.d.ts.map +1 -0
  42. package/dist/commands/servers.js +394 -0
  43. package/dist/commands/tunnel.d.ts.map +1 -1
  44. package/dist/commands/tunnel.js +82 -2
  45. package/dist/commands/webview.d.ts +4 -0
  46. package/dist/commands/webview.d.ts.map +1 -0
  47. package/dist/commands/webview.js +94 -0
  48. package/dist/core/adapter.d.ts +50 -1
  49. package/dist/core/adapter.d.ts.map +1 -1
  50. package/dist/core/adapter.js +59 -1
  51. package/dist/core/adapters/attestation.d.ts.map +1 -1
  52. package/dist/core/adapters/attestation.js +3 -2
  53. package/dist/core/adapters/profiles.d.ts +29 -3
  54. package/dist/core/adapters/profiles.d.ts.map +1 -1
  55. package/dist/core/adapters/profiles.js +53 -1
  56. package/dist/core/adapters/registry.d.ts.map +1 -1
  57. package/dist/core/adapters/registry.js +33 -0
  58. package/dist/core/adapters/types.d.ts +9 -0
  59. package/dist/core/adapters/types.d.ts.map +1 -1
  60. package/dist/core/agents/canonical-emit.d.ts +1 -1
  61. package/dist/core/agents/canonical-emit.d.ts.map +1 -1
  62. package/dist/core/agents/canonical-emit.js +2 -7
  63. package/dist/core/agents/cli.js +25 -16
  64. package/dist/core/agents/coord-client.d.ts.map +1 -1
  65. package/dist/core/agents/coord-client.js +7 -2
  66. package/dist/core/agents/render/session-context.d.ts.map +1 -1
  67. package/dist/core/agents/render/session-context.js +2 -0
  68. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
  69. package/dist/core/agents/rules/claim-conflict.js +3 -9
  70. package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
  71. package/dist/core/agents/rules/commit-conflict.js +2 -8
  72. package/dist/core/agents/rules/stop-hook.d.ts +2 -0
  73. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  74. package/dist/core/agents/rules/stop-hook.js +1 -1
  75. package/dist/core/agents/session-events.d.ts +6 -2
  76. package/dist/core/agents/session-events.d.ts.map +1 -1
  77. package/dist/core/agents/session-events.js +105 -26
  78. package/dist/core/agents/session-finalization-state-v3.d.ts.map +1 -1
  79. package/dist/core/agents/session-finalization-state-v3.js +2 -1
  80. package/dist/core/agents/session-finalizer-v3.d.ts.map +1 -1
  81. package/dist/core/agents/session-finalizer-v3.js +5 -4
  82. package/dist/core/agents/session-name-display.d.ts +28 -0
  83. package/dist/core/agents/session-name-display.d.ts.map +1 -1
  84. package/dist/core/agents/session-name-display.js +43 -0
  85. package/dist/core/agents/state/heartbeat-reader.d.ts +7 -0
  86. package/dist/core/agents/state/heartbeat-reader.d.ts.map +1 -1
  87. package/dist/core/agents/state/heartbeat-writer.d.ts +10 -0
  88. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  89. package/dist/core/agents/state/heartbeat-writer.js +21 -2
  90. package/dist/core/agents/state/live-coordination-view.d.ts +20 -0
  91. package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
  92. package/dist/core/agents/state/live-coordination-view.js +61 -0
  93. package/dist/core/agents/state/live-coordination-writer.js +3 -0
  94. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
  95. package/dist/core/agents/state/stale-sweep.js +2 -10
  96. package/dist/core/agents/turn-changes.d.ts +17 -0
  97. package/dist/core/agents/turn-changes.d.ts.map +1 -0
  98. package/dist/core/agents/turn-changes.js +76 -0
  99. package/dist/core/artifacts/delivery-card.d.ts.map +1 -1
  100. package/dist/core/artifacts/delivery-card.js +11 -3
  101. package/dist/core/artifacts/index.d.ts +140 -4
  102. package/dist/core/artifacts/index.d.ts.map +1 -1
  103. package/dist/core/artifacts/index.js +526 -52
  104. package/dist/core/config.d.ts +73 -11
  105. package/dist/core/config.d.ts.map +1 -1
  106. package/dist/core/config.js +142 -3
  107. package/dist/core/context/index.d.ts.map +1 -1
  108. package/dist/core/context/index.js +2 -1
  109. package/dist/core/conversations/archive.d.ts.map +1 -1
  110. package/dist/core/conversations/archive.js +4 -3
  111. package/dist/core/diagnostics/bundle.d.ts.map +1 -1
  112. package/dist/core/diagnostics/bundle.js +5 -4
  113. package/dist/core/events/legacy-storage/canary.d.ts.map +1 -1
  114. package/dist/core/events/legacy-storage/canary.js +5 -4
  115. package/dist/core/events/legacy-storage/compression.d.ts.map +1 -1
  116. package/dist/core/events/legacy-storage/compression.js +2 -1
  117. package/dist/core/events/v3/adapter-id.d.ts +10 -2
  118. package/dist/core/events/v3/adapter-id.d.ts.map +1 -1
  119. package/dist/core/events/v3/adapter-id.js +16 -0
  120. package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
  121. package/dist/core/events/v3/authority-outbox.js +2 -1
  122. package/dist/core/events/v3/bootstrap.d.ts.map +1 -1
  123. package/dist/core/events/v3/bootstrap.js +5 -4
  124. package/dist/core/events/v3/capabilities.d.ts.map +1 -1
  125. package/dist/core/events/v3/capabilities.js +17 -0
  126. package/dist/core/events/v3/contract.d.ts +5 -0
  127. package/dist/core/events/v3/contract.d.ts.map +1 -1
  128. package/dist/core/events/v3/contract.js +7 -1
  129. package/dist/core/events/v3/control-witness.d.ts.map +1 -1
  130. package/dist/core/events/v3/control-witness.js +3 -2
  131. package/dist/core/events/v3/coordination-transaction-recovery.d.ts.map +1 -1
  132. package/dist/core/events/v3/coordination-transaction-recovery.js +4 -3
  133. package/dist/core/events/v3/fingerprint-keys.d.ts.map +1 -1
  134. package/dist/core/events/v3/fingerprint-keys.js +7 -6
  135. package/dist/core/events/v3/generated.d.ts +1 -1
  136. package/dist/core/events/v3/generated.js +1 -1
  137. package/dist/core/events/v3/live-feed.d.ts.map +1 -1
  138. package/dist/core/events/v3/live-feed.js +5 -4
  139. package/dist/core/events/v3/producers/command-recorder.d.ts.map +1 -1
  140. package/dist/core/events/v3/producers/command-recorder.js +8 -7
  141. package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
  142. package/dist/core/events/v3/producers/coordination-recorder.js +8 -7
  143. package/dist/core/events/v3/producers/diagnostic-summaries.d.ts.map +1 -1
  144. package/dist/core/events/v3/producers/diagnostic-summaries.js +5 -4
  145. package/dist/core/events/v3/producers/hook-base.d.ts +1 -0
  146. package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
  147. package/dist/core/events/v3/producers/hook-base.js +10 -4
  148. package/dist/core/events/v3/producers/hook.d.ts +2 -0
  149. package/dist/core/events/v3/producers/hook.d.ts.map +1 -1
  150. package/dist/core/events/v3/producers/hook.js +16 -1
  151. package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
  152. package/dist/core/events/v3/producers/intake.js +16 -6
  153. package/dist/core/events/v3/producers/recorder.d.ts +2 -4
  154. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  155. package/dist/core/events/v3/producers/recorder.js +125 -101
  156. package/dist/core/events/v3/projection.js +9 -1
  157. package/dist/core/events/v3/recovery.d.ts.map +1 -1
  158. package/dist/core/events/v3/recovery.js +4 -3
  159. package/dist/core/events/v3/support-storage/inventory.d.ts.map +1 -1
  160. package/dist/core/events/v3/support-storage/inventory.js +2 -1
  161. package/dist/core/events/v3/support-storage/maintenance.d.ts.map +1 -1
  162. package/dist/core/events/v3/support-storage/maintenance.js +4 -3
  163. package/dist/core/events/v3/support-storage/pack-reader.d.ts.map +1 -1
  164. package/dist/core/events/v3/support-storage/pack-reader.js +4 -3
  165. package/dist/core/events/v3/support-storage/pack-writer.d.ts.map +1 -1
  166. package/dist/core/events/v3/support-storage/pack-writer.js +6 -5
  167. package/dist/core/events/v3/tool-line-changes.d.ts +8 -0
  168. package/dist/core/events/v3/tool-line-changes.d.ts.map +1 -0
  169. package/dist/core/events/v3/tool-line-changes.js +135 -0
  170. package/dist/core/events/v3/writer.d.ts.map +1 -1
  171. package/dist/core/events/v3/writer.js +8 -7
  172. package/dist/core/governor/planning.d.ts.map +1 -1
  173. package/dist/core/governor/planning.js +6 -5
  174. package/dist/core/governor/service.d.ts.map +1 -1
  175. package/dist/core/governor/service.js +12 -11
  176. package/dist/core/governor/state.d.ts.map +1 -1
  177. package/dist/core/governor/state.js +7 -6
  178. package/dist/core/guard/evaluator.js +1 -0
  179. package/dist/core/guard/storage.d.ts.map +1 -1
  180. package/dist/core/guard/storage.js +3 -2
  181. package/dist/core/hooks/adapter/behaviors/claude-code.d.ts +4 -0
  182. package/dist/core/hooks/adapter/behaviors/claude-code.d.ts.map +1 -0
  183. package/dist/core/hooks/adapter/behaviors/claude-code.js +31 -0
  184. package/dist/core/hooks/adapter/behaviors/codex.d.ts +15 -0
  185. package/dist/core/hooks/adapter/behaviors/codex.d.ts.map +1 -0
  186. package/dist/core/hooks/adapter/behaviors/codex.js +82 -0
  187. package/dist/core/hooks/adapter/behaviors/cursor.d.ts +7 -0
  188. package/dist/core/hooks/adapter/behaviors/cursor.d.ts.map +1 -0
  189. package/dist/core/hooks/adapter/behaviors/cursor.js +35 -0
  190. package/dist/core/hooks/adapter/behaviors/index.d.ts +11 -0
  191. package/dist/core/hooks/adapter/behaviors/index.d.ts.map +1 -0
  192. package/dist/core/hooks/adapter/behaviors/index.js +20 -0
  193. package/dist/core/hooks/adapter/behaviors/openclaw.d.ts +7 -0
  194. package/dist/core/hooks/adapter/behaviors/openclaw.d.ts.map +1 -0
  195. package/dist/core/hooks/adapter/behaviors/openclaw.js +34 -0
  196. package/dist/core/hooks/adapter/behaviors/opencode.d.ts +7 -0
  197. package/dist/core/hooks/adapter/behaviors/opencode.d.ts.map +1 -0
  198. package/dist/core/hooks/adapter/behaviors/opencode.js +34 -0
  199. package/dist/core/hooks/adapter/behaviors/types.d.ts +89 -0
  200. package/dist/core/hooks/adapter/behaviors/types.d.ts.map +1 -0
  201. package/dist/core/hooks/adapter/behaviors/types.js +1 -0
  202. package/dist/core/hooks/adapter/detect.d.ts +1 -1
  203. package/dist/core/hooks/adapter/detect.d.ts.map +1 -1
  204. package/dist/core/hooks/adapter/detect.js +2 -3
  205. package/dist/core/hooks/adapter/events.d.ts +46 -1
  206. package/dist/core/hooks/adapter/events.d.ts.map +1 -1
  207. package/dist/core/hooks/adapter/events.js +36 -0
  208. package/dist/core/hooks/adapter/opencode-plugin.d.ts +60 -0
  209. package/dist/core/hooks/adapter/opencode-plugin.d.ts.map +1 -0
  210. package/dist/core/hooks/adapter/opencode-plugin.js +129 -0
  211. package/dist/core/hooks/adapter/output.d.ts +7 -0
  212. package/dist/core/hooks/adapter/output.d.ts.map +1 -1
  213. package/dist/core/hooks/adapter/output.js +8 -1
  214. package/dist/core/hooks/adapter/runtime-telemetry.js +3 -1
  215. package/dist/core/hooks/adapter/wiring.d.ts +52 -8
  216. package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
  217. package/dist/core/hooks/adapter/wiring.js +133 -12
  218. package/dist/core/hooks/cli.js +676 -531
  219. package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
  220. package/dist/core/hooks/effects/image-capture.js +2 -1
  221. package/dist/core/hooks/prompt-context/contract.d.ts.map +1 -1
  222. package/dist/core/hooks/prompt-context/contract.js +1 -0
  223. package/dist/core/hooks/resolve/transcript.d.ts +1 -1
  224. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
  225. package/dist/core/hooks/resolve/transcript.js +35 -4
  226. package/dist/core/hooks/run-markers.d.ts +37 -0
  227. package/dist/core/hooks/run-markers.d.ts.map +1 -0
  228. package/dist/core/hooks/run-markers.js +109 -0
  229. package/dist/core/hooks/session-name-presence.d.ts +3 -1
  230. package/dist/core/hooks/session-name-presence.d.ts.map +1 -1
  231. package/dist/core/hooks/session-name-presence.js +7 -2
  232. package/dist/core/hooks/shell-waiter.d.ts +2 -0
  233. package/dist/core/hooks/shell-waiter.d.ts.map +1 -0
  234. package/dist/core/hooks/shell-waiter.js +173 -0
  235. package/dist/core/resources/service.d.ts.map +1 -1
  236. package/dist/core/resources/service.js +5 -4
  237. package/dist/core/semantic/review.d.ts.map +1 -1
  238. package/dist/core/semantic/review.js +5 -4
  239. package/dist/core/semantic/service.d.ts.map +1 -1
  240. package/dist/core/semantic/service.js +15 -14
  241. package/dist/core/semantic/storage.d.ts.map +1 -1
  242. package/dist/core/semantic/storage.js +5 -4
  243. package/dist/core/servers/index.d.ts +211 -0
  244. package/dist/core/servers/index.d.ts.map +1 -0
  245. package/dist/core/servers/index.js +560 -0
  246. package/dist/core/servers/net.d.ts +58 -0
  247. package/dist/core/servers/net.d.ts.map +1 -0
  248. package/dist/core/servers/net.js +270 -0
  249. package/dist/core/servers/tunnels.d.ts +14 -0
  250. package/dist/core/servers/tunnels.d.ts.map +1 -0
  251. package/dist/core/servers/tunnels.js +33 -0
  252. package/dist/core/storage/atomic-json.d.ts.map +1 -1
  253. package/dist/core/storage/atomic-json.js +7 -3
  254. package/dist/core/storage/builtins.js +1 -0
  255. package/dist/core/storage/durable-history.d.ts.map +1 -1
  256. package/dist/core/storage/durable-history.js +6 -5
  257. package/dist/core/storage/maintenance-providers.d.ts.map +1 -1
  258. package/dist/core/storage/maintenance-providers.js +2 -1
  259. package/dist/core/storage/maintenance.d.ts.map +1 -1
  260. package/dist/core/storage/maintenance.js +7 -6
  261. package/dist/core/storage/metrics.d.ts.map +1 -1
  262. package/dist/core/storage/metrics.js +5 -1
  263. package/dist/core/storage/modes.d.ts +17 -0
  264. package/dist/core/storage/modes.d.ts.map +1 -0
  265. package/dist/core/storage/modes.js +47 -0
  266. package/dist/core/storage/process-log.d.ts.map +1 -1
  267. package/dist/core/storage/process-log.js +3 -2
  268. package/dist/core/storage/receipts.d.ts.map +1 -1
  269. package/dist/core/storage/receipts.js +4 -3
  270. package/dist/core/storage/segments.d.ts.map +1 -1
  271. package/dist/core/storage/segments.js +7 -6
  272. package/dist/core/supervisor/hook-health.d.ts +2 -1
  273. package/dist/core/supervisor/hook-health.d.ts.map +1 -1
  274. package/dist/core/supervisor/hook-health.js +4 -3
  275. package/dist/core/supervisor/service.d.ts.map +1 -1
  276. package/dist/core/supervisor/service.js +7 -6
  277. package/dist/core/work/state.d.ts.map +1 -1
  278. package/dist/core/work/state.js +8 -7
  279. package/dist/core/workflow/adapters.d.ts +7 -0
  280. package/dist/core/workflow/adapters.d.ts.map +1 -1
  281. package/dist/core/workflow/adapters.js +9 -0
  282. package/dist/core/workflow/approvals.d.ts.map +1 -1
  283. package/dist/core/workflow/approvals.js +6 -5
  284. package/dist/core/workflow/billing.d.ts.map +1 -1
  285. package/dist/core/workflow/billing.js +33 -4
  286. package/dist/core/workflow/durable-record.d.ts.map +1 -1
  287. package/dist/core/workflow/durable-record.js +4 -3
  288. package/dist/core/workflow/engine.d.ts.map +1 -1
  289. package/dist/core/workflow/engine.js +30 -1
  290. package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
  291. package/dist/core/workflow/live-session-v3.js +2 -3
  292. package/dist/core/workflow/run-state.d.ts.map +1 -1
  293. package/dist/core/workflow/run-state.js +3 -2
  294. package/dist/core/workflow/sandbox-projection.d.ts +13 -2
  295. package/dist/core/workflow/sandbox-projection.d.ts.map +1 -1
  296. package/dist/core/workflow/sandbox-projection.js +23 -0
  297. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  298. package/dist/core/workflow/spawn-claude.js +6 -1
  299. package/dist/core/workflow/spawn-codex.d.ts +2 -0
  300. package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
  301. package/dist/core/workflow/spawn-codex.js +20 -1
  302. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
  303. package/dist/core/workflow/spawn-cursor.js +6 -1
  304. package/dist/core/workflow/spawn-opencode.d.ts +36 -0
  305. package/dist/core/workflow/spawn-opencode.d.ts.map +1 -0
  306. package/dist/core/workflow/spawn-opencode.js +135 -0
  307. package/dist/core/workflow/transcript.d.ts.map +1 -1
  308. package/dist/core/workflow/transcript.js +3 -2
  309. package/dist/core/workflow/types.d.ts +47 -2
  310. package/dist/core/workflow/types.d.ts.map +1 -1
  311. package/dist/core/workflow/worker-access.d.ts +92 -0
  312. package/dist/core/workflow/worker-access.d.ts.map +1 -0
  313. package/dist/core/workflow/worker-access.js +450 -0
  314. package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
  315. package/dist/core/workflow/workspaces/leases.js +5 -4
  316. package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
  317. package/dist/core/workflow/workspaces/local-git.js +3 -2
  318. package/dist/core/workflow/workspaces/paths.d.ts.map +1 -1
  319. package/dist/core/workflow/workspaces/paths.js +2 -1
  320. package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
  321. package/dist/core/workflow/workspaces/state.js +10 -9
  322. package/dist/lazy-commands.js +8 -1
  323. package/dist/lib/agent-browser/client.d.ts +9 -0
  324. package/dist/lib/agent-browser/client.d.ts.map +1 -1
  325. package/dist/lib/agent-browser/client.js +13 -0
  326. package/dist/lib/browser/batch-steps.d.ts +18 -0
  327. package/dist/lib/browser/batch-steps.d.ts.map +1 -0
  328. package/dist/lib/browser/batch-steps.js +82 -0
  329. package/dist/lib/browser/client.d.ts +35 -1
  330. package/dist/lib/browser/client.d.ts.map +1 -1
  331. package/dist/lib/browser/client.js +117 -8
  332. package/dist/lib/browser/collect/collector.d.ts +99 -0
  333. package/dist/lib/browser/collect/collector.d.ts.map +1 -0
  334. package/dist/lib/browser/collect/collector.js +202 -0
  335. package/dist/lib/browser/collect/fields.d.ts +31 -0
  336. package/dist/lib/browser/collect/fields.d.ts.map +1 -0
  337. package/dist/lib/browser/collect/fields.js +75 -0
  338. package/dist/lib/browser/collect/index.d.ts +6 -0
  339. package/dist/lib/browser/collect/index.d.ts.map +1 -0
  340. package/dist/lib/browser/collect/index.js +5 -0
  341. package/dist/lib/browser/collect/page-driver.d.ts +14 -0
  342. package/dist/lib/browser/collect/page-driver.d.ts.map +1 -0
  343. package/dist/lib/browser/collect/page-driver.js +306 -0
  344. package/dist/lib/browser/collect/presets.d.ts +52 -0
  345. package/dist/lib/browser/collect/presets.d.ts.map +1 -0
  346. package/dist/lib/browser/collect/presets.js +176 -0
  347. package/dist/lib/browser/collect/scroll-plan.d.ts +85 -0
  348. package/dist/lib/browser/collect/scroll-plan.d.ts.map +1 -0
  349. package/dist/lib/browser/collect/scroll-plan.js +118 -0
  350. package/dist/lib/browser/identity.d.ts +25 -0
  351. package/dist/lib/browser/identity.d.ts.map +1 -0
  352. package/dist/lib/browser/identity.js +66 -0
  353. package/dist/lib/browser/index.d.ts +3 -2
  354. package/dist/lib/browser/index.d.ts.map +1 -1
  355. package/dist/lib/browser/index.js +2 -1
  356. package/dist/lib/browser/launch-args.d.ts +26 -0
  357. package/dist/lib/browser/launch-args.d.ts.map +1 -1
  358. package/dist/lib/browser/launch-args.js +41 -1
  359. package/dist/lib/browser/page-review-judge.d.ts +2 -0
  360. package/dist/lib/browser/page-review-judge.d.ts.map +1 -1
  361. package/dist/lib/browser/page-review-judge.js +13 -0
  362. package/dist/lib/browser/qa-run-contracts.d.ts +10 -0
  363. package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -1
  364. package/dist/lib/browser/qa-run.d.ts.map +1 -1
  365. package/dist/lib/browser/qa-run.js +1 -0
  366. package/dist/lib/browser/user-agent.d.ts +53 -0
  367. package/dist/lib/browser/user-agent.d.ts.map +1 -0
  368. package/dist/lib/browser/user-agent.js +79 -0
  369. package/dist/lib/browser/windows-chrome.d.ts +59 -0
  370. package/dist/lib/browser/windows-chrome.d.ts.map +1 -0
  371. package/dist/lib/browser/windows-chrome.js +179 -0
  372. package/dist/lib/claude-desktop-share.d.ts +136 -0
  373. package/dist/lib/claude-desktop-share.d.ts.map +1 -0
  374. package/dist/lib/claude-desktop-share.js +394 -0
  375. package/dist/lib/disk-usage.d.ts +50 -0
  376. package/dist/lib/disk-usage.d.ts.map +1 -0
  377. package/dist/lib/disk-usage.js +303 -0
  378. package/dist/lib/docs-lint.d.ts +6 -0
  379. package/dist/lib/docs-lint.d.ts.map +1 -1
  380. package/dist/lib/docs-lint.js +8 -3
  381. package/dist/lib/docs-metadata-managed.d.ts +1 -0
  382. package/dist/lib/docs-metadata-managed.d.ts.map +1 -1
  383. package/dist/lib/docs-metadata-managed.js +17 -1
  384. package/dist/lib/guarded-remove.d.ts +34 -0
  385. package/dist/lib/guarded-remove.d.ts.map +1 -0
  386. package/dist/lib/guarded-remove.js +310 -0
  387. package/dist/lib/identities/assume.d.ts.map +1 -1
  388. package/dist/lib/identities/assume.js +4 -9
  389. package/dist/lib/instructions/apply.d.ts.map +1 -1
  390. package/dist/lib/instructions/apply.js +2 -0
  391. package/dist/lib/instructions/templates.js +8 -8
  392. package/dist/lib/pace/index.d.ts +2 -0
  393. package/dist/lib/pace/index.d.ts.map +1 -0
  394. package/dist/lib/pace/index.js +1 -0
  395. package/dist/lib/pace/pace.d.ts +95 -0
  396. package/dist/lib/pace/pace.d.ts.map +1 -0
  397. package/dist/lib/pace/pace.js +313 -0
  398. package/dist/lib/tunnel/error-page.d.ts +1 -1
  399. package/dist/lib/tunnel/error-page.d.ts.map +1 -1
  400. package/dist/lib/tunnel/error-page.js +19 -8
  401. package/dist/lib/tunnel/forward-headers.d.ts +11 -0
  402. package/dist/lib/tunnel/forward-headers.d.ts.map +1 -0
  403. package/dist/lib/tunnel/forward-headers.js +17 -0
  404. package/dist/lib/tunnel/gate.js +34 -10
  405. package/dist/lib/tunnel/path-scope.d.ts +22 -0
  406. package/dist/lib/tunnel/path-scope.d.ts.map +1 -0
  407. package/dist/lib/tunnel/path-scope.js +62 -0
  408. package/dist/lib/tunnel/state.d.ts +5 -0
  409. package/dist/lib/tunnel/state.d.ts.map +1 -1
  410. package/dist/lib/tunnel/state.js +17 -3
  411. package/dist/lib/webview/index.d.ts +2 -0
  412. package/dist/lib/webview/index.d.ts.map +1 -0
  413. package/dist/lib/webview/index.js +1 -0
  414. package/dist/lib/webview/webview.d.ts +116 -0
  415. package/dist/lib/webview/webview.d.ts.map +1 -0
  416. package/dist/lib/webview/webview.js +289 -0
  417. package/opencode-plugin/package.json +19 -0
  418. package/opencode-plugin/src/index.ts +791 -0
  419. package/package.json +16 -4
  420. package/schemas/config.schema.json +104 -0
  421. package/schemas/event-v3.schema.json +164 -0
  422. package/src/commander.ts +26 -0
  423. package/src/commands/agents.ts +47 -18
  424. package/src/commands/artifacts.ts +171 -19
  425. package/src/commands/browse-ai.ts +8 -0
  426. package/src/commands/browse.ts +570 -48
  427. package/src/commands/checkpoint.ts +7 -9
  428. package/src/commands/claude-desktop.ts +61 -0
  429. package/src/commands/deinit.ts +9 -4
  430. package/src/commands/disk.ts +114 -0
  431. package/src/commands/docs.ts +2 -0
  432. package/src/commands/doctor.ts +63 -3
  433. package/src/commands/fetch.ts +14 -0
  434. package/src/commands/files.ts +93 -2
  435. package/src/commands/init.ts +334 -122
  436. package/src/commands/instructions.ts +8 -4
  437. package/src/commands/ledger-v3.ts +2 -1
  438. package/src/commands/rm.ts +115 -0
  439. package/src/commands/servers.ts +506 -0
  440. package/src/commands/tunnel.ts +102 -2
  441. package/src/commands/webview.ts +159 -0
  442. package/src/core/adapter.ts +102 -1
  443. package/src/core/adapters/attestation.ts +3 -2
  444. package/src/core/adapters/profiles.ts +63 -1
  445. package/src/core/adapters/registry.ts +37 -0
  446. package/src/core/adapters/types.ts +10 -0
  447. package/src/core/agents/canonical-emit.ts +2 -5
  448. package/src/core/agents/cli.ts +26 -17
  449. package/src/core/agents/coord-client.ts +9 -3
  450. package/src/core/agents/render/session-context.ts +1 -0
  451. package/src/core/agents/rules/claim-conflict.ts +3 -8
  452. package/src/core/agents/rules/commit-conflict.ts +2 -7
  453. package/src/core/agents/rules/stop-hook.ts +3 -1
  454. package/src/core/agents/session-events.ts +120 -26
  455. package/src/core/agents/session-finalization-state-v3.ts +2 -1
  456. package/src/core/agents/session-finalizer-v3.ts +5 -4
  457. package/src/core/agents/session-name-display.ts +48 -0
  458. package/src/core/agents/state/heartbeat-reader.ts +7 -0
  459. package/src/core/agents/state/heartbeat-writer.ts +24 -2
  460. package/src/core/agents/state/live-coordination-view.ts +74 -0
  461. package/src/core/agents/state/live-coordination-writer.ts +3 -0
  462. package/src/core/agents/state/stale-sweep.ts +2 -9
  463. package/src/core/agents/turn-changes.ts +97 -0
  464. package/src/core/artifacts/delivery-card.ts +12 -3
  465. package/src/core/artifacts/index.ts +701 -47
  466. package/src/core/config.ts +206 -4
  467. package/src/core/context/index.ts +2 -1
  468. package/src/core/conversations/archive.ts +4 -3
  469. package/src/core/diagnostics/bundle.ts +5 -3
  470. package/src/core/events/legacy-storage/canary.ts +5 -4
  471. package/src/core/events/legacy-storage/compression.ts +2 -1
  472. package/src/core/events/v3/adapter-id.ts +29 -1
  473. package/src/core/events/v3/authority-outbox.ts +2 -1
  474. package/src/core/events/v3/bootstrap.ts +5 -4
  475. package/src/core/events/v3/capabilities.ts +17 -0
  476. package/src/core/events/v3/contract.ts +9 -1
  477. package/src/core/events/v3/control-witness.ts +3 -2
  478. package/src/core/events/v3/coordination-transaction-recovery.ts +4 -3
  479. package/src/core/events/v3/fingerprint-keys.ts +7 -6
  480. package/src/core/events/v3/generated.ts +1 -1
  481. package/src/core/events/v3/live-feed.ts +5 -4
  482. package/src/core/events/v3/producers/command-recorder.ts +8 -6
  483. package/src/core/events/v3/producers/coordination-recorder.ts +8 -7
  484. package/src/core/events/v3/producers/diagnostic-summaries.ts +5 -4
  485. package/src/core/events/v3/producers/hook-base.ts +11 -4
  486. package/src/core/events/v3/producers/hook.ts +19 -1
  487. package/src/core/events/v3/producers/intake.ts +18 -6
  488. package/src/core/events/v3/producers/recorder.ts +139 -114
  489. package/src/core/events/v3/projection.ts +11 -1
  490. package/src/core/events/v3/recovery.ts +4 -3
  491. package/src/core/events/v3/support-storage/inventory.ts +2 -1
  492. package/src/core/events/v3/support-storage/maintenance.ts +8 -3
  493. package/src/core/events/v3/support-storage/pack-reader.ts +4 -3
  494. package/src/core/events/v3/support-storage/pack-writer.ts +6 -5
  495. package/src/core/events/v3/tool-line-changes.ts +139 -0
  496. package/src/core/events/v3/writer.ts +8 -7
  497. package/src/core/governor/planning.ts +6 -5
  498. package/src/core/governor/service.ts +12 -11
  499. package/src/core/governor/state.ts +7 -6
  500. package/src/core/guard/evaluator.ts +1 -0
  501. package/src/core/guard/storage.ts +3 -2
  502. package/src/core/hooks/adapter/behaviors/claude-code.ts +33 -0
  503. package/src/core/hooks/adapter/behaviors/codex.ts +96 -0
  504. package/src/core/hooks/adapter/behaviors/cursor.ts +37 -0
  505. package/src/core/hooks/adapter/behaviors/index.ts +31 -0
  506. package/src/core/hooks/adapter/behaviors/openclaw.ts +36 -0
  507. package/src/core/hooks/adapter/behaviors/opencode.ts +36 -0
  508. package/src/core/hooks/adapter/behaviors/types.ts +110 -0
  509. package/src/core/hooks/adapter/detect.ts +2 -3
  510. package/src/core/hooks/adapter/events.ts +72 -1
  511. package/src/core/hooks/adapter/opencode-plugin.ts +177 -0
  512. package/src/core/hooks/adapter/output.ts +8 -1
  513. package/src/core/hooks/adapter/runtime-telemetry.ts +3 -1
  514. package/src/core/hooks/adapter/wiring.ts +198 -16
  515. package/src/core/hooks/cli.ts +776 -539
  516. package/src/core/hooks/effects/image-capture.ts +2 -1
  517. package/src/core/hooks/prompt-context/contract.ts +1 -0
  518. package/src/core/hooks/resolve/transcript.ts +42 -5
  519. package/src/core/hooks/run-markers.ts +132 -0
  520. package/src/core/hooks/session-name-presence.ts +10 -3
  521. package/src/core/hooks/shell-waiter.ts +166 -0
  522. package/src/core/resources/service.ts +5 -4
  523. package/src/core/semantic/review.ts +5 -4
  524. package/src/core/semantic/service.ts +15 -14
  525. package/src/core/semantic/storage.ts +5 -4
  526. package/src/core/servers/index.ts +712 -0
  527. package/src/core/servers/net.ts +283 -0
  528. package/src/core/servers/tunnels.ts +39 -0
  529. package/src/core/storage/atomic-json.ts +7 -3
  530. package/src/core/storage/builtins.ts +1 -0
  531. package/src/core/storage/durable-history.ts +6 -5
  532. package/src/core/storage/maintenance-providers.ts +2 -1
  533. package/src/core/storage/maintenance.ts +7 -6
  534. package/src/core/storage/metrics.ts +5 -1
  535. package/src/core/storage/modes.ts +53 -0
  536. package/src/core/storage/process-log.ts +3 -2
  537. package/src/core/storage/receipts.ts +4 -3
  538. package/src/core/storage/segments.ts +7 -6
  539. package/src/core/supervisor/hook-health.ts +4 -4
  540. package/src/core/supervisor/service.ts +7 -6
  541. package/src/core/work/state.ts +8 -7
  542. package/src/core/workflow/adapters.ts +15 -0
  543. package/src/core/workflow/approvals.ts +6 -5
  544. package/src/core/workflow/billing.ts +34 -4
  545. package/src/core/workflow/durable-record.ts +4 -3
  546. package/src/core/workflow/engine.ts +38 -1
  547. package/src/core/workflow/live-session-v3.ts +2 -3
  548. package/src/core/workflow/run-state.ts +3 -2
  549. package/src/core/workflow/sandbox-projection.ts +38 -2
  550. package/src/core/workflow/spawn-claude.ts +5 -1
  551. package/src/core/workflow/spawn-codex.ts +18 -1
  552. package/src/core/workflow/spawn-cursor.ts +5 -1
  553. package/src/core/workflow/spawn-opencode.ts +153 -0
  554. package/src/core/workflow/transcript.ts +3 -2
  555. package/src/core/workflow/types.ts +62 -2
  556. package/src/core/workflow/worker-access.ts +525 -0
  557. package/src/core/workflow/workspaces/leases.ts +5 -4
  558. package/src/core/workflow/workspaces/local-git.ts +3 -2
  559. package/src/core/workflow/workspaces/paths.ts +2 -1
  560. package/src/core/workflow/workspaces/state.ts +10 -9
  561. package/src/lazy-commands.ts +11 -1
  562. package/src/lib/agent-browser/client.ts +18 -0
  563. package/src/lib/browser/batch-steps.ts +79 -0
  564. package/src/lib/browser/client.ts +147 -9
  565. package/src/lib/browser/collect/collector.ts +306 -0
  566. package/src/lib/browser/collect/fields.ts +87 -0
  567. package/src/lib/browser/collect/index.ts +47 -0
  568. package/src/lib/browser/collect/page-driver.ts +327 -0
  569. package/src/lib/browser/collect/presets.ts +237 -0
  570. package/src/lib/browser/collect/scroll-plan.ts +179 -0
  571. package/src/lib/browser/identity.ts +64 -0
  572. package/src/lib/browser/index.ts +16 -1
  573. package/src/lib/browser/launch-args.ts +53 -1
  574. package/src/lib/browser/page-review-judge.ts +16 -0
  575. package/src/lib/browser/qa-run-contracts.ts +11 -0
  576. package/src/lib/browser/qa-run.ts +1 -0
  577. package/src/lib/browser/user-agent.ts +130 -0
  578. package/src/lib/browser/windows-chrome.ts +227 -0
  579. package/src/lib/claude-desktop-share.ts +511 -0
  580. package/src/lib/disk-usage.ts +347 -0
  581. package/src/lib/docs-lint.ts +8 -3
  582. package/src/lib/docs-metadata-managed.ts +21 -0
  583. package/src/lib/guarded-remove.ts +376 -0
  584. package/src/lib/identities/assume.ts +7 -7
  585. package/src/lib/instructions/apply.ts +2 -0
  586. package/src/lib/instructions/templates.ts +8 -8
  587. package/src/lib/pace/index.ts +14 -0
  588. package/src/lib/pace/pace.ts +370 -0
  589. package/src/lib/tunnel/error-page.ts +25 -9
  590. package/src/lib/tunnel/forward-headers.ts +16 -0
  591. package/src/lib/tunnel/gate.ts +39 -10
  592. package/src/lib/tunnel/path-scope.ts +73 -0
  593. package/src/lib/tunnel/state.ts +20 -3
  594. package/src/lib/webview/index.ts +13 -0
  595. package/src/lib/webview/webview.ts +457 -0
@@ -0,0 +1,115 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { isAbsolute, relative, resolve, sep } from "node:path";
4
+ import type { Command } from "commander";
5
+ import type { EmitContext, HarneryProgramContext } from "../commander.ts";
6
+ import { resolveCoordRoot } from "../core/agents/coord-client.ts";
7
+ import {
8
+ readCoordinationViewV3,
9
+ requireAuthoritySafeCoordinationViewV3,
10
+ } from "../core/events/v3/coordination-view.ts";
11
+ import { liveInstanceIdV3 } from "../core/events/v3/live-route-observer.ts";
12
+ import { resolveOwner } from "../core/hooks/resolve/owner.ts";
13
+ import { guardedRemove, type RemoveOptions } from "../lib/guarded-remove.ts";
14
+
15
+ export function registerRmCommand(
16
+ program: Command,
17
+ emit: EmitContext,
18
+ context?: HarneryProgramContext,
19
+ ): void {
20
+ program
21
+ .command("rm <paths...>")
22
+ .description("Preview guarded removal of untracked files; --yes applies permanent deletion")
23
+ .requiredOption(
24
+ "--root <directory>",
25
+ "Allowed directory; each target must be strictly inside it",
26
+ )
27
+ .option("-r, --recursive", "Permit directory removal after inspecting its complete contents")
28
+ .option(
29
+ "--allow-protected <path>",
30
+ "Remove this one protected path (Git metadata, credentials, Harnery state, keys) inside a target; repeat per path",
31
+ (value: string, previous: string[] = []) => [...previous, value],
32
+ )
33
+ .option("--yes", "Permanently remove validated targets")
34
+ .option("--dry-run", "Preview only, even with --yes")
35
+ .option("--json", "Emit the versioned removal report")
36
+ .action((paths: string[], options: RemoveOptions & { json?: boolean }) => {
37
+ try {
38
+ const coordRoot = context?.resolveCoordRoot?.() ?? context?.repoRoot ?? resolveCoordRoot();
39
+ const checkClaims = (targets: string[]): void => {
40
+ if (!coordRoot || !existsSync(resolve(coordRoot, ".harnery"))) return;
41
+ const view = requireAuthoritySafeCoordinationViewV3(readCoordinationViewV3(coordRoot));
42
+ const owner = resolveOwner({ payload: null, coordRoot });
43
+ const ownId = owner ? liveInstanceIdV3(owner.instance_id) : undefined;
44
+ const ownSession = ownId ? view.instances[ownId]?.session_id : undefined;
45
+ for (const peer of Object.values(view.instances)) {
46
+ if (
47
+ !peer.authority_eligible ||
48
+ peer.instance_id === ownId ||
49
+ (ownSession && peer.session_id === ownSession)
50
+ )
51
+ continue;
52
+ for (const claim of peer.files_touched) {
53
+ const claimed = resolve(coordRoot, claim);
54
+ for (const target of targets) {
55
+ if (overlaps(target, claimed))
56
+ throw new Error(
57
+ `Target overlaps another agent's claim: ${claim} (${peer.instance_id})`,
58
+ );
59
+ }
60
+ }
61
+ }
62
+ };
63
+ // The workspace this command coordinates, and the checkout it runs in:
64
+ // their own .git and .harnery can never be allowed.
65
+ const liveRoots = [coordRoot, gitTopLevel(process.cwd())].filter((dir): dir is string =>
66
+ Boolean(dir),
67
+ );
68
+ const report = guardedRemove(paths, { ...options, liveRoots }, checkClaims);
69
+ if (options.json) {
70
+ emit.config({ format: "json" });
71
+ emit.data(report);
72
+ } else {
73
+ const lines = [
74
+ `${report.applied ? "Removed" : "Would remove"} ${report.targets.length} target(s), ${report.entries} entries, ${report.bytes} bytes:`,
75
+ ...report.targets,
76
+ ];
77
+ if (report.protected.length)
78
+ lines.push(
79
+ "Protected paths named with --allow-protected:",
80
+ ...report.protected.map((path) => ` ${path}`),
81
+ );
82
+ if (report.links.length)
83
+ lines.push(
84
+ "Links removed as links (their targets are untouched):",
85
+ ...report.links.map((link) => ` ${link.path} -> ${link.target}`),
86
+ );
87
+ if (!report.applied)
88
+ lines.push("Preview only. Add --yes to permanently delete these targets.");
89
+ emit.text(lines.join("\n"));
90
+ }
91
+ } catch (error) {
92
+ emit.error({
93
+ code: "removal_refused",
94
+ message: error instanceof Error ? error.message : String(error),
95
+ });
96
+ emit.setExitCode(1);
97
+ }
98
+ });
99
+ }
100
+
101
+ function gitTopLevel(dir: string): string | undefined {
102
+ const result = spawnSync("git", ["-C", dir, "rev-parse", "--show-toplevel"], {
103
+ encoding: "utf8",
104
+ timeout: 10_000,
105
+ });
106
+ return result.status === 0 ? result.stdout.trim() || undefined : undefined;
107
+ }
108
+
109
+ function overlaps(a: string, b: string): boolean {
110
+ const inside = (parent: string, child: string) => {
111
+ const rel = relative(parent, child);
112
+ return rel === "" || (rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
113
+ };
114
+ return inside(a, b) || inside(b, a);
115
+ }
@@ -0,0 +1,506 @@
1
+ import { existsSync, readFileSync, statSync } from "node:fs";
2
+ import { hostname } from "node:os";
3
+ import type { Command } from "commander";
4
+ import type { EmitContext, HarneryProgramContext } from "../commander.ts";
5
+ import { resolveCoordRoot } from "../core/agents/coord-client.ts";
6
+ import { resolveBinName, serverGcConfig } from "../core/config.ts";
7
+ import {
8
+ adoptServer,
9
+ allocateServerPort,
10
+ isValidServerId,
11
+ listServers,
12
+ planServerGc,
13
+ readServer,
14
+ registerServer,
15
+ SERVER_DEFAULT_IDLE_HOURS,
16
+ SERVER_DEFAULT_OWNER_STALE_HOURS,
17
+ type ServerKind,
18
+ type ServersReport,
19
+ type ServerView,
20
+ type StopResult,
21
+ stopServer,
22
+ touchServer,
23
+ unregisterServer,
24
+ } from "../core/servers/index.ts";
25
+
26
+ export function registerServersCommand(
27
+ program: Command,
28
+ emit: EmitContext,
29
+ context?: HarneryProgramContext,
30
+ ): void {
31
+ const root = (): string => {
32
+ const resolved = context?.resolveCoordRoot?.() ?? context?.repoRoot ?? resolveCoordRoot();
33
+ if (!resolved) throw new Error("not inside a Harnery project");
34
+ return resolved;
35
+ };
36
+ const fail = (error: unknown, code = "servers_error"): void => {
37
+ emit.error({ code, message: error instanceof Error ? error.message : String(error) });
38
+ emit.setExitCode(1);
39
+ };
40
+
41
+ const cmd = program
42
+ .command("servers")
43
+ .description(
44
+ "Every local server started in this project: list, stop, clean up idle session servers, read logs",
45
+ );
46
+
47
+ cmd
48
+ .command("list", { isDefault: true })
49
+ .alias("status")
50
+ .description("List registered servers and any unregistered listener running inside the project")
51
+ .option("--no-scan", "Skip the scan for unregistered listeners")
52
+ .option("--json", "Emit the versioned report")
53
+ .action((options: { scan?: boolean; json?: boolean }) => {
54
+ try {
55
+ const report = listServers({ coordRoot: root(), scan: options.scan !== false });
56
+ if (options.json) {
57
+ emit.config({ format: "json" });
58
+ emit.data(report);
59
+ } else emit.text(renderReport(report, resolveBinName(report.coord_root)));
60
+ } catch (error) {
61
+ fail(error);
62
+ }
63
+ });
64
+
65
+ cmd
66
+ .command("stop [ids...]")
67
+ .description("Stop named servers now; --type selects several and previews unless --yes")
68
+ .option("--type <type>", "Select every running server of this type")
69
+ .option(
70
+ "--pid <pid>",
71
+ "Stop an unregistered listener found by the list scan (repeatable)",
72
+ (value: string, previous: string[] = []) => [...previous, value],
73
+ )
74
+ .option("--kind <kind>", "With --type, limit to session or service")
75
+ .option("--yes", "Apply a --type selection")
76
+ .option("--json", "Emit JSON")
77
+ .action(
78
+ async (
79
+ ids: string[],
80
+ options: { type?: string; kind?: string; pid?: string[]; yes?: boolean; json?: boolean },
81
+ ) => {
82
+ try {
83
+ const coordRoot = root();
84
+ const pids = (options.pid ?? []).map((value) => integer(value, "--pid"));
85
+ if (!ids.length && !options.type && !pids.length)
86
+ throw new Error("name server ids, or pass --type or --pid");
87
+ const report = listServers({ coordRoot, scan: pids.length > 0 });
88
+ const strays = pids.map((pid) => {
89
+ const found = report.unregistered.find((item) => item.pid === pid);
90
+ if (!found)
91
+ throw new Error(
92
+ `pid ${pid} is not an unregistered listener inside this project; only pids shown by "servers list" can be stopped this way`,
93
+ );
94
+ return found;
95
+ });
96
+ const byId = new Map(report.servers.map((view) => [view.record.id, view]));
97
+ const missing = ids.filter((id) => !byId.has(id));
98
+ if (missing.length) throw new Error(`no running server named: ${missing.join(", ")}`);
99
+ const selected = [
100
+ ...ids.map((id) => byId.get(id)!),
101
+ ...(options.type
102
+ ? report.servers.filter(
103
+ (view) =>
104
+ view.record.type === options.type &&
105
+ view.state === "running" &&
106
+ (!options.kind || view.record.kind === options.kind) &&
107
+ !ids.includes(view.record.id),
108
+ )
109
+ : []),
110
+ ];
111
+ const preview = Boolean(options.type) && !options.yes;
112
+ const results: Array<StopResult | { id: string; would_stop: true }> = [];
113
+ for (const stray of strays) {
114
+ results.push(
115
+ await stopServer(
116
+ {
117
+ schema_version: 1,
118
+ id: `pid-${stray.pid}`,
119
+ kind: "session",
120
+ type: "unregistered",
121
+ label: stray.command,
122
+ pid: stray.pid,
123
+ host: hostname(),
124
+ started_at: new Date().toISOString(),
125
+ registered_at: new Date().toISOString(),
126
+ },
127
+ { coordRoot },
128
+ ),
129
+ );
130
+ }
131
+ for (const view of selected) {
132
+ if (preview && !ids.includes(view.record.id))
133
+ results.push({ id: view.record.id, would_stop: true });
134
+ else results.push(await stopServer(view.record, { coordRoot }));
135
+ }
136
+ if (options.json) {
137
+ emit.config({ format: "json" });
138
+ emit.data({ applied: !preview, results });
139
+ } else {
140
+ emit.text(
141
+ results.length
142
+ ? results
143
+ .map((r) =>
144
+ "would_stop" in r
145
+ ? `would stop ${r.id}`
146
+ : `${r.stopped ? "stopped" : r.method === "not-running" ? "already stopped" : "could not stop"} ${r.id}${r.detail ? ` (${r.detail})` : ""}`,
147
+ )
148
+ .join("\n") +
149
+ (preview ? `\nPreview only. Re-run with --yes to stop these.` : "")
150
+ : "No matching running servers.",
151
+ );
152
+ }
153
+ if (results.some((r) => "stopped" in r && !r.stopped && r.method === "refused"))
154
+ emit.setExitCode(1);
155
+ } catch (error) {
156
+ fail(error);
157
+ }
158
+ },
159
+ );
160
+
161
+ cmd
162
+ .command("gc")
163
+ .description(
164
+ `Stop idle session servers whose starting agent ended (preview unless --yes); services are never touched`,
165
+ )
166
+ .option(
167
+ "--idle-hours <hours>",
168
+ `Idle window before a server may stop (default ${SERVER_DEFAULT_IDLE_HOURS})`,
169
+ )
170
+ .option(
171
+ "--owner-stale-hours <hours>",
172
+ `Treat a starting agent unobserved this long as gone (default ${SERVER_DEFAULT_OWNER_STALE_HOURS})`,
173
+ )
174
+ .option("--yes", "Stop the selected servers")
175
+ .option("--json", "Emit JSON")
176
+ .action(
177
+ async (options: {
178
+ idleHours?: string;
179
+ ownerStaleHours?: string;
180
+ yes?: boolean;
181
+ json?: boolean;
182
+ }) => {
183
+ try {
184
+ const coordRoot = root();
185
+ const configured = serverGcConfig(coordRoot);
186
+ const idleHours = positive(
187
+ options.idleHours,
188
+ configured.idle_hours ?? SERVER_DEFAULT_IDLE_HOURS,
189
+ "--idle-hours",
190
+ );
191
+ const ownerStaleHours = positive(
192
+ options.ownerStaleHours,
193
+ configured.owner_stale_hours ?? SERVER_DEFAULT_OWNER_STALE_HOURS,
194
+ "--owner-stale-hours",
195
+ );
196
+ const report = listServers({ coordRoot, scan: false, ownerStaleHours });
197
+ const decisions = planServerGc(report.servers, { idleHours, ownerStaleHours });
198
+ const stops = decisions.filter((d) => d.action === "stop");
199
+ const results: StopResult[] = [];
200
+ if (options.yes) {
201
+ for (const decision of stops) {
202
+ const view = report.servers.find((v) => v.record.id === decision.id)!;
203
+ results.push(await stopServer(view.record, { coordRoot }));
204
+ }
205
+ }
206
+ if (options.json) {
207
+ emit.config({ format: "json" });
208
+ emit.data({ applied: Boolean(options.yes), pruned: report.pruned, decisions, results });
209
+ } else {
210
+ const lines = decisions.map(
211
+ (d) =>
212
+ `${d.action === "stop" ? (options.yes ? "stopped" : "would stop") : "keep"} ${d.id} ${d.reason}`,
213
+ );
214
+ if (report.pruned.length)
215
+ lines.push(`Removed records of exited servers: ${report.pruned.join(", ")}`);
216
+ if (!decisions.length) lines.push("No registered servers.");
217
+ if (stops.length && !options.yes)
218
+ lines.push("Preview only. Re-run with --yes to stop.");
219
+ emit.text(lines.join("\n"));
220
+ }
221
+ } catch (error) {
222
+ fail(error);
223
+ }
224
+ },
225
+ );
226
+
227
+ cmd
228
+ .command("adopt")
229
+ .description(
230
+ "Register running servers that never registered (only pids the list reports as unregistered)",
231
+ )
232
+ .option(
233
+ "--pid <pid>",
234
+ "Adopt this unregistered listener (repeatable)",
235
+ (value: string, previous: string[] = []) => [...previous, value],
236
+ )
237
+ .option("--all", "Adopt every unregistered listener the scan reports")
238
+ .option("--type <type>", "Server family to record", "adopted")
239
+ .option("--kind <kind>", "session (cleaned up when idle) or service", "session")
240
+ .option("--label <label>", "Human-readable name (single --pid only)")
241
+ .option("--scope <path>", "What the server serves (default: its working directory)")
242
+ .option("--no-owner", "Record no owner, so gc never stops it")
243
+ .option("--json", "Emit JSON")
244
+ .action(
245
+ (options: {
246
+ pid?: string[];
247
+ all?: boolean;
248
+ type: string;
249
+ kind: string;
250
+ label?: string;
251
+ scope?: string;
252
+ owner?: boolean;
253
+ json?: boolean;
254
+ }) => {
255
+ try {
256
+ const coordRoot = root();
257
+ if (options.kind !== "session" && options.kind !== "service")
258
+ throw new Error("--kind must be session or service");
259
+ const pids = options.all
260
+ ? [...new Set(listServers({ coordRoot, sample: false }).unregistered.map((u) => u.pid))]
261
+ : (options.pid ?? []).map((value) => integer(value, "--pid"));
262
+ if (!pids.length)
263
+ throw new Error(options.all ? "no unregistered listeners" : "pass --pid or --all");
264
+ if (options.label && pids.length > 1) throw new Error("--label needs a single --pid");
265
+ const records = pids.map((pid) =>
266
+ adoptServer(pid, {
267
+ coordRoot,
268
+ type: options.type,
269
+ kind: options.kind as ServerKind,
270
+ ...(options.label ? { label: options.label } : {}),
271
+ ...(options.scope ? { scope: options.scope } : {}),
272
+ owner: options.owner === false ? "none" : "self",
273
+ }),
274
+ );
275
+ if (options.json) {
276
+ emit.config({ format: "json" });
277
+ emit.data({ adopted: records });
278
+ } else emit.text(records.map((r) => `adopted ${r.id} ${r.url ?? ""}`).join("\n"));
279
+ } catch (error) {
280
+ fail(error);
281
+ }
282
+ },
283
+ );
284
+
285
+ cmd
286
+ .command("port <type>")
287
+ .description(
288
+ "Print a free port from the type's configured range (.harnery/config.jsonc servers.port_ranges)",
289
+ )
290
+ .action((type: string) => {
291
+ try {
292
+ const port = allocateServerPort(type, { coordRoot: root() });
293
+ if (port === null) throw new Error(`no port range configured for ${type}`);
294
+ emit.text(String(port));
295
+ } catch (error) {
296
+ fail(error);
297
+ }
298
+ });
299
+
300
+ cmd
301
+ .command("logs <id>")
302
+ .description("Print the end of a server's log file")
303
+ .option("-n, --lines <n>", "Lines to print", "60")
304
+ .action((id: string, options: { lines: string }) => {
305
+ try {
306
+ const record = readServer(id, { coordRoot: root() });
307
+ if (!record) throw new Error(`no server named ${id}`);
308
+ if (!record.log || !existsSync(record.log))
309
+ throw new Error(`${id} did not record a log file`);
310
+ const lines = positive(options.lines, 60, "--lines");
311
+ const size = statSync(record.log).size;
312
+ const content = readFileSync(record.log, "utf8").slice(Math.max(0, size - 512 * 1024));
313
+ emit.text(
314
+ content
315
+ .split("\n")
316
+ .slice(-Math.floor(lines) - 1)
317
+ .join("\n"),
318
+ );
319
+ } catch (error) {
320
+ fail(error);
321
+ }
322
+ });
323
+
324
+ cmd
325
+ .command("open <id>")
326
+ .description("Print a server's URL")
327
+ .action((id: string) => {
328
+ try {
329
+ const record = readServer(id, { coordRoot: root() });
330
+ if (!record) throw new Error(`no server named ${id}`);
331
+ if (!record.url) throw new Error(`${id} did not record a URL`);
332
+ emit.text(record.url);
333
+ } catch (error) {
334
+ fail(error);
335
+ }
336
+ });
337
+
338
+ cmd
339
+ .command("register")
340
+ .description(
341
+ "Record a server started by a script or another language (prints the record; the calling agent is recorded as owner)",
342
+ )
343
+ .requiredOption("--type <type>", "Server family, for example preview or review")
344
+ .option("--kind <kind>", "session (per piece of work) or service (long-running)", "session")
345
+ .option(
346
+ "--pid <pid>",
347
+ "Process that owns the server's lifetime (default: this command's parent)",
348
+ )
349
+ .option("--port <port>", "Listening port")
350
+ .option("--id <id>", "Stable id (default derived from type and scope)")
351
+ .option("--url <url>", "Local URL")
352
+ .option("--scope <path>", "What the server serves, usually a directory")
353
+ .option("--label <label>", "Human-readable name")
354
+ .option("--log <path>", "Log file")
355
+ .option("--cwd <path>", "Working directory")
356
+ .action(
357
+ (options: {
358
+ type: string;
359
+ kind: string;
360
+ pid?: string;
361
+ port?: string;
362
+ id?: string;
363
+ url?: string;
364
+ scope?: string;
365
+ label?: string;
366
+ log?: string;
367
+ cwd?: string;
368
+ }) => {
369
+ try {
370
+ if (options.kind !== "session" && options.kind !== "service")
371
+ throw new Error("--kind must be session or service");
372
+ if (options.id && !isValidServerId(options.id))
373
+ throw new Error(`invalid id: ${options.id}`);
374
+ const record = registerServer(
375
+ {
376
+ kind: options.kind as ServerKind,
377
+ type: options.type,
378
+ pid: options.pid ? integer(options.pid, "--pid") : process.ppid,
379
+ ...(options.port ? { port: integer(options.port, "--port") } : {}),
380
+ ...(options.id ? { id: options.id } : {}),
381
+ ...(options.url ? { url: options.url } : {}),
382
+ ...(options.scope ? { scope: options.scope } : {}),
383
+ ...(options.label ? { label: options.label } : {}),
384
+ ...(options.log ? { log: options.log } : {}),
385
+ ...(options.cwd ? { cwd: options.cwd } : {}),
386
+ },
387
+ { coordRoot: root() },
388
+ );
389
+ emit.config({ format: "json" });
390
+ emit.data(record);
391
+ } catch (error) {
392
+ fail(error);
393
+ }
394
+ },
395
+ );
396
+
397
+ cmd
398
+ .command("unregister <id>")
399
+ .description("Remove a server's record (with --pid, only if the record still names that pid)")
400
+ .option("--pid <pid>", "Only remove when the record names this pid")
401
+ .action((id: string, options: { pid?: string }) => {
402
+ try {
403
+ const removed = unregisterServer(id, {
404
+ coordRoot: root(),
405
+ ...(options.pid ? { pid: integer(options.pid, "--pid") } : {}),
406
+ });
407
+ emit.config({ format: "json" });
408
+ emit.data({ id, removed });
409
+ } catch (error) {
410
+ fail(error);
411
+ }
412
+ });
413
+
414
+ cmd
415
+ .command("touch <id>")
416
+ .description("Mark a server as used now, postponing idle cleanup")
417
+ .action((id: string) => {
418
+ try {
419
+ touchServer(id, { coordRoot: root() });
420
+ } catch (error) {
421
+ fail(error);
422
+ }
423
+ });
424
+ }
425
+
426
+ function integer(value: string, flag: string): number {
427
+ const parsed = Number(value);
428
+ if (!Number.isSafeInteger(parsed) || parsed < 1)
429
+ throw new Error(`${flag} must be a positive integer`);
430
+ return parsed;
431
+ }
432
+
433
+ function positive(value: string | undefined, fallback: number, flag: string): number {
434
+ if (value === undefined) return fallback;
435
+ const parsed = Number(value);
436
+ if (!Number.isFinite(parsed) || parsed <= 0) throw new Error(`${flag} must be a positive number`);
437
+ return parsed;
438
+ }
439
+
440
+ function duration(ms: number): string {
441
+ const minutes = Math.round(ms / 60_000);
442
+ if (minutes < 60) return `${minutes}m`;
443
+ const hours = Math.floor(minutes / 60);
444
+ if (hours < 48) return `${hours}h${String(minutes % 60).padStart(2, "0")}m`;
445
+ return `${Math.floor(hours / 24)}d`;
446
+ }
447
+
448
+ function ownerText(view: ServerView): string {
449
+ if (!view.record.owner) return "no agent";
450
+ const name = view.record.owner.name ?? view.record.owner.instance_id.slice(0, 8);
451
+ return view.owner_state === "live" ? name : `${name} (${view.owner_state})`;
452
+ }
453
+
454
+ /** Plain-text table grouped by kind; exported for tests. */
455
+ export function renderReport(report: ServersReport, bin: string): string {
456
+ const lines: string[] = [];
457
+ for (const kind of ["service", "session"] as const) {
458
+ const views = report.servers.filter((view) => view.record.kind === kind);
459
+ lines.push(
460
+ kind === "service"
461
+ ? "Services (stopped only by hand)"
462
+ : "Session servers (cleaned up when idle and their agent is gone)",
463
+ );
464
+ if (!views.length) {
465
+ lines.push(" none");
466
+ continue;
467
+ }
468
+ for (const view of views) {
469
+ const { record } = view;
470
+ const state = view.state === "running" ? "" : ` [${view.state}]`;
471
+ const started = Date.parse(record.started_at);
472
+ const use =
473
+ kind === "service"
474
+ ? `up ${Number.isFinite(started) ? duration(Date.now() - started) : "?"}`
475
+ : view.connections === null
476
+ ? `idle ${duration(view.idle_ms)}`
477
+ : view.connections > 0
478
+ ? `${view.connections} open connection(s)`
479
+ : `idle ${duration(view.idle_ms)}`;
480
+ lines.push(
481
+ ` ${record.id}${state} ${record.url ?? (record.port ? `port ${record.port}` : "")}`,
482
+ ` ${record.label} · ${ownerText(view)} · ${use}`,
483
+ );
484
+ }
485
+ }
486
+ if (report.scan === "unsupported") {
487
+ lines.push("Unregistered listeners: not checked on this platform");
488
+ } else if (report.unregistered.length) {
489
+ const byPid = new Map<number, { ports: number[]; command: string }>();
490
+ for (const item of report.unregistered) {
491
+ const entry = byPid.get(item.pid) ?? { ports: [], command: item.command };
492
+ entry.ports.push(item.port);
493
+ byPid.set(item.pid, entry);
494
+ }
495
+ lines.push(`Unregistered servers inside this project (${byPid.size} processes):`);
496
+ for (const [pid, entry] of byPid)
497
+ lines.push(` pid ${pid} port ${entry.ports.join(", ")} ${entry.command.slice(0, 100)}`);
498
+ lines.push(
499
+ ` These started without registering. Stop one with: ${bin} servers stop --pid <pid>`,
500
+ );
501
+ }
502
+ if (report.pruned.length)
503
+ lines.push(`Removed records of exited servers: ${report.pruned.join(", ")}`);
504
+ lines.push(`Control: ${bin} servers stop <id> · ${bin} servers gc · ${bin} servers logs <id>`);
505
+ return lines.join("\n");
506
+ }