@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.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { addPlugin } from './cli/add.js';
|
|
3
|
+
import { watchCheck } from './cli-watch.js';
|
|
4
|
+
import { checkHelperBody } from './cli/check-helper-body.js';
|
|
2
5
|
|
|
6
|
+
import { renderProgress, type ProgressEvent } from './progress.js';
|
|
3
7
|
import { realpathSync } from 'node:fs';
|
|
4
8
|
import { pathToFileURL } from 'node:url';
|
|
5
9
|
import {
|
|
@@ -14,10 +18,23 @@ import {
|
|
|
14
18
|
type RunProgress,
|
|
15
19
|
type RunReport,
|
|
16
20
|
} from './cli/run.js';
|
|
21
|
+
import { checkAuthoredTriggers } from './cli/check-triggers.js';
|
|
22
|
+
import { parseWebhookArgs, runServeWebhook } from './cli/serve-webhook.js';
|
|
17
23
|
import { runDirectFlow } from './cli/direct-run.js';
|
|
24
|
+
import { parseReplayArgs, replayJournal, type ReplayArgs } from './cli/replay.js';
|
|
25
|
+
import { checkTypeScriptFlow } from './cli/check-typescript.js';
|
|
26
|
+
import { runCloudCli } from './cli/cloud-run.js';
|
|
18
27
|
import { isAuthoredFlowPath } from './direct-input.js';
|
|
28
|
+
import { parseDeployArgs, runDeploy, type DeployArgs } from './cli/deploy.js';
|
|
29
|
+
import { parseDigestReference } from './bundle-transport.js';
|
|
30
|
+
import { parseBuildArgs, runBuild, type BuildArgs } from './cli/build.js';
|
|
19
31
|
import { runHnMonitor } from './cli/hn-monitor.js';
|
|
20
32
|
import { runTickRunner } from './cli/tick-runner.js';
|
|
33
|
+
import {
|
|
34
|
+
mintObserverUrl,
|
|
35
|
+
resolveObserverLinkEnv,
|
|
36
|
+
type MintObserverOptions,
|
|
37
|
+
} from './observer-link.js';
|
|
21
38
|
|
|
22
39
|
export type { CheckInputDiagnostic, CheckReport } from './cli/check.js';
|
|
23
40
|
|
|
@@ -28,9 +45,16 @@ export interface CliIo {
|
|
|
28
45
|
|
|
29
46
|
type CliExitCode = 0 | 1 | 2 | 3;
|
|
30
47
|
type ParsedArgs =
|
|
31
|
-
| { command: '
|
|
32
|
-
|
|
|
33
|
-
|
|
|
48
|
+
| { command: 'add'; value: string }
|
|
49
|
+
| ReplayArgs
|
|
50
|
+
| BuildArgs
|
|
51
|
+
| DeployArgs
|
|
52
|
+
| { command: 'serve-webhook'; dataDir: string; port: number; admitted?: readonly string[] }
|
|
53
|
+
| { command: 'cloud-run'; value: string; json: boolean; wait: boolean }
|
|
54
|
+
| { command: 'check'; json: boolean; watch: boolean; value: string }
|
|
55
|
+
| { command: 'run'; bucket: string | undefined; reuseFromRunId: string | undefined; localAgent: boolean; dataDir: string; input: string | undefined; json: boolean; spawn: boolean; noObserverLink: boolean; allowHumanInfluenced: boolean; value: string }
|
|
56
|
+
| { command: 'resume'; dataDir: string; json: boolean; spawn: boolean; noObserverLink: boolean; allowHumanInfluenced: boolean; value: string }
|
|
57
|
+
| { command: 'observer'; dataDir: string }
|
|
34
58
|
| { command: 'hn-monitor'; sub: 'start'; dataDir: string; specPath: string; pollIntervalMs: number | undefined }
|
|
35
59
|
| { command: 'tick'; sub: 'start'; dataDir: string; specPath: string; scheduleId: string;
|
|
36
60
|
intervalMs: number; epochMs: number | undefined; maxCatchUp: number | undefined;
|
|
@@ -39,13 +63,22 @@ type ParsedArgs =
|
|
|
39
63
|
const DEFAULT_DATA_DIR = '.relayflowd';
|
|
40
64
|
const USAGE = [
|
|
41
65
|
'Usage:',
|
|
42
|
-
'flows
|
|
43
|
-
'flows
|
|
44
|
-
'flows
|
|
66
|
+
'flows add <helper-name|@flows/helper-name>',
|
|
67
|
+
'flows build [--out <dir>] <flow.yaml|flow.ts>',
|
|
68
|
+
'flows build --verify <bundle-dir>',
|
|
69
|
+
'flows deploy <flow>@sha256:<digest> --to <file-bucket-uri>',
|
|
70
|
+
'flows run <flow>@sha256:<digest> [--bucket <file-bucket-uri>] [--data-dir <dir>] [--json]',
|
|
71
|
+
'flows check [--watch] [--json] <flow.ts|flow.yaml|spec.json>',
|
|
72
|
+
'flows serve-webhook --data-dir <dir> --port <p> [--allow <name>[,<name>]]',
|
|
73
|
+
'flows run [--json] [--no-spawn] [--no-observer-link] [--data-dir <dir>] [--local-agent] [--reuse-from <run-id>] <flow.yaml|spec.json>',
|
|
74
|
+
'flows run --cloud [--json] [--wait] <flow.yaml|spec.json>',
|
|
75
|
+
'flows run [--json] [--no-spawn] [--no-observer-link] [--data-dir <dir>] [--local-agent] <flow.ts> --input <inline-json-or-file>',
|
|
45
76
|
'flows tick start --schedule-id <id> --interval-ms <ms> [--epoch-ms <ms>] [--max-catch-up <n>] [--poll-interval-ms <ms>] [--data-dir <dir>] <spec.json>',
|
|
46
|
-
'flows resume [--json] [--no-spawn] [--data-dir <dir>] <run-id>',
|
|
77
|
+
'flows resume [--allow-human-influenced] [--json] [--no-spawn] [--no-observer-link] [--data-dir <dir>] <run-id>',
|
|
78
|
+
'flows replay [--allow-human-influenced] [--json] [--data-dir <dir>] <run-id> [--at <step-id>]',
|
|
79
|
+
'flows observer [--data-dir <dir>]',
|
|
47
80
|
'flows hn-monitor start [--data-dir <dir>] [--poll-interval-ms <n>] <spec.json>',
|
|
48
|
-
].join('
|
|
81
|
+
].join('\n');
|
|
49
82
|
|
|
50
83
|
/**
|
|
51
84
|
* `FLOWS_NO_SPAWN=1` is `--no-spawn` for a whole environment: the lever for CI
|
|
@@ -66,6 +99,11 @@ export async function runCli(
|
|
|
66
99
|
args: readonly string[],
|
|
67
100
|
io: CliIo = PROCESS_IO,
|
|
68
101
|
): Promise<CliExitCode> {
|
|
102
|
+
if (args.length === 1 && (args[0] === '--help' || args[0] === '-h')) {
|
|
103
|
+
io.stdout(USAGE);
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
69
107
|
const parsed = parseArgs(args);
|
|
70
108
|
if (parsed === undefined) {
|
|
71
109
|
const report = inputFailureReport({ kind: 'invalid_invocation', message: USAGE });
|
|
@@ -73,17 +111,30 @@ export async function runCli(
|
|
|
73
111
|
return 2;
|
|
74
112
|
}
|
|
75
113
|
|
|
114
|
+
if (parsed.command === 'add') return addPlugin(parsed.value, io);
|
|
115
|
+
|
|
116
|
+
if (parsed.command === 'serve-webhook') return runServeWebhook(parsed, io);
|
|
117
|
+
|
|
118
|
+
if (parsed.command === 'cloud-run') return runCloudCli(parsed, io);
|
|
119
|
+
if (parsed.command === 'replay') return replayJournal(parsed, io);
|
|
120
|
+
if (parsed.command === 'build') return runBuild(parsed, io);
|
|
121
|
+
if (parsed.command === 'deploy') return runDeploy(parsed, io);
|
|
122
|
+
|
|
76
123
|
if (parsed.command === 'check') {
|
|
124
|
+
if (parsed.watch) return watchCheck(parsed.value, parsed.json, io);
|
|
77
125
|
// Deliberately daemon-free (kernel/DAEMON-LIFECYCLE.md §4). `checkFlow` is
|
|
78
|
-
// a
|
|
126
|
+
// a compile-and-preflight that opens no daemon socket, and the parser
|
|
79
127
|
// refuses `--data-dir` on `check`, so there is no data dir to attach to.
|
|
80
128
|
// `flows check` keeps working with no daemon, no relayflowd binary and no
|
|
81
129
|
// data directory at all -- a property worth keeping, not an omission.
|
|
82
|
-
const checked =
|
|
130
|
+
const checked = /\.(?:[cm]?[jt]s)$/.test(parsed.value)
|
|
131
|
+
? await checkAuthoredFlowComposed(parsed.value) : checkFlow(parsed.value);
|
|
83
132
|
emitCheckReport(checked.report, parsed.json, io);
|
|
84
133
|
return checked.report.ok ? 0 : 2;
|
|
85
134
|
}
|
|
86
135
|
|
|
136
|
+
if (parsed.command === 'observer') return runObserverCommand(io);
|
|
137
|
+
|
|
87
138
|
if (parsed.command === 'hn-monitor') {
|
|
88
139
|
const controller = new AbortController();
|
|
89
140
|
const onSignal = (): void => controller.abort();
|
|
@@ -130,19 +181,229 @@ export async function runCli(
|
|
|
130
181
|
// single `connect()` seam immediately before journal-client.ts is used --
|
|
131
182
|
// not here. Hoisting it above the dispatch would start a daemon as a side
|
|
132
183
|
// effect of an invocation that is about to be refused for bad input.
|
|
184
|
+
const startedSteps = new Map<string, number>();
|
|
185
|
+
const showProgress = (event: ProgressEvent): void => {
|
|
186
|
+
if (event.type === 'step.started') startedSteps.set(event.stepId, performance.now());
|
|
187
|
+
if (!parsed.json) for (const line of renderProgress([event])) io.stderr(line);
|
|
188
|
+
};
|
|
133
189
|
const lifecycle = {
|
|
134
|
-
|
|
190
|
+
...(parsed.command === 'run' ? { bucket: parsed.bucket } : {}),
|
|
191
|
+
allowHumanInfluenced: parsed.allowHumanInfluenced,
|
|
192
|
+
onPtyReady: (path: string) => io.stderr(`PTY ${path}`),
|
|
193
|
+
...(parsed.command === 'run' && parsed.reuseFromRunId !== undefined ? { reuseFromRunId: parsed.reuseFromRunId } : {}),
|
|
194
|
+
localAgent: parsed.command === 'run' && parsed.localAgent,
|
|
195
|
+
onProgress: showProgress,
|
|
196
|
+
onWait: (progress: RunProgress) => {
|
|
197
|
+
emitWait(progress, io);
|
|
198
|
+
const now = performance.now();
|
|
199
|
+
if (!startedSteps.has(progress.stepId)) startedSteps.set(progress.stepId, now);
|
|
200
|
+
showProgress({ type: 'step.running', stepId: progress.stepId, stepType: progress.stepType,
|
|
201
|
+
elapsedMs: now - startedSteps.get(progress.stepId)! });
|
|
202
|
+
},
|
|
135
203
|
daemon: { spawn: parsed.spawn && spawnAllowedByEnv() },
|
|
136
204
|
};
|
|
205
|
+
// Mint the observer token in parallel with the run so the mint round-trip
|
|
206
|
+
// never adds to the RUN summary latency. The outcome is only consulted at
|
|
207
|
+
// emit time; a rejected promise here can never fail the run (see
|
|
208
|
+
// `observerUrlFrom`, which swallows every failure into `warning`).
|
|
209
|
+
const observerMint = startObserverMint(parsed);
|
|
137
210
|
const execution = parsed.command === 'run'
|
|
138
211
|
? isAuthoredFlowPath(parsed.value)
|
|
139
212
|
? await runDirectFlow(parsed.value, parsed.input, parsed.dataDir, lifecycle)
|
|
140
213
|
: await runFlow(parsed.value, parsed.dataDir, lifecycle)
|
|
141
214
|
: await resumeFlow(parsed.value, parsed.dataDir, lifecycle);
|
|
215
|
+
// In `--json` mode the report is a single machine-readable object that
|
|
216
|
+
// MUST carry `observerUrl` when one is available, so a consumer sees one
|
|
217
|
+
// authoritative signal. That justifies blocking up to `MINT_TIMEOUT_MS`
|
|
218
|
+
// on the mint before emit -- consumers can wait for a bounded time.
|
|
219
|
+
//
|
|
220
|
+
// In plain-text mode the `RUN` line and any check diagnostics carry the
|
|
221
|
+
// primary signal; the observer URL is a nice-to-have follow-up. Blocking
|
|
222
|
+
// the RUN summary on a stalled Relaycast call (up to 5s per failed
|
|
223
|
+
// preflight) is worse than printing `Observer:` on a later line, so we
|
|
224
|
+
// emit the run report immediately and finalize the observer link after.
|
|
225
|
+
if (parsed.json) {
|
|
226
|
+
const observerUrl = await observerUrlFrom(observerMint, io);
|
|
227
|
+
emitRunReport(execution, parsed.json, io, observerUrl);
|
|
228
|
+
return execution.exitCode;
|
|
229
|
+
}
|
|
142
230
|
emitRunReport(execution, parsed.json, io);
|
|
231
|
+
await finalizeObserverLine(observerMint, io);
|
|
143
232
|
return execution.exitCode;
|
|
144
233
|
}
|
|
145
234
|
|
|
235
|
+
/**
|
|
236
|
+
* Compose helper-body checks (from f.slack aspect) with TS-flow MCP checks
|
|
237
|
+
* so an authored .flow.ts surfaces diagnostics from both. Both check
|
|
238
|
+
* functions load the authored flow independently — this can be flattened
|
|
239
|
+
* later, but composing them keeps the rebase minimal and preserves both
|
|
240
|
+
* aspects' coverage.
|
|
241
|
+
*/
|
|
242
|
+
async function checkAuthoredFlowComposed(path: string): Promise<{ report: CheckReport }> {
|
|
243
|
+
const helper = await checkHelperBody(path);
|
|
244
|
+
if (!helper.report.ok) return helper;
|
|
245
|
+
const mcp = await checkTypeScriptFlow(path);
|
|
246
|
+
const triggers = isAuthoredFlowPath(path)
|
|
247
|
+
? await checkAuthoredTriggers(path)
|
|
248
|
+
: undefined;
|
|
249
|
+
const triggerDiagnostics = triggers?.report.diagnostics ?? [];
|
|
250
|
+
const triggerOk = triggers?.report.ok ?? true;
|
|
251
|
+
return {
|
|
252
|
+
report: {
|
|
253
|
+
...mcp.report,
|
|
254
|
+
diagnostics: [...helper.report.diagnostics, ...mcp.report.diagnostics, ...triggerDiagnostics],
|
|
255
|
+
ok: helper.report.ok && mcp.report.ok && triggerOk,
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Start the observer-token mint if the environment says one should happen.
|
|
262
|
+
* Returns `undefined` when no attempt should be made — no workspace key
|
|
263
|
+
* configured, or `FLOWS_NO_OBSERVER=1` / `--no-observer-link` — which is the
|
|
264
|
+
* silent-skip branch. The returned promise always resolves; a rejection here
|
|
265
|
+
* would slip past `observerUrlFrom` and could fail the run, which the feature
|
|
266
|
+
* expressly forbids.
|
|
267
|
+
*/
|
|
268
|
+
function startObserverMint(
|
|
269
|
+
parsed: { command: 'run' | 'resume'; noObserverLink: boolean },
|
|
270
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
271
|
+
mint: (options: MintObserverOptions) => Promise<{ observerUrl?: string; warning?: string }> = mintObserverUrl,
|
|
272
|
+
): Promise<{ observerUrl?: string; warning?: string }> | undefined {
|
|
273
|
+
if (parsed.noObserverLink) return undefined;
|
|
274
|
+
// `resolveObserverLinkEnv` (not `readObserverLinkEnv`) falls back to the
|
|
275
|
+
// `agent-relay cloud login` workspace store (~/.agentworkforce/relay/
|
|
276
|
+
// workspaces.json) when RELAYCAST_WORKSPACE_KEY is unset. Env wins if set;
|
|
277
|
+
// FLOWS_NO_OBSERVER=1 still suppresses regardless of source.
|
|
278
|
+
const link = resolveObserverLinkEnv(env);
|
|
279
|
+
if (link.suppressed || link.workspaceKey === undefined) return undefined;
|
|
280
|
+
return mint({
|
|
281
|
+
workspaceKey: link.workspaceKey,
|
|
282
|
+
...(link.baseUrl !== undefined ? { baseUrl: link.baseUrl } : {}),
|
|
283
|
+
...(link.dashboardUrl !== undefined ? { dashboardUrl: link.dashboardUrl } : {}),
|
|
284
|
+
}).catch((error) => ({
|
|
285
|
+
warning: error instanceof Error ? error.message : 'unknown mint error',
|
|
286
|
+
}));
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Resolve the pending observer-mint into a URL (or nothing), and route any
|
|
291
|
+
* mint warning to stderr under a `[observer]` label. Silent-skip (`undefined`
|
|
292
|
+
* input) prints nothing at all, so a workspace with no observer configured
|
|
293
|
+
* produces no observer output on stderr or stdout.
|
|
294
|
+
*/
|
|
295
|
+
async function observerUrlFrom(
|
|
296
|
+
mint: Promise<{ observerUrl?: string; warning?: string }> | undefined,
|
|
297
|
+
io: CliIo,
|
|
298
|
+
): Promise<string | undefined> {
|
|
299
|
+
if (mint === undefined) return undefined;
|
|
300
|
+
const outcome = await mint;
|
|
301
|
+
if (outcome.warning !== undefined) {
|
|
302
|
+
io.stderr(`[observer] token mint failed: ${outcome.warning}; skipping observer link`);
|
|
303
|
+
}
|
|
304
|
+
return outcome.observerUrl;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Grace budget the plain-text emit path waits for a still-pending mint after
|
|
309
|
+
* the RUN summary is out. `mintObserverUrl` already caps its own network
|
|
310
|
+
* round-trip at `MINT_TIMEOUT_MS` (5s). The grace matches that ceiling so a
|
|
311
|
+
* slow-but-legitimate mint (empirically ~1.6s cold against
|
|
312
|
+
* `cast.agentrelay.com`) is not clipped by a shorter grace. A mint that has
|
|
313
|
+
* not resolved by 5s is genuinely stuck.
|
|
314
|
+
*/
|
|
315
|
+
const OBSERVER_FINALIZE_GRACE_MS = 5_000;
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Finalize the plain-text observer line after the RUN summary is already on
|
|
319
|
+
* stdout. If the mint resolves in time, print `Observer: <url>` on its own
|
|
320
|
+
* stdout line (matching v1 relayflows' convention). If it fails, print the
|
|
321
|
+
* standard `[observer]` diagnostic on stderr. If it is still pending after
|
|
322
|
+
* `OBSERVER_FINALIZE_GRACE_MS`, print a distinct stderr line so the operator
|
|
323
|
+
* knows the mint did not complete rather than seeing silence -- and stop
|
|
324
|
+
* waiting so the CLI can exit.
|
|
325
|
+
*/
|
|
326
|
+
/**
|
|
327
|
+
* `flows observer`: mint and print a single observer URL. Reuses the same
|
|
328
|
+
* env parsing (`readObserverLinkEnv`) and mint (`mintObserverUrl`) as the
|
|
329
|
+
* run/resume verbs, so an operator who already has an observer URL working
|
|
330
|
+
* via `flows run` will get the identical URL shape here. Every failure
|
|
331
|
+
* path -- unset key, suppressed via `FLOWS_NO_OBSERVER`, mint HTTP error,
|
|
332
|
+
* network error, malformed response -- is refused with `REFUSED
|
|
333
|
+
* [observer_link_unavailable] <reason>` on stderr and exit 2, matching the
|
|
334
|
+
* flows CLI refusal shape (see `parseArgs` -> `invalid_invocation`).
|
|
335
|
+
*/
|
|
336
|
+
async function runObserverCommand(
|
|
337
|
+
io: CliIo,
|
|
338
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
339
|
+
mint: (options: MintObserverOptions) => Promise<{ observerUrl?: string; warning?: string }> = mintObserverUrl,
|
|
340
|
+
): Promise<CliExitCode> {
|
|
341
|
+
// Same resolution as `flows run`: env wins, then the `agent-relay cloud
|
|
342
|
+
// login` workspace store. The refusal message names the env var because
|
|
343
|
+
// that is the primary path an operator is expected to configure; the
|
|
344
|
+
// cloud-login fallback is convenience, not the documented contract.
|
|
345
|
+
const link = resolveObserverLinkEnv(env);
|
|
346
|
+
if (link.suppressed) {
|
|
347
|
+
io.stderr('REFUSED [observer_link_unavailable] mint suppressed by FLOWS_NO_OBSERVER=1');
|
|
348
|
+
return 2;
|
|
349
|
+
}
|
|
350
|
+
if (link.workspaceKey === undefined) {
|
|
351
|
+
io.stderr(
|
|
352
|
+
'REFUSED [observer_link_unavailable] no workspace key configured; '
|
|
353
|
+
+ 'set RELAYCAST_WORKSPACE_KEY or run `agent-relay workspace set_key`',
|
|
354
|
+
);
|
|
355
|
+
return 2;
|
|
356
|
+
}
|
|
357
|
+
const outcome: { observerUrl?: string; warning?: string } = await mint({
|
|
358
|
+
workspaceKey: link.workspaceKey,
|
|
359
|
+
...(link.baseUrl !== undefined ? { baseUrl: link.baseUrl } : {}),
|
|
360
|
+
}).catch((error): { warning: string } => ({
|
|
361
|
+
warning: error instanceof Error ? error.message : 'unknown mint error',
|
|
362
|
+
}));
|
|
363
|
+
if (outcome.observerUrl === undefined) {
|
|
364
|
+
// Fold the mint's own warning into the refusal so an operator sees the
|
|
365
|
+
// same phrasing they would have gotten under `flows run` -- no extra
|
|
366
|
+
// interpretation, no lossy summary.
|
|
367
|
+
const reason = outcome.warning ?? 'mint returned no URL';
|
|
368
|
+
io.stderr(`REFUSED [observer_link_unavailable] ${reason}`);
|
|
369
|
+
return 2;
|
|
370
|
+
}
|
|
371
|
+
io.stdout(outcome.observerUrl);
|
|
372
|
+
return 0;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export async function finalizeObserverLine(
|
|
376
|
+
mint: Promise<{ observerUrl?: string; warning?: string }> | undefined,
|
|
377
|
+
io: CliIo,
|
|
378
|
+
graceMs: number = OBSERVER_FINALIZE_GRACE_MS,
|
|
379
|
+
): Promise<void> {
|
|
380
|
+
if (mint === undefined) return;
|
|
381
|
+
// A race between the mint promise and a bounded timer. Using a sentinel
|
|
382
|
+
// symbol (not `undefined`) so we can tell "grace expired" from "mint
|
|
383
|
+
// resolved to no URL and no warning" -- which should never happen, but
|
|
384
|
+
// must not silently claim a timeout if it does.
|
|
385
|
+
const TIMED_OUT: unique symbol = Symbol('observer-finalize-timeout') as never;
|
|
386
|
+
let timer: NodeJS.Timeout | undefined;
|
|
387
|
+
const timeout = new Promise<typeof TIMED_OUT>((resolve) => {
|
|
388
|
+
timer = setTimeout(() => resolve(TIMED_OUT), graceMs);
|
|
389
|
+
// Do not let this timer prolong the event loop past a clean exit; if the
|
|
390
|
+
// mint promise resolves first we clear it below, if the process is
|
|
391
|
+
// otherwise idle Node should not wait for us to give up.
|
|
392
|
+
timer.unref?.();
|
|
393
|
+
});
|
|
394
|
+
const outcome = await Promise.race([mint, timeout]);
|
|
395
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
396
|
+
if (outcome === TIMED_OUT) {
|
|
397
|
+
io.stderr('[observer] mint did not complete in time; skipping observer link');
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (outcome.warning !== undefined) {
|
|
401
|
+
io.stderr(`[observer] token mint failed: ${outcome.warning}; skipping observer link`);
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
if (outcome.observerUrl !== undefined) io.stdout(`Observer: ${outcome.observerUrl}`);
|
|
405
|
+
}
|
|
406
|
+
|
|
146
407
|
function emitWait(
|
|
147
408
|
progress: { runId: string; stepId: string; stepType: string; leaseDeadlineMs: number },
|
|
148
409
|
io: CliIo,
|
|
@@ -155,19 +416,54 @@ function emitWait(
|
|
|
155
416
|
|
|
156
417
|
function parseArgs(args: readonly string[]): ParsedArgs | undefined {
|
|
157
418
|
const command = args[0];
|
|
419
|
+
if (command === 'add') return args.length === 2 ? { command: 'add', value: args[1]! } : undefined;
|
|
420
|
+
if (command === 'replay') return parseReplayArgs(args.slice(1));
|
|
421
|
+
if (command === 'build') return parseBuildArgs(args.slice(1));
|
|
422
|
+
if (command === 'deploy') return parseDeployArgs(args.slice(1));
|
|
423
|
+
if (command === 'serve-webhook') return parseWebhookArgs(args.slice(1));
|
|
158
424
|
if (command === 'hn-monitor') return parseHnMonitorArgs(args.slice(1));
|
|
159
425
|
if (command === 'tick') return parseTickArgs(args.slice(1));
|
|
426
|
+
if (command === 'observer') return parseObserverArgs(args.slice(1));
|
|
160
427
|
if (command !== 'check' && command !== 'run' && command !== 'resume') return undefined;
|
|
161
428
|
|
|
162
429
|
let json = false;
|
|
430
|
+
let watch = false;
|
|
431
|
+
let cloud = false;
|
|
432
|
+
let wait = false;
|
|
433
|
+
let localAgent = false;
|
|
434
|
+
let allowHumanInfluenced = false;
|
|
163
435
|
let dataDir = DEFAULT_DATA_DIR;
|
|
164
436
|
let sawDataDir = false;
|
|
165
437
|
let spawn = true;
|
|
438
|
+
let noObserverLink = false;
|
|
166
439
|
let input: string | undefined;
|
|
167
440
|
let sawInput = false;
|
|
441
|
+
let reuseFromRunId: string | undefined;
|
|
442
|
+
let bucket: string | undefined;
|
|
168
443
|
const positionals: string[] = [];
|
|
169
444
|
for (let index = 1; index < args.length; index += 1) {
|
|
170
445
|
const argument = args[index]!;
|
|
446
|
+
if (argument === '--cloud' || argument === '--wait') {
|
|
447
|
+
if (command !== 'run' || (argument === '--cloud' ? cloud : wait)) return undefined;
|
|
448
|
+
if (argument === '--cloud') cloud = true;
|
|
449
|
+
else wait = true;
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
if (argument === '--allow-human-influenced') {
|
|
453
|
+
if (command === 'check' || allowHumanInfluenced) return undefined;
|
|
454
|
+
allowHumanInfluenced = true;
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
if (argument === '--local-agent') {
|
|
458
|
+
if (command !== 'run' || localAgent) return undefined;
|
|
459
|
+
localAgent = true;
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
if (argument === '--watch') {
|
|
463
|
+
if (command !== 'check' || watch) return undefined;
|
|
464
|
+
watch = true;
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
171
467
|
if (argument === '--json') {
|
|
172
468
|
if (json) return undefined;
|
|
173
469
|
json = true;
|
|
@@ -180,6 +476,13 @@ function parseArgs(args: readonly string[]): ParsedArgs | undefined {
|
|
|
180
476
|
spawn = false;
|
|
181
477
|
continue;
|
|
182
478
|
}
|
|
479
|
+
if (argument === '--no-observer-link') {
|
|
480
|
+
// Only meaningful on verbs that actually emit the observer line — `run`
|
|
481
|
+
// and `resume`. Refused elsewhere so the flag never silently no-ops.
|
|
482
|
+
if (command === 'check' || noObserverLink) return undefined;
|
|
483
|
+
noObserverLink = true;
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
183
486
|
if (argument === '--data-dir') {
|
|
184
487
|
const value = args[index + 1];
|
|
185
488
|
if (command === 'check' || sawDataDir || value === undefined || value.startsWith('-')) return undefined;
|
|
@@ -188,6 +491,19 @@ function parseArgs(args: readonly string[]): ParsedArgs | undefined {
|
|
|
188
491
|
index += 1;
|
|
189
492
|
continue;
|
|
190
493
|
}
|
|
494
|
+
if (argument === '--reuse-from') {
|
|
495
|
+
const value = args[index + 1];
|
|
496
|
+
if (command !== 'run' || reuseFromRunId !== undefined || !value || value.startsWith('-')) return undefined;
|
|
497
|
+
reuseFromRunId = value;
|
|
498
|
+
index += 1;
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
if (argument === '--bucket') {
|
|
502
|
+
const value = args[++index];
|
|
503
|
+
if (command !== 'run' || bucket !== undefined || !value || value.startsWith('-')) return undefined;
|
|
504
|
+
bucket = value;
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
191
507
|
if (argument === '--input') {
|
|
192
508
|
const value = args[index + 1];
|
|
193
509
|
if (command !== 'run' || sawInput || value === undefined || value.startsWith('--')) return undefined;
|
|
@@ -201,12 +517,24 @@ function parseArgs(args: readonly string[]): ParsedArgs | undefined {
|
|
|
201
517
|
}
|
|
202
518
|
if (positionals.length !== 1) return undefined;
|
|
203
519
|
|
|
520
|
+
if (bucket !== undefined && (cloud || !parseDigestReference(positionals[0]!))) return undefined;
|
|
521
|
+
if (cloud) {
|
|
522
|
+
// `--cloud` submits the spec to Cloud, so every flag that only describes a
|
|
523
|
+
// local run -- an inline input, a data dir, a suppressed daemon, a local
|
|
524
|
+
// agent, a local observer-link opt-out -- describes nothing there and is
|
|
525
|
+
// refused rather than ignored.
|
|
526
|
+
if (allowHumanInfluenced || sawInput || sawDataDir || !spawn || localAgent || noObserverLink || reuseFromRunId !== undefined) return undefined;
|
|
527
|
+
return { command: 'cloud-run', value: positionals[0]!, json, wait };
|
|
528
|
+
}
|
|
529
|
+
if (wait) return undefined;
|
|
530
|
+
if (reuseFromRunId !== undefined && isAuthoredFlowPath(positionals[0]!)) return undefined;
|
|
531
|
+
|
|
204
532
|
if (command === 'run' && input !== undefined && !isAuthoredFlowPath(positionals[0]!)) return undefined;
|
|
205
533
|
return command === 'check'
|
|
206
|
-
? { command, json, value: positionals[0]! }
|
|
534
|
+
? { command, json, watch, value: positionals[0]! }
|
|
207
535
|
: command === 'run'
|
|
208
|
-
? { command, dataDir, input, json, spawn, value: positionals[0]! }
|
|
209
|
-
: { command, dataDir, json, spawn, value: positionals[0]! };
|
|
536
|
+
? { command, bucket, reuseFromRunId, localAgent, dataDir, input, json, spawn, noObserverLink, allowHumanInfluenced, value: positionals[0]! }
|
|
537
|
+
: { command, dataDir, json, spawn, noObserverLink, allowHumanInfluenced, value: positionals[0]! };
|
|
210
538
|
}
|
|
211
539
|
|
|
212
540
|
function parseHnMonitorArgs(rest: readonly string[]): ParsedArgs | undefined {
|
|
@@ -243,6 +571,35 @@ function parseHnMonitorArgs(rest: readonly string[]): ParsedArgs | undefined {
|
|
|
243
571
|
return { command: 'hn-monitor', sub: 'start', dataDir, specPath: positionals[0]!, pollIntervalMs };
|
|
244
572
|
}
|
|
245
573
|
|
|
574
|
+
/**
|
|
575
|
+
* `flows observer`. On-demand mint verb: prints an observer URL without
|
|
576
|
+
* running a flow. Takes an optional `--data-dir` (accepted for parity with
|
|
577
|
+
* other verbs, so operators can share one invocation shape across the
|
|
578
|
+
* surface) but does not open a socket, spawn a daemon, or touch the data
|
|
579
|
+
* directory at all -- the mint is a pure Relaycast API round-trip. No
|
|
580
|
+
* positional argument, no other flags.
|
|
581
|
+
*/
|
|
582
|
+
function parseObserverArgs(rest: readonly string[]): ParsedArgs | undefined {
|
|
583
|
+
let dataDir = DEFAULT_DATA_DIR;
|
|
584
|
+
let sawDataDir = false;
|
|
585
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
586
|
+
const argument = rest[index]!;
|
|
587
|
+
if (argument === '--data-dir') {
|
|
588
|
+
const value = rest[index + 1];
|
|
589
|
+
if (sawDataDir || value === undefined || value.startsWith('-')) return undefined;
|
|
590
|
+
dataDir = value;
|
|
591
|
+
sawDataDir = true;
|
|
592
|
+
index += 1;
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
// Any positional or unknown flag is a shape error: this verb has no
|
|
596
|
+
// spec-path or run-id argument, so silently ignoring extras would be
|
|
597
|
+
// worse than refusing them.
|
|
598
|
+
return undefined;
|
|
599
|
+
}
|
|
600
|
+
return { command: 'observer', dataDir };
|
|
601
|
+
}
|
|
602
|
+
|
|
246
603
|
/**
|
|
247
604
|
* `flows tick start`. Numeric flags are parsed here but their BOUNDS are not
|
|
248
605
|
* re-derived: `runTickRunner` calls `assertTickScheduleValid`, the same
|
|
@@ -342,19 +699,41 @@ function emitCheckReport(report: CheckReport, json: boolean, io: CliIo): void {
|
|
|
342
699
|
if (report.ok) io.stdout(`CHECK PASSED ${report.path ?? ''}`.trimEnd());
|
|
343
700
|
}
|
|
344
701
|
|
|
345
|
-
function emitRunReport(
|
|
702
|
+
function emitRunReport(
|
|
703
|
+
execution: RunExecution,
|
|
704
|
+
json: boolean,
|
|
705
|
+
io: CliIo,
|
|
706
|
+
observerUrl?: string,
|
|
707
|
+
): void {
|
|
346
708
|
const { report } = execution;
|
|
347
709
|
emitDiagnostics(report.diagnostics, io);
|
|
348
710
|
if (json) {
|
|
349
|
-
|
|
711
|
+
// Fold `observerUrl` into the JSON report as a sibling of `runId`, so
|
|
712
|
+
// downstream tooling that consumes `--json` gets the same signal a
|
|
713
|
+
// human reader gets from the plain-text `Observer:` line.
|
|
714
|
+
const payload = observerUrl === undefined ? report : { ...report, observerUrl };
|
|
715
|
+
io.stdout(JSON.stringify(payload));
|
|
350
716
|
return;
|
|
351
717
|
}
|
|
352
718
|
if (report.runId === undefined) return;
|
|
353
|
-
const completed = report.completedSteps === undefined ? '' : ` (${report.completedSteps} steps)`;
|
|
719
|
+
const completed = report.completedSteps === undefined ? '' : ` (${report.completedSteps} ${report.completedSteps === 1 ? 'step' : 'steps'})`;
|
|
354
720
|
const reason = report.completionReason === undefined
|
|
355
721
|
? ''
|
|
356
722
|
: ` completionReason: ${report.completionReason}`;
|
|
357
723
|
io.stdout(`RUN ${report.runId} ${report.status ?? 'unknown'}${completed}${reason}`);
|
|
724
|
+
if (report.reuse !== undefined) {
|
|
725
|
+
io.stdout(`REUSE from ${report.reuse.fromRunId}: ${report.reuse.reusedSteps} reused, ${report.reuse.executedSteps} executed`);
|
|
726
|
+
}
|
|
727
|
+
// The observer line no longer rides inline with the RUN summary in
|
|
728
|
+
// plain-text mode: a slow mint used to hold back this whole line and any
|
|
729
|
+
// check diagnostics for up to `MINT_TIMEOUT_MS`. `finalizeObserverLine`
|
|
730
|
+
// now emits `Observer: <url>` as its own follow-up line after this one,
|
|
731
|
+
// matching v1 relayflows' output shape (which also printed the observer
|
|
732
|
+
// URL on its own line). The `observerUrl` parameter is kept for callers
|
|
733
|
+
// that already have a URL ready and want it inline; today only the
|
|
734
|
+
// `--json` path takes that branch (where the URL folds into the JSON
|
|
735
|
+
// payload above), so the plain-text pass through here never uses it.
|
|
736
|
+
if (observerUrl !== undefined) io.stdout(`Observer: ${observerUrl}`);
|
|
358
737
|
}
|
|
359
738
|
|
|
360
739
|
function emitDiagnostics(
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
export interface CloudConnectionOptions {
|
|
2
|
+
/** Cloud application base URL; defaults to https://agentrelay.com/cloud. */
|
|
3
|
+
apiUrl?: string;
|
|
4
|
+
/** Scoped Cloud API token; never a Relay workspace key. */
|
|
5
|
+
token?: string;
|
|
6
|
+
signal?: AbortSignal;
|
|
7
|
+
/** Bounds each HTTP request, never the hosted run. Defaults to 30 seconds. */
|
|
8
|
+
requestTimeoutMs?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class CloudFlowError extends Error {
|
|
12
|
+
constructor(
|
|
13
|
+
readonly code: 'configuration' | 'unsupported_source' | 'invalid_input' | 'invalid_response' | 'http_error' | 'transport_error' | 'transient_error',
|
|
14
|
+
message: string,
|
|
15
|
+
readonly status?: number,
|
|
16
|
+
) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = 'CloudFlowError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function cloudConnection(options: CloudConnectionOptions): { baseUrl: string; token: string } {
|
|
23
|
+
const rawToken = options.token ?? process.env['FLOWS_CLOUD_TOKEN'];
|
|
24
|
+
const token = rawToken?.trim();
|
|
25
|
+
if (!token || /[\r\n]/u.test(rawToken!) || /^(?:rk|ot)_live_/u.test(token)) {
|
|
26
|
+
throw new CloudFlowError('configuration',
|
|
27
|
+
'Set FLOWS_CLOUD_TOKEN to a scoped Cloud API token (workflow:invoke:write and workflow:runs:read).');
|
|
28
|
+
}
|
|
29
|
+
let url: URL;
|
|
30
|
+
try {
|
|
31
|
+
url = new URL(options.apiUrl ?? process.env['FLOWS_CLOUD_URL'] ?? 'https://agentrelay.com/cloud');
|
|
32
|
+
} catch {
|
|
33
|
+
throw new CloudFlowError('configuration', 'FLOWS_CLOUD_URL must be an absolute Cloud application base URL.');
|
|
34
|
+
}
|
|
35
|
+
if (url.protocol !== 'https:'
|
|
36
|
+
|| url.username || url.password || url.search || url.hash || !/^\/[A-Za-z0-9/_-]*$/u.test(url.pathname)) {
|
|
37
|
+
throw new CloudFlowError('configuration', 'Cloud URL must use HTTPS and a plain base path.');
|
|
38
|
+
}
|
|
39
|
+
return { baseUrl: `${url.origin}${url.pathname.replace(/\/+$/u, '')}`, token };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function cloudRequest(
|
|
43
|
+
path: string,
|
|
44
|
+
options: CloudConnectionOptions,
|
|
45
|
+
body?: unknown,
|
|
46
|
+
): Promise<unknown> {
|
|
47
|
+
const { baseUrl, token } = cloudConnection(options);
|
|
48
|
+
const timeout = options.requestTimeoutMs ?? 30_000;
|
|
49
|
+
if (!Number.isSafeInteger(timeout) || timeout < 1 || timeout > 2_147_483_647) {
|
|
50
|
+
throw new CloudFlowError('configuration', 'requestTimeoutMs must be a positive 32-bit integer.');
|
|
51
|
+
}
|
|
52
|
+
const deadline = AbortSignal.timeout(timeout);
|
|
53
|
+
let response: Response;
|
|
54
|
+
try {
|
|
55
|
+
response = await fetch(`${baseUrl}${path}`, {
|
|
56
|
+
method: body === undefined ? 'GET' : 'POST',
|
|
57
|
+
headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },
|
|
58
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
59
|
+
signal: options.signal ? AbortSignal.any([options.signal, deadline]) : deadline,
|
|
60
|
+
// A redirect must never carry the credential to a different origin.
|
|
61
|
+
redirect: 'error',
|
|
62
|
+
});
|
|
63
|
+
} catch (error) {
|
|
64
|
+
options.signal?.throwIfAborted();
|
|
65
|
+
throw transportError(deadline.aborted ? deadline.reason : error);
|
|
66
|
+
}
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
// Do not echo server response bodies: they may contain credentials or source.
|
|
69
|
+
throw new CloudFlowError('http_error', `Cloud request failed with HTTP ${response.status}.`, response.status);
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
return await response.json();
|
|
73
|
+
} catch (error) {
|
|
74
|
+
options.signal?.throwIfAborted();
|
|
75
|
+
if (!(error instanceof SyntaxError)) throw transportError(deadline.aborted ? deadline.reason : error);
|
|
76
|
+
throw new CloudFlowError('invalid_response', 'Cloud returned a non-JSON response.');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Defensive path-segment constraint; accepting a new server ID format needs an SDK change.
|
|
81
|
+
export function cloudRunId(value: unknown): string {
|
|
82
|
+
if (typeof value !== 'string' || !/^[A-Za-z0-9_-]{1,128}$/u.test(value)) {
|
|
83
|
+
throw new CloudFlowError('invalid_response', 'Cloud run ID must contain only letters, digits, underscores, or hyphens.');
|
|
84
|
+
}
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function isCloudRecord(value: unknown): value is Record<string, unknown> {
|
|
89
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function transportError(error: unknown): CloudFlowError {
|
|
93
|
+
const cause = error instanceof Error ? error.cause : undefined;
|
|
94
|
+
const code = isCloudRecord(cause) ? cause.code : undefined;
|
|
95
|
+
const transient = (error instanceof Error && error.name === 'TimeoutError')
|
|
96
|
+
|| ['ECONNRESET', 'ECONNREFUSED', 'EAI_AGAIN', 'ETIMEDOUT',
|
|
97
|
+
'UND_ERR_CONNECT_TIMEOUT', 'UND_ERR_HEADERS_TIMEOUT', 'UND_ERR_BODY_TIMEOUT', 'UND_ERR_SOCKET'].includes(String(code));
|
|
98
|
+
return new CloudFlowError(transient ? 'transient_error' : 'transport_error',
|
|
99
|
+
transient ? 'Cloud transport temporarily unavailable.' : 'Cloud transport failed; check TLS and the configured URL (redirects are refused).');
|
|
100
|
+
}
|