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