stageflow 0.33.0 → 0.36.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 +3 -3
- package/dist/agent/adapterSpill.d.ts +14 -0
- package/dist/agent/adapterSpill.js +83 -0
- package/dist/agent/agentBackend.d.ts +2 -2
- package/dist/agent/agentBackend.js +2 -2
- package/dist/agent/claudeAdapter.d.ts +13 -2
- package/dist/agent/claudeAdapter.js +56 -6
- package/dist/agent/cursorProvider.d.ts +3 -0
- package/dist/agent/cursorProvider.js +23 -6
- package/dist/agent/fakeAgent.d.ts +3 -3
- package/dist/agent/piAdapter.d.ts +2 -2
- package/dist/agent/piAdapter.js +125 -15
- package/dist/agent/piIsolatedMcp.d.ts +1 -0
- package/dist/agent/piIsolatedMcp.js +19 -1
- package/dist/agent/piIsolatedMcpProbe.d.ts +11 -0
- package/dist/agent/piIsolatedMcpProbe.js +20 -0
- package/dist/agent/port.d.ts +4 -2
- package/dist/agent/providerAuth.js +36 -3
- package/dist/agent/{resolveAgentPort.d.ts → resolveStagePort.d.ts} +8 -8
- package/dist/agent/{resolveAgentPort.js → resolveStagePort.js} +11 -11
- package/dist/agent/stageTurnDriver.d.ts +33 -0
- package/dist/agent/stageTurnDriver.js +64 -0
- package/dist/agent/toolOutputBudget.d.ts +46 -0
- package/dist/agent/toolOutputBudget.js +97 -0
- package/dist/agent/toolOutputBudgetExtension.d.ts +22 -0
- package/dist/agent/toolOutputBudgetExtension.js +141 -0
- package/dist/agent/toolOutputSpill.d.ts +34 -0
- package/dist/agent/toolOutputSpill.js +85 -0
- package/dist/browser/agentBrowserLiveViewRelay.d.ts +41 -0
- package/dist/browser/agentBrowserLiveViewRelay.js +801 -0
- package/dist/browser/anchor.d.ts +2 -0
- package/dist/browser/anchor.js +2 -1
- package/dist/browser/auditSink.d.ts +8 -0
- package/dist/browser/browserHost.d.ts +29 -0
- package/dist/browser/browserSweep.d.ts +4 -1
- package/dist/browser/browserSweep.js +7 -0
- package/dist/browser/browserTeardown.d.ts +1 -0
- package/dist/browser/browserTeardown.js +32 -2
- package/dist/browser/containerBrowserEndpoint.d.ts +11 -0
- package/dist/browser/containerBrowserEndpoint.js +57 -0
- package/dist/browser/containerBrowserHost.d.ts +24 -0
- package/dist/browser/containerBrowserHost.js +147 -0
- package/dist/browser/dockerSandboxOrchestrator.d.ts +26 -0
- package/dist/browser/dockerSandboxOrchestrator.js +142 -0
- package/dist/browser/fakeLiveViewRelay.d.ts +15 -0
- package/dist/browser/fakeLiveViewRelay.js +94 -0
- package/dist/browser/fakeLiveViewSource.d.ts +6 -0
- package/dist/browser/fakeLiveViewSource.js +16 -0
- package/dist/browser/fakeRemoteBrowserHost.d.ts +2 -1
- package/dist/browser/fakeRemoteBrowserHost.js +2 -1
- package/dist/browser/fakeSandboxOrchestrator.d.ts +9 -0
- package/dist/browser/fakeSandboxOrchestrator.js +44 -0
- package/dist/browser/gateHandoff.d.ts +0 -2
- package/dist/browser/gateHandoff.js +5 -11
- package/dist/browser/hostCapabilities.d.ts +23 -0
- package/dist/browser/hostCapabilities.js +23 -0
- package/dist/browser/humanLogin.d.ts +2 -2
- package/dist/browser/humanLogin.js +11 -2
- package/dist/browser/liveViewPersisted.d.ts +11 -0
- package/dist/browser/liveViewPersisted.js +22 -0
- package/dist/browser/liveViewRelay.d.ts +76 -0
- package/dist/browser/liveViewRelay.js +1 -0
- package/dist/browser/liveViewSessions.d.ts +25 -0
- package/dist/browser/liveViewSessions.js +139 -0
- package/dist/browser/liveViewSource.d.ts +25 -0
- package/dist/browser/liveViewSource.js +7 -0
- package/dist/browser/liveViewTickets.d.ts +46 -0
- package/dist/browser/liveViewTickets.js +122 -0
- package/dist/browser/localBrowserHost.d.ts +10 -0
- package/dist/browser/localBrowserHost.js +53 -7
- package/dist/browser/ownerScope.d.ts +10 -0
- package/dist/browser/ownerScope.js +6 -0
- package/dist/browser/profileStore.d.ts +5 -2
- package/dist/browser/profileStore.js +3 -5
- package/dist/browser/sandboxOrchestrator.d.ts +60 -0
- package/dist/browser/sandboxOrchestrator.js +8 -0
- package/dist/browser/sessionApiSandboxOrchestrator.d.ts +34 -0
- package/dist/browser/sessionApiSandboxOrchestrator.js +108 -0
- package/dist/browser/stageBrowserEnv.d.ts +7 -0
- package/dist/browser/stageBrowserEnv.js +53 -15
- package/dist/browser/stageHandoff.d.ts +47 -0
- package/dist/browser/stageHandoff.js +101 -0
- package/dist/browser/stageProfileLock.d.ts +1 -0
- package/dist/browser/stageProfileLock.js +1 -3
- package/dist/browser/volumeProfileStore.d.ts +7 -0
- package/dist/browser/volumeProfileStore.js +35 -0
- package/dist/channels/config.d.ts +11 -0
- package/dist/channels/config.js +95 -0
- package/dist/channels/configWriter.d.ts +40 -0
- package/dist/channels/configWriter.js +148 -0
- package/dist/channels/connectionEdits.d.ts +34 -0
- package/dist/channels/connectionEdits.js +53 -0
- package/dist/channels/errorInfo.d.ts +18 -0
- package/dist/channels/errorInfo.js +71 -0
- package/dist/channels/errors.d.ts +12 -0
- package/dist/channels/errors.js +13 -0
- package/dist/channels/health.d.ts +41 -0
- package/dist/channels/health.js +99 -0
- package/dist/channels/host.d.ts +55 -0
- package/dist/channels/host.js +185 -0
- package/dist/channels/http.d.ts +31 -0
- package/dist/channels/http.js +79 -0
- package/dist/channels/index.d.ts +10 -0
- package/dist/channels/index.js +9 -0
- package/dist/channels/ledger.d.ts +37 -0
- package/dist/channels/ledger.js +95 -0
- package/dist/channels/providers/agentmail.d.ts +26 -0
- package/dist/channels/providers/agentmail.js +81 -0
- package/dist/channels/providers/kit.d.ts +40 -0
- package/dist/channels/providers/kit.js +78 -0
- package/dist/channels/providers/photonImessage.d.ts +45 -0
- package/dist/channels/providers/photonImessage.js +152 -0
- package/dist/channels/providers/photonWhatsapp.d.ts +49 -0
- package/dist/channels/providers/photonWhatsapp.js +146 -0
- package/dist/channels/providers/slack.d.ts +25 -0
- package/dist/channels/providers/slack.js +101 -0
- package/dist/channels/providers/telegram.d.ts +21 -0
- package/dist/channels/providers/telegram.js +100 -0
- package/dist/channels/providers.d.ts +5 -0
- package/dist/channels/providers.js +22 -0
- package/dist/channels/registry.d.ts +54 -0
- package/dist/channels/registry.js +186 -0
- package/dist/channels/secretStore.d.ts +21 -0
- package/dist/channels/secretStore.js +75 -0
- package/dist/channels/secrets.d.ts +15 -0
- package/dist/channels/secrets.js +50 -0
- package/dist/channels/service.d.ts +54 -0
- package/dist/channels/service.js +307 -0
- package/dist/channels/stageChannels.d.ts +45 -0
- package/dist/channels/stageChannels.js +110 -0
- package/dist/channels/types.d.ts +142 -0
- package/dist/channels/types.js +1 -0
- package/dist/cli/browserCommand.d.ts +3 -1
- package/dist/cli/browserCommand.js +25 -20
- package/dist/cli/doctorCommand.js +3 -1
- package/dist/cli/resolveCliProjectRoot.d.ts +7 -0
- package/dist/cli/resolveCliProjectRoot.js +27 -0
- package/dist/cli/runCommand.d.ts +1 -1
- package/dist/cli/runCommand.js +59 -17
- package/dist/cli/skillsCommand.d.ts +20 -0
- package/dist/cli/skillsCommand.js +74 -31
- package/dist/cli/validateCommand.d.ts +1 -1
- package/dist/cli/validateCommand.js +22 -4
- package/dist/cli.js +4 -4
- package/dist/config/browseCatalog.d.ts +2 -0
- package/dist/config/browseCatalog.js +6 -0
- package/dist/config/catalogRelativePath.d.ts +4 -0
- package/dist/config/catalogRelativePath.js +37 -9
- package/dist/config/createStage.d.ts +1 -0
- package/dist/config/createStage.js +11 -2
- package/dist/config/createTask.d.ts +30 -0
- package/dist/config/createTask.js +161 -0
- package/dist/config/createTrigger.d.ts +15 -0
- package/dist/config/createTrigger.js +32 -25
- package/dist/config/draftPackage.d.ts +42 -0
- package/dist/config/draftPackage.js +220 -23
- package/dist/config/ensureCatalogScanRoot.d.ts +2 -1
- package/dist/config/ensureCatalogScanRoot.js +6 -2
- package/dist/config/findTaskInCatalog.d.ts +33 -0
- package/dist/config/findTaskInCatalog.js +69 -0
- package/dist/config/hostConfig.d.ts +21 -0
- package/dist/config/hostConfig.js +114 -1
- package/dist/config/importTask.d.ts +19 -0
- package/dist/config/importTask.js +133 -0
- package/dist/config/installSkillRequest.d.ts +40 -0
- package/dist/config/installSkillRequest.js +286 -0
- package/dist/config/loadOutcome.d.ts +11 -6
- package/dist/config/loadPipeline.js +27 -4
- package/dist/config/loadStage.js +17 -0
- package/dist/config/loadStageflowManifest.js +4 -1
- package/dist/config/loadTask.js +2 -0
- package/dist/config/loadTrigger.js +2 -0
- package/dist/config/mergePipelineIncludes.d.ts +3 -0
- package/dist/config/mergePipelineIncludes.js +30 -0
- package/dist/config/normalizePipelineStageEntry.js +10 -1
- package/dist/config/pipelineStageKeys.js +1 -0
- package/dist/config/printTargetYaml.js +0 -1
- package/dist/config/publishDraftPackage.d.ts +5 -1
- package/dist/config/publishDraftPackage.js +4 -1
- package/dist/config/resolveCatalogRoots.d.ts +4 -1
- package/dist/config/resolveCatalogRoots.js +18 -1
- package/dist/config/skillUsageIndex.d.ts +6 -0
- package/dist/config/skillUsageIndex.js +39 -0
- package/dist/config/stageBrowser.js +13 -0
- package/dist/config/triggerAdapterStatus.d.ts +11 -0
- package/dist/config/triggerAdapterStatus.js +90 -0
- package/dist/config/triggerCatalog.d.ts +41 -2
- package/dist/config/triggerCatalog.js +107 -15
- package/dist/config/updateTask.d.ts +30 -0
- package/dist/config/updateTask.js +119 -0
- package/dist/config/updateTrigger.d.ts +38 -0
- package/dist/config/updateTrigger.js +220 -0
- package/dist/config/validateCatalog.d.ts +9 -2
- package/dist/config/validateCatalog.js +71 -7
- package/dist/config/workshopCatalogFilenames.d.ts +37 -0
- package/dist/config/workshopCatalogFilenames.js +125 -0
- package/dist/config/workshopCatalogRepair.d.ts +7 -0
- package/dist/config/workshopCatalogRepair.js +144 -0
- package/dist/config/yamlParsePosition.d.ts +4 -0
- package/dist/config/yamlParsePosition.js +9 -0
- package/dist/errors/codes.d.ts +1 -1
- package/dist/errors/codes.js +1 -0
- package/dist/hosted/runtime.d.ts +2 -2
- package/dist/hosted/types.d.ts +1 -1
- package/dist/hosted/types.js +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/mcp/catalogTools.js +1 -0
- package/dist/mcp/getStartedTool.js +15 -6
- package/dist/mcp/triggerTools.js +2 -8
- package/dist/operatorAgent/agentPort.d.ts +11 -0
- package/dist/operatorAgent/agentPort.js +12 -0
- package/dist/operatorAgent/index.d.ts +1 -0
- package/dist/operatorAgent/index.js +1 -0
- package/dist/operatorAgent/piModel.js +26 -6
- package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +1 -1
- package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +4 -2
- package/dist/operatorAgent/profiles/workshopAuthorTools.js +103 -16
- package/dist/package-meta.d.ts +1 -1
- package/dist/package-meta.js +1 -1
- package/dist/preflight/channelChecks.d.ts +17 -0
- package/dist/preflight/channelChecks.js +106 -0
- package/dist/preflight/pipelinePreflight.d.ts +9 -2
- package/dist/preflight/pipelinePreflight.js +9 -1
- package/dist/project/ensureProjectRootsFromEnv.d.ts +3 -0
- package/dist/project/ensureProjectRootsFromEnv.js +22 -0
- package/dist/project/globalHome.d.ts +1 -0
- package/dist/project/globalHome.js +18 -0
- package/dist/project/workspaceCatalog.d.ts +10 -0
- package/dist/project/workspaceCatalog.js +91 -0
- package/dist/runstore/backup.js +14 -4
- package/dist/runstore/port.d.ts +11 -1
- package/dist/runstore/restore.js +5 -2
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +8 -0
- package/dist/runstore/sqlite/SqliteRunStore.js +29 -9
- package/dist/runstore/sqlite/migrations/012-trigger-fires.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/012-trigger-fires.js +18 -0
- package/dist/runstore/sqlite/migrations/index.js +2 -0
- package/dist/runstore/stageSnapshot.d.ts +3 -1
- package/dist/runstore/stageSnapshot.js +19 -0
- package/dist/runstore/workspaceLayout.js +27 -0
- package/dist/runstore/writeContainedArtifact.d.ts +1 -0
- package/dist/runstore/writeContainedArtifact.js +52 -0
- package/dist/runtime/credentialBinding.js +14 -6
- package/dist/runtime/emailSource.js +20 -28
- package/dist/runtime/githubPollSource.js +20 -28
- package/dist/runtime/pipelineRunner.d.ts +4 -4
- package/dist/runtime/pipelineRunner.js +3 -1
- package/dist/runtime/pipelineScheduler.d.ts +2 -2
- package/dist/runtime/pipelineScheduler.js +3 -0
- package/dist/runtime/repositoryMaterialize.d.ts +1 -1
- package/dist/runtime/resumeReconstruct.d.ts +2 -2
- package/dist/runtime/resumeTimedOut.d.ts +2 -2
- package/dist/runtime/runManager.d.ts +18 -2
- package/dist/runtime/runManager.js +99 -31
- package/dist/runtime/runRetryCoordinator.d.ts +2 -2
- package/dist/runtime/scheduleSource.js +15 -24
- package/dist/runtime/stageAttemptBootstrap.d.ts +2 -2
- package/dist/runtime/stageAttemptBootstrap.js +26 -1
- package/dist/runtime/stageProcessLauncher.js +33 -3
- package/dist/runtime/stageRoots.js +8 -1
- package/dist/runtime/stageRunner.d.ts +2 -2
- package/dist/runtime/stageRunner.js +8 -2
- package/dist/runtime/stageWorker.js +2 -2
- package/dist/runtime/triggerPort.d.ts +5 -3
- package/dist/runtime/triggerRunner.d.ts +1 -0
- package/dist/runtime/triggerRunner.js +27 -20
- package/dist/runtime/webhookAccept.d.ts +25 -0
- package/dist/runtime/webhookAccept.js +56 -0
- package/dist/server/bootstrap.d.ts +10 -2
- package/dist/server/bootstrap.js +19 -1
- package/dist/server/channelRoutes.d.ts +9 -0
- package/dist/server/channelRoutes.js +88 -0
- package/dist/server/controlToken.d.ts +1 -1
- package/dist/server/controlToken.js +18 -2
- package/dist/server/healthSurfaces.d.ts +12 -0
- package/dist/server/healthSurfaces.js +27 -6
- package/dist/server/http.d.ts +19 -10
- package/dist/server/http.js +616 -85
- package/dist/server/liveViewPath.d.ts +9 -0
- package/dist/server/liveViewPath.js +8 -0
- package/dist/server/liveViewRoutes.d.ts +58 -0
- package/dist/server/liveViewRoutes.js +565 -0
- package/dist/server/mcpHost.d.ts +4 -2
- package/dist/server/mcpHost.js +2 -0
- package/dist/server/readCatalogFile.d.ts +29 -0
- package/dist/server/readCatalogFile.js +215 -0
- package/dist/server/triggerRoutes.d.ts +1 -0
- package/dist/server/triggerRoutes.js +200 -73
- package/dist/tools/sendMessage.d.ts +21 -0
- package/dist/tools/sendMessage.js +25 -0
- package/dist/tools/writeStageArtifact.d.ts +9 -0
- package/dist/tools/writeStageArtifact.js +2 -47
- package/dist/types/pipeline.d.ts +5 -2
- package/dist/types/stage.d.ts +14 -1
- package/dist/types/stageflowManifest.d.ts +1 -1
- package/dist/ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/ui/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/dist/ui/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/dist/ui/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/dist/ui/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/dist/ui/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/dist/ui/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/dist/ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/ui/assets/index-BOhqnPNg.js +260 -0
- package/dist/ui/assets/{index-Bv4p1aPK.css → index-Clv4sJpU.css} +1 -1
- package/dist/ui/index.html +4 -4
- package/dist/workshop/autosave.d.ts +2 -2
- package/dist/workshop/autosave.js +4 -6
- package/dist/workshop/buildStore.d.ts +2 -0
- package/dist/workshop/chatAttachments.d.ts +27 -0
- package/dist/workshop/chatAttachments.js +98 -0
- package/dist/workshop/chatTurn.d.ts +5 -2
- package/dist/workshop/chatTurn.js +22 -18
- package/dist/workshop/sessionMutations.d.ts +17 -0
- package/dist/workshop/sessionMutations.js +107 -0
- package/dist/workshop/sessionStore.d.ts +10 -0
- package/dist/workshop/sessionStore.js +52 -0
- package/dist/workshop/workshopPin.d.ts +4 -3
- package/dist/workshop/workshopPin.js +26 -6
- package/dist/workshop/workshopTranscriptTurn.d.ts +8 -0
- package/dist/workshop/workshopTranscriptTurn.js +46 -0
- package/examples/README.md +1 -1
- package/examples/archify-on-pr/README.md +1 -1
- package/examples/archify-on-pr/archify-on-pr.pipeline.yaml +0 -1
- package/examples/archify-on-pr/author-diagrams.yaml +1 -0
- package/examples/channels-smoke/README.md +12 -0
- package/examples/channels-smoke/channels-smoke.pipeline.yaml +11 -0
- package/examples/channels-smoke/channels-smoke.task.yaml +3 -0
- package/examples/channels-smoke/send-email.yaml +32 -0
- package/examples/channels-smoke/send-text.yaml +31 -0
- package/examples/conditional-fork/branch-a.yaml +1 -1
- package/examples/conditional-fork/decide.yaml +2 -1
- package/examples/feature-loop/{implement.yaml → feature-implement.yaml} +1 -1
- package/examples/feature-loop/feature-loop.pipeline.yaml +10 -10
- package/examples/feature-loop/{plan.yaml → feature-plan.yaml} +1 -1
- package/examples/feature-loop/{review.yaml → feature-review.yaml} +1 -1
- package/examples/feedback-loop/{implement.yaml → feedback-implement.yaml} +1 -1
- package/examples/feedback-loop/feedback-loop-wait-human.pipeline.yaml +9 -9
- package/examples/feedback-loop/feedback-loop.pipeline.yaml +9 -9
- package/examples/feedback-loop/{plan.yaml → feedback-plan.yaml} +1 -1
- package/examples/feedback-loop/{review.yaml → feedback-review.yaml} +1 -1
- package/examples/generic-fan-in/{research.yaml → fan-in-research.yaml} +1 -1
- package/examples/generic-fan-in/generic-fan-in-accepted.pipeline.yaml +3 -3
- package/examples/generic-fan-in/generic-fan-in.pipeline.yaml +3 -3
- package/examples/hello-world/{research.yaml → hello-research.yaml} +1 -1
- package/examples/hello-world/hello.pipeline.yaml +2 -2
- package/examples/mcp-hitl-tour/{review.yaml → mcp-hitl-review.yaml} +1 -1
- package/examples/mcp-hitl-tour/mcp-hitl-tour.pipeline.yaml +3 -3
- package/examples/oss-issue-contribution/oss-explain-fix.yaml +1 -0
- package/examples/oss-issue-contribution/oss-explain-issue.yaml +1 -0
- package/examples/oss-issue-contribution/oss-issue-contribution.pipeline.yaml +0 -2
- package/examples/stageflow.yaml +6 -0
- package/examples/triggers/sample-github-pr.trigger.yaml +12 -0
- package/examples/triggers/sample-inbox-email.trigger.yaml +14 -0
- package/examples/triggers/sample-manual-hello.trigger.yaml +5 -0
- package/examples/triggers/sample-nightly-hello.trigger.yaml +8 -0
- package/examples/triggers/sample-signed-webhook.trigger.yaml +11 -0
- package/examples/triggers-readiness/README.md +48 -0
- package/examples/triggers-readiness/readiness-emit.yaml +18 -0
- package/examples/triggers-readiness/readiness.pipeline.yaml +4 -0
- package/examples/triggers-readiness/readiness.task.yaml +6 -0
- package/examples/triggers-readiness/stageflow.yaml +13 -0
- package/examples/triggers-readiness/triggers/readiness-dynamic-manual.trigger.yaml +4 -0
- package/examples/triggers-readiness/triggers/readiness-email.trigger.yaml +12 -0
- package/examples/triggers-readiness/triggers/readiness-github-pr.trigger.yaml +12 -0
- package/examples/triggers-readiness/triggers/readiness-manual.trigger.yaml +5 -0
- package/examples/triggers-readiness/triggers/readiness-schedule.trigger.yaml +8 -0
- package/examples/triggers-readiness/triggers/readiness-webhook.trigger.yaml +11 -0
- package/package.json +8 -3
- package/skills/stageflow/references/control-surface.md +1 -1
- package/skills/stageflow-author/references/catalog-write-conventions.md +2 -2
- package/skills/stageflow-run/SKILL.md +1 -1
- package/skills/stageflow-run/references/mcp-call.md +1 -1
- package/skills/stageflow-setup/SKILL.md +1 -1
- package/dist/ui/assets/index-59cjF0OA.js +0 -133
|
@@ -2,6 +2,7 @@ import { collectEffectiveRequires, } from "../config/toolRequires.js";
|
|
|
2
2
|
import { resolveStageMcpServers, StageMcpError, } from "../config/resolveStageMcpServers.js";
|
|
3
3
|
import { assertSecretPresent, loadSecretRegistry, SecretUnavailableError, } from "../runtime/stageSecrets.js";
|
|
4
4
|
import { isForeverDeniedSecret } from "../runtime/stageEnvironment.js";
|
|
5
|
+
import { checkStageChannels } from "./channelChecks.js";
|
|
5
6
|
import { checkToolchainRequirements, curatedStageEnv, } from "./toolchain.js";
|
|
6
7
|
function toolchainItems(checks) {
|
|
7
8
|
return checks.map((c) => ({
|
|
@@ -165,14 +166,21 @@ export async function runPipelinePreflight(loaded, options) {
|
|
|
165
166
|
projectRoot: options.projectRoot,
|
|
166
167
|
hostEnv,
|
|
167
168
|
});
|
|
169
|
+
const channelChecks = await checkStageChannels(loaded, {
|
|
170
|
+
projectRoot: options.projectRoot,
|
|
171
|
+
hostEnv,
|
|
172
|
+
...options.channels,
|
|
173
|
+
});
|
|
168
174
|
const checks = [
|
|
169
175
|
...toolchainItems(toolchain.checks),
|
|
170
176
|
...secretChecks,
|
|
171
177
|
...mcpChecks,
|
|
178
|
+
...channelChecks,
|
|
172
179
|
];
|
|
173
180
|
const ok = toolchain.ok &&
|
|
174
181
|
!secretChecks.some((c) => c.status !== "ok") &&
|
|
175
|
-
!mcpChecks.some((c) => c.status !== "ok")
|
|
182
|
+
!mcpChecks.some((c) => c.status !== "ok") &&
|
|
183
|
+
!channelChecks.some((c) => c.status !== "ok");
|
|
176
184
|
return { ok, checks, requirements, toolchain };
|
|
177
185
|
}
|
|
178
186
|
export function preflightFailureCode(result, options = {}) {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
export function parseEnsureProjectRootsEnv(env) {
|
|
3
|
+
const raw = env.STAGEFLOW_ENSURE_PROJECT_ROOTS?.trim();
|
|
4
|
+
if (!raw)
|
|
5
|
+
return [];
|
|
6
|
+
return raw
|
|
7
|
+
.split(",")
|
|
8
|
+
.map((entry) => entry.trim())
|
|
9
|
+
.filter((entry) => entry.length > 0);
|
|
10
|
+
}
|
|
11
|
+
export async function ensureProjectRootsFromEnv(env, store) {
|
|
12
|
+
for (const entry of parseEnsureProjectRootsEnv(env)) {
|
|
13
|
+
const abs = path.resolve(entry);
|
|
14
|
+
try {
|
|
15
|
+
await store.ensureProject(abs);
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
19
|
+
console.warn(`stageflow: could not register project_root ${abs}: ${message}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -5,5 +5,6 @@ export declare class StageflowHomeError extends Error {
|
|
|
5
5
|
}
|
|
6
6
|
export declare function resetGlobalStageflowHomeForTests(): void;
|
|
7
7
|
export declare function globalStageflowHome(): string;
|
|
8
|
+
export declare function globalCredentialRoot(): string;
|
|
8
9
|
export declare function assertStageflowHomeWritable(): void;
|
|
9
10
|
export declare function ensureGlobalHome(): string;
|
|
@@ -10,8 +10,10 @@ export class StageflowHomeError extends Error {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
let memoizedHome;
|
|
13
|
+
let memoizedCredentialRoot;
|
|
13
14
|
export function resetGlobalStageflowHomeForTests() {
|
|
14
15
|
memoizedHome = undefined;
|
|
16
|
+
memoizedCredentialRoot = undefined;
|
|
15
17
|
}
|
|
16
18
|
function resolveStageflowHomeFromEnv() {
|
|
17
19
|
const raw = process.env.STAGEFLOW_HOME?.trim();
|
|
@@ -30,6 +32,22 @@ export function globalStageflowHome() {
|
|
|
30
32
|
}
|
|
31
33
|
return memoizedHome;
|
|
32
34
|
}
|
|
35
|
+
function resolveCredentialRootFromEnv() {
|
|
36
|
+
const raw = process.env.STAGEFLOW_CREDENTIAL_HOME?.trim();
|
|
37
|
+
if (raw) {
|
|
38
|
+
if (path.isAbsolute(raw)) {
|
|
39
|
+
return path.resolve(raw);
|
|
40
|
+
}
|
|
41
|
+
return path.resolve(process.cwd(), raw);
|
|
42
|
+
}
|
|
43
|
+
return path.join(os.homedir(), ".stageflow");
|
|
44
|
+
}
|
|
45
|
+
export function globalCredentialRoot() {
|
|
46
|
+
if (memoizedCredentialRoot === undefined) {
|
|
47
|
+
memoizedCredentialRoot = resolveCredentialRootFromEnv();
|
|
48
|
+
}
|
|
49
|
+
return memoizedCredentialRoot;
|
|
50
|
+
}
|
|
33
51
|
function stageflowHomeNotWritableMessage(resolvedPath) {
|
|
34
52
|
const uid = process.getuid?.() ?? 0;
|
|
35
53
|
const gid = process.getgid?.() ?? 0;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const WORKSPACE_CATALOG_ID = "workspace";
|
|
2
|
+
export type WorkspaceProvisionResult = {
|
|
3
|
+
path: string;
|
|
4
|
+
ok: boolean;
|
|
5
|
+
reason?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function resetWorkspaceProvisionForTests(): void;
|
|
8
|
+
export declare function lastWorkspaceProvisionResult(): WorkspaceProvisionResult | undefined;
|
|
9
|
+
export declare function workspaceCatalogPath(home?: string): string;
|
|
10
|
+
export declare function ensureWorkspaceCatalog(home?: string): WorkspaceProvisionResult;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { accessSync, constants, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { globalStageflowHome } from "./globalHome.js";
|
|
4
|
+
export const WORKSPACE_CATALOG_ID = "workspace";
|
|
5
|
+
let lastProvisionResult;
|
|
6
|
+
export function resetWorkspaceProvisionForTests() {
|
|
7
|
+
lastProvisionResult = undefined;
|
|
8
|
+
}
|
|
9
|
+
export function lastWorkspaceProvisionResult() {
|
|
10
|
+
return lastProvisionResult;
|
|
11
|
+
}
|
|
12
|
+
export function workspaceCatalogPath(home) {
|
|
13
|
+
return path.join(home ?? globalStageflowHome(), "workspace");
|
|
14
|
+
}
|
|
15
|
+
function workspaceManifestYaml() {
|
|
16
|
+
return `version: 1
|
|
17
|
+
catalog:
|
|
18
|
+
pipelines:
|
|
19
|
+
- .
|
|
20
|
+
tasks:
|
|
21
|
+
- .
|
|
22
|
+
triggers:
|
|
23
|
+
- .
|
|
24
|
+
patterns:
|
|
25
|
+
pipeline: "*.pipeline.yaml"
|
|
26
|
+
task: "*.task.yaml"
|
|
27
|
+
trigger: "*.trigger.yaml"
|
|
28
|
+
`;
|
|
29
|
+
}
|
|
30
|
+
function workspaceNotWritableReason(resolvedPath) {
|
|
31
|
+
const uid = process.getuid?.() ?? 0;
|
|
32
|
+
const gid = process.getgid?.() ?? 0;
|
|
33
|
+
return `Workspace catalog is not writable (${resolvedPath}). Running as uid=${uid} gid=${gid}. Try: sudo chown -R ${uid}:${gid} ${path.dirname(resolvedPath)}`;
|
|
34
|
+
}
|
|
35
|
+
function isWritableDir(dir) {
|
|
36
|
+
try {
|
|
37
|
+
accessSync(dir, constants.W_OK);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
const code = err.code;
|
|
42
|
+
if (code === "ENOENT") {
|
|
43
|
+
const parent = path.dirname(dir);
|
|
44
|
+
if (parent === dir)
|
|
45
|
+
return false;
|
|
46
|
+
try {
|
|
47
|
+
accessSync(parent, constants.W_OK);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function ensureWorkspaceCatalog(home) {
|
|
58
|
+
const wsPath = workspaceCatalogPath(home);
|
|
59
|
+
if (!isWritableDir(wsPath)) {
|
|
60
|
+
const result = {
|
|
61
|
+
path: wsPath,
|
|
62
|
+
ok: false,
|
|
63
|
+
reason: workspaceNotWritableReason(wsPath),
|
|
64
|
+
};
|
|
65
|
+
lastProvisionResult = result;
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
mkdirSync(wsPath, { recursive: true });
|
|
70
|
+
const manifestPath = path.join(wsPath, "stageflow.yaml");
|
|
71
|
+
try {
|
|
72
|
+
accessSync(manifestPath, constants.F_OK);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
writeFileSync(manifestPath, workspaceManifestYaml(), "utf8");
|
|
76
|
+
}
|
|
77
|
+
const result = { path: wsPath, ok: true };
|
|
78
|
+
lastProvisionResult = result;
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
83
|
+
const result = {
|
|
84
|
+
path: wsPath,
|
|
85
|
+
ok: false,
|
|
86
|
+
reason: `${workspaceNotWritableReason(wsPath)} (${message})`,
|
|
87
|
+
};
|
|
88
|
+
lastProvisionResult = result;
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
}
|
package/dist/runstore/backup.js
CHANGED
|
@@ -8,7 +8,8 @@ import { pipeline } from "node:stream/promises";
|
|
|
8
8
|
import { createWriteStream } from "node:fs";
|
|
9
9
|
import Database from "better-sqlite3";
|
|
10
10
|
import { PACKAGE_VERSION } from "../package-meta.js";
|
|
11
|
-
import { globalStageflowHome } from "../project/globalHome.js";
|
|
11
|
+
import { globalCredentialRoot, globalStageflowHome } from "../project/globalHome.js";
|
|
12
|
+
import { isUsableAuthFile } from "../runtime/credentialBinding.js";
|
|
12
13
|
import { readFilesystemSize } from "./diskUsage.js";
|
|
13
14
|
import { isInsideDir } from "./workspaceLayout.js";
|
|
14
15
|
export class BackupError extends Error {
|
|
@@ -45,6 +46,14 @@ export function assertBackupOutPathAllowed(outPath, homeDir = globalStageflowHom
|
|
|
45
46
|
}
|
|
46
47
|
return resolved;
|
|
47
48
|
}
|
|
49
|
+
function isCredentialRootDir(dir) {
|
|
50
|
+
return path.resolve(dir) === path.resolve(globalCredentialRoot());
|
|
51
|
+
}
|
|
52
|
+
function packAuthMember(homeDir, authPath, noCredentials) {
|
|
53
|
+
return (!noCredentials &&
|
|
54
|
+
isCredentialRootDir(homeDir) &&
|
|
55
|
+
isUsableAuthFile(authPath));
|
|
56
|
+
}
|
|
48
57
|
function fingerprint(filePath) {
|
|
49
58
|
if (!existsSync(filePath))
|
|
50
59
|
return null;
|
|
@@ -203,7 +212,8 @@ export async function createBackup(options) {
|
|
|
203
212
|
});
|
|
204
213
|
contents.push("settings.json");
|
|
205
214
|
}
|
|
206
|
-
|
|
215
|
+
const includeAuth = packAuthMember(homeDir, authPath, noCredentials);
|
|
216
|
+
if (includeAuth) {
|
|
207
217
|
const stagedAuth = path.join(staging, "auth.json");
|
|
208
218
|
copyFileSync(authPath, stagedAuth);
|
|
209
219
|
chmodSync(stagedAuth, 0o600);
|
|
@@ -226,14 +236,14 @@ export async function createBackup(options) {
|
|
|
226
236
|
schema_version: userVersion,
|
|
227
237
|
contents,
|
|
228
238
|
db_only: false,
|
|
229
|
-
credentials_included:
|
|
239
|
+
credentials_included: includeAuth,
|
|
230
240
|
a2a_artifacts_included: includeA2a,
|
|
231
241
|
settings_fingerprint_before: settingsBefore,
|
|
232
242
|
settings_fingerprint_after: settingsAfter,
|
|
233
243
|
auth_fingerprint_before: authBefore,
|
|
234
244
|
auth_fingerprint_after: authAfter,
|
|
235
245
|
fingerprint_skew: fingerprintSkew,
|
|
236
|
-
secret_warning: SECRET_WARNING,
|
|
246
|
+
secret_warning: includeAuth ? SECRET_WARNING : "",
|
|
237
247
|
};
|
|
238
248
|
const manifestPath = path.join(staging, "manifest.json");
|
|
239
249
|
writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, {
|
package/dist/runstore/port.d.ts
CHANGED
|
@@ -99,6 +99,7 @@ export type StageSnapshot = {
|
|
|
99
99
|
attempt_count: number;
|
|
100
100
|
/** Total $ spent on this stage across every attempt; omitted when no attempt reported usage. */
|
|
101
101
|
cost_usd?: number;
|
|
102
|
+
usage?: StageUsage;
|
|
102
103
|
};
|
|
103
104
|
export type StageExecution = {
|
|
104
105
|
run_id: string;
|
|
@@ -470,12 +471,21 @@ export interface RunStore {
|
|
|
470
471
|
upsertTrigger(input: UpsertTriggerInput): Promise<TriggerRecord>;
|
|
471
472
|
getTrigger(id: string): Promise<TriggerRecord | null>;
|
|
472
473
|
listTriggers(): Promise<TriggerRecord[]>;
|
|
473
|
-
/** Record a fire: stamps last_fired_at/last_run_id. Throws if the trigger is missing. */
|
|
474
|
+
/** Record a fire: stamps last_fired_at/last_run_id and appends trigger_fires. Throws if the trigger is missing. */
|
|
474
475
|
recordTriggerFired(id: string, runId: string): Promise<void>;
|
|
476
|
+
/** Newest first. */
|
|
477
|
+
listTriggerFires(triggerId: string, limit: number): Promise<Array<{
|
|
478
|
+
fired_at: string;
|
|
479
|
+
run_id: string;
|
|
480
|
+
}>>;
|
|
475
481
|
/** Persist the next computed fire time for a schedule-kind trigger. Throws if the trigger is missing. */
|
|
476
482
|
setTriggerNextRun(id: string, nextRunAt: string): Promise<void>;
|
|
477
483
|
/** Read a single adapter-owned state value for a trigger, or null if unset. */
|
|
478
484
|
getTriggerAdapterState(triggerId: string, key: string): Promise<string | null>;
|
|
485
|
+
getTriggerAdapterStateMeta(triggerId: string, key: string): Promise<{
|
|
486
|
+
value: string;
|
|
487
|
+
updated_at: string;
|
|
488
|
+
} | null>;
|
|
479
489
|
/** Upsert a single adapter-owned state value for a trigger. */
|
|
480
490
|
setTriggerAdapterState(triggerId: string, key: string, value: string): Promise<void>;
|
|
481
491
|
readRun(runId: string): Promise<RunDetail>;
|
package/dist/runstore/restore.js
CHANGED
|
@@ -7,7 +7,8 @@ import { pipeline } from "node:stream/promises";
|
|
|
7
7
|
import { createWriteStream } from "node:fs";
|
|
8
8
|
import Database from "better-sqlite3";
|
|
9
9
|
import { PACKAGE_VERSION } from "../package-meta.js";
|
|
10
|
-
import { globalStageflowHome } from "../project/globalHome.js";
|
|
10
|
+
import { globalCredentialRoot, globalStageflowHome } from "../project/globalHome.js";
|
|
11
|
+
import { isUsableAuthFile } from "../runtime/credentialBinding.js";
|
|
11
12
|
import { CURRENT_SCHEMA_VERSION } from "./sqlite/migrations/index.js";
|
|
12
13
|
import { StoreSchemaError } from "./sqlite/storeSchemaError.js";
|
|
13
14
|
import { assertStoreQuickCheck } from "./sqlite/applyStorePragmas.js";
|
|
@@ -234,7 +235,9 @@ export async function applyRestoreArchive(options) {
|
|
|
234
235
|
if (existsSync(settingsSrc)) {
|
|
235
236
|
copyFileSync(settingsSrc, path.join(homeDir, "settings.json"));
|
|
236
237
|
}
|
|
237
|
-
|
|
238
|
+
const restoreAuth = path.resolve(homeDir) === path.resolve(globalCredentialRoot()) &&
|
|
239
|
+
isUsableAuthFile(authSrc);
|
|
240
|
+
if (restoreAuth) {
|
|
238
241
|
const agentDir = path.join(homeDir, "agent");
|
|
239
242
|
mkdirSync(agentDir, { recursive: true });
|
|
240
243
|
try {
|
|
@@ -82,8 +82,16 @@ export declare class SqliteRunStore implements RunStore {
|
|
|
82
82
|
getTrigger(id: string): Promise<TriggerRecord | null>;
|
|
83
83
|
listTriggers(): Promise<TriggerRecord[]>;
|
|
84
84
|
recordTriggerFired(id: string, runId: string): Promise<void>;
|
|
85
|
+
listTriggerFires(triggerId: string, limit: number): Promise<Array<{
|
|
86
|
+
fired_at: string;
|
|
87
|
+
run_id: string;
|
|
88
|
+
}>>;
|
|
85
89
|
setTriggerNextRun(id: string, nextRunAt: string): Promise<void>;
|
|
86
90
|
getTriggerAdapterState(triggerId: string, key: string): Promise<string | null>;
|
|
91
|
+
getTriggerAdapterStateMeta(triggerId: string, key: string): Promise<{
|
|
92
|
+
value: string;
|
|
93
|
+
updated_at: string;
|
|
94
|
+
} | null>;
|
|
87
95
|
setTriggerAdapterState(triggerId: string, key: string, value: string): Promise<void>;
|
|
88
96
|
readRun(runId: string): Promise<RunDetail>;
|
|
89
97
|
createFeedbackLoop(runId: string, input: CreateFeedbackLoopInput): Promise<FeedbackLoopRecord>;
|
|
@@ -942,13 +942,29 @@ export class SqliteRunStore {
|
|
|
942
942
|
async recordTriggerFired(id, runId) {
|
|
943
943
|
await this.ready();
|
|
944
944
|
const now = new Date().toISOString();
|
|
945
|
-
const
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
945
|
+
const apply = this.db.transaction(() => {
|
|
946
|
+
const result = this.db
|
|
947
|
+
.prepare(`UPDATE triggers SET last_fired_at = @last_fired_at, last_run_id = @last_run_id, updated_at = @updated_at
|
|
948
|
+
WHERE id = @id`)
|
|
949
|
+
.run({ id, last_fired_at: now, last_run_id: runId, updated_at: now });
|
|
950
|
+
if (result.changes === 0) {
|
|
951
|
+
throw new Error(`Trigger not found: ${id}`);
|
|
952
|
+
}
|
|
953
|
+
this.db
|
|
954
|
+
.prepare(`INSERT INTO trigger_fires (trigger_id, run_id, fired_at)
|
|
955
|
+
VALUES (@trigger_id, @run_id, @fired_at)`)
|
|
956
|
+
.run({ trigger_id: id, run_id: runId, fired_at: now });
|
|
957
|
+
});
|
|
958
|
+
apply();
|
|
959
|
+
}
|
|
960
|
+
async listTriggerFires(triggerId, limit) {
|
|
961
|
+
await this.ready();
|
|
962
|
+
return this.db
|
|
963
|
+
.prepare(`SELECT fired_at, run_id FROM trigger_fires
|
|
964
|
+
WHERE trigger_id = ?
|
|
965
|
+
ORDER BY fired_at DESC, id DESC
|
|
966
|
+
LIMIT ?`)
|
|
967
|
+
.all(triggerId, limit);
|
|
952
968
|
}
|
|
953
969
|
async setTriggerNextRun(id, nextRunAt) {
|
|
954
970
|
await this.ready();
|
|
@@ -962,11 +978,15 @@ export class SqliteRunStore {
|
|
|
962
978
|
}
|
|
963
979
|
}
|
|
964
980
|
async getTriggerAdapterState(triggerId, key) {
|
|
981
|
+
const row = await this.getTriggerAdapterStateMeta(triggerId, key);
|
|
982
|
+
return row?.value ?? null;
|
|
983
|
+
}
|
|
984
|
+
async getTriggerAdapterStateMeta(triggerId, key) {
|
|
965
985
|
await this.ready();
|
|
966
986
|
const row = this.db
|
|
967
|
-
.prepare(`SELECT value FROM trigger_adapter_state WHERE trigger_id = ? AND key = ?`)
|
|
987
|
+
.prepare(`SELECT value, updated_at FROM trigger_adapter_state WHERE trigger_id = ? AND key = ?`)
|
|
968
988
|
.get(triggerId, key);
|
|
969
|
-
return row
|
|
989
|
+
return row ?? null;
|
|
970
990
|
}
|
|
971
991
|
async setTriggerAdapterState(triggerId, key, value) {
|
|
972
992
|
await this.ready();
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { PACKAGE_VERSION } from "../../../package-meta.js";
|
|
2
|
+
export const MIGRATION_012 = {
|
|
3
|
+
version: 12,
|
|
4
|
+
name: "012_trigger_fires",
|
|
5
|
+
minStageflowVersion: PACKAGE_VERSION,
|
|
6
|
+
up(db) {
|
|
7
|
+
db.exec(`
|
|
8
|
+
CREATE TABLE IF NOT EXISTS trigger_fires (
|
|
9
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
10
|
+
trigger_id TEXT NOT NULL,
|
|
11
|
+
run_id TEXT NOT NULL,
|
|
12
|
+
fired_at TEXT NOT NULL
|
|
13
|
+
);
|
|
14
|
+
CREATE INDEX IF NOT EXISTS trigger_fires_trigger_fired
|
|
15
|
+
ON trigger_fires (trigger_id, fired_at DESC);
|
|
16
|
+
`);
|
|
17
|
+
},
|
|
18
|
+
};
|
|
@@ -10,6 +10,7 @@ import { MIGRATION_008 } from "./008-triggers-table.js";
|
|
|
10
10
|
import { MIGRATION_009 } from "./009-trigger-next-run.js";
|
|
11
11
|
import { MIGRATION_010 } from "./010-trigger-adapter-state.js";
|
|
12
12
|
import { MIGRATION_011 } from "./011-email-dispatch-key.js";
|
|
13
|
+
import { MIGRATION_012 } from "./012-trigger-fires.js";
|
|
13
14
|
const DEFAULT_MIGRATIONS = [
|
|
14
15
|
MIGRATION_001,
|
|
15
16
|
MIGRATION_002,
|
|
@@ -22,6 +23,7 @@ const DEFAULT_MIGRATIONS = [
|
|
|
22
23
|
MIGRATION_009,
|
|
23
24
|
MIGRATION_010,
|
|
24
25
|
MIGRATION_011,
|
|
26
|
+
MIGRATION_012,
|
|
25
27
|
];
|
|
26
28
|
export const CURRENT_SCHEMA_VERSION = DEFAULT_MIGRATIONS[DEFAULT_MIGRATIONS.length - 1].version;
|
|
27
29
|
function throwStoreSchemaTooNew(db, version, maxKnown) {
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type StageUsage } from "../types/usage.js";
|
|
2
|
+
import type { RunStore, StageExecution, StageSnapshot } from "./port.js";
|
|
2
3
|
export declare function buildStageSnapshotFromStore(store: RunStore, runId: string, stageId: string, workspaceDir: string): Promise<StageSnapshot>;
|
|
4
|
+
export declare function mergeStageExecutionUsage(executions: StageExecution[]): StageUsage | undefined;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { addModelUsage, emptyStageUsage } from "../types/usage.js";
|
|
1
2
|
import { derivePendingPrompt } from "../hitl/qaTrail.js";
|
|
2
3
|
import { stageStatusFromEvents } from "./port.js";
|
|
3
4
|
import { listArtifactNames } from "./workspaceLayout.js";
|
|
@@ -19,6 +20,7 @@ export async function buildStageSnapshotFromStore(store, runId, stageId, workspa
|
|
|
19
20
|
let envelope;
|
|
20
21
|
let attempt_count;
|
|
21
22
|
let cost_usd;
|
|
23
|
+
let usage;
|
|
22
24
|
if (latest !== null) {
|
|
23
25
|
events = await store.listStageEvents(runId, stageId, latest.attempt);
|
|
24
26
|
status = stageStatusFromEvents(events);
|
|
@@ -26,6 +28,7 @@ export async function buildStageSnapshotFromStore(store, runId, stageId, workspa
|
|
|
26
28
|
attempt_count = await store.countStageAttempts(runId, stageId);
|
|
27
29
|
const executions = await store.listStageExecutions(runId, stageId);
|
|
28
30
|
cost_usd = sumStageExecutionCost(executions);
|
|
31
|
+
usage = mergeStageExecutionUsage(executions);
|
|
29
32
|
}
|
|
30
33
|
else {
|
|
31
34
|
events = await store.listStageEvents(runId, stageId);
|
|
@@ -46,8 +49,24 @@ export async function buildStageSnapshotFromStore(store, runId, stageId, workspa
|
|
|
46
49
|
attempt_count,
|
|
47
50
|
...(pending ? { pending_prompt: pending } : {}),
|
|
48
51
|
...(cost_usd !== undefined ? { cost_usd } : {}),
|
|
52
|
+
...(usage !== undefined ? { usage } : {}),
|
|
49
53
|
};
|
|
50
54
|
}
|
|
55
|
+
export function mergeStageExecutionUsage(executions) {
|
|
56
|
+
const usages = executions
|
|
57
|
+
.map((e) => e.usage)
|
|
58
|
+
.filter((u) => u !== undefined);
|
|
59
|
+
if (usages.length === 0)
|
|
60
|
+
return undefined;
|
|
61
|
+
const merged = emptyStageUsage();
|
|
62
|
+
for (const u of usages) {
|
|
63
|
+
for (const [model, breakdown] of Object.entries(u.models)) {
|
|
64
|
+
addModelUsage(merged, model, breakdown);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
merged.costUsd = usages.reduce((sum, u) => sum + u.costUsd, 0);
|
|
68
|
+
return merged;
|
|
69
|
+
}
|
|
51
70
|
/** Sum cost across every attempt — retries are real spend too, so a stage's total isn't just its latest attempt. */
|
|
52
71
|
function sumStageExecutionCost(executions) {
|
|
53
72
|
const costs = executions.map((e) => e.cost_usd).filter((c) => c !== undefined);
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { access, lstat, mkdir, readdir, realpath } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { globalCredentialRoot } from "../project/globalHome.js";
|
|
4
|
+
import { STAGEFLOW_AGENT_AUTH_PATH_ENV } from "../runtime/credentialBinding.js";
|
|
5
|
+
import { SF_STAGE_WORKER } from "../runtime/stageWorkerProtocol.js";
|
|
3
6
|
export { stageDir } from "./paths.js";
|
|
4
7
|
export function guardStageId(stageId) {
|
|
5
8
|
if (stageId.includes("..") ||
|
|
@@ -101,6 +104,15 @@ export async function resolveEffectiveRealPath(candidatePath) {
|
|
|
101
104
|
}
|
|
102
105
|
}
|
|
103
106
|
}
|
|
107
|
+
function credentialRootForFileToolDenial() {
|
|
108
|
+
if (process.env[SF_STAGE_WORKER] === "1") {
|
|
109
|
+
const stamped = process.env[STAGEFLOW_AGENT_AUTH_PATH_ENV]?.trim();
|
|
110
|
+
if (!stamped)
|
|
111
|
+
return undefined;
|
|
112
|
+
return path.resolve(path.dirname(stamped), "..");
|
|
113
|
+
}
|
|
114
|
+
return globalCredentialRoot();
|
|
115
|
+
}
|
|
104
116
|
export async function durableRootFileToolDenial(candidatePath, runWorkspaceDir, durableRoot, allowlistedRoots) {
|
|
105
117
|
const fileReal = await resolveEffectiveRealPath(candidatePath);
|
|
106
118
|
if (fileReal === undefined) {
|
|
@@ -137,6 +149,21 @@ export async function durableRootFileToolDenial(candidatePath, runWorkspaceDir,
|
|
|
137
149
|
if (isInsideDir(fileReal, durableReal)) {
|
|
138
150
|
return STAGEFLOW_PATH_DENIED;
|
|
139
151
|
}
|
|
152
|
+
const credentialRoot = credentialRootForFileToolDenial();
|
|
153
|
+
if (credentialRoot !== undefined) {
|
|
154
|
+
let credentialReal;
|
|
155
|
+
try {
|
|
156
|
+
credentialReal = await realpath(credentialRoot);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
credentialReal = path.resolve(credentialRoot);
|
|
160
|
+
}
|
|
161
|
+
if (credentialReal !== durableReal &&
|
|
162
|
+
!isInsideDir(credentialReal, durableReal) &&
|
|
163
|
+
isInsideDir(fileReal, credentialReal)) {
|
|
164
|
+
return STAGEFLOW_PATH_DENIED;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
140
167
|
return undefined;
|
|
141
168
|
}
|
|
142
169
|
export function resolveArtifactTarget(runWorkspaceDir, stageId, attempt, relativePath) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function writeContainedArtifact(runWorkspaceDir: string, artifactsDirectory: string, absolutePath: string, content: string): Promise<void>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomic write of one file under a stage attempt's artifacts directory.
|
|
3
|
+
* Refuses symlinked or hard-linked targets and any path that resolves outside
|
|
4
|
+
* the artifacts directory.
|
|
5
|
+
*/
|
|
6
|
+
import { randomBytes } from "node:crypto";
|
|
7
|
+
import { lstat, realpath, rename, unlink, writeFile } from "node:fs/promises";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { ensureRealArtifactsDir, isInsideDir, resolveContainedParent, } from "./workspaceLayout.js";
|
|
10
|
+
async function assertSafeWriteTarget(targetPath) {
|
|
11
|
+
try {
|
|
12
|
+
const st = await lstat(targetPath);
|
|
13
|
+
if (st.isSymbolicLink()) {
|
|
14
|
+
throw new Error("path escapes the stage artifacts directory");
|
|
15
|
+
}
|
|
16
|
+
if (st.nlink > 1) {
|
|
17
|
+
throw new Error("refusing to overwrite hard-linked file");
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
if (err.code !== "ENOENT") {
|
|
22
|
+
throw err;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export async function writeContainedArtifact(runWorkspaceDir, artifactsDirectory, absolutePath, content) {
|
|
27
|
+
const { realArtifacts } = await ensureRealArtifactsDir(runWorkspaceDir, artifactsDirectory);
|
|
28
|
+
const relativeToArtifacts = path.relative(artifactsDirectory, absolutePath);
|
|
29
|
+
const parent = await resolveContainedParent(realArtifacts, relativeToArtifacts);
|
|
30
|
+
const baseName = path.basename(absolutePath);
|
|
31
|
+
const targetPath = path.join(parent, baseName);
|
|
32
|
+
await assertSafeWriteTarget(targetPath);
|
|
33
|
+
const tmpName = `.sf-artifact-${randomBytes(8).toString("hex")}.tmp`;
|
|
34
|
+
const tmpPath = path.join(parent, tmpName);
|
|
35
|
+
try {
|
|
36
|
+
await writeFile(tmpPath, content, "utf8");
|
|
37
|
+
await rename(tmpPath, targetPath);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
try {
|
|
41
|
+
await unlink(tmpPath);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// best-effort cleanup
|
|
45
|
+
}
|
|
46
|
+
throw err;
|
|
47
|
+
}
|
|
48
|
+
const realWritten = await realpath(targetPath);
|
|
49
|
+
if (!isInsideDir(realWritten, realArtifacts)) {
|
|
50
|
+
throw new Error("path escapes the stage artifacts directory");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { chmodSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { ensureGlobalHome } from "../project/globalHome.js";
|
|
4
|
+
import { ensureGlobalHome, globalCredentialRoot } from "../project/globalHome.js";
|
|
5
5
|
import { resolveProjectContext, } from "../project/resolveProjectContext.js";
|
|
6
6
|
import { parseCredentialSource, readCredentialSourceFromContext, writeCredentialSourceToContext, } from "./settingsFile.js";
|
|
7
7
|
export { parseCredentialSource, readCredentialSourceFromContext, writeCredentialSourceToContext, };
|
|
8
8
|
export function sfOwnedAgentDir() {
|
|
9
|
-
return path.join(
|
|
9
|
+
return path.join(globalCredentialRoot(), "agent");
|
|
10
10
|
}
|
|
11
11
|
export function sfOwnedAuthPath() {
|
|
12
12
|
return path.join(sfOwnedAgentDir(), "auth.json");
|
|
@@ -42,7 +42,15 @@ export function isUsableAuthFile(authPath) {
|
|
|
42
42
|
}
|
|
43
43
|
export function ensureSfOwnedAuthStore() {
|
|
44
44
|
ensureGlobalHome();
|
|
45
|
-
const
|
|
45
|
+
const agentDir = sfOwnedAgentDir();
|
|
46
|
+
mkdirSync(agentDir, { recursive: true });
|
|
47
|
+
try {
|
|
48
|
+
chmodSync(agentDir, 0o700);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// best-effort on non-POSIX
|
|
52
|
+
}
|
|
53
|
+
const authPath = path.join(agentDir, "auth.json");
|
|
46
54
|
if (!existsSync(authPath)) {
|
|
47
55
|
writeFileSync(authPath, "{}\n", { encoding: "utf8", mode: 0o600 });
|
|
48
56
|
}
|
|
@@ -63,12 +71,12 @@ export function currentExplicitAuthPath() {
|
|
|
63
71
|
return explicitAuthPath.getStore();
|
|
64
72
|
}
|
|
65
73
|
export function resolveCredentialBinding(ctx, _options = {}) {
|
|
74
|
+
const explicit = currentExplicitAuthPath();
|
|
66
75
|
const projectCtx = typeof ctx === "string" ? resolveProjectContext(ctx) : ctx;
|
|
67
76
|
const persisted = readCredentialSourceFromContext(projectCtx);
|
|
68
|
-
const authPath = ensureSfOwnedAuthStore();
|
|
69
77
|
return {
|
|
70
78
|
source: "sf_owned",
|
|
71
|
-
authPath,
|
|
79
|
+
authPath: explicit ?? sfOwnedAuthPath(),
|
|
72
80
|
provisional: persisted === undefined,
|
|
73
81
|
};
|
|
74
82
|
}
|