@jr2/orchestrator 0.4.1 → 0.5.1

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 (327) hide show
  1. package/README.md +20 -2
  2. package/console/components/app.ts +2 -0
  3. package/console/components/fleet.ts +14 -1
  4. package/console/components/machine-pane.ts +41 -2
  5. package/console/main.ts +13 -1
  6. package/console/store.ts +102 -6
  7. package/console/style.css +21 -0
  8. package/docs/agents-block.md +5 -0
  9. package/docs/concepts/agents-and-turns.md +442 -0
  10. package/docs/concepts/architecture.md +230 -0
  11. package/docs/concepts/charts.md +143 -0
  12. package/docs/concepts/composition.md +357 -0
  13. package/docs/concepts/converge.md +254 -0
  14. package/docs/concepts/durable-runs.md +189 -0
  15. package/docs/concepts/effects.md +603 -0
  16. package/docs/concepts/gates.md +208 -0
  17. package/docs/concepts/machines-and-workflows.md +289 -0
  18. package/docs/concepts/pools-and-sources.md +230 -0
  19. package/docs/concepts/secrets-and-trust.md +222 -0
  20. package/docs/concepts/the-menu.md +378 -0
  21. package/docs/concepts/workspaces.md +678 -0
  22. package/docs/get-started/a-model-decides.md +563 -0
  23. package/docs/get-started/an-agent-in-a-sandbox.md +381 -0
  24. package/docs/get-started/connect-a-model.md +248 -0
  25. package/docs/get-started/create-an-instance.md +194 -0
  26. package/docs/get-started/have-your-assistant-write-a-machine.md +922 -0
  27. package/docs/get-started/install.md +126 -0
  28. package/docs/guides/add-tools-to-the-sandbox.md +295 -0
  29. package/docs/guides/answer-a-gate.md +304 -0
  30. package/docs/guides/bind-open-parts.md +223 -0
  31. package/docs/guides/cancel-a-run.md +146 -0
  32. package/docs/guides/clean-up.md +206 -0
  33. package/docs/guides/fix-a-failed-converge.md +361 -0
  34. package/docs/guides/give-the-cluster-git-credentials.md +222 -0
  35. package/docs/guides/iterate-on-a-machine-on-kind.md +190 -0
  36. package/docs/guides/let-an-agent-write-a-chart.md +226 -0
  37. package/docs/guides/look-inside-a-sandbox.md +174 -0
  38. package/docs/guides/mirror-the-kit-images.md +176 -0
  39. package/docs/guides/move-from-kind-to-a-shared-cluster.md +302 -0
  40. package/docs/guides/open-the-console.md +91 -0
  41. package/docs/guides/publish-a-machine-as-an-npm-package.md +350 -0
  42. package/docs/guides/push-and-sign-a-branch.md +486 -0
  43. package/docs/guides/run-a-chart-by-name.md +270 -0
  44. package/docs/guides/run-a-command-or-call-a-url.md +544 -0
  45. package/docs/guides/run-one-worker-per-item.md +321 -0
  46. package/docs/guides/set-an-agents-access-and-dials.md +375 -0
  47. package/docs/guides/size-a-workspace.md +204 -0
  48. package/docs/guides/start-a-run-from-a-run.md +237 -0
  49. package/docs/guides/start-a-run.md +207 -0
  50. package/docs/guides/upgrade-the-kit-version.md +240 -0
  51. package/docs/guides/use-a-hosted-provider.md +208 -0
  52. package/docs/guides/use-a-local-model.md +313 -0
  53. package/docs/guides/watch-a-run.md +326 -0
  54. package/docs/guides/write-a-workflow.md +318 -0
  55. package/docs/index.md +44 -0
  56. package/docs/llms.txt +216 -0
  57. package/docs/reference/authoring-surface/agent.md +128 -0
  58. package/docs/reference/authoring-surface/answer.md +140 -0
  59. package/docs/reference/authoring-surface/arg.md +60 -0
  60. package/docs/reference/authoring-surface/basic.md +62 -0
  61. package/docs/reference/authoring-surface/bearer.md +53 -0
  62. package/docs/reference/authoring-surface/chartschema.md +23 -0
  63. package/docs/reference/authoring-surface/chartschemafor.md +48 -0
  64. package/docs/reference/authoring-surface/command.md +191 -0
  65. package/docs/reference/authoring-surface/customize.md +64 -0
  66. package/docs/reference/authoring-surface/defineconfig.md +41 -0
  67. package/docs/reference/authoring-surface/defineevent.md +114 -0
  68. package/docs/reference/authoring-surface/dispatch.md +126 -0
  69. package/docs/reference/authoring-surface/fail.md +83 -0
  70. package/docs/reference/authoring-surface/file.md +122 -0
  71. package/docs/reference/authoring-surface/frompick.md +277 -0
  72. package/docs/reference/authoring-surface/http.md +159 -0
  73. package/docs/reference/authoring-surface/isfailure.md +58 -0
  74. package/docs/reference/authoring-surface/jr2setup.md +237 -0
  75. package/docs/reference/authoring-surface/open.md +74 -0
  76. package/docs/reference/authoring-surface/openbody.md +86 -0
  77. package/docs/reference/authoring-surface/paramsof.md +125 -0
  78. package/docs/reference/authoring-surface/pool.md +186 -0
  79. package/docs/reference/authoring-surface/push.md +112 -0
  80. package/docs/reference/authoring-surface/secret.md +118 -0
  81. package/docs/reference/authoring-surface/sentby.md +83 -0
  82. package/docs/reference/authoring-surface/source.md +51 -0
  83. package/docs/reference/authoring-surface/text.md +62 -0
  84. package/docs/reference/authoring-surface/type-agentdeclaration.md +27 -0
  85. package/docs/reference/authoring-surface/type-agentdefinition.md +80 -0
  86. package/docs/reference/authoring-surface/type-agentlogic.md +15 -0
  87. package/docs/reference/authoring-surface/type-agentslots.md +12 -0
  88. package/docs/reference/authoring-surface/type-answeraction.md +12 -0
  89. package/docs/reference/authoring-surface/type-arg.md +19 -0
  90. package/docs/reference/authoring-surface/type-argnames.md +12 -0
  91. package/docs/reference/authoring-surface/type-binding.md +62 -0
  92. package/docs/reference/authoring-surface/type-bindingof.md +12 -0
  93. package/docs/reference/authoring-surface/type-bodyacceptsdoor.md +15 -0
  94. package/docs/reference/authoring-surface/type-bodyacceptsslots.md +17 -0
  95. package/docs/reference/authoring-surface/type-ceilings.md +41 -0
  96. package/docs/reference/authoring-surface/type-chainlink.md +19 -0
  97. package/docs/reference/authoring-surface/type-chartaction.md +46 -0
  98. package/docs/reference/authoring-surface/type-chartagent.md +28 -0
  99. package/docs/reference/authoring-surface/type-chartevent.md +20 -0
  100. package/docs/reference/authoring-surface/type-chartguard.md +20 -0
  101. package/docs/reference/authoring-surface/type-chartinvoke.md +28 -0
  102. package/docs/reference/authoring-surface/type-chartjson.md +14 -0
  103. package/docs/reference/authoring-surface/type-chartstate.md +46 -0
  104. package/docs/reference/authoring-surface/type-charttext.md +40 -0
  105. package/docs/reference/authoring-surface/type-charttransition.md +22 -0
  106. package/docs/reference/authoring-surface/type-charttransitions.md +14 -0
  107. package/docs/reference/authoring-surface/type-chartvalue.md +15 -0
  108. package/docs/reference/authoring-surface/type-childat.md +12 -0
  109. package/docs/reference/authoring-surface/type-childslots.md +12 -0
  110. package/docs/reference/authoring-surface/type-commandenv.md +15 -0
  111. package/docs/reference/authoring-surface/type-commandinput.md +19 -0
  112. package/docs/reference/authoring-surface/type-commandoptions.md +78 -0
  113. package/docs/reference/authoring-surface/type-commandoutput.md +104 -0
  114. package/docs/reference/authoring-surface/type-customize.md +109 -0
  115. package/docs/reference/authoring-surface/type-dispatchdefinition.md +39 -0
  116. package/docs/reference/authoring-surface/type-dispatchinput.md +14 -0
  117. package/docs/reference/authoring-surface/type-dispatchopenparts.md +14 -0
  118. package/docs/reference/authoring-surface/type-dispatchoutput.md +65 -0
  119. package/docs/reference/authoring-surface/type-duration.md +15 -0
  120. package/docs/reference/authoring-surface/type-effectbinding.md +93 -0
  121. package/docs/reference/authoring-surface/type-effectlogic.md +12 -0
  122. package/docs/reference/authoring-surface/type-envopenparts.md +14 -0
  123. package/docs/reference/authoring-surface/type-eventaudience.md +27 -0
  124. package/docs/reference/authoring-surface/type-eventdef.md +45 -0
  125. package/docs/reference/authoring-surface/type-eventfrom.md +16 -0
  126. package/docs/reference/authoring-surface/type-eventsemantics.md +26 -0
  127. package/docs/reference/authoring-surface/type-execoutcome.md +34 -0
  128. package/docs/reference/authoring-surface/type-failslot.md +14 -0
  129. package/docs/reference/authoring-surface/type-failure.md +19 -0
  130. package/docs/reference/authoring-surface/type-failureinput.md +20 -0
  131. package/docs/reference/authoring-surface/type-filebody.md +41 -0
  132. package/docs/reference/authoring-surface/type-frominput.md +14 -0
  133. package/docs/reference/authoring-surface/type-hostinjectedinput.md +58 -0
  134. package/docs/reference/authoring-surface/type-httpdefinition.md +101 -0
  135. package/docs/reference/authoring-surface/type-httpinput.md +12 -0
  136. package/docs/reference/authoring-surface/type-httpmethod.md +14 -0
  137. package/docs/reference/authoring-surface/type-httpopenparts.md +12 -0
  138. package/docs/reference/authoring-surface/type-httpoutput.md +89 -0
  139. package/docs/reference/authoring-surface/type-httprequestparts.md +33 -0
  140. package/docs/reference/authoring-surface/type-jr2config.md +126 -0
  141. package/docs/reference/authoring-surface/type-lends.md +38 -0
  142. package/docs/reference/authoring-surface/type-logicat.md +12 -0
  143. package/docs/reference/authoring-surface/type-markedif.md +12 -0
  144. package/docs/reference/authoring-surface/type-mergedactors.md +12 -0
  145. package/docs/reference/authoring-surface/type-namedsecret.md +25 -0
  146. package/docs/reference/authoring-surface/type-noslotspec.md +26 -0
  147. package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +32 -0
  148. package/docs/reference/authoring-surface/type-openbodydeclaration.md +40 -0
  149. package/docs/reference/authoring-surface/type-openbodylogic.md +14 -0
  150. package/docs/reference/authoring-surface/type-openbodyoutput.md +42 -0
  151. package/docs/reference/authoring-surface/type-openbodyslot.md +18 -0
  152. package/docs/reference/authoring-surface/type-opencommandinput.md +17 -0
  153. package/docs/reference/authoring-surface/type-openeffect.md +12 -0
  154. package/docs/reference/authoring-surface/type-openeffectslots.md +12 -0
  155. package/docs/reference/authoring-surface/type-openof.md +12 -0
  156. package/docs/reference/authoring-surface/type-opensecret.md +23 -0
  157. package/docs/reference/authoring-surface/type-opensecretmark.md +12 -0
  158. package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +31 -0
  159. package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +32 -0
  160. package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
  161. package/docs/reference/authoring-surface/type-poolspec.md +60 -0
  162. package/docs/reference/authoring-surface/type-pushinput.md +15 -0
  163. package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
  164. package/docs/reference/authoring-surface/type-pushoutput.md +124 -0
  165. package/docs/reference/authoring-surface/type-pushwireoutput.md +12 -0
  166. package/docs/reference/authoring-surface/type-quantity.md +14 -0
  167. package/docs/reference/authoring-surface/type-reached.md +12 -0
  168. package/docs/reference/authoring-surface/type-reposlot.md +70 -0
  169. package/docs/reference/authoring-surface/type-reposlotsof.md +16 -0
  170. package/docs/reference/authoring-surface/type-sandboxoptions.md +77 -0
  171. package/docs/reference/authoring-surface/type-secret.md +25 -0
  172. package/docs/reference/authoring-surface/type-sender.md +19 -0
  173. package/docs/reference/authoring-surface/type-sentbyguards.md +12 -0
  174. package/docs/reference/authoring-surface/type-size.md +85 -0
  175. package/docs/reference/authoring-surface/type-slothandles.md +32 -0
  176. package/docs/reference/authoring-surface/type-sourcenextresult.md +17 -0
  177. package/docs/reference/authoring-surface/type-sourcespec.md +44 -0
  178. package/docs/reference/authoring-surface/type-syncstate.md +12 -0
  179. package/docs/reference/authoring-surface/type-text.md +19 -0
  180. package/docs/reference/authoring-surface/type-textpart.md +24 -0
  181. package/docs/reference/authoring-surface/type-thinkinglevel.md +39 -0
  182. package/docs/reference/authoring-surface/type-toparameterizedobject.md +14 -0
  183. package/docs/reference/authoring-surface/type-toprovidedactor.md +14 -0
  184. package/docs/reference/authoring-surface/type-unknown.md +21 -0
  185. package/docs/reference/authoring-surface/type-usercontainer.md +77 -0
  186. package/docs/reference/authoring-surface/type-withoutemptyindex.md +20 -0
  187. package/docs/reference/authoring-surface/type-workflowevent.md +46 -0
  188. package/docs/reference/authoring-surface/type-workspaceaccess.md +16 -0
  189. package/docs/reference/authoring-surface/type-workspaced.md +94 -0
  190. package/docs/reference/authoring-surface/type-workspacehandles.md +23 -0
  191. package/docs/reference/authoring-surface/type-workspacemachine.md +29 -0
  192. package/docs/reference/authoring-surface/type-workspaceof.md +13 -0
  193. package/docs/reference/authoring-surface/type-workspaceoptions.md +39 -0
  194. package/docs/reference/authoring-surface/type-workspacespec.md +37 -0
  195. package/docs/reference/authoring-surface/workspace.md +259 -0
  196. package/docs/reference/authoring-surface.md +197 -0
  197. package/docs/reference/cli.md +433 -0
  198. package/docs/reference/config.md +756 -0
  199. package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +113 -0
  200. package/docs/reference/decisions/0002-duplex-streaming-actor.md +110 -0
  201. package/docs/reference/decisions/0004-worktree-provisioning-model.md +124 -0
  202. package/docs/reference/decisions/0005-sandbox-pod-composition.md +230 -0
  203. package/docs/reference/decisions/0006-agent-control-surface.md +47 -0
  204. package/docs/reference/decisions/0007-durable-machine-state.md +109 -0
  205. package/docs/reference/decisions/0008-orchestrator-as-deployed-app.md +60 -0
  206. package/docs/reference/decisions/0009-cli-and-instance-interface.md +275 -0
  207. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +160 -0
  208. package/docs/reference/decisions/0011-workflow-defined-events.md +141 -0
  209. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +103 -0
  210. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +157 -0
  211. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +95 -0
  212. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +265 -0
  213. package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +124 -0
  214. package/docs/reference/decisions/0017-pool-over-a-source-port.md +69 -0
  215. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +111 -0
  216. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +275 -0
  217. package/docs/reference/decisions/0020-private-ca-trust-is-a-named-concept.md +60 -0
  218. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +100 -0
  219. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +103 -0
  220. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +176 -0
  221. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +439 -0
  222. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +65 -0
  223. package/docs/reference/decisions/0026-a-turn-that-is-over-is-never-prompted.md +62 -0
  224. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +237 -0
  225. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +89 -0
  226. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +154 -0
  227. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +100 -0
  228. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +190 -0
  229. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +61 -0
  230. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +127 -0
  231. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +57 -0
  232. package/docs/reference/decisions/0035-a-runaway-turn-is-ended-rerolled-once-then-a-fault.md +92 -0
  233. package/docs/reference/decisions/0036-a-turn-compacts-its-own-context-mid-flight.md +138 -0
  234. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +196 -0
  235. package/docs/reference/decisions/0038-jr2-up-builds-every-image-it-deploys.md +170 -0
  236. package/docs/reference/decisions/0039-image-garbage-collects-by-reachability.md +115 -0
  237. package/docs/reference/decisions/0041-a-build-the-host-already-holds-is-not-spent-again.md +64 -0
  238. package/docs/reference/decisions/0042-ready-is-not-routable-an-admission-retries-its-connection.md +142 -0
  239. package/docs/reference/decisions/0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md +170 -0
  240. package/docs/reference/decisions/0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md +85 -0
  241. package/docs/reference/decisions/0045-the-platform-joins-the-image-address-and-the-cluster-chooses-it.md +82 -0
  242. package/docs/reference/decisions/0046-a-converge-failure-carries-its-diagnosis.md +34 -0
  243. package/docs/reference/decisions/0047-the-git-ssh-key-source-is-the-users-choice.md +47 -0
  244. package/docs/reference/decisions/0048-the-orchestrator-boots-without-its-repos.md +36 -0
  245. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +126 -0
  246. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +73 -0
  247. package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +227 -0
  248. package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +81 -0
  249. package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +111 -0
  250. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +126 -0
  251. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +107 -0
  252. package/docs/reference/decisions/0056-a-global-jr2-hands-off-to-the-instances-own-and-an-instance-has-one-kit-version.md +62 -0
  253. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +152 -0
  254. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +106 -0
  255. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +384 -0
  256. package/docs/reference/decisions/0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md +111 -0
  257. package/docs/reference/decisions/0061-a-memory-kill-ends-the-agents-processes-not-the-harness.md +83 -0
  258. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +212 -0
  259. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +83 -0
  260. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +79 -0
  261. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +65 -0
  262. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +550 -0
  263. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +430 -0
  264. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +464 -0
  265. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +180 -0
  266. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +323 -0
  267. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +183 -0
  268. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +144 -0
  269. package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +122 -0
  270. package/docs/reference/decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md +76 -0
  271. package/docs/reference/decisions.md +83 -0
  272. package/docs/reference/glossary.md +648 -0
  273. package/docs/reference/http-api.md +1348 -0
  274. package/docs/reference/machines.md +190 -0
  275. package/docs/reference/mechanism-events.md +72 -0
  276. package/docs/why.md +206 -0
  277. package/package.json +5 -3
  278. package/src/actor.ts +541 -79
  279. package/src/agent.ts +114 -38
  280. package/src/ambient.ts +2 -1
  281. package/src/assembly-checks.ts +189 -7
  282. package/src/cause.ts +234 -0
  283. package/src/chart.ts +169 -37
  284. package/src/charts.ts +34 -6
  285. package/src/config.ts +430 -178
  286. package/src/custodian.ts +74 -22
  287. package/src/customize.ts +188 -46
  288. package/src/dispatch-effect.ts +945 -0
  289. package/src/door-compat.ts +217 -0
  290. package/src/durability.ts +11 -4
  291. package/src/effect.ts +341 -44
  292. package/src/effector-wire.ts +160 -11
  293. package/src/fail.ts +209 -0
  294. package/src/harness-client.ts +541 -47
  295. package/src/http-effect.ts +495 -53
  296. package/src/http.ts +427 -16
  297. package/src/index.ts +60 -82
  298. package/src/instance.ts +68 -8
  299. package/src/internal.ts +84 -0
  300. package/src/machine-doc.ts +21 -3
  301. package/src/mechanism-routes.ts +168 -0
  302. package/src/model-keys.generated.ts +53 -0
  303. package/src/model-keys.ts +18 -58
  304. package/src/notices.ts +66 -13
  305. package/src/open-body.ts +159 -23
  306. package/src/open-effect.ts +81 -8
  307. package/src/open.ts +59 -6
  308. package/src/parts.ts +184 -13
  309. package/src/party.ts +68 -2
  310. package/src/pick.ts +463 -11
  311. package/src/pool.ts +247 -36
  312. package/src/registration.ts +83 -4
  313. package/src/repos.ts +2 -1
  314. package/src/run-host.ts +565 -31
  315. package/src/run-output.ts +38 -0
  316. package/src/sandbox-effect.ts +273 -38
  317. package/src/sandbox-kube.ts +3 -1
  318. package/src/secret-placement.ts +52 -17
  319. package/src/server.ts +3 -0
  320. package/src/setup.ts +239 -56
  321. package/src/size.ts +121 -9
  322. package/src/snapshot-store.ts +54 -10
  323. package/src/stub-harness.ts +44 -16
  324. package/src/timers.ts +14 -1
  325. package/src/vocabulary.ts +69 -14
  326. package/src/wire.ts +90 -16
  327. package/src/workspace.ts +697 -175
@@ -83,20 +83,33 @@ import { httpFileWith, sandboxEffectorPort } from "./sandbox-effect.ts";
83
83
 
84
84
  // --- Naming a secret -----------------------------------------------------------------------------
85
85
 
86
- /** A secret an Effect names, by its name in the config's `secrets` list (ADR-0068). Naming it in an
87
- * Effect is what places it in that Effect's Effector: an Effect secret. Its value never leaves the
88
- * Effector; on the wire it is a name. */
86
+ // Naming it in an Effect is what places it in that Effect's Effector (ADR-0068). On the wire it is
87
+ // a name.
88
+ /** What `secret("name")` gives: an Effect secret, by its name in the `secrets` list of
89
+ * `jr2.config.ts`. Put it in an Effect's `auth`, a header, a `text` or a command's `env`. Its
90
+ * `.path` is the path of a file that holds the value. The value stays in the Effector, so the
91
+ * Machine and the Agent never see it. */
89
92
  export type Secret = { readonly secret: string; readonly path: { readonly secret: string; readonly path: true } };
90
93
 
91
- /** A string the Effector builds: literal text and secrets, in order — the `text` template's value. */
94
+ /** What the `text` template gives: a string of literal text and Effect secrets, in order. The
95
+ * Effector builds the string, so the Machine never sees the value of a secret. */
92
96
  export type Text = { readonly text: readonly TextPart[] };
93
97
 
94
98
  declare const openSecretMark: unique symbol;
95
99
  /** The TYPE half of an Open secret (ADR-0054): whatever carries `secret(open)` — the secret, its
96
100
  * `.path`, a `text` or a `basic` pair holding it, a `bearer` of it — carries this mark, so the
97
- * Effect's constructor states the Open part "secret" and `customize()` offers `{ secret }`. Phantom. */
101
+ * Effect's constructor states the Open part "secret" and `customize()` offers `{ secret }`.
102
+ * Phantom.
103
+ *
104
+ * @internal
105
+ */
98
106
  export type OpenSecretMark = { readonly [openSecretMark]: true };
99
107
 
108
+ /** A named secret, never `secret(open)`: where a definition's Open part is a whole that holds a
109
+ * secret — a dispatch's target (ADR-0071) — the secret is bound with it, to a name, so the mark
110
+ * `secret(open)` carries may not be present. One type for the definition and the binding. */
111
+ export type NamedSecret = Secret & { readonly [K in keyof OpenSecretMark]?: never };
112
+
100
113
  /** `secret(open)`: a secret a packaged Machine names in its auth, a header or a command's env, and
101
114
  * leaves for its composer to name (ADR-0054). */
102
115
  export type OpenSecret = {
@@ -116,9 +129,97 @@ const SECRET_NAME = /^[A-Za-z0-9._-]{1,253}$/;
116
129
  * (a value and its `.path`) is told from one that names two. */
117
130
  const openSecretOf = new WeakMap<object, object>();
118
131
 
119
- /** Name a secret: `secret("jira")`. `secret(open)` leaves the name to the composer (ADR-0054). */
120
- export function secret(name: typeof open): OpenSecret;
132
+ /**
133
+ * Name an Effect secret: `secret("jira")`. The name is the `name` of an entry in the `secrets`
134
+ * list of `jr2.config.ts`. The Effector reads the value and puts it into the call, or into the env
135
+ * of a `command`. The Agent never reads it, and the Orchestrator never holds it.
136
+ *
137
+ * Use it in `bearer`, `basic` or `text`, or in the `env` of a `command`. In an `env`,
138
+ * `secret("x")` gives the value, and `secret("x").path` gives the path of a file that holds the
139
+ * value.
140
+ *
141
+ * `secret(open)` leaves the name to the composer of a packaged Machine, who binds it one time:
142
+ * `customize(machine, { effects: { <slot>: { secret: "<name>" } } })`. An Effect can have one Open
143
+ * secret, in its `auth`, a header or its `env`. A url cannot have one: leave the whole url Open
144
+ * (`url: open`) instead.
145
+ *
146
+ * It refuses, when you call it:
147
+ * - a name that is not letters, digits, `.`, `_` and `-`, that starts with `.`, or that is longer
148
+ * than 253 characters: `secret("<name>"): a secret's name is letters, digits, ".", "_" and "-",
149
+ * and does not start with "." — it is the file the Effector reads it from (ADR-0068)`
150
+ * - an Effect that names `secret(open)` in two places that are two secrets: `an Effect takes one
151
+ * Open secret — it names secret(open) in two places that are two secrets; name one secret(open)
152
+ * and use it in each place, or name the others with secret("<name>") (ADR-0054)`
153
+ * - `secret(open)` in a url: ``secret(open) in a url: a url's secret is not an Open part — leave
154
+ * the whole url Open (`url: open`) and let the composer bind it with its secret in its path
155
+ * (ADR-0054)``
156
+ *
157
+ * And at `jr2 up`:
158
+ * - a name that the `secrets` list does not have: ``workflow "<workflow>": the <kind> Effect
159
+ * "<slot>" names secret("<name>"), which the `secrets` list does not hold — add it: secrets: [{
160
+ * name: "<name>", value: process.env.<NAME> }] (ADR-0068)``
161
+ * - a value of fewer than 8 characters: `secrets[<i>] (<name>) is an Effect secret shorter than 8
162
+ * characters — the Effector replaces every match of a secret in what it returns, and a value
163
+ * this short would match ordinary text. Use the whole credential; the value itself is not shown
164
+ * (ADR-0068)`
165
+ *
166
+ * @param name - The name of an entry in the `secrets` list.
167
+ * @returns The Effect secret, for an Effect. Its `.path` names the file of the secret, for the
168
+ * `env` of a `command`.
169
+ *
170
+ * @example
171
+ * ```ts
172
+ * import { command, fail, jr2Setup, secret, workspace, type Workspaced } from "@jr2/orchestrator";
173
+ *
174
+ * type Input = Workspaced<{}, "app">;
175
+ *
176
+ * const body = jr2Setup({
177
+ * types: {} as { context: Input; input: Input },
178
+ * actors: {
179
+ * // The command reads the file at $KUBECONFIG. The Agent never reads the secret.
180
+ * deploy: command(["kubectl", "apply", "-f", "k8s/"], {
181
+ * env: { KUBECONFIG: secret("kube").path },
182
+ * }),
183
+ * fail: fail(),
184
+ * },
185
+ * }).createMachine({
186
+ * id: "deploy",
187
+ * context: ({ input }) => input,
188
+ * on: { "workspace.lost": ".lost" },
189
+ * initial: "deploying",
190
+ * states: {
191
+ * deploying: { invoke: { src: "deploy", onDone: "done" } },
192
+ * done: { type: "final" },
193
+ * lost: { invoke: { src: "fail", input: { reason: "the Sandbox is gone" } } },
194
+ * },
195
+ * });
196
+ *
197
+ * export const machine = workspace(body, {
198
+ * repos: { app: { url: "https://github.com/you/app.git" } },
199
+ * spec: () => ({ branch: "deploy" }),
200
+ * });
201
+ * ```
202
+ *
203
+ * @see docs/concepts/secrets-and-trust.md
204
+ * @see docs/guides/run-a-command-or-call-a-url.md
205
+ * @see docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md
206
+ * @see docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md
207
+ */
121
208
  export function secret(name: string): Secret;
209
+ /**
210
+ * Leave an Effect secret Open: `secret(open)`, in a Machine that you publish as a package. The
211
+ * composer binds its name one time: `customize(machine, { effects: { <slot>: { secret: "<name>"
212
+ * } } })`. {@link secret} tells the rest, and what it refuses.
213
+ *
214
+ * @param name - `open`.
215
+ * @returns The Open secret, for an Effect. Its `.path` names the file of the secret, for the `env`
216
+ * of a `command`.
217
+ *
218
+ * @see docs/concepts/secrets-and-trust.md
219
+ * @see docs/guides/bind-open-parts.md
220
+ * @see docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md
221
+ */
222
+ export function secret(name: typeof open): OpenSecret;
122
223
  export function secret(name: string | typeof open): Secret | OpenSecret {
123
224
  if ((name as unknown) === open) {
124
225
  // An Open secret (ADR-0054): the composer binds its name once, with `customize()`'s
@@ -144,7 +245,11 @@ export function secret(name: string | typeof open): Secret | OpenSecret {
144
245
  return s;
145
246
  }
146
247
 
147
- /** What a value that may hold `secret(open)` gives: marked when it does. */
248
+ /**
249
+ * What a value that may hold `secret(open)` gives: marked when it does.
250
+ *
251
+ * @internal
252
+ */
148
253
  type MarkedIf<V, T> = [Extract<V, OpenSecretMark>] extends [never] ? T : T & OpenSecretMark;
149
254
 
150
255
  /** The values of a record type, or never. */
@@ -154,7 +259,10 @@ type ValuesOf<R> = R extends Readonly<Record<string, infer V>> ? V : never;
154
259
  export type OpenSecretIn<V> = [Extract<V, OpenSecretMark>] extends [never] ? never : "secret";
155
260
 
156
261
  /** The Open parts an http definition's types state (ADR-0054): its url, when it is `open`, and its
157
- * secret, when its auth or a header holds `secret(open)`. */
262
+ * secret, when its auth or a header holds `secret(open)`.
263
+ *
264
+ * @internal
265
+ */
158
266
  export type HttpOpenParts<U, A, H> = (U extends typeof open ? "url" : never) | OpenSecretIn<A | ValuesOf<H>>;
159
267
 
160
268
  /** A secret's wire part: `{ secret: name }` — for an Open secret, a part that stands for it. */
@@ -165,7 +273,46 @@ function partOf(s: Secret): TextPart {
165
273
  return part;
166
274
  }
167
275
 
168
- /** A string with secrets in it: text`Token ${secret("x")}`. Each `${…}` is a secret or a string. */
276
+ /**
277
+ * Make a string from literal text and Effect secrets, as a tagged template:
278
+ * `` text`Token ${secret("x")}` ``. The Effector puts in the value of each secret when it makes
279
+ * the call. Use it for a url with a secret in its path, or for a header value. Use it also for
280
+ * `auth`, where it is the whole `Authorization` header, or for a part of `basic`.
281
+ *
282
+ * It refuses a `${…}` that is not a `secret(...)` or a string: `` text`…`: a value is a
283
+ * secret("name") or a string, got <value> ``.
284
+ *
285
+ * @param strings - The literal text of the template.
286
+ * @param values - The `${…}` values, each a `secret(...)` or a string.
287
+ * @returns The string, which the Effector makes.
288
+ *
289
+ * @example
290
+ * ```ts
291
+ * import { http, jr2Setup, secret, text } from "@jr2/orchestrator";
292
+ *
293
+ * export const machine = jr2Setup({
294
+ * actors: {
295
+ * notify: http({
296
+ * method: "POST",
297
+ * url: text`https://api.telegram.org/bot${secret("telegram")}/sendMessage`,
298
+ * headers: { "x-run-source": text`jr2` },
299
+ * request: () => ({ json: { chat_id: "12345", text: "The run is done." } }),
300
+ * }),
301
+ * },
302
+ * }).createMachine({
303
+ * id: "notify",
304
+ * initial: "notifying",
305
+ * states: {
306
+ * notifying: { invoke: { src: "notify", onDone: "done" } },
307
+ * done: { type: "final" },
308
+ * },
309
+ * });
310
+ * ```
311
+ *
312
+ * @see docs/guides/run-a-command-or-call-a-url.md
313
+ * @see docs/concepts/secrets-and-trust.md
314
+ * @see docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md
315
+ */
169
316
  export function text<const V extends Array<Secret | OpenSecret | string>>(
170
317
  strings: TemplateStringsArray,
171
318
  ...values: V
@@ -189,15 +336,87 @@ export function text(strings: TemplateStringsArray, ...values: Array<Secret | st
189
336
  return { text: parts };
190
337
  }
191
338
 
192
- /** `Authorization: Bearer <value>`. The secret may be `secret(open)` (ADR-0054). */
339
+ /**
340
+ * Make the `auth` of an `http` Effect that sends `Authorization: Bearer <value>`, with the value
341
+ * of an Effect secret. In a packaged Machine, the secret can be `secret(open)`.
342
+ *
343
+ * It refuses a value that is not a secret: `bearer(…) takes a secret("name")`.
344
+ *
345
+ * @param s - The secret: `secret("name")`, or `secret(open)`.
346
+ * @returns The `auth` of an `http` Effect.
347
+ *
348
+ * @example
349
+ * ```ts
350
+ * import { bearer, http, jr2Setup, secret } from "@jr2/orchestrator";
351
+ *
352
+ * export const machine = jr2Setup({
353
+ * actors: {
354
+ * status: http({
355
+ * method: "GET",
356
+ * url: "https://api.github.com/repos/you/app",
357
+ * auth: bearer(secret("github")),
358
+ * }),
359
+ * },
360
+ * }).createMachine({
361
+ * id: "status",
362
+ * initial: "reading",
363
+ * states: {
364
+ * reading: { invoke: { src: "status", onDone: "done" } },
365
+ * done: { type: "final" },
366
+ * },
367
+ * });
368
+ * ```
369
+ *
370
+ * @see docs/guides/run-a-command-or-call-a-url.md
371
+ * @see docs/concepts/secrets-and-trust.md
372
+ * @see docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md
373
+ * @see docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md
374
+ */
193
375
  export function bearer<const S extends Secret>(s: S): MarkedIf<S, { readonly bearer: Secret }>;
194
376
  export function bearer(s: Secret): { readonly bearer: Secret } {
195
377
  if (!isSecret(s)) throw new Error('bearer(…) takes a secret("name")');
196
378
  return { bearer: s };
197
379
  }
198
380
 
199
- /** `Authorization: Basic base64(user:password)`. Each is a string, a secret, or a `text`; either may
200
- * hold `secret(open)` (ADR-0054). */
381
+ /**
382
+ * Make the `auth` of an `http` Effect that sends `Authorization: Basic <value>`, where the value
383
+ * is the base64 of `user:password`. The Effector puts in the value of each secret.
384
+ *
385
+ * Each of `user` and `password` is a string, a `secret(...)` or a `text`. In a packaged Machine,
386
+ * one of them can hold `secret(open)`. It refuses another value: `` expected a string, a
387
+ * secret("name") or a text`…`, got <value> ``.
388
+ *
389
+ * @param user - The user name: a string, a `secret(...)` or a `text`.
390
+ * @param password - The password: a string, a `secret(...)` or a `text`.
391
+ * @returns The `auth` of an `http` Effect.
392
+ *
393
+ * @example
394
+ * ```ts
395
+ * import { basic, http, jr2Setup, secret } from "@jr2/orchestrator";
396
+ *
397
+ * export const machine = jr2Setup({
398
+ * actors: {
399
+ * issue: http({
400
+ * method: "GET",
401
+ * url: "https://jira.example.com/rest/api/2/issue/APP-1",
402
+ * auth: basic("jr2-bot@example.com", secret("jira-token")),
403
+ * }),
404
+ * },
405
+ * }).createMachine({
406
+ * id: "issue",
407
+ * initial: "reading",
408
+ * states: {
409
+ * reading: { invoke: { src: "issue", onDone: "done" } },
410
+ * done: { type: "final" },
411
+ * },
412
+ * });
413
+ * ```
414
+ *
415
+ * @see docs/guides/run-a-command-or-call-a-url.md
416
+ * @see docs/concepts/secrets-and-trust.md
417
+ * @see docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md
418
+ * @see docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md
419
+ */
201
420
  export function basic<const U extends string | Secret | Text, const P extends string | Secret | Text>(
202
421
  user: U,
203
422
  password: P,
@@ -285,29 +504,129 @@ export type FromInput<S, T> = T | ((input: S extends z.ZodType ? z.infer<S> : vo
285
504
  /** `file(path, { slot?, segments?, contentType? })`: a Worktree file as an `http` body. */
286
505
  export type FileBody<S extends z.ZodType | undefined = undefined> = {
287
506
  readonly file: {
288
- /** The file, relative to the Repo Slot's Worktree. */
507
+ /** The file, relative to the Repo Slot's Worktree, or to `/work` in a Workspace with no Repo
508
+ * Slot. */
289
509
  readonly path: FromInput<S, string>;
290
- /** The Repo Slot whose Worktree holds it — needed only where the Workspace carries several. */
510
+ /** The Repo Slot whose Worktree holds it. Write it only when the Workspace has more than one.
511
+ */
291
512
  readonly slot?: string;
292
- /** Path segments appended to the url, as `request`'s `path` is for a json or text body. */
513
+ /** Path segments that jr2 adds to the end of the url, as `path` of `request` does for a json
514
+ * or text body. */
293
515
  readonly segments?: FromInput<S, readonly string[]>;
294
- /** The body's content type: this or a `content-type` header of the definition's, not both (a
295
- * definition with both is refused). With neither it is `application/octet-stream`. */
516
+ /** The content type of the body. Write it or a `content-type` header, not both. With neither,
517
+ * it is `application/octet-stream`. */
296
518
  readonly contentType?: string;
297
519
  };
298
520
  };
299
521
 
300
522
  /**
301
- * A Worktree file as an `http` body (ADR-0067): `body: file("dist/report.pdf")`. The Effect then runs in
302
- * the Sandbox Effector of the Workspace whose body invokes it, which opens the file beneath the
303
- * Worktree — a symlink at any component, `..`, an absolute path, a FIFO, a directory and a file
304
- * with more than one link are output `refused`, with nothing sent. `path` and `segments` may be
305
- * functions of the Effect's input.
523
+ * Make a Worktree file the body of an `http` Effect: `body: file("dist/report.pdf")`. The Effect
524
+ * then runs in the Sandbox Effector of the Workspace whose body invokes it. That Effector opens the
525
+ * file in the Worktree and sends it. In a Workspace with no Repo Slot, the path is relative to
526
+ * `/work`, the folder that its steps share. Use it with `PUT`, `POST` or `PATCH`. `path` and
527
+ * `segments` can be functions of the input of the Effect.
528
+ *
529
+ * Some paths and files give the output `refused`, and then nothing is sent. The path has a symlink
530
+ * in it, has `..`, or is absolute. Or the file is a FIFO, a directory, or a file with more than one
531
+ * link.
532
+ *
533
+ * It refuses, when you call it:
534
+ * - a path that is not a string or a function: `file(path): the path is a string, or a function of
535
+ * the Effect's input that returns one`
536
+ *
537
+ * And when `http` takes the definition:
538
+ * - a method other than `PUT`, `POST` and `PATCH`: `http({ method: "<method>", body: file(...) }):
539
+ * a file body is sent with PUT, POST, PATCH (ADR-0068)`
540
+ * - a `request` beside it: ``http({ body: file(...), request }): with a file body the run's part
541
+ * is file()'s — its path, from the input if it must be, and its `segments` — so write no
542
+ * `request` (ADR-0068)``
543
+ * - a `content-type` header and `contentType`: `http({ headers, body: file(path, { contentType })
544
+ * }): "content-type" is set by file()'s contentType — write one of them (ADR-0067)`
545
+ *
546
+ * And at `jr2 up`, an Effect that no `workspace()` invokes: `workflow "<workflow>": the http Effect
547
+ * "<slot>" sends a Worktree file as its body, and no workspace() invokes it — outside a Workspace
548
+ * there is no Worktree to read; invoke it from a workspace()'s body, or send a json or text body
549
+ * (ADR-0068)`.
550
+ *
551
+ * @param path - The file, relative to the Worktree of the Repo Slot, or to `/work` in a Workspace
552
+ * with no Repo Slot: a string, or a function of the input of the Effect that gives one.
553
+ * @param options - Optional. Each field says what it does.
554
+ * @returns The `body` of an `http` Effect.
555
+ *
556
+ * @example
557
+ * ```ts
558
+ * import {
559
+ * bearer,
560
+ * command,
561
+ * fail,
562
+ * file,
563
+ * http,
564
+ * jr2Setup,
565
+ * secret,
566
+ * workspace,
567
+ * type Workspaced,
568
+ * } from "@jr2/orchestrator";
569
+ *
570
+ * type Input = Workspaced<{}, "app">;
571
+ *
572
+ * const body = jr2Setup({
573
+ * types: {} as { context: Input; input: Input },
574
+ * actors: {
575
+ * build: command(["npm", "run", "report"]),
576
+ * upload: http({
577
+ * method: "PUT",
578
+ * url: "https://files.example.com/reports",
579
+ * auth: bearer(secret("files")),
580
+ * body: file("dist/report.pdf", {
581
+ * segments: ["report.pdf"],
582
+ * contentType: "application/pdf",
583
+ * }),
584
+ * }),
585
+ * fail: fail(),
586
+ * },
587
+ * }).createMachine({
588
+ * id: "report",
589
+ * context: ({ input }) => input,
590
+ * on: { "workspace.lost": ".lost" },
591
+ * initial: "building",
592
+ * states: {
593
+ * building: { invoke: { src: "build", onDone: "uploading" } },
594
+ * uploading: { invoke: { src: "upload", onDone: "done" } },
595
+ * done: { type: "final" },
596
+ * lost: { invoke: { src: "fail", input: { reason: "the Sandbox is gone" } } },
597
+ * },
598
+ * });
599
+ *
600
+ * export const machine = workspace(body, {
601
+ * repos: { app: { url: "https://github.com/you/app.git" } },
602
+ * spec: () => ({ branch: "report" }),
603
+ * });
604
+ * ```
605
+ *
606
+ * @see docs/guides/run-a-command-or-call-a-url.md
607
+ * @see docs/concepts/effects.md
608
+ * @see docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md
306
609
  */
307
610
  export function file<S extends z.ZodType | undefined = undefined>(
308
611
  path: FromInput<S, string>,
309
- options: { slot?: string; segments?: FromInput<S, readonly string[]>; contentType?: string } = {},
612
+ options: {
613
+ /** The Repo Slot whose Worktree holds the file. Write it only when the Workspace has more than
614
+ * one. */
615
+ slot?: string;
616
+ /**
617
+ * Path segments that jr2 adds to the end of the url, as `path` does in `request` for a json or
618
+ * text body. A list of strings, or a function of the input of the Effect that gives one.
619
+ */
620
+ segments?: FromInput<S, readonly string[]>;
621
+ /**
622
+ * The content type of the body. Default `application/octet-stream`. Do not write it beside a
623
+ * `content-type` header.
624
+ */
625
+ contentType?: string;
626
+ } = {},
310
627
  ): FileBody<S> {
628
+ // The Sandbox Effector opens the file beneath the Worktree with no symlink at any component
629
+ // (ADR-0067, ADR-0068); the refusals it makes are output, never a fault.
311
630
  if (typeof path !== "string" && typeof path !== "function") {
312
631
  throw new Error("file(path): the path is a string, or a function of the Effect's input that returns one");
313
632
  }
@@ -326,12 +645,16 @@ const FILE_METHODS: readonly HttpMethod[] = ["PUT", "POST", "PATCH"];
326
645
 
327
646
  // --- The definition ------------------------------------------------------------------------------
328
647
 
329
- /** What `request` returns: the run's part of the request. Everything else is the definition's. */
648
+ /** What the `request` of an `http` definition returns: the part of the request that changes for
649
+ * each run. The definition fixes the rest. */
330
650
  export type HttpRequestParts = {
331
- /** Path segments appended to the url: each is encoded; `.`, `..` and an empty one are refused
332
- * in the Effector as output, with nothing sent. */
651
+ /**
652
+ * Path segments that jr2 adds to the end of the url. jr2 encodes each segment. A segment that
653
+ * is `.`, `..` or empty gives the output `refused`, and nothing is sent.
654
+ */
333
655
  path?: readonly string[];
334
- /** A JSON body. */
656
+ /** A JSON body. Do not give `text` too. When it starts, the Effect refuses both: ``http:
657
+ * `request` returned both json and text``. */
335
658
  json?: unknown;
336
659
  /** A text body. */
337
660
  text?: string;
@@ -339,33 +662,97 @@ export type HttpRequestParts = {
339
662
 
340
663
  /** An `http` Effect's definition. */
341
664
  export type HttpDefinition<S extends z.ZodType | undefined = undefined> = {
665
+ /**
666
+ * The HTTP method: `GET`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE` or `OPTIONS`. It refuses
667
+ * another: `http({ method: "<method>" }): the method is one of GET, HEAD, POST, PUT, PATCH,
668
+ * DELETE, OPTIONS`.
669
+ */
342
670
  method: HttpMethod;
343
- /** The url. Its scheme and host are fixed here — never a param — and a secret may sit only in
344
- * its path: text`https://api.telegram.org/bot${secret("tg")}/sendMessage`. Or `open`: a packaged
345
- * Machine that cannot know the host leaves the url Open, and the composer binds it once with
346
- * `customize(machine, { effects: { <slot>: { url } } })` — never per run (ADR-0054). */
671
+ // The scheme and host are literal so that no param reaches the host (ADR-0068); an Open url is
672
+ // bound once by the composer, never per run (ADR-0054).
673
+ /**
674
+ * The url: `http://` or `https://`, then a literal host. No input, param or Effect secret can
675
+ * change where the call goes. An Effect secret can be only in the path:
676
+ * `` text`https://api.telegram.org/bot${secret("tg")}/sendMessage` ``.
677
+ *
678
+ * Or `open`: a packaged Machine that cannot know the host leaves the url Open, and the composer
679
+ * binds it one time with `customize(machine, { effects: { <slot>: { url } } })`.
680
+ *
681
+ * It refuses:
682
+ * - a scheme or host that is not literal: `` http({ url }): the url is an http:// or https:// url
683
+ * whose scheme and host are literal — a secret may sit only in its path, after a "/":
684
+ * text`https://api.example.com/bot${secret("tg")}/send` (ADR-0068) ``
685
+ * - an Effect secret after `?` or `#`: `` http({ url }): secret("<name>") sits in the url's query
686
+ * or fragment — a secret may sit only in its path; send it in `auth` or a header instead:
687
+ * headers: { "x-api-key": text`${secret("...")}` } (ADR-0068) ``
688
+ * - a host that is not a host: `http({ url }): "<scheme>://<host>" is not a host`
689
+ * - a user name or password in the url: ``http({ url }): the url carries userinfo — use `auth` ``
690
+ */
347
691
  url: string | Text | typeof open;
348
- /** Static headers, lowercase; a value may be a `text` with a secret. */
692
+ /**
693
+ * Headers that are the same for each run. Each name is lowercase. A value is a string, or a
694
+ * `text` with an Effect secret.
695
+ *
696
+ * It refuses:
697
+ * - a name that is not lowercase, or that frames or routes the request (`host`,
698
+ * `content-length`, `transfer-encoding`, `connection`, `upgrade`, `te`, `trailer`,
699
+ * `keep-alive`, `proxy-*`): `http({ headers }): "<name>" is not a header an Effect sets — a
700
+ * lowercase name that does not frame or route the request (ADR-0068)`
701
+ * - an `authorization` header beside `auth`: ``http({ headers }): "authorization" is set by
702
+ * `auth` — write one of them``
703
+ */
349
704
  headers?: Readonly<Record<string, string | Text>>;
705
+ /**
706
+ * The `Authorization` header: `bearer(secret("x"))`, `basic(user, password)`, or a `text` that
707
+ * is the whole value.
708
+ */
350
709
  auth?: { readonly bearer: Secret } | { readonly basic: { user: Text; password: Text } } | Text;
351
- /** The Effect's input — what an invoke's `input` (or `fromPick`) hands it. */
710
+ /**
711
+ * The zod schema of the input of the Effect: what the `input` of the `invoke`, or `fromPick`,
712
+ * gives it. jr2 parses the input with it before the call. When it starts, input that does not
713
+ * agree with it makes the Effect fail (its `onError` route): `http "<slot>": its input does not
714
+ * match the definition's input schema — <path>: <message>`, one `<path>: <message>` for each
715
+ * problem, joined by `; `.
716
+ */
352
717
  input?: S;
353
- /** The run's part of the request, from the input. */
718
+ /**
719
+ * A function that makes the part of the request that changes for each run, from the input:
720
+ * `path` segments, and a `json` or `text` body. It cannot change the host, a header or an
721
+ * Effect secret. Do not write it beside `body`.
722
+ */
354
723
  request?: (input: S extends z.ZodType ? z.infer<S> : void) => HttpRequestParts;
355
- /** A Worktree file as the body: `file(path, { slot?, segments?, contentType? })` (ADR-0067). Then the
356
- * Effect runs in its Workspace's Sandbox Effector, and `request` is not written: `file()`'s
357
- * `segments` are the path. */
724
+ // A file body is an ADR-0067 Effect: the Sandbox Effector of the Workspace sends it.
725
+ /**
726
+ * A Worktree file as the body: `file(path, { slot?, segments?, contentType? })`. The Effect then
727
+ * runs in the Sandbox Effector of its Workspace. Do not write `request` beside it: the `segments`
728
+ * of `file` are the path. It refuses another body: ``http({ body }): a body is file(path, {
729
+ * slot?, segments?, contentType? }) — json and text come from `request` ``.
730
+ */
358
731
  body?: FileBody<S>;
359
- /** Default 30s. Past it with no answer: output `unknown`. */
732
+ /**
733
+ * How long jr2 waits for the answer: a number and a unit, such as `"30s"` or `"2m"`. Default
734
+ * `"30s"`. With no answer in that time, the output is `unknown`. It refuses a value that is not
735
+ * a positive duration: `http: timeout "<timeout>" is not a positive duration (like "30s",
736
+ * "2m")`.
737
+ */
360
738
  timeout?: Duration;
361
- /** Bytes of a text body returned. Default 65536. */
739
+ /**
740
+ * The largest number of bytes of a text response body that the output keeps. Default 65536.
741
+ * When jr2 cuts the body, `truncated` is `true`. It refuses a value that is not a positive whole
742
+ * number: `http({ maxBody }): a positive number of bytes`.
743
+ */
362
744
  maxBody?: number;
363
- /** Marked by the author: the call may be sent twice (it carries its own idempotency key, or it
364
- * is a read). A restore that finds no replica holding it then sends it again; unmarked, it
365
- * settles `unknown` (ADR-0067). Default false. */
745
+ // The restore rule (ADR-0067): a restore that finds no replica holding the call sends it again
746
+ // only when it is marked; unmarked, it settles `unknown`.
747
+ /**
748
+ * `true` when it is safe to send the call two times: it has its own idempotency key, or it only
749
+ * reads. Default `false`. After an Orchestrator restart, jr2 cannot always tell if the call went
750
+ * out. Then it sends an idempotent call again, and gives any other call the output `unknown`.
751
+ */
366
752
  idempotent?: boolean;
367
753
  };
368
754
 
755
+ /** @internal */
369
756
  export type HttpInput<S> = S extends z.ZodType ? z.infer<S> : void;
370
757
 
371
758
  const DEFAULT_TIMEOUT_MS = 30_000;
@@ -504,8 +891,8 @@ export function openHttpParts<S extends z.ZodType | undefined>(def: HttpDefiniti
504
891
  /**
505
892
  * The unbound logic of an http definition with Open parts (ADR-0054) — its url, an Open secret in
506
893
  * its auth or a header, or both — or undefined when every part is fixed. Binding builds the Effect
507
- * again, whole, with `rebuild` (its own constructor) over the definition with the bound parts, so the
508
- * bound one is checked like any definition; a binding that names one of two Open parts gives an
894
+ * again, whole, with `rebuild` (its own constructor) over the definition with the bound parts, so
895
+ * the bound one is checked like any definition; a binding that names one of two Open parts gives an
509
896
  * Effect still Open in the other.
510
897
  */
511
898
  export function openHttpOf<S extends z.ZodType | undefined>(
@@ -879,8 +1266,8 @@ async function drive<I, O>(
879
1266
  /**
880
1267
  * The state that invoked this Effect exited (D1) — or the host is parking the run. Parking keeps
881
1268
  * the call for the restore to find. An exit stops the polling and records the abort, "outcome
882
- * unknown": an http Effect has no stop route, so the Effector finishes the call and drops an outcome nobody
883
- * reads. The Machine never hears it.
1269
+ * unknown": an http Effect has no stop route, so the Effector finishes the call and drops an
1270
+ * outcome nobody reads. The Machine never hears it.
884
1271
  */
885
1272
  function stopped(self: AnyActorRef, system: AnyActorSystem, effectId: string): void {
886
1273
  const run = running.get(self);
@@ -955,12 +1342,65 @@ function parseDuration(d: Duration): number {
955
1342
  }
956
1343
 
957
1344
  /**
958
- * THE authoring surface for an `http` Effect (ADR-0068): `actors: { comment: http({ method, url,
959
- * auth: bearer(secret("jira")), input, request }) }`, invoked as `src: "comment"`. It runs in the
960
- * Instance Effector, which `jr2 up` converges because this definition is placed there — or, with
961
- * `body: file(...)`, in the Sandbox Effector of the Workspace whose body invokes it (ADR-0067). The
962
- * Orchestrator signs the call and holds no secret. Every outcome is output:
963
- * `response | unreachable | refused | unknown`.
1345
+ * Make an `http` Effect: a call to a URL, with Effect secrets that the Agent never reads. Put it
1346
+ * in `actors`, and invoke it by its key: `actors: { comment: http({ ... }) }`, then `invoke: {
1347
+ * src: "comment" }`.
1348
+ *
1349
+ * It runs in the Instance Effector, which `jr2 up` deploys when a Machine has an `http` Effect.
1350
+ * With `body: file(...)`, it runs in the Sandbox Effector of the Workspace whose body invokes it.
1351
+ * The Effector makes the call. The Orchestrator holds no secret.
1352
+ *
1353
+ * Every outcome is output (`HttpOutput`): `response`, `unreachable`, `refused` or `unknown`. A 4xx
1354
+ * or 5xx status is output too. jr2 sends the call at most one time, unless you mark it
1355
+ * `idempotent`. When its state exits while the call runs, jr2 stops waiting for it. The Effector
1356
+ * can still send it.
1357
+ *
1358
+ * Each field of {@link HttpDefinition} tells what it refuses. `createMachine` also refuses an
1359
+ * Effect written directly in an `invoke`: `machine "<id>": state "<state>" invokes an Effect
1360
+ * inline — a Part is a key of actors, so it has a name that the ledger, the report and
1361
+ * customize() share: actors: { <name>: http(...) }, then invoke: { src: "<name>" }`.
1362
+ *
1363
+ * @param def - The definition. Each field tells what it does.
1364
+ * @returns An Effect, for `actors`.
1365
+ *
1366
+ * @example
1367
+ * ```ts
1368
+ * import { z } from "zod";
1369
+ * import { bearer, http, jr2Setup, secret } from "@jr2/orchestrator";
1370
+ *
1371
+ * const comment = http({
1372
+ * method: "POST",
1373
+ * url: "https://jira.example.com/rest/api/2/issue",
1374
+ * auth: bearer(secret("jira")),
1375
+ * input: z.object({ key: z.string(), body: z.string() }),
1376
+ * request: ({ key, body }) => ({ path: [key, "comment"], json: { body } }),
1377
+ * });
1378
+ *
1379
+ * export const machine = jr2Setup({
1380
+ * types: {} as { context: { key: string }; input: { key: string } },
1381
+ * actors: { comment },
1382
+ * }).createMachine({
1383
+ * id: "comment",
1384
+ * context: ({ input }) => input,
1385
+ * initial: "commenting",
1386
+ * states: {
1387
+ * commenting: {
1388
+ * invoke: {
1389
+ * src: "comment",
1390
+ * input: ({ context }) => ({ key: context.key, body: "The build passed." }),
1391
+ * onDone: "done",
1392
+ * },
1393
+ * },
1394
+ * done: { type: "final" },
1395
+ * },
1396
+ * });
1397
+ * ```
1398
+ *
1399
+ * @see docs/concepts/effects.md
1400
+ * @see docs/guides/run-a-command-or-call-a-url.md
1401
+ * @see docs/concepts/secrets-and-trust.md
1402
+ * @see docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md
1403
+ * @see docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md
964
1404
  */
965
1405
  export function http<
966
1406
  S extends z.ZodType | undefined = undefined,
@@ -973,6 +1413,8 @@ export function http<
973
1413
  export function http<S extends z.ZodType | undefined = undefined>(
974
1414
  def: HttpDefinition<S>,
975
1415
  ): EffectLogic<HttpInput<S>, HttpOutput> {
1416
+ // The Orchestrator signs the start, and the Effector builds the request: the definition's parts,
1417
+ // and `request`'s from the input (ADR-0068).
976
1418
  if (def.body !== undefined) return httpFileWith(sandboxEffectorPort, def);
977
1419
  return httpWith(
978
1420
  (target) => createEffectorClient({ key: target.key, transport: nodeEffectorTransport(target.service) }),