@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.
- 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 +442 -0
- package/docs/concepts/architecture.md +230 -0
- package/docs/concepts/charts.md +143 -0
- package/docs/concepts/composition.md +357 -0
- package/docs/concepts/converge.md +254 -0
- package/docs/concepts/durable-runs.md +189 -0
- package/docs/concepts/effects.md +603 -0
- package/docs/concepts/gates.md +208 -0
- package/docs/concepts/machines-and-workflows.md +289 -0
- package/docs/concepts/pools-and-sources.md +230 -0
- package/docs/concepts/secrets-and-trust.md +222 -0
- package/docs/concepts/the-menu.md +378 -0
- package/docs/concepts/workspaces.md +678 -0
- package/docs/get-started/a-model-decides.md +563 -0
- package/docs/get-started/an-agent-in-a-sandbox.md +381 -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 +922 -0
- package/docs/get-started/install.md +126 -0
- package/docs/guides/add-tools-to-the-sandbox.md +295 -0
- package/docs/guides/answer-a-gate.md +304 -0
- package/docs/guides/bind-open-parts.md +223 -0
- package/docs/guides/cancel-a-run.md +146 -0
- package/docs/guides/clean-up.md +206 -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 +226 -0
- package/docs/guides/look-inside-a-sandbox.md +174 -0
- package/docs/guides/mirror-the-kit-images.md +176 -0
- package/docs/guides/move-from-kind-to-a-shared-cluster.md +302 -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 +486 -0
- package/docs/guides/run-a-chart-by-name.md +270 -0
- package/docs/guides/run-a-command-or-call-a-url.md +544 -0
- package/docs/guides/run-one-worker-per-item.md +321 -0
- package/docs/guides/set-an-agents-access-and-dials.md +375 -0
- package/docs/guides/size-a-workspace.md +204 -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 +208 -0
- package/docs/guides/use-a-local-model.md +313 -0
- package/docs/guides/watch-a-run.md +326 -0
- package/docs/guides/write-a-workflow.md +318 -0
- package/docs/index.md +44 -0
- package/docs/llms.txt +216 -0
- package/docs/reference/authoring-surface/agent.md +128 -0
- package/docs/reference/authoring-surface/answer.md +140 -0
- package/docs/reference/authoring-surface/arg.md +60 -0
- package/docs/reference/authoring-surface/basic.md +62 -0
- package/docs/reference/authoring-surface/bearer.md +53 -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 +191 -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 +83 -0
- package/docs/reference/authoring-surface/file.md +122 -0
- package/docs/reference/authoring-surface/frompick.md +277 -0
- package/docs/reference/authoring-surface/http.md +159 -0
- package/docs/reference/authoring-surface/isfailure.md +58 -0
- package/docs/reference/authoring-surface/jr2setup.md +237 -0
- package/docs/reference/authoring-surface/open.md +74 -0
- package/docs/reference/authoring-surface/openbody.md +86 -0
- package/docs/reference/authoring-surface/paramsof.md +125 -0
- package/docs/reference/authoring-surface/pool.md +186 -0
- package/docs/reference/authoring-surface/push.md +112 -0
- package/docs/reference/authoring-surface/secret.md +118 -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 +12 -0
- package/docs/reference/authoring-surface/type-answeraction.md +12 -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-binding.md +62 -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 +19 -0
- package/docs/reference/authoring-surface/type-commandoptions.md +78 -0
- package/docs/reference/authoring-surface/type-commandoutput.md +104 -0
- package/docs/reference/authoring-surface/type-customize.md +109 -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 +65 -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 +41 -0
- package/docs/reference/authoring-surface/type-frominput.md +14 -0
- package/docs/reference/authoring-surface/type-hostinjectedinput.md +58 -0
- package/docs/reference/authoring-surface/type-httpdefinition.md +101 -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 +89 -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-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-noslotspec.md +26 -0
- package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +32 -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 +17 -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 +31 -0
- package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +32 -0
- package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
- package/docs/reference/authoring-surface/type-poolspec.md +60 -0
- package/docs/reference/authoring-surface/type-pushinput.md +15 -0
- package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
- package/docs/reference/authoring-surface/type-pushoutput.md +124 -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 +77 -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 +32 -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 +94 -0
- package/docs/reference/authoring-surface/type-workspacehandles.md +23 -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 +39 -0
- package/docs/reference/authoring-surface/type-workspacespec.md +37 -0
- package/docs/reference/authoring-surface/workspace.md +259 -0
- package/docs/reference/authoring-surface.md +197 -0
- package/docs/reference/cli.md +433 -0
- package/docs/reference/config.md +756 -0
- package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +113 -0
- package/docs/reference/decisions/0002-duplex-streaming-actor.md +110 -0
- package/docs/reference/decisions/0004-worktree-provisioning-model.md +124 -0
- package/docs/reference/decisions/0005-sandbox-pod-composition.md +230 -0
- package/docs/reference/decisions/0006-agent-control-surface.md +47 -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 +157 -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 +265 -0
- package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +124 -0
- package/docs/reference/decisions/0017-pool-over-a-source-port.md +69 -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 +275 -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 +100 -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 +176 -0
- package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +439 -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 +237 -0
- package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +89 -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 +190 -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 +127 -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 +227 -0
- package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +81 -0
- package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +111 -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 +106 -0
- package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +384 -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 +212 -0
- package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +83 -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 +550 -0
- package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +430 -0
- package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +464 -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 +323 -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.md +83 -0
- package/docs/reference/glossary.md +648 -0
- package/docs/reference/http-api.md +1348 -0
- package/docs/reference/machines.md +190 -0
- package/docs/reference/mechanism-events.md +72 -0
- package/docs/why.md +206 -0
- package/package.json +5 -3
- package/src/actor.ts +541 -79
- package/src/agent.ts +114 -38
- package/src/ambient.ts +2 -1
- package/src/assembly-checks.ts +189 -7
- package/src/cause.ts +234 -0
- package/src/chart.ts +169 -37
- package/src/charts.ts +34 -6
- package/src/config.ts +430 -178
- package/src/custodian.ts +74 -22
- package/src/customize.ts +188 -46
- package/src/dispatch-effect.ts +945 -0
- package/src/door-compat.ts +217 -0
- package/src/durability.ts +11 -4
- package/src/effect.ts +341 -44
- package/src/effector-wire.ts +160 -11
- package/src/fail.ts +209 -0
- package/src/harness-client.ts +541 -47
- package/src/http-effect.ts +495 -53
- package/src/http.ts +427 -16
- package/src/index.ts +60 -82
- package/src/instance.ts +68 -8
- package/src/internal.ts +84 -0
- package/src/machine-doc.ts +21 -3
- package/src/mechanism-routes.ts +168 -0
- package/src/model-keys.generated.ts +53 -0
- package/src/model-keys.ts +18 -58
- package/src/notices.ts +66 -13
- package/src/open-body.ts +159 -23
- package/src/open-effect.ts +81 -8
- package/src/open.ts +59 -6
- package/src/parts.ts +184 -13
- package/src/party.ts +68 -2
- package/src/pick.ts +463 -11
- package/src/pool.ts +247 -36
- package/src/registration.ts +83 -4
- package/src/repos.ts +2 -1
- package/src/run-host.ts +565 -31
- package/src/run-output.ts +38 -0
- package/src/sandbox-effect.ts +273 -38
- package/src/sandbox-kube.ts +3 -1
- package/src/secret-placement.ts +52 -17
- package/src/server.ts +3 -0
- package/src/setup.ts +239 -56
- 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/vocabulary.ts +69 -14
- package/src/wire.ts +90 -16
- package/src/workspace.ts +697 -175
package/src/effector-wire.ts
CHANGED
|
@@ -21,11 +21,14 @@ import { z } from "zod";
|
|
|
21
21
|
|
|
22
22
|
export const HTTP_METHODS = ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] as const;
|
|
23
23
|
export const HttpMethodShape = z.enum(HTTP_METHODS);
|
|
24
|
+
/** The method of an `http` Effect: `GET`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE` or `OPTIONS`. */
|
|
24
25
|
export type HttpMethod = z.infer<typeof HttpMethodShape>;
|
|
25
26
|
|
|
26
27
|
/** One part of a string the Effector builds: literal text, or a secret it holds, by name. A
|
|
27
28
|
* secret's value is never on the wire. */
|
|
28
29
|
export const TextPartShape = z.union([z.strictObject({ text: z.string() }), z.strictObject({ secret: z.string() })]);
|
|
30
|
+
/** One part of a {@link Text}: `{ text }`, literal text, or `{ secret }`, the name of an Effect
|
|
31
|
+
* secret. The `text` template makes the parts. */
|
|
29
32
|
export type TextPart = z.infer<typeof TextPartShape>;
|
|
30
33
|
|
|
31
34
|
export const WireAuthShape = z.union([
|
|
@@ -71,7 +74,8 @@ const HEADER_NAME = /^[a-z0-9!#$%&'*+.^_`|~-]+$/;
|
|
|
71
74
|
export const isEffectHeader = (name: string): boolean =>
|
|
72
75
|
HEADER_NAME.test(name) && !FRAMING_HEADERS.has(name) && !name.startsWith("proxy-");
|
|
73
76
|
|
|
74
|
-
/** An `http` start; its body may be a Worktree file (ADR-0067), which only a Sandbox Effector
|
|
77
|
+
/** An `http` start; its body may be a Worktree file (ADR-0067), which only a Sandbox Effector
|
|
78
|
+
* serves. */
|
|
75
79
|
export const HttpStartShape = z.strictObject({
|
|
76
80
|
kind: z.literal("http"),
|
|
77
81
|
method: HttpMethodShape,
|
|
@@ -103,7 +107,8 @@ export type Pair = z.infer<typeof PairShape>;
|
|
|
103
107
|
/** A `push` start: the Workspace's branch to the Repo Slot's Binding, with the push credential of
|
|
104
108
|
* the `git.credentials` entry the url falls under — the Effector's config holds the entries, and
|
|
105
109
|
* matches the url as the fence does (`credential` names one by its `match` instead). `pin` is the
|
|
106
|
-
* commit; absent, the Effector reads the Agent's branch tip and its answer carries it (`pinned`).
|
|
110
|
+
* commit; absent, the Effector reads the Agent's branch tip and its answer carries it (`pinned`).
|
|
111
|
+
*/
|
|
107
112
|
export const PushStartShape = z.strictObject({
|
|
108
113
|
kind: z.literal("push"),
|
|
109
114
|
push: z.strictObject({
|
|
@@ -142,8 +147,71 @@ export const CommandStartShape = z.strictObject({
|
|
|
142
147
|
});
|
|
143
148
|
export type CommandStartRequest = z.infer<typeof CommandStartShape>;
|
|
144
149
|
|
|
145
|
-
/**
|
|
146
|
-
export const
|
|
150
|
+
/** A string the Effector builds, as parts (the `text` template's wire form). */
|
|
151
|
+
export const TextShape = z.array(TextPartShape);
|
|
152
|
+
|
|
153
|
+
/** A secret the Effector holds, by name. */
|
|
154
|
+
export const SecretRefShape = z.strictObject({ secret: z.string() });
|
|
155
|
+
|
|
156
|
+
/** A Workflow's name as `jr2 run` names it (ADR-0071): a Machine's filename stem, or
|
|
157
|
+
* `<wrapper>/<name>` for a named Chart. The Go side's `Validate` holds the same rule. */
|
|
158
|
+
export const WORKFLOW_NAME = /^[a-z0-9][a-z0-9-]*(\/[a-z0-9][a-z0-9-]*)?$/;
|
|
159
|
+
|
|
160
|
+
/** A dispatch target's origin: a scheme and a host — a DNS name or IPv4 address, or a bracketed
|
|
161
|
+
* IPv6 address — a port when it is not the scheme's, and no path, query, fragment, userinfo or any
|
|
162
|
+
* other character no origin has — the Effector appends the door's path (ADR-0071). The Go side's
|
|
163
|
+
* `origin` is the same rule. */
|
|
164
|
+
export const ORIGIN = /^https?:\/\/(\[[0-9A-Fa-f:.]+\]|[A-Za-z0-9.-]+)(:[0-9]{1,5})?$/;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Which run started a run, and its place in the chain (ADR-0071): the caller's run and slot, the
|
|
168
|
+
* caller's ledger row id (`effect`, the door's idempotency key), the dispatched run's depth — one
|
|
169
|
+
* below its caller, stated by the caller and recorded as sent, so at least 1 — and, for a caller
|
|
170
|
+
* on another Orchestrator, that Instance as opaque text.
|
|
171
|
+
*/
|
|
172
|
+
export const DispatchedByShape = z.strictObject({
|
|
173
|
+
run: z.string(),
|
|
174
|
+
slot: z.string(),
|
|
175
|
+
effect: z.string(),
|
|
176
|
+
depth: z.number().int().positive(),
|
|
177
|
+
instance: z.string().optional(),
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* A `dispatch` start (ADR-0071), which only the Instance Effector serves: a run of `workflow` on the
|
|
182
|
+
* Orchestrator at `url` (its origin, one text part), the body `input` whole, the Instance token as a
|
|
183
|
+
* bearer by name, `dispatchedBy` as the `jr2-dispatched-by` header — `instance` set, since the
|
|
184
|
+
* caller is foreign by construction — and, with `wait`, a poll of `GET /runs/<id>` until the run
|
|
185
|
+
* ends or `timeoutMs` passes.
|
|
186
|
+
*/
|
|
187
|
+
export const DispatchStartShape = z.strictObject({
|
|
188
|
+
kind: z.literal("dispatch"),
|
|
189
|
+
url: TextShape.refine(
|
|
190
|
+
(parts) => parts.length === 1 && "text" in parts[0]! && ORIGIN.test(parts[0].text),
|
|
191
|
+
"a dispatch url is one text part, an origin: http(s)://host[:port]",
|
|
192
|
+
),
|
|
193
|
+
workflow: z.string().regex(WORKFLOW_NAME),
|
|
194
|
+
input: z.record(z.string(), z.unknown()),
|
|
195
|
+
auth: z.strictObject({ bearer: SecretRefShape }),
|
|
196
|
+
// Every id non-empty, as the door's header shape requires (http.ts): an empty one would be the
|
|
197
|
+
// door's 400, which reads as a defect of the input. The effect is a ledger row id, the key. The Go
|
|
198
|
+
// side's `validateDispatch` holds the same rule.
|
|
199
|
+
dispatchedBy: DispatchedByShape.extend({
|
|
200
|
+
run: z.string().min(1),
|
|
201
|
+
slot: z.string().min(1),
|
|
202
|
+
effect: z
|
|
203
|
+
.string()
|
|
204
|
+
.regex(/^[A-Za-z0-9._-]{1,128}$/)
|
|
205
|
+
.refine((id) => id !== "." && id !== ".."),
|
|
206
|
+
instance: z.string().min(1),
|
|
207
|
+
}),
|
|
208
|
+
wait: z.boolean(),
|
|
209
|
+
timeoutMs: z.number().int().positive(),
|
|
210
|
+
});
|
|
211
|
+
export type DispatchStartRequest = z.infer<typeof DispatchStartShape>;
|
|
212
|
+
|
|
213
|
+
/** Every start: the http kinds, a push, a credentialed command, a dispatch. */
|
|
214
|
+
export const StartRequestShape = z.union([HttpStartShape, PushStartShape, CommandStartShape, DispatchStartShape]);
|
|
147
215
|
export type StartRequest = z.infer<typeof StartRequestShape>;
|
|
148
216
|
|
|
149
217
|
/** The most bytes of a start's body the Effector reads (its `maxRequestBody`). A longer body
|
|
@@ -175,17 +243,68 @@ export const HttpOutputShape = z.union([
|
|
|
175
243
|
HttpResponseShape,
|
|
176
244
|
z.strictObject({ outcome: z.enum(["unreachable", "refused", "unknown"]), reason: z.string() }),
|
|
177
245
|
]);
|
|
246
|
+
/**
|
|
247
|
+
* The output of an `http` Effect: what the `onDone` of its state gets as `event.output`. Every
|
|
248
|
+
* outcome is output. A 4xx or 5xx status is output too. Route on `outcome`:
|
|
249
|
+
*
|
|
250
|
+
* - `response`: an answer came back. `status` is its HTTP status. `headers`, `contentType` and
|
|
251
|
+
* `size` tell about the body. `body` is there when the body is text: at most `maxBody` bytes
|
|
252
|
+
* (`truncated` is `true` when jr2 cut it). A body that is not text gives only its size and
|
|
253
|
+
* content type. jr2 replaces each value of an Effect secret that the Effector holds, and its
|
|
254
|
+
* usual encodings, with `[secret:<name>]`, in the body and the headers.
|
|
255
|
+
* - `unreachable`: the call did not go out, for example because the name did not resolve, or the
|
|
256
|
+
* connection or TLS failed. `reason` tells why.
|
|
257
|
+
* - `refused`: jr2 or the Effector refused the call, and nothing was sent. For example, a path
|
|
258
|
+
* segment that is `.`, `..` or empty, or a request that is too large. `reason` tells why.
|
|
259
|
+
* - `unknown`: the call can have gone out, but no answer came, or jr2 cannot tell after a restart.
|
|
260
|
+
* `reason` tells why. jr2 does not send it again unless it is marked `idempotent`.
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* ```ts
|
|
264
|
+
* import { fail, http, jr2Setup, type HttpOutput } from "@jr2/orchestrator";
|
|
265
|
+
*
|
|
266
|
+
* const ok = (output: HttpOutput): boolean => output.outcome === "response" && output.status < 300;
|
|
267
|
+
*
|
|
268
|
+
* export const machine = jr2Setup({
|
|
269
|
+
* actors: { health: http({ method: "GET", url: "https://app.example.com/healthz" }), fail: fail() },
|
|
270
|
+
* }).createMachine({
|
|
271
|
+
* id: "health",
|
|
272
|
+
* initial: "checking",
|
|
273
|
+
* states: {
|
|
274
|
+
* checking: {
|
|
275
|
+
* invoke: {
|
|
276
|
+
* src: "health",
|
|
277
|
+
* onDone: [
|
|
278
|
+
* { guard: ({ event }) => ok(event.output), target: "healthy" },
|
|
279
|
+
* { target: "unhealthy" },
|
|
280
|
+
* ],
|
|
281
|
+
* },
|
|
282
|
+
* },
|
|
283
|
+
* healthy: { type: "final" },
|
|
284
|
+
* // Not a final state: the run ends `failed`, and `jr2 run` exits 1.
|
|
285
|
+
* unhealthy: { invoke: { src: "fail", input: { reason: "the app is not healthy" } } },
|
|
286
|
+
* },
|
|
287
|
+
* });
|
|
288
|
+
* ```
|
|
289
|
+
*
|
|
290
|
+
* @see docs/concepts/effects.md
|
|
291
|
+
* @see docs/guides/run-a-command-or-call-a-url.md
|
|
292
|
+
* @see docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md
|
|
293
|
+
*/
|
|
178
294
|
export type HttpOutput = z.infer<typeof HttpOutputShape>;
|
|
179
295
|
|
|
180
296
|
/**
|
|
181
297
|
* A `push` Effect's output (ADR-0067). `pushed`: the published commit, its source (the Agent's
|
|
182
298
|
* commit), what the remote held before, how the new commits were found after a signed push
|
|
183
|
-
* (`ancestry | pair | cherry | force`), `unchanged` when the remote held it already, `
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
299
|
+
* (`ancestry | pair | cherry | force`), `unchanged` when the remote held it already, `newCommits`
|
|
300
|
+
* (how many commits the published commit adds to what jr2 found held, the remote's branch and the
|
|
301
|
+
* Repo cache; 0 when jr2 found the published commit already held, which the Agent reads in words,
|
|
302
|
+
* menu.ts), `squashed` when the published
|
|
303
|
+
* commit is one squashed commit (`signed | unsigned`; absent for signed copies; what the published
|
|
304
|
+
* commit is, an earlier push's included, never only this push's options), and — when the published
|
|
305
|
+
* commit is not the Agent's own — the published commits as a bundle for the sync. `rejected`: a
|
|
306
|
+
* stage and git's words. `diverged`: the published commit is not in the Agent's history and
|
|
307
|
+
* nothing paired it; nothing was pushed.
|
|
189
308
|
*/
|
|
190
309
|
export const PushOutputShape = z.union([
|
|
191
310
|
z.strictObject({
|
|
@@ -195,6 +314,9 @@ export const PushOutputShape = z.union([
|
|
|
195
314
|
previous: z.string().optional(),
|
|
196
315
|
via: z.string().optional(),
|
|
197
316
|
unchanged: z.boolean().optional(),
|
|
317
|
+
// Optional: a live Sandbox keeps the runtime it started with (ADR-0037), so its older Effector
|
|
318
|
+
// sends no count after `jr2 up`. With no count the Harness makes no claim (menu.ts).
|
|
319
|
+
newCommits: z.number().int().nonnegative().optional(),
|
|
198
320
|
squashed: z.enum(["signed", "unsigned"]).optional(),
|
|
199
321
|
bundle: z.string().optional(),
|
|
200
322
|
}),
|
|
@@ -207,6 +329,7 @@ export const PushOutputShape = z.union([
|
|
|
207
329
|
z.strictObject({ outcome: z.literal("diverged"), reason: z.string(), published: z.string(), source: z.string() }),
|
|
208
330
|
z.strictObject({ outcome: z.literal("unknown"), reason: z.string() }),
|
|
209
331
|
]);
|
|
332
|
+
/** @internal */
|
|
210
333
|
export type PushWireOutput = z.infer<typeof PushOutputShape>;
|
|
211
334
|
|
|
212
335
|
/** A credentialed command's output, as the command kind's (ADR-0067), and `dirty` when the
|
|
@@ -233,6 +356,29 @@ export const CommandOutputShape = z.union([
|
|
|
233
356
|
]);
|
|
234
357
|
export type CommandWireOutput = z.infer<typeof CommandOutputShape>;
|
|
235
358
|
|
|
359
|
+
/**
|
|
360
|
+
* A `dispatch` Effect's output on the wire (ADR-0071): its own outcome set. `settled.status` is the
|
|
361
|
+
* callee's status as the door served it, in the store's words, and `output` the callee Machine's,
|
|
362
|
+
* passed through scrubbed; `refused` is the door's status and its report, told from an http
|
|
363
|
+
* `refused` (which has a `reason`) by its fields.
|
|
364
|
+
*/
|
|
365
|
+
export const DispatchOutputShape = z.discriminatedUnion("outcome", [
|
|
366
|
+
z.strictObject({ outcome: z.literal("started"), run: z.string() }),
|
|
367
|
+
z.strictObject({
|
|
368
|
+
outcome: z.literal("settled"),
|
|
369
|
+
run: z.string(),
|
|
370
|
+
// The store's words (ADR-0025) and the timeout's `live`, as `DispatchOutput` types them for the
|
|
371
|
+
// Machine: a door that serves another word (another Kit version) is a malformed answer, the
|
|
372
|
+
// invoke's error, never a status the Machine's type does not name (ADR-0071).
|
|
373
|
+
status: z.enum(["done", "failed", "error", "cancelled", "lost", "drifted", "live"]),
|
|
374
|
+
output: z.unknown().optional(),
|
|
375
|
+
}),
|
|
376
|
+
z.strictObject({ outcome: z.literal("refused"), status: z.number().int(), report: z.array(z.string()) }),
|
|
377
|
+
z.strictObject({ outcome: z.literal("unreachable"), reason: z.string() }),
|
|
378
|
+
z.strictObject({ outcome: z.literal("unknown"), reason: z.string() }),
|
|
379
|
+
]);
|
|
380
|
+
export type DispatchWireOutput = z.infer<typeof DispatchOutputShape>;
|
|
381
|
+
|
|
236
382
|
/** The replica that holds an Effect, and where to poll it. */
|
|
237
383
|
export const ReplicaShape = z.strictObject({ name: z.string(), address: z.string() });
|
|
238
384
|
export type Replica = z.infer<typeof ReplicaShape>;
|
|
@@ -245,7 +391,10 @@ export const EffectStateShape = z.strictObject({
|
|
|
245
391
|
/** The commit a push or a credentialed command runs against, read when it started: the
|
|
246
392
|
* Orchestrator writes it to the ledger's intent row. */
|
|
247
393
|
pinned: z.string().optional(),
|
|
248
|
-
|
|
394
|
+
/** The run a dispatch started, once the door answered and while its wait runs: the Orchestrator
|
|
395
|
+
* writes it to the ledger's intent row, so the caller names its callee during the wait (ADR-0071). */
|
|
396
|
+
run: z.string().optional(),
|
|
397
|
+
output: z.union([HttpOutputShape, PushOutputShape, CommandOutputShape, DispatchOutputShape]).optional(),
|
|
249
398
|
});
|
|
250
399
|
export type EffectState = z.infer<typeof EffectStateShape>;
|
|
251
400
|
|
package/src/fail.ts
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// The `fail()` Part (ADR-0073): the end that a Machine chooses for work that did not succeed — a
|
|
2
|
+
// Failure (GLOSSARY.md). It is a Part and not an Effect, because it does no work outside the
|
|
3
|
+
// Machine: its actor throws an error that carries a brand, and xstate sends that error up through
|
|
4
|
+
// each parent Machine by its own rules. An `onError` on the way catches it; `isFailure` tells it
|
|
5
|
+
// from a defect. When no state catches it, the run host ends the run `failed`.
|
|
6
|
+
//
|
|
7
|
+
// The brand is a registry symbol (`Symbol.for`), not a module symbol, for the reason the walk is
|
|
8
|
+
// structural (parts.ts): an Instance resolves its OWN `@jr2/orchestrator`, so the copy that throws
|
|
9
|
+
// and the copy that reads the error (the run host, the CLI's walk) can be two module instances. The
|
|
10
|
+
// brand is not exported: an author makes a Failure only with `fail()`.
|
|
11
|
+
|
|
12
|
+
import { fromPromise, type PromiseActorLogic } from "xstate";
|
|
13
|
+
|
|
14
|
+
const FAILURE: unique symbol = Symbol.for("jr2.failure") as never;
|
|
15
|
+
const FAIL_SLOT: unique symbol = Symbol.for("jr2.fail.slot") as never;
|
|
16
|
+
|
|
17
|
+
/** What a Failure carries: `reason`, text for a person, and `detail`, optional JSON for a machine
|
|
18
|
+
* reader. The input of `fail()` and the output of a run that ends `failed`. */
|
|
19
|
+
export type FailureInput = { reason: string; detail?: unknown };
|
|
20
|
+
|
|
21
|
+
/** A Failure as `isFailure` narrows an error to it: the payload's fields, on the error itself. */
|
|
22
|
+
export type Failure = { readonly reason: string; readonly detail?: unknown };
|
|
23
|
+
|
|
24
|
+
/** Make the branded error. Only this module makes one. */
|
|
25
|
+
function failure(input: FailureInput): Error & Failure {
|
|
26
|
+
const error = new Error(input.reason) as Error & Failure & { [FAILURE]: true };
|
|
27
|
+
error.name = "Failure";
|
|
28
|
+
Object.defineProperty(error, "reason", { value: input.reason, enumerable: true });
|
|
29
|
+
if (input.detail !== undefined) Object.defineProperty(error, "detail", { value: input.detail, enumerable: true });
|
|
30
|
+
Object.defineProperty(error, FAILURE, { value: true });
|
|
31
|
+
return error;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Refuse an input that is not a Failure's. A defect of the Machine, not a Failure: it ends the run
|
|
35
|
+
* `error`, with this text as the fault. */
|
|
36
|
+
function checked(raw: unknown): FailureInput {
|
|
37
|
+
const input = raw as Partial<FailureInput> | undefined;
|
|
38
|
+
if (typeof input !== "object" || input === null || typeof input.reason !== "string" || input.reason === "") {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`fail: the input must be { reason: string; detail?: unknown } with a reason that is not empty (got ${describe(raw)}) — ` +
|
|
41
|
+
`invoke: { src: "fail", input: { reason: "the tests failed" } } (ADR-0073)`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
return input.detail === undefined ? { reason: input.reason } : { reason: input.reason, detail: input.detail };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function describe(value: unknown): string {
|
|
48
|
+
try {
|
|
49
|
+
return JSON.stringify(value) ?? String(value);
|
|
50
|
+
} catch {
|
|
51
|
+
return String(value);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The actor logic that throws. Unbranded: what `workspace()` invokes to send a caught Failure up
|
|
56
|
+
* again, which is the wrapper's mechanism and no state of the author's that fails the run. */
|
|
57
|
+
export const failLogic: PromiseActorLogic<never, FailureInput> = fromPromise<never, FailureInput>(async ({ input }) => {
|
|
58
|
+
throw failure(checked(input));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
/** The logic `fail()` returns: {@link failLogic} with the brand the walk reads. */
|
|
62
|
+
export type FailSlot = PromiseActorLogic<never, FailureInput> & { readonly [FAIL_SLOT]: true };
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Fail the run: the Part a Machine invokes to end work that did not succeed. Put it in `actors`
|
|
66
|
+
* under a name, and invoke it from any state, at any depth: `actors: { fail: fail() }`, then
|
|
67
|
+
* `invoke: { src: "fail", input: { reason } }`.
|
|
68
|
+
*
|
|
69
|
+
* The input is `{ reason: string; detail?: unknown }`. `reason` is text for a person, and it must
|
|
70
|
+
* not be empty. `detail` is optional JSON for a machine reader.
|
|
71
|
+
*
|
|
72
|
+
* The Failure goes up from the state that invokes `fail`, through each parent Machine, as an error
|
|
73
|
+
* does. An `onError` on the way catches it; {@link isFailure} tells it from a defect. When no state
|
|
74
|
+
* catches it, the run ends `failed`: its output is `{ reason, detail }`, and `jr2 run` exits 1. A
|
|
75
|
+
* `workspace()` that a Failure goes through deletes its Sandbox first.
|
|
76
|
+
*
|
|
77
|
+
* To fail, invoke `fail`. Do not throw from a promise to fail: a throw is a defect, and the run
|
|
78
|
+
* ends `error`. A final state means `done`: do not use one for work that did not succeed. To keep
|
|
79
|
+
* the work for a person to read, wait at a Gate before the state that fails.
|
|
80
|
+
*
|
|
81
|
+
* It refuses an input that is not a Failure's, and the run ends `error`: ``fail: the input must be
|
|
82
|
+
* { reason: string; detail?: unknown } with a reason that is not empty (got <value>) — invoke:
|
|
83
|
+
* { src: "fail", input: { reason: "the tests failed" } } (ADR-0073)``.
|
|
84
|
+
*
|
|
85
|
+
* @returns The actor logic of the slot.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* import { z } from "zod";
|
|
90
|
+
* import { command, fail, jr2Setup, workspace, type Workspaced } from "@jr2/orchestrator";
|
|
91
|
+
*
|
|
92
|
+
* const runInput = z.object({ branch: z.string() });
|
|
93
|
+
*
|
|
94
|
+
* const body = jr2Setup({
|
|
95
|
+
* types: {} as { input: Workspaced<z.infer<typeof runInput>, "app"> },
|
|
96
|
+
* actors: {
|
|
97
|
+
* tests: command(["npm", "test"], { timeout: "15m" }),
|
|
98
|
+
* fail: fail(),
|
|
99
|
+
* },
|
|
100
|
+
* }).createMachine({
|
|
101
|
+
* id: "check",
|
|
102
|
+
* on: { "workspace.lost": ".lost" },
|
|
103
|
+
* initial: "testing",
|
|
104
|
+
* states: {
|
|
105
|
+
* testing: {
|
|
106
|
+
* invoke: {
|
|
107
|
+
* src: "tests",
|
|
108
|
+
* onDone: [
|
|
109
|
+
* {
|
|
110
|
+
* guard: ({ event }) => event.output.outcome === "exited" && event.output.code === 0,
|
|
111
|
+
* target: "passed",
|
|
112
|
+
* },
|
|
113
|
+
* { target: "failing" },
|
|
114
|
+
* ],
|
|
115
|
+
* },
|
|
116
|
+
* },
|
|
117
|
+
* // Not a final state: the run ends `failed`, and the Workspace deletes the Sandbox.
|
|
118
|
+
* failing: { invoke: { src: "fail", input: { reason: "npm test did not pass" } } },
|
|
119
|
+
* passed: { type: "final" },
|
|
120
|
+
* lost: { invoke: { src: "fail", input: { reason: "the Sandbox is gone" } } },
|
|
121
|
+
* },
|
|
122
|
+
* });
|
|
123
|
+
*
|
|
124
|
+
* export const machine = workspace(body, {
|
|
125
|
+
* input: runInput,
|
|
126
|
+
* repos: { app: "https://github.com/you/app.git" },
|
|
127
|
+
* spec: ({ input }) => ({ branch: input.branch }),
|
|
128
|
+
* });
|
|
129
|
+
* ```
|
|
130
|
+
*
|
|
131
|
+
* @see docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md
|
|
132
|
+
*/
|
|
133
|
+
export function fail(): FailSlot {
|
|
134
|
+
// Logic of its own per slot, so the brand is on this slot's logic and on no other's.
|
|
135
|
+
const logic = fromPromise<never, FailureInput>(async ({ input }) => {
|
|
136
|
+
throw failure(checked(input));
|
|
137
|
+
});
|
|
138
|
+
return Object.defineProperty(logic, FAIL_SLOT, { value: true }) as FailSlot;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Is this actor logic a `fail()` slot? A test on the brand, like `isAgent`: what the walk reads to
|
|
142
|
+
* list each state that can fail the run (ADR-0073). */
|
|
143
|
+
export function isFailSlot(logic: unknown): logic is FailSlot {
|
|
144
|
+
return typeof logic === "object" && logic !== null && (logic as { [FAIL_SLOT]?: unknown })[FAIL_SLOT] === true;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Is this error a Failure, which a state invoked `fail` to make? A defect — any other error — is
|
|
149
|
+
* not. Use it in the guard of an `onError` to catch a Failure and let a defect go up, and read the
|
|
150
|
+
* Failure's `reason` and `detail` from the error it narrows.
|
|
151
|
+
*
|
|
152
|
+
* @param error - The error of an error event: `event.error` in an `onError`.
|
|
153
|
+
* @returns True for a Failure, with `error` narrowed to `{ reason, detail? }`.
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* import { z } from "zod";
|
|
158
|
+
* import { defineEvent, fail, isFailure, jr2Setup } from "@jr2/orchestrator";
|
|
159
|
+
*
|
|
160
|
+
* const retry = defineEvent({ name: "retry", description: "Try the plan again.", input: z.object({}) });
|
|
161
|
+
*
|
|
162
|
+
* const attempt = jr2Setup({ actors: { fail: fail() } }).createMachine({
|
|
163
|
+
* id: "attempt",
|
|
164
|
+
* initial: "stuck",
|
|
165
|
+
* states: { stuck: { invoke: { src: "fail", input: { reason: "no plan fits the ticket" } } } },
|
|
166
|
+
* });
|
|
167
|
+
*
|
|
168
|
+
* export const machine = jr2Setup({ events: [retry], actors: { attempt } }).createMachine({
|
|
169
|
+
* id: "planner",
|
|
170
|
+
* initial: "planning",
|
|
171
|
+
* states: {
|
|
172
|
+
* planning: {
|
|
173
|
+
* invoke: {
|
|
174
|
+
* src: "attempt",
|
|
175
|
+
* onDone: "planned",
|
|
176
|
+
* // A Failure waits for a person; a defect has no route, so it goes up and the run ends `error`.
|
|
177
|
+
* onError: { guard: ({ event }) => isFailure(event.error), target: "asking" },
|
|
178
|
+
* },
|
|
179
|
+
* },
|
|
180
|
+
* asking: { invoke: { src: "gate" }, on: { retry: "planning" } },
|
|
181
|
+
* planned: { type: "final" },
|
|
182
|
+
* },
|
|
183
|
+
* });
|
|
184
|
+
* ```
|
|
185
|
+
*
|
|
186
|
+
* @see docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md
|
|
187
|
+
*/
|
|
188
|
+
export function isFailure(error: unknown): error is Failure {
|
|
189
|
+
return (
|
|
190
|
+
typeof error === "object" &&
|
|
191
|
+
error !== null &&
|
|
192
|
+
(error as { [FAILURE]?: unknown })[FAILURE] === true &&
|
|
193
|
+
typeof (error as { reason?: unknown }).reason === "string"
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** The name of the guard that every Chart has (ADR-0070, ADR-0073): {@link isFailure} on the
|
|
198
|
+
* event's `error`, so a Chart's `onError` can tell a Failure from a defect. It is a predicate and
|
|
199
|
+
* gives no capability, so it is never lent, as `jr2.sentBy` is not. */
|
|
200
|
+
export const FAILURE_GUARD = "jr2.failure";
|
|
201
|
+
|
|
202
|
+
/** The implementation of {@link FAILURE_GUARD}: true when the event's `error` is a Failure. */
|
|
203
|
+
export const failureGuard = ({ event }: { event: { error?: unknown } }): boolean => isFailure(event.error);
|
|
204
|
+
|
|
205
|
+
/** A Failure's payload as plain JSON data: what a failed run's output is, and what `workspace()`
|
|
206
|
+
* keeps to send it up again. */
|
|
207
|
+
export function failurePayload(error: Failure): FailureInput {
|
|
208
|
+
return error.detail === undefined ? { reason: error.reason } : { reason: error.reason, detail: error.detail };
|
|
209
|
+
}
|