@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/effect.ts
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
// route on, the input kept in the persisted snapshot, a `src` typo as a compile error, and
|
|
5
5
|
// `.provide()` as the unit-test seam.
|
|
6
6
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
7
|
+
// The kinds: `command`, run in the Harness container of the enclosing Workspace's Sandbox (the
|
|
8
|
+
// Harness's `/exec/:id`, ADR-0067), here; `http`, run in the Instance Effector (http-effect.ts,
|
|
9
|
+
// ADR-0068); `push`, in the Sandbox Effector (sandbox-effect.ts); and `dispatch`, which starts a run
|
|
10
|
+
// of a registered Workflow, in the Orchestrator's own process or, toward another Orchestrator, in
|
|
11
|
+
// the Instance Effector (dispatch-effect.ts, ADR-0071). What this module adds to xstate is what makes an Effect safe to
|
|
10
12
|
// restart:
|
|
11
13
|
//
|
|
12
14
|
// - THE LEDGER. The run host keeps one row per Effect, in the run's blob beside the snapshot
|
|
@@ -53,8 +55,58 @@ import {
|
|
|
53
55
|
|
|
54
56
|
// --- Parts: what `jr2 up` and the Machine doc read off a slot, without running anything ---------
|
|
55
57
|
|
|
56
|
-
|
|
58
|
+
// ONE argument, whatever its bytes: never shell text.
|
|
59
|
+
/** One entry of a command's argv that the input of the Effect fills, as `arg("name")` makes it. It
|
|
60
|
+
* is one argument, whatever its text. No shell reads it. */
|
|
57
61
|
export type Arg<N extends string = string> = { readonly arg: N };
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Mark one argv entry of a `command` that the input of the Effect fills. `arg("file")` is one
|
|
65
|
+
* argument: the value of the `file` key of the input, whatever its characters. No shell reads it.
|
|
66
|
+
*
|
|
67
|
+
* The input of the Effect then has a string key for each `arg`. Give it in the `input` of the
|
|
68
|
+
* `invoke`, or use `fromPick` to give it from a pick.
|
|
69
|
+
*
|
|
70
|
+
* When it starts, with no string in the input for an entry, the Effect fails (its `onError`
|
|
71
|
+
* route): `command "<slot>": argv slot <<name>> has no string value in its input (got <type>) —
|
|
72
|
+
* map it in the invoke's input`. The angle brackets around the name are in the text.
|
|
73
|
+
*
|
|
74
|
+
* @param name - The key of the input that fills this entry.
|
|
75
|
+
* @returns The argv entry, for the argv list of `command`.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* import { arg, command, fail, jr2Setup, workspace, type Workspaced } from "@jr2/orchestrator";
|
|
80
|
+
*
|
|
81
|
+
* type Input = Workspaced<{ file: string }, "app">;
|
|
82
|
+
*
|
|
83
|
+
* const body = jr2Setup({
|
|
84
|
+
* types: {} as { context: Input; input: Input },
|
|
85
|
+
* actors: { lint: command(["npx", "eslint", arg("file")]), fail: fail() },
|
|
86
|
+
* }).createMachine({
|
|
87
|
+
* id: "lint",
|
|
88
|
+
* context: ({ input }) => input,
|
|
89
|
+
* on: { "workspace.lost": ".lost" },
|
|
90
|
+
* initial: "lint",
|
|
91
|
+
* states: {
|
|
92
|
+
* lint: {
|
|
93
|
+
* invoke: { src: "lint", input: ({ context }) => ({ file: context.file }), onDone: "done" },
|
|
94
|
+
* },
|
|
95
|
+
* done: { type: "final" },
|
|
96
|
+
* lost: { invoke: { src: "fail", input: { reason: "the Sandbox is gone" } } },
|
|
97
|
+
* },
|
|
98
|
+
* });
|
|
99
|
+
*
|
|
100
|
+
* export const machine = workspace(body, {
|
|
101
|
+
* repos: { app: { url: "https://github.com/you/app.git" } },
|
|
102
|
+
* spec: () => ({ branch: "lint" }),
|
|
103
|
+
* });
|
|
104
|
+
* ```
|
|
105
|
+
*
|
|
106
|
+
* @see docs/concepts/effects.md
|
|
107
|
+
* @see docs/guides/run-a-command-or-call-a-url.md
|
|
108
|
+
* @see docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md
|
|
109
|
+
*/
|
|
58
110
|
export const arg = <const N extends string>(name: N): Arg<N> => ({ arg: name });
|
|
59
111
|
|
|
60
112
|
/** A `command` Effect's plain data. */
|
|
@@ -103,10 +155,10 @@ export type HttpParts = {
|
|
|
103
155
|
readonly maxBody: number;
|
|
104
156
|
/** Marked by the author: a restore that cannot find the Effect sends it again. */
|
|
105
157
|
readonly idempotent: boolean;
|
|
106
|
-
/** The body is a Worktree file (`body: file(...)`, ADR-0067): the Effect runs in the Sandbox
|
|
107
|
-
* of the Workspace whose body invokes it. `slot` is the Repo Slot whose Worktree holds
|
|
108
|
-
* needed only where the Workspace carries several. Absent: a json, text or no body, in
|
|
109
|
-
* Instance Effector. */
|
|
158
|
+
/** The body is a Worktree file (`body: file(...)`, ADR-0067): the Effect runs in the Sandbox
|
|
159
|
+
* Effector of the Workspace whose body invokes it. `slot` is the Repo Slot whose Worktree holds
|
|
160
|
+
* the file — needed only where the Workspace carries several. Absent: a json, text or no body, in
|
|
161
|
+
* the Instance Effector. */
|
|
110
162
|
readonly file?: { readonly slot?: string; readonly contentType?: string };
|
|
111
163
|
};
|
|
112
164
|
|
|
@@ -128,6 +180,33 @@ export type PushParts = {
|
|
|
128
180
|
readonly inputKeys: readonly string[];
|
|
129
181
|
};
|
|
130
182
|
|
|
183
|
+
/** A `dispatch` Effect's target: another Orchestrator's origin and the secret that holds its
|
|
184
|
+
* Instance token, by name (ADR-0071). */
|
|
185
|
+
export type DispatchTarget = { readonly url: string; readonly token: string };
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* A `dispatch` Effect's plain data (ADR-0071): the Workflow it starts a run of, as `jr2 run` names
|
|
189
|
+
* it (`review`, or `<wrapper>/<name>` for a named Chart), whether it waits for that run to end, the
|
|
190
|
+
* bound on the start (or the wait), and the target. No target: it runs in the Orchestrator's own
|
|
191
|
+
* process, through the run host, with no wire and no token. A target: it runs in the Instance
|
|
192
|
+
* Effector, which holds the token as an Effect secret.
|
|
193
|
+
*/
|
|
194
|
+
export type DispatchParts = {
|
|
195
|
+
readonly kind: "dispatch";
|
|
196
|
+
readonly workflow: string;
|
|
197
|
+
readonly wait: boolean;
|
|
198
|
+
readonly timeoutMs: number;
|
|
199
|
+
readonly target?: DispatchTarget;
|
|
200
|
+
/** The keys of the slot's input schema: what a pick param may fill. Undefined with no schema:
|
|
201
|
+
* the callee's door alone checks the body, so the load walk reads everything (ADR-0071). */
|
|
202
|
+
readonly inputKeys: readonly string[] | undefined;
|
|
203
|
+
/** `[target.token]`, or none in-process: what places the token (ADR-0068). */
|
|
204
|
+
readonly secrets: readonly string[];
|
|
205
|
+
/** Always false: a target's token is never `secret(open)` — the whole target is the Open part
|
|
206
|
+
* (ADR-0071). */
|
|
207
|
+
readonly openSecret: boolean;
|
|
208
|
+
};
|
|
209
|
+
|
|
131
210
|
/**
|
|
132
211
|
* An Effect whose url or argv a package left Open (ADR-0054): the same parts with the sentinel in
|
|
133
212
|
* that one place. The kind, the placement, the timeout and the secrets the other parts name are
|
|
@@ -140,21 +219,36 @@ export type OpenHttpParts = Omit<HttpParts, "url" | "target"> & {
|
|
|
140
219
|
readonly target: typeof open;
|
|
141
220
|
};
|
|
142
221
|
|
|
222
|
+
/** A `dispatch` whose target a package left Open (ADR-0071): the composer binds the url and the
|
|
223
|
+
* token together, once. */
|
|
224
|
+
export type OpenDispatchParts = Omit<DispatchParts, "target"> & { readonly target: typeof open };
|
|
225
|
+
|
|
143
226
|
/** Every kind's parts, bound or with an Open part. */
|
|
144
|
-
export type EffectParts =
|
|
227
|
+
export type EffectParts =
|
|
228
|
+
| CommandParts
|
|
229
|
+
| HttpParts
|
|
230
|
+
| PushParts
|
|
231
|
+
| OpenCommandParts
|
|
232
|
+
| OpenHttpParts
|
|
233
|
+
| DispatchParts
|
|
234
|
+
| OpenDispatchParts;
|
|
145
235
|
|
|
146
236
|
/** The kinds, as their parts name them. */
|
|
147
|
-
export const EFFECT_KINDS: ReadonlySet<string> = new Set(["command", "http", "push"]);
|
|
237
|
+
export const EFFECT_KINDS: ReadonlySet<string> = new Set(["command", "http", "push", "dispatch"]);
|
|
148
238
|
|
|
149
239
|
/**
|
|
150
240
|
* Where an Effect runs, derived from its kind and what it names, never authored (ADR-0067,
|
|
151
241
|
* ADR-0068): a `command` that names no secret in the Harness container of its Workspace; an
|
|
152
242
|
* `http` Effect with no Worktree file in the Instance Effector; an `http` whose body is a Worktree
|
|
153
|
-
* file, a `push` and a `command` that names a secret in the Sandbox Effector of its Workspace
|
|
243
|
+
* file, a `push` and a `command` that names a secret in the Sandbox Effector of its Workspace; a
|
|
244
|
+
* `dispatch` with no target in the Orchestrator's own process, and one with a target (an Open one
|
|
245
|
+
* included: it has one once bound) in the Instance Effector (ADR-0071).
|
|
154
246
|
*/
|
|
155
|
-
export type Placement = "harness" | "instance" | "sandbox";
|
|
247
|
+
export type Placement = "harness" | "instance" | "sandbox" | "orchestrator";
|
|
156
248
|
export function placementOf(parts: EffectParts): Placement {
|
|
157
249
|
switch (parts.kind) {
|
|
250
|
+
case "dispatch":
|
|
251
|
+
return parts.target === undefined ? "orchestrator" : "instance";
|
|
158
252
|
case "http":
|
|
159
253
|
return parts.file ? "sandbox" : "instance";
|
|
160
254
|
case "push":
|
|
@@ -192,9 +286,12 @@ export function targetText(parts: { readonly target: HttpParts["target"] | typeo
|
|
|
192
286
|
}
|
|
193
287
|
|
|
194
288
|
/** The keys an Effect's input has, read off its parts: a command's argv slot names (and `sha` with
|
|
195
|
-
* `pin`); an http Effect's input schema keys; a push's `sha` and `message
|
|
196
|
-
* argv: its slots are the composer's, unknown until
|
|
289
|
+
* `pin`); an http Effect's input schema keys; a push's `sha` and `message`; a dispatch's input
|
|
290
|
+
* schema keys. Undefined for an Open argv: its slots are the composer's, unknown until
|
|
291
|
+
* `customize()` binds it (ADR-0054); and for a dispatch with no input schema: the callee's door is
|
|
292
|
+
* the only check, so nothing here says what it reads (ADR-0071). */
|
|
197
293
|
export function inputKeysOf(parts: EffectParts): string[] | undefined {
|
|
294
|
+
if (parts.kind === "dispatch") return parts.inputKeys === undefined ? undefined : [...parts.inputKeys];
|
|
198
295
|
if (parts.kind !== "command") return [...parts.inputKeys];
|
|
199
296
|
if (parts.argv === open) return undefined;
|
|
200
297
|
return [...parts.argv.flatMap((a) => (typeof a === "string" ? [] : [a.arg])), ...(parts.pin ? ["sha"] : [])];
|
|
@@ -202,14 +299,111 @@ export function inputKeysOf(parts: EffectParts): string[] | undefined {
|
|
|
202
299
|
|
|
203
300
|
// --- Output -------------------------------------------------------------------------------------
|
|
204
301
|
|
|
205
|
-
/** The
|
|
206
|
-
*
|
|
302
|
+
/** The output `{ outcome: "unknown", reason }`: the Effect may have acted, and jr2 cannot tell.
|
|
303
|
+
* For example, the Harness lost the exec, or its state exited while it ran. Route on it. jr2 does
|
|
304
|
+
* not run the Effect again. */
|
|
207
305
|
export type Unknown = { outcome: "unknown"; reason: string };
|
|
208
306
|
|
|
209
|
-
|
|
210
|
-
|
|
307
|
+
// `dirty` is a credentialed command's alone (ADR-0067): it runs in the Sandbox Effector against
|
|
308
|
+
// the pinned commit, never the live Worktree.
|
|
309
|
+
/**
|
|
310
|
+
* The output of a `command` Effect: what the `onDone` of its state gets as `event.output`. Every
|
|
311
|
+
* outcome is output. A failed program is output too. Route on `outcome`:
|
|
312
|
+
*
|
|
313
|
+
* - `exited`: the program ended. `code` is its exit code. A program that the container does not
|
|
314
|
+
* have is `exited` with code 127.
|
|
315
|
+
* - `timeout`: the command ran past its `timeout`, and jr2 killed it.
|
|
316
|
+
* - `killed`: a signal ended it. `signal` names the signal.
|
|
317
|
+
* - `unknown`: jr2 cannot tell what the command did, for example because the Harness restarted
|
|
318
|
+
* while it ran. `reason` tells why. jr2 does not run it again.
|
|
319
|
+
*
|
|
320
|
+
* `tail` is the end of what the command wrote, at most the `tail` option of `command` in
|
|
321
|
+
* characters. `dirty` is `true` when a command that names an Effect secret ran against a commit,
|
|
322
|
+
* and the Worktree held changes that it did not see.
|
|
323
|
+
*
|
|
324
|
+
* @example
|
|
325
|
+
* ```ts
|
|
326
|
+
* import {
|
|
327
|
+
* command,
|
|
328
|
+
* type CommandOutput,
|
|
329
|
+
* fail,
|
|
330
|
+
* jr2Setup,
|
|
331
|
+
* workspace,
|
|
332
|
+
* type Workspaced,
|
|
333
|
+
* } from "@jr2/orchestrator";
|
|
334
|
+
*
|
|
335
|
+
* type Input = Workspaced<{}, "app">;
|
|
336
|
+
*
|
|
337
|
+
* const passed = (output: CommandOutput): boolean =>
|
|
338
|
+
* output.outcome === "exited" && output.code === 0;
|
|
339
|
+
*
|
|
340
|
+
* const body = jr2Setup({
|
|
341
|
+
* types: {} as { context: Input; input: Input },
|
|
342
|
+
* actors: { tests: command(["npm", "test"], { timeout: "15m" }), fail: fail() },
|
|
343
|
+
* }).createMachine({
|
|
344
|
+
* id: "tests",
|
|
345
|
+
* context: ({ input }) => input,
|
|
346
|
+
* on: { "workspace.lost": ".lost" },
|
|
347
|
+
* initial: "testing",
|
|
348
|
+
* states: {
|
|
349
|
+
* testing: {
|
|
350
|
+
* invoke: {
|
|
351
|
+
* src: "tests",
|
|
352
|
+
* onDone: [
|
|
353
|
+
* { guard: ({ event }) => passed(event.output), target: "green" },
|
|
354
|
+
* { target: "red" },
|
|
355
|
+
* ],
|
|
356
|
+
* },
|
|
357
|
+
* },
|
|
358
|
+
* green: { type: "final" },
|
|
359
|
+
* // Not a final state: a final state means `done`, and the run ends `failed` here.
|
|
360
|
+
* red: { invoke: { src: "fail", input: { reason: "npm test did not pass" } } },
|
|
361
|
+
* lost: { invoke: { src: "fail", input: { reason: "the Sandbox is gone" } } },
|
|
362
|
+
* },
|
|
363
|
+
* });
|
|
364
|
+
*
|
|
365
|
+
* export const machine = workspace(body, {
|
|
366
|
+
* repos: { app: { url: "https://github.com/you/app.git" } },
|
|
367
|
+
* spec: () => ({ branch: "tests" }),
|
|
368
|
+
* });
|
|
369
|
+
* ```
|
|
370
|
+
*
|
|
371
|
+
* @see docs/concepts/effects.md
|
|
372
|
+
* @see docs/guides/run-a-command-or-call-a-url.md
|
|
373
|
+
* @see docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md
|
|
374
|
+
*/
|
|
211
375
|
export type CommandOutput = (ExecOutcome & { dirty?: boolean }) | Unknown;
|
|
212
376
|
|
|
377
|
+
// Reach is output, as an http Effect's is (ADR-0067): a body a model wrote can be fixed through
|
|
378
|
+
// the Chain, so a refusal is output too.
|
|
379
|
+
/**
|
|
380
|
+
* The output of a `dispatch` Effect. Route on `outcome`:
|
|
381
|
+
*
|
|
382
|
+
* - `started`: the Orchestrator accepted the new run. `run` is its id.
|
|
383
|
+
* - `settled`: with `wait`, the new run ended. `status` is `done`, `failed`, `error`, `cancelled`,
|
|
384
|
+
* `lost` or `drifted`, and `output` is the output of its Machine. For `failed`, `output` is the
|
|
385
|
+
* Failure, `{ reason, detail? }`. `status` is `live` when the timeout came first, and the run
|
|
386
|
+
* continues.
|
|
387
|
+
* - `refused`: the Orchestrator started no run. `status` is the HTTP status: 400 for an input that
|
|
388
|
+
* the Workflow does not take, 404 for a Workflow that it does not have, 409 for a named Chart that
|
|
389
|
+
* cannot run now, 422 for a Chart that it refuses. `report` holds the lines of the refusal.
|
|
390
|
+
* - `unreachable`: the Orchestrator of the target did not answer. No run started.
|
|
391
|
+
* - `unknown`: jr2 cannot tell if a run started.
|
|
392
|
+
*
|
|
393
|
+
* @see docs/concepts/effects.md
|
|
394
|
+
*/
|
|
395
|
+
export type DispatchOutput =
|
|
396
|
+
| { outcome: "started"; run: string }
|
|
397
|
+
| {
|
|
398
|
+
outcome: "settled";
|
|
399
|
+
run: string;
|
|
400
|
+
status: "done" | "failed" | "error" | "cancelled" | "lost" | "drifted" | "live";
|
|
401
|
+
output?: unknown;
|
|
402
|
+
}
|
|
403
|
+
| { outcome: "refused"; status: number; report: string[] }
|
|
404
|
+
| { outcome: "unreachable"; reason: string }
|
|
405
|
+
| Unknown;
|
|
406
|
+
|
|
213
407
|
// --- The ledger row (persisted in the run's blob, ADR-0007) --------------------------------------
|
|
214
408
|
|
|
215
409
|
/**
|
|
@@ -238,6 +432,19 @@ export type EffectRow =
|
|
|
238
432
|
pinned?: string;
|
|
239
433
|
at: string;
|
|
240
434
|
}
|
|
435
|
+
/** A dispatch's intent (ADR-0071): where it runs, and — once the start's answer names it — the
|
|
436
|
+
* run it started. A dispatch is idempotent by construction: its ledger id rides every start as
|
|
437
|
+
* the door's key, so a restore sends it again and gets the same run. */
|
|
438
|
+
| {
|
|
439
|
+
state: "intent";
|
|
440
|
+
kind: "dispatch";
|
|
441
|
+
slot: string;
|
|
442
|
+
placement: "orchestrator" | "instance";
|
|
443
|
+
idempotent: true;
|
|
444
|
+
wait: boolean;
|
|
445
|
+
run?: string;
|
|
446
|
+
at: string;
|
|
447
|
+
}
|
|
241
448
|
/** `sandbox` and `repo`: the Workspace and the Repo Slot an Effect of the Sandbox Effector ran on
|
|
242
449
|
* — every later push of its own Workspace and Repo Slot (one branch, one remote) sends its pair,
|
|
243
450
|
* and the newest push's unfinished sync runs again in that slot's Worktree. */
|
|
@@ -250,7 +457,9 @@ export type EffectRow =
|
|
|
250
457
|
output: unknown;
|
|
251
458
|
at: string;
|
|
252
459
|
}
|
|
253
|
-
|
|
460
|
+
/** `run`: for a dispatch, the run it started, kept past the abort — the callee runs on, and the
|
|
461
|
+
* caller's row still names it (ADR-0071). */
|
|
462
|
+
| { state: "aborted"; kind: EffectParts["kind"]; slot: string; run?: string; at: string };
|
|
254
463
|
|
|
255
464
|
/**
|
|
256
465
|
* Retention (ADR-0067): drop, in place, the output tail of every settled row once a save on disk
|
|
@@ -297,26 +506,33 @@ const TAILS: Record<EffectRow["kind"], readonly string[]> = {
|
|
|
297
506
|
// every later push of its Repo Slot sends the pair (`pushPairsOf`), and the newest push's sync
|
|
298
507
|
// runs again when it did not finish. The sync's bundle and git's words are its tail.
|
|
299
508
|
push: ["bundle", "detail"],
|
|
509
|
+
// A dispatch's tail is the callee's output, which `GET /runs/<id>` serves for as long as the run
|
|
510
|
+
// is kept, and a refusal's report, which can carry the callee door's whole schema: the feed's
|
|
511
|
+
// settled marker carries both whole. The row keeps the outcome, the run id, its status and the
|
|
512
|
+
// door's HTTP status (ADR-0071).
|
|
513
|
+
dispatch: ["output", "report"],
|
|
300
514
|
};
|
|
301
515
|
|
|
302
|
-
/** A kind's tail for one output. The NEWEST push of its Repo Slot whose sync did not finish
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
516
|
+
/** A kind's tail for one output. The NEWEST push of its Repo Slot whose sync did not finish (`busy`,
|
|
517
|
+
* `not-synced`) keeps its bundle: the sync that runs again before the next push of that slot moves
|
|
518
|
+
* the branch from it, and the Agent's store may not hold the published commits without it. Once a
|
|
519
|
+
* sync finished, or once a newer push of the slot published, the bundle goes: only the newest
|
|
520
|
+
* push's sync runs again (`lastPushOf`), so an older bundle has no reader. */
|
|
307
521
|
function tailsOf(kind: EffectRow["kind"], output: Record<string, unknown>, newest: boolean): readonly string[] {
|
|
308
522
|
if (kind === "push" && newest && (output.sync === "busy" || output.sync === "not-synced")) return ["detail"];
|
|
309
523
|
return TAILS[kind];
|
|
310
524
|
}
|
|
311
525
|
|
|
312
|
-
/** A settled push row that published: its pair, and its sync's answer and bundle when it has them.
|
|
526
|
+
/** A settled push row that published: its pair, and its sync's answer and bundle when it has them.
|
|
527
|
+
*/
|
|
313
528
|
export type PushedRow = { published: string; source: string; sync?: SyncAnswer["sync"]; bundle?: string } & Record<
|
|
314
529
|
string,
|
|
315
530
|
unknown
|
|
316
531
|
>;
|
|
317
532
|
|
|
318
533
|
/** The ledger's last push of a Workspace's Repo Slot that published: its row, by `at`, never this
|
|
319
|
-
* Effect's (`self`). A Workspace with two Repo Slots has two branches on two remotes, so two pairs.
|
|
534
|
+
* Effect's (`self`). A Workspace with two Repo Slots has two branches on two remotes, so two pairs.
|
|
535
|
+
*/
|
|
320
536
|
export function lastPushOf(
|
|
321
537
|
rows: Readonly<Record<string, EffectRow>>,
|
|
322
538
|
sandbox: string,
|
|
@@ -386,14 +602,15 @@ export type EffectMarker = {
|
|
|
386
602
|
effect: string;
|
|
387
603
|
stage: "started" | "output" | "settled" | "aborted";
|
|
388
604
|
text?: string;
|
|
389
|
-
output?: CommandOutput | HttpOutput;
|
|
605
|
+
output?: CommandOutput | HttpOutput | DispatchOutput;
|
|
390
606
|
};
|
|
391
607
|
|
|
392
608
|
// --- The port -----------------------------------------------------------------------------------
|
|
393
609
|
|
|
394
610
|
/** The Harness's exec, as the Effect drives it. `read` answers undefined for an exec the Harness
|
|
395
611
|
* does not know (404): it restarted, and jr2 settles `unknown`. `start` rejects with `ExecNotSent`
|
|
396
|
-
* only when the request certainly never reached the Harness — the one failure it is sent again on.
|
|
612
|
+
* only when the request certainly never reached the Harness — the one failure it is sent again on.
|
|
613
|
+
*/
|
|
397
614
|
export interface ExecPort {
|
|
398
615
|
start(id: string, req: ExecRequest, signal: AbortSignal): Promise<void>;
|
|
399
616
|
read(id: string, offset: number | undefined, signal: AbortSignal): Promise<ExecRead | undefined>;
|
|
@@ -416,9 +633,13 @@ type Fault = { type: "jr2.effect.error"; error: unknown };
|
|
|
416
633
|
|
|
417
634
|
/** An Effect slot's logic: xstate `ActorLogic`, typed by its input and output, branded with its
|
|
418
635
|
* parts. The brand is a runtime property (`isEffect`) and part of the type, as `definition` is
|
|
419
|
-
* not on an Agent's: nothing substitutes a fake for an Effect's parts.
|
|
636
|
+
* not on an Agent's: nothing substitutes a fake for an Effect's parts.
|
|
637
|
+
*
|
|
638
|
+
* @internal
|
|
639
|
+
*/
|
|
420
640
|
export type EffectLogic<I, O> = ActorLogic<EffectSnapshot<O, I>, EventObject, I> & EffectSlot;
|
|
421
641
|
|
|
642
|
+
/** @internal */
|
|
422
643
|
type ArgNames<A extends readonly unknown[]> = A[number] extends infer E ? (E extends Arg<infer N> ? N : never) : never;
|
|
423
644
|
/** A command's input: one string per argv slot, and with `pin` the approved commit (`{ sha }`);
|
|
424
645
|
* none when there is neither. */
|
|
@@ -428,36 +649,110 @@ export type CommandInput<A extends readonly unknown[], P = false> = [
|
|
|
428
649
|
? void
|
|
429
650
|
: { [K in ArgNames<A> | ([P] extends [true] ? "sha" : never)]: string };
|
|
430
651
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
652
|
+
// An Open argv's slots are the composer's (ADR-0054), so any key is one string — what the
|
|
653
|
+
// package's invoke hands it, read by the argv the composer binds (`argvOf`).
|
|
654
|
+
/** The input of a `command(open)`: a string for each `arg()` of the argv that the Instance binds,
|
|
655
|
+
* and with `pin`, the commit (`{ sha }`). The compiler cannot check the names. When the input has
|
|
656
|
+
* no string for an `arg()`, the Effect fails when it starts. */
|
|
434
657
|
export type OpenCommandInput<P = false> = Readonly<Record<string, string>> &
|
|
435
658
|
([P] extends [true] ? { sha: string } : unknown);
|
|
436
659
|
|
|
660
|
+
/** A length of time: a positive number and a unit, `s`, `m` or `h`. For example `"90s"`, `"10m"`
|
|
661
|
+
* or `"1h"`. */
|
|
437
662
|
export type Duration = `${number}s` | `${number}m` | `${number}h`;
|
|
438
663
|
|
|
664
|
+
/** The options of `command()`: the Repo Slot it runs in, how long it can run, how much of its
|
|
665
|
+
* output it keeps, its Effect secrets, and the commit it runs against. */
|
|
439
666
|
export type CommandOptions = {
|
|
440
|
-
/**
|
|
667
|
+
/**
|
|
668
|
+
* The Repo Slot whose Worktree is the working directory of the command. Write it only when the
|
|
669
|
+
* Workspace has more than one Repo Slot. In a Workspace with no Repo Slot, the command starts in
|
|
670
|
+
* `/work`, the folder that the steps of the Workspace share.
|
|
671
|
+
*
|
|
672
|
+
* The command starts in the root of that Worktree. To run it in a subfolder, give the path as an
|
|
673
|
+
* argument, or write `["sh", "-c", "cd <dir> && <program>"]`.
|
|
674
|
+
*
|
|
675
|
+
* When it starts, it refuses:
|
|
676
|
+
* - no `slot` under a Workspace with more than one Repo Slot: `command "<slot>" runs under a
|
|
677
|
+
* Workspace carrying more than one Repo Slot (<slots>) and names none — say which Worktree it
|
|
678
|
+
* runs in: command(argv, { slot: "<slot>" }) (ADR-0051)`. When `env` names an Effect secret,
|
|
679
|
+
* the text is `command "<slot>" runs under a Workspace carrying more than one Repo Slot
|
|
680
|
+
* (<slots>) — say which: { slot: "<slot>" } (ADR-0051)`.
|
|
681
|
+
* - with an Effect secret in `env`, a Workspace with no Repo Slot: `command "<slot>" runs under
|
|
682
|
+
* a Workspace with no Repo Slot: a command with an Effect secret runs against one commit of a
|
|
683
|
+
* Repo Slot's branch, and there is none — declare a Repo Slot, or name no secret in its env, and
|
|
684
|
+
* it runs in /work beside the Agent (ADR-0051)`.
|
|
685
|
+
* - a `slot` that the Workspace does not have: `command "<slot>" names Repo Slot "<name>", which
|
|
686
|
+
* its Workspace does not carry (<slots>)`.
|
|
687
|
+
*/
|
|
441
688
|
slot?: string;
|
|
442
|
-
/**
|
|
689
|
+
/**
|
|
690
|
+
* How long the command can run: a number and a unit, such as `"90s"`, `"10m"` or `"1h"`.
|
|
691
|
+
* Default `"10m"`. When the time ends, jr2 kills the process group, and the output is
|
|
692
|
+
* `timeout`. It refuses a value that is not a positive duration: `command: timeout "<timeout>"
|
|
693
|
+
* is not a positive duration (like "90s", "10m", "1h")`.
|
|
694
|
+
*/
|
|
443
695
|
timeout?: Duration;
|
|
444
|
-
/**
|
|
696
|
+
/** The number of characters that `tail` keeps from the end of what the command wrote. Default
|
|
697
|
+
* 4096. */
|
|
445
698
|
tail?: number;
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
*
|
|
699
|
+
// Naming a secret is the placement (ADR-0067, ADR-0068): there is no placement key.
|
|
700
|
+
/**
|
|
701
|
+
* The environment variables of the command, each an Effect secret. `secret("x")` gives the
|
|
702
|
+
* value, and `secret("x").path` gives the path of a file that holds the value. When `env` names
|
|
703
|
+
* one, the command runs in the Sandbox Effector against one commit, not in the Worktree that the
|
|
704
|
+
* Agent changes. Without `env`, it runs in the Harness container, beside the Agent.
|
|
705
|
+
*
|
|
706
|
+
* It refuses a name that is not a variable name, and `PATH`, `HOME` and `TMPDIR`: ``command(argv,
|
|
707
|
+
* { env }): "<name>" is not a variable the author sets — PATH, HOME and TMPDIR are the
|
|
708
|
+
* Effector's (ADR-0067)``.
|
|
709
|
+
*/
|
|
449
710
|
env?: CommandEnv;
|
|
450
|
-
/**
|
|
451
|
-
*
|
|
711
|
+
/**
|
|
712
|
+
* Only for a command whose `env` names an Effect secret. `true`: the input of the Effect names
|
|
713
|
+
* the commit to run against (`{ sha }`, a full commit id, for example one that a Gate approved).
|
|
714
|
+
* Default: the tip of the Agent's branch when the Effect starts.
|
|
715
|
+
*
|
|
716
|
+
* It refuses `pin: true` on a command that names no Effect secret: ``command(argv, { pin: true
|
|
717
|
+
* }) names no secret in its env: `pin` chooses the commit a credentialed command runs against in
|
|
718
|
+
* the Sandbox Effector, and a command that names no secret runs in the live Worktree (ADR-0067)
|
|
719
|
+
* — name its secret, command(argv, { env: { NAME: secret("x") }, pin: true }), or drop `pin` ``.
|
|
720
|
+
*
|
|
721
|
+
* When it starts, it refuses an input `sha` that is not a full commit id: `command "<slot>"
|
|
722
|
+
* (pin): its input's sha is not a full commit id`.
|
|
723
|
+
*/
|
|
452
724
|
pin?: boolean;
|
|
453
725
|
};
|
|
454
726
|
|
|
455
727
|
/** The Open part a command's options state (ADR-0054): its secret, when its env holds
|
|
456
|
-
* `secret(open)` or its `.path`.
|
|
728
|
+
* `secret(open)` or its `.path`.
|
|
729
|
+
*
|
|
730
|
+
* @internal
|
|
731
|
+
*/
|
|
457
732
|
export type EnvOpenParts<Opt> = Opt extends { readonly env: infer E }
|
|
458
733
|
? OpenSecretIn<E extends Readonly<Record<string, infer V>> ? V : never>
|
|
459
734
|
: never;
|
|
460
735
|
|
|
736
|
+
/**
|
|
737
|
+
* Why an Effect kind cannot run in a Workspace with no Repo Slot (ADR-0051, ADR-0067), or undefined
|
|
738
|
+
* when it can: a `push` and a credentialed `command` run on a commit of a slot's branch, and that
|
|
739
|
+
* Workspace has none. The invoke and the `jr2 up` walk refuse with the same words.
|
|
740
|
+
*/
|
|
741
|
+
export function needsASlot(kind: string): string | undefined {
|
|
742
|
+
if (kind === "push")
|
|
743
|
+
return 'a push publishes a Repo Slot\'s branch, and there is none — declare one: repos: { app: "<url>" }';
|
|
744
|
+
if (kind === "command") {
|
|
745
|
+
return (
|
|
746
|
+
"a command with an Effect secret runs against one commit of a Repo Slot's branch, and there is none — " +
|
|
747
|
+
"declare a Repo Slot, or name no secret in its env, and it runs in /work beside the Agent"
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
return undefined;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/** The work root of a Sandbox pod (ADR-0004): where a command runs in a Workspace with no Repo Slot. */
|
|
754
|
+
const WORK_ROOT = "/work";
|
|
755
|
+
|
|
461
756
|
const DEFAULT_TIMEOUT_MS = 10 * 60_000;
|
|
462
757
|
const DEFAULT_TAIL = 4096;
|
|
463
758
|
/** How long a re-asked request waits, at most, after a transport failure. */
|
|
@@ -852,8 +1147,10 @@ function argvOf(slot: string, parts: CommandParts, input: unknown): string[] {
|
|
|
852
1147
|
|
|
853
1148
|
/**
|
|
854
1149
|
* The cwd: a Repo Slot's Worktree (ADR-0051). Named by `slot`, or the one slot where the Workspace
|
|
855
|
-
* carries one; a Workspace with none
|
|
856
|
-
*
|
|
1150
|
+
* carries one; a Workspace with none runs it in `/work`, which its steps share. Named, never left to
|
|
1151
|
+
* the Harness: a spawn with no cwd runs in the Sandbox Image's own `WORKDIR`, which is the image's.
|
|
1152
|
+
* Several and none named is refused, as a Turn's Frame is (ADR-0057): the kit gives no slot a
|
|
1153
|
+
* meaning.
|
|
857
1154
|
*/
|
|
858
1155
|
function cwdOf(slot: string, parts: CommandParts, ambient: AmbientHandles): { cwd?: string } {
|
|
859
1156
|
const slots = Object.keys(ambient.repos);
|
|
@@ -867,7 +1164,7 @@ function cwdOf(slot: string, parts: CommandParts, ambient: AmbientHandles): { cw
|
|
|
867
1164
|
}
|
|
868
1165
|
return { cwd };
|
|
869
1166
|
}
|
|
870
|
-
if (slots.length === 0) return {};
|
|
1167
|
+
if (slots.length === 0) return { cwd: WORK_ROOT };
|
|
871
1168
|
if (slots.length === 1) return { cwd: ambient.repos[slots[0]!]! };
|
|
872
1169
|
throw new Error(
|
|
873
1170
|
`command "${slot}" runs under a Workspace carrying more than one Repo Slot (${slots.join(", ")}) and names ` +
|