stageflow 0.23.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -5
- package/dist/a2a/configDiscovery.d.ts +9 -0
- package/dist/a2a/configDiscovery.js +18 -0
- package/dist/a2a/contracts.d.ts +94 -0
- package/dist/a2a/contracts.js +164 -0
- package/dist/a2a/limits.d.ts +14 -0
- package/dist/a2a/limits.js +29 -0
- package/dist/a2a/registry.d.ts +46 -0
- package/dist/a2a/registry.js +178 -0
- package/dist/a2a/server.d.ts +36 -0
- package/dist/a2a/server.js +344 -0
- package/dist/a2a/service.d.ts +97 -0
- package/dist/a2a/service.js +545 -0
- package/dist/a2a/store.d.ts +109 -0
- package/dist/a2a/store.js +202 -0
- package/dist/agent/activity.d.ts +3 -0
- package/dist/agent/bootProviderConfig.d.ts +21 -0
- package/dist/agent/bootProviderConfig.js +74 -0
- package/dist/agent/piAdapter.d.ts +9 -1
- package/dist/agent/piAdapter.js +58 -3
- package/dist/agent/piIsolatedMcp.d.ts +2 -0
- package/dist/agent/piIsolatedMcp.js +10 -2
- package/dist/agent/port.d.ts +6 -0
- package/dist/agent/streamLogRedact.d.ts +1 -1
- package/dist/agent/streamLogRedact.js +4 -11
- package/dist/cli/a2aCommand.d.ts +8 -0
- package/dist/cli/a2aCommand.js +131 -0
- package/dist/cli/artifactCommand.js +9 -16
- package/dist/cli/backupCommand.d.ts +9 -0
- package/dist/cli/backupCommand.js +111 -0
- package/dist/cli/debugRunCommand.d.ts +10 -0
- package/dist/cli/debugRunCommand.js +109 -0
- package/dist/cli/doctorCommand.d.ts +32 -0
- package/dist/cli/doctorCommand.js +453 -0
- package/dist/cli/envelopeCommand.js +9 -2
- package/dist/cli/exportAllCommand.d.ts +55 -0
- package/dist/cli/exportAllCommand.js +218 -0
- package/dist/cli/exportRunCommand.js +23 -21
- package/dist/cli/hostClient.d.ts +21 -3
- package/dist/cli/hostClient.js +93 -12
- package/dist/cli/initTemplates.d.ts +1 -1
- package/dist/cli/initTemplates.js +8 -1
- package/dist/cli/resolveSafeOutPath.d.ts +2 -0
- package/dist/cli/resolveSafeOutPath.js +40 -0
- package/dist/cli/restoreCommand.d.ts +10 -0
- package/dist/cli/restoreCommand.js +90 -0
- package/dist/cli/runCommand.d.ts +3 -2
- package/dist/cli/runCommand.js +93 -11
- package/dist/cli/runOutput.d.ts +4 -0
- package/dist/cli/runOutput.js +33 -7
- package/dist/cli/runStageCommand.d.ts +34 -0
- package/dist/cli/runStageCommand.js +426 -0
- package/dist/cli/runsCommand.d.ts +1 -1
- package/dist/cli/runsCommand.js +162 -8
- package/dist/cli.js +190 -30
- package/dist/config/catalogRelativePath.d.ts +62 -0
- package/dist/config/catalogRelativePath.js +118 -0
- package/dist/config/configOrigin.d.ts +27 -0
- package/dist/config/configOrigin.js +59 -0
- package/dist/config/hostConfig.d.ts +55 -0
- package/dist/config/hostConfig.js +333 -0
- package/dist/config/listSkills.d.ts +26 -0
- package/dist/config/listSkills.js +190 -3
- package/dist/config/loadOutcome.d.ts +1 -1
- package/dist/config/loadPipeline.js +23 -2
- package/dist/config/loadStage.js +18 -0
- package/dist/config/loadTask.d.ts +2 -0
- package/dist/config/loadTask.js +57 -18
- package/dist/config/materializeStageModels.js +5 -1
- package/dist/config/mergePipelineIncludes.d.ts +3 -0
- package/dist/config/mergePipelineIncludes.js +19 -0
- package/dist/config/migrateYaml.js +6 -11
- package/dist/config/multiProjectCatalog.d.ts +30 -0
- package/dist/config/multiProjectCatalog.js +142 -0
- package/dist/config/normalizePipelineStageEntry.js +20 -1
- package/dist/config/pipelineStageKeys.js +2 -0
- package/dist/config/resolveCatalogRoots.d.ts +27 -0
- package/dist/config/resolveCatalogRoots.js +76 -0
- package/dist/config/resolveModel.d.ts +7 -1
- package/dist/config/resolveModel.js +11 -1
- package/dist/config/resolveStageMcpServers.d.ts +9 -1
- package/dist/config/resolveStageMcpServers.js +36 -9
- package/dist/config/secretFromEnvOrFile.d.ts +9 -0
- package/dist/config/secretFromEnvOrFile.js +34 -0
- package/dist/config/seededCatalog.d.ts +11 -0
- package/dist/config/seededCatalog.js +29 -0
- package/dist/config/toolRequires.d.ts +24 -0
- package/dist/config/toolRequires.js +115 -0
- package/dist/config/validateCatalog.d.ts +5 -1
- package/dist/config/validateCatalog.js +140 -1
- package/dist/diagnostics/checks.d.ts +39 -0
- package/dist/diagnostics/checks.js +135 -0
- package/dist/errors/codes.d.ts +14 -0
- package/dist/errors/codes.js +59 -0
- package/dist/git/cache.d.ts +14 -0
- package/dist/git/cache.js +150 -0
- package/dist/git/credentials.d.ts +9 -0
- package/dist/git/credentials.js +88 -0
- package/dist/git/errors.d.ts +20 -0
- package/dist/git/errors.js +94 -0
- package/dist/git/exec.d.ts +16 -0
- package/dist/git/exec.js +135 -0
- package/dist/git/operations.d.ts +29 -0
- package/dist/git/operations.js +153 -0
- package/dist/git/version.d.ts +1 -0
- package/dist/git/version.js +10 -0
- package/dist/logging/audit.d.ts +16 -0
- package/dist/logging/audit.js +19 -0
- package/dist/logging/commonWords.d.ts +2 -0
- package/dist/logging/commonWords.js +13 -0
- package/dist/logging/logger.d.ts +49 -0
- package/dist/logging/logger.js +193 -0
- package/dist/logging/namedSecrets.d.ts +6 -0
- package/dist/logging/namedSecrets.js +58 -0
- package/dist/logging/redact.d.ts +13 -0
- package/dist/logging/redact.js +88 -0
- package/dist/mcp/catalogTools.js +591 -76
- package/dist/mcp/checkoutTools.d.ts +52 -0
- package/dist/mcp/checkoutTools.js +332 -0
- package/dist/mcp/controlTools.js +179 -5
- package/dist/mcp/deps.d.ts +1 -0
- package/dist/mcp/getStartedTool.d.ts +3 -0
- package/dist/mcp/getStartedTool.js +62 -0
- package/dist/mcp/readArtifact.d.ts +11 -0
- package/dist/mcp/readArtifact.js +46 -30
- package/dist/mcp/server.js +1 -0
- package/dist/mcp/tools.js +4 -0
- package/dist/net/proxy.d.ts +14 -0
- package/dist/net/proxy.js +78 -0
- package/dist/package-meta.d.ts +2 -1
- package/dist/package-meta.js +2 -1
- package/dist/preflight/bash.d.ts +8 -0
- package/dist/preflight/bash.js +34 -0
- package/dist/preflight/claudeRoot.d.ts +11 -0
- package/dist/preflight/claudeRoot.js +23 -0
- package/dist/preflight/mcpCommands.d.ts +8 -0
- package/dist/preflight/mcpCommands.js +36 -0
- package/dist/preflight/pipelinePreflight.d.ts +40 -0
- package/dist/preflight/pipelinePreflight.js +192 -0
- package/dist/preflight/tls.d.ts +6 -0
- package/dist/preflight/tls.js +18 -0
- package/dist/preflight/toolchain.d.ts +45 -0
- package/dist/preflight/toolchain.js +219 -0
- package/dist/project/findProjectRoot.js +3 -4
- package/dist/project/globalHome.d.ts +7 -0
- package/dist/project/globalHome.js +63 -2
- package/dist/project/resolveProjectContext.js +2 -2
- package/dist/project/resolveStageflowContext.js +2 -2
- package/dist/projection/projectRun.d.ts +2 -1
- package/dist/projection/projectRun.js +1 -0
- package/dist/runstore/assertTmpdir.d.ts +1 -0
- package/dist/runstore/assertTmpdir.js +21 -0
- package/dist/runstore/backup.d.ts +52 -0
- package/dist/runstore/backup.js +292 -0
- package/dist/runstore/createStore.d.ts +27 -0
- package/dist/runstore/createStore.js +75 -7
- package/dist/runstore/debugBundle.d.ts +60 -0
- package/dist/runstore/debugBundle.js +143 -0
- package/dist/runstore/diskUsage.d.ts +50 -0
- package/dist/runstore/diskUsage.js +214 -0
- package/dist/runstore/exportRunPayload.d.ts +8 -0
- package/dist/runstore/exportRunPayload.js +23 -0
- package/dist/runstore/normalizeCatalogPath.d.ts +1 -0
- package/dist/runstore/normalizeCatalogPath.js +10 -0
- package/dist/runstore/paths.d.ts +5 -0
- package/dist/runstore/paths.js +145 -1
- package/dist/runstore/port.d.ts +119 -8
- package/dist/runstore/port.js +8 -1
- package/dist/runstore/restore.d.ts +81 -0
- package/dist/runstore/restore.js +355 -0
- package/dist/runstore/retention.d.ts +27 -0
- package/dist/runstore/retention.js +74 -0
- package/dist/runstore/runManifest.d.ts +146 -0
- package/dist/runstore/runManifest.js +288 -0
- package/dist/runstore/runProjection.d.ts +3 -1
- package/dist/runstore/runProjection.js +63 -1
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +37 -3
- package/dist/runstore/sqlite/SqliteRunStore.js +401 -227
- package/dist/runstore/sqlite/applyStorePragmas.d.ts +39 -0
- package/dist/runstore/sqlite/applyStorePragmas.js +145 -0
- package/dist/runstore/sqlite/migrations/001-baseline.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/001-baseline.js +10 -0
- package/dist/runstore/sqlite/migrations/002-repository-binding.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/002-repository-binding.js +29 -0
- package/dist/runstore/sqlite/migrations/003-run-lifecycle.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/003-run-lifecycle.js +28 -0
- package/dist/runstore/sqlite/migrations/004-auto-resume-count.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/004-auto-resume-count.js +19 -0
- package/dist/runstore/sqlite/migrations/005-config-origins.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/005-config-origins.js +19 -0
- package/dist/runstore/sqlite/migrations/006-pipeline-body-and-caller.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/006-pipeline-body-and-caller.js +28 -0
- package/dist/runstore/sqlite/migrations/007-projects-registry.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/007-projects-registry.js +31 -0
- package/dist/runstore/sqlite/migrations/index.d.ts +21 -0
- package/dist/runstore/sqlite/migrations/index.js +72 -0
- package/dist/runstore/sqlite/schema.d.ts +20 -1
- package/dist/runstore/sqlite/schema.js +283 -1
- package/dist/runstore/sqlite/storeOpenError.d.ts +6 -0
- package/dist/runstore/sqlite/storeOpenError.js +11 -0
- package/dist/runstore/sqlite/storeSchemaError.d.ts +5 -0
- package/dist/runstore/sqlite/storeSchemaError.js +8 -0
- package/dist/runstore/stageExecution.js +3 -0
- package/dist/runstore/storeFilesystem.d.ts +32 -0
- package/dist/runstore/storeFilesystem.js +148 -0
- package/dist/runstore/submission.d.ts +11 -0
- package/dist/runstore/submission.js +7 -0
- package/dist/runstore/trackProjection.js +2 -0
- package/dist/runstore/workspaceLayout.d.ts +13 -0
- package/dist/runstore/workspaceLayout.js +76 -0
- package/dist/runtime/completionCheckRunner.d.ts +3 -0
- package/dist/runtime/completionCheckRunner.js +45 -7
- package/dist/runtime/containerLimits.d.ts +16 -0
- package/dist/runtime/containerLimits.js +84 -0
- package/dist/runtime/credentialMaterialisation.d.ts +28 -0
- package/dist/runtime/credentialMaterialisation.js +128 -0
- package/dist/runtime/gitCheckoutCapability.js +7 -13
- package/dist/runtime/pipelineRunner.d.ts +38 -1
- package/dist/runtime/pipelineRunner.js +179 -15
- package/dist/runtime/pipelineScheduler.d.ts +15 -0
- package/dist/runtime/pipelineScheduler.js +167 -42
- package/dist/runtime/reloadRunCatalog.js +12 -1
- package/dist/runtime/repositoryMaterialize.d.ts +47 -0
- package/dist/runtime/repositoryMaterialize.js +235 -0
- package/dist/runtime/resolveEnvelopeRef.d.ts +30 -0
- package/dist/runtime/resolveEnvelopeRef.js +79 -0
- package/dist/runtime/resumeTimedOut.d.ts +1 -1
- package/dist/runtime/resumeTimedOut.js +5 -2
- package/dist/runtime/runChangeBus.js +9 -0
- package/dist/runtime/runDeletion.d.ts +7 -0
- package/dist/runtime/runDeletion.js +37 -0
- package/dist/runtime/runManager.d.ts +179 -4
- package/dist/runtime/runManager.js +1644 -83
- package/dist/runtime/runRetentionSweep.d.ts +31 -0
- package/dist/runtime/runRetentionSweep.js +328 -0
- package/dist/runtime/runRetryCoordinator.d.ts +2 -0
- package/dist/runtime/runRetryCoordinator.js +19 -2
- package/dist/runtime/runSkills.d.ts +40 -0
- package/dist/runtime/runSkills.js +173 -0
- package/dist/runtime/stageAttemptBootstrap.d.ts +5 -1
- package/dist/runtime/stageAttemptBootstrap.js +195 -24
- package/dist/runtime/stageCacheEnv.d.ts +6 -0
- package/dist/runtime/stageCacheEnv.js +34 -0
- package/dist/runtime/stageConcurrency.d.ts +2 -1
- package/dist/runtime/stageConcurrency.js +4 -2
- package/dist/runtime/stageEnvironment.d.ts +38 -0
- package/dist/runtime/stageEnvironment.js +193 -0
- package/dist/runtime/stageProcessLauncher.d.ts +18 -0
- package/dist/runtime/stageProcessLauncher.js +194 -38
- package/dist/runtime/stageRecovery.d.ts +3 -1
- package/dist/runtime/stageRecovery.js +19 -4
- package/dist/runtime/stageRoots.d.ts +38 -1
- package/dist/runtime/stageRoots.js +118 -0
- package/dist/runtime/stageRunner.d.ts +1 -0
- package/dist/runtime/stageRunner.js +3 -1
- package/dist/runtime/stageSecretDecl.d.ts +9 -0
- package/dist/runtime/stageSecretDecl.js +102 -0
- package/dist/runtime/stageSecrets.d.ts +40 -0
- package/dist/runtime/stageSecrets.js +181 -0
- package/dist/runtime/stageWorker.d.ts +3 -2
- package/dist/runtime/stageWorker.js +58 -10
- package/dist/runtime/stageWorkerProtocol.d.ts +5 -0
- package/dist/runtime/stageWorkerProtocol.js +8 -0
- package/dist/runtime/startPayload.d.ts +32 -0
- package/dist/runtime/startPayload.js +59 -0
- package/dist/runtime/taskInput.js +14 -5
- package/dist/runtime/verifiedStageExecution.d.ts +1 -0
- package/dist/runtime/verifiedStageExecution.js +2 -1
- package/dist/runtime/workspaceBinding.d.ts +20 -0
- package/dist/runtime/workspaceBinding.js +60 -0
- package/dist/server/allowedHosts.d.ts +19 -0
- package/dist/server/allowedHosts.js +179 -0
- package/dist/server/bootstrap.d.ts +38 -2
- package/dist/server/bootstrap.js +219 -4
- package/dist/server/controlToken.d.ts +47 -0
- package/dist/server/controlToken.js +241 -0
- package/dist/server/createHttpHost.d.ts +11 -2
- package/dist/server/createHttpHost.js +109 -10
- package/dist/server/ensureGlobalService.d.ts +5 -1
- package/dist/server/ensureGlobalService.js +35 -1
- package/dist/server/healthSurfaces.d.ts +6 -0
- package/dist/server/healthSurfaces.js +98 -0
- package/dist/server/http.d.ts +12 -1
- package/dist/server/http.js +1085 -485
- package/dist/server/listenHost.d.ts +5 -0
- package/dist/server/listenHost.js +56 -0
- package/dist/server/mcpHost.d.ts +10 -1
- package/dist/server/mcpHost.js +36 -6
- package/dist/server/operatorResults.d.ts +2 -2
- package/dist/server/operatorResults.js +40 -29
- package/dist/server/requestAuthContext.d.ts +12 -0
- package/dist/server/requestAuthContext.js +21 -0
- package/dist/server/shutdown.d.ts +69 -0
- package/dist/server/shutdown.js +219 -0
- package/dist/types/pipeline.d.ts +7 -0
- package/dist/types/stage.d.ts +8 -0
- package/dist/types/task.d.ts +8 -0
- package/dist/ui/assets/index-B6hlSXXn.js +120 -0
- package/dist/ui/index.html +1 -1
- package/examples/README.md +98 -0
- package/examples/archify-on-pr/README.md +213 -0
- package/examples/archify-on-pr/archify-on-pr.pipeline.yaml +19 -0
- package/examples/archify-on-pr/archify-on-pr.task.yaml +19 -0
- package/examples/archify-on-pr/author-diagrams.yaml +106 -0
- package/examples/archify-on-pr/detect-changes.yaml +89 -0
- package/examples/ci-validate/README.md +35 -0
- package/examples/ci-validate/check.yaml +13 -0
- package/examples/ci-validate/ci-demo.pipeline.yaml +4 -0
- package/examples/ci-validate/validate.sh +5 -0
- package/examples/compliance-review-demo/README.md +125 -0
- package/examples/compliance-review-demo/compliance-review-demo.pipeline.yaml +29 -0
- package/examples/compliance-review-demo/connect-account.yaml +47 -0
- package/examples/compliance-review-demo/map-to-control.yaml +123 -0
- package/examples/compliance-review-demo/poll-bucket-config.yaml +103 -0
- package/examples/compliance-review-demo/publish-report.yaml +79 -0
- package/examples/compliance-review-demo/review-verdict.yaml +122 -0
- package/examples/compliance-review-demo/run-compliance-check.yaml +96 -0
- package/examples/compliance-review-demo/store-evidence.yaml +143 -0
- package/examples/conditional-fork/README.md +89 -0
- package/examples/conditional-fork/branch-a.yaml +13 -0
- package/examples/conditional-fork/branch-b.yaml +13 -0
- package/examples/conditional-fork/decide.yaml +35 -0
- package/examples/conditional-fork/fork-demo.pipeline.yaml +20 -0
- package/examples/conditional-fork/fork-demo.task.yaml +8 -0
- package/examples/context7-mcp/README.md +42 -0
- package/examples/context7-mcp/context7-mcp.pipeline.yaml +15 -0
- package/examples/context7-mcp/context7-mcp.task.yaml +4 -0
- package/examples/context7-mcp/fetch-docs.yaml +52 -0
- package/examples/context7-mcp/resolve-library.yaml +31 -0
- package/examples/context7-mcp/write-brief.yaml +52 -0
- package/examples/feature-loop/README.md +104 -0
- package/examples/feature-loop/address-feedback.yaml +148 -0
- package/examples/feature-loop/align.yaml +116 -0
- package/examples/feature-loop/decompose.yaml +86 -0
- package/examples/feature-loop/default-model-hierarchy.task.yaml +58 -0
- package/examples/feature-loop/feature-loop.pipeline.yaml +140 -0
- package/examples/feature-loop/feature-loop.task.yaml +25 -0
- package/examples/feature-loop/implement.yaml +128 -0
- package/examples/feature-loop/plan.yaml +111 -0
- package/examples/feature-loop/publish.yaml +167 -0
- package/examples/feature-loop/review.yaml +146 -0
- package/examples/feature-loop/verify.yaml +106 -0
- package/examples/feedback-loop/README.md +72 -0
- package/examples/feedback-loop/VERIFY.md +208 -0
- package/examples/feedback-loop/continue-only.task.yaml +4 -0
- package/examples/feedback-loop/exhaust-require-continue.task.yaml +6 -0
- package/examples/feedback-loop/exhaust-wait-human.task.yaml +11 -0
- package/examples/feedback-loop/feedback-loop-wait-human.pipeline.yaml +23 -0
- package/examples/feedback-loop/feedback-loop.pipeline.yaml +23 -0
- package/examples/feedback-loop/feedback-loop.task.yaml +4 -0
- package/examples/feedback-loop/implement.yaml +14 -0
- package/examples/feedback-loop/plan.yaml +14 -0
- package/examples/feedback-loop/review.yaml +15 -0
- package/examples/feedback-loop/send-back-once.task.yaml +6 -0
- package/examples/feedback-loop/submit.yaml +12 -0
- package/examples/generic-fan-in/README.md +151 -0
- package/examples/generic-fan-in/clarify.yaml +18 -0
- package/examples/generic-fan-in/generic-fan-in-accepted.pipeline.yaml +19 -0
- package/examples/generic-fan-in/generic-fan-in-accepted.task.yaml +8 -0
- package/examples/generic-fan-in/generic-fan-in.pipeline.yaml +18 -0
- package/examples/generic-fan-in/generic-fan-in.task.yaml +9 -0
- package/examples/generic-fan-in/research.yaml +45 -0
- package/examples/generic-fan-in/synthesize.yaml +66 -0
- package/examples/generic-fan-in/validation.yaml +45 -0
- package/examples/github-release/README.md +78 -0
- package/examples/github-release/draft-release-notes.yaml +64 -0
- package/examples/github-release/github-release.pipeline.yaml +11 -0
- package/examples/github-release/github-release.task.yaml +13 -0
- package/examples/github-release/publish-github-release.yaml +64 -0
- package/examples/hello-world/README.md +28 -0
- package/examples/hello-world/hello.pipeline.yaml +4 -0
- package/examples/hello-world/my-task.task.yaml +6 -0
- package/examples/hello-world/research.yaml +18 -0
- package/examples/mcp-hitl-tour/README.md +113 -0
- package/examples/mcp-hitl-tour/draft-angle.yaml +33 -0
- package/examples/mcp-hitl-tour/draft-short.yaml +32 -0
- package/examples/mcp-hitl-tour/emit-angles.yaml +45 -0
- package/examples/mcp-hitl-tour/gather.yaml +31 -0
- package/examples/mcp-hitl-tour/intake.yaml +62 -0
- package/examples/mcp-hitl-tour/mcp-hitl-tour-exhaust.task.yaml +13 -0
- package/examples/mcp-hitl-tour/mcp-hitl-tour.pipeline.yaml +54 -0
- package/examples/mcp-hitl-tour/mcp-hitl-tour.task.yaml +16 -0
- package/examples/mcp-hitl-tour/polish.yaml +33 -0
- package/examples/mcp-hitl-tour/review.yaml +17 -0
- package/examples/mcp-hitl-tour/ship.yaml +25 -0
- package/examples/oss-issue-contribution/README.md +218 -0
- package/examples/oss-issue-contribution/oss-address-review-feedback.yaml +117 -0
- package/examples/oss-issue-contribution/oss-approve-contribution.yaml +181 -0
- package/examples/oss-issue-contribution/oss-approve-plan.yaml +111 -0
- package/examples/oss-issue-contribution/oss-explain-fix.yaml +108 -0
- package/examples/oss-issue-contribution/oss-explain-issue.yaml +114 -0
- package/examples/oss-issue-contribution/oss-implement-source-fix.yaml +171 -0
- package/examples/oss-issue-contribution/oss-investigate-area.yaml +116 -0
- package/examples/oss-issue-contribution/oss-issue-contribution.pipeline.yaml +125 -0
- package/examples/oss-issue-contribution/oss-issue-intake.yaml +106 -0
- package/examples/oss-issue-contribution/oss-plan-investigation.yaml +112 -0
- package/examples/oss-issue-contribution/oss-plan-review.yaml +109 -0
- package/examples/oss-issue-contribution/oss-reproduce-issue.yaml +140 -0
- package/examples/oss-issue-contribution/oss-review-change.yaml +107 -0
- package/examples/oss-issue-contribution/oss-verify-fix.yaml +130 -0
- package/examples/oss-issue-contribution/oss-write-regression-test.yaml +135 -0
- package/examples/oss-issue-contribution/pi-7629.task.yaml +79 -0
- package/examples/oss-issue-contribution/pi-8919.task.yaml +75 -0
- package/examples/oss-issue-contribution/pydantic-ai-7971.task.yaml +53 -0
- package/examples/oss-quick-fix/README.md +65 -0
- package/examples/oss-quick-fix/implement-fix.yaml +84 -0
- package/examples/oss-quick-fix/oss-quick-fix.pipeline.yaml +35 -0
- package/examples/oss-quick-fix/oss-quick-fix.task.yaml +19 -0
- package/examples/oss-quick-fix/ship-fix.yaml +81 -0
- package/examples/oss-quick-fix/understand-issue.yaml +67 -0
- package/examples/oss-quick-fix/verify-fix.yaml +73 -0
- package/examples/plan-review/README.md +37 -0
- package/examples/plan-review/plan-review-followup.yaml +13 -0
- package/examples/plan-review/plan-review.pipeline.yaml +9 -0
- package/examples/plan-review/plan-review.task.yaml +9 -0
- package/examples/plan-review/plan-review.yaml +25 -0
- package/examples/playwright-mcp/README.md +39 -0
- package/examples/playwright-mcp/playwright-mcp.pipeline.yaml +5 -0
- package/examples/playwright-mcp/playwright-mcp.task.yaml +4 -0
- package/examples/playwright-mcp/screenshot.yaml +40 -0
- package/examples/retry-tour/README.md +75 -0
- package/examples/retry-tour/retry-tour.pipeline.yaml +122 -0
- package/examples/retry-tour/retry-tour.task.yaml +9 -0
- package/examples/route-if-tour/README.md +202 -0
- package/examples/route-if-tour/route-if-tour.pipeline.yaml +950 -0
- package/examples/route-if-tour/route-if-tour.task.yaml +4 -0
- package/examples/route-wiring-smoke-test/01-core-routing.pipeline.yaml +168 -0
- package/examples/route-wiring-smoke-test/02-on-gating.pipeline.yaml +44 -0
- package/examples/route-wiring-smoke-test/03-two-entry-points.pipeline.yaml +28 -0
- package/examples/route-wiring-smoke-test/04-entry-false-equivalent-to-omitted.pipeline.yaml +27 -0
- package/examples/route-wiring-smoke-test/05-fork-choice.pipeline.yaml +110 -0
- package/examples/route-wiring-smoke-test/06-fan-out-fan-in.pipeline.yaml +82 -0
- package/examples/route-wiring-smoke-test/07-loop-human-decision.pipeline.yaml +82 -0
- package/examples/route-wiring-smoke-test/08-skip-fallback-and-multi-on-gate.pipeline.yaml +77 -0
- package/examples/route-wiring-smoke-test/09-multi-loop-targets.pipeline.yaml +102 -0
- package/examples/route-wiring-smoke-test/10-uses-dialect-fork.pipeline.yaml +23 -0
- package/examples/route-wiring-smoke-test/11-sequential-io-handoff.pipeline.yaml +37 -0
- package/examples/route-wiring-smoke-test/12-complex-io-schemas.pipeline.yaml +138 -0
- package/examples/route-wiring-smoke-test/13-ref-io-handoff.pipeline.yaml +84 -0
- package/examples/route-wiring-smoke-test/14-if-eq-gating.pipeline.yaml +50 -0
- package/examples/route-wiring-smoke-test/15-if-all-gated.pipeline.yaml +37 -0
- package/examples/route-wiring-smoke-test/16-if-composition.pipeline.yaml +65 -0
- package/examples/route-wiring-smoke-test/17-if-join.pipeline.yaml +80 -0
- package/examples/route-wiring-smoke-test/README.md +377 -0
- package/examples/route-wiring-smoke-test/rejected/01-reject-legacy-needs.pipeline.yaml +26 -0
- package/examples/route-wiring-smoke-test/rejected/02-reject-legacy-fork.pipeline.yaml +25 -0
- package/examples/route-wiring-smoke-test/rejected/03-reject-legacy-feedback-loop.pipeline.yaml +30 -0
- package/examples/route-wiring-smoke-test/rejected/04-reject-missing-entry.pipeline.yaml +24 -0
- package/examples/route-wiring-smoke-test/rejected/05-reject-unreachable.pipeline.yaml +35 -0
- package/examples/route-wiring-smoke-test/rejected/06-reject-allow-none-without-route-select.pipeline.yaml +37 -0
- package/examples/route-wiring-smoke-test/rejected/07-reject-cycle.pipeline.yaml +27 -0
- package/examples/route-wiring-smoke-test/rejected/08-reject-loop-non-ancestor.pipeline.yaml +42 -0
- package/examples/route-wiring-smoke-test/rejected/09-reject-route-select-leaf.pipeline.yaml +26 -0
- package/examples/route-wiring-smoke-test/rejected/12-reject-loop-replay-unsafe.pipeline.yaml +55 -0
- package/examples/route-wiring-smoke-test/rejected/13-reject-multiple-loop-entries.pipeline.yaml +51 -0
- package/examples/route-wiring-smoke-test/rejected/14-reject-duplicate-stage-id.pipeline.yaml +35 -0
- package/examples/route-wiring-smoke-test/rejected/15-reject-unknown-route-target.pipeline.yaml +15 -0
- package/examples/route-wiring-smoke-test/rejected/16-reject-malformed-route-item.pipeline.yaml +15 -0
- package/examples/route-wiring-smoke-test/rejected/17-reject-io-incompatible.pipeline.yaml +33 -0
- package/examples/route-wiring-smoke-test/rejected/18-reject-nested-io.pipeline.yaml +51 -0
- package/examples/route-wiring-smoke-test/rejected/19-reject-array-item-io.pipeline.yaml +55 -0
- package/examples/route-wiring-smoke-test/rejected/20-reject-closed-io.pipeline.yaml +36 -0
- package/examples/route-wiring-smoke-test/rejected/21-reject-ref-io.pipeline.yaml +42 -0
- package/examples/route-wiring-smoke-test/rejected/22-reject-if-unknown-field.pipeline.yaml +33 -0
- package/examples/route-wiring-smoke-test/rejected/23-reject-if-optional-field.pipeline.yaml +32 -0
- package/examples/route-wiring-smoke-test/rejected/24-reject-if-empty-all.pipeline.yaml +31 -0
- package/examples/route-wiring-smoke-test/rejected/25-reject-if-gt-on-string.pipeline.yaml +33 -0
- package/examples/route-wiring-smoke-test/rejected/26-reject-if-optional-nested.pipeline.yaml +36 -0
- package/examples/route-wiring-smoke-test/rejected/27-reject-if-on-loop.pipeline.yaml +63 -0
- package/examples/route-wiring-smoke-test/rejected/29-reject-if-on-failed.pipeline.yaml +46 -0
- package/examples/route-wiring-smoke-test/smoke-test.task.yaml +4 -0
- package/examples/route-wiring-smoke-test/stages/branch-a.yaml +12 -0
- package/examples/route-wiring-smoke-test/stages/branch-b.yaml +12 -0
- package/examples/route-wiring-smoke-test/stages/decide.yaml +12 -0
- package/examples/route-wiring-smoke-test/stages/finish.yaml +12 -0
- package/examples/ship-feature/README.md +190 -0
- package/examples/ship-feature/feat-address-feedback.yaml +119 -0
- package/examples/ship-feature/feat-implement.yaml +180 -0
- package/examples/ship-feature/feat-plan.yaml +120 -0
- package/examples/ship-feature/feat-review.yaml +109 -0
- package/examples/ship-feature/feat-ship.yaml +203 -0
- package/examples/ship-feature/ship-feature.pipeline.yaml +44 -0
- package/examples/ship-feature/ship-feature.task.yaml +78 -0
- package/examples/stage-mcp/README.md +144 -0
- package/examples/stage-mcp/echo-mcp.mjs +20 -0
- package/examples/stage-mcp/stage-mcp-dead.pipeline.yaml +5 -0
- package/examples/stage-mcp/stage-mcp-dead.task.yaml +3 -0
- package/examples/stage-mcp/stage-mcp-missing-var.pipeline.yaml +5 -0
- package/examples/stage-mcp/stage-mcp-missing-var.task.yaml +3 -0
- package/examples/stage-mcp/stage-mcp.pipeline.yaml +5 -0
- package/examples/stage-mcp/stage-mcp.task.yaml +4 -0
- package/examples/stage-mcp/use-echo.yaml +20 -0
- package/examples/verify-tour/README.md +80 -0
- package/examples/verify-tour/verify-tour.pipeline.yaml +162 -0
- package/examples/verify-tour/verify-tour.task.yaml +11 -0
- package/package.json +6 -1
- package/skills/stageflow/SKILL.md +6 -6
- package/skills/stageflow/references/control-surface.md +11 -5
- package/skills/stageflow/scripts/detect-host.mjs +14 -1
- package/skills/stageflow/scripts/detect-host.test.mjs +29 -1
- package/skills/stageflow-author/SKILL.md +4 -3
- package/skills/stageflow-author/references/catalog-mapping.md +16 -0
- package/skills/stageflow-author/references/catalog-write-conventions.md +1 -1
- package/skills/stageflow-run/SKILL.md +20 -12
- package/skills/stageflow-run/references/mcp-call.md +9 -1
- package/skills/stageflow-run/references/task-and-pipeline-selection.md +16 -5
- package/skills/stageflow-run/scripts/mcp-call.mjs +46 -6
- package/skills/stageflow-run/scripts/mcp-call.test.mjs +64 -0
- package/skills/stageflow-session-capture/references/catalog-authoring.md +2 -0
- package/skills/stageflow-setup/SKILL.md +40 -8
- package/dist/ui/assets/index-Da5EHU6M.js +0 -119
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { createHash, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { readSecretFromEnvOrFile } from "../config/secretFromEnvOrFile.js";
|
|
3
|
+
import { isLoopbackHostname } from "./allowedHosts.js";
|
|
4
|
+
const CONTROL_TOKEN_PREFIX = "STAGEFLOW_CONTROL_TOKEN_";
|
|
5
|
+
const DEFAULT_CALLER_ID = "default";
|
|
6
|
+
function digest(value) {
|
|
7
|
+
return createHash("sha256").update(value).digest();
|
|
8
|
+
}
|
|
9
|
+
function validateToken(token, label) {
|
|
10
|
+
if (!token || /\s/.test(token) || token.length < 32) {
|
|
11
|
+
throw new Error(`${label} requires a token of at least 32 characters with no whitespace`);
|
|
12
|
+
}
|
|
13
|
+
return token;
|
|
14
|
+
}
|
|
15
|
+
function normalizeCallerId(name) {
|
|
16
|
+
return name.toLowerCase();
|
|
17
|
+
}
|
|
18
|
+
/** Parse named drive callers from STAGEFLOW_CONTROL_TOKEN_<NAME> / _FILE. */
|
|
19
|
+
export function listNamedControlTokenEnvKeys(env = process.env) {
|
|
20
|
+
const out = [];
|
|
21
|
+
const seen = new Set();
|
|
22
|
+
for (const key of Object.keys(env)) {
|
|
23
|
+
if (!key.startsWith(CONTROL_TOKEN_PREFIX))
|
|
24
|
+
continue;
|
|
25
|
+
let namePart;
|
|
26
|
+
let isFile = false;
|
|
27
|
+
if (key.endsWith("_FILE")) {
|
|
28
|
+
namePart = key.slice(CONTROL_TOKEN_PREFIX.length, -"_FILE".length);
|
|
29
|
+
isFile = true;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
namePart = key.slice(CONTROL_TOKEN_PREFIX.length);
|
|
33
|
+
}
|
|
34
|
+
if (namePart === "" || namePart === "FILE")
|
|
35
|
+
continue;
|
|
36
|
+
const caller_id = normalizeCallerId(namePart);
|
|
37
|
+
const dedupe = `${caller_id}:${isFile ? "file" : "plain"}`;
|
|
38
|
+
if (seen.has(dedupe))
|
|
39
|
+
continue;
|
|
40
|
+
seen.add(dedupe);
|
|
41
|
+
out.push({ envKey: key, caller_id, isFile });
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
function assertUniqueDigests(entries) {
|
|
46
|
+
const byHex = new Map();
|
|
47
|
+
for (const entry of entries) {
|
|
48
|
+
const hex = entry.digest.toString("hex");
|
|
49
|
+
const prior = byHex.get(hex);
|
|
50
|
+
if (prior !== undefined) {
|
|
51
|
+
throw new Error(`Duplicate control token digests for ${prior} and ${entry.label}`);
|
|
52
|
+
}
|
|
53
|
+
byHex.set(hex, entry.label);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export function loadControlTokens(env = process.env) {
|
|
57
|
+
const driveRaw = readSecretFromEnvOrFile(env, "STAGEFLOW_CONTROL_TOKEN");
|
|
58
|
+
const readRaw = readSecretFromEnvOrFile(env, "STAGEFLOW_READ_TOKEN");
|
|
59
|
+
const callerBases = new Map();
|
|
60
|
+
for (const named of listNamedControlTokenEnvKeys(env)) {
|
|
61
|
+
const base = named.isFile
|
|
62
|
+
? named.envKey.slice(0, -"_FILE".length)
|
|
63
|
+
: named.envKey;
|
|
64
|
+
if (!callerBases.has(named.caller_id)) {
|
|
65
|
+
callerBases.set(named.caller_id, base);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const namedDrive = [];
|
|
69
|
+
for (const [caller_id, base] of callerBases) {
|
|
70
|
+
const raw = readSecretFromEnvOrFile(env, base);
|
|
71
|
+
if (raw === undefined)
|
|
72
|
+
continue;
|
|
73
|
+
namedDrive.push({
|
|
74
|
+
caller_id,
|
|
75
|
+
digest: digest(validateToken(raw, base)),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const driveDigest = driveRaw
|
|
79
|
+
? digest(validateToken(driveRaw, "STAGEFLOW_CONTROL_TOKEN"))
|
|
80
|
+
: undefined;
|
|
81
|
+
const readDigest = readRaw
|
|
82
|
+
? digest(validateToken(readRaw, "STAGEFLOW_READ_TOKEN"))
|
|
83
|
+
: undefined;
|
|
84
|
+
const uniqueness = [];
|
|
85
|
+
if (driveDigest) {
|
|
86
|
+
uniqueness.push({ label: "STAGEFLOW_CONTROL_TOKEN", digest: driveDigest });
|
|
87
|
+
}
|
|
88
|
+
if (readDigest) {
|
|
89
|
+
uniqueness.push({ label: "STAGEFLOW_READ_TOKEN", digest: readDigest });
|
|
90
|
+
}
|
|
91
|
+
for (const named of namedDrive) {
|
|
92
|
+
uniqueness.push({
|
|
93
|
+
label: `STAGEFLOW_CONTROL_TOKEN_${named.caller_id}`,
|
|
94
|
+
digest: named.digest,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
assertUniqueDigests(uniqueness);
|
|
98
|
+
return { driveDigest, readDigest, namedDrive };
|
|
99
|
+
}
|
|
100
|
+
/** Plain bearer for CLI→host calls. Drive preferred; read only for GET/HEAD. */
|
|
101
|
+
export function resolveClientBearerToken(method, env = process.env) {
|
|
102
|
+
const driveRaw = readSecretFromEnvOrFile(env, "STAGEFLOW_CONTROL_TOKEN");
|
|
103
|
+
if (driveRaw !== undefined) {
|
|
104
|
+
return validateToken(driveRaw, "STAGEFLOW_CONTROL_TOKEN");
|
|
105
|
+
}
|
|
106
|
+
if (method === "GET" || method === "HEAD") {
|
|
107
|
+
const readRaw = readSecretFromEnvOrFile(env, "STAGEFLOW_READ_TOKEN");
|
|
108
|
+
if (readRaw !== undefined) {
|
|
109
|
+
return validateToken(readRaw, "STAGEFLOW_READ_TOKEN");
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
export function clientAuthorizationHeaders(method, env = process.env) {
|
|
115
|
+
const token = resolveClientBearerToken(method, env);
|
|
116
|
+
if (token === undefined)
|
|
117
|
+
return {};
|
|
118
|
+
return { Authorization: `Bearer ${token}` };
|
|
119
|
+
}
|
|
120
|
+
export function hasDriveToken(tokens) {
|
|
121
|
+
return tokens.driveDigest !== undefined || tokens.namedDrive.length > 0;
|
|
122
|
+
}
|
|
123
|
+
export function hasAnyToken(tokens) {
|
|
124
|
+
return (tokens.driveDigest !== undefined ||
|
|
125
|
+
tokens.readDigest !== undefined ||
|
|
126
|
+
tokens.namedDrive.length > 0);
|
|
127
|
+
}
|
|
128
|
+
export function isLoopbackBind(bind) {
|
|
129
|
+
const normalized = bind.startsWith("[") && bind.endsWith("]")
|
|
130
|
+
? bind.slice(1, -1)
|
|
131
|
+
: bind;
|
|
132
|
+
return isLoopbackHostname(normalized);
|
|
133
|
+
}
|
|
134
|
+
export function refuseBindMessage(bind) {
|
|
135
|
+
return `Refusing to start: Stageflow is configured to bind ${bind}, which is reachable from outside this
|
|
136
|
+
machine, but no control token is set.
|
|
137
|
+
|
|
138
|
+
Anyone who can reach this port could start a pipeline run, and pipeline stages execute arbitrary
|
|
139
|
+
shell commands with this process's environment — including provider API keys.
|
|
140
|
+
|
|
141
|
+
Set one of:
|
|
142
|
+
STAGEFLOW_CONTROL_TOKEN=<at least 32 characters>
|
|
143
|
+
STAGEFLOW_CONTROL_TOKEN_FILE=/path/to/secret
|
|
144
|
+
STAGEFLOW_CONTROL_TOKEN_<NAME>=<at least 32 characters>
|
|
145
|
+
|
|
146
|
+
Or bind to loopback instead:
|
|
147
|
+
--host 127.0.0.1 (or unset STAGEFLOW_BIND)`;
|
|
148
|
+
}
|
|
149
|
+
export function assertBindAllowed(bind, tokens) {
|
|
150
|
+
if (!isLoopbackBind(bind) && !hasDriveToken(tokens)) {
|
|
151
|
+
throw new BindRefusedError(refuseBindMessage(bind));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
export class BindRefusedError extends Error {
|
|
155
|
+
exitCode = 1;
|
|
156
|
+
constructor(message) {
|
|
157
|
+
super(message);
|
|
158
|
+
this.name = "BindRefusedError";
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
export function requiredScopeFor(method, pathname) {
|
|
162
|
+
if (pathname === "/mcp")
|
|
163
|
+
return "drive";
|
|
164
|
+
if (!pathname.startsWith("/api/"))
|
|
165
|
+
return null;
|
|
166
|
+
if (pathname === "/api/backup" || pathname.startsWith("/api/backup/")) {
|
|
167
|
+
return "drive";
|
|
168
|
+
}
|
|
169
|
+
if (method === "GET" || method === "HEAD")
|
|
170
|
+
return "read";
|
|
171
|
+
return "drive";
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Digest once; timingSafeEqual against every loaded digest (no early-return oracle).
|
|
175
|
+
* Named control tokens are drive-only; READ stays the singular global token.
|
|
176
|
+
*/
|
|
177
|
+
export function authenticateBearer(tokens, authorization) {
|
|
178
|
+
const match = authorization?.match(/^Bearer ([^\s]+)$/i);
|
|
179
|
+
if (!match)
|
|
180
|
+
return undefined;
|
|
181
|
+
const hash = digest(match[1]);
|
|
182
|
+
const candidates = [];
|
|
183
|
+
if (tokens.driveDigest) {
|
|
184
|
+
candidates.push({
|
|
185
|
+
scope: "drive",
|
|
186
|
+
caller_id: DEFAULT_CALLER_ID,
|
|
187
|
+
digest: tokens.driveDigest,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
if (tokens.readDigest) {
|
|
191
|
+
candidates.push({
|
|
192
|
+
scope: "read",
|
|
193
|
+
caller_id: DEFAULT_CALLER_ID,
|
|
194
|
+
digest: tokens.readDigest,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
for (const named of tokens.namedDrive) {
|
|
198
|
+
candidates.push({
|
|
199
|
+
scope: "drive",
|
|
200
|
+
caller_id: named.caller_id,
|
|
201
|
+
digest: named.digest,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
let matched;
|
|
205
|
+
for (const candidate of candidates) {
|
|
206
|
+
if (timingSafeEqual(candidate.digest, hash)) {
|
|
207
|
+
matched = { scope: candidate.scope, caller_id: candidate.caller_id };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return matched;
|
|
211
|
+
}
|
|
212
|
+
function scopeSatisfies(granted, required) {
|
|
213
|
+
if (required === "read")
|
|
214
|
+
return granted === "read" || granted === "drive";
|
|
215
|
+
return granted === "drive";
|
|
216
|
+
}
|
|
217
|
+
export function enforceBearerAuth(tokens, req, res, required) {
|
|
218
|
+
if (!hasAnyToken(tokens))
|
|
219
|
+
return { ok: true, auth: undefined };
|
|
220
|
+
const granted = authenticateBearer(tokens, req.headers.authorization);
|
|
221
|
+
if (granted === undefined) {
|
|
222
|
+
const payload = JSON.stringify({ error: "Unauthorized" });
|
|
223
|
+
res.writeHead(401, {
|
|
224
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
225
|
+
"Content-Length": Buffer.byteLength(payload),
|
|
226
|
+
"WWW-Authenticate": "Bearer",
|
|
227
|
+
});
|
|
228
|
+
res.end(payload);
|
|
229
|
+
return { ok: false };
|
|
230
|
+
}
|
|
231
|
+
if (!scopeSatisfies(granted.scope, required)) {
|
|
232
|
+
const payload = JSON.stringify({ error: "Forbidden" });
|
|
233
|
+
res.writeHead(403, {
|
|
234
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
235
|
+
"Content-Length": Buffer.byteLength(payload),
|
|
236
|
+
});
|
|
237
|
+
res.end(payload);
|
|
238
|
+
return { ok: false };
|
|
239
|
+
}
|
|
240
|
+
return { ok: true, auth: granted };
|
|
241
|
+
}
|
|
@@ -3,7 +3,11 @@ import type { RunStore } from "../runstore/port.js";
|
|
|
3
3
|
import type { RunManager } from "../runtime/runManager.js";
|
|
4
4
|
import type { RunChangeBus } from "../runtime/runChangeBus.js";
|
|
5
5
|
import type { StageflowHostBootstrap } from "./bootstrap.js";
|
|
6
|
+
import { type AllowedHosts } from "./allowedHosts.js";
|
|
7
|
+
import { requiredScopeFor, type ControlTokens } from "./controlToken.js";
|
|
6
8
|
export declare const DEFAULT_PORT = 3847;
|
|
9
|
+
export declare const DEFAULT_REQUEST_TIMEOUT_MS = 60000;
|
|
10
|
+
export declare const DEFAULT_MAX_CONNECTIONS = 256;
|
|
7
11
|
export declare function json(res: ServerResponse, status: number, body: unknown): void;
|
|
8
12
|
export type HttpHostRouteContext = {
|
|
9
13
|
req: IncomingMessage;
|
|
@@ -19,6 +23,10 @@ export type CreateHttpHostOptions = {
|
|
|
19
23
|
host: string;
|
|
20
24
|
port: number;
|
|
21
25
|
routes: (ctx: HttpHostRouteContext) => Promise<boolean | void>;
|
|
26
|
+
allowedHosts?: AllowedHosts;
|
|
27
|
+
controlTokens?: ControlTokens;
|
|
28
|
+
requestTimeoutMs?: number;
|
|
29
|
+
maxConnections?: number;
|
|
22
30
|
};
|
|
23
31
|
export type HttpHostEnvelope = {
|
|
24
32
|
server: Server;
|
|
@@ -26,9 +34,10 @@ export type HttpHostEnvelope = {
|
|
|
26
34
|
host: string;
|
|
27
35
|
url: string;
|
|
28
36
|
mcpUrl: string;
|
|
29
|
-
manager
|
|
30
|
-
store
|
|
37
|
+
manager?: RunManager;
|
|
38
|
+
store?: RunStore;
|
|
31
39
|
runChangeBus: RunChangeBus;
|
|
32
40
|
mcpStateless: boolean;
|
|
33
41
|
};
|
|
34
42
|
export declare function createHttpHost(options: CreateHttpHostOptions): Promise<HttpHostEnvelope>;
|
|
43
|
+
export { requiredScopeFor };
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { createServer, } from "node:http";
|
|
2
|
-
import {
|
|
2
|
+
import { logger as rootLogger } from "../logging/logger.js";
|
|
3
|
+
import { assertAllowedHttpAccess, resolveAllowedHosts, } from "./allowedHosts.js";
|
|
4
|
+
import { enforceBearerAuth, loadControlTokens, requiredScopeFor, } from "./controlToken.js";
|
|
5
|
+
import { requestAuthFromBearer, runWithRequestAuth, } from "./requestAuthContext.js";
|
|
6
|
+
import { advertisedHost } from "./listenHost.js";
|
|
7
|
+
import { handleLivez, handleReadyz } from "./healthSurfaces.js";
|
|
3
8
|
export const DEFAULT_PORT = 3847;
|
|
9
|
+
export const DEFAULT_REQUEST_TIMEOUT_MS = 60_000;
|
|
10
|
+
export const DEFAULT_MAX_CONNECTIONS = 256;
|
|
11
|
+
const log = rootLogger.child({ component: "http" });
|
|
4
12
|
export function json(res, status, body) {
|
|
5
13
|
const payload = JSON.stringify(body);
|
|
6
14
|
res.writeHead(status, {
|
|
@@ -9,25 +17,101 @@ export function json(res, status, body) {
|
|
|
9
17
|
});
|
|
10
18
|
res.end(payload);
|
|
11
19
|
}
|
|
20
|
+
function resolveRequestTimeoutMs(env = process.env) {
|
|
21
|
+
const raw = env.STAGEFLOW_REQUEST_TIMEOUT_MS;
|
|
22
|
+
if (raw === undefined || raw.trim() === "")
|
|
23
|
+
return DEFAULT_REQUEST_TIMEOUT_MS;
|
|
24
|
+
const n = Number(raw);
|
|
25
|
+
if (!Number.isFinite(n) || n < 0)
|
|
26
|
+
return DEFAULT_REQUEST_TIMEOUT_MS;
|
|
27
|
+
return n;
|
|
28
|
+
}
|
|
29
|
+
function resolveMaxConnections(env = process.env) {
|
|
30
|
+
const raw = env.STAGEFLOW_MAX_CONNECTIONS;
|
|
31
|
+
if (raw === undefined || raw.trim() === "")
|
|
32
|
+
return DEFAULT_MAX_CONNECTIONS;
|
|
33
|
+
const n = Number(raw);
|
|
34
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
35
|
+
return DEFAULT_MAX_CONNECTIONS;
|
|
36
|
+
return Math.floor(n);
|
|
37
|
+
}
|
|
12
38
|
export async function createHttpHost(options) {
|
|
13
39
|
const { boot, host, port, routes } = options;
|
|
14
|
-
const
|
|
15
|
-
const
|
|
40
|
+
const allowedHosts = options.allowedHosts ?? resolveAllowedHosts();
|
|
41
|
+
const controlTokens = options.controlTokens ?? loadControlTokens();
|
|
42
|
+
const requestTimeoutMs = options.requestTimeoutMs ?? resolveRequestTimeoutMs();
|
|
43
|
+
const maxConnections = options.maxConnections ?? resolveMaxConnections();
|
|
16
44
|
const server = createServer(async (req, res) => {
|
|
17
45
|
const method = req.method ?? "GET";
|
|
18
|
-
const url = new URL(req.url ?? "/", `http://${host}:${port}`);
|
|
46
|
+
const url = new URL(req.url ?? "/", `http://${advertisedHost(host)}:${port}`);
|
|
19
47
|
const pathname = url.pathname;
|
|
48
|
+
if ((pathname === "/livez" || pathname === "/readyz") &&
|
|
49
|
+
(method === "GET" || method === "HEAD")) {
|
|
50
|
+
if (!assertAllowedHttpAccess(allowedHosts, req, res))
|
|
51
|
+
return;
|
|
52
|
+
if (pathname === "/livez") {
|
|
53
|
+
handleLivez(req, res);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
await handleReadyz(req, res, boot);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (pathname === "/api/a2a/status" && method === "GET") {
|
|
60
|
+
if (!assertAllowedHttpAccess(allowedHosts, req, res))
|
|
61
|
+
return;
|
|
62
|
+
if (boot.serveBlocked !== undefined) {
|
|
63
|
+
json(res, 503, {
|
|
64
|
+
error: boot.serveBlocked.reason,
|
|
65
|
+
code: boot.serveBlocked.code,
|
|
66
|
+
});
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const a2aAuth = enforceBearerAuth(controlTokens, req, res, "read");
|
|
70
|
+
if (!a2aAuth.ok)
|
|
71
|
+
return;
|
|
72
|
+
json(res, 200, boot.a2a?.status ?? { state: "disabled" });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (boot.serveBlocked !== undefined) {
|
|
76
|
+
const a2aOwned = pathname === "/a2a" ||
|
|
77
|
+
pathname === "/.well-known/agent-card.json" ||
|
|
78
|
+
pathname.startsWith("/a2a/contracts/") ||
|
|
79
|
+
pathname.startsWith("/a2a/artifacts/");
|
|
80
|
+
if (a2aOwned) {
|
|
81
|
+
if (!assertAllowedHttpAccess(allowedHosts, req, res))
|
|
82
|
+
return;
|
|
83
|
+
json(res, 503, {
|
|
84
|
+
error: boot.serveBlocked.reason,
|
|
85
|
+
code: boot.serveBlocked.code,
|
|
86
|
+
});
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (await boot.a2a?.handle(req, res, pathname))
|
|
91
|
+
return;
|
|
20
92
|
if (pathname === "/mcp") {
|
|
21
|
-
if (!
|
|
93
|
+
if (!assertAllowedHttpAccess(allowedHosts, req, res))
|
|
94
|
+
return;
|
|
95
|
+
if (boot.serveBlocked !== undefined) {
|
|
96
|
+
json(res, 503, {
|
|
97
|
+
error: boot.serveBlocked.reason,
|
|
98
|
+
code: boot.serveBlocked.code,
|
|
99
|
+
});
|
|
22
100
|
return;
|
|
23
101
|
}
|
|
102
|
+
const mcpAuth = enforceBearerAuth(controlTokens, req, res, "drive");
|
|
103
|
+
if (!mcpAuth.ok)
|
|
104
|
+
return;
|
|
105
|
+
res.setTimeout(0);
|
|
24
106
|
try {
|
|
25
|
-
await boot.mcpHandler.handle(req, res);
|
|
107
|
+
await runWithRequestAuth(requestAuthFromBearer(mcpAuth.auth, "mcp"), () => boot.mcpHandler.handle(req, res));
|
|
26
108
|
}
|
|
27
109
|
catch (err) {
|
|
110
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
111
|
+
log.error("mcp.handler_error", message);
|
|
28
112
|
if (!res.headersSent) {
|
|
29
113
|
res.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
|
|
30
|
-
res.end(
|
|
114
|
+
res.end(message);
|
|
31
115
|
}
|
|
32
116
|
}
|
|
33
117
|
return;
|
|
@@ -45,26 +129,41 @@ export async function createHttpHost(options) {
|
|
|
45
129
|
return;
|
|
46
130
|
json(res, 404, { error: "Not found" });
|
|
47
131
|
});
|
|
48
|
-
|
|
132
|
+
// requestTimeout bounds receiving the request body (408); idle socket timeout
|
|
133
|
+
// stays 0 so MCP SSE can outlive requestTimeout.
|
|
134
|
+
server.requestTimeout = requestTimeoutMs;
|
|
135
|
+
server.maxConnections = maxConnections;
|
|
49
136
|
server.on("close", () => {
|
|
137
|
+
boot.stopGcInterval();
|
|
50
138
|
void boot.mcpHandler.close();
|
|
51
139
|
});
|
|
52
140
|
await new Promise((resolve, reject) => {
|
|
53
141
|
server.listen(port, host, () => resolve());
|
|
54
142
|
server.on("error", reject);
|
|
55
143
|
});
|
|
144
|
+
server.on("error", (err) => {
|
|
145
|
+
log.error("host.server_error", err instanceof Error ? err.message : String(err));
|
|
146
|
+
});
|
|
56
147
|
const address = server.address();
|
|
57
148
|
const boundPort = address && typeof address !== "string" ? address.port : port;
|
|
58
|
-
const
|
|
149
|
+
const publicHost = advertisedHost(host);
|
|
150
|
+
const url = `http://${publicHost}:${boundPort}`;
|
|
151
|
+
const mcpUrl = `${url}/mcp`;
|
|
152
|
+
log.info("host.listening", `listening on ${url}`, {
|
|
153
|
+
host,
|
|
154
|
+
port: boundPort,
|
|
155
|
+
mcp_url: mcpUrl,
|
|
156
|
+
});
|
|
59
157
|
return {
|
|
60
158
|
server,
|
|
61
159
|
port: boundPort,
|
|
62
160
|
host,
|
|
63
161
|
url,
|
|
64
|
-
mcpUrl
|
|
162
|
+
mcpUrl,
|
|
65
163
|
manager: boot.manager,
|
|
66
164
|
store: boot.store,
|
|
67
165
|
runChangeBus: boot.runChangeBus,
|
|
68
166
|
mcpStateless: boot.mcpStateless,
|
|
69
167
|
};
|
|
70
168
|
}
|
|
169
|
+
export { requiredScopeFor };
|
|
@@ -11,12 +11,15 @@ export type ServiceProbeResult = "up" | "unreachable" | "unhealthy";
|
|
|
11
11
|
export declare function hostBaseUrl(): string;
|
|
12
12
|
export declare function probeGlobalServiceDetailed(): Promise<ServiceProbeResult>;
|
|
13
13
|
export declare function defaultProbeHost(): Promise<HostProbeResult>;
|
|
14
|
+
export declare const STAGEFLOW_NO_AUTOSTART = "STAGEFLOW_NO_AUTOSTART";
|
|
15
|
+
/** Truthy when set and not empty / `0` / `false` (case-insensitive). */
|
|
16
|
+
export declare function isNoAutostartEnabled(env?: NodeJS.ProcessEnv): boolean;
|
|
14
17
|
export type EnsureGlobalServiceResult = {
|
|
15
18
|
ok: true;
|
|
16
19
|
alreadyRunning: boolean;
|
|
17
20
|
} | {
|
|
18
21
|
ok: false;
|
|
19
|
-
reason: "port_occupied" | "spawn_failed" | "timed_out";
|
|
22
|
+
reason: "port_occupied" | "spawn_failed" | "timed_out" | "autostart_disabled" | "bind_refused";
|
|
20
23
|
message: string;
|
|
21
24
|
};
|
|
22
25
|
export interface EnsureGlobalServiceOptions {
|
|
@@ -26,6 +29,7 @@ export interface EnsureGlobalServiceOptions {
|
|
|
26
29
|
spawnFn?: (command: string, args: string[], options: Parameters<typeof spawnDefault>[2]) => ChildProcess;
|
|
27
30
|
pollIntervalMs?: number;
|
|
28
31
|
timeoutMs?: number;
|
|
32
|
+
env?: NodeJS.ProcessEnv;
|
|
29
33
|
}
|
|
30
34
|
/**
|
|
31
35
|
* Health-probe-then-spawn: makes sure a global Stageflow service is
|
|
@@ -3,7 +3,9 @@ import { openSync } from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { ensureGlobalHome } from "../project/globalHome.js";
|
|
6
|
+
import { assertBindAllowed, BindRefusedError, loadControlTokens, } from "./controlToken.js";
|
|
6
7
|
import { DEFAULT_PORT } from "./createHttpHost.js";
|
|
8
|
+
import { resolveListenHost } from "./listenHost.js";
|
|
7
9
|
/**
|
|
8
10
|
* The well-known port the global service listens on. Overridable via
|
|
9
11
|
* STAGEFLOW_SERVICE_PORT — real usage never sets this (there's exactly one
|
|
@@ -46,7 +48,7 @@ export async function probeGlobalServiceDetailed() {
|
|
|
46
48
|
try {
|
|
47
49
|
let res;
|
|
48
50
|
try {
|
|
49
|
-
res = await fetch(`${hostBaseUrl()}/
|
|
51
|
+
res = await fetch(`${hostBaseUrl()}/livez`, { signal: ac.signal });
|
|
50
52
|
}
|
|
51
53
|
catch {
|
|
52
54
|
return "unreachable";
|
|
@@ -70,6 +72,15 @@ export async function defaultProbeHost() {
|
|
|
70
72
|
}
|
|
71
73
|
const DEFAULT_POLL_INTERVAL_MS = 200;
|
|
72
74
|
const DEFAULT_TIMEOUT_MS = Number(process.env.STAGEFLOW_AUTOSTART_TIMEOUT_MS) || 10_000;
|
|
75
|
+
export const STAGEFLOW_NO_AUTOSTART = "STAGEFLOW_NO_AUTOSTART";
|
|
76
|
+
/** Truthy when set and not empty / `0` / `false` (case-insensitive). */
|
|
77
|
+
export function isNoAutostartEnabled(env = process.env) {
|
|
78
|
+
const raw = env[STAGEFLOW_NO_AUTOSTART];
|
|
79
|
+
if (raw === undefined || raw.trim() === "")
|
|
80
|
+
return false;
|
|
81
|
+
const normalized = raw.trim().toLowerCase();
|
|
82
|
+
return normalized !== "0" && normalized !== "false";
|
|
83
|
+
}
|
|
73
84
|
function sleep(ms) {
|
|
74
85
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
75
86
|
}
|
|
@@ -83,6 +94,7 @@ export async function ensureGlobalService(options = {}) {
|
|
|
83
94
|
const spawnFn = options.spawnFn ?? spawnDefault;
|
|
84
95
|
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
85
96
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
97
|
+
const env = options.env ?? process.env;
|
|
86
98
|
const initial = await probeHost();
|
|
87
99
|
if (initial === "up") {
|
|
88
100
|
return { ok: true, alreadyRunning: true };
|
|
@@ -94,6 +106,28 @@ export async function ensureGlobalService(options = {}) {
|
|
|
94
106
|
message: `Port ${resolveServicePort()} is already in use by a process that isn't the Stageflow service (its health check didn't return valid health JSON). Free the port and try again.`,
|
|
95
107
|
};
|
|
96
108
|
}
|
|
109
|
+
if (isNoAutostartEnabled(env)) {
|
|
110
|
+
const url = hostBaseUrl();
|
|
111
|
+
return {
|
|
112
|
+
ok: false,
|
|
113
|
+
reason: "autostart_disabled",
|
|
114
|
+
message: `No Stageflow Host is answering at ${url}. Autostart is disabled (STAGEFLOW_NO_AUTOSTART). Start the Host with \`sf mcp\`, or in Docker check that the container's entrypoint is running.`,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
const bind = resolveListenHost({ env });
|
|
119
|
+
assertBindAllowed(bind, loadControlTokens(env));
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
if (err instanceof BindRefusedError) {
|
|
123
|
+
return {
|
|
124
|
+
ok: false,
|
|
125
|
+
reason: "bind_refused",
|
|
126
|
+
message: err.message,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
throw err;
|
|
130
|
+
}
|
|
97
131
|
const cliEntry = options.cliEntry ?? process.argv[1] ?? "";
|
|
98
132
|
const globalHome = ensureGlobalHome();
|
|
99
133
|
const logPath = path.join(globalHome, "service.log");
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import type { StageflowHostBootstrap } from "./bootstrap.js";
|
|
3
|
+
export declare function handleLivez(_req: IncomingMessage, res: ServerResponse): void;
|
|
4
|
+
export declare function handleReadyz(_req: IncomingMessage, res: ServerResponse, boot: StageflowHostBootstrap): Promise<void>;
|
|
5
|
+
export declare function buildRichHealthPayload(boot: StageflowHostBootstrap): Promise<Record<string, unknown>>;
|
|
6
|
+
export declare function handleApiHealth(_req: IncomingMessage, res: ServerResponse, boot: StageflowHostBootstrap): Promise<void>;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { PACKAGE_VERSION, BUILD_SHA } from "../package-meta.js";
|
|
2
|
+
import { globalStageflowHome } from "../project/globalHome.js";
|
|
3
|
+
import { redactHostConfig } from "../config/hostConfig.js";
|
|
4
|
+
import { resolveCatalogRoots } from "../config/resolveCatalogRoots.js";
|
|
5
|
+
import { json } from "./createHttpHost.js";
|
|
6
|
+
import { livezBody, probeGitVersion, readSchemaHealth, runReadyzChecks, } from "../diagnostics/checks.js";
|
|
7
|
+
import { buildEgressHealth } from "../net/proxy.js";
|
|
8
|
+
import { toolchainHealthMap } from "../preflight/toolchain.js";
|
|
9
|
+
export function handleLivez(_req, res) {
|
|
10
|
+
json(res, 200, livezBody());
|
|
11
|
+
}
|
|
12
|
+
export async function handleReadyz(_req, res, boot) {
|
|
13
|
+
if (boot.serveBlocked !== undefined) {
|
|
14
|
+
json(res, 503, {
|
|
15
|
+
ready: false,
|
|
16
|
+
code: boot.serveBlocked.code,
|
|
17
|
+
error: boot.serveBlocked.reason,
|
|
18
|
+
checks: {
|
|
19
|
+
store_openable: false,
|
|
20
|
+
home_writable: false,
|
|
21
|
+
migrations_complete: false,
|
|
22
|
+
git_present: false,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (boot.store === undefined) {
|
|
28
|
+
json(res, 503, {
|
|
29
|
+
ready: false,
|
|
30
|
+
error: "store unavailable",
|
|
31
|
+
checks: {
|
|
32
|
+
store_openable: false,
|
|
33
|
+
home_writable: false,
|
|
34
|
+
migrations_complete: false,
|
|
35
|
+
git_present: false,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const result = await runReadyzChecks({
|
|
41
|
+
store: boot.store,
|
|
42
|
+
homeDir: globalStageflowHome(),
|
|
43
|
+
});
|
|
44
|
+
json(res, result.ready ? 200 : 503, result);
|
|
45
|
+
}
|
|
46
|
+
export async function buildRichHealthPayload(boot) {
|
|
47
|
+
if (boot.manager === undefined || boot.store === undefined) {
|
|
48
|
+
throw new Error("buildRichHealthPayload requires store and manager");
|
|
49
|
+
}
|
|
50
|
+
const capacity = await boot.manager.getHealthWithDisk();
|
|
51
|
+
const gitVersion = await probeGitVersion();
|
|
52
|
+
const schema = readSchemaHealth(boot.store);
|
|
53
|
+
const catalogRoots = await resolveCatalogRoots({
|
|
54
|
+
store: boot.store,
|
|
55
|
+
bootCwd: boot.cwd,
|
|
56
|
+
});
|
|
57
|
+
const payload = {
|
|
58
|
+
...capacity,
|
|
59
|
+
capacity: {
|
|
60
|
+
global: {
|
|
61
|
+
activeRunIds: capacity.activeRunIds,
|
|
62
|
+
activeCount: capacity.activeCount,
|
|
63
|
+
maxConcurrent: capacity.maxConcurrent,
|
|
64
|
+
slotsAvailable: capacity.slotsAvailable,
|
|
65
|
+
},
|
|
66
|
+
per_project: boot.manager.getPerProjectCapacity(),
|
|
67
|
+
},
|
|
68
|
+
version: PACKAGE_VERSION,
|
|
69
|
+
build_sha: BUILD_SHA,
|
|
70
|
+
stageflow_home: globalStageflowHome(),
|
|
71
|
+
schema,
|
|
72
|
+
git_version: gitVersion ?? null,
|
|
73
|
+
toolchain: toolchainHealthMap(),
|
|
74
|
+
store_filesystem: boot.storeFilesystem ?? null,
|
|
75
|
+
egress: buildEgressHealth(),
|
|
76
|
+
catalog_roots: catalogRoots.map((r) => ({
|
|
77
|
+
project_root: r.project_root,
|
|
78
|
+
kind: r.kind,
|
|
79
|
+
read_only: r.read_only,
|
|
80
|
+
})),
|
|
81
|
+
providers: boot.providerBoot
|
|
82
|
+
? {
|
|
83
|
+
configured: boot.providerBoot.configured,
|
|
84
|
+
failures: boot.providerBoot.failures,
|
|
85
|
+
}
|
|
86
|
+
: { configured: [], failures: [] },
|
|
87
|
+
};
|
|
88
|
+
if (boot.hostConfig) {
|
|
89
|
+
payload.config = redactHostConfig(boot.hostConfig);
|
|
90
|
+
}
|
|
91
|
+
if (boot.a2a?.status) {
|
|
92
|
+
payload.a2a = boot.a2a.status;
|
|
93
|
+
}
|
|
94
|
+
return payload;
|
|
95
|
+
}
|
|
96
|
+
export async function handleApiHealth(_req, res, boot) {
|
|
97
|
+
json(res, 200, await buildRichHealthPayload(boot));
|
|
98
|
+
}
|
package/dist/server/http.d.ts
CHANGED
|
@@ -4,7 +4,10 @@ import type { RunStoreKind } from "../runstore/createStore.js";
|
|
|
4
4
|
import type { RunStore } from "../runstore/port.js";
|
|
5
5
|
import type { RunManager } from "../runtime/runManager.js";
|
|
6
6
|
import type { RunChangeBus } from "../runtime/runChangeBus.js";
|
|
7
|
+
import { type AllowedHosts } from "./allowedHosts.js";
|
|
8
|
+
import { type ControlTokens } from "./controlToken.js";
|
|
7
9
|
import { DEFAULT_PORT, type HttpHostEnvelope, type HttpHostRouteContext } from "./createHttpHost.js";
|
|
10
|
+
import { type ShutdownController } from "./shutdown.js";
|
|
8
11
|
export type UiServerOptions = {
|
|
9
12
|
agent: AgentPort;
|
|
10
13
|
cwd?: string;
|
|
@@ -19,6 +22,8 @@ export type UiServerOptions = {
|
|
|
19
22
|
providerAuthContext?: ProviderAuthContext;
|
|
20
23
|
mcpStateless?: boolean;
|
|
21
24
|
runChangeBus?: RunChangeBus;
|
|
25
|
+
allowedHosts?: AllowedHosts;
|
|
26
|
+
controlTokens?: ControlTokens;
|
|
22
27
|
};
|
|
23
28
|
export declare function defaultUiDistDir(): string;
|
|
24
29
|
export type OperatorRouteDeps = {
|
|
@@ -30,6 +35,10 @@ export type OperatorRouteDeps = {
|
|
|
30
35
|
providerAuthContext: ProviderAuthContext | undefined;
|
|
31
36
|
/** Omit for a headless service (e.g. `sf mcp`) — GETs outside the API surface just 404. */
|
|
32
37
|
uiDistDir?: string;
|
|
38
|
+
allowedHosts?: AllowedHosts;
|
|
39
|
+
controlTokens?: ControlTokens;
|
|
40
|
+
/** Live shutdown controller; set after listen so restore can beginDrain. */
|
|
41
|
+
getShutdown?: () => import("./shutdown.js").ShutdownController | undefined;
|
|
33
42
|
};
|
|
34
43
|
/**
|
|
35
44
|
* The full operator-console route surface: REST API (runs, stages,
|
|
@@ -39,5 +48,7 @@ export type OperatorRouteDeps = {
|
|
|
39
48
|
* API; only the former needs the UI files.
|
|
40
49
|
*/
|
|
41
50
|
export declare function createOperatorRoutes(deps: OperatorRouteDeps): (ctx: HttpHostRouteContext) => Promise<boolean | void>;
|
|
42
|
-
export declare function startUiServer(options: UiServerOptions): Promise<HttpHostEnvelope
|
|
51
|
+
export declare function startUiServer(options: UiServerOptions): Promise<HttpHostEnvelope & {
|
|
52
|
+
shutdown: ShutdownController;
|
|
53
|
+
}>;
|
|
43
54
|
export { DEFAULT_PORT };
|