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
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ResolveCatalogRelativePathInput } from "../config/catalogRelativePath.js";
|
|
2
|
+
import { type ValidationFinding } from "../config/validateCatalog.js";
|
|
3
|
+
export declare const CATALOG_FILE_MAX_BYTES: number;
|
|
4
|
+
export type CatalogFileReadErrorCode = "unsupported_file_type" | "path_outside_project_root" | "catalog_file_not_found" | "catalog_file_too_large";
|
|
5
|
+
export type ReadCatalogFileResult = {
|
|
6
|
+
ok: true;
|
|
7
|
+
path: string;
|
|
8
|
+
content: string;
|
|
9
|
+
} | {
|
|
10
|
+
ok: false;
|
|
11
|
+
status: 400 | 404 | 413;
|
|
12
|
+
error: string;
|
|
13
|
+
code: CatalogFileReadErrorCode;
|
|
14
|
+
};
|
|
15
|
+
export type WriteCatalogFileResult = {
|
|
16
|
+
ok: true;
|
|
17
|
+
path: string;
|
|
18
|
+
content: string;
|
|
19
|
+
} | {
|
|
20
|
+
ok: false;
|
|
21
|
+
status: 400 | 404 | 413;
|
|
22
|
+
error: string;
|
|
23
|
+
code: CatalogFileReadErrorCode | "stage_invalid";
|
|
24
|
+
findings?: ValidationFinding[];
|
|
25
|
+
};
|
|
26
|
+
export declare function readCatalogFileForHttp(input: ResolveCatalogRelativePathInput): Promise<ReadCatalogFileResult>;
|
|
27
|
+
export declare function writeCatalogFileForHttp(input: ResolveCatalogRelativePathInput & {
|
|
28
|
+
content: string;
|
|
29
|
+
}): Promise<WriteCatalogFileResult>;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { readFile, realpath, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { CatalogPathError, isInsideProjectRoot, resolveCatalogRelativePath, } from "../config/catalogRelativePath.js";
|
|
4
|
+
import { loadStageOutcome } from "../config/loadStage.js";
|
|
5
|
+
import { findingsFromLoadIssues, } from "../config/validateCatalog.js";
|
|
6
|
+
export const CATALOG_FILE_MAX_BYTES = 512 * 1024;
|
|
7
|
+
function isCatalogYamlExtension(inputPath) {
|
|
8
|
+
const ext = path.extname(inputPath).toLowerCase();
|
|
9
|
+
return ext === ".yaml" || ext === ".yml";
|
|
10
|
+
}
|
|
11
|
+
export async function readCatalogFileForHttp(input) {
|
|
12
|
+
if (!isCatalogYamlExtension(input.inputPath)) {
|
|
13
|
+
return {
|
|
14
|
+
ok: false,
|
|
15
|
+
status: 400,
|
|
16
|
+
error: "Only .yaml and .yml catalog files can be read",
|
|
17
|
+
code: "unsupported_file_type",
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
let resolved;
|
|
21
|
+
try {
|
|
22
|
+
resolved = resolveCatalogRelativePath(input);
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
if (err instanceof CatalogPathError) {
|
|
26
|
+
if (err.code === "path_outside_project_root") {
|
|
27
|
+
return {
|
|
28
|
+
ok: false,
|
|
29
|
+
status: 400,
|
|
30
|
+
error: err.message,
|
|
31
|
+
code: "path_outside_project_root",
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
let rootReal;
|
|
39
|
+
let fileReal;
|
|
40
|
+
try {
|
|
41
|
+
rootReal = await realpath(resolved.root.path);
|
|
42
|
+
fileReal = await realpath(resolved.absolutePath);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
const code = err.code;
|
|
46
|
+
if (code === "ENOENT") {
|
|
47
|
+
return {
|
|
48
|
+
ok: false,
|
|
49
|
+
status: 404,
|
|
50
|
+
error: "Catalog file not found",
|
|
51
|
+
code: "catalog_file_not_found",
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
if (!isInsideProjectRoot(rootReal, fileReal)) {
|
|
57
|
+
return {
|
|
58
|
+
ok: false,
|
|
59
|
+
status: 400,
|
|
60
|
+
error: `path escapes project_root ${resolved.root.project_root}`,
|
|
61
|
+
code: "path_outside_project_root",
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
if (!isCatalogYamlExtension(fileReal)) {
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
status: 400,
|
|
68
|
+
error: "Only .yaml and .yml catalog files can be read",
|
|
69
|
+
code: "unsupported_file_type",
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
let fileStat;
|
|
73
|
+
try {
|
|
74
|
+
fileStat = await stat(fileReal);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
const code = err.code;
|
|
78
|
+
if (code === "ENOENT") {
|
|
79
|
+
return {
|
|
80
|
+
ok: false,
|
|
81
|
+
status: 404,
|
|
82
|
+
error: "Catalog file not found",
|
|
83
|
+
code: "catalog_file_not_found",
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
throw err;
|
|
87
|
+
}
|
|
88
|
+
if (!fileStat.isFile()) {
|
|
89
|
+
return {
|
|
90
|
+
ok: false,
|
|
91
|
+
status: 400,
|
|
92
|
+
error: "Only .yaml and .yml catalog files can be read",
|
|
93
|
+
code: "unsupported_file_type",
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (fileStat.size > CATALOG_FILE_MAX_BYTES) {
|
|
97
|
+
return {
|
|
98
|
+
ok: false,
|
|
99
|
+
status: 413,
|
|
100
|
+
error: `Catalog file exceeds ${CATALOG_FILE_MAX_BYTES} byte limit`,
|
|
101
|
+
code: "catalog_file_too_large",
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const content = await readFile(fileReal, "utf8");
|
|
105
|
+
return { ok: true, path: resolved.relativePath, content };
|
|
106
|
+
}
|
|
107
|
+
export async function writeCatalogFileForHttp(input) {
|
|
108
|
+
if (!isCatalogYamlExtension(input.inputPath)) {
|
|
109
|
+
return {
|
|
110
|
+
ok: false,
|
|
111
|
+
status: 400,
|
|
112
|
+
error: "Only .yaml and .yml catalog files can be written",
|
|
113
|
+
code: "unsupported_file_type",
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
if (Buffer.byteLength(input.content, "utf8") > CATALOG_FILE_MAX_BYTES) {
|
|
117
|
+
return {
|
|
118
|
+
ok: false,
|
|
119
|
+
status: 413,
|
|
120
|
+
error: `Catalog file exceeds ${CATALOG_FILE_MAX_BYTES} byte limit`,
|
|
121
|
+
code: "catalog_file_too_large",
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
let resolved;
|
|
125
|
+
try {
|
|
126
|
+
resolved = resolveCatalogRelativePath(input);
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
if (err instanceof CatalogPathError) {
|
|
130
|
+
if (err.code === "path_outside_project_root") {
|
|
131
|
+
return {
|
|
132
|
+
ok: false,
|
|
133
|
+
status: 400,
|
|
134
|
+
error: err.message,
|
|
135
|
+
code: "path_outside_project_root",
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
throw err;
|
|
139
|
+
}
|
|
140
|
+
throw err;
|
|
141
|
+
}
|
|
142
|
+
let rootReal;
|
|
143
|
+
let fileReal;
|
|
144
|
+
try {
|
|
145
|
+
rootReal = await realpath(resolved.root.path);
|
|
146
|
+
fileReal = await realpath(resolved.absolutePath);
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
const code = err.code;
|
|
150
|
+
if (code === "ENOENT") {
|
|
151
|
+
return {
|
|
152
|
+
ok: false,
|
|
153
|
+
status: 404,
|
|
154
|
+
error: "Catalog file not found",
|
|
155
|
+
code: "catalog_file_not_found",
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
throw err;
|
|
159
|
+
}
|
|
160
|
+
if (!isInsideProjectRoot(rootReal, fileReal)) {
|
|
161
|
+
return {
|
|
162
|
+
ok: false,
|
|
163
|
+
status: 400,
|
|
164
|
+
error: `path escapes project_root ${resolved.root.project_root}`,
|
|
165
|
+
code: "path_outside_project_root",
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
if (!isCatalogYamlExtension(fileReal)) {
|
|
169
|
+
return {
|
|
170
|
+
ok: false,
|
|
171
|
+
status: 400,
|
|
172
|
+
error: "Only .yaml and .yml catalog files can be written",
|
|
173
|
+
code: "unsupported_file_type",
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
let fileStat;
|
|
177
|
+
try {
|
|
178
|
+
fileStat = await stat(fileReal);
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
const code = err.code;
|
|
182
|
+
if (code === "ENOENT") {
|
|
183
|
+
return {
|
|
184
|
+
ok: false,
|
|
185
|
+
status: 404,
|
|
186
|
+
error: "Catalog file not found",
|
|
187
|
+
code: "catalog_file_not_found",
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
throw err;
|
|
191
|
+
}
|
|
192
|
+
if (!fileStat.isFile()) {
|
|
193
|
+
return {
|
|
194
|
+
ok: false,
|
|
195
|
+
status: 400,
|
|
196
|
+
error: "Only .yaml and .yml catalog files can be written",
|
|
197
|
+
code: "unsupported_file_type",
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
const original = await readFile(fileReal, "utf8");
|
|
201
|
+
await writeFile(fileReal, input.content, "utf8");
|
|
202
|
+
const outcome = await loadStageOutcome(fileReal);
|
|
203
|
+
if (!outcome.ok) {
|
|
204
|
+
await writeFile(fileReal, original, "utf8");
|
|
205
|
+
const findings = findingsFromLoadIssues(resolved.root.path, fileReal, outcome.issues);
|
|
206
|
+
return {
|
|
207
|
+
ok: false,
|
|
208
|
+
status: 400,
|
|
209
|
+
error: findings[0]?.message ?? "Invalid stage YAML",
|
|
210
|
+
code: "stage_invalid",
|
|
211
|
+
findings,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return { ok: true, path: resolved.relativePath, content: input.content };
|
|
215
|
+
}
|
|
@@ -3,6 +3,7 @@ import type { Logger } from "../logging/logger.js";
|
|
|
3
3
|
import type { RunManager } from "../runtime/runManager.js";
|
|
4
4
|
import type { RunStore } from "../runstore/port.js";
|
|
5
5
|
export type { TriggerListItem } from "../config/triggerCatalog.js";
|
|
6
|
+
export type { TriggerAdapterStatus } from "../config/triggerAdapterStatus.js";
|
|
6
7
|
export type TriggerRoutesCtx = {
|
|
7
8
|
cwd: string;
|
|
8
9
|
manager: RunManager;
|
|
@@ -1,18 +1,40 @@
|
|
|
1
1
|
import { fireTrigger } from "../runtime/triggerRunner.js";
|
|
2
|
-
import {
|
|
3
|
-
import { matchesEventFilter } from "../runtime/triggerEventMatch.js";
|
|
4
|
-
import { verifyHmacSignature } from "../runtime/webhookSignature.js";
|
|
2
|
+
import { acceptWebhook } from "../runtime/webhookAccept.js";
|
|
5
3
|
import { mapStartFailure } from "./operatorResults.js";
|
|
6
4
|
import { writeAudit } from "../logging/audit.js";
|
|
7
5
|
import { callerIdFromRequestAuth, getRequestAuth, } from "./requestAuthContext.js";
|
|
8
|
-
import { loadCatalogTriggers } from "../config/triggerCatalog.js";
|
|
6
|
+
import { findCatalogTrigger, loadCatalogTriggers, triggerStoreId, } from "../config/triggerCatalog.js";
|
|
9
7
|
import { createTrigger, parseCreateTriggerBody } from "../config/createTrigger.js";
|
|
8
|
+
import { parsePatchTriggerBody, parsePutTriggerBody, updateTriggerDefinition, updateTriggerEnabled, projectRootFilterFromBody, } from "../config/updateTrigger.js";
|
|
9
|
+
import { readTriggerAdapterStatus, } from "../config/triggerAdapterStatus.js";
|
|
10
10
|
import { CatalogPathError, catalogPathErrorBody, resolveWritableCatalogRoot, } from "../config/catalogRelativePath.js";
|
|
11
11
|
import { resolveStageflowContext } from "../project/resolveStageflowContext.js";
|
|
12
|
-
import { catalogContextFromStageflow } from "../config/resolveCatalogContext.js";
|
|
13
|
-
import { getCatalogScanPaths } from "../config/browseCatalog.js";
|
|
14
|
-
import { readSecretFromEnvOrFile } from "../config/secretFromEnvOrFile.js";
|
|
15
12
|
import { coerceTaskFile } from "../config/loadTask.js";
|
|
13
|
+
async function enrichTriggerListItem(item, store) {
|
|
14
|
+
const adapter_status = await readTriggerAdapterStatus(item, store);
|
|
15
|
+
return { ...item, adapter_status };
|
|
16
|
+
}
|
|
17
|
+
function resolveListedTrigger(items, id, projectRootWire) {
|
|
18
|
+
if (projectRootWire) {
|
|
19
|
+
const found = items.find((item) => item.id === id && item.project_root === projectRootWire);
|
|
20
|
+
if (!found) {
|
|
21
|
+
return { error: `Trigger not found: ${id}`, status: 404 };
|
|
22
|
+
}
|
|
23
|
+
return found;
|
|
24
|
+
}
|
|
25
|
+
const matches = items.filter((item) => item.id === id);
|
|
26
|
+
if (matches.length === 0) {
|
|
27
|
+
return { error: `Trigger not found: ${id}`, status: 404 };
|
|
28
|
+
}
|
|
29
|
+
if (matches.length > 1) {
|
|
30
|
+
return {
|
|
31
|
+
error: `Trigger id is ambiguous across catalog roots: ${id}`,
|
|
32
|
+
status: 409,
|
|
33
|
+
project_roots: [...new Set(matches.map((m) => m.project_root))].sort((a, b) => a.localeCompare(b)),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return matches[0];
|
|
37
|
+
}
|
|
16
38
|
export async function handleTriggerRoutes(req, res, ctx) {
|
|
17
39
|
const { cwd, manager, store, json, readJsonBody, readRawBody, auditLog } = ctx;
|
|
18
40
|
const method = req.method ?? "GET";
|
|
@@ -78,12 +100,9 @@ export async function handleTriggerRoutes(req, res, ctx) {
|
|
|
78
100
|
return true;
|
|
79
101
|
}
|
|
80
102
|
if (method === "GET" && pathname === "/api/triggers") {
|
|
81
|
-
const items = await loadCatalogTriggers(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return true;
|
|
85
|
-
}
|
|
86
|
-
json(res, 200, { triggers: items });
|
|
103
|
+
const items = await loadCatalogTriggers({ store, bootCwd: cwd });
|
|
104
|
+
const triggers = await Promise.all(items.map((item) => enrichTriggerListItem(item, store)));
|
|
105
|
+
json(res, 200, { triggers });
|
|
87
106
|
return true;
|
|
88
107
|
}
|
|
89
108
|
const fireMatch = pathname.match(/^\/api\/triggers\/([^/]+)\/fire$/);
|
|
@@ -107,8 +126,14 @@ export async function handleTriggerRoutes(req, res, ctx) {
|
|
|
107
126
|
}
|
|
108
127
|
}
|
|
109
128
|
const id = decodeURIComponent(fireMatch[1] ?? "");
|
|
129
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
130
|
+
const projectRootWire = url.searchParams.get("project_root") ?? undefined;
|
|
110
131
|
const callerId = callerIdFromRequestAuth();
|
|
111
|
-
const result = await fireTrigger(id, store, manager, {
|
|
132
|
+
const result = await fireTrigger(id, store, manager, {
|
|
133
|
+
cwd,
|
|
134
|
+
task,
|
|
135
|
+
...(projectRootWire ? { project_root: projectRootWire } : {}),
|
|
136
|
+
});
|
|
112
137
|
if (!result.ok) {
|
|
113
138
|
writeAudit(auditLog, {
|
|
114
139
|
caller_id: callerId,
|
|
@@ -145,41 +170,45 @@ export async function handleTriggerRoutes(req, res, ctx) {
|
|
|
145
170
|
if (method === "POST" && webhookMatch) {
|
|
146
171
|
const id = decodeURIComponent(webhookMatch[1] ?? "");
|
|
147
172
|
const callerId = callerIdFromRequestAuth();
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
:
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
173
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
174
|
+
const projectRootWire = url.searchParams.get("project_root") ?? undefined;
|
|
175
|
+
const located = await findCatalogTrigger({
|
|
176
|
+
store,
|
|
177
|
+
bootCwd: cwd,
|
|
178
|
+
triggerId: id,
|
|
179
|
+
...(projectRootWire ? { projectRootFilter: projectRootWire } : {}),
|
|
180
|
+
});
|
|
181
|
+
if (!located.ok) {
|
|
182
|
+
if (located.status === 409 && located.code === "ambiguous_trigger") {
|
|
183
|
+
json(res, located.status, {
|
|
184
|
+
error: located.reason,
|
|
185
|
+
code: located.code,
|
|
186
|
+
project_roots: located.project_roots,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
else if (located.status === 400 && located.code === "unknown_project_root") {
|
|
190
|
+
json(res, located.status, {
|
|
191
|
+
error: located.reason,
|
|
192
|
+
code: located.code,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
json(res, located.status, { error: located.reason });
|
|
197
|
+
}
|
|
163
198
|
return true;
|
|
164
199
|
}
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (secret === undefined) {
|
|
200
|
+
const rawBody = await readRawBody(req);
|
|
201
|
+
const accepted = acceptWebhook({
|
|
202
|
+
trigger: located.match.definition,
|
|
203
|
+
rawBody,
|
|
204
|
+
headers: req.headers,
|
|
205
|
+
env: process.env,
|
|
206
|
+
});
|
|
207
|
+
if (accepted.kind === "not_configured") {
|
|
174
208
|
json(res, 400, { error: "this trigger has no webhook signing configured" });
|
|
175
209
|
return true;
|
|
176
210
|
}
|
|
177
|
-
|
|
178
|
-
const headerValue = req.headers[header.toLowerCase()];
|
|
179
|
-
const signature = Array.isArray(headerValue) ? headerValue[0] : headerValue;
|
|
180
|
-
if (typeof signature !== "string" ||
|
|
181
|
-
signature.length === 0 ||
|
|
182
|
-
!verifyHmacSignature(rawBody, signature, secret, scheme)) {
|
|
211
|
+
if (accepted.kind === "bad_signature") {
|
|
183
212
|
writeAudit(auditLog, {
|
|
184
213
|
caller_id: callerId,
|
|
185
214
|
surface: "webhook",
|
|
@@ -190,31 +219,19 @@ export async function handleTriggerRoutes(req, res, ctx) {
|
|
|
190
219
|
json(res, 401, { error: "Invalid webhook signature" });
|
|
191
220
|
return true;
|
|
192
221
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
payload = rawBody.length === 0 ? {} : JSON.parse(rawBody.toString("utf8"));
|
|
196
|
-
}
|
|
197
|
-
catch {
|
|
198
|
-
json(res, 400, { error: "Invalid JSON body" });
|
|
222
|
+
if (accepted.kind === "bad_json") {
|
|
223
|
+
json(res, 400, { error: accepted.error });
|
|
199
224
|
return true;
|
|
200
225
|
}
|
|
201
|
-
if (
|
|
202
|
-
json(res, 400, { error: "Invalid JSON body: expected an object" });
|
|
203
|
-
return true;
|
|
204
|
-
}
|
|
205
|
-
const payloadRecord = payload;
|
|
206
|
-
if (!matchesEventFilter(trigger.event?.match, payloadRecord)) {
|
|
226
|
+
if (accepted.kind === "no_match") {
|
|
207
227
|
json(res, 200, { fired: false });
|
|
208
228
|
return true;
|
|
209
229
|
}
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
: undefined;
|
|
217
|
-
const result = await fireTrigger(id, store, manager, { cwd, task });
|
|
230
|
+
const result = await fireTrigger(id, store, manager, {
|
|
231
|
+
cwd,
|
|
232
|
+
task: accepted.task,
|
|
233
|
+
project_root: located.match.project_root,
|
|
234
|
+
});
|
|
218
235
|
if (!result.ok) {
|
|
219
236
|
writeAudit(auditLog, {
|
|
220
237
|
caller_id: callerId,
|
|
@@ -248,20 +265,130 @@ export async function handleTriggerRoutes(req, res, ctx) {
|
|
|
248
265
|
});
|
|
249
266
|
return true;
|
|
250
267
|
}
|
|
268
|
+
const statusMatch = pathname.match(/^\/api\/triggers\/([^/]+)\/status$/);
|
|
269
|
+
if (method === "GET" && statusMatch) {
|
|
270
|
+
const id = decodeURIComponent(statusMatch[1] ?? "");
|
|
271
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
272
|
+
const projectRootWire = url.searchParams.get("project_root") ?? undefined;
|
|
273
|
+
const items = await loadCatalogTriggers({ store, bootCwd: cwd });
|
|
274
|
+
const found = resolveListedTrigger(items, id, projectRootWire);
|
|
275
|
+
if ("error" in found) {
|
|
276
|
+
json(res, found.status, { error: found.error, ...(found.project_roots ? { project_roots: found.project_roots } : {}) });
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
const adapter_status = await readTriggerAdapterStatus(found, store);
|
|
280
|
+
json(res, 200, adapter_status);
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
const firesMatch = pathname.match(/^\/api\/triggers\/([^/]+)\/fires$/);
|
|
284
|
+
if (method === "GET" && firesMatch) {
|
|
285
|
+
const id = decodeURIComponent(firesMatch[1] ?? "");
|
|
286
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
287
|
+
const projectRootWire = url.searchParams.get("project_root") ?? undefined;
|
|
288
|
+
const items = await loadCatalogTriggers({ store, bootCwd: cwd });
|
|
289
|
+
const found = resolveListedTrigger(items, id, projectRootWire);
|
|
290
|
+
if ("error" in found) {
|
|
291
|
+
json(res, found.status, { error: found.error, ...(found.project_roots ? { project_roots: found.project_roots } : {}) });
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
const fires = await store.listTriggerFires(triggerStoreId(found.project_root, found.id), 20);
|
|
295
|
+
json(res, 200, { fires });
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
const putMatch = pathname.match(/^\/api\/triggers\/([^/]+)$/);
|
|
299
|
+
if (method === "PUT" && putMatch) {
|
|
300
|
+
let body;
|
|
301
|
+
try {
|
|
302
|
+
body = await readJsonBody(req);
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
306
|
+
return true;
|
|
307
|
+
}
|
|
308
|
+
const id = decodeURIComponent(putMatch[1] ?? "");
|
|
309
|
+
const parsed = parsePutTriggerBody(body, id);
|
|
310
|
+
if ("ok" in parsed) {
|
|
311
|
+
json(res, parsed.status, { error: parsed.error });
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
const projectRootFilter = projectRootFilterFromBody(body);
|
|
315
|
+
const callerId = callerIdFromRequestAuth();
|
|
316
|
+
const result = await updateTriggerDefinition(cwd, store, id, parsed, {
|
|
317
|
+
projectRootFilter,
|
|
318
|
+
});
|
|
319
|
+
if (!result.ok) {
|
|
320
|
+
writeAudit(auditLog, {
|
|
321
|
+
caller_id: callerId,
|
|
322
|
+
surface: getRequestAuth()?.surface ?? "rest",
|
|
323
|
+
action: "trigger_update",
|
|
324
|
+
outcome: "error",
|
|
325
|
+
});
|
|
326
|
+
json(res, result.status, { error: result.error });
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
writeAudit(auditLog, {
|
|
330
|
+
caller_id: callerId,
|
|
331
|
+
surface: getRequestAuth()?.surface ?? "rest",
|
|
332
|
+
action: "trigger_update",
|
|
333
|
+
outcome: "ok",
|
|
334
|
+
});
|
|
335
|
+
const enriched = await enrichTriggerListItem(result.trigger, store);
|
|
336
|
+
json(res, 200, enriched);
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
const patchMatch = pathname.match(/^\/api\/triggers\/([^/]+)$/);
|
|
340
|
+
if (method === "PATCH" && patchMatch) {
|
|
341
|
+
let body;
|
|
342
|
+
try {
|
|
343
|
+
body = await readJsonBody(req);
|
|
344
|
+
}
|
|
345
|
+
catch {
|
|
346
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
const parsed = parsePatchTriggerBody(body);
|
|
350
|
+
if ("ok" in parsed) {
|
|
351
|
+
json(res, parsed.status, { error: parsed.error });
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
const id = decodeURIComponent(patchMatch[1] ?? "");
|
|
355
|
+
const projectRootFilter = projectRootFilterFromBody(body);
|
|
356
|
+
const callerId = callerIdFromRequestAuth();
|
|
357
|
+
const result = await updateTriggerEnabled(cwd, store, id, parsed.enabled, {
|
|
358
|
+
projectRootFilter,
|
|
359
|
+
});
|
|
360
|
+
if (!result.ok) {
|
|
361
|
+
writeAudit(auditLog, {
|
|
362
|
+
caller_id: callerId,
|
|
363
|
+
surface: getRequestAuth()?.surface ?? "rest",
|
|
364
|
+
action: "trigger_patch",
|
|
365
|
+
outcome: "error",
|
|
366
|
+
});
|
|
367
|
+
json(res, result.status, { error: result.error });
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
writeAudit(auditLog, {
|
|
371
|
+
caller_id: callerId,
|
|
372
|
+
surface: getRequestAuth()?.surface ?? "rest",
|
|
373
|
+
action: "trigger_patch",
|
|
374
|
+
outcome: "ok",
|
|
375
|
+
});
|
|
376
|
+
const enriched = await enrichTriggerListItem(result.trigger, store);
|
|
377
|
+
json(res, 200, enriched);
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
251
380
|
const idMatch = pathname.match(/^\/api\/triggers\/([^/]+)$/);
|
|
252
381
|
if (method === "GET" && idMatch) {
|
|
253
382
|
const id = decodeURIComponent(idMatch[1] ?? "");
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
if (!found) {
|
|
261
|
-
json(res, 404, { error: `Trigger not found: ${id}` });
|
|
383
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
384
|
+
const projectRootWire = url.searchParams.get("project_root") ?? undefined;
|
|
385
|
+
const items = await loadCatalogTriggers({ store, bootCwd: cwd });
|
|
386
|
+
const found = resolveListedTrigger(items, id, projectRootWire);
|
|
387
|
+
if ("error" in found) {
|
|
388
|
+
json(res, found.status, { error: found.error, ...(found.project_roots ? { project_roots: found.project_roots } : {}) });
|
|
262
389
|
return true;
|
|
263
390
|
}
|
|
264
|
-
json(res, 200, found);
|
|
391
|
+
json(res, 200, await enrichTriggerListItem(found, store));
|
|
265
392
|
return true;
|
|
266
393
|
}
|
|
267
394
|
return false;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Type } from "typebox";
|
|
2
|
+
import { type StageChannels } from "../channels/host.js";
|
|
3
|
+
export declare function createSendMessageTool(channels: StageChannels): {
|
|
4
|
+
name: string;
|
|
5
|
+
label: string;
|
|
6
|
+
description: string;
|
|
7
|
+
parameters: Type.TObject<{
|
|
8
|
+
channel: Type.TOptional<Type.TString>;
|
|
9
|
+
text: Type.TString;
|
|
10
|
+
subject: Type.TOptional<Type.TString>;
|
|
11
|
+
operationKey: Type.TString;
|
|
12
|
+
}>;
|
|
13
|
+
execute(_toolCallId: string, input: unknown): Promise<{
|
|
14
|
+
content: {
|
|
15
|
+
type: "text";
|
|
16
|
+
text: string;
|
|
17
|
+
}[];
|
|
18
|
+
details: unknown;
|
|
19
|
+
isError?: boolean;
|
|
20
|
+
}>;
|
|
21
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Type } from "typebox";
|
|
2
|
+
import { MAX_MESSAGE_TEXT, channelFault } from "../channels/host.js";
|
|
3
|
+
export function createSendMessageTool(channels) {
|
|
4
|
+
return {
|
|
5
|
+
name: "send_message",
|
|
6
|
+
label: "Send message",
|
|
7
|
+
description: "Send a message on one of this stage's declared communication channels. The recipient is fixed by Stageflow configuration and cannot be chosen. Pass the channel alias when the stage declares more than one. Reuse the same operationKey when retrying the same message. Acceptance means provider submission, not delivery or that anyone read it. Never retry an unknown outcome with a new key. Do not put secrets in a message.",
|
|
8
|
+
parameters: Type.Object({
|
|
9
|
+
channel: Type.Optional(Type.String()),
|
|
10
|
+
text: Type.String({ maxLength: MAX_MESSAGE_TEXT }),
|
|
11
|
+
subject: Type.Optional(Type.String()),
|
|
12
|
+
operationKey: Type.String(),
|
|
13
|
+
}),
|
|
14
|
+
async execute(_toolCallId, input) {
|
|
15
|
+
try {
|
|
16
|
+
const receipt = await channels.send(input);
|
|
17
|
+
return { content: [{ type: "text", text: JSON.stringify(receipt) }], details: receipt };
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
const details = channelFault(error);
|
|
21
|
+
return { content: [{ type: "text", text: JSON.stringify(details) }], details, isError: true };
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom Pi tool for writing factory stage artifact files under the run
|
|
3
|
+
* workspace. Paths are relative to
|
|
4
|
+
* `stages/<stageId>/attempts/<attempt>/artifacts/` and must resolve inside
|
|
5
|
+
* that directory.
|
|
6
|
+
*
|
|
7
|
+
* Parameter schemas use `typebox` (Pi's `defineTool` TSchema), not
|
|
8
|
+
* `@sinclair/typebox`. Unlike emit, this tool does not terminate the turn.
|
|
9
|
+
*/
|
|
1
10
|
import { Type } from "typebox";
|
|
2
11
|
export type WriteStageArtifactOptions = {
|
|
3
12
|
runWorkspaceDir: string;
|