@relayflows/sdk 2.0.8 → 2.0.10
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/dist/adapters/base.d.ts +46 -0
- package/dist/adapters/base.d.ts.map +1 -0
- package/dist/adapters/base.js +15 -0
- package/dist/adapters/base.js.map +1 -0
- package/dist/adapters/claude.d.ts +5 -0
- package/dist/adapters/claude.d.ts.map +1 -0
- package/dist/adapters/claude.js +44 -0
- package/dist/adapters/claude.js.map +1 -0
- package/dist/adapters/codex.d.ts +5 -0
- package/dist/adapters/codex.d.ts.map +1 -0
- package/dist/adapters/codex.js +45 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/index.d.ts +14 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +29 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/wrapper.d.ts +12 -0
- package/dist/adapters/wrapper.d.ts.map +1 -0
- package/dist/adapters/wrapper.js +31 -0
- package/dist/adapters/wrapper.js.map +1 -0
- package/dist/agent-relay-transport.d.ts +84 -0
- package/dist/agent-relay-transport.d.ts.map +1 -0
- package/dist/agent-relay-transport.js +134 -0
- package/dist/agent-relay-transport.js.map +1 -0
- package/dist/authored-budget.d.ts +13 -0
- package/dist/authored-budget.d.ts.map +1 -0
- package/dist/authored-budget.js +78 -0
- package/dist/authored-budget.js.map +1 -0
- package/dist/authored-flow-error.d.ts +1 -1
- package/dist/authored-flow-error.d.ts.map +1 -1
- package/dist/authored-flow-error.js.map +1 -1
- package/dist/authored-flow-executor.d.ts +13 -7
- package/dist/authored-flow-executor.d.ts.map +1 -1
- package/dist/authored-flow-executor.js +115 -194
- package/dist/authored-flow-executor.js.map +1 -1
- package/dist/authored-flow-loader.d.ts +10 -1
- package/dist/authored-flow-loader.d.ts.map +1 -1
- package/dist/authored-flow-loader.js +56 -3
- package/dist/authored-flow-loader.js.map +1 -1
- package/dist/authored-flow-operation.d.ts +9 -1
- package/dist/authored-flow-operation.d.ts.map +1 -1
- package/dist/authored-flow-operation.js +35 -4
- package/dist/authored-flow-operation.js.map +1 -1
- package/dist/authored-helper-effect.d.ts +12 -0
- package/dist/authored-helper-effect.d.ts.map +1 -0
- package/dist/authored-helper-effect.js +144 -0
- package/dist/authored-helper-effect.js.map +1 -0
- package/dist/authored-mcp.d.ts +17 -0
- package/dist/authored-mcp.d.ts.map +1 -0
- package/dist/authored-mcp.js +171 -0
- package/dist/authored-mcp.js.map +1 -0
- package/dist/authored-memory.d.ts +7 -0
- package/dist/authored-memory.d.ts.map +1 -0
- package/dist/authored-memory.js +60 -0
- package/dist/authored-memory.js.map +1 -0
- package/dist/authored-plugin-effect.d.ts +16 -0
- package/dist/authored-plugin-effect.d.ts.map +1 -0
- package/dist/authored-plugin-effect.js +163 -0
- package/dist/authored-plugin-effect.js.map +1 -0
- package/dist/authored-step-output.d.ts +23 -0
- package/dist/authored-step-output.d.ts.map +1 -0
- package/dist/authored-step-output.js +67 -0
- package/dist/authored-step-output.js.map +1 -0
- package/dist/authored-worker-step.d.ts +15 -0
- package/dist/authored-worker-step.d.ts.map +1 -0
- package/dist/authored-worker-step.js +136 -0
- package/dist/authored-worker-step.js.map +1 -0
- package/dist/budget-preflight.d.ts +5 -0
- package/dist/budget-preflight.d.ts.map +1 -0
- package/dist/budget-preflight.js +33 -0
- package/dist/budget-preflight.js.map +1 -0
- package/dist/budget.d.ts +14 -0
- package/dist/budget.d.ts.map +1 -0
- package/dist/budget.js +52 -0
- package/dist/budget.js.map +1 -0
- package/dist/bundle-transport.d.ts +18 -0
- package/dist/bundle-transport.d.ts.map +1 -0
- package/dist/bundle-transport.js +111 -0
- package/dist/bundle-transport.js.map +1 -0
- package/dist/bundle-typescript.d.ts +8 -0
- package/dist/bundle-typescript.d.ts.map +1 -0
- package/dist/bundle-typescript.js +108 -0
- package/dist/bundle-typescript.js.map +1 -0
- package/dist/bundle.d.ts +25 -0
- package/dist/bundle.d.ts.map +1 -0
- package/dist/bundle.js +199 -0
- package/dist/bundle.js.map +1 -0
- package/dist/child-stop.d.ts +59 -0
- package/dist/child-stop.d.ts.map +1 -0
- package/dist/child-stop.js +94 -0
- package/dist/child-stop.js.map +1 -0
- package/dist/cli/add.d.ts +6 -0
- package/dist/cli/add.d.ts.map +1 -0
- package/dist/cli/add.js +47 -0
- package/dist/cli/add.js.map +1 -0
- package/dist/cli/build.d.ts +13 -0
- package/dist/cli/build.d.ts.map +1 -0
- package/dist/cli/build.js +224 -0
- package/dist/cli/build.js.map +1 -0
- package/dist/cli/bundle-preflight.d.ts +5 -0
- package/dist/cli/bundle-preflight.d.ts.map +1 -0
- package/dist/cli/bundle-preflight.js +28 -0
- package/dist/cli/bundle-preflight.js.map +1 -0
- package/dist/cli/check-helper-body.d.ts +4 -0
- package/dist/cli/check-helper-body.d.ts.map +1 -0
- package/dist/cli/check-helper-body.js +15 -0
- package/dist/cli/check-helper-body.js.map +1 -0
- package/dist/cli/check-triggers.d.ts +14 -0
- package/dist/cli/check-triggers.d.ts.map +1 -0
- package/dist/cli/check-triggers.js +37 -0
- package/dist/cli/check-triggers.js.map +1 -0
- package/dist/cli/check-typescript.d.ts +20 -0
- package/dist/cli/check-typescript.d.ts.map +1 -0
- package/dist/cli/check-typescript.js +54 -0
- package/dist/cli/check-typescript.js.map +1 -0
- package/dist/cli/check.d.ts +36 -3
- package/dist/cli/check.d.ts.map +1 -1
- package/dist/cli/check.js +122 -11
- package/dist/cli/check.js.map +1 -1
- package/dist/cli/cloud-run.d.ts +8 -0
- package/dist/cli/cloud-run.d.ts.map +1 -0
- package/dist/cli/cloud-run.js +50 -0
- package/dist/cli/cloud-run.js.map +1 -0
- package/dist/cli/deploy.d.ts +9 -0
- package/dist/cli/deploy.d.ts.map +1 -0
- package/dist/cli/deploy.js +58 -0
- package/dist/cli/deploy.js.map +1 -0
- package/dist/cli/deterministic-failure.d.ts +5 -0
- package/dist/cli/deterministic-failure.d.ts.map +1 -0
- package/dist/cli/deterministic-failure.js +66 -0
- package/dist/cli/deterministic-failure.js.map +1 -0
- package/dist/cli/direct-run.d.ts.map +1 -1
- package/dist/cli/direct-run.js +83 -7
- package/dist/cli/direct-run.js.map +1 -1
- package/dist/cli/hn-monitor.d.ts.map +1 -1
- package/dist/cli/hn-monitor.js +2 -1
- package/dist/cli/hn-monitor.js.map +1 -1
- package/dist/cli/replay.d.ts +12 -0
- package/dist/cli/replay.d.ts.map +1 -0
- package/dist/cli/replay.js +82 -0
- package/dist/cli/replay.js.map +1 -0
- package/dist/cli/reuse.d.ts +8 -0
- package/dist/cli/reuse.d.ts.map +1 -0
- package/dist/cli/reuse.js +25 -0
- package/dist/cli/reuse.js.map +1 -0
- package/dist/cli/run-digest.d.ts +4 -0
- package/dist/cli/run-digest.d.ts.map +1 -0
- package/dist/cli/run-digest.js +24 -0
- package/dist/cli/run-digest.js.map +1 -0
- package/dist/cli/run.d.ts +16 -5
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +99 -17
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/serve-webhook.d.ts +40 -0
- package/dist/cli/serve-webhook.d.ts.map +1 -0
- package/dist/cli/serve-webhook.js +233 -0
- package/dist/cli/serve-webhook.js.map +1 -0
- package/dist/cli/tick-runner.d.ts.map +1 -1
- package/dist/cli/tick-runner.js +2 -1
- package/dist/cli/tick-runner.js.map +1 -1
- package/dist/cli-adapter.d.ts +8 -14
- package/dist/cli-adapter.d.ts.map +1 -1
- package/dist/cli-adapter.js +19 -64
- package/dist/cli-adapter.js.map +1 -1
- package/dist/cli-watch.d.ts +13 -0
- package/dist/cli-watch.d.ts.map +1 -0
- package/dist/cli-watch.js +185 -0
- package/dist/cli-watch.js.map +1 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +387 -14
- package/dist/cli.js.map +1 -1
- package/dist/cloud-http.d.ts +22 -0
- package/dist/cloud-http.d.ts.map +1 -0
- package/dist/cloud-http.js +84 -0
- package/dist/cloud-http.js.map +1 -0
- package/dist/cloud-run.d.ts +38 -0
- package/dist/cloud-run.d.ts.map +1 -0
- package/dist/cloud-run.js +112 -0
- package/dist/cloud-run.js.map +1 -0
- package/dist/compile.d.ts +21 -3
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +97 -23
- package/dist/compile.js.map +1 -1
- package/dist/create-flow.d.ts +17 -0
- package/dist/create-flow.d.ts.map +1 -0
- package/dist/create-flow.js +72 -0
- package/dist/create-flow.js.map +1 -0
- package/dist/daemon-connection.d.ts +14 -0
- package/dist/daemon-connection.d.ts.map +1 -1
- package/dist/daemon-connection.js +33 -1
- package/dist/daemon-connection.js.map +1 -1
- package/dist/demo-hn-monitor.js +2 -1
- package/dist/demo-hn-monitor.js.map +1 -1
- package/dist/effect-channel.d.ts +30 -0
- package/dist/effect-channel.d.ts.map +1 -0
- package/dist/effect-channel.js +71 -0
- package/dist/effect-channel.js.map +1 -0
- package/dist/failure-kinds.d.ts +14 -3
- package/dist/failure-kinds.d.ts.map +1 -1
- package/dist/failure-kinds.js +27 -0
- package/dist/failure-kinds.js.map +1 -1
- package/dist/gate-contract.d.ts.map +1 -1
- package/dist/gate-contract.js +9 -0
- package/dist/gate-contract.js.map +1 -1
- package/dist/helper-preflight.d.ts +18 -0
- package/dist/helper-preflight.d.ts.map +1 -0
- package/dist/helper-preflight.js +32 -0
- package/dist/helper-preflight.js.map +1 -0
- package/dist/helper-storage.d.ts +5 -0
- package/dist/helper-storage.d.ts.map +1 -0
- package/dist/helper-storage.js +31 -0
- package/dist/helper-storage.js.map +1 -0
- package/dist/helper-writeback.d.ts +12 -0
- package/dist/helper-writeback.d.ts.map +1 -0
- package/dist/helper-writeback.js +95 -0
- package/dist/helper-writeback.js.map +1 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/input-binding.d.ts +4 -0
- package/dist/input-binding.d.ts.map +1 -0
- package/dist/input-binding.js +78 -0
- package/dist/input-binding.js.map +1 -0
- package/dist/journal-client.d.ts +12 -4
- package/dist/journal-client.d.ts.map +1 -1
- package/dist/journal-client.js +17 -6
- package/dist/journal-client.js.map +1 -1
- package/dist/journal-offset.d.ts +3 -0
- package/dist/journal-offset.d.ts.map +1 -0
- package/dist/journal-offset.js +69 -0
- package/dist/journal-offset.js.map +1 -0
- package/dist/journal-reader.d.ts +30 -0
- package/dist/journal-reader.d.ts.map +1 -0
- package/dist/journal-reader.js +156 -0
- package/dist/journal-reader.js.map +1 -0
- package/dist/json-schema.d.ts +2 -0
- package/dist/json-schema.d.ts.map +1 -1
- package/dist/json-schema.js +38 -22
- package/dist/json-schema.js.map +1 -1
- package/dist/llm-worker.d.ts +16 -0
- package/dist/llm-worker.d.ts.map +1 -0
- package/dist/llm-worker.js +79 -0
- package/dist/llm-worker.js.map +1 -0
- package/dist/local-agent.d.ts +8 -0
- package/dist/local-agent.d.ts.map +1 -0
- package/dist/local-agent.js +25 -0
- package/dist/local-agent.js.map +1 -0
- package/dist/mcp-client.d.ts +15 -0
- package/dist/mcp-client.d.ts.map +1 -0
- package/dist/mcp-client.js +104 -0
- package/dist/mcp-client.js.map +1 -0
- package/dist/mcp-config.d.ts +4 -0
- package/dist/mcp-config.d.ts.map +1 -0
- package/dist/mcp-config.js +61 -0
- package/dist/mcp-config.js.map +1 -0
- package/dist/mcp-stdio.d.ts +24 -0
- package/dist/mcp-stdio.d.ts.map +1 -0
- package/dist/mcp-stdio.js +89 -0
- package/dist/mcp-stdio.js.map +1 -0
- package/dist/model-pricing.d.ts +22 -0
- package/dist/model-pricing.d.ts.map +1 -0
- package/dist/model-pricing.js +30 -0
- package/dist/model-pricing.js.map +1 -0
- package/dist/mount-registry.d.ts +9 -0
- package/dist/mount-registry.d.ts.map +1 -0
- package/dist/mount-registry.js +47 -0
- package/dist/mount-registry.js.map +1 -0
- package/dist/named-gate-lowering.d.ts +9 -0
- package/dist/named-gate-lowering.d.ts.map +1 -0
- package/dist/named-gate-lowering.js +152 -0
- package/dist/named-gate-lowering.js.map +1 -0
- package/dist/named-gates.d.ts +10 -0
- package/dist/named-gates.d.ts.map +1 -0
- package/dist/named-gates.js +69 -0
- package/dist/named-gates.js.map +1 -0
- package/dist/observer-link.d.ts +129 -0
- package/dist/observer-link.d.ts.map +1 -0
- package/dist/observer-link.js +274 -0
- package/dist/observer-link.js.map +1 -0
- package/dist/plugin-loader.d.ts +13 -0
- package/dist/plugin-loader.d.ts.map +1 -0
- package/dist/plugin-loader.js +110 -0
- package/dist/plugin-loader.js.map +1 -0
- package/dist/plugin-manifest.d.ts +27 -0
- package/dist/plugin-manifest.d.ts.map +1 -0
- package/dist/plugin-manifest.js +89 -0
- package/dist/plugin-manifest.js.map +1 -0
- package/dist/preflight.d.ts +26 -2
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +175 -1
- package/dist/preflight.js.map +1 -1
- package/dist/progress.d.ts +14 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +39 -0
- package/dist/progress.js.map +1 -0
- package/dist/protocol.d.ts +11 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/pty-sidechannel.d.ts +14 -0
- package/dist/pty-sidechannel.d.ts.map +1 -0
- package/dist/pty-sidechannel.js +103 -0
- package/dist/pty-sidechannel.js.map +1 -0
- package/dist/scope-compiler.d.ts +52 -0
- package/dist/scope-compiler.d.ts.map +1 -0
- package/dist/scope-compiler.js +62 -0
- package/dist/scope-compiler.js.map +1 -0
- package/dist/slack-preflight.d.ts +6 -0
- package/dist/slack-preflight.d.ts.map +1 -0
- package/dist/slack-preflight.js +28 -0
- package/dist/slack-preflight.js.map +1 -0
- package/dist/slack-writeback.d.ts +40 -0
- package/dist/slack-writeback.d.ts.map +1 -0
- package/dist/slack-writeback.js +60 -0
- package/dist/slack-writeback.js.map +1 -0
- package/dist/spec.d.ts +134 -4
- package/dist/spec.d.ts.map +1 -1
- package/dist/spec.js.map +1 -1
- package/dist/step-dependencies.d.ts.map +1 -1
- package/dist/step-dependencies.js +2 -1
- package/dist/step-dependencies.js.map +1 -1
- package/dist/step-fields.d.ts +4 -4
- package/dist/step-fields.d.ts.map +1 -1
- package/dist/step-fields.js +5 -2
- package/dist/step-fields.js.map +1 -1
- package/dist/trigger-executor.d.ts +7 -0
- package/dist/trigger-executor.d.ts.map +1 -0
- package/dist/trigger-executor.js +35 -0
- package/dist/trigger-executor.js.map +1 -0
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +53 -6
- package/dist/validate.js.map +1 -1
- package/dist/webhook-rate-limit.d.ts +29 -0
- package/dist/webhook-rate-limit.d.ts.map +1 -0
- package/dist/webhook-rate-limit.js +38 -0
- package/dist/webhook-rate-limit.js.map +1 -0
- package/dist/webhook-signature.d.ts +22 -0
- package/dist/webhook-signature.d.ts.map +1 -0
- package/dist/webhook-signature.js +53 -0
- package/dist/webhook-signature.js.map +1 -0
- package/dist/worker-cli.d.ts +13 -1
- package/dist/worker-cli.d.ts.map +1 -1
- package/dist/worker-cli.js +133 -12
- package/dist/worker-cli.js.map +1 -1
- package/dist/worker-input.d.ts +4 -0
- package/dist/worker-input.d.ts.map +1 -0
- package/dist/worker-input.js +14 -0
- package/dist/worker-input.js.map +1 -0
- package/dist/worker-lease.d.ts +5 -0
- package/dist/worker-lease.d.ts.map +1 -0
- package/dist/worker-lease.js +80 -0
- package/dist/worker-lease.js.map +1 -0
- package/dist/worker-spend.d.ts +19 -0
- package/dist/worker-spend.d.ts.map +1 -0
- package/dist/worker-spend.js +22 -0
- package/dist/worker-spend.js.map +1 -0
- package/dist/worker-usage.d.ts +7 -0
- package/dist/worker-usage.d.ts.map +1 -0
- package/dist/worker-usage.js +54 -0
- package/dist/worker-usage.js.map +1 -0
- package/dist/worker.d.ts +2 -0
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +22 -8
- package/dist/worker.js.map +1 -1
- package/dist/wrapper-session.d.ts +1 -1
- package/dist/wrapper-session.d.ts.map +1 -1
- package/dist/wrapper-session.js +56 -19
- package/dist/wrapper-session.js.map +1 -1
- package/dist/yaml-helper-effect.d.ts +8 -0
- package/dist/yaml-helper-effect.d.ts.map +1 -0
- package/dist/yaml-helper-effect.js +79 -0
- package/dist/yaml-helper-effect.js.map +1 -0
- package/dist/yaml-helpers.d.ts +18 -0
- package/dist/yaml-helpers.d.ts.map +1 -0
- package/dist/yaml-helpers.js +116 -0
- package/dist/yaml-helpers.js.map +1 -0
- package/package.json +19 -2
- package/src/adapters/base.ts +53 -0
- package/src/adapters/claude.ts +55 -0
- package/src/adapters/codex.ts +56 -0
- package/src/adapters/index.ts +36 -0
- package/src/adapters/wrapper.ts +42 -0
- package/src/agent-relay-transport.ts +187 -0
- package/src/authored-budget.ts +69 -0
- package/src/authored-flow-error.ts +10 -0
- package/src/authored-flow-executor.ts +170 -293
- package/src/authored-flow-loader.ts +60 -3
- package/src/authored-flow-operation.ts +38 -5
- package/src/authored-helper-effect.ts +143 -0
- package/src/authored-mcp.ts +166 -0
- package/src/authored-memory.ts +55 -0
- package/src/authored-plugin-effect.ts +152 -0
- package/src/authored-step-output.ts +107 -0
- package/src/authored-worker-step.ts +181 -0
- package/src/budget-preflight.ts +31 -0
- package/src/budget.ts +49 -0
- package/src/bundle-transport.ts +79 -0
- package/src/bundle-typescript.ts +98 -0
- package/src/bundle.ts +182 -0
- package/src/child-stop.ts +138 -0
- package/src/cli/add.ts +45 -0
- package/src/cli/build.ts +214 -0
- package/src/cli/bundle-preflight.ts +28 -0
- package/src/cli/check-helper-body.ts +14 -0
- package/src/cli/check-triggers.ts +41 -0
- package/src/cli/check-typescript.ts +63 -0
- package/src/cli/check.ts +132 -14
- package/src/cli/cloud-run.ts +47 -0
- package/src/cli/deploy.ts +52 -0
- package/src/cli/deterministic-failure.ts +69 -0
- package/src/cli/direct-run.ts +79 -7
- package/src/cli/hn-monitor.ts +2 -1
- package/src/cli/replay.ts +81 -0
- package/src/cli/reuse.ts +23 -0
- package/src/cli/run-digest.ts +24 -0
- package/src/cli/run.ts +104 -21
- package/src/cli/serve-webhook.ts +246 -0
- package/src/cli/tick-runner.ts +2 -1
- package/src/cli-adapter.ts +31 -74
- package/src/cli-watch.ts +174 -0
- package/src/cli.ts +396 -17
- package/src/cloud-http.ts +100 -0
- package/src/cloud-run.ts +137 -0
- package/src/compile.ts +102 -23
- package/src/create-flow.ts +82 -0
- package/src/daemon-connection.ts +32 -1
- package/src/demo-hn-monitor.ts +2 -1
- package/src/effect-channel.ts +97 -0
- package/src/failure-kinds.ts +38 -0
- package/src/gate-contract.ts +7 -0
- package/src/helper-preflight.ts +34 -0
- package/src/helper-storage.ts +23 -0
- package/src/helper-writeback.ts +83 -0
- package/src/index.ts +26 -0
- package/src/input-binding.ts +76 -0
- package/src/journal-client.ts +20 -7
- package/src/journal-offset.ts +60 -0
- package/src/journal-reader.ts +165 -0
- package/src/json-schema.ts +34 -18
- package/src/llm-worker.ts +82 -0
- package/src/local-agent.ts +30 -0
- package/src/mcp-client.ts +97 -0
- package/src/mcp-config.ts +42 -0
- package/src/mcp-stdio.ts +84 -0
- package/src/model-pricing.ts +32 -0
- package/src/mount-registry.ts +44 -0
- package/src/named-gate-lowering.ts +147 -0
- package/src/named-gates.ts +76 -0
- package/src/observer-link.ts +368 -0
- package/src/plugin-loader.ts +88 -0
- package/src/plugin-manifest.ts +76 -0
- package/src/preflight.ts +178 -4
- package/src/progress.ts +51 -0
- package/src/protocol.ts +12 -0
- package/src/pty-sidechannel.ts +92 -0
- package/src/scope-compiler.ts +105 -0
- package/src/slack-preflight.ts +25 -0
- package/src/slack-writeback.ts +63 -0
- package/src/spec.ts +112 -4
- package/src/step-dependencies.ts +3 -1
- package/src/step-fields.ts +5 -2
- package/src/trigger-executor.ts +34 -0
- package/src/validate.ts +47 -6
- package/src/webhook-rate-limit.ts +69 -0
- package/src/webhook-signature.ts +74 -0
- package/src/worker-cli.ts +156 -11
- package/src/worker-input.ts +15 -0
- package/src/worker-lease.ts +85 -0
- package/src/worker-spend.ts +23 -0
- package/src/worker-usage.ts +48 -0
- package/src/worker.ts +27 -9
- package/src/wrapper-session.ts +50 -15
- package/src/yaml-helper-effect.ts +78 -0
- package/src/yaml-helpers.ts +131 -0
package/src/cli/build.ts
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { readFile, lstat } from 'node:fs/promises';
|
|
2
|
+
import { dirname, extname, join, resolve } from 'node:path';
|
|
3
|
+
import { parse } from 'yaml';
|
|
4
|
+
import { sealBundle, verifyBundle, type BundleFile } from '../bundle.js';
|
|
5
|
+
import { canonicalize } from '../canonical.js';
|
|
6
|
+
import { compileSpec, toKernelSpec } from '../compile.js';
|
|
7
|
+
import { preflight } from '../preflight.js';
|
|
8
|
+
import { buildTypescript } from '../bundle-typescript.js';
|
|
9
|
+
import { checkBuildableFlow, readProjectConfig, type CheckReport } from './check.js';
|
|
10
|
+
import type { CliIo } from '../cli.js';
|
|
11
|
+
import type { FlowSpec } from '../spec.js';
|
|
12
|
+
|
|
13
|
+
export interface BuildArgs { command: 'build'; value: string; out?: string; verify: boolean; json: boolean }
|
|
14
|
+
|
|
15
|
+
export function parseBuildArgs(args: readonly string[]): BuildArgs | undefined {
|
|
16
|
+
if (args[0] === '--verify') {
|
|
17
|
+
return args.length === 2 && !args[1]!.startsWith('-')
|
|
18
|
+
? { command: 'build', value: args[1]!, verify: true, json: false } : undefined;
|
|
19
|
+
}
|
|
20
|
+
let out: string | undefined;
|
|
21
|
+
let value: string | undefined;
|
|
22
|
+
let json = false;
|
|
23
|
+
for (let i = 0; i < args.length; i++) {
|
|
24
|
+
const arg = args[i]!;
|
|
25
|
+
if (arg === '--out') {
|
|
26
|
+
if (out !== undefined || args[i + 1] === undefined || args[i + 1]!.startsWith('-')) return undefined;
|
|
27
|
+
out = args[++i];
|
|
28
|
+
} else if (arg === '--json') {
|
|
29
|
+
if (json) return undefined;
|
|
30
|
+
json = true;
|
|
31
|
+
} else if (arg.startsWith('-') || value !== undefined) return undefined;
|
|
32
|
+
else value = arg;
|
|
33
|
+
}
|
|
34
|
+
return value === undefined ? undefined : { command: 'build', value, out, verify: false, json };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Emit a check report in the same shape `flows check` uses so build-time
|
|
39
|
+
* refusals are consumable by the same tooling.
|
|
40
|
+
*/
|
|
41
|
+
function emitBuildCheckReport(report: CheckReport, json: boolean, io: CliIo): void {
|
|
42
|
+
for (const diagnostic of report.diagnostics) {
|
|
43
|
+
if (diagnostic.severity !== 'refusal') continue;
|
|
44
|
+
io.stderr(`REFUSED [${diagnostic.kind}] ${diagnostic.message}`);
|
|
45
|
+
}
|
|
46
|
+
if (json) io.stdout(JSON.stringify(report));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function runBuild(args: BuildArgs, io: CliIo): Promise<0 | 2> {
|
|
50
|
+
try {
|
|
51
|
+
if (args.verify) {
|
|
52
|
+
io.stdout(`VERIFIED sha256:${await verifyBundle(args.value)}`);
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
// Gate the build on the same preflight pipeline `flows check` uses.
|
|
56
|
+
// Refusals never leave partial artifacts — no file capture, no canonical
|
|
57
|
+
// spec write, no digest computation, no bundle directory creation. A
|
|
58
|
+
// previous `dist/flows/<name>@sha256:<hex>/` directory from an earlier
|
|
59
|
+
// successful build is not touched (buildFlow is never called).
|
|
60
|
+
const gate = await checkBuildableFlow(args.value);
|
|
61
|
+
if (!gate.report.ok) {
|
|
62
|
+
emitBuildCheckReport(gate.report, args.json, io);
|
|
63
|
+
return 2;
|
|
64
|
+
}
|
|
65
|
+
io.stdout(await buildFlow(args.value, args.out ?? 'dist/flows', io.stderr));
|
|
66
|
+
return 0;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
io.stderr(`REFUSED [bundle_invalid] ${error instanceof Error ? error.message : String(error)}`);
|
|
69
|
+
return 2;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Build checks never probe credentials, workers, or the author's PATH. */
|
|
74
|
+
export async function buildFlow(path: string, out: string, warn: (line: string) => void): Promise<string> {
|
|
75
|
+
const input = resolve(path);
|
|
76
|
+
const directory = dirname(input);
|
|
77
|
+
const files: BundleFile[] = [];
|
|
78
|
+
let authoring: FlowSpec;
|
|
79
|
+
let authored = false;
|
|
80
|
+
let compiler: string | undefined;
|
|
81
|
+
if (extname(input) === '.ts') {
|
|
82
|
+
const result = await buildTypescript(input);
|
|
83
|
+
authoring = result.spec;
|
|
84
|
+
authored = result.authored;
|
|
85
|
+
compiler = result.compiler;
|
|
86
|
+
files.push(...result.files);
|
|
87
|
+
} else if (['.yaml', '.yml'].includes(extname(input))) {
|
|
88
|
+
authoring = compileSpec(parse(await readFile(input, 'utf8')));
|
|
89
|
+
files.push({ path: 'lockfile.json', data: canonicalize({ version: 1, adapters: [] }) });
|
|
90
|
+
} else throw new Error('build expects a .yaml, .yml, or .ts flow');
|
|
91
|
+
|
|
92
|
+
// The current preflight API reports uncollected environment facts as
|
|
93
|
+
// probe_failed. Preserve that truthful report verbatim and separately declare
|
|
94
|
+
// the deployment obligations; never manufacture successful auth probes.
|
|
95
|
+
//
|
|
96
|
+
// Load the nearest flows.json so declared model allowlists and the project
|
|
97
|
+
// CLI participate in build-time preflight. Without them a declared `model:`
|
|
98
|
+
// that appears in flows.json/models refuses the whole build as
|
|
99
|
+
// `model_unknown`, because preflight defaults treat "no registry" as "no
|
|
100
|
+
// model is known" and only environment probes (deferred here) would rescue
|
|
101
|
+
// it. Model existence is a build-provable fact; only the live probe is not.
|
|
102
|
+
const config = readProjectConfig(directory);
|
|
103
|
+
const report = preflight(authoring, {
|
|
104
|
+
...(config.cli !== undefined ? { projectCli: config.cli } : {}),
|
|
105
|
+
...(config.path !== undefined ? { projectConfigPath: config.path } : {}),
|
|
106
|
+
projectSearchStart: directory,
|
|
107
|
+
models: config.models,
|
|
108
|
+
...(config.path !== undefined ? { modelRegistryPath: config.path } : {}),
|
|
109
|
+
probes: {
|
|
110
|
+
cli: () => { throw new Error('deferred to deployment'); },
|
|
111
|
+
executor: () => { throw new Error('deferred to deployment'); },
|
|
112
|
+
command: () => { throw new Error('deferred to deployment'); },
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
const refusals = report.diagnostics.filter(d => d.severity === 'refusal' && d.kind !== 'probe_failed');
|
|
116
|
+
if (refusals.length > 0) throw new Error(refusals.map(d => `[${d.kind}] ${d.message}`).join('\n'));
|
|
117
|
+
authoring = await captureFiles(authoring, directory, files);
|
|
118
|
+
files.push(
|
|
119
|
+
{ path: 'spec.canonical.json', data: canonicalize(toKernelSpec(authoring)) },
|
|
120
|
+
{ path: 'preflight.json', data: canonicalize(report) },
|
|
121
|
+
{ path: 'metadata.json', data: canonicalize({
|
|
122
|
+
version: 1, platform: { os: process.platform, arch: process.arch },
|
|
123
|
+
executables: files.filter(file => file.path === 'flow' || file.executable).map(file => file.path).sort(),
|
|
124
|
+
kind: authored ? 'authored-typescript' : 'declarative',
|
|
125
|
+
...(compiler === undefined ? {} : { compiler }),
|
|
126
|
+
preflight: { buildPassed: true, deferred: ['commands', 'credentials', 'workers', 'mcp_servers'],
|
|
127
|
+
...(authored ? { dynamicSteps: 'exported spec declaration checked; body was not executed during build' } : {}) },
|
|
128
|
+
}) },
|
|
129
|
+
);
|
|
130
|
+
return sealBundle({ name: authoring.name ?? 'flow', out, files,
|
|
131
|
+
repo: await repositoryRoot(directory), warn });
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function repositoryRoot(start: string): Promise<string> {
|
|
135
|
+
for (let dir = start; ; dir = dirname(dir)) {
|
|
136
|
+
try { await lstat(join(dir, '.git')); return dir; }
|
|
137
|
+
catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; }
|
|
138
|
+
if (dirname(dir) === dir) return start;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Relative CLI executables are the file references in the current spec schema.
|
|
143
|
+
* Shell commands are opaque; explicit ./file words are captured without
|
|
144
|
+
* interpreting substitutions or running a shell at build time. */
|
|
145
|
+
async function captureFiles(flow: FlowSpec, directory: string, files: BundleFile[]): Promise<FlowSpec> {
|
|
146
|
+
// Cache the in-flight *promise*, not the resolved string. Two concurrent
|
|
147
|
+
// captures of the same path (Promise.all over named agents that share a CLI)
|
|
148
|
+
// both pass a `captured.has(path)` guard before either has finished awaiting
|
|
149
|
+
// its lstat/readFile, so both would push the same `assets/...` entry -- and
|
|
150
|
+
// sealBundle then refuses the whole flow as a duplicate. Storing the promise
|
|
151
|
+
// dedups on the first synchronous look-up.
|
|
152
|
+
const captured = new Map<string, Promise<string>>();
|
|
153
|
+
function capture(path: string): Promise<string> {
|
|
154
|
+
if (!path.includes('/')) return Promise.resolve(path);
|
|
155
|
+
if (!path.startsWith('./') || path.split('/').includes('..') || path.includes('\\')) {
|
|
156
|
+
return Promise.reject(new Error(`${path}: bundle file references must start with ./ and stay inside the flow directory`));
|
|
157
|
+
}
|
|
158
|
+
const existing = captured.get(path);
|
|
159
|
+
if (existing !== undefined) return existing;
|
|
160
|
+
const pending = (async () => {
|
|
161
|
+
const parts = path.slice(2).split('/');
|
|
162
|
+
if (parts.some(p => p === '' || p === '.')) throw new Error(`${path}: invalid file reference`);
|
|
163
|
+
let executable = false;
|
|
164
|
+
for (let i = 1; i <= parts.length; i++) {
|
|
165
|
+
const stat = await lstat(join(directory, ...parts.slice(0, i)));
|
|
166
|
+
if (i === parts.length ? !stat.isFile() : !stat.isDirectory()) throw new Error(`${path}: expected regular file without symlinks`);
|
|
167
|
+
if (i === parts.length) executable = (stat.mode & 0o111) !== 0;
|
|
168
|
+
}
|
|
169
|
+
const target = `assets/${parts.join('/')}`;
|
|
170
|
+
files.push({ path: target, data: await readFile(join(directory, ...parts)), executable });
|
|
171
|
+
return `./${target}`;
|
|
172
|
+
})();
|
|
173
|
+
captured.set(path, pending);
|
|
174
|
+
return pending;
|
|
175
|
+
}
|
|
176
|
+
// Capture the flow-level `cli` default and every named-agent `cli` BEFORE
|
|
177
|
+
// walking steps. Otherwise a step that inherits its CLI from the flow header
|
|
178
|
+
// or an agents-map entry never emits its own `cli`, so the step-level walk
|
|
179
|
+
// below misses it, the executable never enters the bundle, and the sealed
|
|
180
|
+
// spec still points at the author's absolute path outside the bundle.
|
|
181
|
+
const flowCli = flow.cli !== undefined ? await capture(flow.cli) : undefined;
|
|
182
|
+
const agents: FlowSpec['agents'] = flow.agents === undefined ? undefined
|
|
183
|
+
: Object.fromEntries(await Promise.all(Object.entries(flow.agents).map(
|
|
184
|
+
async ([name, agent]) => [name, { ...agent, cli: await capture(agent.cli) }] as const,
|
|
185
|
+
)));
|
|
186
|
+
const compiled = toKernelSpec(flow);
|
|
187
|
+
// Lower named/flow CLI resolution first so every runtime reference is bound.
|
|
188
|
+
const steps: FlowSpec['steps'] = [];
|
|
189
|
+
for (const [index, step] of flow.steps.entries()) {
|
|
190
|
+
if (step.type !== 'deterministic') {
|
|
191
|
+
const lowered = compiled.steps[index];
|
|
192
|
+
const cli = lowered && lowered.type !== 'deterministic' ? lowered.cli : undefined;
|
|
193
|
+
steps.push(cli === undefined ? step : { ...step, cli: await capture(cli) });
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
let command = step.command;
|
|
197
|
+
const references = [...command.matchAll(/(?:^|[\s;|&<>])(?:"(\.\/[^"$`]+)"|'(\.\/[^']+)'|(\.\/[^\s;|&<>"'$`]+))/g)];
|
|
198
|
+
// Replace only each complete matched word, from right to left so offsets
|
|
199
|
+
// stay valid and similarly prefixed filenames cannot rewrite one another.
|
|
200
|
+
for (const match of references.reverse()) {
|
|
201
|
+
const ref = (match[1] ?? match[2] ?? match[3])!;
|
|
202
|
+
const start = match.index! + match[0].indexOf(ref);
|
|
203
|
+
command = command.slice(0, start) + await capture(ref) + command.slice(start + ref.length);
|
|
204
|
+
}
|
|
205
|
+
if (/^\s*["']?\//.test(command)) throw new Error(`${step.id}: absolute command paths cannot be bundled`);
|
|
206
|
+
steps.push({ ...step, command });
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
...flow,
|
|
210
|
+
...(flowCli !== undefined ? { cli: flowCli } : {}),
|
|
211
|
+
...(agents !== undefined ? { agents } : {}),
|
|
212
|
+
steps,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { BundleFailure } from '../bundle-transport.js';
|
|
4
|
+
import { compileSpec, kernelToAuthoring } from '../compile.js';
|
|
5
|
+
import { checkAuthoredFlow, type CheckExecution } from './check.js';
|
|
6
|
+
|
|
7
|
+
/** Refuse unsupported execution: a TS declaration is not its body, and
|
|
8
|
+
* daemon-relative assets must never resolve in an unrelated checkout. */
|
|
9
|
+
export async function checkRunnableBundle(directory: string, name: string): Promise<CheckExecution> {
|
|
10
|
+
const metadata = JSON.parse(await readFile(join(directory, 'metadata.json'), 'utf8'));
|
|
11
|
+
const manifest = JSON.parse(await readFile(join(directory, 'manifest.json'), 'utf8')) as { path: string }[];
|
|
12
|
+
const flow = compileSpec(kernelToAuthoring(JSON.parse(await readFile(join(directory, 'spec.canonical.json'), 'utf8'))));
|
|
13
|
+
if (flow.name !== undefined && flow.name !== name) throw new BundleFailure('bundle_signature_invalid', 'Bundle name does not match the requested flow.');
|
|
14
|
+
flow.name ??= name;
|
|
15
|
+
if (metadata.kind !== 'declarative' || flow.steps.some(step => step.type !== 'deterministic'
|
|
16
|
+
|| step.requirements !== undefined) || (flow.triggers?.length ?? 0) > 0
|
|
17
|
+
|| manifest.some(entry => entry.path.startsWith('assets/'))) {
|
|
18
|
+
throw new BundleFailure('bundle_unsupported',
|
|
19
|
+
'This slice runs declarative deterministic bundles without assets, placement requirements, or triggers.');
|
|
20
|
+
}
|
|
21
|
+
// Keep existing fail-closed environment checks before transport/journal.
|
|
22
|
+
// Splitting static and environment checks is follow-up.
|
|
23
|
+
// This deterministic-only slice declares no project models or executors.
|
|
24
|
+
// Never discover flows.json in the deployment/cache tree or its ancestors.
|
|
25
|
+
return checkAuthoredFlow(flow, join(directory, 'spec.canonical.json'), {
|
|
26
|
+
directory, models: [], executors: [],
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { loadAuthoredFlow } from '../authored-flow-loader.js';
|
|
2
|
+
import { checkSlackHelpers } from '../slack-preflight.js';
|
|
3
|
+
import { inputFailureReport, type CheckExecution } from './check.js';
|
|
4
|
+
|
|
5
|
+
/** Imports the definition, but never executes arbitrary authored body code. */
|
|
6
|
+
export async function checkHelperBody(path: string): Promise<CheckExecution> {
|
|
7
|
+
try {
|
|
8
|
+
const { handle, getDefinition } = await loadAuthoredFlow(path);
|
|
9
|
+
return { report: { ...checkSlackHelpers(getDefinition(handle)), path } };
|
|
10
|
+
} catch (error) {
|
|
11
|
+
return { report: inputFailureReport({ kind: 'invalid_spec',
|
|
12
|
+
message: error instanceof Error ? error.message : 'Could not import authored flow.' }, path) };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { dirname, resolve } from 'node:path';
|
|
2
|
+
import { loadAuthoredFlow, type LoadedAuthoredFlow } from '../authored-flow-loader.js';
|
|
3
|
+
import { preflightWebhookTriggers } from '../preflight.js';
|
|
4
|
+
import { checkSlackHelpers } from '../slack-preflight.js';
|
|
5
|
+
import { inputFailureReport, readProjectConfig, type CheckReport } from './check.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Inspect an authored flow without running a handler or contacting the daemon.
|
|
9
|
+
* Combines the two authored-only preflight paths that share a loaded flow: the
|
|
10
|
+
* webhook-trigger check (E) and the f.slack helper check (B). Skipping either
|
|
11
|
+
* turned this into a silent trapdoor -- a `.flow.ts` using `f.slack.post`
|
|
12
|
+
* without a token would pass `flows check` and only crash at run.
|
|
13
|
+
*/
|
|
14
|
+
export async function checkAuthoredTriggers(path: string): Promise<{
|
|
15
|
+
report: CheckReport;
|
|
16
|
+
loaded?: LoadedAuthoredFlow;
|
|
17
|
+
}> {
|
|
18
|
+
try {
|
|
19
|
+
const loaded = await loadAuthoredFlow(path);
|
|
20
|
+
const definition = loaded.getDefinition(loaded.handle);
|
|
21
|
+
const config = readProjectConfig(dirname(resolve(path)));
|
|
22
|
+
const triggerDiagnostics = preflightWebhookTriggers(
|
|
23
|
+
(definition.handlers ?? []).map(handler => handler.trigger), config.executors,
|
|
24
|
+
);
|
|
25
|
+
const helperReport = checkSlackHelpers(definition);
|
|
26
|
+
const diagnostics = [...triggerDiagnostics, ...helperReport.diagnostics];
|
|
27
|
+
return {
|
|
28
|
+
loaded,
|
|
29
|
+
report: {
|
|
30
|
+
ok: diagnostics.length === 0, path, gates: [], resolutions: [], diagnostics,
|
|
31
|
+
...(config.path === undefined ? {} : { projectConfigPath: config.path }),
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
} catch (error) {
|
|
35
|
+
return { report: inputFailureReport({
|
|
36
|
+
kind: typeof error === 'object' && error !== null && 'kind' in error && error.kind === 'config_invalid'
|
|
37
|
+
? 'config_invalid' : 'invalid_spec',
|
|
38
|
+
message: error instanceof Error ? error.message : 'Cannot inspect authored triggers',
|
|
39
|
+
}, path) };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { LoadedPlugin } from '../plugin-loader.js';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import type { AuthoredFlowDefinition } from '../authored-flow.js';
|
|
4
|
+
import { loadAuthoredFlow } from '../authored-flow-loader.js';
|
|
5
|
+
import { preflight } from '../preflight.js';
|
|
6
|
+
import { SPEC_SCHEMA_VERSION, type McpServerConfig } from '../spec.js';
|
|
7
|
+
import { inputFailureReport, readProjectConfig, type CheckReport } from './check.js';
|
|
8
|
+
|
|
9
|
+
export interface CheckedMcp {
|
|
10
|
+
plugins?: readonly LoadedPlugin[];
|
|
11
|
+
report: CheckReport;
|
|
12
|
+
servers: Readonly<Record<string, McpServerConfig>>;
|
|
13
|
+
inventory: Readonly<Record<string, readonly string[]>>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function checkTypeScriptFlow(path: string): Promise<{ report: CheckReport }> {
|
|
17
|
+
try {
|
|
18
|
+
const { handle, getDefinition } = await loadAuthoredFlow(path);
|
|
19
|
+
return await checkMcpHeader(getDefinition(handle), path);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
return { report: inputFailureReport({ kind: 'invalid_spec', message: (error as Error).message }, path) };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Check declarations without executing the authored body. */
|
|
26
|
+
export async function checkMcpHeader(
|
|
27
|
+
definition: Pick<AuthoredFlowDefinition, 'name' | 'header'>,
|
|
28
|
+
path: string,
|
|
29
|
+
): Promise<CheckedMcp> {
|
|
30
|
+
const empty = { servers: Object.freeze({}), inventory: Object.freeze({}) };
|
|
31
|
+
const KNOWN_HEADER_FIELDS = new Set(['tools', 'budget', 'identity', 'memory', 'workspace', 'use']);
|
|
32
|
+
const header = definition.header ?? {};
|
|
33
|
+
const unsupported = Object.keys(header).filter(key => !KNOWN_HEADER_FIELDS.has(key));
|
|
34
|
+
if (header.tools?.relayfile !== undefined) unsupported.push('tools.relayfile');
|
|
35
|
+
if (unsupported.length) {
|
|
36
|
+
return { ...empty, report: inputFailureReport({ kind: 'invalid_spec',
|
|
37
|
+
message: `flow "${definition.name}" uses unsupported header fields: ${unsupported.join(', ')}` }, path) };
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const config = readProjectConfig(dirname(resolve(path)));
|
|
41
|
+
const result = await preflight({ version: SPEC_SCHEMA_VERSION, name: definition.name,
|
|
42
|
+
steps: [{ id: 'header', type: 'deterministic', command: ':' }] }, {
|
|
43
|
+
pluginSearchStart: dirname(resolve(path)),
|
|
44
|
+
mcpServers: header.tools?.mcp ?? [], mcp: config.mcp,
|
|
45
|
+
probes: { command: () => true, cli: () => { throw new Error('no CLI declared'); }, executor: () => false },
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
plugins: result.plugins,
|
|
49
|
+
servers: config.mcp ?? empty.servers,
|
|
50
|
+
inventory: result.mcpTools ?? empty.inventory,
|
|
51
|
+
report: { ...result, path, projectConfigPath: config.path, gates: [],
|
|
52
|
+
diagnostics: result.diagnostics.filter(d => d.stepId !== 'header') },
|
|
53
|
+
};
|
|
54
|
+
} catch (error) {
|
|
55
|
+
return { ...empty, report: inputFailureReport({ kind: 'config_invalid', message: (error as Error).message }, path) };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export class McpPreflightError extends Error {
|
|
60
|
+
constructor(readonly report: CheckReport) {
|
|
61
|
+
super(report.diagnostics.map(d => d.message).join('; '));
|
|
62
|
+
}
|
|
63
|
+
}
|
package/src/cli/check.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { dirname, isAbsolute, join, parse as parsePath, resolve } from 'node:pat
|
|
|
3
3
|
import { spawnSync } from 'node:child_process';
|
|
4
4
|
import { parse as parseYaml } from 'yaml';
|
|
5
5
|
import { CompileError, compileSpec, kernelToAuthoring } from '../compile.js';
|
|
6
|
+
import { helperReady } from '../yaml-helper-effect.js';
|
|
6
7
|
import {
|
|
7
8
|
adapterIdentification,
|
|
8
9
|
authenticationProbe,
|
|
@@ -14,8 +15,10 @@ import {
|
|
|
14
15
|
import { MODEL_ENV } from '../worker-cli.js';
|
|
15
16
|
import { modelNameError } from '../model-name.js';
|
|
16
17
|
import type { FlowSpec } from '../spec.js';
|
|
18
|
+
import type { McpServerConfig } from '../spec.js';
|
|
19
|
+
import { parseMcpConfig } from '../mcp-config.js';
|
|
17
20
|
import type { StepGateInspection } from '../gate-contract.js';
|
|
18
|
-
import type { CheckFailureKind } from '../failure-kinds.js';
|
|
21
|
+
import type { CheckFailureKind, CheckWarningKind } from '../failure-kinds.js';
|
|
19
22
|
import {
|
|
20
23
|
preflight,
|
|
21
24
|
CliProbeError,
|
|
@@ -25,7 +28,9 @@ import {
|
|
|
25
28
|
type PreflightProbes,
|
|
26
29
|
} from '../preflight.js';
|
|
27
30
|
|
|
28
|
-
interface ProjectConfig {
|
|
31
|
+
export interface ProjectConfig {
|
|
32
|
+
deploy?: { bucket: string };
|
|
33
|
+
mcp?: Record<string, McpServerConfig>;
|
|
29
34
|
cli?: string;
|
|
30
35
|
executors: string[];
|
|
31
36
|
models: string[];
|
|
@@ -34,12 +39,19 @@ interface ProjectConfig {
|
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
export interface CheckReport {
|
|
42
|
+
mcpTools?: Readonly<Record<string, readonly string[]>>;
|
|
37
43
|
ok: boolean;
|
|
38
44
|
path?: string;
|
|
39
45
|
projectConfigPath?: string;
|
|
40
46
|
gates: StepGateInspection[];
|
|
41
47
|
resolutions: CliResolution[];
|
|
42
|
-
diagnostics: Array<PreflightDiagnostic | CheckInputDiagnostic>;
|
|
48
|
+
diagnostics: Array<PreflightDiagnostic | CheckInputDiagnostic | CheckWarningDiagnostic>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface CheckWarningDiagnostic {
|
|
52
|
+
severity: 'warning';
|
|
53
|
+
kind: CheckWarningKind;
|
|
54
|
+
message: string;
|
|
43
55
|
}
|
|
44
56
|
|
|
45
57
|
export interface CheckInputDiagnostic {
|
|
@@ -53,7 +65,7 @@ export interface CheckExecution {
|
|
|
53
65
|
flow?: FlowSpec;
|
|
54
66
|
}
|
|
55
67
|
|
|
56
|
-
class CheckFailure extends Error {
|
|
68
|
+
export class CheckFailure extends Error {
|
|
57
69
|
constructor(readonly kind: CheckFailureKind, message: string) {
|
|
58
70
|
super(message);
|
|
59
71
|
}
|
|
@@ -63,8 +75,25 @@ class CheckFailure extends Error {
|
|
|
63
75
|
export function checkFlow(path: string): CheckExecution {
|
|
64
76
|
const absolutePath = resolve(path);
|
|
65
77
|
try {
|
|
66
|
-
const
|
|
67
|
-
|
|
78
|
+
const source = readFlowSource(absolutePath);
|
|
79
|
+
const hint = path.endsWith('.flow.yaml') && !/^\uFEFF?[ \t]*# yaml-language-server:/.test(source)
|
|
80
|
+
? [{ severity: 'warning' as const, kind: 'editor_schema_missing' as const,
|
|
81
|
+
message: 'For editor validation, add this first line: # yaml-language-server: $schema=https://schema.relayflows.dev/v0.1/flows.schema.json' }]
|
|
82
|
+
: [];
|
|
83
|
+
let execution: CheckExecution;
|
|
84
|
+
try {
|
|
85
|
+
execution = checkAuthoredFlow(readFlow(source, absolutePath), path);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (!(error instanceof CheckFailure)) throw error;
|
|
88
|
+
execution = { report: inputFailureReport(error, path) };
|
|
89
|
+
}
|
|
90
|
+
// The editor-schema hint is a documentation nudge, emitted for every
|
|
91
|
+
// .flow.yaml without a first-line yaml-language-server comment.
|
|
92
|
+
// Firing it even when a refusal is present is deliberate: an editor
|
|
93
|
+
// showing squiggles on this file should still tell the author how to
|
|
94
|
+
// wire the schema, so the next edit gets real-time feedback.
|
|
95
|
+
execution.report.diagnostics.push(...hint);
|
|
96
|
+
return execution;
|
|
68
97
|
} catch (error) {
|
|
69
98
|
const failure = error instanceof CheckFailure
|
|
70
99
|
? error
|
|
@@ -74,10 +103,10 @@ export function checkFlow(path: string): CheckExecution {
|
|
|
74
103
|
}
|
|
75
104
|
|
|
76
105
|
/** Preflight a validated authored flow through the same path as YAML/JSON. */
|
|
77
|
-
export function checkAuthoredFlow(authoring: FlowSpec, path: string): CheckExecution {
|
|
106
|
+
export function checkAuthoredFlow(authoring: FlowSpec, path: string, projectConfig?: ProjectConfig): CheckExecution {
|
|
78
107
|
const absolutePath = resolve(path);
|
|
79
108
|
try {
|
|
80
|
-
const config = readProjectConfig(dirname(absolutePath));
|
|
109
|
+
const config = projectConfig ?? readProjectConfig(dirname(absolutePath));
|
|
81
110
|
const probes = systemProbes(dirname(absolutePath), config);
|
|
82
111
|
const result = preflight(authoring, {
|
|
83
112
|
projectCli: config.cli,
|
|
@@ -114,6 +143,72 @@ export function checkAuthoredFlow(authoring: FlowSpec, path: string): CheckExecu
|
|
|
114
143
|
}
|
|
115
144
|
}
|
|
116
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Build-time gate that runs the same preflight pipeline as `checkFlow`
|
|
148
|
+
* but with deferred probes — a build machine is not the deployment target,
|
|
149
|
+
* so `cli`/`command`/`executor` existence is checked at run-time, not here.
|
|
150
|
+
*
|
|
151
|
+
* "Build-provable" refusals (unknown model, `use:` unresolved, invalid
|
|
152
|
+
* verification, missing bundle assets, budget syntax, etc.) still surface,
|
|
153
|
+
* because they are properties of the flow spec, not of the build host.
|
|
154
|
+
*/
|
|
155
|
+
export async function checkBuildableFlow(path: string): Promise<CheckExecution> {
|
|
156
|
+
const absolutePath = resolve(path);
|
|
157
|
+
try {
|
|
158
|
+
const authored = /\.(?:[cm]?[jt]s)$/.test(path);
|
|
159
|
+
if (authored) {
|
|
160
|
+
// TS flows are gated by `buildFlow` itself, which runs
|
|
161
|
+
// `buildTypescript` to compile the authored spec and then invokes
|
|
162
|
+
// preflight with deferred probes at the same refusal threshold as
|
|
163
|
+
// this gate. Running `checkTypeScriptFlow` here would need
|
|
164
|
+
// `@relayflows/surface` to be resolvable from the flow's directory,
|
|
165
|
+
// which is not a build-time invariant. Return an ok report so the
|
|
166
|
+
// gate delegates to `buildFlow`'s inline preflight.
|
|
167
|
+
return {
|
|
168
|
+
report: { ok: true, path, gates: [], resolutions: [], diagnostics: [] },
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
const source = readFlowSource(absolutePath);
|
|
172
|
+
const authoring: FlowSpec = readFlow(source, absolutePath);
|
|
173
|
+
const config = readProjectConfig(dirname(absolutePath));
|
|
174
|
+
const deferred: PreflightProbes = {
|
|
175
|
+
cli: () => { throw new Error('deferred to deployment'); },
|
|
176
|
+
executor: () => { throw new Error('deferred to deployment'); },
|
|
177
|
+
command: () => { throw new Error('deferred to deployment'); },
|
|
178
|
+
};
|
|
179
|
+
const result = preflight(authoring, {
|
|
180
|
+
...(config.cli !== undefined ? { projectCli: config.cli } : {}),
|
|
181
|
+
...(config.path !== undefined ? { projectConfigPath: config.path } : {}),
|
|
182
|
+
projectSearchStart: dirname(absolutePath),
|
|
183
|
+
models: config.models,
|
|
184
|
+
...(config.path !== undefined ? { modelRegistryPath: config.path } : {}),
|
|
185
|
+
probes: deferred,
|
|
186
|
+
});
|
|
187
|
+
// Refusals rooted in build-machine environment probes (`probe_failed`)
|
|
188
|
+
// are excluded from the build gate — the build host is not the
|
|
189
|
+
// deployment target, and those checks are re-run at `flows run`.
|
|
190
|
+
const diagnostics = result.diagnostics.filter(
|
|
191
|
+
(d) => !(d.severity === 'refusal' && d.kind === 'probe_failed'),
|
|
192
|
+
);
|
|
193
|
+
const ok = !diagnostics.some((d) => d.severity === 'refusal');
|
|
194
|
+
return {
|
|
195
|
+
report: {
|
|
196
|
+
ok,
|
|
197
|
+
path,
|
|
198
|
+
...(config.path !== undefined ? { projectConfigPath: config.path } : {}),
|
|
199
|
+
gates: result.gates,
|
|
200
|
+
resolutions: result.resolutions,
|
|
201
|
+
diagnostics,
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
} catch (error) {
|
|
205
|
+
const failure = error instanceof CheckFailure
|
|
206
|
+
? error
|
|
207
|
+
: new CheckFailure('invalid_spec', `Flow "${path}" could not be checked as a Relayflow spec.`);
|
|
208
|
+
return { report: inputFailureReport(failure, path) };
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
117
212
|
export function inputFailureReport(
|
|
118
213
|
failure: { kind: CheckFailureKind; message: string },
|
|
119
214
|
path?: string,
|
|
@@ -127,14 +222,15 @@ export function inputFailureReport(
|
|
|
127
222
|
};
|
|
128
223
|
}
|
|
129
224
|
|
|
130
|
-
function
|
|
131
|
-
let source: string;
|
|
225
|
+
function readFlowSource(path: string): string {
|
|
132
226
|
try {
|
|
133
|
-
|
|
227
|
+
return readFileSync(path, 'utf8');
|
|
134
228
|
} catch {
|
|
135
229
|
throw new CheckFailure('input_unreadable', `Flow "${path}" is not readable.`);
|
|
136
230
|
}
|
|
231
|
+
}
|
|
137
232
|
|
|
233
|
+
function readFlow(source: string, path: string): FlowSpec {
|
|
138
234
|
let parsed: unknown;
|
|
139
235
|
try {
|
|
140
236
|
parsed = parseYaml(source);
|
|
@@ -161,7 +257,7 @@ function readFlow(path: string): FlowSpec {
|
|
|
161
257
|
}
|
|
162
258
|
}
|
|
163
259
|
|
|
164
|
-
function readProjectConfig(start: string): ProjectConfig {
|
|
260
|
+
export function readProjectConfig(start: string): ProjectConfig {
|
|
165
261
|
const configPath = findConfig(start);
|
|
166
262
|
if (configPath === undefined) return { executors: [], models: [], directory: start };
|
|
167
263
|
let value: unknown;
|
|
@@ -170,8 +266,16 @@ function readProjectConfig(start: string): ProjectConfig {
|
|
|
170
266
|
} catch {
|
|
171
267
|
throw new CheckFailure('config_invalid', `Project config "${configPath}" is not valid JSON.`);
|
|
172
268
|
}
|
|
173
|
-
if (!isObject(value) || Object.keys(value).some((key) => !['cli', 'executors', 'models'].includes(key))) {
|
|
174
|
-
throw new CheckFailure('config_invalid', `Project config "${configPath}" expects only cli, executors, and
|
|
269
|
+
if (!isObject(value) || Object.keys(value).some((key) => !['cli', 'executors', 'models', 'mcp', 'deploy', 'plugins'].includes(key))) {
|
|
270
|
+
throw new CheckFailure('config_invalid', `Project config "${configPath}" expects only cli, executors, models, mcp, deploy, and plugins.`);
|
|
271
|
+
}
|
|
272
|
+
if (value['deploy'] !== undefined && (!isObject(value['deploy'])
|
|
273
|
+
|| Object.keys(value['deploy']).some(key => key !== 'bucket')
|
|
274
|
+
|| !isNonEmptyString(value['deploy']['bucket']))) {
|
|
275
|
+
throw new CheckFailure('config_invalid', `Project config "${configPath}" has invalid deploy.bucket.`);
|
|
276
|
+
}
|
|
277
|
+
if (value['plugins'] !== undefined && (!Array.isArray(value['plugins']) || !value['plugins'].every(isNonEmptyString))) {
|
|
278
|
+
throw new CheckFailure('config_invalid', 'Project plugins must be package names.');
|
|
175
279
|
}
|
|
176
280
|
if (value['cli'] !== undefined && !isNonEmptyString(value['cli'])) {
|
|
177
281
|
throw new CheckFailure('config_invalid', `Project config "${configPath}" has an invalid cli.`);
|
|
@@ -193,7 +297,20 @@ function readProjectConfig(start: string): ProjectConfig {
|
|
|
193
297
|
throw new CheckFailure('config_invalid', `Project config "${configPath}" has duplicate models.`);
|
|
194
298
|
}
|
|
195
299
|
}
|
|
300
|
+
let mcp: Record<string, McpServerConfig> | undefined;
|
|
301
|
+
if (value['mcp'] !== undefined) {
|
|
302
|
+
try { mcp = parseMcpConfig(value['mcp']); }
|
|
303
|
+
catch (error) {
|
|
304
|
+
throw new CheckFailure('config_invalid', `Project config "${configPath}": ${(error as Error).message}.`);
|
|
305
|
+
}
|
|
306
|
+
for (const entry of Object.values(mcp)) {
|
|
307
|
+
if ('command' in entry) entry.command = resolveExecutable(entry.command, dirname(configPath))
|
|
308
|
+
?? canonicalCli(entry.command, dirname(configPath));
|
|
309
|
+
}
|
|
310
|
+
}
|
|
196
311
|
return {
|
|
312
|
+
...(value['deploy'] !== undefined ? { deploy: value['deploy'] as { bucket: string } } : {}),
|
|
313
|
+
...(mcp !== undefined ? { mcp } : {}),
|
|
197
314
|
...(value['cli'] !== undefined ? { cli: value['cli'] as string } : {}),
|
|
198
315
|
executors: (value['executors'] as string[] | undefined) ?? [],
|
|
199
316
|
models: (value['models'] as string[] | undefined) ?? [],
|
|
@@ -220,6 +337,7 @@ function findConfig(start: string): string | undefined {
|
|
|
220
337
|
|
|
221
338
|
function systemProbes(flowDirectory: string, config: ProjectConfig): PreflightProbes {
|
|
222
339
|
return {
|
|
340
|
+
helper: helperReady,
|
|
223
341
|
cli: (cli, source, model) => probeCli(cli, source === 'project' ? config.directory : flowDirectory, model),
|
|
224
342
|
executor: (trigger) => config.executors.includes(trigger.executor),
|
|
225
343
|
command: (binary) => executableExists(binary, flowDirectory),
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { CloudFlowError } from '../cloud-http.js';
|
|
2
|
+
import { runInCloud, waitForCloudFlowRun } from '../cloud-run.js';
|
|
3
|
+
import type { CliIo } from '../cli.js';
|
|
4
|
+
|
|
5
|
+
/** Presentation only: the central CLI parser owns argv; the SDK owns the lifecycle. */
|
|
6
|
+
export async function runCloudCli(
|
|
7
|
+
{ value: path, json, wait }: { value: string; json: boolean; wait: boolean },
|
|
8
|
+
io: CliIo,
|
|
9
|
+
): Promise<0 | 1 | 2> {
|
|
10
|
+
const controller = new AbortController();
|
|
11
|
+
const abort = (): void => controller.abort();
|
|
12
|
+
process.once('SIGINT', abort);
|
|
13
|
+
process.once('SIGTERM', abort);
|
|
14
|
+
let runId: string | undefined;
|
|
15
|
+
try {
|
|
16
|
+
const receipt = await runInCloud({ path }, { signal: controller.signal });
|
|
17
|
+
runId = receipt.runId;
|
|
18
|
+
if (!json) {
|
|
19
|
+
io.stdout(`ACCEPTED ${receipt.runId} (${receipt.status})`);
|
|
20
|
+
io.stdout(receipt.apiUrl);
|
|
21
|
+
}
|
|
22
|
+
if (!wait) {
|
|
23
|
+
if (json) io.stdout(JSON.stringify({ ok: true, ...receipt }));
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
const run = await waitForCloudFlowRun(receipt.runId, { signal: controller.signal });
|
|
27
|
+
const ok = run.status === 'completed';
|
|
28
|
+
if (json) io.stdout(JSON.stringify({ ok, ...receipt, ...run }));
|
|
29
|
+
else io.stdout(`${run.status.toUpperCase()} ${run.runId} completionReason: ${'completionReason' in run ? run.completionReason : 'unavailable'}`);
|
|
30
|
+
return ok ? 0 : 1;
|
|
31
|
+
} catch (error) {
|
|
32
|
+
const code = controller.signal.aborted ? (runId ? 'observation_aborted' : 'admission_unknown')
|
|
33
|
+
: error instanceof CloudFlowError ? error.code : 'cloud_run_failed';
|
|
34
|
+
const message = controller.signal.aborted
|
|
35
|
+
? runId ? 'Stopped observing; the hosted run has not been cancelled.'
|
|
36
|
+
: 'Submission interrupted before a receipt was received. Admission is unknown; Cloud may have started the run. Do not resubmit blindly.'
|
|
37
|
+
: error instanceof Error ? error.message : 'Cloud run failed.';
|
|
38
|
+
if (json) io.stdout(JSON.stringify({ ok: false, code, message, ...(runId ? { runId } : {}) }));
|
|
39
|
+
else io.stderr(`${code}: ${message}${runId ? ` (run ${runId})` : ''}`);
|
|
40
|
+
return error instanceof CloudFlowError
|
|
41
|
+
&& (['configuration', 'unsupported_source', 'invalid_input'].includes(error.code)
|
|
42
|
+
|| (runId === undefined && error.code === 'http_error' && [401, 403].includes(error.status ?? 0))) ? 2 : 1;
|
|
43
|
+
} finally {
|
|
44
|
+
process.off('SIGINT', abort);
|
|
45
|
+
process.off('SIGTERM', abort);
|
|
46
|
+
}
|
|
47
|
+
}
|