stageflow 0.25.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 +10 -3
- package/dist/a2a/contracts.d.ts +1 -0
- package/dist/a2a/contracts.js +1 -0
- package/dist/a2a/registry.d.ts +2 -0
- package/dist/a2a/registry.js +20 -1
- package/dist/a2a/server.d.ts +7 -0
- package/dist/a2a/server.js +5 -2
- package/dist/a2a/service.d.ts +1 -1
- package/dist/a2a/service.js +57 -8
- package/dist/a2a/store.d.ts +8 -5
- package/dist/a2a/store.js +24 -67
- 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/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 +1 -0
- package/dist/cli/runStageCommand.js +44 -25
- package/dist/cli/runsCommand.d.ts +1 -1
- package/dist/cli/runsCommand.js +162 -8
- package/dist/cli.js +171 -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 +456 -80
- 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 +18 -1
- package/dist/runstore/createStore.js +64 -5
- 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 +111 -13
- 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 +28 -3
- package/dist/runstore/sqlite/SqliteRunStore.js +352 -219
- 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 +277 -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/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 +36 -1
- package/dist/runtime/pipelineRunner.js +178 -17
- package/dist/runtime/pipelineScheduler.d.ts +15 -0
- package/dist/runtime/pipelineScheduler.js +135 -27
- package/dist/runtime/reloadRunCatalog.js +6 -1
- package/dist/runtime/repositoryMaterialize.d.ts +47 -0
- package/dist/runtime/repositoryMaterialize.js +235 -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 +158 -4
- package/dist/runtime/runManager.js +1596 -79
- 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 +36 -2
- package/dist/server/bootstrap.js +209 -5
- 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 +103 -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 +5 -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
package/dist/runstore/port.js
CHANGED
|
@@ -6,6 +6,8 @@ export function stageStatusFromEvents(events) {
|
|
|
6
6
|
status = "running";
|
|
7
7
|
if (ev.event === "waiting_for_input")
|
|
8
8
|
status = "waiting_for_input";
|
|
9
|
+
if (ev.event === "interrupted")
|
|
10
|
+
status = "interrupted";
|
|
9
11
|
if (ev.event === "succeeded")
|
|
10
12
|
status = "succeeded";
|
|
11
13
|
if (ev.event === "failed")
|
|
@@ -54,7 +56,11 @@ function stageResolvedForSuccess(stage, dag, byId) {
|
|
|
54
56
|
}
|
|
55
57
|
return false;
|
|
56
58
|
}
|
|
57
|
-
|
|
59
|
+
const OPERATOR_TERMINAL_STATUSES = new Set(["cancelled", "queued"]);
|
|
60
|
+
export function deriveStatusFromStages(stages, dag, currentStatus) {
|
|
61
|
+
if (currentStatus !== undefined && OPERATOR_TERMINAL_STATUSES.has(currentStatus)) {
|
|
62
|
+
return currentStatus;
|
|
63
|
+
}
|
|
58
64
|
if (stages.length === 0)
|
|
59
65
|
return "created";
|
|
60
66
|
if (findUnhandledFailedStage(stages, dag))
|
|
@@ -65,6 +71,7 @@ export function deriveStatusFromStages(stages, dag) {
|
|
|
65
71
|
}
|
|
66
72
|
if (stages.some((s) => s.status === "running" ||
|
|
67
73
|
s.status === "waiting_for_input" ||
|
|
74
|
+
s.status === "interrupted" ||
|
|
68
75
|
s.status === "succeeded")) {
|
|
69
76
|
return "running";
|
|
70
77
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { type ServiceProbeResult } from "../server/ensureGlobalService.js";
|
|
2
|
+
export type RestoreErrorCode = "restore_host_live" | "restore_store_busy" | "restore_verify_failed" | "restore_schema_too_new" | "restore_version_mismatch" | "restore_unsafe_member" | "restore_failed" | "restore_not_found";
|
|
3
|
+
export declare class RestoreError extends Error {
|
|
4
|
+
readonly code: RestoreErrorCode;
|
|
5
|
+
readonly detail?: Record<string, unknown>;
|
|
6
|
+
constructor(message: string, code: RestoreErrorCode, detail?: Record<string, unknown>);
|
|
7
|
+
}
|
|
8
|
+
export type RestoreManifest = {
|
|
9
|
+
created_at?: string;
|
|
10
|
+
stageflow_version?: string;
|
|
11
|
+
schema_version?: number;
|
|
12
|
+
contents?: string[];
|
|
13
|
+
credentials_included?: boolean;
|
|
14
|
+
a2a_artifacts_included?: boolean;
|
|
15
|
+
fingerprint_skew?: boolean;
|
|
16
|
+
};
|
|
17
|
+
export type VerifiedRestoreArchive = {
|
|
18
|
+
stagingDir: string;
|
|
19
|
+
dbPath: string;
|
|
20
|
+
manifest: RestoreManifest | null;
|
|
21
|
+
schemaVersion: number;
|
|
22
|
+
missingA2aArtifactRows: number;
|
|
23
|
+
};
|
|
24
|
+
export declare function restorePendingDir(homeDir?: string): string;
|
|
25
|
+
export declare function restoreMarkerPath(homeDir?: string): string;
|
|
26
|
+
export declare function restoreAppliedPath(homeDir?: string): string;
|
|
27
|
+
export declare function restoreFailedPath(homeDir?: string): string;
|
|
28
|
+
export type RestoreMarker = {
|
|
29
|
+
archive: string;
|
|
30
|
+
attempts: number;
|
|
31
|
+
staged_at: string;
|
|
32
|
+
drain_deadline?: string;
|
|
33
|
+
};
|
|
34
|
+
export declare function extractVerifiedArchive(archivePath: string): Promise<VerifiedRestoreArchive>;
|
|
35
|
+
export declare function assertHostDownForRestore(options?: {
|
|
36
|
+
probe?: () => Promise<ServiceProbeResult>;
|
|
37
|
+
}): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Open an exclusive lock on state.db with busy_timeout=0 and hold it through
|
|
40
|
+
* the provided callback (used to relocate the DB triplet safely).
|
|
41
|
+
*/
|
|
42
|
+
export declare function withExclusiveStoreLock<T>(homeDir: string, fn: () => T): T;
|
|
43
|
+
export type ApplyRestoreResult = {
|
|
44
|
+
schema_version: number;
|
|
45
|
+
missing_a2a_artifact_rows: number;
|
|
46
|
+
pre_restore_stamp: string;
|
|
47
|
+
};
|
|
48
|
+
export declare function applyRestoreArchive(options: {
|
|
49
|
+
archivePath: string;
|
|
50
|
+
homeDir?: string;
|
|
51
|
+
force?: boolean;
|
|
52
|
+
restoreCredentials?: boolean;
|
|
53
|
+
probe?: () => Promise<ServiceProbeResult>;
|
|
54
|
+
}): Promise<ApplyRestoreResult>;
|
|
55
|
+
export declare function stageRestoreForBoot(options: {
|
|
56
|
+
archivePath: string;
|
|
57
|
+
homeDir?: string;
|
|
58
|
+
graceMs?: number;
|
|
59
|
+
}): Promise<{
|
|
60
|
+
marker: RestoreMarker;
|
|
61
|
+
stagedPath: string;
|
|
62
|
+
}>;
|
|
63
|
+
export declare function resolveBackupNameForRestore(name: string, homeDir?: string): Promise<string>;
|
|
64
|
+
export type BootRestoreOutcome = {
|
|
65
|
+
status: "none";
|
|
66
|
+
} | {
|
|
67
|
+
status: "applied";
|
|
68
|
+
result: ApplyRestoreResult;
|
|
69
|
+
} | {
|
|
70
|
+
status: "failed";
|
|
71
|
+
reason: string;
|
|
72
|
+
attempts: number;
|
|
73
|
+
} | {
|
|
74
|
+
status: "blocked";
|
|
75
|
+
reason: string;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Apply a staged restore marker before opening the store for serving.
|
|
79
|
+
* Never re-applies restore.applied. Two failures → restore.failed (hard non-serve).
|
|
80
|
+
*/
|
|
81
|
+
export declare function applyPendingRestoreAtBoot(homeDir?: string): Promise<BootRestoreOutcome>;
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { chmodSync, copyFileSync, createReadStream, existsSync, mkdirSync, readFileSync, renameSync, rmSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
2
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
3
|
+
import { createGunzip } from "node:zlib";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { pipeline } from "node:stream/promises";
|
|
7
|
+
import { createWriteStream } from "node:fs";
|
|
8
|
+
import Database from "better-sqlite3";
|
|
9
|
+
import { PACKAGE_VERSION } from "../package-meta.js";
|
|
10
|
+
import { globalStageflowHome } from "../project/globalHome.js";
|
|
11
|
+
import { CURRENT_SCHEMA_VERSION } from "./sqlite/migrations/index.js";
|
|
12
|
+
import { StoreSchemaError } from "./sqlite/storeSchemaError.js";
|
|
13
|
+
import { assertStoreQuickCheck } from "./sqlite/applyStorePragmas.js";
|
|
14
|
+
import { verifyDbSnapshot } from "./backup.js";
|
|
15
|
+
import { probeGlobalServiceDetailed, } from "../server/ensureGlobalService.js";
|
|
16
|
+
export class RestoreError extends Error {
|
|
17
|
+
code;
|
|
18
|
+
detail;
|
|
19
|
+
constructor(message, code, detail) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = "RestoreError";
|
|
22
|
+
this.code = code;
|
|
23
|
+
if (detail !== undefined)
|
|
24
|
+
this.detail = detail;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const ALLOWED_MEMBERS = new Set([
|
|
28
|
+
"state.db",
|
|
29
|
+
"settings.json",
|
|
30
|
+
"agent/auth.json",
|
|
31
|
+
"manifest.json",
|
|
32
|
+
]);
|
|
33
|
+
export function restorePendingDir(homeDir = globalStageflowHome()) {
|
|
34
|
+
return path.join(homeDir, "restore-pending");
|
|
35
|
+
}
|
|
36
|
+
export function restoreMarkerPath(homeDir = globalStageflowHome()) {
|
|
37
|
+
return path.join(restorePendingDir(homeDir), "marker.json");
|
|
38
|
+
}
|
|
39
|
+
export function restoreAppliedPath(homeDir = globalStageflowHome()) {
|
|
40
|
+
return path.join(homeDir, "restore.applied");
|
|
41
|
+
}
|
|
42
|
+
export function restoreFailedPath(homeDir = globalStageflowHome()) {
|
|
43
|
+
return path.join(homeDir, "restore.failed");
|
|
44
|
+
}
|
|
45
|
+
function majorVersion(version) {
|
|
46
|
+
return version.split(".")[0] ?? version;
|
|
47
|
+
}
|
|
48
|
+
async function gunzipToFile(src, dest) {
|
|
49
|
+
await pipeline(createReadStream(src), createGunzip(), createWriteStream(dest));
|
|
50
|
+
}
|
|
51
|
+
function parseUstarMembers(tarPath, destDir) {
|
|
52
|
+
const buf = readFileSync(tarPath);
|
|
53
|
+
const written = [];
|
|
54
|
+
let offset = 0;
|
|
55
|
+
while (offset + 512 <= buf.length) {
|
|
56
|
+
const header = buf.subarray(offset, offset + 512);
|
|
57
|
+
offset += 512;
|
|
58
|
+
if (header.every((b) => b === 0))
|
|
59
|
+
break;
|
|
60
|
+
const name = header.subarray(0, 100).toString("utf8").replace(/\0.*$/, "");
|
|
61
|
+
const sizeOctal = header.subarray(124, 135).toString("utf8").replace(/\0| /g, "");
|
|
62
|
+
const size = Number.parseInt(sizeOctal, 8) || 0;
|
|
63
|
+
const type = header[156];
|
|
64
|
+
if (name.includes("..") || path.isAbsolute(name) || name.startsWith("/")) {
|
|
65
|
+
throw new RestoreError(`restore_unsafe_member: ${name}`, "restore_unsafe_member");
|
|
66
|
+
}
|
|
67
|
+
if (type === 0x32 /* symlink */ || type === 0x31 /* hard link */) {
|
|
68
|
+
throw new RestoreError(`restore_unsafe_member: link ${name}`, "restore_unsafe_member");
|
|
69
|
+
}
|
|
70
|
+
if (!ALLOWED_MEMBERS.has(name) && name !== "") {
|
|
71
|
+
throw new RestoreError(`restore_unsafe_member: ${name} not in allowlist`, "restore_unsafe_member");
|
|
72
|
+
}
|
|
73
|
+
const data = buf.subarray(offset, offset + size);
|
|
74
|
+
offset += size + ((512 - (size % 512)) % 512);
|
|
75
|
+
if (name === "")
|
|
76
|
+
continue;
|
|
77
|
+
const outPath = path.join(destDir, name);
|
|
78
|
+
mkdirSync(path.dirname(outPath), { recursive: true });
|
|
79
|
+
writeFileSync(outPath, data);
|
|
80
|
+
written.push(name);
|
|
81
|
+
}
|
|
82
|
+
return written;
|
|
83
|
+
}
|
|
84
|
+
export async function extractVerifiedArchive(archivePath) {
|
|
85
|
+
const stagingDir = await mkdtemp(path.join(tmpdir(), "sf-restore-"));
|
|
86
|
+
try {
|
|
87
|
+
let dbPath = archivePath;
|
|
88
|
+
let manifest = null;
|
|
89
|
+
if (archivePath.endsWith(".tar.gz") || archivePath.endsWith(".tgz")) {
|
|
90
|
+
const tarPath = path.join(stagingDir, "archive.tar");
|
|
91
|
+
await gunzipToFile(archivePath, tarPath);
|
|
92
|
+
parseUstarMembers(tarPath, stagingDir);
|
|
93
|
+
dbPath = path.join(stagingDir, "state.db");
|
|
94
|
+
const manifestFile = path.join(stagingDir, "manifest.json");
|
|
95
|
+
if (existsSync(manifestFile)) {
|
|
96
|
+
manifest = JSON.parse(readFileSync(manifestFile, "utf8"));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
const copied = path.join(stagingDir, "state.db");
|
|
101
|
+
copyFileSync(archivePath, copied);
|
|
102
|
+
dbPath = copied;
|
|
103
|
+
}
|
|
104
|
+
if (!existsSync(dbPath)) {
|
|
105
|
+
throw new RestoreError("restore_verify_failed: archive missing state.db", "restore_verify_failed");
|
|
106
|
+
}
|
|
107
|
+
const db = new Database(dbPath, { readonly: true, fileMustExist: true });
|
|
108
|
+
let schemaVersion;
|
|
109
|
+
try {
|
|
110
|
+
assertStoreQuickCheck(db);
|
|
111
|
+
schemaVersion = db.pragma("user_version", { simple: true });
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
db.close();
|
|
115
|
+
}
|
|
116
|
+
if (schemaVersion > CURRENT_SCHEMA_VERSION) {
|
|
117
|
+
throw new StoreSchemaError(`store_schema_too_new: on_disk=${schemaVersion} binary_max=${CURRENT_SCHEMA_VERSION}`, "store_schema_too_new");
|
|
118
|
+
}
|
|
119
|
+
verifyDbSnapshot(dbPath, schemaVersion);
|
|
120
|
+
let missingA2aArtifactRows = 0;
|
|
121
|
+
if (manifest && manifest.a2a_artifacts_included !== true) {
|
|
122
|
+
const probe = new Database(dbPath, { readonly: true, fileMustExist: true });
|
|
123
|
+
try {
|
|
124
|
+
const row = probe
|
|
125
|
+
.prepare("SELECT COUNT(*) AS c FROM sqlite_master WHERE type='table' AND name='a2a_artifacts'")
|
|
126
|
+
.get();
|
|
127
|
+
if (row.c > 0) {
|
|
128
|
+
const count = probe
|
|
129
|
+
.prepare("SELECT COUNT(*) AS c FROM a2a_artifacts")
|
|
130
|
+
.get();
|
|
131
|
+
missingA2aArtifactRows = count.c;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
missingA2aArtifactRows = 0;
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
probe.close();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
stagingDir,
|
|
143
|
+
dbPath,
|
|
144
|
+
manifest,
|
|
145
|
+
schemaVersion,
|
|
146
|
+
missingA2aArtifactRows,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
await rm(stagingDir, { recursive: true, force: true });
|
|
151
|
+
throw err;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
export async function assertHostDownForRestore(options) {
|
|
155
|
+
const probe = options?.probe ?? probeGlobalServiceDetailed;
|
|
156
|
+
const result = await probe();
|
|
157
|
+
if (result === "up") {
|
|
158
|
+
throw new RestoreError("restore_host_live: Host is up (GET /livez). Stop it before restore.", "restore_host_live");
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Open an exclusive lock on state.db with busy_timeout=0 and hold it through
|
|
163
|
+
* the provided callback (used to relocate the DB triplet safely).
|
|
164
|
+
*/
|
|
165
|
+
export function withExclusiveStoreLock(homeDir, fn) {
|
|
166
|
+
const dbPath = path.join(homeDir, "state.db");
|
|
167
|
+
if (!existsSync(dbPath)) {
|
|
168
|
+
return fn();
|
|
169
|
+
}
|
|
170
|
+
const lockDb = new Database(dbPath);
|
|
171
|
+
try {
|
|
172
|
+
lockDb.pragma("busy_timeout = 0");
|
|
173
|
+
try {
|
|
174
|
+
lockDb.prepare("BEGIN EXCLUSIVE").run();
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
178
|
+
if (/SQLITE_BUSY|database is locked/i.test(message)) {
|
|
179
|
+
throw new RestoreError("restore_store_busy: another process holds state.db", "restore_store_busy");
|
|
180
|
+
}
|
|
181
|
+
throw err;
|
|
182
|
+
}
|
|
183
|
+
return fn();
|
|
184
|
+
}
|
|
185
|
+
finally {
|
|
186
|
+
try {
|
|
187
|
+
lockDb.exec("ROLLBACK");
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
// ignore
|
|
191
|
+
}
|
|
192
|
+
lockDb.close();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function moveAsideTriplet(homeDir, stamp) {
|
|
196
|
+
for (const name of ["state.db", "state.db-wal", "state.db-shm"]) {
|
|
197
|
+
const from = path.join(homeDir, name);
|
|
198
|
+
if (!existsSync(from))
|
|
199
|
+
continue;
|
|
200
|
+
renameSync(from, `${from}.pre-restore-${stamp}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function assertNoSidecars(dbPath) {
|
|
204
|
+
for (const side of [`${dbPath}-wal`, `${dbPath}-shm`]) {
|
|
205
|
+
if (existsSync(side)) {
|
|
206
|
+
unlinkSync(side);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (existsSync(`${dbPath}-wal`) || existsSync(`${dbPath}-shm`)) {
|
|
210
|
+
throw new RestoreError("restore_verify_failed: stale WAL/SHM sidecars remain", "restore_verify_failed");
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
export async function applyRestoreArchive(options) {
|
|
214
|
+
const homeDir = options.homeDir ?? globalStageflowHome();
|
|
215
|
+
await assertHostDownForRestore({ probe: options.probe });
|
|
216
|
+
const verified = await extractVerifiedArchive(options.archivePath);
|
|
217
|
+
try {
|
|
218
|
+
if (verified.manifest?.stageflow_version &&
|
|
219
|
+
majorVersion(verified.manifest.stageflow_version) !==
|
|
220
|
+
majorVersion(PACKAGE_VERSION) &&
|
|
221
|
+
options.force !== true) {
|
|
222
|
+
throw new RestoreError(`restore_version_mismatch: archive ${verified.manifest.stageflow_version} vs binary ${PACKAGE_VERSION} (pass --force)`, "restore_version_mismatch");
|
|
223
|
+
}
|
|
224
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
225
|
+
const restoreCredentials = options.restoreCredentials !== false;
|
|
226
|
+
withExclusiveStoreLock(homeDir, () => {
|
|
227
|
+
moveAsideTriplet(homeDir, stamp);
|
|
228
|
+
const destDb = path.join(homeDir, "state.db");
|
|
229
|
+
copyFileSync(verified.dbPath, destDb);
|
|
230
|
+
assertNoSidecars(destDb);
|
|
231
|
+
if (restoreCredentials) {
|
|
232
|
+
const settingsSrc = path.join(verified.stagingDir, "settings.json");
|
|
233
|
+
const authSrc = path.join(verified.stagingDir, "agent", "auth.json");
|
|
234
|
+
if (existsSync(settingsSrc)) {
|
|
235
|
+
copyFileSync(settingsSrc, path.join(homeDir, "settings.json"));
|
|
236
|
+
}
|
|
237
|
+
if (existsSync(authSrc)) {
|
|
238
|
+
const agentDir = path.join(homeDir, "agent");
|
|
239
|
+
mkdirSync(agentDir, { recursive: true });
|
|
240
|
+
try {
|
|
241
|
+
chmodSync(agentDir, 0o700);
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
// best-effort
|
|
245
|
+
}
|
|
246
|
+
const authDest = path.join(agentDir, "auth.json");
|
|
247
|
+
copyFileSync(authSrc, authDest);
|
|
248
|
+
try {
|
|
249
|
+
chmodSync(authDest, 0o600);
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
// best-effort
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
const check = new Database(path.join(homeDir, "state.db"));
|
|
258
|
+
try {
|
|
259
|
+
assertStoreQuickCheck(check);
|
|
260
|
+
}
|
|
261
|
+
finally {
|
|
262
|
+
check.close();
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
schema_version: verified.schemaVersion,
|
|
266
|
+
missing_a2a_artifact_rows: verified.missingA2aArtifactRows,
|
|
267
|
+
pre_restore_stamp: stamp,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
finally {
|
|
271
|
+
await rm(verified.stagingDir, { recursive: true, force: true });
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
export async function stageRestoreForBoot(options) {
|
|
275
|
+
const homeDir = options.homeDir ?? globalStageflowHome();
|
|
276
|
+
if (existsSync(restoreFailedPath(homeDir))) {
|
|
277
|
+
throw new RestoreError("restore_failed: clear restore.failed before staging another restore", "restore_failed");
|
|
278
|
+
}
|
|
279
|
+
const verified = await extractVerifiedArchive(options.archivePath);
|
|
280
|
+
await rm(verified.stagingDir, { recursive: true, force: true });
|
|
281
|
+
if (verified.manifest?.stageflow_version &&
|
|
282
|
+
majorVersion(verified.manifest.stageflow_version) !==
|
|
283
|
+
majorVersion(PACKAGE_VERSION)) {
|
|
284
|
+
throw new RestoreError(`restore_version_mismatch: archive ${verified.manifest.stageflow_version} vs binary ${PACKAGE_VERSION}`, "restore_version_mismatch");
|
|
285
|
+
}
|
|
286
|
+
const pending = restorePendingDir(homeDir);
|
|
287
|
+
mkdirSync(pending, { recursive: true });
|
|
288
|
+
const basename = `staged-${Date.now()}${path.extname(options.archivePath) || ".tar.gz"}`;
|
|
289
|
+
const stagedPath = path.join(pending, basename);
|
|
290
|
+
copyFileSync(options.archivePath, stagedPath);
|
|
291
|
+
const graceMs = options.graceMs ?? 8000;
|
|
292
|
+
const marker = {
|
|
293
|
+
archive: basename,
|
|
294
|
+
attempts: 0,
|
|
295
|
+
staged_at: new Date().toISOString(),
|
|
296
|
+
drain_deadline: new Date(Date.now() + graceMs).toISOString(),
|
|
297
|
+
};
|
|
298
|
+
writeFileSync(restoreMarkerPath(homeDir), `${JSON.stringify(marker, null, 2)}\n`);
|
|
299
|
+
return { marker, stagedPath };
|
|
300
|
+
}
|
|
301
|
+
export async function resolveBackupNameForRestore(name, homeDir = globalStageflowHome()) {
|
|
302
|
+
const { resolveBackupDownloadPath } = await import("./backup.js");
|
|
303
|
+
return resolveBackupDownloadPath(name, homeDir);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Apply a staged restore marker before opening the store for serving.
|
|
307
|
+
* Never re-applies restore.applied. Two failures → restore.failed (hard non-serve).
|
|
308
|
+
*/
|
|
309
|
+
export async function applyPendingRestoreAtBoot(homeDir = globalStageflowHome()) {
|
|
310
|
+
if (existsSync(restoreFailedPath(homeDir))) {
|
|
311
|
+
const raw = readFileSync(restoreFailedPath(homeDir), "utf8");
|
|
312
|
+
let reason = "restore.failed present";
|
|
313
|
+
try {
|
|
314
|
+
reason = JSON.parse(raw).reason ?? reason;
|
|
315
|
+
}
|
|
316
|
+
catch {
|
|
317
|
+
// keep default
|
|
318
|
+
}
|
|
319
|
+
return { status: "blocked", reason };
|
|
320
|
+
}
|
|
321
|
+
if (existsSync(restoreAppliedPath(homeDir))) {
|
|
322
|
+
return { status: "none" };
|
|
323
|
+
}
|
|
324
|
+
const markerFile = restoreMarkerPath(homeDir);
|
|
325
|
+
if (!existsSync(markerFile)) {
|
|
326
|
+
return { status: "none" };
|
|
327
|
+
}
|
|
328
|
+
const marker = JSON.parse(readFileSync(markerFile, "utf8"));
|
|
329
|
+
const archivePath = path.join(restorePendingDir(homeDir), marker.archive);
|
|
330
|
+
marker.attempts += 1;
|
|
331
|
+
writeFileSync(markerFile, `${JSON.stringify(marker, null, 2)}\n`);
|
|
332
|
+
try {
|
|
333
|
+
if (!existsSync(archivePath)) {
|
|
334
|
+
throw new RestoreError(`restore_not_found: staged archive missing (${marker.archive})`, "restore_not_found");
|
|
335
|
+
}
|
|
336
|
+
const result = await applyRestoreArchive({
|
|
337
|
+
archivePath,
|
|
338
|
+
homeDir,
|
|
339
|
+
force: true,
|
|
340
|
+
probe: async () => "unreachable",
|
|
341
|
+
});
|
|
342
|
+
writeFileSync(restoreAppliedPath(homeDir), `${JSON.stringify({ ...result, applied_at: new Date().toISOString() }, null, 2)}\n`);
|
|
343
|
+
rmSync(markerFile, { force: true });
|
|
344
|
+
return { status: "applied", result };
|
|
345
|
+
}
|
|
346
|
+
catch (err) {
|
|
347
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
348
|
+
if (marker.attempts >= 2) {
|
|
349
|
+
writeFileSync(restoreFailedPath(homeDir), `${JSON.stringify({ reason, attempts: marker.attempts, at: new Date().toISOString() }, null, 2)}\n`);
|
|
350
|
+
rmSync(markerFile, { force: true });
|
|
351
|
+
return { status: "failed", reason, attempts: marker.attempts };
|
|
352
|
+
}
|
|
353
|
+
return { status: "failed", reason, attempts: marker.attempts };
|
|
354
|
+
}
|
|
355
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { RunMeta, RunStatus } from "./port.js";
|
|
2
|
+
export type RetentionDecision = "none" | "slim" | "purge";
|
|
3
|
+
export type StatusRetentionWindow = {
|
|
4
|
+
slimMs: number;
|
|
5
|
+
purgeMs: number;
|
|
6
|
+
};
|
|
7
|
+
export type RetentionWindows = {
|
|
8
|
+
succeeded: StatusRetentionWindow;
|
|
9
|
+
failed: StatusRetentionWindow;
|
|
10
|
+
cancelled: StatusRetentionWindow;
|
|
11
|
+
};
|
|
12
|
+
export declare const DAY_MS: number;
|
|
13
|
+
/** Defaults: succeeded 3d/30d; failed and cancelled 30d/90d (R30 / AE6). */
|
|
14
|
+
export declare const DEFAULT_RETENTION_WINDOWS: RetentionWindows;
|
|
15
|
+
export declare const DEFAULT_BARE_CACHE_TTL_MS: number;
|
|
16
|
+
export declare const DEFAULT_SLIM_ARTIFACT_MAX_BYTES: number;
|
|
17
|
+
export type RetentionRow = Pick<RunMeta, "status" | "finished_at" | "slimmed_at">;
|
|
18
|
+
/** Resolve retention windows from KD1 defaults plus per-status env overrides. */
|
|
19
|
+
export declare function retentionWindowsFromEnv(env?: NodeJS.ProcessEnv): RetentionWindows;
|
|
20
|
+
export declare function bareCacheTtlFromEnv(env?: NodeJS.ProcessEnv): number;
|
|
21
|
+
export declare function slimArtifactMaxBytesFromEnv(env?: NodeJS.ProcessEnv): number;
|
|
22
|
+
/**
|
|
23
|
+
* Clock-injected eligibility. Reads `finished_at` only (KTD1), never `updated_at`.
|
|
24
|
+
* Already-slimmed rows past SLIM but not PURGE return `"none"` (R18).
|
|
25
|
+
*/
|
|
26
|
+
export declare function retentionDecision(row: RetentionRow, now: Date, windows?: RetentionWindows): RetentionDecision;
|
|
27
|
+
export declare function isTerminalRunStatus(status: RunStatus | undefined): status is "succeeded" | "failed" | "cancelled";
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export const DAY_MS = 24 * 60 * 60 * 1000;
|
|
2
|
+
/** Defaults: succeeded 3d/30d; failed and cancelled 30d/90d (R30 / AE6). */
|
|
3
|
+
export const DEFAULT_RETENTION_WINDOWS = {
|
|
4
|
+
succeeded: { slimMs: 3 * DAY_MS, purgeMs: 30 * DAY_MS },
|
|
5
|
+
cancelled: { slimMs: 30 * DAY_MS, purgeMs: 90 * DAY_MS },
|
|
6
|
+
failed: { slimMs: 30 * DAY_MS, purgeMs: 90 * DAY_MS },
|
|
7
|
+
};
|
|
8
|
+
export const DEFAULT_BARE_CACHE_TTL_MS = 30 * DAY_MS;
|
|
9
|
+
export const DEFAULT_SLIM_ARTIFACT_MAX_BYTES = 1024 * 1024;
|
|
10
|
+
const TERMINAL = new Set([
|
|
11
|
+
"succeeded",
|
|
12
|
+
"failed",
|
|
13
|
+
"cancelled",
|
|
14
|
+
]);
|
|
15
|
+
function parsePositiveMs(raw) {
|
|
16
|
+
if (raw === undefined)
|
|
17
|
+
return undefined;
|
|
18
|
+
const trimmed = raw.trim();
|
|
19
|
+
if (trimmed === "")
|
|
20
|
+
return undefined;
|
|
21
|
+
const n = Number.parseInt(trimmed, 10);
|
|
22
|
+
if (!Number.isFinite(n) || n < 0)
|
|
23
|
+
return undefined;
|
|
24
|
+
return n;
|
|
25
|
+
}
|
|
26
|
+
function windowFromEnv(env, status, defaults) {
|
|
27
|
+
const statusKey = status.toUpperCase();
|
|
28
|
+
return {
|
|
29
|
+
slimMs: parsePositiveMs(env[`STAGEFLOW_SLIM_${statusKey}_MS`]) ?? defaults.slimMs,
|
|
30
|
+
purgeMs: parsePositiveMs(env[`STAGEFLOW_PURGE_${statusKey}_MS`]) ??
|
|
31
|
+
defaults.purgeMs,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Resolve retention windows from KD1 defaults plus per-status env overrides. */
|
|
35
|
+
export function retentionWindowsFromEnv(env = process.env) {
|
|
36
|
+
return {
|
|
37
|
+
succeeded: windowFromEnv(env, "succeeded", DEFAULT_RETENTION_WINDOWS.succeeded),
|
|
38
|
+
failed: windowFromEnv(env, "failed", DEFAULT_RETENTION_WINDOWS.failed),
|
|
39
|
+
cancelled: windowFromEnv(env, "cancelled", DEFAULT_RETENTION_WINDOWS.cancelled),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function bareCacheTtlFromEnv(env = process.env) {
|
|
43
|
+
return (parsePositiveMs(env.STAGEFLOW_BARE_CACHE_TTL_MS) ?? DEFAULT_BARE_CACHE_TTL_MS);
|
|
44
|
+
}
|
|
45
|
+
export function slimArtifactMaxBytesFromEnv(env = process.env) {
|
|
46
|
+
return (parsePositiveMs(env.STAGEFLOW_SLIM_ARTIFACT_MAX_BYTES) ??
|
|
47
|
+
DEFAULT_SLIM_ARTIFACT_MAX_BYTES);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Clock-injected eligibility. Reads `finished_at` only (KTD1), never `updated_at`.
|
|
51
|
+
* Already-slimmed rows past SLIM but not PURGE return `"none"` (R18).
|
|
52
|
+
*/
|
|
53
|
+
export function retentionDecision(row, now, windows = DEFAULT_RETENTION_WINDOWS) {
|
|
54
|
+
const status = row.status;
|
|
55
|
+
if (status === undefined || !TERMINAL.has(status))
|
|
56
|
+
return "none";
|
|
57
|
+
if (row.finished_at === undefined || row.finished_at === "")
|
|
58
|
+
return "none";
|
|
59
|
+
const finishedMs = Date.parse(row.finished_at);
|
|
60
|
+
if (!Number.isFinite(finishedMs))
|
|
61
|
+
return "none";
|
|
62
|
+
const ageMs = now.getTime() - finishedMs;
|
|
63
|
+
if (ageMs < 0)
|
|
64
|
+
return "none";
|
|
65
|
+
const window = windows[status];
|
|
66
|
+
if (ageMs >= window.purgeMs)
|
|
67
|
+
return "purge";
|
|
68
|
+
if (ageMs >= window.slimMs && row.slimmed_at === undefined)
|
|
69
|
+
return "slim";
|
|
70
|
+
return "none";
|
|
71
|
+
}
|
|
72
|
+
export function isTerminalRunStatus(status) {
|
|
73
|
+
return status !== undefined && TERMINAL.has(status);
|
|
74
|
+
}
|