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
|
@@ -1,10 +1,49 @@
|
|
|
1
|
+
import { type ResolveCatalogRootsOptions } from "./resolveCatalogRoots.js";
|
|
1
2
|
import type { RunStore } from "../runstore/port.js";
|
|
2
3
|
import type { TriggerFile } from "../types/trigger.js";
|
|
4
|
+
import type { TriggerRecord } from "../runstore/port.js";
|
|
3
5
|
export type TriggerListItem = TriggerFile & {
|
|
4
6
|
definition_ref: string;
|
|
7
|
+
project_root: string;
|
|
5
8
|
last_fired_at?: string;
|
|
6
9
|
last_run_id?: string;
|
|
7
10
|
next_run_at?: string;
|
|
8
11
|
};
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
export type CatalogTriggerMatch = {
|
|
13
|
+
definition: TriggerFile;
|
|
14
|
+
path: string;
|
|
15
|
+
project_root: string;
|
|
16
|
+
rootPath: string;
|
|
17
|
+
definition_ref: string;
|
|
18
|
+
};
|
|
19
|
+
/** Store row id namespaced by catalog wire root so two roots can share a trigger id. */
|
|
20
|
+
export declare function triggerStoreId(wireProjectRoot: string, catalogId: string): string;
|
|
21
|
+
export declare function readTriggerStoreRecord(store: RunStore, wireProjectRoot: string, catalogId: string): Promise<TriggerRecord | null>;
|
|
22
|
+
export declare function listCatalogTriggerMatches(options: ResolveCatalogRootsOptions & {
|
|
23
|
+
sourceRootsOnly?: boolean;
|
|
24
|
+
}): Promise<CatalogTriggerMatch[]>;
|
|
25
|
+
export type FindCatalogTriggerResult = {
|
|
26
|
+
ok: true;
|
|
27
|
+
match: CatalogTriggerMatch;
|
|
28
|
+
} | {
|
|
29
|
+
ok: false;
|
|
30
|
+
status: 404;
|
|
31
|
+
reason: string;
|
|
32
|
+
} | {
|
|
33
|
+
ok: false;
|
|
34
|
+
status: 409;
|
|
35
|
+
reason: string;
|
|
36
|
+
code: "ambiguous_trigger";
|
|
37
|
+
project_roots: string[];
|
|
38
|
+
} | {
|
|
39
|
+
ok: false;
|
|
40
|
+
status: 400;
|
|
41
|
+
reason: string;
|
|
42
|
+
code: "unknown_project_root";
|
|
43
|
+
};
|
|
44
|
+
export declare function findCatalogTrigger(options: ResolveCatalogRootsOptions & {
|
|
45
|
+
triggerId: string;
|
|
46
|
+
projectRootFilter?: string;
|
|
47
|
+
}): Promise<FindCatalogTriggerResult>;
|
|
48
|
+
/** Catalog-authoritative trigger list across all roots, overlaid with store fire state. */
|
|
49
|
+
export declare function loadCatalogTriggers(options: ResolveCatalogRootsOptions): Promise<TriggerListItem[]>;
|
|
@@ -1,27 +1,114 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { catalogContextFromStageflow } from "./resolveCatalogContext.js";
|
|
3
2
|
import { getCatalogScanPaths } from "./browseCatalog.js";
|
|
4
|
-
import {
|
|
3
|
+
import { loadStageflowManifestOutcome } from "./loadStageflowManifest.js";
|
|
5
4
|
import { loadTriggerOutcome } from "./loadTrigger.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
import { isRootReadable, resolveCatalogRoots, findCatalogRoot, } from "./resolveCatalogRoots.js";
|
|
6
|
+
const TRIGGER_STORE_SEP = "::";
|
|
7
|
+
/** Store row id namespaced by catalog wire root so two roots can share a trigger id. */
|
|
8
|
+
export function triggerStoreId(wireProjectRoot, catalogId) {
|
|
9
|
+
return `${wireProjectRoot}${TRIGGER_STORE_SEP}${catalogId}`;
|
|
10
|
+
}
|
|
11
|
+
export async function readTriggerStoreRecord(store, wireProjectRoot, catalogId) {
|
|
12
|
+
const namespaced = await store.getTrigger(triggerStoreId(wireProjectRoot, catalogId));
|
|
13
|
+
if (namespaced)
|
|
14
|
+
return namespaced;
|
|
15
|
+
return store.getTrigger(catalogId);
|
|
16
|
+
}
|
|
17
|
+
async function catalogContextForRoot(root) {
|
|
18
|
+
const outcome = await loadStageflowManifestOutcome(root.path);
|
|
19
|
+
if (!outcome.ok)
|
|
20
|
+
return null;
|
|
21
|
+
return {
|
|
22
|
+
projectRoot: root.path,
|
|
23
|
+
manifest: outcome.value,
|
|
24
|
+
manifestStatus: "ok",
|
|
25
|
+
issues: [],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
async function triggersInRoot(root) {
|
|
29
|
+
if (root.available === false)
|
|
30
|
+
return [];
|
|
31
|
+
if (!(await isRootReadable(root.path)))
|
|
32
|
+
return [];
|
|
33
|
+
const ctx = await catalogContextForRoot(root);
|
|
34
|
+
if (!ctx)
|
|
35
|
+
return [];
|
|
9
36
|
const scanPaths = await getCatalogScanPaths(ctx);
|
|
10
37
|
if (!scanPaths)
|
|
11
|
-
return
|
|
12
|
-
const
|
|
13
|
-
const items = [];
|
|
38
|
+
return [];
|
|
39
|
+
const matches = [];
|
|
14
40
|
for (const filePath of scanPaths.triggerPaths) {
|
|
15
41
|
const outcome = await loadTriggerOutcome(filePath);
|
|
16
42
|
if (!outcome.ok)
|
|
17
43
|
continue;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
: filePath
|
|
21
|
-
|
|
44
|
+
matches.push({
|
|
45
|
+
definition: outcome.value,
|
|
46
|
+
path: filePath,
|
|
47
|
+
project_root: root.project_root,
|
|
48
|
+
rootPath: root.path,
|
|
49
|
+
definition_ref: path.relative(root.path, filePath).replace(/\\/g, "/"),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return matches;
|
|
53
|
+
}
|
|
54
|
+
export async function listCatalogTriggerMatches(options) {
|
|
55
|
+
const roots = await resolveCatalogRoots(options);
|
|
56
|
+
const selected = options.sourceRootsOnly
|
|
57
|
+
? roots.filter((r) => r.kind !== "seeded")
|
|
58
|
+
: roots;
|
|
59
|
+
const matches = [];
|
|
60
|
+
for (const root of selected) {
|
|
61
|
+
matches.push(...(await triggersInRoot(root)));
|
|
62
|
+
}
|
|
63
|
+
return matches;
|
|
64
|
+
}
|
|
65
|
+
export async function findCatalogTrigger(options) {
|
|
66
|
+
const roots = await resolveCatalogRoots(options);
|
|
67
|
+
if (options.projectRootFilter !== undefined) {
|
|
68
|
+
const match = findCatalogRoot(roots, options.projectRootFilter);
|
|
69
|
+
if (match === undefined) {
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
status: 400,
|
|
73
|
+
reason: `Unknown project_root: ${options.projectRootFilter}`,
|
|
74
|
+
code: "unknown_project_root",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const inRoot = await triggersInRoot(match);
|
|
78
|
+
const found = inRoot.find((t) => t.definition.id === options.triggerId);
|
|
79
|
+
if (!found) {
|
|
80
|
+
return { ok: false, status: 404, reason: `Trigger not found: ${options.triggerId}` };
|
|
81
|
+
}
|
|
82
|
+
return { ok: true, match: found };
|
|
83
|
+
}
|
|
84
|
+
const all = await listCatalogTriggerMatches(options);
|
|
85
|
+
const matches = all.filter((m) => m.definition.id === options.triggerId);
|
|
86
|
+
if (matches.length === 0) {
|
|
87
|
+
return { ok: false, status: 404, reason: `Trigger not found: ${options.triggerId}` };
|
|
88
|
+
}
|
|
89
|
+
const projectRoots = [...new Set(matches.map((m) => m.project_root))];
|
|
90
|
+
if (projectRoots.length > 1) {
|
|
91
|
+
return {
|
|
92
|
+
ok: false,
|
|
93
|
+
status: 409,
|
|
94
|
+
reason: `Trigger id is ambiguous across catalog roots: ${options.triggerId}`,
|
|
95
|
+
code: "ambiguous_trigger",
|
|
96
|
+
project_roots: projectRoots.sort((a, b) => a.localeCompare(b)),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return { ok: true, match: matches[0] };
|
|
100
|
+
}
|
|
101
|
+
/** Catalog-authoritative trigger list across all roots, overlaid with store fire state. */
|
|
102
|
+
export async function loadCatalogTriggers(options) {
|
|
103
|
+
const matches = await listCatalogTriggerMatches(options);
|
|
104
|
+
const store = options.store;
|
|
105
|
+
const items = [];
|
|
106
|
+
for (const match of matches) {
|
|
107
|
+
const record = await readTriggerStoreRecord(store, match.project_root, match.definition.id);
|
|
22
108
|
items.push({
|
|
23
|
-
...
|
|
24
|
-
|
|
109
|
+
...match.definition,
|
|
110
|
+
project_root: match.project_root,
|
|
111
|
+
definition_ref: match.definition_ref,
|
|
25
112
|
...(record?.last_fired_at !== undefined
|
|
26
113
|
? { last_fired_at: record.last_fired_at }
|
|
27
114
|
: {}),
|
|
@@ -33,5 +120,10 @@ export async function loadCatalogTriggers(cwd, store) {
|
|
|
33
120
|
: {}),
|
|
34
121
|
});
|
|
35
122
|
}
|
|
36
|
-
return items.sort((a, b) =>
|
|
123
|
+
return items.sort((a, b) => {
|
|
124
|
+
const rootCmp = a.project_root.localeCompare(b.project_root);
|
|
125
|
+
if (rootCmp !== 0)
|
|
126
|
+
return rootCmp;
|
|
127
|
+
return a.id.localeCompare(b.id);
|
|
128
|
+
});
|
|
37
129
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type TaskDetailWithRoot } from "./findTaskInCatalog.js";
|
|
2
|
+
import { type ResolveCatalogRootsOptions } from "./resolveCatalogRoots.js";
|
|
3
|
+
declare const OPTIONAL_KEYS: readonly ["context", "constraints", "checkout", "repository", "ref"];
|
|
4
|
+
type OptionalKey = (typeof OPTIONAL_KEYS)[number];
|
|
5
|
+
export type UpdateTaskInput = {
|
|
6
|
+
goal: string;
|
|
7
|
+
optional: Partial<Record<OptionalKey, string | null>>;
|
|
8
|
+
};
|
|
9
|
+
export type UpdateTaskParseError = {
|
|
10
|
+
ok: false;
|
|
11
|
+
status: 400;
|
|
12
|
+
error: string;
|
|
13
|
+
};
|
|
14
|
+
export type UpdateTaskResult = {
|
|
15
|
+
ok: true;
|
|
16
|
+
task: TaskDetailWithRoot;
|
|
17
|
+
} | {
|
|
18
|
+
ok: false;
|
|
19
|
+
status: 400 | 404 | 409 | 500;
|
|
20
|
+
error: string;
|
|
21
|
+
code?: "unknown_project_root" | "ambiguous_task";
|
|
22
|
+
project_roots?: string[];
|
|
23
|
+
};
|
|
24
|
+
export declare function parseUpdateTaskBody(body: unknown): UpdateTaskInput | UpdateTaskParseError;
|
|
25
|
+
export declare function updateTask(options: ResolveCatalogRootsOptions & {
|
|
26
|
+
taskId: string;
|
|
27
|
+
input: UpdateTaskInput;
|
|
28
|
+
projectRootFilter?: string;
|
|
29
|
+
}): Promise<UpdateTaskResult>;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parseDocument } from "yaml";
|
|
4
|
+
import { findTaskInCatalogMulti, findTaskInProjectRoot, } from "./findTaskInCatalog.js";
|
|
5
|
+
import { loadTaskOutcome } from "./loadTask.js";
|
|
6
|
+
import { findCatalogRoot, resolveCatalogRoots, } from "./resolveCatalogRoots.js";
|
|
7
|
+
const OPTIONAL_KEYS = ["context", "constraints", "checkout", "repository", "ref"];
|
|
8
|
+
function isPlainObject(value) {
|
|
9
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
export function parseUpdateTaskBody(body) {
|
|
12
|
+
if (!isPlainObject(body)) {
|
|
13
|
+
return { ok: false, status: 400, error: "Request body must be an object" };
|
|
14
|
+
}
|
|
15
|
+
if (typeof body.goal !== "string" || body.goal.length === 0) {
|
|
16
|
+
return { ok: false, status: 400, error: "goal is required" };
|
|
17
|
+
}
|
|
18
|
+
const optional = {};
|
|
19
|
+
for (const key of OPTIONAL_KEYS) {
|
|
20
|
+
if (!Object.hasOwn(body, key))
|
|
21
|
+
continue;
|
|
22
|
+
const value = body[key];
|
|
23
|
+
if (value === null || value === "") {
|
|
24
|
+
optional[key] = null;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (typeof value !== "string") {
|
|
28
|
+
return { ok: false, status: 400, error: `${key} must be a string` };
|
|
29
|
+
}
|
|
30
|
+
optional[key] = value;
|
|
31
|
+
}
|
|
32
|
+
return { goal: body.goal, optional };
|
|
33
|
+
}
|
|
34
|
+
export async function updateTask(options) {
|
|
35
|
+
const lookup = await findTaskInCatalogMulti({
|
|
36
|
+
...options,
|
|
37
|
+
projectRootFilter: options.projectRootFilter,
|
|
38
|
+
});
|
|
39
|
+
if (!lookup.ok) {
|
|
40
|
+
if (lookup.status === 409) {
|
|
41
|
+
return {
|
|
42
|
+
ok: false,
|
|
43
|
+
status: 409,
|
|
44
|
+
error: lookup.error,
|
|
45
|
+
code: lookup.code,
|
|
46
|
+
project_roots: lookup.project_roots,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (lookup.status === 400) {
|
|
50
|
+
return {
|
|
51
|
+
ok: false,
|
|
52
|
+
status: 400,
|
|
53
|
+
error: lookup.error,
|
|
54
|
+
code: lookup.code,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return { ok: false, status: 404, error: lookup.error };
|
|
58
|
+
}
|
|
59
|
+
const roots = await resolveCatalogRoots(options);
|
|
60
|
+
const catalogRoot = findCatalogRoot(roots, lookup.task.project_root);
|
|
61
|
+
if (catalogRoot === undefined) {
|
|
62
|
+
return { ok: false, status: 404, error: `Task not found: ${options.taskId}` };
|
|
63
|
+
}
|
|
64
|
+
const filePath = path.resolve(catalogRoot.path, lookup.task.path);
|
|
65
|
+
let original;
|
|
66
|
+
try {
|
|
67
|
+
original = await readFile(filePath, "utf8");
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
71
|
+
return { ok: false, status: 500, error: message };
|
|
72
|
+
}
|
|
73
|
+
const doc = parseDocument(original);
|
|
74
|
+
if (!doc.contents || typeof doc.contents !== "object") {
|
|
75
|
+
return { ok: false, status: 500, error: "Task YAML must be an object" };
|
|
76
|
+
}
|
|
77
|
+
doc.set("goal", options.input.goal);
|
|
78
|
+
for (const key of OPTIONAL_KEYS) {
|
|
79
|
+
if (!Object.hasOwn(options.input.optional, key))
|
|
80
|
+
continue;
|
|
81
|
+
const value = options.input.optional[key];
|
|
82
|
+
if (value === null || value === "")
|
|
83
|
+
doc.delete(key);
|
|
84
|
+
else
|
|
85
|
+
doc.set(key, value);
|
|
86
|
+
}
|
|
87
|
+
const patched = doc.toString();
|
|
88
|
+
const text = patched.endsWith("\n") ? patched : `${patched}\n`;
|
|
89
|
+
try {
|
|
90
|
+
await writeFile(filePath, text, "utf8");
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
94
|
+
return { ok: false, status: 500, error: message };
|
|
95
|
+
}
|
|
96
|
+
const outcome = await loadTaskOutcome(filePath);
|
|
97
|
+
if (!outcome.ok) {
|
|
98
|
+
try {
|
|
99
|
+
await writeFile(filePath, original, "utf8");
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
103
|
+
return { ok: false, status: 500, error: message };
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
status: 400,
|
|
108
|
+
error: outcome.issues[0]?.message ?? "Invalid task",
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const task = await findTaskInProjectRoot(catalogRoot.path, options.taskId);
|
|
112
|
+
if (!task) {
|
|
113
|
+
return { ok: false, status: 500, error: "Task updated but not found" };
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
ok: true,
|
|
117
|
+
task: { ...task, project_root: lookup.task.project_root },
|
|
118
|
+
};
|
|
119
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { RunStore } from "../runstore/port.js";
|
|
2
|
+
import type { TriggerEvent, TriggerSchedule } from "../types/trigger.js";
|
|
3
|
+
import { type TriggerListItem } from "./triggerCatalog.js";
|
|
4
|
+
export type PatchTriggerInput = {
|
|
5
|
+
enabled: boolean;
|
|
6
|
+
};
|
|
7
|
+
export type PatchTriggerParseError = {
|
|
8
|
+
ok: false;
|
|
9
|
+
status: 400;
|
|
10
|
+
error: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function parsePatchTriggerBody(body: unknown): PatchTriggerInput | PatchTriggerParseError;
|
|
13
|
+
export type PutTriggerInput = {
|
|
14
|
+
pipeline: string;
|
|
15
|
+
task?: string;
|
|
16
|
+
kind: "manual" | "schedule" | "event";
|
|
17
|
+
schedule?: TriggerSchedule;
|
|
18
|
+
event?: TriggerEvent;
|
|
19
|
+
enabled: boolean;
|
|
20
|
+
};
|
|
21
|
+
export declare function parsePutTriggerBody(body: unknown, triggerId: string): PutTriggerInput | PatchTriggerParseError;
|
|
22
|
+
export type UpdateTriggerResult = {
|
|
23
|
+
ok: true;
|
|
24
|
+
trigger: TriggerListItem;
|
|
25
|
+
} | {
|
|
26
|
+
ok: false;
|
|
27
|
+
status: 403 | 404 | 409 | 400 | 422 | 500;
|
|
28
|
+
error: string;
|
|
29
|
+
project_roots?: string[];
|
|
30
|
+
};
|
|
31
|
+
declare function projectRootFilterFromBody(body: unknown): string | undefined;
|
|
32
|
+
export declare function updateTriggerEnabled(cwd: string, store: RunStore, triggerId: string, enabled: boolean, options?: {
|
|
33
|
+
projectRootFilter?: string;
|
|
34
|
+
}): Promise<UpdateTriggerResult>;
|
|
35
|
+
export declare function updateTriggerDefinition(cwd: string, store: RunStore, triggerId: string, input: PutTriggerInput, options?: {
|
|
36
|
+
projectRootFilter?: string;
|
|
37
|
+
}): Promise<UpdateTriggerResult>;
|
|
38
|
+
export { projectRootFilterFromBody };
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { parseDocument } from "yaml";
|
|
3
|
+
import { findCatalogTrigger, loadCatalogTriggers, triggerStoreId, } from "./triggerCatalog.js";
|
|
4
|
+
import { resolveCatalogRoots, findCatalogRoot } from "./resolveCatalogRoots.js";
|
|
5
|
+
import { loadTriggerFromYamlOutcome } from "./loadTrigger.js";
|
|
6
|
+
import { catalogContextFromStageflow } from "./resolveCatalogContext.js";
|
|
7
|
+
import { getCatalogScanPaths } from "./browseCatalog.js";
|
|
8
|
+
import { resolveStageflowContext } from "../project/resolveStageflowContext.js";
|
|
9
|
+
import { parseEventField, parseScheduleField, scheduleCronError, triggerReferenceError, } from "./createTrigger.js";
|
|
10
|
+
export function parsePatchTriggerBody(body) {
|
|
11
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
12
|
+
return { ok: false, status: 400, error: "Request body must be an object" };
|
|
13
|
+
}
|
|
14
|
+
const record = body;
|
|
15
|
+
const keys = Object.keys(record);
|
|
16
|
+
if (keys.length !== 1 || keys[0] !== "enabled") {
|
|
17
|
+
return {
|
|
18
|
+
ok: false,
|
|
19
|
+
status: 400,
|
|
20
|
+
error: "Only enabled is allowed in the request body",
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
if (typeof record.enabled !== "boolean") {
|
|
24
|
+
return { ok: false, status: 400, error: "enabled must be a boolean" };
|
|
25
|
+
}
|
|
26
|
+
return { enabled: record.enabled };
|
|
27
|
+
}
|
|
28
|
+
function isPlainObject(value) {
|
|
29
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
30
|
+
}
|
|
31
|
+
export function parsePutTriggerBody(body, triggerId) {
|
|
32
|
+
if (!isPlainObject(body)) {
|
|
33
|
+
return { ok: false, status: 400, error: "Request body must be an object" };
|
|
34
|
+
}
|
|
35
|
+
if ("id" in body && body.id !== triggerId) {
|
|
36
|
+
return { ok: false, status: 400, error: "id cannot be changed" };
|
|
37
|
+
}
|
|
38
|
+
if (typeof body.pipeline !== "string" || !body.pipeline.trim()) {
|
|
39
|
+
return { ok: false, status: 400, error: "pipeline is required" };
|
|
40
|
+
}
|
|
41
|
+
if (body.task !== undefined &&
|
|
42
|
+
body.task !== null &&
|
|
43
|
+
(typeof body.task !== "string" || !body.task.trim())) {
|
|
44
|
+
return { ok: false, status: 400, error: "task must be a non-empty string" };
|
|
45
|
+
}
|
|
46
|
+
if (body.kind !== "manual" && body.kind !== "schedule" && body.kind !== "event") {
|
|
47
|
+
return {
|
|
48
|
+
ok: false,
|
|
49
|
+
status: 400,
|
|
50
|
+
error: "kind must be one of: manual, schedule, event",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const scheduleRaw = body.kind === "schedule" ? (body.schedule === null ? undefined : body.schedule) : undefined;
|
|
54
|
+
const eventRaw = body.kind === "event" ? (body.event === null ? undefined : body.event) : undefined;
|
|
55
|
+
const schedule = parseScheduleField(scheduleRaw);
|
|
56
|
+
if (!schedule.ok) {
|
|
57
|
+
return { ok: false, status: 400, error: schedule.message };
|
|
58
|
+
}
|
|
59
|
+
const event = parseEventField(eventRaw);
|
|
60
|
+
if (!event.ok) {
|
|
61
|
+
return { ok: false, status: 400, error: event.message };
|
|
62
|
+
}
|
|
63
|
+
if (body.kind === "schedule" && schedule.value === undefined) {
|
|
64
|
+
return { ok: false, status: 400, error: "schedule.cron is required for kind=schedule" };
|
|
65
|
+
}
|
|
66
|
+
if (body.kind === "event" && event.value === undefined) {
|
|
67
|
+
return { ok: false, status: 400, error: "event.source is required for kind=event" };
|
|
68
|
+
}
|
|
69
|
+
if (typeof body.enabled !== "boolean") {
|
|
70
|
+
return { ok: false, status: 400, error: "enabled must be a boolean" };
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
pipeline: body.pipeline,
|
|
74
|
+
...(typeof body.task === "string" ? { task: body.task } : {}),
|
|
75
|
+
kind: body.kind,
|
|
76
|
+
...(schedule.value !== undefined ? { schedule: schedule.value } : {}),
|
|
77
|
+
...(event.value !== undefined ? { event: event.value } : {}),
|
|
78
|
+
enabled: body.enabled,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function projectRootFilterFromBody(body) {
|
|
82
|
+
if (!body || typeof body !== "object" || Array.isArray(body))
|
|
83
|
+
return undefined;
|
|
84
|
+
const wire = body.project_root;
|
|
85
|
+
return typeof wire === "string" && wire.trim().length > 0 ? wire.trim() : undefined;
|
|
86
|
+
}
|
|
87
|
+
async function locateWritableTrigger(cwd, store, triggerId, projectRootFilter) {
|
|
88
|
+
const located = await findCatalogTrigger({
|
|
89
|
+
store,
|
|
90
|
+
bootCwd: cwd,
|
|
91
|
+
triggerId,
|
|
92
|
+
projectRootFilter,
|
|
93
|
+
});
|
|
94
|
+
if (!located.ok) {
|
|
95
|
+
return {
|
|
96
|
+
ok: false,
|
|
97
|
+
status: located.status,
|
|
98
|
+
error: located.reason,
|
|
99
|
+
...(located.status === 409 ? { project_roots: located.project_roots } : {}),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const roots = await resolveCatalogRoots({ store, bootCwd: cwd });
|
|
103
|
+
const root = findCatalogRoot(roots, located.match.project_root);
|
|
104
|
+
if (root?.read_only) {
|
|
105
|
+
return { ok: false, status: 403, error: "catalog_root_read_only" };
|
|
106
|
+
}
|
|
107
|
+
return { ok: true, match: located.match };
|
|
108
|
+
}
|
|
109
|
+
export async function updateTriggerEnabled(cwd, store, triggerId, enabled, options = {}) {
|
|
110
|
+
const located = await locateWritableTrigger(cwd, store, triggerId, options.projectRootFilter);
|
|
111
|
+
if (!located.ok) {
|
|
112
|
+
return located;
|
|
113
|
+
}
|
|
114
|
+
const { match } = located;
|
|
115
|
+
const yamlText = await readFile(match.path, "utf8");
|
|
116
|
+
const doc = parseDocument(yamlText);
|
|
117
|
+
if (!doc.contents || typeof doc.contents !== "object") {
|
|
118
|
+
return { ok: false, status: 500, error: "Trigger YAML must be an object" };
|
|
119
|
+
}
|
|
120
|
+
doc.set("enabled", enabled);
|
|
121
|
+
const patched = doc.toString();
|
|
122
|
+
const loadOutcome = loadTriggerFromYamlOutcome(patched, match.path);
|
|
123
|
+
if (!loadOutcome.ok) {
|
|
124
|
+
return {
|
|
125
|
+
ok: false,
|
|
126
|
+
status: 400,
|
|
127
|
+
error: loadOutcome.issues[0]?.message ?? "Invalid trigger after patch",
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
await writeFile(match.path, patched, "utf8");
|
|
131
|
+
await store.upsertTrigger({
|
|
132
|
+
id: triggerStoreId(match.project_root, loadOutcome.value.id),
|
|
133
|
+
definitionRef: match.definition_ref,
|
|
134
|
+
enabled: loadOutcome.value.enabled,
|
|
135
|
+
});
|
|
136
|
+
const items = await loadCatalogTriggers({ store, bootCwd: cwd });
|
|
137
|
+
const item = items.find((row) => row.id === triggerId && row.project_root === match.project_root);
|
|
138
|
+
if (!item) {
|
|
139
|
+
return { ok: false, status: 500, error: "Trigger updated but not listed" };
|
|
140
|
+
}
|
|
141
|
+
return { ok: true, trigger: item };
|
|
142
|
+
}
|
|
143
|
+
export async function updateTriggerDefinition(cwd, store, triggerId, input, options = {}) {
|
|
144
|
+
const located = await locateWritableTrigger(cwd, store, triggerId, options.projectRootFilter);
|
|
145
|
+
if (!located.ok) {
|
|
146
|
+
return located;
|
|
147
|
+
}
|
|
148
|
+
const { match } = located;
|
|
149
|
+
const ctx = catalogContextFromStageflow(await resolveStageflowContext(match.rootPath));
|
|
150
|
+
const scanPaths = await getCatalogScanPaths(ctx);
|
|
151
|
+
if (!scanPaths) {
|
|
152
|
+
return { ok: false, status: 404, error: "No Stageflow catalog found" };
|
|
153
|
+
}
|
|
154
|
+
const refError = await triggerReferenceError(scanPaths, input.pipeline, input.task);
|
|
155
|
+
if (refError) {
|
|
156
|
+
return { ok: false, status: 422, error: refError };
|
|
157
|
+
}
|
|
158
|
+
if (input.kind === "schedule") {
|
|
159
|
+
if (!input.schedule) {
|
|
160
|
+
return { ok: false, status: 422, error: "schedule.cron is required for kind=schedule" };
|
|
161
|
+
}
|
|
162
|
+
const cronError = scheduleCronError(input.schedule);
|
|
163
|
+
if (cronError) {
|
|
164
|
+
return { ok: false, status: 422, error: cronError };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const yamlText = await readFile(match.path, "utf8");
|
|
168
|
+
const doc = parseDocument(yamlText);
|
|
169
|
+
if (!doc.contents || typeof doc.contents !== "object") {
|
|
170
|
+
return { ok: false, status: 500, error: "Trigger YAML must be an object" };
|
|
171
|
+
}
|
|
172
|
+
doc.set("pipeline", input.pipeline);
|
|
173
|
+
if (input.task !== undefined)
|
|
174
|
+
doc.set("task", input.task);
|
|
175
|
+
else
|
|
176
|
+
doc.delete("task");
|
|
177
|
+
doc.set("kind", input.kind);
|
|
178
|
+
if (input.kind === "schedule" && input.schedule) {
|
|
179
|
+
doc.delete("event");
|
|
180
|
+
doc.set("schedule", {
|
|
181
|
+
cron: input.schedule.cron,
|
|
182
|
+
...(input.schedule.timezone !== undefined ? { timezone: input.schedule.timezone } : {}),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
else if (input.kind === "event" && input.event) {
|
|
186
|
+
doc.delete("schedule");
|
|
187
|
+
doc.set("event", {
|
|
188
|
+
source: input.event.source,
|
|
189
|
+
...(input.event.match !== undefined ? { match: input.event.match } : {}),
|
|
190
|
+
...(input.event.config !== undefined ? { config: input.event.config } : {}),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
doc.delete("schedule");
|
|
195
|
+
doc.delete("event");
|
|
196
|
+
}
|
|
197
|
+
doc.set("enabled", input.enabled);
|
|
198
|
+
const patched = doc.toString();
|
|
199
|
+
const loadOutcome = loadTriggerFromYamlOutcome(patched, match.path);
|
|
200
|
+
if (!loadOutcome.ok) {
|
|
201
|
+
return {
|
|
202
|
+
ok: false,
|
|
203
|
+
status: 400,
|
|
204
|
+
error: loadOutcome.issues[0]?.message ?? "Invalid trigger after update",
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
await writeFile(match.path, patched, "utf8");
|
|
208
|
+
await store.upsertTrigger({
|
|
209
|
+
id: triggerStoreId(match.project_root, loadOutcome.value.id),
|
|
210
|
+
definitionRef: match.definition_ref,
|
|
211
|
+
enabled: loadOutcome.value.enabled,
|
|
212
|
+
});
|
|
213
|
+
const items = await loadCatalogTriggers({ store, bootCwd: cwd });
|
|
214
|
+
const item = items.find((row) => row.id === triggerId && row.project_root === match.project_root);
|
|
215
|
+
if (!item) {
|
|
216
|
+
return { ok: false, status: 500, error: "Trigger updated but not listed" };
|
|
217
|
+
}
|
|
218
|
+
return { ok: true, trigger: item };
|
|
219
|
+
}
|
|
220
|
+
export { projectRootFilterFromBody };
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { InlinePipelineDefinition, LoadedPipeline } from "../types/pipeline.js";
|
|
2
2
|
import type { TriggerFile } from "../types/trigger.js";
|
|
3
3
|
import type { LoadIssue } from "./loadOutcome.js";
|
|
4
|
+
import type { AnyChannelProvider } from "../channels/types.js";
|
|
4
5
|
export type ValidationSeverity = "error" | "warning";
|
|
5
6
|
export type ValidationCategory = "pipeline" | "stage" | "catalog" | "task" | "trigger";
|
|
6
|
-
export type ValidationFindingCode = "pipeline.invalid_shape" | "pipeline.dag_error" | "pipeline.missing_stage" | "pipeline.load_error" | "pipeline.string_stage_ref" | "pipeline.stage_uses_inline_conflict" | "pipeline.stage_missing_body" | "pipeline.stage_id_mismatch" | "pipeline.invalid_completion" | "catalog.path_case_mismatch" | "pipeline.invalid_recovery" | "pipeline.include_cycle" | "pipeline.include_invalid" | "pipeline.include_duplicate_stage" | "pipeline.invalid_agent" | "pipeline.invalid_model" | "pipeline.invalid_verify" | "pipeline.io_incompatible" | "pipeline.model_applies" | "pipeline.route_if_invalid" | "pipeline.route_all_gated" | "pipeline.invalid_requires" | "pipeline.requires_conflict" | "stage.invalid_shape" | "stage.invalid_model" | "stage.missing_model" | "stage.invalid_payload_schema" | "stage.invalid_clone_input_schema" | "stage.unresolved_schema_ref" | "stage.invalid_clone_actions" | "stage.invalid_gate_kinds" | "stage.invalid_pre_emit_checks" | "stage.invalid_timeout_ms" | "stage.invalid_skill" | "stage.invalid_mcp" | "stage.invalid_browser" | "stage.invalid_secrets" | "stage.unknown_secret" | "stage.denied_secret" | "stage.invalid_requires" | "stage.invalid_agent" | "stage.invalid_io" | "stage.load_error" | "stage.id_filename_mismatch" | "task.invalid_shape" | "task.load_error" | "task.entry_input_unmet" | "task.binding_conflict" | "task.repository_ref_required" | "task.ref_without_repository" | "task.repository_invalid" | "trigger.invalid_shape" | "trigger.load_error" | "trigger.unknown_pipeline" | "trigger.unknown_task" | "catalog.duplicate_pipeline_id" | "catalog.manifest_missing" | "catalog.manifest_invalid" | "catalog.empty_catalog" | "catalog.manifest_load_error" | "catalog.invalid_mcp" | "catalog.mcp_command_missing" | "catalog.mixed_yaml_dialect" | "catalog.legacy_yaml" | "catalog.stageflow_home_absolute_path";
|
|
7
|
+
export type ValidationFindingCode = "pipeline.invalid_shape" | "pipeline.dag_error" | "pipeline.missing_stage" | "pipeline.load_error" | "pipeline.string_stage_ref" | "pipeline.stage_uses_inline_conflict" | "pipeline.stage_missing_body" | "pipeline.stage_id_mismatch" | "pipeline.stage_wrapper_skill_rejected" | "pipeline.invalid_completion" | "catalog.path_case_mismatch" | "pipeline.invalid_recovery" | "pipeline.include_cycle" | "pipeline.include_invalid" | "pipeline.include_duplicate_stage" | "pipeline.invalid_agent" | "pipeline.invalid_model" | "pipeline.invalid_verify" | "pipeline.io_incompatible" | "pipeline.model_applies" | "pipeline.route_if_invalid" | "pipeline.route_all_gated" | "pipeline.invalid_requires" | "pipeline.requires_conflict" | "pipeline.invalid_channels" | "stage.invalid_shape" | "stage.invalid_model" | "stage.missing_model" | "stage.invalid_payload_schema" | "stage.invalid_clone_input_schema" | "stage.unresolved_schema_ref" | "stage.invalid_clone_actions" | "stage.invalid_gate_kinds" | "stage.invalid_pre_emit_checks" | "stage.invalid_timeout_ms" | "stage.invalid_skill" | "stage.invalid_mcp" | "stage.invalid_channels" | "stage.channel_unavailable" | "stage.invalid_browser" | "stage.invalid_secrets" | "stage.unknown_secret" | "stage.denied_secret" | "stage.invalid_requires" | "stage.invalid_agent" | "stage.invalid_io" | "stage.load_error" | "stage.id_filename_mismatch" | "task.invalid_shape" | "task.load_error" | "task.entry_input_unmet" | "task.binding_conflict" | "task.repository_ref_required" | "task.ref_without_repository" | "task.repository_invalid" | "trigger.invalid_shape" | "trigger.load_error" | "trigger.unknown_pipeline" | "trigger.unknown_task" | "catalog.duplicate_pipeline_id" | "catalog.manifest_missing" | "catalog.manifest_invalid" | "catalog.empty_catalog" | "catalog.manifest_load_error" | "catalog.invalid_mcp" | "catalog.mcp_command_missing" | "catalog.mixed_yaml_dialect" | "catalog.legacy_yaml" | "catalog.stageflow_home_absolute_path";
|
|
7
8
|
export type ValidationFinding = {
|
|
8
9
|
severity: ValidationSeverity;
|
|
9
10
|
code: ValidationFindingCode | string;
|
|
@@ -12,8 +13,10 @@ export type ValidationFinding = {
|
|
|
12
13
|
category: ValidationCategory;
|
|
13
14
|
pipelineId?: string;
|
|
14
15
|
stageId?: string;
|
|
16
|
+
line?: number;
|
|
17
|
+
column?: number;
|
|
15
18
|
};
|
|
16
|
-
export type ValidationScope = "full" | "pipeline" | "task" | "trigger";
|
|
19
|
+
export type ValidationScope = "full" | "pipeline" | "task" | "trigger" | "stage";
|
|
17
20
|
export type ValidateCatalogOptions = {
|
|
18
21
|
cwd?: string;
|
|
19
22
|
projectRoot?: string;
|
|
@@ -21,6 +24,8 @@ export type ValidateCatalogOptions = {
|
|
|
21
24
|
pipeline?: string;
|
|
22
25
|
task?: string;
|
|
23
26
|
trigger?: string;
|
|
27
|
+
stage?: string;
|
|
28
|
+
stageContent?: string;
|
|
24
29
|
strict?: boolean;
|
|
25
30
|
};
|
|
26
31
|
export type ValidationResult = {
|
|
@@ -46,6 +51,7 @@ export type ValidatePipelineOptions = {
|
|
|
46
51
|
projectRoot?: string;
|
|
47
52
|
validateStages?: boolean;
|
|
48
53
|
strict?: boolean;
|
|
54
|
+
channelProviders?: readonly AnyChannelProvider[];
|
|
49
55
|
};
|
|
50
56
|
export declare function validatePipeline(nameOrPath: string, options?: ValidatePipelineOptions): Promise<ValidationResult>;
|
|
51
57
|
export type LoadPipelineValidatedResult = {
|
|
@@ -60,6 +66,7 @@ export declare function loadPipelineValidated(nameOrPath: string | InlinePipelin
|
|
|
60
66
|
cwd?: string;
|
|
61
67
|
projectRoot?: string;
|
|
62
68
|
validateStages?: boolean;
|
|
69
|
+
channelProviders?: readonly AnyChannelProvider[];
|
|
63
70
|
}): Promise<LoadPipelineValidatedResult>;
|
|
64
71
|
export declare function collectPipelineIdsFromPaths(pipelinePaths: string[]): Promise<Map<string, string[]>>;
|
|
65
72
|
export declare function collectTaskIdsFromPaths(taskPaths: string[]): Promise<Set<string>>;
|