stageflow 0.23.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -5
- package/dist/a2a/configDiscovery.d.ts +9 -0
- package/dist/a2a/configDiscovery.js +18 -0
- package/dist/a2a/contracts.d.ts +94 -0
- package/dist/a2a/contracts.js +164 -0
- package/dist/a2a/limits.d.ts +14 -0
- package/dist/a2a/limits.js +29 -0
- package/dist/a2a/registry.d.ts +46 -0
- package/dist/a2a/registry.js +178 -0
- package/dist/a2a/server.d.ts +36 -0
- package/dist/a2a/server.js +344 -0
- package/dist/a2a/service.d.ts +97 -0
- package/dist/a2a/service.js +545 -0
- package/dist/a2a/store.d.ts +109 -0
- package/dist/a2a/store.js +202 -0
- package/dist/agent/activity.d.ts +3 -0
- package/dist/agent/bootProviderConfig.d.ts +21 -0
- package/dist/agent/bootProviderConfig.js +74 -0
- package/dist/agent/piAdapter.d.ts +9 -1
- package/dist/agent/piAdapter.js +58 -3
- package/dist/agent/piIsolatedMcp.d.ts +2 -0
- package/dist/agent/piIsolatedMcp.js +10 -2
- package/dist/agent/port.d.ts +6 -0
- package/dist/agent/streamLogRedact.d.ts +1 -1
- package/dist/agent/streamLogRedact.js +4 -11
- package/dist/cli/a2aCommand.d.ts +8 -0
- package/dist/cli/a2aCommand.js +131 -0
- package/dist/cli/artifactCommand.js +9 -16
- package/dist/cli/backupCommand.d.ts +9 -0
- package/dist/cli/backupCommand.js +111 -0
- package/dist/cli/debugRunCommand.d.ts +10 -0
- package/dist/cli/debugRunCommand.js +109 -0
- package/dist/cli/doctorCommand.d.ts +32 -0
- package/dist/cli/doctorCommand.js +453 -0
- package/dist/cli/envelopeCommand.js +9 -2
- package/dist/cli/exportAllCommand.d.ts +55 -0
- package/dist/cli/exportAllCommand.js +218 -0
- package/dist/cli/exportRunCommand.js +23 -21
- package/dist/cli/hostClient.d.ts +21 -3
- package/dist/cli/hostClient.js +93 -12
- package/dist/cli/initTemplates.d.ts +1 -1
- package/dist/cli/initTemplates.js +8 -1
- package/dist/cli/resolveSafeOutPath.d.ts +2 -0
- package/dist/cli/resolveSafeOutPath.js +40 -0
- package/dist/cli/restoreCommand.d.ts +10 -0
- package/dist/cli/restoreCommand.js +90 -0
- package/dist/cli/runCommand.d.ts +3 -2
- package/dist/cli/runCommand.js +93 -11
- package/dist/cli/runOutput.d.ts +4 -0
- package/dist/cli/runOutput.js +33 -7
- package/dist/cli/runStageCommand.d.ts +34 -0
- package/dist/cli/runStageCommand.js +426 -0
- package/dist/cli/runsCommand.d.ts +1 -1
- package/dist/cli/runsCommand.js +162 -8
- package/dist/cli.js +190 -30
- package/dist/config/catalogRelativePath.d.ts +62 -0
- package/dist/config/catalogRelativePath.js +118 -0
- package/dist/config/configOrigin.d.ts +27 -0
- package/dist/config/configOrigin.js +59 -0
- package/dist/config/hostConfig.d.ts +55 -0
- package/dist/config/hostConfig.js +333 -0
- package/dist/config/listSkills.d.ts +26 -0
- package/dist/config/listSkills.js +190 -3
- package/dist/config/loadOutcome.d.ts +1 -1
- package/dist/config/loadPipeline.js +23 -2
- package/dist/config/loadStage.js +18 -0
- package/dist/config/loadTask.d.ts +2 -0
- package/dist/config/loadTask.js +57 -18
- package/dist/config/materializeStageModels.js +5 -1
- package/dist/config/mergePipelineIncludes.d.ts +3 -0
- package/dist/config/mergePipelineIncludes.js +19 -0
- package/dist/config/migrateYaml.js +6 -11
- package/dist/config/multiProjectCatalog.d.ts +30 -0
- package/dist/config/multiProjectCatalog.js +142 -0
- package/dist/config/normalizePipelineStageEntry.js +20 -1
- package/dist/config/pipelineStageKeys.js +2 -0
- package/dist/config/resolveCatalogRoots.d.ts +27 -0
- package/dist/config/resolveCatalogRoots.js +76 -0
- package/dist/config/resolveModel.d.ts +7 -1
- package/dist/config/resolveModel.js +11 -1
- package/dist/config/resolveStageMcpServers.d.ts +9 -1
- package/dist/config/resolveStageMcpServers.js +36 -9
- package/dist/config/secretFromEnvOrFile.d.ts +9 -0
- package/dist/config/secretFromEnvOrFile.js +34 -0
- package/dist/config/seededCatalog.d.ts +11 -0
- package/dist/config/seededCatalog.js +29 -0
- package/dist/config/toolRequires.d.ts +24 -0
- package/dist/config/toolRequires.js +115 -0
- package/dist/config/validateCatalog.d.ts +5 -1
- package/dist/config/validateCatalog.js +140 -1
- package/dist/diagnostics/checks.d.ts +39 -0
- package/dist/diagnostics/checks.js +135 -0
- package/dist/errors/codes.d.ts +14 -0
- package/dist/errors/codes.js +59 -0
- package/dist/git/cache.d.ts +14 -0
- package/dist/git/cache.js +150 -0
- package/dist/git/credentials.d.ts +9 -0
- package/dist/git/credentials.js +88 -0
- package/dist/git/errors.d.ts +20 -0
- package/dist/git/errors.js +94 -0
- package/dist/git/exec.d.ts +16 -0
- package/dist/git/exec.js +135 -0
- package/dist/git/operations.d.ts +29 -0
- package/dist/git/operations.js +153 -0
- package/dist/git/version.d.ts +1 -0
- package/dist/git/version.js +10 -0
- package/dist/logging/audit.d.ts +16 -0
- package/dist/logging/audit.js +19 -0
- package/dist/logging/commonWords.d.ts +2 -0
- package/dist/logging/commonWords.js +13 -0
- package/dist/logging/logger.d.ts +49 -0
- package/dist/logging/logger.js +193 -0
- package/dist/logging/namedSecrets.d.ts +6 -0
- package/dist/logging/namedSecrets.js +58 -0
- package/dist/logging/redact.d.ts +13 -0
- package/dist/logging/redact.js +88 -0
- package/dist/mcp/catalogTools.js +591 -76
- package/dist/mcp/checkoutTools.d.ts +52 -0
- package/dist/mcp/checkoutTools.js +332 -0
- package/dist/mcp/controlTools.js +179 -5
- package/dist/mcp/deps.d.ts +1 -0
- package/dist/mcp/getStartedTool.d.ts +3 -0
- package/dist/mcp/getStartedTool.js +62 -0
- package/dist/mcp/readArtifact.d.ts +11 -0
- package/dist/mcp/readArtifact.js +46 -30
- package/dist/mcp/server.js +1 -0
- package/dist/mcp/tools.js +4 -0
- package/dist/net/proxy.d.ts +14 -0
- package/dist/net/proxy.js +78 -0
- package/dist/package-meta.d.ts +2 -1
- package/dist/package-meta.js +2 -1
- package/dist/preflight/bash.d.ts +8 -0
- package/dist/preflight/bash.js +34 -0
- package/dist/preflight/claudeRoot.d.ts +11 -0
- package/dist/preflight/claudeRoot.js +23 -0
- package/dist/preflight/mcpCommands.d.ts +8 -0
- package/dist/preflight/mcpCommands.js +36 -0
- package/dist/preflight/pipelinePreflight.d.ts +40 -0
- package/dist/preflight/pipelinePreflight.js +192 -0
- package/dist/preflight/tls.d.ts +6 -0
- package/dist/preflight/tls.js +18 -0
- package/dist/preflight/toolchain.d.ts +45 -0
- package/dist/preflight/toolchain.js +219 -0
- package/dist/project/findProjectRoot.js +3 -4
- package/dist/project/globalHome.d.ts +7 -0
- package/dist/project/globalHome.js +63 -2
- package/dist/project/resolveProjectContext.js +2 -2
- package/dist/project/resolveStageflowContext.js +2 -2
- package/dist/projection/projectRun.d.ts +2 -1
- package/dist/projection/projectRun.js +1 -0
- package/dist/runstore/assertTmpdir.d.ts +1 -0
- package/dist/runstore/assertTmpdir.js +21 -0
- package/dist/runstore/backup.d.ts +52 -0
- package/dist/runstore/backup.js +292 -0
- package/dist/runstore/createStore.d.ts +27 -0
- package/dist/runstore/createStore.js +75 -7
- package/dist/runstore/debugBundle.d.ts +60 -0
- package/dist/runstore/debugBundle.js +143 -0
- package/dist/runstore/diskUsage.d.ts +50 -0
- package/dist/runstore/diskUsage.js +214 -0
- package/dist/runstore/exportRunPayload.d.ts +8 -0
- package/dist/runstore/exportRunPayload.js +23 -0
- package/dist/runstore/normalizeCatalogPath.d.ts +1 -0
- package/dist/runstore/normalizeCatalogPath.js +10 -0
- package/dist/runstore/paths.d.ts +5 -0
- package/dist/runstore/paths.js +145 -1
- package/dist/runstore/port.d.ts +119 -8
- package/dist/runstore/port.js +8 -1
- package/dist/runstore/restore.d.ts +81 -0
- package/dist/runstore/restore.js +355 -0
- package/dist/runstore/retention.d.ts +27 -0
- package/dist/runstore/retention.js +74 -0
- package/dist/runstore/runManifest.d.ts +146 -0
- package/dist/runstore/runManifest.js +288 -0
- package/dist/runstore/runProjection.d.ts +3 -1
- package/dist/runstore/runProjection.js +63 -1
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +37 -3
- package/dist/runstore/sqlite/SqliteRunStore.js +401 -227
- package/dist/runstore/sqlite/applyStorePragmas.d.ts +39 -0
- package/dist/runstore/sqlite/applyStorePragmas.js +145 -0
- package/dist/runstore/sqlite/migrations/001-baseline.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/001-baseline.js +10 -0
- package/dist/runstore/sqlite/migrations/002-repository-binding.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/002-repository-binding.js +29 -0
- package/dist/runstore/sqlite/migrations/003-run-lifecycle.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/003-run-lifecycle.js +28 -0
- package/dist/runstore/sqlite/migrations/004-auto-resume-count.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/004-auto-resume-count.js +19 -0
- package/dist/runstore/sqlite/migrations/005-config-origins.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/005-config-origins.js +19 -0
- package/dist/runstore/sqlite/migrations/006-pipeline-body-and-caller.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/006-pipeline-body-and-caller.js +28 -0
- package/dist/runstore/sqlite/migrations/007-projects-registry.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/007-projects-registry.js +31 -0
- package/dist/runstore/sqlite/migrations/index.d.ts +21 -0
- package/dist/runstore/sqlite/migrations/index.js +72 -0
- package/dist/runstore/sqlite/schema.d.ts +20 -1
- package/dist/runstore/sqlite/schema.js +283 -1
- package/dist/runstore/sqlite/storeOpenError.d.ts +6 -0
- package/dist/runstore/sqlite/storeOpenError.js +11 -0
- package/dist/runstore/sqlite/storeSchemaError.d.ts +5 -0
- package/dist/runstore/sqlite/storeSchemaError.js +8 -0
- package/dist/runstore/stageExecution.js +3 -0
- package/dist/runstore/storeFilesystem.d.ts +32 -0
- package/dist/runstore/storeFilesystem.js +148 -0
- package/dist/runstore/submission.d.ts +11 -0
- package/dist/runstore/submission.js +7 -0
- package/dist/runstore/trackProjection.js +2 -0
- package/dist/runstore/workspaceLayout.d.ts +13 -0
- package/dist/runstore/workspaceLayout.js +76 -0
- package/dist/runtime/completionCheckRunner.d.ts +3 -0
- package/dist/runtime/completionCheckRunner.js +45 -7
- package/dist/runtime/containerLimits.d.ts +16 -0
- package/dist/runtime/containerLimits.js +84 -0
- package/dist/runtime/credentialMaterialisation.d.ts +28 -0
- package/dist/runtime/credentialMaterialisation.js +128 -0
- package/dist/runtime/gitCheckoutCapability.js +7 -13
- package/dist/runtime/pipelineRunner.d.ts +38 -1
- package/dist/runtime/pipelineRunner.js +179 -15
- package/dist/runtime/pipelineScheduler.d.ts +15 -0
- package/dist/runtime/pipelineScheduler.js +167 -42
- package/dist/runtime/reloadRunCatalog.js +12 -1
- package/dist/runtime/repositoryMaterialize.d.ts +47 -0
- package/dist/runtime/repositoryMaterialize.js +235 -0
- package/dist/runtime/resolveEnvelopeRef.d.ts +30 -0
- package/dist/runtime/resolveEnvelopeRef.js +79 -0
- package/dist/runtime/resumeTimedOut.d.ts +1 -1
- package/dist/runtime/resumeTimedOut.js +5 -2
- package/dist/runtime/runChangeBus.js +9 -0
- package/dist/runtime/runDeletion.d.ts +7 -0
- package/dist/runtime/runDeletion.js +37 -0
- package/dist/runtime/runManager.d.ts +179 -4
- package/dist/runtime/runManager.js +1644 -83
- package/dist/runtime/runRetentionSweep.d.ts +31 -0
- package/dist/runtime/runRetentionSweep.js +328 -0
- package/dist/runtime/runRetryCoordinator.d.ts +2 -0
- package/dist/runtime/runRetryCoordinator.js +19 -2
- package/dist/runtime/runSkills.d.ts +40 -0
- package/dist/runtime/runSkills.js +173 -0
- package/dist/runtime/stageAttemptBootstrap.d.ts +5 -1
- package/dist/runtime/stageAttemptBootstrap.js +195 -24
- package/dist/runtime/stageCacheEnv.d.ts +6 -0
- package/dist/runtime/stageCacheEnv.js +34 -0
- package/dist/runtime/stageConcurrency.d.ts +2 -1
- package/dist/runtime/stageConcurrency.js +4 -2
- package/dist/runtime/stageEnvironment.d.ts +38 -0
- package/dist/runtime/stageEnvironment.js +193 -0
- package/dist/runtime/stageProcessLauncher.d.ts +18 -0
- package/dist/runtime/stageProcessLauncher.js +194 -38
- package/dist/runtime/stageRecovery.d.ts +3 -1
- package/dist/runtime/stageRecovery.js +19 -4
- package/dist/runtime/stageRoots.d.ts +38 -1
- package/dist/runtime/stageRoots.js +118 -0
- package/dist/runtime/stageRunner.d.ts +1 -0
- package/dist/runtime/stageRunner.js +3 -1
- package/dist/runtime/stageSecretDecl.d.ts +9 -0
- package/dist/runtime/stageSecretDecl.js +102 -0
- package/dist/runtime/stageSecrets.d.ts +40 -0
- package/dist/runtime/stageSecrets.js +181 -0
- package/dist/runtime/stageWorker.d.ts +3 -2
- package/dist/runtime/stageWorker.js +58 -10
- package/dist/runtime/stageWorkerProtocol.d.ts +5 -0
- package/dist/runtime/stageWorkerProtocol.js +8 -0
- package/dist/runtime/startPayload.d.ts +32 -0
- package/dist/runtime/startPayload.js +59 -0
- package/dist/runtime/taskInput.js +14 -5
- package/dist/runtime/verifiedStageExecution.d.ts +1 -0
- package/dist/runtime/verifiedStageExecution.js +2 -1
- package/dist/runtime/workspaceBinding.d.ts +20 -0
- package/dist/runtime/workspaceBinding.js +60 -0
- package/dist/server/allowedHosts.d.ts +19 -0
- package/dist/server/allowedHosts.js +179 -0
- package/dist/server/bootstrap.d.ts +38 -2
- package/dist/server/bootstrap.js +219 -4
- package/dist/server/controlToken.d.ts +47 -0
- package/dist/server/controlToken.js +241 -0
- package/dist/server/createHttpHost.d.ts +11 -2
- package/dist/server/createHttpHost.js +109 -10
- package/dist/server/ensureGlobalService.d.ts +5 -1
- package/dist/server/ensureGlobalService.js +35 -1
- package/dist/server/healthSurfaces.d.ts +6 -0
- package/dist/server/healthSurfaces.js +98 -0
- package/dist/server/http.d.ts +12 -1
- package/dist/server/http.js +1085 -485
- package/dist/server/listenHost.d.ts +5 -0
- package/dist/server/listenHost.js +56 -0
- package/dist/server/mcpHost.d.ts +10 -1
- package/dist/server/mcpHost.js +36 -6
- package/dist/server/operatorResults.d.ts +2 -2
- package/dist/server/operatorResults.js +40 -29
- package/dist/server/requestAuthContext.d.ts +12 -0
- package/dist/server/requestAuthContext.js +21 -0
- package/dist/server/shutdown.d.ts +69 -0
- package/dist/server/shutdown.js +219 -0
- package/dist/types/pipeline.d.ts +7 -0
- package/dist/types/stage.d.ts +8 -0
- package/dist/types/task.d.ts +8 -0
- package/dist/ui/assets/index-B6hlSXXn.js +120 -0
- package/dist/ui/index.html +1 -1
- package/examples/README.md +98 -0
- package/examples/archify-on-pr/README.md +213 -0
- package/examples/archify-on-pr/archify-on-pr.pipeline.yaml +19 -0
- package/examples/archify-on-pr/archify-on-pr.task.yaml +19 -0
- package/examples/archify-on-pr/author-diagrams.yaml +106 -0
- package/examples/archify-on-pr/detect-changes.yaml +89 -0
- package/examples/ci-validate/README.md +35 -0
- package/examples/ci-validate/check.yaml +13 -0
- package/examples/ci-validate/ci-demo.pipeline.yaml +4 -0
- package/examples/ci-validate/validate.sh +5 -0
- package/examples/compliance-review-demo/README.md +125 -0
- package/examples/compliance-review-demo/compliance-review-demo.pipeline.yaml +29 -0
- package/examples/compliance-review-demo/connect-account.yaml +47 -0
- package/examples/compliance-review-demo/map-to-control.yaml +123 -0
- package/examples/compliance-review-demo/poll-bucket-config.yaml +103 -0
- package/examples/compliance-review-demo/publish-report.yaml +79 -0
- package/examples/compliance-review-demo/review-verdict.yaml +122 -0
- package/examples/compliance-review-demo/run-compliance-check.yaml +96 -0
- package/examples/compliance-review-demo/store-evidence.yaml +143 -0
- package/examples/conditional-fork/README.md +89 -0
- package/examples/conditional-fork/branch-a.yaml +13 -0
- package/examples/conditional-fork/branch-b.yaml +13 -0
- package/examples/conditional-fork/decide.yaml +35 -0
- package/examples/conditional-fork/fork-demo.pipeline.yaml +20 -0
- package/examples/conditional-fork/fork-demo.task.yaml +8 -0
- package/examples/context7-mcp/README.md +42 -0
- package/examples/context7-mcp/context7-mcp.pipeline.yaml +15 -0
- package/examples/context7-mcp/context7-mcp.task.yaml +4 -0
- package/examples/context7-mcp/fetch-docs.yaml +52 -0
- package/examples/context7-mcp/resolve-library.yaml +31 -0
- package/examples/context7-mcp/write-brief.yaml +52 -0
- package/examples/feature-loop/README.md +104 -0
- package/examples/feature-loop/address-feedback.yaml +148 -0
- package/examples/feature-loop/align.yaml +116 -0
- package/examples/feature-loop/decompose.yaml +86 -0
- package/examples/feature-loop/default-model-hierarchy.task.yaml +58 -0
- package/examples/feature-loop/feature-loop.pipeline.yaml +140 -0
- package/examples/feature-loop/feature-loop.task.yaml +25 -0
- package/examples/feature-loop/implement.yaml +128 -0
- package/examples/feature-loop/plan.yaml +111 -0
- package/examples/feature-loop/publish.yaml +167 -0
- package/examples/feature-loop/review.yaml +146 -0
- package/examples/feature-loop/verify.yaml +106 -0
- package/examples/feedback-loop/README.md +72 -0
- package/examples/feedback-loop/VERIFY.md +208 -0
- package/examples/feedback-loop/continue-only.task.yaml +4 -0
- package/examples/feedback-loop/exhaust-require-continue.task.yaml +6 -0
- package/examples/feedback-loop/exhaust-wait-human.task.yaml +11 -0
- package/examples/feedback-loop/feedback-loop-wait-human.pipeline.yaml +23 -0
- package/examples/feedback-loop/feedback-loop.pipeline.yaml +23 -0
- package/examples/feedback-loop/feedback-loop.task.yaml +4 -0
- package/examples/feedback-loop/implement.yaml +14 -0
- package/examples/feedback-loop/plan.yaml +14 -0
- package/examples/feedback-loop/review.yaml +15 -0
- package/examples/feedback-loop/send-back-once.task.yaml +6 -0
- package/examples/feedback-loop/submit.yaml +12 -0
- package/examples/generic-fan-in/README.md +151 -0
- package/examples/generic-fan-in/clarify.yaml +18 -0
- package/examples/generic-fan-in/generic-fan-in-accepted.pipeline.yaml +19 -0
- package/examples/generic-fan-in/generic-fan-in-accepted.task.yaml +8 -0
- package/examples/generic-fan-in/generic-fan-in.pipeline.yaml +18 -0
- package/examples/generic-fan-in/generic-fan-in.task.yaml +9 -0
- package/examples/generic-fan-in/research.yaml +45 -0
- package/examples/generic-fan-in/synthesize.yaml +66 -0
- package/examples/generic-fan-in/validation.yaml +45 -0
- package/examples/github-release/README.md +78 -0
- package/examples/github-release/draft-release-notes.yaml +64 -0
- package/examples/github-release/github-release.pipeline.yaml +11 -0
- package/examples/github-release/github-release.task.yaml +13 -0
- package/examples/github-release/publish-github-release.yaml +64 -0
- package/examples/hello-world/README.md +28 -0
- package/examples/hello-world/hello.pipeline.yaml +4 -0
- package/examples/hello-world/my-task.task.yaml +6 -0
- package/examples/hello-world/research.yaml +18 -0
- package/examples/mcp-hitl-tour/README.md +113 -0
- package/examples/mcp-hitl-tour/draft-angle.yaml +33 -0
- package/examples/mcp-hitl-tour/draft-short.yaml +32 -0
- package/examples/mcp-hitl-tour/emit-angles.yaml +45 -0
- package/examples/mcp-hitl-tour/gather.yaml +31 -0
- package/examples/mcp-hitl-tour/intake.yaml +62 -0
- package/examples/mcp-hitl-tour/mcp-hitl-tour-exhaust.task.yaml +13 -0
- package/examples/mcp-hitl-tour/mcp-hitl-tour.pipeline.yaml +54 -0
- package/examples/mcp-hitl-tour/mcp-hitl-tour.task.yaml +16 -0
- package/examples/mcp-hitl-tour/polish.yaml +33 -0
- package/examples/mcp-hitl-tour/review.yaml +17 -0
- package/examples/mcp-hitl-tour/ship.yaml +25 -0
- package/examples/oss-issue-contribution/README.md +218 -0
- package/examples/oss-issue-contribution/oss-address-review-feedback.yaml +117 -0
- package/examples/oss-issue-contribution/oss-approve-contribution.yaml +181 -0
- package/examples/oss-issue-contribution/oss-approve-plan.yaml +111 -0
- package/examples/oss-issue-contribution/oss-explain-fix.yaml +108 -0
- package/examples/oss-issue-contribution/oss-explain-issue.yaml +114 -0
- package/examples/oss-issue-contribution/oss-implement-source-fix.yaml +171 -0
- package/examples/oss-issue-contribution/oss-investigate-area.yaml +116 -0
- package/examples/oss-issue-contribution/oss-issue-contribution.pipeline.yaml +125 -0
- package/examples/oss-issue-contribution/oss-issue-intake.yaml +106 -0
- package/examples/oss-issue-contribution/oss-plan-investigation.yaml +112 -0
- package/examples/oss-issue-contribution/oss-plan-review.yaml +109 -0
- package/examples/oss-issue-contribution/oss-reproduce-issue.yaml +140 -0
- package/examples/oss-issue-contribution/oss-review-change.yaml +107 -0
- package/examples/oss-issue-contribution/oss-verify-fix.yaml +130 -0
- package/examples/oss-issue-contribution/oss-write-regression-test.yaml +135 -0
- package/examples/oss-issue-contribution/pi-7629.task.yaml +79 -0
- package/examples/oss-issue-contribution/pi-8919.task.yaml +75 -0
- package/examples/oss-issue-contribution/pydantic-ai-7971.task.yaml +53 -0
- package/examples/oss-quick-fix/README.md +65 -0
- package/examples/oss-quick-fix/implement-fix.yaml +84 -0
- package/examples/oss-quick-fix/oss-quick-fix.pipeline.yaml +35 -0
- package/examples/oss-quick-fix/oss-quick-fix.task.yaml +19 -0
- package/examples/oss-quick-fix/ship-fix.yaml +81 -0
- package/examples/oss-quick-fix/understand-issue.yaml +67 -0
- package/examples/oss-quick-fix/verify-fix.yaml +73 -0
- package/examples/plan-review/README.md +37 -0
- package/examples/plan-review/plan-review-followup.yaml +13 -0
- package/examples/plan-review/plan-review.pipeline.yaml +9 -0
- package/examples/plan-review/plan-review.task.yaml +9 -0
- package/examples/plan-review/plan-review.yaml +25 -0
- package/examples/playwright-mcp/README.md +39 -0
- package/examples/playwright-mcp/playwright-mcp.pipeline.yaml +5 -0
- package/examples/playwright-mcp/playwright-mcp.task.yaml +4 -0
- package/examples/playwright-mcp/screenshot.yaml +40 -0
- package/examples/retry-tour/README.md +75 -0
- package/examples/retry-tour/retry-tour.pipeline.yaml +122 -0
- package/examples/retry-tour/retry-tour.task.yaml +9 -0
- package/examples/route-if-tour/README.md +202 -0
- package/examples/route-if-tour/route-if-tour.pipeline.yaml +950 -0
- package/examples/route-if-tour/route-if-tour.task.yaml +4 -0
- package/examples/route-wiring-smoke-test/01-core-routing.pipeline.yaml +168 -0
- package/examples/route-wiring-smoke-test/02-on-gating.pipeline.yaml +44 -0
- package/examples/route-wiring-smoke-test/03-two-entry-points.pipeline.yaml +28 -0
- package/examples/route-wiring-smoke-test/04-entry-false-equivalent-to-omitted.pipeline.yaml +27 -0
- package/examples/route-wiring-smoke-test/05-fork-choice.pipeline.yaml +110 -0
- package/examples/route-wiring-smoke-test/06-fan-out-fan-in.pipeline.yaml +82 -0
- package/examples/route-wiring-smoke-test/07-loop-human-decision.pipeline.yaml +82 -0
- package/examples/route-wiring-smoke-test/08-skip-fallback-and-multi-on-gate.pipeline.yaml +77 -0
- package/examples/route-wiring-smoke-test/09-multi-loop-targets.pipeline.yaml +102 -0
- package/examples/route-wiring-smoke-test/10-uses-dialect-fork.pipeline.yaml +23 -0
- package/examples/route-wiring-smoke-test/11-sequential-io-handoff.pipeline.yaml +37 -0
- package/examples/route-wiring-smoke-test/12-complex-io-schemas.pipeline.yaml +138 -0
- package/examples/route-wiring-smoke-test/13-ref-io-handoff.pipeline.yaml +84 -0
- package/examples/route-wiring-smoke-test/14-if-eq-gating.pipeline.yaml +50 -0
- package/examples/route-wiring-smoke-test/15-if-all-gated.pipeline.yaml +37 -0
- package/examples/route-wiring-smoke-test/16-if-composition.pipeline.yaml +65 -0
- package/examples/route-wiring-smoke-test/17-if-join.pipeline.yaml +80 -0
- package/examples/route-wiring-smoke-test/README.md +377 -0
- package/examples/route-wiring-smoke-test/rejected/01-reject-legacy-needs.pipeline.yaml +26 -0
- package/examples/route-wiring-smoke-test/rejected/02-reject-legacy-fork.pipeline.yaml +25 -0
- package/examples/route-wiring-smoke-test/rejected/03-reject-legacy-feedback-loop.pipeline.yaml +30 -0
- package/examples/route-wiring-smoke-test/rejected/04-reject-missing-entry.pipeline.yaml +24 -0
- package/examples/route-wiring-smoke-test/rejected/05-reject-unreachable.pipeline.yaml +35 -0
- package/examples/route-wiring-smoke-test/rejected/06-reject-allow-none-without-route-select.pipeline.yaml +37 -0
- package/examples/route-wiring-smoke-test/rejected/07-reject-cycle.pipeline.yaml +27 -0
- package/examples/route-wiring-smoke-test/rejected/08-reject-loop-non-ancestor.pipeline.yaml +42 -0
- package/examples/route-wiring-smoke-test/rejected/09-reject-route-select-leaf.pipeline.yaml +26 -0
- package/examples/route-wiring-smoke-test/rejected/12-reject-loop-replay-unsafe.pipeline.yaml +55 -0
- package/examples/route-wiring-smoke-test/rejected/13-reject-multiple-loop-entries.pipeline.yaml +51 -0
- package/examples/route-wiring-smoke-test/rejected/14-reject-duplicate-stage-id.pipeline.yaml +35 -0
- package/examples/route-wiring-smoke-test/rejected/15-reject-unknown-route-target.pipeline.yaml +15 -0
- package/examples/route-wiring-smoke-test/rejected/16-reject-malformed-route-item.pipeline.yaml +15 -0
- package/examples/route-wiring-smoke-test/rejected/17-reject-io-incompatible.pipeline.yaml +33 -0
- package/examples/route-wiring-smoke-test/rejected/18-reject-nested-io.pipeline.yaml +51 -0
- package/examples/route-wiring-smoke-test/rejected/19-reject-array-item-io.pipeline.yaml +55 -0
- package/examples/route-wiring-smoke-test/rejected/20-reject-closed-io.pipeline.yaml +36 -0
- package/examples/route-wiring-smoke-test/rejected/21-reject-ref-io.pipeline.yaml +42 -0
- package/examples/route-wiring-smoke-test/rejected/22-reject-if-unknown-field.pipeline.yaml +33 -0
- package/examples/route-wiring-smoke-test/rejected/23-reject-if-optional-field.pipeline.yaml +32 -0
- package/examples/route-wiring-smoke-test/rejected/24-reject-if-empty-all.pipeline.yaml +31 -0
- package/examples/route-wiring-smoke-test/rejected/25-reject-if-gt-on-string.pipeline.yaml +33 -0
- package/examples/route-wiring-smoke-test/rejected/26-reject-if-optional-nested.pipeline.yaml +36 -0
- package/examples/route-wiring-smoke-test/rejected/27-reject-if-on-loop.pipeline.yaml +63 -0
- package/examples/route-wiring-smoke-test/rejected/29-reject-if-on-failed.pipeline.yaml +46 -0
- package/examples/route-wiring-smoke-test/smoke-test.task.yaml +4 -0
- package/examples/route-wiring-smoke-test/stages/branch-a.yaml +12 -0
- package/examples/route-wiring-smoke-test/stages/branch-b.yaml +12 -0
- package/examples/route-wiring-smoke-test/stages/decide.yaml +12 -0
- package/examples/route-wiring-smoke-test/stages/finish.yaml +12 -0
- package/examples/ship-feature/README.md +190 -0
- package/examples/ship-feature/feat-address-feedback.yaml +119 -0
- package/examples/ship-feature/feat-implement.yaml +180 -0
- package/examples/ship-feature/feat-plan.yaml +120 -0
- package/examples/ship-feature/feat-review.yaml +109 -0
- package/examples/ship-feature/feat-ship.yaml +203 -0
- package/examples/ship-feature/ship-feature.pipeline.yaml +44 -0
- package/examples/ship-feature/ship-feature.task.yaml +78 -0
- package/examples/stage-mcp/README.md +144 -0
- package/examples/stage-mcp/echo-mcp.mjs +20 -0
- package/examples/stage-mcp/stage-mcp-dead.pipeline.yaml +5 -0
- package/examples/stage-mcp/stage-mcp-dead.task.yaml +3 -0
- package/examples/stage-mcp/stage-mcp-missing-var.pipeline.yaml +5 -0
- package/examples/stage-mcp/stage-mcp-missing-var.task.yaml +3 -0
- package/examples/stage-mcp/stage-mcp.pipeline.yaml +5 -0
- package/examples/stage-mcp/stage-mcp.task.yaml +4 -0
- package/examples/stage-mcp/use-echo.yaml +20 -0
- package/examples/verify-tour/README.md +80 -0
- package/examples/verify-tour/verify-tour.pipeline.yaml +162 -0
- package/examples/verify-tour/verify-tour.task.yaml +11 -0
- package/package.json +6 -1
- package/skills/stageflow/SKILL.md +6 -6
- package/skills/stageflow/references/control-surface.md +11 -5
- package/skills/stageflow/scripts/detect-host.mjs +14 -1
- package/skills/stageflow/scripts/detect-host.test.mjs +29 -1
- package/skills/stageflow-author/SKILL.md +4 -3
- package/skills/stageflow-author/references/catalog-mapping.md +16 -0
- package/skills/stageflow-author/references/catalog-write-conventions.md +1 -1
- package/skills/stageflow-run/SKILL.md +20 -12
- package/skills/stageflow-run/references/mcp-call.md +9 -1
- package/skills/stageflow-run/references/task-and-pipeline-selection.md +16 -5
- package/skills/stageflow-run/scripts/mcp-call.mjs +46 -6
- package/skills/stageflow-run/scripts/mcp-call.test.mjs +64 -0
- package/skills/stageflow-session-capture/references/catalog-authoring.md +2 -0
- package/skills/stageflow-setup/SKILL.md +40 -8
- package/dist/ui/assets/index-Da5EHU6M.js +0 -119
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { GitError } from "./errors.js";
|
|
2
|
+
import { runGit, runGitSync } from "./exec.js";
|
|
3
|
+
const TIMEOUT = {
|
|
4
|
+
resolve: 30_000,
|
|
5
|
+
worktree: 120_000,
|
|
6
|
+
fetch: 300_000,
|
|
7
|
+
clone: 900_000,
|
|
8
|
+
default: 30_000,
|
|
9
|
+
};
|
|
10
|
+
const FULL_SHA_RE = /^[0-9a-f]{40}$/i;
|
|
11
|
+
function baseOpts(cwd, args, timeoutMs, opts) {
|
|
12
|
+
return {
|
|
13
|
+
cwd,
|
|
14
|
+
args,
|
|
15
|
+
timeoutMs,
|
|
16
|
+
env: opts?.env,
|
|
17
|
+
gitBin: opts?.gitBin,
|
|
18
|
+
signal: opts?.signal,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export const BARE_ORIGIN_FETCH_HEADS = "+refs/heads/*:refs/remotes/origin/*";
|
|
22
|
+
export const BARE_ORIGIN_FETCH_TAGS = "+refs/tags/*:refs/tags/*";
|
|
23
|
+
export async function configureBareOriginFetch(repoDir, opts) {
|
|
24
|
+
try {
|
|
25
|
+
await runGit(baseOpts(repoDir, ["config", "--unset", "remote.origin.mirror"], TIMEOUT.default, opts));
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// unset fails when the key is absent
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
await runGit(baseOpts(repoDir, ["config", "--unset-all", "remote.origin.fetch"], TIMEOUT.default, opts));
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// unset-all fails when the key is absent
|
|
35
|
+
}
|
|
36
|
+
await runGit(baseOpts(repoDir, ["config", "--add", "remote.origin.fetch", BARE_ORIGIN_FETCH_HEADS], TIMEOUT.default, opts));
|
|
37
|
+
await runGit(baseOpts(repoDir, ["config", "--add", "remote.origin.fetch", BARE_ORIGIN_FETCH_TAGS], TIMEOUT.default, opts));
|
|
38
|
+
}
|
|
39
|
+
export async function pruneNonStageflowLocalHeads(repoDir, opts) {
|
|
40
|
+
const listed = await runGit(baseOpts(repoDir, ["for-each-ref", "--format=%(refname)", "refs/heads/"], TIMEOUT.default, opts));
|
|
41
|
+
const refs = listed.stdout
|
|
42
|
+
.split("\n")
|
|
43
|
+
.map((line) => line.trim())
|
|
44
|
+
.filter((line) => line.length > 0);
|
|
45
|
+
for (const ref of refs) {
|
|
46
|
+
if (ref === "refs/heads/stageflow" || ref.startsWith("refs/heads/stageflow/")) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
await runGit(baseOpts(repoDir, ["update-ref", "-d", ref], TIMEOUT.default, opts));
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// best-effort prune under heal lock
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export async function cloneBare(url, dest, opts) {
|
|
58
|
+
await runGit(baseOpts(undefined, ["clone", "--bare", url, dest], TIMEOUT.clone, opts));
|
|
59
|
+
await configureBareOriginFetch(dest, opts);
|
|
60
|
+
}
|
|
61
|
+
export async function fetch(repoDir, refspecs = [], opts) {
|
|
62
|
+
await runGit(baseOpts(repoDir, ["fetch", "--prune", "origin", ...refspecs], TIMEOUT.fetch, opts));
|
|
63
|
+
}
|
|
64
|
+
export async function remoteUpdate(repoDir, opts) {
|
|
65
|
+
await runGit(baseOpts(repoDir, ["remote", "update", "--prune"], TIMEOUT.fetch, opts));
|
|
66
|
+
}
|
|
67
|
+
async function revParseCommit(repoDir, ref, opts) {
|
|
68
|
+
const result = await runGit(baseOpts(repoDir, ["rev-parse", "--verify", "--end-of-options", `${ref}^{commit}`], TIMEOUT.resolve, opts));
|
|
69
|
+
return result.stdout.trim();
|
|
70
|
+
}
|
|
71
|
+
async function tryRevParseCommit(repoDir, ref, opts) {
|
|
72
|
+
try {
|
|
73
|
+
return await revParseCommit(repoDir, ref, opts);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
if (err instanceof GitError && err.code === "ref_not_found") {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export async function resolveRef(repoDir, ref, opts) {
|
|
83
|
+
if (FULL_SHA_RE.test(ref) || ref.startsWith("refs/")) {
|
|
84
|
+
return revParseCommit(repoDir, ref, opts);
|
|
85
|
+
}
|
|
86
|
+
const candidates = [
|
|
87
|
+
`refs/remotes/origin/${ref}`,
|
|
88
|
+
`origin/${ref}`,
|
|
89
|
+
`refs/tags/${ref}`,
|
|
90
|
+
];
|
|
91
|
+
for (const candidate of candidates) {
|
|
92
|
+
const sha = await tryRevParseCommit(repoDir, candidate, opts);
|
|
93
|
+
if (sha !== null)
|
|
94
|
+
return sha;
|
|
95
|
+
}
|
|
96
|
+
return revParseCommit(repoDir, candidates[0], opts);
|
|
97
|
+
}
|
|
98
|
+
export async function catFileCommit(repoDir, sha, opts) {
|
|
99
|
+
try {
|
|
100
|
+
await runGit(baseOpts(repoDir, ["cat-file", "-e", `${sha}^{commit}`], TIMEOUT.resolve, opts));
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export async function worktreeAdd(repoDir, input, opts) {
|
|
108
|
+
await runGit(baseOpts(repoDir, ["worktree", "add", "-b", input.branch, input.worktreePath, input.startPoint], TIMEOUT.worktree, opts));
|
|
109
|
+
}
|
|
110
|
+
export async function worktreeRemove(repoDir, worktreePath, opts) {
|
|
111
|
+
await runGit(baseOpts(repoDir, ["worktree", "remove", "--force", worktreePath], TIMEOUT.worktree, opts));
|
|
112
|
+
}
|
|
113
|
+
export async function worktreePrune(repoDir, opts) {
|
|
114
|
+
await runGit(baseOpts(repoDir, ["worktree", "prune"], TIMEOUT.worktree, opts));
|
|
115
|
+
}
|
|
116
|
+
export async function deleteBranch(repoDir, branch, opts) {
|
|
117
|
+
await runGit(baseOpts(repoDir, ["branch", "-D", branch], TIMEOUT.default, opts));
|
|
118
|
+
}
|
|
119
|
+
export async function revParse(repoDir, args, opts) {
|
|
120
|
+
const result = await runGit(baseOpts(repoDir, ["rev-parse", ...args], TIMEOUT.resolve, opts));
|
|
121
|
+
return result.stdout.trim();
|
|
122
|
+
}
|
|
123
|
+
export async function diff(repoDir, args = [], opts) {
|
|
124
|
+
const result = await runGit(baseOpts(repoDir, ["diff", ...args], TIMEOUT.default, opts));
|
|
125
|
+
return result.stdout;
|
|
126
|
+
}
|
|
127
|
+
export function statusPorcelain(repoDir, pathspec, opts) {
|
|
128
|
+
const args = ["status", "--porcelain"];
|
|
129
|
+
if (pathspec !== undefined) {
|
|
130
|
+
args.push("--", pathspec);
|
|
131
|
+
}
|
|
132
|
+
return runGitSync(baseOpts(repoDir, args, TIMEOUT.default, opts)).stdout;
|
|
133
|
+
}
|
|
134
|
+
export async function statusPorcelainZ(repoDir, opts) {
|
|
135
|
+
const result = await runGit(baseOpts(repoDir, ["status", "--porcelain=v1", "-z", "--untracked-files=all"], TIMEOUT.default, opts));
|
|
136
|
+
return result.stdout;
|
|
137
|
+
}
|
|
138
|
+
export async function lsFilesOthersZ(repoDir, opts) {
|
|
139
|
+
const result = await runGit(baseOpts(repoDir, ["ls-files", "--others", "--exclude-standard", "-z"], TIMEOUT.default, opts));
|
|
140
|
+
return result.stdout;
|
|
141
|
+
}
|
|
142
|
+
export async function checkRefFormat(branchName, opts) {
|
|
143
|
+
try {
|
|
144
|
+
await runGit(baseOpts(undefined, ["check-ref-format", "--branch", branchName], TIMEOUT.resolve, opts));
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
export function revParseSync(cwd, args, opts) {
|
|
152
|
+
return runGitSync(baseOpts(cwd, ["rev-parse", ...args], TIMEOUT.resolve, opts)).stdout.trim();
|
|
153
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function gitVersion(gitBin?: string): Promise<string>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { runGit } from "./exec.js";
|
|
2
|
+
export async function gitVersion(gitBin = "git") {
|
|
3
|
+
const result = await runGit({
|
|
4
|
+
gitBin,
|
|
5
|
+
args: ["version"],
|
|
6
|
+
timeoutMs: 5_000,
|
|
7
|
+
});
|
|
8
|
+
const match = result.stdout.match(/git version\s+(\S+)/i);
|
|
9
|
+
return match?.[1] ?? result.stdout.trim();
|
|
10
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Logger } from "./logger.js";
|
|
2
|
+
import type { CallerSurface } from "../server/requestAuthContext.js";
|
|
3
|
+
export type AuditOutcome = "ok" | "error";
|
|
4
|
+
export type AuditRecord = {
|
|
5
|
+
caller_id: string | null;
|
|
6
|
+
surface: CallerSurface;
|
|
7
|
+
action: string;
|
|
8
|
+
target_run_id?: string;
|
|
9
|
+
outcome: AuditOutcome;
|
|
10
|
+
error_code?: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Append-only mutating-call audit on the Slot 4 JSON logger.
|
|
14
|
+
* Allowlisted fields only — never Authorization, pipeline_body, or skills.
|
|
15
|
+
*/
|
|
16
|
+
export declare function writeAudit(log: Logger, record: AuditRecord): void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Append-only mutating-call audit on the Slot 4 JSON logger.
|
|
3
|
+
* Allowlisted fields only — never Authorization, pipeline_body, or skills.
|
|
4
|
+
*/
|
|
5
|
+
export function writeAudit(log, record) {
|
|
6
|
+
const fields = {
|
|
7
|
+
caller_id: record.caller_id,
|
|
8
|
+
surface: record.surface,
|
|
9
|
+
action: record.action,
|
|
10
|
+
outcome: record.outcome,
|
|
11
|
+
};
|
|
12
|
+
if (record.target_run_id !== undefined) {
|
|
13
|
+
fields.target_run_id = record.target_run_id;
|
|
14
|
+
}
|
|
15
|
+
if (record.error_code !== undefined) {
|
|
16
|
+
fields.error_code = record.error_code;
|
|
17
|
+
}
|
|
18
|
+
log.info("audit", `${record.action} ${record.outcome}`, fields);
|
|
19
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Common words that must not be value-redacted even if length ≥ 8. */
|
|
2
|
+
export const COMMON_WORDS = new Set([
|
|
3
|
+
"password",
|
|
4
|
+
"username",
|
|
5
|
+
"localhost",
|
|
6
|
+
"127.0.0.1",
|
|
7
|
+
"true",
|
|
8
|
+
"false",
|
|
9
|
+
"undefined",
|
|
10
|
+
"development",
|
|
11
|
+
"production",
|
|
12
|
+
"staging",
|
|
13
|
+
]);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
2
|
+
export type LogFormat = "json" | "pretty";
|
|
3
|
+
export type LoggerBindings = {
|
|
4
|
+
component?: string;
|
|
5
|
+
run_id?: string;
|
|
6
|
+
stage_id?: string;
|
|
7
|
+
attempt?: number;
|
|
8
|
+
};
|
|
9
|
+
export type LogFields = Record<string, unknown>;
|
|
10
|
+
export type LogRecord = {
|
|
11
|
+
ts: string;
|
|
12
|
+
level: LogLevel;
|
|
13
|
+
event: string;
|
|
14
|
+
msg: string;
|
|
15
|
+
component?: string;
|
|
16
|
+
run_id?: string;
|
|
17
|
+
stage_id?: string;
|
|
18
|
+
attempt?: number;
|
|
19
|
+
truncated?: boolean;
|
|
20
|
+
original_bytes?: number;
|
|
21
|
+
} & LogFields;
|
|
22
|
+
export type Logger = {
|
|
23
|
+
debug(event: string, msg: string, fields?: LogFields): void;
|
|
24
|
+
info(event: string, msg: string, fields?: LogFields): void;
|
|
25
|
+
warn(event: string, msg: string, fields?: LogFields): void;
|
|
26
|
+
error(event: string, msg: string, fields?: LogFields): void;
|
|
27
|
+
child(bindings: LoggerBindings): Logger;
|
|
28
|
+
};
|
|
29
|
+
export declare const LOG_FORMAT_ENV = "STAGEFLOW_LOG_FORMAT";
|
|
30
|
+
export declare const LOG_LEVEL_ENV = "STAGEFLOW_LOG_LEVEL";
|
|
31
|
+
export declare const LOG_MAX_LINE_BYTES_ENV = "STAGEFLOW_LOG_MAX_LINE_BYTES";
|
|
32
|
+
export declare const DEFAULT_LOG_LEVEL: LogLevel;
|
|
33
|
+
export declare const DEFAULT_LOG_MAX_LINE_BYTES = 8192;
|
|
34
|
+
export type CreateLoggerOptions = {
|
|
35
|
+
env?: Record<string, string | undefined>;
|
|
36
|
+
stdoutIsTTY?: boolean;
|
|
37
|
+
format?: LogFormat;
|
|
38
|
+
level?: LogLevel;
|
|
39
|
+
maxLineBytes?: number;
|
|
40
|
+
knownSecrets?: readonly string[];
|
|
41
|
+
bindings?: LoggerBindings;
|
|
42
|
+
write?: (line: string) => void;
|
|
43
|
+
now?: () => Date;
|
|
44
|
+
};
|
|
45
|
+
export declare function resolveLogFormat(env?: Record<string, string | undefined>, stdoutIsTTY?: boolean): LogFormat;
|
|
46
|
+
export declare function resolveLogLevel(env?: Record<string, string | undefined>): LogLevel;
|
|
47
|
+
export declare function resolveLogMaxLineBytes(env?: Record<string, string | undefined>): number;
|
|
48
|
+
export declare function createLogger(options?: CreateLoggerOptions): Logger;
|
|
49
|
+
export declare const logger: Logger;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { redact } from "./redact.js";
|
|
2
|
+
import { getNamedSecrets } from "./namedSecrets.js";
|
|
3
|
+
export const LOG_FORMAT_ENV = "STAGEFLOW_LOG_FORMAT";
|
|
4
|
+
export const LOG_LEVEL_ENV = "STAGEFLOW_LOG_LEVEL";
|
|
5
|
+
export const LOG_MAX_LINE_BYTES_ENV = "STAGEFLOW_LOG_MAX_LINE_BYTES";
|
|
6
|
+
export const DEFAULT_LOG_LEVEL = "info";
|
|
7
|
+
export const DEFAULT_LOG_MAX_LINE_BYTES = 8192;
|
|
8
|
+
const LEVEL_RANK = {
|
|
9
|
+
debug: 10,
|
|
10
|
+
info: 20,
|
|
11
|
+
warn: 30,
|
|
12
|
+
error: 40,
|
|
13
|
+
};
|
|
14
|
+
export function resolveLogFormat(env = process.env, stdoutIsTTY = Boolean(process.stdout.isTTY)) {
|
|
15
|
+
const raw = env[LOG_FORMAT_ENV]?.trim().toLowerCase();
|
|
16
|
+
if (raw === "json" || raw === "pretty")
|
|
17
|
+
return raw;
|
|
18
|
+
return stdoutIsTTY ? "pretty" : "json";
|
|
19
|
+
}
|
|
20
|
+
export function resolveLogLevel(env = process.env) {
|
|
21
|
+
const raw = env[LOG_LEVEL_ENV]?.trim().toLowerCase();
|
|
22
|
+
if (raw === "debug" || raw === "info" || raw === "warn" || raw === "error") {
|
|
23
|
+
return raw;
|
|
24
|
+
}
|
|
25
|
+
return DEFAULT_LOG_LEVEL;
|
|
26
|
+
}
|
|
27
|
+
export function resolveLogMaxLineBytes(env = process.env) {
|
|
28
|
+
const raw = env[LOG_MAX_LINE_BYTES_ENV];
|
|
29
|
+
if (raw === undefined || raw.trim() === "")
|
|
30
|
+
return DEFAULT_LOG_MAX_LINE_BYTES;
|
|
31
|
+
const n = Number.parseInt(raw, 10);
|
|
32
|
+
if (!Number.isFinite(n) || n < 64)
|
|
33
|
+
return DEFAULT_LOG_MAX_LINE_BYTES;
|
|
34
|
+
return n;
|
|
35
|
+
}
|
|
36
|
+
function formatPretty(record) {
|
|
37
|
+
const parts = [
|
|
38
|
+
record.ts,
|
|
39
|
+
record.level.toUpperCase(),
|
|
40
|
+
record.component !== undefined ? `[${record.component}]` : undefined,
|
|
41
|
+
`${record.event}:`,
|
|
42
|
+
record.msg,
|
|
43
|
+
].filter((part) => part !== undefined && part !== "");
|
|
44
|
+
const correlation = [];
|
|
45
|
+
if (record.run_id !== undefined)
|
|
46
|
+
correlation.push(`run_id=${record.run_id}`);
|
|
47
|
+
if (record.stage_id !== undefined) {
|
|
48
|
+
correlation.push(`stage_id=${record.stage_id}`);
|
|
49
|
+
}
|
|
50
|
+
if (record.attempt !== undefined) {
|
|
51
|
+
correlation.push(`attempt=${record.attempt}`);
|
|
52
|
+
}
|
|
53
|
+
if (record.truncated === true)
|
|
54
|
+
correlation.push("truncated=true");
|
|
55
|
+
const reserved = new Set([
|
|
56
|
+
"ts",
|
|
57
|
+
"level",
|
|
58
|
+
"event",
|
|
59
|
+
"msg",
|
|
60
|
+
"component",
|
|
61
|
+
"run_id",
|
|
62
|
+
"stage_id",
|
|
63
|
+
"attempt",
|
|
64
|
+
"truncated",
|
|
65
|
+
"original_bytes",
|
|
66
|
+
]);
|
|
67
|
+
for (const [key, value] of Object.entries(record)) {
|
|
68
|
+
if (reserved.has(key))
|
|
69
|
+
continue;
|
|
70
|
+
if (value === undefined)
|
|
71
|
+
continue;
|
|
72
|
+
correlation.push(`${key}=${typeof value === "string" ? value : JSON.stringify(value)}`);
|
|
73
|
+
}
|
|
74
|
+
return correlation.length > 0
|
|
75
|
+
? `${parts.join(" ")} ${correlation.join(" ")}`
|
|
76
|
+
: parts.join(" ");
|
|
77
|
+
}
|
|
78
|
+
function serializeRecord(record, format, maxLineBytes) {
|
|
79
|
+
if (format === "pretty") {
|
|
80
|
+
let line = formatPretty(record);
|
|
81
|
+
const bytes = Buffer.byteLength(line, "utf8");
|
|
82
|
+
if (bytes <= maxLineBytes)
|
|
83
|
+
return line;
|
|
84
|
+
const overhead = bytes - Buffer.byteLength(record.msg, "utf8");
|
|
85
|
+
const msgBudget = Math.max(0, maxLineBytes - overhead - 1);
|
|
86
|
+
const truncatedMsg = Buffer.from(record.msg, "utf8")
|
|
87
|
+
.subarray(0, msgBudget)
|
|
88
|
+
.toString("utf8");
|
|
89
|
+
return formatPretty({
|
|
90
|
+
...record,
|
|
91
|
+
msg: truncatedMsg,
|
|
92
|
+
truncated: true,
|
|
93
|
+
original_bytes: bytes,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
let payload = { ...record };
|
|
97
|
+
let line = JSON.stringify(payload);
|
|
98
|
+
let bytes = Buffer.byteLength(line, "utf8");
|
|
99
|
+
if (bytes <= maxLineBytes)
|
|
100
|
+
return line;
|
|
101
|
+
const originalBytes = bytes;
|
|
102
|
+
let lo = 0;
|
|
103
|
+
let hi = Buffer.byteLength(record.msg, "utf8");
|
|
104
|
+
let bestMsg = "";
|
|
105
|
+
while (lo <= hi) {
|
|
106
|
+
const mid = Math.floor((lo + hi) / 2);
|
|
107
|
+
const candidateMsg = Buffer.from(record.msg, "utf8")
|
|
108
|
+
.subarray(0, mid)
|
|
109
|
+
.toString("utf8");
|
|
110
|
+
payload = {
|
|
111
|
+
...record,
|
|
112
|
+
msg: candidateMsg,
|
|
113
|
+
truncated: true,
|
|
114
|
+
original_bytes: originalBytes,
|
|
115
|
+
};
|
|
116
|
+
line = JSON.stringify(payload);
|
|
117
|
+
bytes = Buffer.byteLength(line, "utf8");
|
|
118
|
+
if (bytes <= maxLineBytes) {
|
|
119
|
+
bestMsg = candidateMsg;
|
|
120
|
+
lo = mid + 1;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
hi = mid - 1;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return JSON.stringify({
|
|
127
|
+
...record,
|
|
128
|
+
msg: bestMsg,
|
|
129
|
+
truncated: true,
|
|
130
|
+
original_bytes: originalBytes,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
export function createLogger(options = {}) {
|
|
134
|
+
const env = options.env ?? process.env;
|
|
135
|
+
const format = options.format ??
|
|
136
|
+
resolveLogFormat(env, options.stdoutIsTTY ?? Boolean(process.stdout.isTTY));
|
|
137
|
+
const level = options.level ?? resolveLogLevel(env);
|
|
138
|
+
const maxLineBytes = options.maxLineBytes ?? resolveLogMaxLineBytes(env);
|
|
139
|
+
const knownSecrets = options.knownSecrets ?? [];
|
|
140
|
+
const bindings = options.bindings ?? {};
|
|
141
|
+
const write = options.write ??
|
|
142
|
+
((line) => {
|
|
143
|
+
process.stdout.write(`${line}\n`);
|
|
144
|
+
});
|
|
145
|
+
const now = options.now ?? (() => new Date());
|
|
146
|
+
const emit = (recordLevel, event, msg, fields) => {
|
|
147
|
+
if (LEVEL_RANK[recordLevel] < LEVEL_RANK[level])
|
|
148
|
+
return;
|
|
149
|
+
const record = {
|
|
150
|
+
ts: now().toISOString(),
|
|
151
|
+
level: recordLevel,
|
|
152
|
+
event,
|
|
153
|
+
msg,
|
|
154
|
+
...bindings,
|
|
155
|
+
...fields,
|
|
156
|
+
};
|
|
157
|
+
const redacted = redact(record, {
|
|
158
|
+
knownSecrets,
|
|
159
|
+
namedSecrets: getNamedSecrets(),
|
|
160
|
+
});
|
|
161
|
+
write(serializeRecord(redacted, format, maxLineBytes));
|
|
162
|
+
};
|
|
163
|
+
const logger = {
|
|
164
|
+
debug(event, msg, fields) {
|
|
165
|
+
emit("debug", event, msg, fields);
|
|
166
|
+
},
|
|
167
|
+
info(event, msg, fields) {
|
|
168
|
+
emit("info", event, msg, fields);
|
|
169
|
+
},
|
|
170
|
+
warn(event, msg, fields) {
|
|
171
|
+
emit("warn", event, msg, fields);
|
|
172
|
+
},
|
|
173
|
+
error(event, msg, fields) {
|
|
174
|
+
emit("error", event, msg, fields);
|
|
175
|
+
},
|
|
176
|
+
child(childBindings) {
|
|
177
|
+
return createLogger({
|
|
178
|
+
env,
|
|
179
|
+
format,
|
|
180
|
+
level,
|
|
181
|
+
maxLineBytes,
|
|
182
|
+
knownSecrets,
|
|
183
|
+
write,
|
|
184
|
+
now,
|
|
185
|
+
bindings: { ...bindings, ...childBindings },
|
|
186
|
+
});
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
return logger;
|
|
190
|
+
}
|
|
191
|
+
export const logger = createLogger({
|
|
192
|
+
bindings: { component: "host" },
|
|
193
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { NamedSecret } from "../logging/redact.js";
|
|
2
|
+
export declare const REDACTION_SECRETS_FILENAME = "redaction-secrets.json";
|
|
3
|
+
export declare function registerNamedSecrets(secrets: readonly NamedSecret[]): void;
|
|
4
|
+
export declare function clearNamedSecretsForTests(): void;
|
|
5
|
+
export declare function getNamedSecrets(): readonly NamedSecret[];
|
|
6
|
+
export declare function loadNamedSecretsFromAttemptDir(attemptDir: string): void;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
let registry = [];
|
|
4
|
+
export const REDACTION_SECRETS_FILENAME = "redaction-secrets.json";
|
|
5
|
+
export function registerNamedSecrets(secrets) {
|
|
6
|
+
const next = [...registry];
|
|
7
|
+
for (const secret of secrets) {
|
|
8
|
+
const idx = next.findIndex((s) => s.name === secret.name);
|
|
9
|
+
if (idx >= 0)
|
|
10
|
+
next[idx] = secret;
|
|
11
|
+
else
|
|
12
|
+
next.push(secret);
|
|
13
|
+
}
|
|
14
|
+
registry = next;
|
|
15
|
+
}
|
|
16
|
+
export function clearNamedSecretsForTests() {
|
|
17
|
+
registry = [];
|
|
18
|
+
}
|
|
19
|
+
export function getNamedSecrets() {
|
|
20
|
+
return registry;
|
|
21
|
+
}
|
|
22
|
+
export function loadNamedSecretsFromAttemptDir(attemptDir) {
|
|
23
|
+
const filePath = path.join(attemptDir, REDACTION_SECRETS_FILENAME);
|
|
24
|
+
let raw;
|
|
25
|
+
try {
|
|
26
|
+
raw = readFileSync(filePath, "utf8");
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
const code = err.code;
|
|
30
|
+
if (code === "ENOENT")
|
|
31
|
+
return;
|
|
32
|
+
throw err;
|
|
33
|
+
}
|
|
34
|
+
let parsed;
|
|
35
|
+
try {
|
|
36
|
+
parsed = JSON.parse(raw);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!Array.isArray(parsed))
|
|
42
|
+
return;
|
|
43
|
+
const secrets = [];
|
|
44
|
+
for (const item of parsed) {
|
|
45
|
+
if (item !== null &&
|
|
46
|
+
typeof item === "object" &&
|
|
47
|
+
typeof item.name === "string" &&
|
|
48
|
+
typeof item.value === "string") {
|
|
49
|
+
secrets.push({
|
|
50
|
+
name: item.name,
|
|
51
|
+
value: item.value,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (secrets.length > 0) {
|
|
56
|
+
registerNamedSecrets(secrets);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type NamedSecret = {
|
|
2
|
+
name: string;
|
|
3
|
+
value: string;
|
|
4
|
+
};
|
|
5
|
+
export type RedactOptions = {
|
|
6
|
+
patterns?: RegExp[];
|
|
7
|
+
knownSecrets?: readonly string[];
|
|
8
|
+
namedSecrets?: readonly NamedSecret[];
|
|
9
|
+
};
|
|
10
|
+
export declare function shouldRegisterValue(value: string): boolean;
|
|
11
|
+
export declare function redactString(text: string, options?: RedactOptions): string;
|
|
12
|
+
export declare function redact<T extends Record<string, unknown>>(record: T, options?: RedactOptions): T;
|
|
13
|
+
export declare function stripUrlUserinfo(url: string): string;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { COMMON_WORDS } from "./commonWords.js";
|
|
2
|
+
const SECRET_PATTERNS = [
|
|
3
|
+
/\bsk-[A-Za-z0-9_-]{16,}\b/g,
|
|
4
|
+
/\bghp_[A-Za-z0-9]{20,}\b/g,
|
|
5
|
+
/\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
|
|
6
|
+
/\bBearer\s+[A-Za-z0-9\-._~+/]+=*/gi,
|
|
7
|
+
/\b[A-Za-z_]*KEY[A-Za-z_]*\s*[:=]\s*['"]?[A-Za-z0-9+/_-]{16,}['"]?/gi,
|
|
8
|
+
];
|
|
9
|
+
const REDACTED = "[redacted]";
|
|
10
|
+
export function shouldRegisterValue(value) {
|
|
11
|
+
if (value.length < 8)
|
|
12
|
+
return false;
|
|
13
|
+
const lower = value.toLowerCase();
|
|
14
|
+
if (COMMON_WORDS.has(lower))
|
|
15
|
+
return false;
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
function markerFor(name) {
|
|
19
|
+
return name !== undefined && name.length > 0
|
|
20
|
+
? `[redacted:${name}]`
|
|
21
|
+
: REDACTED;
|
|
22
|
+
}
|
|
23
|
+
function replaceAll(haystack, needle, replacement) {
|
|
24
|
+
if (needle.length === 0 || !haystack.includes(needle))
|
|
25
|
+
return haystack;
|
|
26
|
+
return haystack.split(needle).join(replacement);
|
|
27
|
+
}
|
|
28
|
+
function scrubString(value, patterns, knownSecrets, namedSecrets) {
|
|
29
|
+
let out = value;
|
|
30
|
+
for (const pattern of patterns) {
|
|
31
|
+
out = out.replace(pattern, REDACTED);
|
|
32
|
+
}
|
|
33
|
+
for (const named of namedSecrets) {
|
|
34
|
+
if (!shouldRegisterValue(named.value))
|
|
35
|
+
continue;
|
|
36
|
+
const marker = markerFor(named.name);
|
|
37
|
+
out = replaceAll(out, named.value, marker);
|
|
38
|
+
out = replaceAll(out, Buffer.from(named.value, "utf8").toString("base64"), marker);
|
|
39
|
+
out = replaceAll(out, encodeURIComponent(named.value), marker);
|
|
40
|
+
}
|
|
41
|
+
for (const secret of knownSecrets) {
|
|
42
|
+
if (secret.length === 0)
|
|
43
|
+
continue;
|
|
44
|
+
if (!out.includes(secret))
|
|
45
|
+
continue;
|
|
46
|
+
out = replaceAll(out, secret, REDACTED);
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
function scrubValue(value, patterns, knownSecrets, namedSecrets) {
|
|
51
|
+
if (typeof value === "string") {
|
|
52
|
+
return scrubString(value, patterns, knownSecrets, namedSecrets);
|
|
53
|
+
}
|
|
54
|
+
if (Array.isArray(value)) {
|
|
55
|
+
return value.map((item) => scrubValue(item, patterns, knownSecrets, namedSecrets));
|
|
56
|
+
}
|
|
57
|
+
if (value !== null && typeof value === "object") {
|
|
58
|
+
const out = {};
|
|
59
|
+
for (const [key, child] of Object.entries(value)) {
|
|
60
|
+
out[key] = scrubValue(child, patterns, knownSecrets, namedSecrets);
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
export function redactString(text, options = {}) {
|
|
67
|
+
return scrubString(text, options.patterns ?? SECRET_PATTERNS, options.knownSecrets ?? [], options.namedSecrets ?? []);
|
|
68
|
+
}
|
|
69
|
+
export function redact(record, options = {}) {
|
|
70
|
+
const patterns = options.patterns ?? SECRET_PATTERNS;
|
|
71
|
+
const knownSecrets = options.knownSecrets ?? [];
|
|
72
|
+
const namedSecrets = options.namedSecrets ?? [];
|
|
73
|
+
return scrubValue(record, patterns, knownSecrets, namedSecrets);
|
|
74
|
+
}
|
|
75
|
+
export function stripUrlUserinfo(url) {
|
|
76
|
+
try {
|
|
77
|
+
const parsed = new URL(url);
|
|
78
|
+
if (parsed.username || parsed.password) {
|
|
79
|
+
parsed.username = "";
|
|
80
|
+
parsed.password = "";
|
|
81
|
+
return parsed.toString();
|
|
82
|
+
}
|
|
83
|
+
return url;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return url.replace(/\/\/([^/@]+)@/g, "//");
|
|
87
|
+
}
|
|
88
|
+
}
|