cloving 1.2.0 → 1.3.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 (518) hide show
  1. package/README.md +525 -185
  2. package/dist/src/agent-harness-shell.d.ts +12 -0
  3. package/dist/src/agent-harness-shell.js +90 -0
  4. package/dist/src/agent-harness-versions.d.ts +8 -0
  5. package/dist/src/agent-harness-versions.js +49 -0
  6. package/dist/src/agent-harnesses.d.ts +4 -0
  7. package/dist/src/agent-harnesses.js +13 -0
  8. package/dist/src/authority/commands.d.ts +1 -0
  9. package/dist/src/authority/commands.js +24 -4
  10. package/dist/src/authority/gate-reduction.d.ts +1 -0
  11. package/dist/src/authority/gate-reduction.js +18 -3
  12. package/dist/src/authority/gates.d.ts +2 -1
  13. package/dist/src/authority/gates.js +5 -1
  14. package/dist/src/authority/operations.js +1 -1
  15. package/dist/src/authority/reduction.d.ts +1 -1
  16. package/dist/src/authority/reduction.js +6 -1
  17. package/dist/src/authority/status-input.d.ts +1 -1
  18. package/dist/src/authority/status.d.ts +1 -0
  19. package/dist/src/authority/status.js +27 -3
  20. package/dist/src/authority/transitions.d.ts +5 -5
  21. package/dist/src/authority/transitions.js +6 -0
  22. package/dist/src/broker/actor/authority-delivery.js +1 -0
  23. package/dist/src/broker/actor/authority-queries.d.ts +7 -1
  24. package/dist/src/broker/actor/authority-queries.js +17 -2
  25. package/dist/src/broker/actor/authority-receipts.js +11 -1
  26. package/dist/src/broker/actor/authority-session.js +2 -2
  27. package/dist/src/broker/actor/authority-settings.js +4 -0
  28. package/dist/src/broker/actor/delivery-intents.d.ts +2 -1
  29. package/dist/src/broker/actor/delivery-intents.js +4 -4
  30. package/dist/src/broker/actor/effect-delivery.js +4 -1
  31. package/dist/src/broker/actor/effect-queries.d.ts +1 -0
  32. package/dist/src/broker/actor/effect-queries.js +7 -0
  33. package/dist/src/broker/actor/pin-check.js +1 -1
  34. package/dist/src/broker/actor/stop-plan.js +1 -1
  35. package/dist/src/broker/actor/turn-state.js +1 -1
  36. package/dist/src/broker/authority-admission.js +22 -9
  37. package/dist/src/broker/authority-restore.js +1 -0
  38. package/dist/src/broker/decision-restore.js +4 -4
  39. package/dist/src/broker/socket-address.d.ts +3 -1
  40. package/dist/src/broker/socket-address.js +2 -2
  41. package/dist/src/cli/arguments.d.ts +11 -13
  42. package/dist/src/cli/arguments.js +37 -107
  43. package/dist/src/cli/command-effects.d.ts +7 -0
  44. package/dist/src/cli/command-effects.js +39 -0
  45. package/dist/src/cli/commands.js +11 -4
  46. package/dist/src/cli/config-arguments.d.ts +11 -0
  47. package/dist/src/cli/config-arguments.js +59 -0
  48. package/dist/src/cli/config-command.d.ts +2 -0
  49. package/dist/src/cli/config-command.js +105 -0
  50. package/dist/src/cli/config-file.d.ts +3 -0
  51. package/dist/src/cli/config-file.js +50 -0
  52. package/dist/src/cli/config-lock.d.ts +7 -0
  53. package/dist/src/cli/config-lock.js +61 -0
  54. package/dist/src/cli/config-notice.d.ts +5 -0
  55. package/dist/src/cli/config-notice.js +46 -0
  56. package/dist/src/cli/doctor-github.d.ts +9 -0
  57. package/dist/src/cli/doctor-github.js +138 -0
  58. package/dist/src/cli/doctor-harness.d.ts +13 -0
  59. package/dist/src/cli/doctor-harness.js +242 -0
  60. package/dist/src/cli/doctor-local.d.ts +18 -0
  61. package/dist/src/cli/doctor-local.js +77 -0
  62. package/dist/src/cli/doctor-runtime.d.ts +15 -0
  63. package/dist/src/cli/doctor-runtime.js +176 -0
  64. package/dist/src/cli/doctor-service.d.ts +1 -0
  65. package/dist/src/cli/doctor-service.js +11 -0
  66. package/dist/src/cli/doctor-workflows.d.ts +3 -0
  67. package/dist/src/cli/doctor-workflows.js +45 -0
  68. package/dist/src/cli/doctor.d.ts +15 -0
  69. package/dist/src/cli/doctor.js +190 -0
  70. package/dist/src/cli/github-ci-progress.d.ts +8 -0
  71. package/dist/src/cli/github-ci-progress.js +83 -0
  72. package/dist/src/cli/github-retry.d.ts +6 -0
  73. package/dist/src/cli/github-retry.js +47 -0
  74. package/dist/src/cli/harness-probes.d.ts +22 -0
  75. package/dist/src/cli/harness-probes.js +183 -0
  76. package/dist/src/cli/main.d.ts +7 -1
  77. package/dist/src/cli/main.js +39 -61
  78. package/dist/src/cli/phone-arguments.d.ts +14 -0
  79. package/dist/src/cli/phone-arguments.js +37 -0
  80. package/dist/src/cli/phone-client.d.ts +2 -0
  81. package/dist/src/cli/phone-client.js +32 -0
  82. package/dist/src/cli/phone-command.d.ts +13 -0
  83. package/dist/src/cli/phone-command.js +101 -0
  84. package/dist/src/cli/probe-command.d.ts +36 -0
  85. package/dist/src/cli/probe-command.js +59 -0
  86. package/dist/src/cli/project-settings.d.ts +4 -0
  87. package/dist/src/cli/project-settings.js +33 -0
  88. package/dist/src/cli/render.d.ts +2 -1
  89. package/dist/src/cli/render.js +44 -28
  90. package/dist/src/cli/report-github.d.ts +18 -0
  91. package/dist/src/cli/report-github.js +200 -0
  92. package/dist/src/cli/report.d.ts +17 -0
  93. package/dist/src/cli/report.js +192 -0
  94. package/dist/src/cli/repository-arguments.d.ts +5 -0
  95. package/dist/src/cli/repository-arguments.js +34 -0
  96. package/dist/src/cli/runtime-arguments.d.ts +4 -0
  97. package/dist/src/cli/runtime-arguments.js +15 -0
  98. package/dist/src/cli/runtime-directory.d.ts +6 -2
  99. package/dist/src/cli/runtime-directory.js +72 -14
  100. package/dist/src/cli/toolchain.d.ts +2 -0
  101. package/dist/src/cli/toolchain.js +22 -2
  102. package/dist/src/cli/usage-render.d.ts +5 -0
  103. package/dist/src/cli/usage-render.js +40 -1
  104. package/dist/src/cli/usage.d.ts +3 -3
  105. package/dist/src/cli/usage.js +70 -44
  106. package/dist/src/cli/watch-actions.d.ts +2 -0
  107. package/dist/src/cli/watch-actions.js +4 -4
  108. package/dist/src/cli/watch-board-view.d.ts +1 -1
  109. package/dist/src/cli/watch-board-view.js +8 -2
  110. package/dist/src/cli/watch-board.js +23 -10
  111. package/dist/src/cli/watch-card-forms.js +2 -2
  112. package/dist/src/cli/watch-client.d.ts +1 -1
  113. package/dist/src/cli/watch-client.js +7 -2
  114. package/dist/src/cli/watch-header-view.d.ts +27 -0
  115. package/dist/src/cli/watch-header-view.js +116 -0
  116. package/dist/src/cli/watch-model.js +17 -4
  117. package/dist/src/cli/watch-page.js +48 -12
  118. package/dist/src/cli/watch-phone-client.d.ts +1 -0
  119. package/dist/src/cli/watch-phone-client.js +84 -0
  120. package/dist/src/cli/watch-phone-view.d.ts +56 -0
  121. package/dist/src/cli/watch-phone-view.js +113 -0
  122. package/dist/src/cli/watch-phone.d.ts +5 -0
  123. package/dist/src/cli/watch-phone.js +53 -0
  124. package/dist/src/cli/watch-pull-request.d.ts +3 -1
  125. package/dist/src/cli/watch-pull-request.js +4 -0
  126. package/dist/src/cli/watch-render.d.ts +9 -1
  127. package/dist/src/cli/watch-server.js +23 -5
  128. package/dist/src/cli/watch-turn.d.ts +4 -1
  129. package/dist/src/cli/watch-turn.js +37 -1
  130. package/dist/src/cli/watch-usage.js +5 -1
  131. package/dist/src/cli/watch-view.d.ts +6 -20
  132. package/dist/src/cli/watch-view.js +14 -106
  133. package/dist/src/cli.js +106 -23
  134. package/dist/src/command-settings.d.ts +16 -0
  135. package/dist/src/command-settings.js +42 -0
  136. package/dist/src/configuration-change.d.ts +11 -0
  137. package/dist/src/configuration-change.js +62 -0
  138. package/dist/src/configuration-display.d.ts +21 -0
  139. package/dist/src/configuration-display.js +69 -0
  140. package/dist/src/configuration-schema.d.ts +49 -0
  141. package/dist/src/configuration-schema.js +159 -0
  142. package/dist/src/daemon/claude-turn.d.ts +19 -5
  143. package/dist/src/daemon/claude-turn.js +116 -53
  144. package/dist/src/daemon/claude-worker.d.ts +3 -3
  145. package/dist/src/daemon/claude-worker.js +65 -76
  146. package/dist/src/daemon/command-journal.d.ts +5 -0
  147. package/dist/src/daemon/command-journal.js +50 -0
  148. package/dist/src/daemon/command-process.d.ts +28 -0
  149. package/dist/src/daemon/command-process.js +164 -0
  150. package/dist/src/daemon/command-worker.d.ts +19 -0
  151. package/dist/src/daemon/command-worker.js +182 -0
  152. package/dist/src/daemon/cursor-stream.d.ts +4 -0
  153. package/dist/src/daemon/cursor-stream.js +154 -0
  154. package/dist/src/daemon/daemon-adapters.d.ts +58 -0
  155. package/dist/src/daemon/daemon-adapters.js +102 -0
  156. package/dist/src/daemon/daemon-loops.d.ts +14 -0
  157. package/dist/src/daemon/daemon-loops.js +39 -0
  158. package/dist/src/daemon/daemon.d.ts +2 -33
  159. package/dist/src/daemon/daemon.js +126 -132
  160. package/dist/src/daemon/declared-input.d.ts +3 -0
  161. package/dist/src/daemon/declared-input.js +9 -0
  162. package/dist/src/daemon/delivery-router.js +1 -1
  163. package/dist/src/daemon/gemini-stream.d.ts +4 -0
  164. package/dist/src/daemon/gemini-stream.js +129 -0
  165. package/dist/src/daemon/github-ci-rerun.d.ts +26 -0
  166. package/dist/src/daemon/github-ci-rerun.js +92 -0
  167. package/dist/src/daemon/github-ci.js +89 -8
  168. package/dist/src/daemon/github-merge-call.d.ts +2 -0
  169. package/dist/src/daemon/github-merge.js +7 -3
  170. package/dist/src/daemon/github-methods.js +10 -0
  171. package/dist/src/daemon/github-operation.d.ts +1 -0
  172. package/dist/src/daemon/github-retry.js +8 -44
  173. package/dist/src/daemon/issue-queue.d.ts +1 -0
  174. package/dist/src/daemon/issue-queue.js +3 -26
  175. package/dist/src/daemon/notify.js +2 -1
  176. package/dist/src/daemon/opencode-stream.d.ts +5 -0
  177. package/dist/src/daemon/opencode-stream.js +87 -0
  178. package/dist/src/daemon/queue-status.d.ts +4 -0
  179. package/dist/src/daemon/queue-status.js +38 -0
  180. package/dist/src/daemon/retry-worktree.d.ts +18 -0
  181. package/dist/src/daemon/retry-worktree.js +139 -0
  182. package/dist/src/daemon/retrying-turn.d.ts +2 -0
  183. package/dist/src/daemon/retrying-turn.js +108 -30
  184. package/dist/src/daemon/schedule-journal.d.ts +19 -0
  185. package/dist/src/daemon/schedule-journal.js +57 -0
  186. package/dist/src/daemon/text-output.d.ts +18 -0
  187. package/dist/src/daemon/text-output.js +44 -0
  188. package/dist/src/daemon/ticket-registration.d.ts +5 -7
  189. package/dist/src/daemon/ticket-registration.js +85 -40
  190. package/dist/src/daemon/turn-command.d.ts +10 -0
  191. package/dist/src/daemon/turn-command.js +33 -0
  192. package/dist/src/daemon/turn-report.d.ts +18 -0
  193. package/dist/src/daemon/turn-report.js +79 -0
  194. package/dist/src/daemon/work-tree.d.ts +2 -1
  195. package/dist/src/daemon/work-tree.js +15 -12
  196. package/dist/src/daemon/workflow-generations.d.ts +32 -0
  197. package/dist/src/daemon/workflow-generations.js +57 -0
  198. package/dist/src/daemon/workflow-replay.d.ts +16 -0
  199. package/dist/src/daemon/workflow-replay.js +58 -0
  200. package/dist/src/daemon/workflow-schedules.d.ts +27 -0
  201. package/dist/src/daemon/workflow-schedules.js +185 -0
  202. package/dist/src/decision-wait.d.ts +1 -0
  203. package/dist/src/decision-wait.js +3 -0
  204. package/dist/src/delivery-backoff.d.ts +1 -0
  205. package/dist/src/delivery-backoff.js +3 -0
  206. package/dist/src/doctor-report.d.ts +24 -0
  207. package/dist/src/doctor-report.js +70 -0
  208. package/dist/src/execution-pin.d.ts +5 -0
  209. package/dist/src/execution-pin.js +67 -4
  210. package/dist/src/grok-result.d.ts +12 -0
  211. package/dist/src/grok-result.js +61 -0
  212. package/dist/src/harness-defaults.d.ts +35 -0
  213. package/dist/src/harness-defaults.js +121 -0
  214. package/dist/src/host/decision-resume.d.ts +35 -0
  215. package/dist/src/host/decision-resume.js +86 -0
  216. package/dist/src/host/gate-bridge.d.ts +48 -0
  217. package/dist/src/host/gate-bridge.js +124 -0
  218. package/dist/src/host/run-wait.d.ts +26 -0
  219. package/dist/src/host/run-wait.js +37 -0
  220. package/dist/src/host/start-delivery.d.ts +1 -1
  221. package/dist/src/host/start-delivery.js +10 -3
  222. package/dist/src/host/step-bridge-host.d.ts +6 -20
  223. package/dist/src/host/step-bridge-host.js +44 -58
  224. package/dist/src/merge-request.d.ts +19 -0
  225. package/dist/src/merge-request.js +32 -0
  226. package/dist/src/phone/canonical-json.d.ts +1 -0
  227. package/dist/src/phone/canonical-json.js +16 -0
  228. package/dist/src/phone/phone-link.d.ts +7 -0
  229. package/dist/src/phone/phone-link.js +21 -0
  230. package/dist/src/phone/qr-data.d.ts +8 -0
  231. package/dist/src/phone/qr-data.js +133 -0
  232. package/dist/src/phone/qr-matrix.d.ts +6 -0
  233. package/dist/src/phone/qr-matrix.js +175 -0
  234. package/dist/src/phone/qr-penalty.d.ts +3 -0
  235. package/dist/src/phone/qr-penalty.js +67 -0
  236. package/dist/src/phone/qr-picture.d.ts +5 -0
  237. package/dist/src/phone/qr-picture.js +24 -0
  238. package/dist/src/private-config.schema.json +263 -0
  239. package/dist/src/project-config.schema.json +65 -0
  240. package/dist/src/project-settings.d.ts +19 -0
  241. package/dist/src/project-settings.js +131 -0
  242. package/dist/src/relay/answer-check.d.ts +24 -0
  243. package/dist/src/relay/answer-check.js +145 -0
  244. package/dist/src/relay/answer-inbox.d.ts +15 -0
  245. package/dist/src/relay/answer-inbox.js +173 -0
  246. package/dist/src/relay/daemon-keys.d.ts +16 -0
  247. package/dist/src/relay/daemon-keys.js +75 -0
  248. package/dist/src/relay/decision-check.d.ts +3 -0
  249. package/dist/src/relay/decision-check.js +77 -0
  250. package/dist/src/relay/decision-close.d.ts +6 -0
  251. package/dist/src/relay/decision-close.js +72 -0
  252. package/dist/src/relay/decision-envelopes.d.ts +29 -0
  253. package/dist/src/relay/decision-envelopes.js +93 -0
  254. package/dist/src/relay/decision-watch.d.ts +14 -0
  255. package/dist/src/relay/decision-watch.js +68 -0
  256. package/dist/src/relay/envelope-post.d.ts +8 -0
  257. package/dist/src/relay/envelope-post.js +71 -0
  258. package/dist/src/relay/envelope.d.ts +28 -0
  259. package/dist/src/relay/envelope.js +53 -0
  260. package/dist/src/relay/hpke.d.ts +25 -0
  261. package/dist/src/relay/hpke.js +108 -0
  262. package/dist/src/relay/pairing-checks.d.ts +42 -0
  263. package/dist/src/relay/pairing-checks.js +172 -0
  264. package/dist/src/relay/pairing-follow.d.ts +8 -0
  265. package/dist/src/relay/pairing-follow.js +95 -0
  266. package/dist/src/relay/pairing-session.d.ts +29 -0
  267. package/dist/src/relay/pairing-session.js +87 -0
  268. package/dist/src/relay/phone-delivery.d.ts +51 -0
  269. package/dist/src/relay/phone-delivery.js +139 -0
  270. package/dist/src/relay/phone-devices.d.ts +36 -0
  271. package/dist/src/relay/phone-devices.js +46 -0
  272. package/dist/src/relay/phone-inbox.d.ts +18 -0
  273. package/dist/src/relay/phone-inbox.js +17 -0
  274. package/dist/src/relay/phone-operations.d.ts +18 -0
  275. package/dist/src/relay/phone-operations.js +121 -0
  276. package/dist/src/relay/phone-outbox.d.ts +21 -0
  277. package/dist/src/relay/phone-outbox.js +25 -0
  278. package/dist/src/relay/phone-socket.d.ts +6 -0
  279. package/dist/src/relay/phone-socket.js +63 -0
  280. package/dist/src/relay/relay-control.d.ts +7 -0
  281. package/dist/src/relay/relay-control.js +24 -0
  282. package/dist/src/relay/relay-files.d.ts +2 -0
  283. package/dist/src/relay/relay-files.js +23 -0
  284. package/dist/src/relay/relay-heartbeat.d.ts +17 -0
  285. package/dist/src/relay/relay-heartbeat.js +81 -0
  286. package/dist/src/relay/relay-http.d.ts +14 -0
  287. package/dist/src/relay/relay-http.js +63 -0
  288. package/dist/src/relay/relay-registration.d.ts +15 -0
  289. package/dist/src/relay/relay-registration.js +63 -0
  290. package/dist/src/relay/relay-service.d.ts +25 -0
  291. package/dist/src/relay/relay-service.js +216 -0
  292. package/dist/src/relay/relay-store.d.ts +48 -0
  293. package/dist/src/relay/relay-store.js +131 -0
  294. package/dist/src/relay/sealed-post.d.ts +25 -0
  295. package/dist/src/relay/sealed-post.js +52 -0
  296. package/dist/src/relay.d.ts +1 -0
  297. package/dist/src/relay.js +8 -0
  298. package/dist/src/report-statistics.d.ts +50 -0
  299. package/dist/src/report-statistics.js +70 -0
  300. package/dist/src/report.d.ts +171 -0
  301. package/dist/src/report.js +119 -0
  302. package/dist/src/repository-identity.d.ts +4 -0
  303. package/dist/src/repository-identity.js +4 -0
  304. package/dist/src/review-subject.d.ts +41 -0
  305. package/dist/src/review-subject.js +91 -0
  306. package/dist/src/run-wait.d.ts +23 -0
  307. package/dist/src/run-wait.js +20 -0
  308. package/dist/src/service/active-tickets.d.ts +4 -0
  309. package/dist/src/service/active-tickets.js +49 -0
  310. package/dist/src/service/command-effects.d.ts +4 -0
  311. package/dist/src/service/command-effects.js +16 -0
  312. package/dist/src/service/decision-handler.d.ts +4 -0
  313. package/dist/src/service/decision-handler.js +39 -4
  314. package/dist/src/service/gateway.js +18 -3
  315. package/dist/src/service/registry.d.ts +16 -3
  316. package/dist/src/service/registry.js +34 -3
  317. package/dist/src/service/replies.d.ts +9 -1
  318. package/dist/src/service/replies.js +12 -0
  319. package/dist/src/service/run-handlers.js +14 -55
  320. package/dist/src/service/run-metadata.d.ts +8 -0
  321. package/dist/src/service/run-metadata.js +53 -0
  322. package/dist/src/service/run-waits.d.ts +3 -0
  323. package/dist/src/service/run-waits.js +41 -0
  324. package/dist/src/service/service.d.ts +18 -2
  325. package/dist/src/service/service.js +23 -7
  326. package/dist/src/service/submission-handler.d.ts +0 -1
  327. package/dist/src/service/submission-handler.js +6 -34
  328. package/dist/src/service/worker-port.d.ts +1 -0
  329. package/dist/src/service/worker-totals.js +8 -23
  330. package/dist/src/step-bridge.d.ts +23 -0
  331. package/dist/src/steps/decision-gate.d.ts +2 -1
  332. package/dist/src/steps/decision-gate.js +7 -0
  333. package/dist/src/steps/merge-by-person.d.ts +2 -0
  334. package/dist/src/steps/merge-by-person.js +18 -0
  335. package/dist/src/steps/merge-when-green.d.ts +2 -0
  336. package/dist/src/steps/merge-when-green.js +62 -0
  337. package/dist/src/steps/review-by-agent.d.ts +5 -0
  338. package/dist/src/steps/review-by-agent.js +124 -0
  339. package/dist/src/steps/review-by-person.d.ts +4 -0
  340. package/dist/src/steps/review-by-person.js +16 -0
  341. package/dist/src/steps/review-subject.d.ts +2 -0
  342. package/dist/src/steps/review-subject.js +11 -0
  343. package/dist/src/steps/run-command.d.ts +2 -0
  344. package/dist/src/steps/run-command.js +15 -0
  345. package/dist/src/steps/sleep.d.ts +3 -0
  346. package/dist/src/steps/sleep.js +19 -0
  347. package/dist/src/steps/wait-for-decision.d.ts +2 -0
  348. package/dist/src/steps/wait-for-decision.js +71 -0
  349. package/dist/src/steps/wait-for-run.d.ts +5 -0
  350. package/dist/src/steps/wait-for-run.js +9 -0
  351. package/dist/src/storage/decision-snapshots.d.ts +1 -0
  352. package/dist/src/storage/decision-snapshots.js +7 -1
  353. package/dist/src/submission.d.ts +4 -0
  354. package/dist/src/submission.js +6 -2
  355. package/dist/src/testing.d.ts +29 -0
  356. package/dist/src/testing.js +264 -0
  357. package/dist/src/ticket-status-stream.d.ts +9 -0
  358. package/dist/src/ticket-status-stream.js +94 -0
  359. package/dist/src/token-prices.d.ts +12 -4
  360. package/dist/src/token-prices.js +15 -2
  361. package/dist/src/turn-profile.d.ts +1 -0
  362. package/dist/src/turn-profile.js +8 -1
  363. package/dist/src/worker-usage.d.ts +16 -0
  364. package/dist/src/worker-usage.js +57 -0
  365. package/dist/src/workflow-admission.d.ts +5 -0
  366. package/dist/src/workflow-candidate.d.ts +7 -2
  367. package/dist/src/workflow-candidate.js +47 -5
  368. package/dist/src/workflow-declaration.d.ts +21 -3
  369. package/dist/src/workflow-declaration.js +77 -9
  370. package/dist/src/workflow-discovery.d.ts +2 -1
  371. package/dist/src/workflow-discovery.js +4 -1
  372. package/dist/src/workflow-refusals.js +3 -0
  373. package/dist/src/workflow-schedule.d.ts +22 -0
  374. package/dist/src/workflow-schedule.js +53 -0
  375. package/dist/src/workflows.d.ts +11 -1
  376. package/dist/src/workflows.js +8 -0
  377. package/dist/tools/admit-workflow.d.ts +1 -0
  378. package/dist/tools/admit-workflow.js +18 -5
  379. package/dist/tools/doctor-adoption.d.ts +1 -0
  380. package/dist/tools/doctor-adoption.js +11 -0
  381. package/dist/tools/project-configuration.d.ts +4 -0
  382. package/dist/tools/project-configuration.js +47 -0
  383. package/dist/tools/self-host-processes.d.ts +1 -0
  384. package/dist/tools/self-host-processes.js +33 -22
  385. package/dist/tools/self-host-project.d.ts +4 -0
  386. package/dist/tools/self-host-project.js +61 -14
  387. package/dist/tools/self-host-runtime.d.ts +16 -0
  388. package/dist/tools/self-host-runtime.js +60 -0
  389. package/dist/tools/self-host-workflows.d.ts +1 -0
  390. package/dist/tools/self-host-workflows.js +36 -1
  391. package/dist/tools/self-host.js +86 -61
  392. package/dist/tools/workflow-candidate-counts.d.ts +6 -2
  393. package/dist/tools/workflow-candidate-counts.js +31 -16
  394. package/dist/tools/workflow-candidate-fields.d.ts +5 -0
  395. package/dist/tools/workflow-candidate-fields.js +24 -0
  396. package/dist/tools/workflow-candidate-helper-counts.d.ts +10 -0
  397. package/dist/tools/workflow-candidate-helper-counts.js +129 -0
  398. package/dist/tools/workflow-candidate-program.d.ts +2 -0
  399. package/dist/tools/workflow-candidate-program.js +36 -8
  400. package/dist/tools/workflow-candidate-source.js +77 -15
  401. package/dist/tools/workflow-candidate-syntax.d.ts +8 -2
  402. package/dist/tools/workflow-candidate-syntax.js +70 -26
  403. package/dist/tools/workflow-command.d.ts +1 -0
  404. package/dist/tools/workflow-command.js +143 -0
  405. package/dist/tools/workflow-folder-capture.d.ts +1 -0
  406. package/dist/tools/workflow-folder-capture.js +47 -2
  407. package/dist/tools/workflow-folders.d.ts +17 -1
  408. package/dist/tools/workflow-folders.js +59 -6
  409. package/dist/tools/workflow-test-hook.d.ts +7 -0
  410. package/dist/tools/workflow-test-hook.js +16 -0
  411. package/dist/tools/workflow-test-loader.d.ts +1 -0
  412. package/dist/tools/workflow-test-loader.js +10 -0
  413. package/dist/workflows/develop-ticket.js +1 -1
  414. package/dist/workflows/ticket-prompts.js +5 -1
  415. package/package.json +46 -4
  416. package/templates/develop/README.md +78 -0
  417. package/templates/develop/develop/accept.ts +11 -0
  418. package/templates/develop/develop/flow-tests.ts +357 -0
  419. package/templates/develop/develop/local.ts +40 -0
  420. package/templates/develop/develop/option-tests.ts +174 -0
  421. package/templates/develop/develop/partial-choice.ts +29 -0
  422. package/templates/develop/develop/partial.ts +28 -0
  423. package/templates/develop/develop/pull.ts +152 -0
  424. package/templates/develop/develop/queue-partial.ts +39 -0
  425. package/templates/develop/develop/refusal-tests.ts +174 -0
  426. package/templates/develop/develop/report.ts +156 -0
  427. package/templates/develop/develop/round.ts +40 -0
  428. package/templates/develop/develop/scenarios.ts +87 -0
  429. package/templates/develop/develop/turn.ts +67 -0
  430. package/templates/develop/develop.declaration.json +24 -0
  431. package/templates/develop/develop.test.ts +287 -0
  432. package/templates/develop/develop.ts +30 -0
  433. package/templates/develop/template.json +153 -0
  434. package/dist/src/chats/analysis-batches.d.ts +0 -42
  435. package/dist/src/chats/analysis-batches.js +0 -97
  436. package/dist/src/chats/analysis-checkpoint.d.ts +0 -29
  437. package/dist/src/chats/analysis-checkpoint.js +0 -31
  438. package/dist/src/chats/analysis-contract.d.ts +0 -80
  439. package/dist/src/chats/analysis-contract.js +0 -16
  440. package/dist/src/chats/analysis-data.d.ts +0 -3
  441. package/dist/src/chats/analysis-data.js +0 -85
  442. package/dist/src/chats/analysis-input.d.ts +0 -18
  443. package/dist/src/chats/analysis-input.js +0 -97
  444. package/dist/src/chats/analysis-links.d.ts +0 -2
  445. package/dist/src/chats/analysis-links.js +0 -25
  446. package/dist/src/chats/analysis-merge.d.ts +0 -16
  447. package/dist/src/chats/analysis-merge.js +0 -134
  448. package/dist/src/chats/analysis-prompt.d.ts +0 -18
  449. package/dist/src/chats/analysis-prompt.js +0 -42
  450. package/dist/src/chats/analysis-proposal.d.ts +0 -4
  451. package/dist/src/chats/analysis-proposal.js +0 -41
  452. package/dist/src/chats/analysis-provenance.d.ts +0 -4
  453. package/dist/src/chats/analysis-provenance.js +0 -99
  454. package/dist/src/chats/analysis-shape.d.ts +0 -4
  455. package/dist/src/chats/analysis-shape.js +0 -235
  456. package/dist/src/chats/analysis.d.ts +0 -3
  457. package/dist/src/chats/analysis.js +0 -42
  458. package/dist/src/chats/corpus-input.d.ts +0 -5
  459. package/dist/src/chats/corpus-input.js +0 -288
  460. package/dist/src/chats/destination.d.ts +0 -4
  461. package/dist/src/chats/destination.js +0 -96
  462. package/dist/src/chats/import.d.ts +0 -15
  463. package/dist/src/chats/import.js +0 -243
  464. package/dist/src/chats/input-file.d.ts +0 -3
  465. package/dist/src/chats/input-file.js +0 -32
  466. package/dist/src/chats/normalize.d.ts +0 -29
  467. package/dist/src/chats/normalize.js +0 -155
  468. package/dist/src/chats/previous-import.d.ts +0 -5
  469. package/dist/src/chats/previous-import.js +0 -34
  470. package/dist/src/chats/reader.d.ts +0 -21
  471. package/dist/src/chats/reader.js +0 -118
  472. package/dist/src/chats/redact-import.d.ts +0 -14
  473. package/dist/src/chats/redact-import.js +0 -141
  474. package/dist/src/chats/redaction.d.ts +0 -41
  475. package/dist/src/chats/redaction.js +0 -161
  476. package/dist/src/chats/reference-mapping.d.ts +0 -16
  477. package/dist/src/chats/reference-mapping.js +0 -153
  478. package/dist/src/chats/revision-input.d.ts +0 -3
  479. package/dist/src/chats/revision-input.js +0 -180
  480. package/dist/src/chats/revision.d.ts +0 -48
  481. package/dist/src/chats/revision.js +0 -94
  482. package/dist/src/chats/selection.d.ts +0 -25
  483. package/dist/src/chats/selection.js +0 -109
  484. package/dist/src/chats/source.d.ts +0 -23
  485. package/dist/src/chats/source.js +0 -135
  486. package/dist/src/chats/structure.d.ts +0 -57
  487. package/dist/src/chats/structure.js +0 -216
  488. package/dist/src/chats/workflow-generation-projection.d.ts +0 -8
  489. package/dist/src/chats/workflow-generation-projection.js +0 -75
  490. package/dist/src/chats/workflow-generation-source.d.ts +0 -25
  491. package/dist/src/chats/workflow-generation-source.js +0 -124
  492. package/dist/src/chats/workflow-generation-text.d.ts +0 -23
  493. package/dist/src/chats/workflow-generation-text.js +0 -74
  494. package/dist/src/chats/workflow-generation.d.ts +0 -69
  495. package/dist/src/chats/workflow-generation.js +0 -142
  496. package/dist/src/daemon/analysis-launch.d.ts +0 -5
  497. package/dist/src/daemon/analysis-launch.js +0 -81
  498. package/dist/src/daemon/analysis-token.d.ts +0 -6
  499. package/dist/src/daemon/analysis-token.js +0 -88
  500. package/dist/src/daemon/analysis-worker.d.ts +0 -20
  501. package/dist/src/daemon/analysis-worker.js +0 -271
  502. package/dist/src/daemon/chats-batch-binding.d.ts +0 -23
  503. package/dist/src/daemon/chats-batch-binding.js +0 -99
  504. package/dist/src/daemon/chats-capture.d.ts +0 -18
  505. package/dist/src/daemon/chats-capture.js +0 -194
  506. package/dist/src/daemon/chats-checkpoints.d.ts +0 -41
  507. package/dist/src/daemon/chats-checkpoints.js +0 -186
  508. package/dist/src/daemon/chats-files.d.ts +0 -42
  509. package/dist/src/daemon/chats-files.js +0 -252
  510. package/dist/src/daemon/chats-follow-up.d.ts +0 -38
  511. package/dist/src/daemon/chats-follow-up.js +0 -180
  512. package/dist/src/daemon/chats-question.d.ts +0 -15
  513. package/dist/src/daemon/chats-question.js +0 -93
  514. package/dist/src/daemon/chats-worker.d.ts +0 -13
  515. package/dist/src/daemon/chats-worker.js +0 -201
  516. package/dist/workflows/analyze-chats.d.ts +0 -15
  517. package/dist/workflows/analyze-chats.declaration.json +0 -22
  518. package/dist/workflows/analyze-chats.js +0 -250
@@ -0,0 +1,24 @@
1
+ import { follow, send } from "../cli/client.js";
2
+ const allowed = new Set([
3
+ "questions",
4
+ "status",
5
+ "events",
6
+ "decision",
7
+ ]);
8
+ export function relayControl(runtimeDirectory) {
9
+ return (request) => {
10
+ if (!allowed.has(request.operation))
11
+ return Promise.reject(new Error(`relay-operation-refused: The phone relay may not send ${request.operation}.`));
12
+ return send(runtimeDirectory, request);
13
+ };
14
+ }
15
+ export function relayEvents(runtimeDirectory, stream) {
16
+ return follow(runtimeDirectory, {
17
+ request: {
18
+ operation: "events",
19
+ parameters: { after: stream.after, follow: true },
20
+ },
21
+ onEvent: stream.onEvent,
22
+ signal: stream.stopped,
23
+ });
24
+ }
@@ -0,0 +1,2 @@
1
+ export declare function readOptional(path: string): Promise<string | undefined>;
2
+ export declare function writePrivate(path: string, value: unknown): Promise<void>;
@@ -0,0 +1,23 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { readFile, rename, rm, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ export async function readOptional(path) {
5
+ return readFile(path, "utf8").catch((error) => {
6
+ if (Reflect.get(Object(error), "code") === "ENOENT")
7
+ return undefined;
8
+ throw error;
9
+ });
10
+ }
11
+ export async function writePrivate(path, value) {
12
+ const temporary = join(path, "..", `.${randomUUID()}.tmp`);
13
+ try {
14
+ await writeFile(temporary, `${JSON.stringify(value, null, 2)}\n`, {
15
+ mode: 0o600,
16
+ flag: "wx",
17
+ });
18
+ await rename(temporary, path);
19
+ }
20
+ finally {
21
+ await rm(temporary, { force: true });
22
+ }
23
+ }
@@ -0,0 +1,17 @@
1
+ import type { RelayStore } from "./relay-store.ts";
2
+ export interface HeartbeatTiming {
3
+ readonly intervalMilliseconds: number;
4
+ readonly firstBackoffMilliseconds: number;
5
+ readonly longestBackoffMilliseconds: number;
6
+ }
7
+ export declare const heartbeatTiming: HeartbeatTiming;
8
+ export interface HeartbeatContext {
9
+ readonly phoneDirectory: string;
10
+ readonly store: RelayStore;
11
+ readonly lost: () => void;
12
+ readonly timing: HeartbeatTiming;
13
+ readonly wait: (milliseconds: number, signal: AbortSignal) => Promise<void>;
14
+ }
15
+ export type BeatOutcome = "beat" | "failed" | "lost" | "replaced" | "unregistered";
16
+ export declare function heartbeat(context: HeartbeatContext, signal?: AbortSignal): Promise<BeatOutcome>;
17
+ export declare function runHeartbeats(context: HeartbeatContext, signal: AbortSignal): Promise<void>;
@@ -0,0 +1,81 @@
1
+ import { rm } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { relayRequest } from "./relay-http.js";
4
+ import { readLink } from "./relay-registration.js";
5
+ export const heartbeatTiming = {
6
+ intervalMilliseconds: 60_000,
7
+ firstBackoffMilliseconds: 5000,
8
+ longestBackoffMilliseconds: 300_000,
9
+ };
10
+ function revokedDevices(body) {
11
+ const devices = typeof body === "object" && body !== null
12
+ ? Reflect.get(body, "devices")
13
+ : undefined;
14
+ if (!Array.isArray(devices))
15
+ return [];
16
+ return devices.flatMap((device) => {
17
+ const deviceId = Reflect.get(Object(device), "device_id");
18
+ const revokedAt = Reflect.get(Object(device), "revoked_at");
19
+ return typeof deviceId === "string" && typeof revokedAt === "string"
20
+ ? [deviceId]
21
+ : [];
22
+ });
23
+ }
24
+ async function applyRevocations(context, call) {
25
+ const { link, signal } = call;
26
+ if (signal.aborted)
27
+ return;
28
+ const reply = await relayRequest(link.origin, {
29
+ method: "GET",
30
+ path: "/api/v1/relay/devices",
31
+ token: link.token,
32
+ signal,
33
+ });
34
+ if (reply.status !== 200)
35
+ return;
36
+ const known = new Set((await context.store.devices())
37
+ .filter((device) => device.removedAt === null)
38
+ .map((device) => device.deviceId));
39
+ for (const deviceId of revokedDevices(reply.body))
40
+ if (known.has(deviceId))
41
+ await context.store.remove(deviceId);
42
+ }
43
+ async function lostRegistration(context, link) {
44
+ const current = await readLink(context.phoneDirectory);
45
+ if (current?.token !== link.token)
46
+ return "replaced";
47
+ await context.store.removeAll();
48
+ await rm(join(context.phoneDirectory, "link.json"), { force: true });
49
+ context.lost();
50
+ return "lost";
51
+ }
52
+ export async function heartbeat(context, signal = new AbortController().signal) {
53
+ const link = await readLink(context.phoneDirectory);
54
+ if (link === undefined)
55
+ return "unregistered";
56
+ const reply = await relayRequest(link.origin, {
57
+ method: "PUT",
58
+ path: "/api/v1/relay/daemon/heartbeat",
59
+ token: link.token,
60
+ signal,
61
+ }).catch(() => undefined);
62
+ if (reply?.status === 401)
63
+ return lostRegistration(context, link);
64
+ if (reply === undefined || reply.status >= 300)
65
+ return "failed";
66
+ await applyRevocations(context, { link, signal }).catch(() => undefined);
67
+ return "beat";
68
+ }
69
+ function pause(timing, failures) {
70
+ if (failures === 0)
71
+ return timing.intervalMilliseconds;
72
+ return Math.min(timing.firstBackoffMilliseconds * 2 ** (failures - 1), timing.longestBackoffMilliseconds);
73
+ }
74
+ export async function runHeartbeats(context, signal) {
75
+ let failures = 0;
76
+ while (!signal.aborted) {
77
+ const outcome = await heartbeat(context, signal).catch(() => "failed");
78
+ failures = outcome === "failed" ? failures + 1 : 0;
79
+ await context.wait(pause(context.timing, failures), signal);
80
+ }
81
+ }
@@ -0,0 +1,14 @@
1
+ export interface RelayCall {
2
+ readonly method: "GET" | "POST" | "PUT" | "DELETE";
3
+ readonly path: string;
4
+ readonly token?: string;
5
+ readonly body?: unknown;
6
+ readonly timeoutMilliseconds?: number;
7
+ readonly signal?: AbortSignal;
8
+ }
9
+ export interface RelayReply {
10
+ readonly status: number;
11
+ readonly body: unknown;
12
+ }
13
+ export declare function relayOrigin(value: string | undefined): string;
14
+ export declare function relayRequest(origin: string, call: RelayCall): Promise<RelayReply>;
@@ -0,0 +1,63 @@
1
+ import http from "node:http";
2
+ import https from "node:https";
3
+ export function relayOrigin(value) {
4
+ const given = value ?? "https://cloving.ai";
5
+ const url = URL.parse(given);
6
+ const local = url?.protocol === "http:" && url.hostname === "127.0.0.1";
7
+ if (url === null ||
8
+ (url.protocol !== "https:" && !local) ||
9
+ url.origin !== given ||
10
+ given.length > 161)
11
+ throw new Error(`relay-origin-invalid: CLOVING_RELAY_ORIGIN must be an https origin of at most 161 characters, such as https://cloving.ai.`);
12
+ return url.origin;
13
+ }
14
+ function parsed(text) {
15
+ try {
16
+ return JSON.parse(text);
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ }
22
+ function headers(call, payload) {
23
+ return {
24
+ accept: "application/json",
25
+ ...(payload === undefined ? {} : { "content-type": "application/json" }),
26
+ ...(call.token === undefined
27
+ ? {}
28
+ : { authorization: `Bearer ${call.token}` }),
29
+ };
30
+ }
31
+ export function relayRequest(origin, call) {
32
+ const url = new URL(call.path, origin);
33
+ const transport = url.protocol === "https:" ? https : http;
34
+ const payload = call.body === undefined ? undefined : JSON.stringify(call.body);
35
+ return new Promise((settle, reject) => {
36
+ const request = transport.request(url, {
37
+ method: call.method,
38
+ headers: headers(call, payload),
39
+ timeout: call.timeoutMilliseconds ?? 30_000,
40
+ ...(call.signal === undefined ? {} : { signal: call.signal }),
41
+ }, (response) => {
42
+ let text = "";
43
+ response.setEncoding("utf8");
44
+ response.on("data", (chunk) => {
45
+ text += chunk;
46
+ });
47
+ response.once("end", () => {
48
+ settle({ status: response.statusCode ?? 0, body: parsed(text) });
49
+ });
50
+ const incomplete = () => {
51
+ if (!response.complete)
52
+ reject(new Error("relay-response-incomplete: cloving.ai closed the answer before its end."));
53
+ };
54
+ response.once("error", incomplete);
55
+ response.once("close", incomplete);
56
+ });
57
+ request.once("timeout", () => {
58
+ request.destroy(new Error("relay-timeout: cloving.ai did not answer."));
59
+ });
60
+ request.once("error", reject);
61
+ request.end(payload);
62
+ });
63
+ }
@@ -0,0 +1,15 @@
1
+ import { type PhoneLink } from "../phone/phone-link.ts";
2
+ import { type DaemonKeys } from "./daemon-keys.ts";
3
+ export interface RegistrationContext {
4
+ readonly phoneDirectory: string;
5
+ readonly origin: string;
6
+ readonly computerName: string;
7
+ }
8
+ export interface Registration {
9
+ readonly link: PhoneLink;
10
+ readonly keys: DaemonKeys;
11
+ }
12
+ export declare function readLink(phoneDirectory: string): Promise<PhoneLink | undefined>;
13
+ export declare function storedRegistration(phoneDirectory: string): Promise<Registration | undefined>;
14
+ export declare function forgetRegistration(phoneDirectory: string): Promise<void>;
15
+ export declare function registration(context: RegistrationContext): Promise<Registration>;
@@ -0,0 +1,63 @@
1
+ import { rm } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { parsePhoneLink } from "../phone/phone-link.js";
4
+ import { newDaemonKeys, parseDaemonKeys, } from "./daemon-keys.js";
5
+ import { readOptional, writePrivate } from "./relay-files.js";
6
+ import { relayRequest } from "./relay-http.js";
7
+ export function readLink(phoneDirectory) {
8
+ return readOptional(join(phoneDirectory, "link.json")).then(parsePhoneLink);
9
+ }
10
+ function readKeys(phoneDirectory) {
11
+ return readOptional(join(phoneDirectory, "daemon-keys.json")).then(parseDaemonKeys);
12
+ }
13
+ export async function storedRegistration(phoneDirectory) {
14
+ const link = await readLink(phoneDirectory);
15
+ const keys = await readKeys(phoneDirectory);
16
+ if (link === undefined || keys === undefined)
17
+ return undefined;
18
+ return { link, keys };
19
+ }
20
+ export async function forgetRegistration(phoneDirectory) {
21
+ await rm(join(phoneDirectory, "link.json"), { force: true });
22
+ }
23
+ function text(body, name) {
24
+ const value = typeof body === "object" && body !== null
25
+ ? Reflect.get(body, name)
26
+ : undefined;
27
+ if (typeof value !== "string" || value === "")
28
+ throw new Error(`registration-invalid: cloving.ai sent no ${name}.`);
29
+ return value;
30
+ }
31
+ async function registered(context, keys) {
32
+ const reply = await relayRequest(context.origin, {
33
+ method: "POST",
34
+ path: "/api/v1/relay/daemons",
35
+ body: {
36
+ name: context.computerName,
37
+ agreement_public_key: keys.agreementPublicKey,
38
+ signing_public_key: keys.signingPublicKey,
39
+ },
40
+ });
41
+ if (reply.status !== 201)
42
+ throw new Error(`registration-failed: cloving.ai answered ${String(reply.status)}.`);
43
+ const link = {
44
+ daemonId: text(reply.body, "daemon_id"),
45
+ origin: context.origin,
46
+ token: text(reply.body, "token"),
47
+ registeredAt: new Date().toISOString(),
48
+ };
49
+ await writePrivate(join(context.phoneDirectory, "link.json"), link);
50
+ return { link, keys };
51
+ }
52
+ export async function registration(context) {
53
+ const link = await readLink(context.phoneDirectory);
54
+ const stored = await readKeys(context.phoneDirectory);
55
+ if (link !== undefined &&
56
+ stored !== undefined &&
57
+ link.origin === context.origin)
58
+ return { link, keys: stored };
59
+ const keys = stored ?? newDaemonKeys();
60
+ if (stored === undefined)
61
+ await writePrivate(join(context.phoneDirectory, "daemon-keys.json"), keys);
62
+ return registered(context, keys);
63
+ }
@@ -0,0 +1,25 @@
1
+ import { type InboxTiming } from "./answer-inbox.ts";
2
+ import { type WatchTiming } from "./decision-watch.ts";
3
+ import { type HeartbeatTiming } from "./relay-heartbeat.ts";
4
+ export interface RelayPaths {
5
+ readonly runtimeDirectory: string;
6
+ readonly phoneDirectory: string;
7
+ }
8
+ export interface RelayOptions extends RelayPaths {
9
+ readonly origin?: string;
10
+ readonly computerName?: string;
11
+ readonly secret?: () => Buffer;
12
+ readonly heartbeatTiming?: HeartbeatTiming;
13
+ readonly watchTiming?: WatchTiming;
14
+ readonly inboxTiming?: InboxTiming;
15
+ }
16
+ export interface Relay {
17
+ readonly close: () => Promise<void>;
18
+ }
19
+ export declare function computerName(name: string): string;
20
+ export declare function startRelay(options: RelayOptions): Promise<Relay>;
21
+ export interface RelayProgram {
22
+ readonly stopped: Promise<unknown>;
23
+ readonly error: (line: string) => void;
24
+ }
25
+ export declare function runRelay(environment: Readonly<Record<string, string | undefined>>, program: RelayProgram): Promise<number>;
@@ -0,0 +1,216 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { setTimeout as delay } from "node:timers/promises";
3
+ import { mkdir } from "node:fs/promises";
4
+ import { hostname } from "node:os";
5
+ import { dirname, join } from "node:path";
6
+ import { inboxTiming, runInbox } from "./answer-inbox.js";
7
+ import { watchDecisions, watchTiming, } from "./decision-watch.js";
8
+ import { followPairing } from "./pairing-follow.js";
9
+ import { pairingSessions, RegistrationEnded, } from "./pairing-session.js";
10
+ import { pairAccept, pairDeny, phoneRemove, phoneReset, result, } from "./phone-operations.js";
11
+ import { listenPhoneSocket } from "./phone-socket.js";
12
+ import { heartbeatTiming, runHeartbeats, } from "./relay-heartbeat.js";
13
+ import { relayControl } from "./relay-control.js";
14
+ import { relayOrigin } from "./relay-http.js";
15
+ import { forgetRegistration, readLink, registration, } from "./relay-registration.js";
16
+ import { openRelayStore } from "./relay-store.js";
17
+ function unknownOperation(request) {
18
+ return {
19
+ kind: "error",
20
+ error: {
21
+ code: "unknown-operation",
22
+ detail: `The phone relay has no operation ${request.operation}.`,
23
+ },
24
+ };
25
+ }
26
+ function pairingView(open) {
27
+ if (open === undefined)
28
+ return null;
29
+ const request = open.request;
30
+ return {
31
+ pairingId: open.pairingId,
32
+ expiresAt: open.expiresAt,
33
+ stage: open.stage,
34
+ request: request === undefined
35
+ ? null
36
+ : {
37
+ deviceId: request.deviceId,
38
+ name: request.name,
39
+ platform: request.platform,
40
+ code: request.code,
41
+ },
42
+ };
43
+ }
44
+ async function phoneState(relay) {
45
+ const link = await readLink(relay.context.phoneDirectory);
46
+ return result({
47
+ registered: link !== undefined,
48
+ daemonId: link?.daemonId ?? null,
49
+ origin: link?.origin ?? null,
50
+ pairing: pairingView(relay.sessions.current()),
51
+ refusal: relay.sessions.refusal(),
52
+ reason: relay.registration.lost ? "registration-lost" : null,
53
+ devices: (await relay.store.devices()).map((device) => ({
54
+ deviceId: device.deviceId,
55
+ name: device.name,
56
+ platform: device.platform,
57
+ pairedAt: device.pairedAt,
58
+ state: device.state,
59
+ removedAt: device.removedAt,
60
+ })),
61
+ });
62
+ }
63
+ async function opened(relay) {
64
+ const startedIn = relay.sessions.generation();
65
+ try {
66
+ return await relay.sessions.start(await registration(relay.context), startedIn);
67
+ }
68
+ catch (error) {
69
+ if (!(error instanceof RegistrationEnded))
70
+ throw error;
71
+ await relay.store.removeAll();
72
+ await forgetRegistration(relay.context.phoneDirectory);
73
+ return relay.sessions.start(await registration(relay.context), startedIn);
74
+ }
75
+ }
76
+ const starts = new WeakMap();
77
+ function sharedStart(relay) {
78
+ const running = starts.get(relay);
79
+ if (running !== undefined)
80
+ return running;
81
+ const starting = opened(relay).finally(() => starts.delete(relay));
82
+ starts.set(relay, starting);
83
+ return starting;
84
+ }
85
+ async function pairStart(relay) {
86
+ const open = relay.sessions.current() ?? (await sharedStart(relay));
87
+ relay.registration.lost = false;
88
+ return result({ link: open.link, expiresAt: open.expiresAt });
89
+ }
90
+ function pairCancel(relay) {
91
+ relay.sessions.cancel();
92
+ return Promise.resolve(result({ pairing: null }));
93
+ }
94
+ async function resetAfterStart(relay) {
95
+ relay.sessions.cancel();
96
+ await starts.get(relay)?.catch(() => undefined);
97
+ return phoneReset(relay);
98
+ }
99
+ const operations = {
100
+ "phone-state": phoneState,
101
+ "phone-pair-start": pairStart,
102
+ "phone-pair-cancel": pairCancel,
103
+ "phone-pair-accept": pairAccept,
104
+ "phone-pair-deny": pairDeny,
105
+ "phone-remove": phoneRemove,
106
+ "phone-reset": resetAfterStart,
107
+ };
108
+ export function computerName(name) {
109
+ return name.replace(/\.local$/, "").slice(0, 64);
110
+ }
111
+ function deliveringStore(options, opened) {
112
+ const stop = new AbortController();
113
+ const reading = new AbortController();
114
+ const running = opened.run(stop.signal);
115
+ const context = {
116
+ runtimeDirectory: options.runtimeDirectory,
117
+ phoneDirectory: options.phoneDirectory,
118
+ store: opened,
119
+ control: relayControl(options.runtimeDirectory),
120
+ now: () => Date.now(),
121
+ };
122
+ const watch = watchDecisions(context, options.watchTiming ?? watchTiming);
123
+ const answers = runInbox(context, {
124
+ signal: reading.signal,
125
+ timing: options.inboxTiming ?? inboxTiming,
126
+ });
127
+ return {
128
+ store: {
129
+ ...opened,
130
+ connect: async (deviceId) => {
131
+ const found = await opened.connect(deviceId);
132
+ watch.wake();
133
+ return found;
134
+ },
135
+ },
136
+ close: async () => {
137
+ reading.abort();
138
+ await answers;
139
+ await watch.close();
140
+ stop.abort();
141
+ await running;
142
+ },
143
+ };
144
+ }
145
+ export async function startRelay(options) {
146
+ await mkdir(options.runtimeDirectory, { recursive: true, mode: 0o700 });
147
+ await mkdir(options.phoneDirectory, { recursive: true, mode: 0o700 });
148
+ const opened = await openRelayStore(options.phoneDirectory);
149
+ const delivering = deliveringStore(options, opened);
150
+ const { store } = delivering;
151
+ const follower = {
152
+ follow: () => Promise.resolve(),
153
+ };
154
+ const relay = {
155
+ context: {
156
+ phoneDirectory: options.phoneDirectory,
157
+ origin: options.origin ?? relayOrigin(undefined),
158
+ computerName: options.computerName ?? computerName(hostname()),
159
+ },
160
+ store,
161
+ sessions: pairingSessions({
162
+ secret: options.secret ?? (() => randomBytes(16)),
163
+ follow: (open) => follower.follow(open),
164
+ }),
165
+ registration: { lost: false },
166
+ };
167
+ follower.follow = (open) => followPairing(relay, open);
168
+ const socket = await listenPhoneSocket(join(options.runtimeDirectory, "phone.sock"), (request) => {
169
+ const operation = operations[request.operation];
170
+ return operation === undefined
171
+ ? Promise.resolve(unknownOperation(request))
172
+ : operation(relay, request.parameters);
173
+ });
174
+ const stop = new AbortController();
175
+ const beating = runHeartbeats({
176
+ phoneDirectory: options.phoneDirectory,
177
+ store,
178
+ timing: options.heartbeatTiming ?? heartbeatTiming,
179
+ lost: () => {
180
+ relay.registration.lost = true;
181
+ relay.sessions.cancel();
182
+ },
183
+ wait: (milliseconds, signal) => delay(milliseconds, undefined, { signal }).catch(() => undefined),
184
+ }, stop.signal);
185
+ return {
186
+ close: async () => {
187
+ stop.abort();
188
+ relay.sessions.cancel();
189
+ await beating;
190
+ await socket.close();
191
+ await delivering.close();
192
+ await opened.close();
193
+ },
194
+ };
195
+ }
196
+ export async function runRelay(environment, program) {
197
+ const runtimeDirectory = environment["CLOVING_RUNTIME_DIR"];
198
+ if (runtimeDirectory === undefined || runtimeDirectory === "") {
199
+ program.error("runtime-directory-unknown: The phone relay needs CLOVING_RUNTIME_DIR.\n");
200
+ return 1;
201
+ }
202
+ try {
203
+ const relay = await startRelay({
204
+ runtimeDirectory,
205
+ phoneDirectory: join(dirname(runtimeDirectory), "state", "phone"),
206
+ origin: relayOrigin(environment["CLOVING_RELAY_ORIGIN"]),
207
+ });
208
+ await program.stopped;
209
+ await relay.close();
210
+ return 0;
211
+ }
212
+ catch (error) {
213
+ program.error(`phone-relay-failed: ${String(Reflect.get(Object(error), "message"))}\n`);
214
+ return 1;
215
+ }
216
+ }
@@ -0,0 +1,48 @@
1
+ import { type DeviceDelivery, type DeviceEnvelopes, type PostedEnvelope } from "./phone-delivery.ts";
2
+ import { type PhoneDevice } from "./phone-devices.ts";
3
+ import { type InboxWork } from "./phone-inbox.ts";
4
+ import { type OpenDecision } from "./phone-outbox.ts";
5
+ export interface DeliveryOffer {
6
+ readonly runId: string;
7
+ readonly promptDigest: string;
8
+ readonly deviceId: string;
9
+ readonly envelopes: readonly PostedEnvelope[];
10
+ }
11
+ export interface DeliveryClosing {
12
+ readonly devices: readonly DeviceEnvelopes[];
13
+ readonly at: number;
14
+ }
15
+ export interface RelayStore {
16
+ readonly devices: () => Promise<readonly PhoneDevice[]>;
17
+ readonly add: (device: PhoneDevice) => Promise<string | undefined>;
18
+ readonly connect: (deviceId: string) => Promise<boolean>;
19
+ readonly nextSequence: (deviceId: string) => Promise<number>;
20
+ readonly remove: (deviceId: string) => Promise<boolean>;
21
+ readonly removeAll: () => Promise<number>;
22
+ readonly delivery: RelayDeliveries;
23
+ readonly inbox: RelayInbox;
24
+ readonly run: (signal: AbortSignal) => Promise<void>;
25
+ readonly close: () => Promise<void>;
26
+ }
27
+ export interface RelayDeliveries {
28
+ readonly offer: (decisionId: string, offer: DeliveryOffer) => Promise<boolean>;
29
+ readonly close: (decisionId: string, closing: DeliveryClosing) => Promise<boolean>;
30
+ readonly open: () => Promise<readonly OpenDecision[]>;
31
+ readonly list: (decisionId: string) => Promise<readonly DeviceDelivery[]>;
32
+ readonly promptDigest: (decisionId: string) => Promise<string | null>;
33
+ readonly sweep: () => Promise<number>;
34
+ readonly drainAt: (now: Date) => Promise<void>;
35
+ }
36
+ export interface RelayInbox {
37
+ readonly cursor: () => Promise<number>;
38
+ readonly work: (answerId: string) => Promise<InboxWork | undefined>;
39
+ readonly record: (work: InboxWork) => Promise<InboxWork>;
40
+ readonly complete: (done: {
41
+ readonly answerId: string;
42
+ readonly sequence: number;
43
+ }) => Promise<void>;
44
+ }
45
+ export interface RelayStoreOptions {
46
+ readonly now?: () => number;
47
+ }
48
+ export declare function openRelayStore(phoneDirectory: string, options?: RelayStoreOptions): Promise<RelayStore>;