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
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { chmodSync, copyFileSync, createReadStream, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
3
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { createGzip } from "node:zlib";
|
|
7
|
+
import { pipeline } from "node:stream/promises";
|
|
8
|
+
import { createWriteStream } from "node:fs";
|
|
9
|
+
import Database from "better-sqlite3";
|
|
10
|
+
import { PACKAGE_VERSION } from "../package-meta.js";
|
|
11
|
+
import { globalStageflowHome } from "../project/globalHome.js";
|
|
12
|
+
import { readFilesystemSize } from "./diskUsage.js";
|
|
13
|
+
import { isInsideDir } from "./workspaceLayout.js";
|
|
14
|
+
export class BackupError extends Error {
|
|
15
|
+
code;
|
|
16
|
+
detail;
|
|
17
|
+
constructor(message, code, detail) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = "BackupError";
|
|
20
|
+
this.code = code;
|
|
21
|
+
if (detail !== undefined)
|
|
22
|
+
this.detail = detail;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const SECRET_WARNING = "This backup may contain provider credentials and must be treated as a secret.";
|
|
26
|
+
const FREE_SPACE_MARGIN_BYTES = 8 * 1024 * 1024;
|
|
27
|
+
export function backupsDir(homeDir = globalStageflowHome()) {
|
|
28
|
+
return path.join(homeDir, "backups");
|
|
29
|
+
}
|
|
30
|
+
export function defaultBackupOutPath(homeDir = globalStageflowHome(), now = new Date(), dbOnly = false) {
|
|
31
|
+
const stamp = now.toISOString().replace(/[:.]/g, "-");
|
|
32
|
+
const ext = dbOnly ? ".db" : ".tar.gz";
|
|
33
|
+
return path.join(backupsDir(homeDir), `stageflow-${stamp}${ext}`);
|
|
34
|
+
}
|
|
35
|
+
export function assertBackupOutPathAllowed(outPath, homeDir = globalStageflowHome()) {
|
|
36
|
+
const segments = outPath.split(/[/\\]/);
|
|
37
|
+
if (segments.some((segment) => segment === "..")) {
|
|
38
|
+
throw new BackupError("backup_out_denied: path must not contain .. segments", "backup_out_denied");
|
|
39
|
+
}
|
|
40
|
+
const resolved = path.resolve(outPath);
|
|
41
|
+
const worktrees = path.resolve(homeDir, "worktrees");
|
|
42
|
+
const runs = path.resolve(homeDir, "runs");
|
|
43
|
+
if (isInsideDir(resolved, worktrees) || isInsideDir(resolved, runs)) {
|
|
44
|
+
throw new BackupError("backup_out_denied: refusing to write under worktrees/ or runs/", "backup_out_denied", { path: resolved });
|
|
45
|
+
}
|
|
46
|
+
return resolved;
|
|
47
|
+
}
|
|
48
|
+
function fingerprint(filePath) {
|
|
49
|
+
if (!existsSync(filePath))
|
|
50
|
+
return null;
|
|
51
|
+
const st = statSync(filePath);
|
|
52
|
+
return `${st.size}:${Math.trunc(st.mtimeMs)}`;
|
|
53
|
+
}
|
|
54
|
+
function stateDbTripletBytes(homeDir) {
|
|
55
|
+
let total = 0;
|
|
56
|
+
for (const name of ["state.db", "state.db-wal", "state.db-shm"]) {
|
|
57
|
+
const p = path.join(homeDir, name);
|
|
58
|
+
if (!existsSync(p))
|
|
59
|
+
continue;
|
|
60
|
+
total += statSync(p).size;
|
|
61
|
+
}
|
|
62
|
+
return total;
|
|
63
|
+
}
|
|
64
|
+
export function verifyDbSnapshot(snapshotPath, expectedUserVersion) {
|
|
65
|
+
const db = new Database(snapshotPath, {
|
|
66
|
+
readonly: true,
|
|
67
|
+
fileMustExist: true,
|
|
68
|
+
});
|
|
69
|
+
try {
|
|
70
|
+
const qc = db.pragma("quick_check", { simple: true });
|
|
71
|
+
if (qc !== "ok") {
|
|
72
|
+
throw new BackupError(`backup_verify_failed: quick_check returned ${String(qc)}`, "backup_verify_failed", { quick_check: qc });
|
|
73
|
+
}
|
|
74
|
+
const version = db.pragma("user_version", { simple: true });
|
|
75
|
+
if (version !== expectedUserVersion) {
|
|
76
|
+
throw new BackupError(`backup_verify_failed: user_version ${version} != source ${expectedUserVersion}`, "backup_verify_failed", { user_version: version, expected: expectedUserVersion });
|
|
77
|
+
}
|
|
78
|
+
if (existsSync(`${snapshotPath}-wal`) || existsSync(`${snapshotPath}-shm`)) {
|
|
79
|
+
throw new BackupError("backup_verify_failed: snapshot has WAL/SHM sidecars", "backup_verify_failed");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
db.close();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function sha256File(filePath) {
|
|
87
|
+
const hash = createHash("sha256");
|
|
88
|
+
hash.update(readFileSync(filePath));
|
|
89
|
+
return hash.digest("hex");
|
|
90
|
+
}
|
|
91
|
+
function writeUstarHeader(name, size, mode) {
|
|
92
|
+
const buf = Buffer.alloc(512, 0);
|
|
93
|
+
const nameBytes = Buffer.from(name, "utf8");
|
|
94
|
+
if (nameBytes.length > 100) {
|
|
95
|
+
throw new Error(`tar member name too long: ${name}`);
|
|
96
|
+
}
|
|
97
|
+
nameBytes.copy(buf, 0);
|
|
98
|
+
buf.write(mode.toString(8).padStart(7, "0"), 100, 7, "utf8");
|
|
99
|
+
buf.write("0000000", 108, 7, "utf8");
|
|
100
|
+
buf.write("0000000", 116, 7, "utf8");
|
|
101
|
+
buf.write(size.toString(8).padStart(11, "0"), 124, 11, "utf8");
|
|
102
|
+
buf.write(Math.floor(Date.now() / 1000).toString(8).padStart(11, "0"), 136, 11, "utf8");
|
|
103
|
+
buf.write(" ", 148, 8, "utf8");
|
|
104
|
+
buf[156] = 0x30; // regular file
|
|
105
|
+
Buffer.from("ustar\0", "utf8").copy(buf, 257);
|
|
106
|
+
Buffer.from("00", "utf8").copy(buf, 263);
|
|
107
|
+
let sum = 0;
|
|
108
|
+
for (let i = 0; i < 512; i += 1)
|
|
109
|
+
sum += buf[i];
|
|
110
|
+
buf.write(`${sum.toString(8).padStart(6, "0")}\0 `, 148, 8, "utf8");
|
|
111
|
+
return buf;
|
|
112
|
+
}
|
|
113
|
+
async function writeTarGz(archivePath, members) {
|
|
114
|
+
const partial = `${archivePath}.partial`;
|
|
115
|
+
if (existsSync(partial))
|
|
116
|
+
rmSync(partial, { force: true });
|
|
117
|
+
const gzip = createGzip();
|
|
118
|
+
const out = createWriteStream(partial, { mode: 0o600 });
|
|
119
|
+
const writing = pipeline(gzip, out);
|
|
120
|
+
for (const member of members) {
|
|
121
|
+
const data = readFileSync(member.filePath);
|
|
122
|
+
gzip.write(writeUstarHeader(member.name, data.length, member.mode));
|
|
123
|
+
gzip.write(data);
|
|
124
|
+
const pad = (512 - (data.length % 512)) % 512;
|
|
125
|
+
if (pad > 0)
|
|
126
|
+
gzip.write(Buffer.alloc(pad, 0));
|
|
127
|
+
}
|
|
128
|
+
gzip.write(Buffer.alloc(1024, 0));
|
|
129
|
+
gzip.end();
|
|
130
|
+
await writing;
|
|
131
|
+
chmodSync(partial, 0o600);
|
|
132
|
+
renameSync(partial, archivePath);
|
|
133
|
+
chmodSync(archivePath, 0o600);
|
|
134
|
+
}
|
|
135
|
+
export async function createBackup(options) {
|
|
136
|
+
const homeDir = options.homeDir ?? globalStageflowHome();
|
|
137
|
+
const dbOnly = options.dbOnly === true;
|
|
138
|
+
const noCredentials = options.noCredentials === true;
|
|
139
|
+
const includeA2a = options.includeA2aArtifacts === true;
|
|
140
|
+
const now = options.now?.() ?? new Date();
|
|
141
|
+
const createdAt = now.toISOString();
|
|
142
|
+
const outPath = assertBackupOutPathAllowed(options.outPath ?? defaultBackupOutPath(homeDir, now, dbOnly), homeDir);
|
|
143
|
+
mkdirSync(path.dirname(outPath), { recursive: true });
|
|
144
|
+
const freeReader = options.freeSpace ?? readFilesystemSize;
|
|
145
|
+
const free = await freeReader(homeDir);
|
|
146
|
+
const needed = stateDbTripletBytes(homeDir) + FREE_SPACE_MARGIN_BYTES;
|
|
147
|
+
if (free.freeBytes < needed) {
|
|
148
|
+
throw new BackupError(`backup_insufficient_disk: need ~${needed} bytes free, have ${free.freeBytes}`, "backup_insufficient_disk", { needed_bytes: needed, free_bytes: free.freeBytes });
|
|
149
|
+
}
|
|
150
|
+
const settingsPath = path.join(homeDir, "settings.json");
|
|
151
|
+
const authPath = path.join(homeDir, "agent", "auth.json");
|
|
152
|
+
const settingsBefore = fingerprint(settingsPath);
|
|
153
|
+
const authBefore = fingerprint(authPath);
|
|
154
|
+
const staging = await mkdtemp(path.join(tmpdir(), "sf-backup-"));
|
|
155
|
+
const snapshotPath = path.join(staging, "state.db");
|
|
156
|
+
try {
|
|
157
|
+
const { userVersion } = await options.store.snapshotInto(snapshotPath);
|
|
158
|
+
verifyDbSnapshot(snapshotPath, userVersion);
|
|
159
|
+
if (dbOnly) {
|
|
160
|
+
const partial = `${outPath}.partial`;
|
|
161
|
+
if (existsSync(partial))
|
|
162
|
+
rmSync(partial, { force: true });
|
|
163
|
+
if (existsSync(outPath))
|
|
164
|
+
rmSync(outPath, { force: true });
|
|
165
|
+
copyFileSync(snapshotPath, partial);
|
|
166
|
+
chmodSync(partial, 0o600);
|
|
167
|
+
try {
|
|
168
|
+
renameSync(partial, outPath);
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
const code = err.code;
|
|
172
|
+
if (code === "EXDEV") {
|
|
173
|
+
throw new BackupError("backup_cross_device: stage partial beside target on the same filesystem", "backup_cross_device");
|
|
174
|
+
}
|
|
175
|
+
throw err;
|
|
176
|
+
}
|
|
177
|
+
chmodSync(outPath, 0o600);
|
|
178
|
+
const bytes = statSync(outPath).size;
|
|
179
|
+
return {
|
|
180
|
+
path: outPath,
|
|
181
|
+
bytes,
|
|
182
|
+
sha256: sha256File(outPath),
|
|
183
|
+
schema_version: userVersion,
|
|
184
|
+
stageflow_version: PACKAGE_VERSION,
|
|
185
|
+
created_at: createdAt,
|
|
186
|
+
contents: ["state.db"],
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
const settingsAfter = fingerprint(settingsPath);
|
|
190
|
+
const authAfter = fingerprint(authPath);
|
|
191
|
+
const fingerprintSkew = settingsBefore !== settingsAfter || authBefore !== authAfter;
|
|
192
|
+
const contents = ["state.db", "manifest.json"];
|
|
193
|
+
const members = [
|
|
194
|
+
{ name: "state.db", filePath: snapshotPath, mode: 0o600 },
|
|
195
|
+
];
|
|
196
|
+
if (existsSync(settingsPath)) {
|
|
197
|
+
const stagedSettings = path.join(staging, "settings.json");
|
|
198
|
+
copyFileSync(settingsPath, stagedSettings);
|
|
199
|
+
members.push({
|
|
200
|
+
name: "settings.json",
|
|
201
|
+
filePath: stagedSettings,
|
|
202
|
+
mode: 0o600,
|
|
203
|
+
});
|
|
204
|
+
contents.push("settings.json");
|
|
205
|
+
}
|
|
206
|
+
if (!noCredentials && existsSync(authPath)) {
|
|
207
|
+
const stagedAuth = path.join(staging, "auth.json");
|
|
208
|
+
copyFileSync(authPath, stagedAuth);
|
|
209
|
+
chmodSync(stagedAuth, 0o600);
|
|
210
|
+
members.push({
|
|
211
|
+
name: "agent/auth.json",
|
|
212
|
+
filePath: stagedAuth,
|
|
213
|
+
mode: 0o600,
|
|
214
|
+
});
|
|
215
|
+
contents.push("agent/auth.json");
|
|
216
|
+
}
|
|
217
|
+
if (includeA2a) {
|
|
218
|
+
const a2aRoot = path.join(homeDir, "a2a-artifacts");
|
|
219
|
+
if (existsSync(a2aRoot)) {
|
|
220
|
+
contents.push("a2a-artifacts");
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const manifest = {
|
|
224
|
+
created_at: createdAt,
|
|
225
|
+
stageflow_version: PACKAGE_VERSION,
|
|
226
|
+
schema_version: userVersion,
|
|
227
|
+
contents,
|
|
228
|
+
db_only: false,
|
|
229
|
+
credentials_included: !noCredentials && existsSync(authPath),
|
|
230
|
+
a2a_artifacts_included: includeA2a,
|
|
231
|
+
settings_fingerprint_before: settingsBefore,
|
|
232
|
+
settings_fingerprint_after: settingsAfter,
|
|
233
|
+
auth_fingerprint_before: authBefore,
|
|
234
|
+
auth_fingerprint_after: authAfter,
|
|
235
|
+
fingerprint_skew: fingerprintSkew,
|
|
236
|
+
secret_warning: SECRET_WARNING,
|
|
237
|
+
};
|
|
238
|
+
const manifestPath = path.join(staging, "manifest.json");
|
|
239
|
+
writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, {
|
|
240
|
+
mode: 0o600,
|
|
241
|
+
});
|
|
242
|
+
members.push({
|
|
243
|
+
name: "manifest.json",
|
|
244
|
+
filePath: manifestPath,
|
|
245
|
+
mode: 0o600,
|
|
246
|
+
});
|
|
247
|
+
if (existsSync(outPath))
|
|
248
|
+
rmSync(outPath, { force: true });
|
|
249
|
+
await writeTarGz(outPath, members);
|
|
250
|
+
const bytes = statSync(outPath).size;
|
|
251
|
+
return {
|
|
252
|
+
path: outPath,
|
|
253
|
+
bytes,
|
|
254
|
+
sha256: sha256File(outPath),
|
|
255
|
+
schema_version: userVersion,
|
|
256
|
+
stageflow_version: PACKAGE_VERSION,
|
|
257
|
+
created_at: createdAt,
|
|
258
|
+
contents,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
finally {
|
|
262
|
+
await rm(staging, { recursive: true, force: true });
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
export async function resolveBackupDownloadPath(name, homeDir = globalStageflowHome()) {
|
|
266
|
+
if (typeof name !== "string" ||
|
|
267
|
+
name.trim() === "" ||
|
|
268
|
+
name.includes("..") ||
|
|
269
|
+
name.includes("/") ||
|
|
270
|
+
name.includes("\\") ||
|
|
271
|
+
path.isAbsolute(name) ||
|
|
272
|
+
name.toLowerCase().endsWith(".partial")) {
|
|
273
|
+
throw new BackupError("backup_out_denied: invalid backup name", "backup_out_denied");
|
|
274
|
+
}
|
|
275
|
+
const root = backupsDir(homeDir);
|
|
276
|
+
const candidate = path.resolve(root, name);
|
|
277
|
+
const { classifyRealPathContainment } = await import("./workspaceLayout.js");
|
|
278
|
+
const containment = await classifyRealPathContainment(candidate, root);
|
|
279
|
+
if (containment.status === "missing") {
|
|
280
|
+
throw new BackupError(`backup not found: ${name}`, "backup_out_denied");
|
|
281
|
+
}
|
|
282
|
+
if (containment.status === "outside") {
|
|
283
|
+
throw new BackupError("backup_out_denied: path escapes backups directory", "backup_out_denied");
|
|
284
|
+
}
|
|
285
|
+
if (path.basename(containment.realPath).toLowerCase().endsWith(".partial")) {
|
|
286
|
+
throw new BackupError("backup_out_denied: invalid backup name", "backup_out_denied");
|
|
287
|
+
}
|
|
288
|
+
return containment.realPath;
|
|
289
|
+
}
|
|
290
|
+
export function openBackupReadStream(filePath) {
|
|
291
|
+
return createReadStream(filePath);
|
|
292
|
+
}
|
|
@@ -1,13 +1,30 @@
|
|
|
1
|
-
import
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import type { EnsureGlobalServiceResult } from "../server/ensureGlobalService.js";
|
|
2
3
|
import type { RunStore } from "./port.js";
|
|
4
|
+
import { type StoreSchemaErrorCode } from "./sqlite/storeSchemaError.js";
|
|
3
5
|
export type RunStoreKind = "sqlite";
|
|
6
|
+
export type RunStoreOpenerMode = "assert" | "migrate";
|
|
4
7
|
export type RunStoreConfig = {
|
|
5
8
|
/** Project / factory root. Store data lives under `<rootDir>/.stageflow`. */
|
|
6
9
|
rootDir: string;
|
|
7
10
|
/** Defaults to `SF_STORE` env or `"sqlite"`. */
|
|
8
11
|
kind?: RunStoreKind | "disk";
|
|
12
|
+
/** Default `assert`; Host and tests that need a fresh schema pass `migrate`. */
|
|
13
|
+
openerMode?: RunStoreOpenerMode;
|
|
9
14
|
};
|
|
10
15
|
export declare const DISK_STORE_REJECTED = "SF_STORE=disk is no longer a live RunStore. SQLite is the only adapter. Disk-era .stageflow/runs trees import automatically when the SQLite store is empty.";
|
|
16
|
+
export declare function storeNeedsHostMigration(rootDir: string): boolean;
|
|
17
|
+
export declare function createRunStoreAfterHostEnsure(config: RunStoreConfig, ensureService: () => Promise<EnsureGlobalServiceResult>): Promise<{
|
|
18
|
+
ok: true;
|
|
19
|
+
store: RunStore;
|
|
20
|
+
} | {
|
|
21
|
+
ok: false;
|
|
22
|
+
message: string;
|
|
23
|
+
reason?: Extract<EnsureGlobalServiceResult, {
|
|
24
|
+
ok: false;
|
|
25
|
+
}>["reason"];
|
|
26
|
+
code?: StoreSchemaErrorCode;
|
|
27
|
+
}>;
|
|
11
28
|
/**
|
|
12
29
|
* Composition-root factory: hands back the concrete SQLite connection alongside the `RunStore`,
|
|
13
30
|
* for the one caller wiring a second, colocated store (A2A's tables) into the same `state.db`
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import { mkdirSync } from "node:fs";
|
|
2
|
-
import
|
|
1
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import Database from "better-sqlite3";
|
|
4
|
+
import { assertStageflowHomeWritable, globalStageflowHome, } from "../project/globalHome.js";
|
|
5
|
+
import { flattenNestedGlobalStore, isGlobalStageflowHome, migrateLegacyStoreRoot, resolveStoreRoot, } from "./paths.js";
|
|
3
6
|
import { SqliteRunStore } from "./sqlite/SqliteRunStore.js";
|
|
7
|
+
import { CURRENT_SCHEMA_VERSION } from "./sqlite/migrations/index.js";
|
|
8
|
+
import { StoreSchemaError } from "./sqlite/storeSchemaError.js";
|
|
4
9
|
export const DISK_STORE_REJECTED = "SF_STORE=disk is no longer a live RunStore. SQLite is the only adapter. Disk-era .stageflow/runs trees import automatically when the SQLite store is empty.";
|
|
5
10
|
function resolveKind(kind) {
|
|
6
11
|
const raw = kind ?? process.env.SF_STORE?.trim().toLowerCase();
|
|
@@ -10,6 +15,50 @@ function resolveKind(kind) {
|
|
|
10
15
|
}
|
|
11
16
|
return "sqlite";
|
|
12
17
|
}
|
|
18
|
+
function resolveOpenerMode(config) {
|
|
19
|
+
if (config.openerMode !== undefined) {
|
|
20
|
+
return config.openerMode;
|
|
21
|
+
}
|
|
22
|
+
if (process.env.VITEST === "true") {
|
|
23
|
+
return "migrate";
|
|
24
|
+
}
|
|
25
|
+
return "assert";
|
|
26
|
+
}
|
|
27
|
+
export function storeNeedsHostMigration(rootDir) {
|
|
28
|
+
const storeRoot = resolveStoreRoot(rootDir);
|
|
29
|
+
const dbPath = path.join(storeRoot, "state.db");
|
|
30
|
+
if (!existsSync(dbPath)) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
const db = new Database(dbPath, { readonly: true });
|
|
34
|
+
try {
|
|
35
|
+
const version = db.pragma("user_version", { simple: true });
|
|
36
|
+
return version < CURRENT_SCHEMA_VERSION;
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
db.close();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export async function createRunStoreAfterHostEnsure(config, ensureService) {
|
|
43
|
+
if (storeNeedsHostMigration(config.rootDir)) {
|
|
44
|
+
const ensured = await ensureService();
|
|
45
|
+
if (!ensured.ok) {
|
|
46
|
+
return { ok: false, message: ensured.message, reason: ensured.reason };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
return {
|
|
51
|
+
ok: true,
|
|
52
|
+
store: createRunStore({ ...config, openerMode: "assert" }),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
if (err instanceof StoreSchemaError) {
|
|
57
|
+
return { ok: false, message: err.message, code: err.code };
|
|
58
|
+
}
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
13
62
|
/**
|
|
14
63
|
* Composition-root factory: hands back the concrete SQLite connection alongside the `RunStore`,
|
|
15
64
|
* for the one caller wiring a second, colocated store (A2A's tables) into the same `state.db`
|
|
@@ -17,10 +66,20 @@ function resolveKind(kind) {
|
|
|
17
66
|
*/
|
|
18
67
|
export function createRunStoreWithConnection(config) {
|
|
19
68
|
resolveKind(config.kind);
|
|
20
|
-
|
|
21
|
-
|
|
69
|
+
const globalHome = isGlobalStageflowHome(config.rootDir);
|
|
70
|
+
if (globalHome) {
|
|
71
|
+
assertStageflowHomeWritable();
|
|
72
|
+
const home = globalStageflowHome();
|
|
73
|
+
mkdirSync(home, { recursive: true });
|
|
74
|
+
flattenNestedGlobalStore(home);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
migrateLegacyStoreRoot(config.rootDir);
|
|
78
|
+
}
|
|
79
|
+
const storeRoot = resolveStoreRoot(config.rootDir);
|
|
22
80
|
mkdirSync(storeRoot, { recursive: true });
|
|
23
|
-
const
|
|
81
|
+
const openerMode = resolveOpenerMode(config);
|
|
82
|
+
const store = new SqliteRunStore(storeRoot, { openerMode });
|
|
24
83
|
return { store, connection: store.connection };
|
|
25
84
|
}
|
|
26
85
|
/**
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type HostConfigPublicEcho } from "../config/hostConfig.js";
|
|
2
|
+
import { type RunExportPayload } from "./exportRunPayload.js";
|
|
3
|
+
import type { RunStore, StageLogEvent } from "./port.js";
|
|
4
|
+
import { readStageVerificationHistory } from "./verificationHistory.js";
|
|
5
|
+
export declare const DEBUG_BUNDLE_EVENTS_MAX_BYTES: number;
|
|
6
|
+
export declare const DEBUG_BUNDLE_VERIFICATION_MAX_BYTES: number;
|
|
7
|
+
export declare const DEBUG_BUNDLE_STREAM_LOG_MAX_BYTES: number;
|
|
8
|
+
export declare const DEBUG_BUNDLE_DIFF_MAX_BYTES = 262144;
|
|
9
|
+
export declare const DEBUG_BUNDLE_MAX_STAGES = 64;
|
|
10
|
+
export declare const DEBUG_BUNDLE_MAX_ATTEMPTS_PER_STAGE = 32;
|
|
11
|
+
export type DebugCappedMarker = {
|
|
12
|
+
truncated?: boolean;
|
|
13
|
+
original_bytes?: number;
|
|
14
|
+
};
|
|
15
|
+
export type DebugStageEventsSection = DebugCappedMarker & {
|
|
16
|
+
stages: Array<{
|
|
17
|
+
stage_id: string;
|
|
18
|
+
attempts: Array<{
|
|
19
|
+
attempt: number;
|
|
20
|
+
events: StageLogEvent[];
|
|
21
|
+
}>;
|
|
22
|
+
}>;
|
|
23
|
+
};
|
|
24
|
+
export type DebugVerificationSection = DebugCappedMarker & {
|
|
25
|
+
stages: Awaited<ReturnType<typeof readStageVerificationHistory>>[];
|
|
26
|
+
};
|
|
27
|
+
export type DebugStreamLogTail = DebugCappedMarker & {
|
|
28
|
+
stage_id: string;
|
|
29
|
+
attempt: number;
|
|
30
|
+
text: string;
|
|
31
|
+
};
|
|
32
|
+
export type DebugStreamLogsSection = DebugCappedMarker & {
|
|
33
|
+
tails: DebugStreamLogTail[];
|
|
34
|
+
};
|
|
35
|
+
export type DebugDiffSection = ({
|
|
36
|
+
available: true;
|
|
37
|
+
mode: "stat" | "patch";
|
|
38
|
+
base_sha: string;
|
|
39
|
+
base_source: "resolved_sha" | "HEAD";
|
|
40
|
+
content: string;
|
|
41
|
+
untracked: string[];
|
|
42
|
+
} & DebugCappedMarker) | {
|
|
43
|
+
available: false;
|
|
44
|
+
code?: string;
|
|
45
|
+
error?: string;
|
|
46
|
+
};
|
|
47
|
+
export type DebugBundle = RunExportPayload & {
|
|
48
|
+
stage_events: DebugStageEventsSection;
|
|
49
|
+
verification: DebugVerificationSection;
|
|
50
|
+
diff: DebugDiffSection;
|
|
51
|
+
stream_log_tails: DebugStreamLogsSection;
|
|
52
|
+
host_config: HostConfigPublicEcho;
|
|
53
|
+
};
|
|
54
|
+
export declare function buildDebugBundle(store: RunStore, runId: string, options?: {
|
|
55
|
+
env?: NodeJS.ProcessEnv;
|
|
56
|
+
streamLogMaxBytes?: number;
|
|
57
|
+
eventsMaxBytes?: number;
|
|
58
|
+
verificationMaxBytes?: number;
|
|
59
|
+
diffMaxBytes?: number;
|
|
60
|
+
}): Promise<DebugBundle>;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { loadHostConfig, redactHostConfig, } from "../config/hostConfig.js";
|
|
2
|
+
import { getNamedSecrets } from "../logging/namedSecrets.js";
|
|
3
|
+
import { redact } from "../logging/redact.js";
|
|
4
|
+
import { DEFAULT_DIFF_MAX_BYTES, getRunDiff, } from "../mcp/checkoutTools.js";
|
|
5
|
+
import { readStreamLogTail } from "../mcp/tailStreamLog.js";
|
|
6
|
+
import { buildRunExportPayload, } from "./exportRunPayload.js";
|
|
7
|
+
import { readStageVerificationHistory } from "./verificationHistory.js";
|
|
8
|
+
import { attemptStreamLogPath } from "./workspaceLayout.js";
|
|
9
|
+
export const DEBUG_BUNDLE_EVENTS_MAX_BYTES = 256 * 1024;
|
|
10
|
+
export const DEBUG_BUNDLE_VERIFICATION_MAX_BYTES = 256 * 1024;
|
|
11
|
+
export const DEBUG_BUNDLE_STREAM_LOG_MAX_BYTES = 64 * 1024;
|
|
12
|
+
export const DEBUG_BUNDLE_DIFF_MAX_BYTES = DEFAULT_DIFF_MAX_BYTES;
|
|
13
|
+
export const DEBUG_BUNDLE_MAX_STAGES = 64;
|
|
14
|
+
export const DEBUG_BUNDLE_MAX_ATTEMPTS_PER_STAGE = 32;
|
|
15
|
+
function utf8Bytes(text) {
|
|
16
|
+
return Buffer.byteLength(text, "utf8");
|
|
17
|
+
}
|
|
18
|
+
function takeUtf8Tail(text, maxBytes) {
|
|
19
|
+
const buf = Buffer.from(text, "utf8");
|
|
20
|
+
if (buf.length <= maxBytes)
|
|
21
|
+
return { text };
|
|
22
|
+
return {
|
|
23
|
+
text: buf.subarray(buf.length - maxBytes).toString("utf8"),
|
|
24
|
+
truncated: true,
|
|
25
|
+
original_bytes: buf.length,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function enforceSectionCap(section, maxBytes, shrink) {
|
|
29
|
+
const encoded = JSON.stringify(section);
|
|
30
|
+
const original_bytes = utf8Bytes(encoded);
|
|
31
|
+
if (original_bytes <= maxBytes)
|
|
32
|
+
return section;
|
|
33
|
+
return {
|
|
34
|
+
...shrink(section),
|
|
35
|
+
truncated: true,
|
|
36
|
+
original_bytes,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function emptyStageEvents() {
|
|
40
|
+
return { stages: [] };
|
|
41
|
+
}
|
|
42
|
+
function emptyVerification() {
|
|
43
|
+
return { stages: [] };
|
|
44
|
+
}
|
|
45
|
+
export async function buildDebugBundle(store, runId, options = {}) {
|
|
46
|
+
const detail = await store.readRun(runId);
|
|
47
|
+
const exportPayload = buildRunExportPayload(detail);
|
|
48
|
+
const workspaceDir = store.getWorkspaceDir(runId);
|
|
49
|
+
const streamLogMaxBytes = options.streamLogMaxBytes ?? DEBUG_BUNDLE_STREAM_LOG_MAX_BYTES;
|
|
50
|
+
const eventsMaxBytes = options.eventsMaxBytes ?? DEBUG_BUNDLE_EVENTS_MAX_BYTES;
|
|
51
|
+
const verificationMaxBytes = options.verificationMaxBytes ?? DEBUG_BUNDLE_VERIFICATION_MAX_BYTES;
|
|
52
|
+
const diffMaxBytes = options.diffMaxBytes ?? DEBUG_BUNDLE_DIFF_MAX_BYTES;
|
|
53
|
+
const stageSnapshots = detail.stages.slice(0, DEBUG_BUNDLE_MAX_STAGES);
|
|
54
|
+
const stagesOmitted = detail.stages.length > DEBUG_BUNDLE_MAX_STAGES;
|
|
55
|
+
const stageEventRows = [];
|
|
56
|
+
const verificationRows = [];
|
|
57
|
+
const streamTails = [];
|
|
58
|
+
for (const stage of stageSnapshots) {
|
|
59
|
+
const executions = await store.listStageExecutions(runId, stage.stage_id);
|
|
60
|
+
const attemptNums = executions.length > 0
|
|
61
|
+
? executions.map((e) => e.attempt)
|
|
62
|
+
: stage.attempt_count > 0
|
|
63
|
+
? Array.from({ length: stage.attempt_count }, (_, i) => i + 1)
|
|
64
|
+
: [];
|
|
65
|
+
const cappedAttempts = attemptNums.slice(0, DEBUG_BUNDLE_MAX_ATTEMPTS_PER_STAGE);
|
|
66
|
+
const attemptEvents = [];
|
|
67
|
+
for (const attempt of cappedAttempts) {
|
|
68
|
+
const events = await store.listStageEvents(runId, stage.stage_id, attempt);
|
|
69
|
+
attemptEvents.push({ attempt, events });
|
|
70
|
+
const logPath = attemptStreamLogPath(workspaceDir, stage.stage_id, attempt);
|
|
71
|
+
const tail = await readStreamLogTail(logPath, undefined);
|
|
72
|
+
const capped = takeUtf8Tail(tail.text, streamLogMaxBytes);
|
|
73
|
+
streamTails.push({
|
|
74
|
+
stage_id: stage.stage_id,
|
|
75
|
+
attempt,
|
|
76
|
+
text: capped.text,
|
|
77
|
+
...(capped.truncated
|
|
78
|
+
? {
|
|
79
|
+
truncated: true,
|
|
80
|
+
original_bytes: capped.original_bytes,
|
|
81
|
+
}
|
|
82
|
+
: {}),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (attemptNums.length === 0) {
|
|
86
|
+
const events = await store.listStageEvents(runId, stage.stage_id);
|
|
87
|
+
attemptEvents.push({ attempt: 0, events });
|
|
88
|
+
}
|
|
89
|
+
stageEventRows.push({
|
|
90
|
+
stage_id: stage.stage_id,
|
|
91
|
+
attempts: attemptEvents,
|
|
92
|
+
});
|
|
93
|
+
verificationRows.push(await readStageVerificationHistory(store, runId, stage.stage_id));
|
|
94
|
+
}
|
|
95
|
+
const stage_events = enforceSectionCap({
|
|
96
|
+
stages: stageEventRows,
|
|
97
|
+
...(stagesOmitted ? { truncated: true } : {}),
|
|
98
|
+
}, eventsMaxBytes, () => emptyStageEvents());
|
|
99
|
+
const verification = enforceSectionCap({
|
|
100
|
+
stages: verificationRows,
|
|
101
|
+
...(stagesOmitted ? { truncated: true } : {}),
|
|
102
|
+
}, verificationMaxBytes, () => emptyVerification());
|
|
103
|
+
const stream_log_tails = enforceSectionCap({ tails: streamTails }, Math.max(streamLogMaxBytes, streamLogMaxBytes * Math.max(1, streamTails.length)), (full) => ({
|
|
104
|
+
tails: full.tails.map((t) => ({
|
|
105
|
+
stage_id: t.stage_id,
|
|
106
|
+
attempt: t.attempt,
|
|
107
|
+
text: "",
|
|
108
|
+
truncated: true,
|
|
109
|
+
original_bytes: t.original_bytes ?? utf8Bytes(t.text),
|
|
110
|
+
})),
|
|
111
|
+
}));
|
|
112
|
+
const diffResult = await getRunDiff(store, runId, {
|
|
113
|
+
mode: "patch",
|
|
114
|
+
maxBytes: diffMaxBytes,
|
|
115
|
+
});
|
|
116
|
+
const diff = diffResult.ok
|
|
117
|
+
? {
|
|
118
|
+
available: true,
|
|
119
|
+
mode: diffResult.mode,
|
|
120
|
+
base_sha: diffResult.base_sha,
|
|
121
|
+
base_source: diffResult.base_source,
|
|
122
|
+
content: diffResult.content,
|
|
123
|
+
untracked: diffResult.untracked,
|
|
124
|
+
...(diffResult.truncated ? { truncated: true } : {}),
|
|
125
|
+
}
|
|
126
|
+
: {
|
|
127
|
+
available: false,
|
|
128
|
+
code: diffResult.code,
|
|
129
|
+
error: diffResult.error,
|
|
130
|
+
};
|
|
131
|
+
const host_config = redactHostConfig(loadHostConfig({ env: options.env ?? process.env }));
|
|
132
|
+
const bundle = {
|
|
133
|
+
...exportPayload,
|
|
134
|
+
stage_events,
|
|
135
|
+
verification,
|
|
136
|
+
diff,
|
|
137
|
+
stream_log_tails,
|
|
138
|
+
host_config,
|
|
139
|
+
};
|
|
140
|
+
return redact(bundle, {
|
|
141
|
+
namedSecrets: getNamedSecrets(),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { RunStore } from "./port.js";
|
|
2
|
+
export type DiskBreakdown = {
|
|
3
|
+
runs_bytes: number;
|
|
4
|
+
worktrees_bytes: number;
|
|
5
|
+
repos_bytes: number;
|
|
6
|
+
state_db_bytes: number;
|
|
7
|
+
a2a_artifacts_bytes: number;
|
|
8
|
+
/** Shared dependency cache ($STAGEFLOW_HOME/cache); excluded from slim GC / per-run quotas. */
|
|
9
|
+
cache_bytes: number;
|
|
10
|
+
free_bytes: number;
|
|
11
|
+
};
|
|
12
|
+
export type FilesystemSize = {
|
|
13
|
+
freeBytes: number;
|
|
14
|
+
totalBytes: number;
|
|
15
|
+
};
|
|
16
|
+
export type FreeSpaceReader = (rootPath: string) => Promise<FilesystemSize>;
|
|
17
|
+
export declare const DISK_WARN_LOG_PREFIX = "stageflow.disk_warn";
|
|
18
|
+
export declare function getDiskUsageWalkCallCount(): number;
|
|
19
|
+
export declare function resetDiskUsageWalkCallCount(): void;
|
|
20
|
+
export declare function setFreeSpaceReaderForTests(reader: FreeSpaceReader | null): void;
|
|
21
|
+
/** Walk `rootPath` summing `stat().blocks * 512`. Missing path → 0. */
|
|
22
|
+
export declare function diskUsageOf(rootPath: string): Promise<number>;
|
|
23
|
+
export declare function readFilesystemSize(rootPath: string): Promise<FilesystemSize>;
|
|
24
|
+
/** Category breakdown for the durable root, reusing `diskUsageOf` for directory trees. */
|
|
25
|
+
export declare function durableRootDiskBreakdown(durableRoot: string, options?: {
|
|
26
|
+
freeSpace?: FreeSpaceReader;
|
|
27
|
+
}): Promise<DiskBreakdown>;
|
|
28
|
+
/**
|
|
29
|
+
* Parse `STAGEFLOW_DISK_WARN_BYTES` / floor-style env: integer bytes or `N%` of filesystem.
|
|
30
|
+
* Empty / unset / invalid → undefined (no threshold).
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseDiskBytesThreshold(raw: string | undefined, filesystemTotalBytes: number): number | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Resolve `STAGEFLOW_MIN_FREE_DISK_BYTES`: bytes or `%` of filesystem.
|
|
35
|
+
* Unset / empty / invalid → `max(2 GiB, 10% of filesystemTotalBytes)`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function resolveMinFreeDiskFloor(raw: string | undefined, filesystemTotalBytes: number): number;
|
|
38
|
+
/**
|
|
39
|
+
* One named warning when free space is below `STAGEFLOW_DISK_WARN_BYTES`. Never throws.
|
|
40
|
+
* @returns true when a warning line was emitted
|
|
41
|
+
*/
|
|
42
|
+
export declare function warnDurableRootDiskIfNeeded(durableRoot: string, options?: {
|
|
43
|
+
env?: NodeJS.ProcessEnv;
|
|
44
|
+
freeSpace?: FreeSpaceReader;
|
|
45
|
+
log?: (line: string) => void;
|
|
46
|
+
}): Promise<boolean>;
|
|
47
|
+
/** Measure one run workspace and persist `disk_bytes` / `disk_measured_at`. */
|
|
48
|
+
export declare function refreshRunDiskUsage(store: RunStore, runId: string, options?: {
|
|
49
|
+
now?: Date;
|
|
50
|
+
}): Promise<number>;
|