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,186 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { inspect } from "node:util";
|
|
4
|
+
import { loadConnectionsFile } from "./config.js";
|
|
5
|
+
import { ChannelError } from "./errors.js";
|
|
6
|
+
import { channelProviders } from "./providers.js";
|
|
7
|
+
import { resolveSecrets } from "./secrets.js";
|
|
8
|
+
import { ChannelSecretStore } from "./secretStore.js";
|
|
9
|
+
import { globalCredentialRoot, globalStageflowHome } from "../project/globalHome.js";
|
|
10
|
+
export const CONNECTIONS_FILE_NAME = "connections.yaml";
|
|
11
|
+
function hostConnectionsFile(env) {
|
|
12
|
+
const home = env.STAGEFLOW_HOME?.trim() || (env === process.env ? globalStageflowHome() : undefined);
|
|
13
|
+
return home ? path.join(path.resolve(home), CONNECTIONS_FILE_NAME) : undefined;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Where connections live. Connections are host-level, like provider credentials, so the default is
|
|
17
|
+
* `$STAGEFLOW_HOME/connections.yaml`. A `connections.yaml` in the project root takes precedence when
|
|
18
|
+
* it exists (CI and checked-in setups), and `STAGEFLOW_CONNECTIONS_FILE` overrides both.
|
|
19
|
+
*/
|
|
20
|
+
export function connectionsFilePath(cwd, env = process.env) {
|
|
21
|
+
const override = env.STAGEFLOW_CONNECTIONS_FILE?.trim();
|
|
22
|
+
if (override)
|
|
23
|
+
return path.isAbsolute(override) ? path.resolve(override) : path.resolve(cwd, override);
|
|
24
|
+
const project = path.join(path.resolve(cwd), CONNECTIONS_FILE_NAME);
|
|
25
|
+
if (existsSync(project))
|
|
26
|
+
return project;
|
|
27
|
+
return hostConnectionsFile(env) ?? project;
|
|
28
|
+
}
|
|
29
|
+
export function defaultSecretStore() {
|
|
30
|
+
return new ChannelSecretStore(path.join(globalCredentialRoot(), "channel-secrets.json"));
|
|
31
|
+
}
|
|
32
|
+
/** Host-owned, read-only view of `connections.yaml`. Reads return copies. */
|
|
33
|
+
export class ChannelRegistry {
|
|
34
|
+
scope;
|
|
35
|
+
env;
|
|
36
|
+
secretStore;
|
|
37
|
+
providers = new Map();
|
|
38
|
+
credentials = new Map();
|
|
39
|
+
connections = new Map();
|
|
40
|
+
constructor(options) {
|
|
41
|
+
this.scope = options.scope ?? path.resolve(options.cwd);
|
|
42
|
+
this.env = options.env ?? process.env;
|
|
43
|
+
for (const provider of options.providers ?? channelProviders())
|
|
44
|
+
this.providers.set(provider.id, provider);
|
|
45
|
+
this.secretStore = options.secretStore ?? { get: name => defaultSecretStore().get(name) };
|
|
46
|
+
const file = options.parsed ?? loadConnectionsFile(options.file ?? connectionsFilePath(options.cwd, this.env));
|
|
47
|
+
for (const credential of file.credentials)
|
|
48
|
+
this.addCredential(credential);
|
|
49
|
+
for (const entry of file.connections)
|
|
50
|
+
this.addConnection(entry);
|
|
51
|
+
}
|
|
52
|
+
list() {
|
|
53
|
+
return [...this.connections.values()].map(entry => this.summarize(entry));
|
|
54
|
+
}
|
|
55
|
+
summarize(loaded) {
|
|
56
|
+
const identity = this.providers.get(loaded.provider)?.describe?.(loaded.config)?.identity;
|
|
57
|
+
return {
|
|
58
|
+
id: loaded.id, scope: this.scope, provider: loaded.provider, displayName: loaded.displayName,
|
|
59
|
+
enabled: loaded.enabled, ...(identity ? { identity } : {}),
|
|
60
|
+
...(loaded.credential ? { credential: loaded.credential } : {}),
|
|
61
|
+
secrets: Object.fromEntries(Object.entries(loaded.secrets).map(([field, ref]) => [field, ref.startsWith("store:") ? "store" : "env"])),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
get(id, requireEnabled = true) {
|
|
65
|
+
return this.summarize(this.entry(id, requireEnabled));
|
|
66
|
+
}
|
|
67
|
+
providerById(providerId) {
|
|
68
|
+
return this.requireProvider(providerId);
|
|
69
|
+
}
|
|
70
|
+
/** Environment variable names that connections and credentials point at with `env:` references. */
|
|
71
|
+
envSecretNames() {
|
|
72
|
+
const names = new Set();
|
|
73
|
+
for (const entry of [...this.credentials.values(), ...this.connections.values()]) {
|
|
74
|
+
for (const ref of Object.values(entry.secrets))
|
|
75
|
+
if (ref.startsWith("env:"))
|
|
76
|
+
names.add(ref.slice("env:".length));
|
|
77
|
+
}
|
|
78
|
+
return [...names];
|
|
79
|
+
}
|
|
80
|
+
providerList() {
|
|
81
|
+
return [...this.providers.values()];
|
|
82
|
+
}
|
|
83
|
+
hasCredential(id) {
|
|
84
|
+
return this.credentials.has(id);
|
|
85
|
+
}
|
|
86
|
+
/** Resolves a shared credential by id, for providers that reference one. */
|
|
87
|
+
resolveCredential(id) {
|
|
88
|
+
const entry = this.credentials.get(id);
|
|
89
|
+
if (!entry)
|
|
90
|
+
throw new ChannelError("CHANNEL_INVALID_INPUT", false, { field: "credential" });
|
|
91
|
+
return { id: entry.id, provider: entry.provider, secrets: resolveSecrets(entry.secrets, this.env, `credential:${entry.id}`, this.secretStore) };
|
|
92
|
+
}
|
|
93
|
+
hasConnection(id) {
|
|
94
|
+
return this.connections.has(id);
|
|
95
|
+
}
|
|
96
|
+
providerFor(id) {
|
|
97
|
+
return this.requireProvider(this.entry(id, false).provider);
|
|
98
|
+
}
|
|
99
|
+
validateTarget(id, target) {
|
|
100
|
+
const result = this.providerFor(id).validateTarget(target);
|
|
101
|
+
if (!result.ok)
|
|
102
|
+
throw new ChannelError("CHANNEL_TARGET_INVALID", false, { connectionId: id });
|
|
103
|
+
return result.value;
|
|
104
|
+
}
|
|
105
|
+
/** Reads secrets from the environment at call time. Values are never cached on the registry. */
|
|
106
|
+
resolve(id, requireEnabled = true) {
|
|
107
|
+
const entry = this.entry(id, requireEnabled);
|
|
108
|
+
const secrets = resolveSecrets(entry.secrets, this.env, entry.id, this.secretStore);
|
|
109
|
+
const credentialEntry = entry.credential ? this.credentials.get(entry.credential) : undefined;
|
|
110
|
+
const credential = credentialEntry && {
|
|
111
|
+
id: credentialEntry.id,
|
|
112
|
+
provider: credentialEntry.provider,
|
|
113
|
+
secrets: resolveSecrets(credentialEntry.secrets, this.env, `credential:${credentialEntry.id}`, this.secretStore),
|
|
114
|
+
};
|
|
115
|
+
return sealed({
|
|
116
|
+
id: entry.id, scope: this.scope, provider: entry.provider, displayName: entry.displayName,
|
|
117
|
+
config: structuredClone(entry.config), secrets, ...(credential ? { credential } : {}),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
entry(id, requireEnabled) {
|
|
121
|
+
const entry = this.connections.get(id);
|
|
122
|
+
if (!entry)
|
|
123
|
+
throw new ChannelError("CHANNEL_NOT_FOUND", false, { connectionId: id });
|
|
124
|
+
if (requireEnabled && !entry.enabled)
|
|
125
|
+
throw new ChannelError("CHANNEL_DISABLED", false, { connectionId: id });
|
|
126
|
+
return entry;
|
|
127
|
+
}
|
|
128
|
+
requireProvider(id) {
|
|
129
|
+
const provider = this.providers.get(id);
|
|
130
|
+
if (!provider)
|
|
131
|
+
throw new ChannelError("CHANNEL_PROVIDER_UNSUPPORTED");
|
|
132
|
+
return provider;
|
|
133
|
+
}
|
|
134
|
+
addCredential(entry) {
|
|
135
|
+
this.credentials.set(entry.id, entry);
|
|
136
|
+
}
|
|
137
|
+
addConnection(entry) {
|
|
138
|
+
const provider = this.providers.get(entry.provider);
|
|
139
|
+
if (!provider)
|
|
140
|
+
throw new ChannelError("CHANNEL_PROVIDER_UNSUPPORTED", false, { connectionId: entry.id });
|
|
141
|
+
const config = provider.validateConfig(entry.config);
|
|
142
|
+
if (!config.ok)
|
|
143
|
+
throw new ChannelError("CHANNEL_INVALID_INPUT", false, { connectionId: entry.id, field: "config" });
|
|
144
|
+
this.checkSecretNames(provider, entry);
|
|
145
|
+
this.checkCredential(provider, entry);
|
|
146
|
+
this.connections.set(entry.id, {
|
|
147
|
+
...entry, config: config.value, displayName: entry.displayName ?? entry.id,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
checkSecretNames(provider, entry) {
|
|
151
|
+
const allowed = new Set([...provider.secrets.required, ...(provider.secrets.optional ?? [])]);
|
|
152
|
+
for (const name of Object.keys(entry.secrets)) {
|
|
153
|
+
if (!allowed.has(name))
|
|
154
|
+
throw new ChannelError("CHANNEL_INVALID_INPUT", false, { connectionId: entry.id, field: name });
|
|
155
|
+
}
|
|
156
|
+
for (const name of provider.secrets.required) {
|
|
157
|
+
if (!(name in entry.secrets))
|
|
158
|
+
throw new ChannelError("CHANNEL_INVALID_INPUT", false, { connectionId: entry.id, field: name });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
checkCredential(provider, entry) {
|
|
162
|
+
if (!provider.credentialProvider) {
|
|
163
|
+
if (entry.credential)
|
|
164
|
+
throw new ChannelError("CHANNEL_INVALID_INPUT", false, { connectionId: entry.id, field: "credential" });
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const credential = entry.credential ? this.credentials.get(entry.credential) : undefined;
|
|
168
|
+
if (!credential || credential.provider !== provider.credentialProvider) {
|
|
169
|
+
throw new ChannelError("CHANNEL_INVALID_INPUT", false, { connectionId: entry.id, field: "credential" });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
export function sealed(value) {
|
|
174
|
+
const { secrets, credential, ...visible } = value;
|
|
175
|
+
const out = { ...visible };
|
|
176
|
+
Object.defineProperty(out, "secrets", { value: secrets, enumerable: false });
|
|
177
|
+
if (credential) {
|
|
178
|
+
const { secrets: credentialSecrets, ...credentialVisible } = credential;
|
|
179
|
+
const sealedCredential = { ...credentialVisible };
|
|
180
|
+
Object.defineProperty(sealedCredential, "secrets", { value: credentialSecrets, enumerable: false });
|
|
181
|
+
Object.defineProperty(out, "credential", { value: sealedCredential, enumerable: false });
|
|
182
|
+
}
|
|
183
|
+
Object.defineProperty(out, "toJSON", { value: () => visible, enumerable: false });
|
|
184
|
+
Object.defineProperty(out, inspect.custom, { value: () => ({ ...visible, secrets: "[redacted]" }), enumerable: false });
|
|
185
|
+
return out;
|
|
186
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const STORE_NAME_PATTERN: RegExp;
|
|
2
|
+
/** Read side used when resolving `store:NAME` references. */
|
|
3
|
+
export interface SecretReader {
|
|
4
|
+
get(name: string): string | undefined;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Secrets entered through the console. One JSON file, owner-only, replaced atomically. It lives
|
|
8
|
+
* in the credential root next to provider auth, so it has the same trust level as that file:
|
|
9
|
+
* anything running as the same user can read it. Values are never logged or returned by the API.
|
|
10
|
+
*/
|
|
11
|
+
export declare class ChannelSecretStore implements SecretReader {
|
|
12
|
+
private readonly file;
|
|
13
|
+
constructor(file: string);
|
|
14
|
+
private read;
|
|
15
|
+
private write;
|
|
16
|
+
get(name: string): string | undefined;
|
|
17
|
+
has(name: string): boolean;
|
|
18
|
+
set(name: string, value: string): void;
|
|
19
|
+
delete(name: string): void;
|
|
20
|
+
deleteWithPrefix(prefix: string): void;
|
|
21
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { ChannelError } from "./errors.js";
|
|
4
|
+
export const STORE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,199}$/;
|
|
5
|
+
const MAX_STORE_BYTES = 1048576;
|
|
6
|
+
const MAX_SECRET_LENGTH = 8192;
|
|
7
|
+
/**
|
|
8
|
+
* Secrets entered through the console. One JSON file, owner-only, replaced atomically. It lives
|
|
9
|
+
* in the credential root next to provider auth, so it has the same trust level as that file:
|
|
10
|
+
* anything running as the same user can read it. Values are never logged or returned by the API.
|
|
11
|
+
*/
|
|
12
|
+
export class ChannelSecretStore {
|
|
13
|
+
file;
|
|
14
|
+
constructor(file) {
|
|
15
|
+
this.file = file;
|
|
16
|
+
}
|
|
17
|
+
read() {
|
|
18
|
+
if (!existsSync(this.file))
|
|
19
|
+
return {};
|
|
20
|
+
try {
|
|
21
|
+
if (statSync(this.file).size > MAX_STORE_BYTES)
|
|
22
|
+
throw new ChannelError("CHANNEL_RESOURCE_LIMIT");
|
|
23
|
+
const parsed = JSON.parse(readFileSync(this.file, "utf8"));
|
|
24
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
25
|
+
throw new ChannelError("CHANNEL_STORAGE_FAILED");
|
|
26
|
+
const out = {};
|
|
27
|
+
for (const [name, value] of Object.entries(parsed))
|
|
28
|
+
if (typeof value === "string")
|
|
29
|
+
out[name] = value;
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (error instanceof ChannelError)
|
|
34
|
+
throw error;
|
|
35
|
+
throw new ChannelError("CHANNEL_STORAGE_FAILED");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
write(values) {
|
|
39
|
+
const temporary = `${this.file}.${process.pid}.tmp`;
|
|
40
|
+
try {
|
|
41
|
+
mkdirSync(path.dirname(this.file), { recursive: true, mode: 0o700 });
|
|
42
|
+
writeFileSync(temporary, JSON.stringify(values), { mode: 0o600 });
|
|
43
|
+
renameSync(temporary, this.file);
|
|
44
|
+
chmodSync(this.file, 0o600);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
throw new ChannelError("CHANNEL_STORAGE_FAILED");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
get(name) {
|
|
51
|
+
return this.read()[name];
|
|
52
|
+
}
|
|
53
|
+
has(name) {
|
|
54
|
+
return this.get(name) !== undefined;
|
|
55
|
+
}
|
|
56
|
+
set(name, value) {
|
|
57
|
+
if (!STORE_NAME_PATTERN.test(name) || value.length === 0 || value.length > MAX_SECRET_LENGTH || /[\r\n\0]/.test(value)) {
|
|
58
|
+
throw new ChannelError("CHANNEL_INVALID_INPUT");
|
|
59
|
+
}
|
|
60
|
+
this.write({ ...this.read(), [name]: value });
|
|
61
|
+
}
|
|
62
|
+
delete(name) {
|
|
63
|
+
const values = this.read();
|
|
64
|
+
if (!(name in values))
|
|
65
|
+
return;
|
|
66
|
+
delete values[name];
|
|
67
|
+
this.write(values);
|
|
68
|
+
}
|
|
69
|
+
deleteWithPrefix(prefix) {
|
|
70
|
+
const values = this.read();
|
|
71
|
+
const kept = Object.fromEntries(Object.entries(values).filter(([name]) => !name.startsWith(prefix)));
|
|
72
|
+
if (Object.keys(kept).length !== Object.keys(values).length)
|
|
73
|
+
this.write(kept);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SecretReader } from "./secretStore.js";
|
|
2
|
+
import type { SecretRef } from "./types.js";
|
|
3
|
+
export declare const SECRET_REF_PATTERN: RegExp;
|
|
4
|
+
export declare function isSecretRef(value: unknown): value is SecretRef;
|
|
5
|
+
/**
|
|
6
|
+
* Resolves `env:NAME` with the repo's `NAME` / `NAME_FILE` convention, or `store:NAME` from the
|
|
7
|
+
* console secret store. Errors carry the connection id and field only, never a value or path.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveSecretRef(ref: SecretRef, env: NodeJS.ProcessEnv, where: {
|
|
10
|
+
connectionId: string;
|
|
11
|
+
field: string;
|
|
12
|
+
}, store?: SecretReader): string;
|
|
13
|
+
export declare function resolveSecrets(refs: Readonly<Record<string, SecretRef>>, env: NodeJS.ProcessEnv, connectionId: string, store?: SecretReader): Record<string, string>;
|
|
14
|
+
/** Removes every secret value from text before it reaches an error, log, or tool result. */
|
|
15
|
+
export declare function redactSecrets(text: string, secrets: Iterable<string>): string;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { readSecretFromEnvOrFile, SecretFromEnvError } from "../config/secretFromEnvOrFile.js";
|
|
2
|
+
import { ChannelError } from "./errors.js";
|
|
3
|
+
export const SECRET_REF_PATTERN = /^(?:env:[A-Za-z_][A-Za-z0-9_]*|store:[A-Za-z0-9][A-Za-z0-9._-]{0,199})$/;
|
|
4
|
+
export function isSecretRef(value) {
|
|
5
|
+
return typeof value === "string" && SECRET_REF_PATTERN.test(value);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Resolves `env:NAME` with the repo's `NAME` / `NAME_FILE` convention, or `store:NAME` from the
|
|
9
|
+
* console secret store. Errors carry the connection id and field only, never a value or path.
|
|
10
|
+
*/
|
|
11
|
+
export function resolveSecretRef(ref, env, where, store) {
|
|
12
|
+
let value;
|
|
13
|
+
if (ref.startsWith("store:")) {
|
|
14
|
+
try {
|
|
15
|
+
value = store?.get(ref.slice("store:".length));
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
throw new ChannelError("CHANNEL_SECRET_UNAVAILABLE", false, where);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
const name = ref.slice("env:".length);
|
|
23
|
+
try {
|
|
24
|
+
value = readSecretFromEnvOrFile(env, name);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
if (error instanceof SecretFromEnvError)
|
|
28
|
+
throw new ChannelError("CHANNEL_SECRET_UNAVAILABLE", false, where);
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (value === undefined || value.length === 0)
|
|
33
|
+
throw new ChannelError("CHANNEL_SECRET_UNAVAILABLE", false, where);
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
export function resolveSecrets(refs, env, connectionId, store) {
|
|
37
|
+
const out = {};
|
|
38
|
+
for (const [field, ref] of Object.entries(refs))
|
|
39
|
+
out[field] = resolveSecretRef(ref, env, { connectionId, field }, store);
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
/** Removes every secret value from text before it reaches an error, log, or tool result. */
|
|
43
|
+
export function redactSecrets(text, secrets) {
|
|
44
|
+
let result = text;
|
|
45
|
+
for (const secret of secrets) {
|
|
46
|
+
if (secret.length >= 4)
|
|
47
|
+
result = result.split(secret).join("[redacted]");
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type ChannelErrorCode } from "./errors.js";
|
|
2
|
+
import { type ChannelHealthOptions } from "./health.js";
|
|
3
|
+
import type { ConnectionHealth, ConnectionSummary, ProviderMeta } from "./types.js";
|
|
4
|
+
export type ChannelListing = {
|
|
5
|
+
connections: Array<ConnectionSummary & {
|
|
6
|
+
health?: ConnectionHealth;
|
|
7
|
+
}>;
|
|
8
|
+
/** Set when `connections.yaml` could not be loaded. The file's content is never included. */
|
|
9
|
+
error?: ChannelErrorCode;
|
|
10
|
+
limits: {
|
|
11
|
+
channelsPerStage: number;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
export type ChannelMeta = {
|
|
15
|
+
providers: Array<ProviderMeta & {
|
|
16
|
+
id: string;
|
|
17
|
+
}>;
|
|
18
|
+
errors: Record<string, {
|
|
19
|
+
reason: string;
|
|
20
|
+
hint?: string;
|
|
21
|
+
}>;
|
|
22
|
+
};
|
|
23
|
+
export type ChannelService = {
|
|
24
|
+
/** What the console shows about each provider and each error code. Static; needs no secrets. */
|
|
25
|
+
meta(): ChannelMeta;
|
|
26
|
+
list(): ChannelListing;
|
|
27
|
+
/** Runs one live check now. Returns undefined when the connection is unknown or disabled. */
|
|
28
|
+
checkNow(id: string): Promise<ConnectionHealth | undefined>;
|
|
29
|
+
/** Checks entered values without saving anything. */
|
|
30
|
+
test(input: unknown): Promise<ConnectionHealth>;
|
|
31
|
+
/** Static check of a pinned recipient for a connection. No network, no secret read. */
|
|
32
|
+
validateTarget(id: string, input: unknown): {
|
|
33
|
+
ok: true;
|
|
34
|
+
} | {
|
|
35
|
+
ok: false;
|
|
36
|
+
issues: string[];
|
|
37
|
+
};
|
|
38
|
+
create(input: unknown): Promise<{
|
|
39
|
+
id: string;
|
|
40
|
+
health: ConnectionHealth;
|
|
41
|
+
}>;
|
|
42
|
+
update(id: string, input: unknown): Promise<{
|
|
43
|
+
health?: ConnectionHealth;
|
|
44
|
+
}>;
|
|
45
|
+
remove(id: string): Promise<void>;
|
|
46
|
+
stop(): Promise<void>;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Host-side read and edit model for the Connections page. A `connections.yaml` that fails to load
|
|
50
|
+
* does not stop the host: the listing reports the error code and stages that use channels fail on
|
|
51
|
+
* their own, while every other stage is unaffected. Edits keep secrets out of the YAML: each value
|
|
52
|
+
* goes to the secret store and the file gets a `store:NAME` reference.
|
|
53
|
+
*/
|
|
54
|
+
export declare function startChannelService(cwd: string, options?: Pick<ChannelHealthOptions, "intervalMs" | "timeoutMs">): ChannelService;
|