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
package/dist/server/http.js
CHANGED
|
@@ -7,20 +7,32 @@ import { mapProviderAuthError } from "../agent/providerInspect.js";
|
|
|
7
7
|
import { handleProviderRoutes } from "./providerRoutes.js";
|
|
8
8
|
import { handleProjectMcpRoutes } from "./projectMcpRoutes.js";
|
|
9
9
|
import { handleTriggerRoutes } from "./triggerRoutes.js";
|
|
10
|
+
import { readCatalogFileForHttp, writeCatalogFileForHttp, } from "./readCatalogFile.js";
|
|
11
|
+
import { applyLiveViewHeaders, createLiveViewRoutes, isLiveViewDialogPath, isLiveViewInputPath, isLiveViewPath, isLiveViewReopenTabPath, } from "./liveViewRoutes.js";
|
|
10
12
|
import { createPipeline, parseCreatePipelineBody } from "../config/createPipeline.js";
|
|
11
13
|
import { createStage, parseCreateStageBody } from "../config/createStage.js";
|
|
12
|
-
import {
|
|
14
|
+
import { createTask, parseCreateTaskBody } from "../config/createTask.js";
|
|
15
|
+
import { importTaskFromIssue, parseImportTaskBody } from "../config/importTask.js";
|
|
16
|
+
import { parseUpdateTaskBody, updateTask } from "../config/updateTask.js";
|
|
17
|
+
import { loadDraftPackage, loadTaskArtifact, parseAttachTaskBody, parseCreateDraftPackageBody, parseDraftPackageBody, parseOpenDraftPackageBody, parseOverwriteDraftPackageBody, parsePlanDraftPackageBody, planDraftPackage, validateDraftPackage, } from "../config/draftPackage.js";
|
|
13
18
|
import { publishDraftPackage } from "../config/publishDraftPackage.js";
|
|
14
19
|
import { clearWorkshopAutosave, detectDiskChange, draftPackageDiskRelativePaths, fingerprintPackageFiles, parseWorkshopAutosaveRecord, readWorkshopAutosave, resolveWorkshopAutosaveStoreRoot, workshopAutosaveSlotKey, writeWorkshopAutosave, } from "../workshop/autosave.js";
|
|
15
20
|
import { acceptWorkshopSessionMutation, iterateWorkshopChatStreamFrames, runWorkshopChatTurn, stopWorkshopChatTurn, undoWorkshopSessionMutation, WorkshopChatSessionRegistry, WorkshopSessionStoreError, } from "../workshop/chatTurn.js";
|
|
21
|
+
import { parseWorkshopChatAttachments, parseWorkshopChatContext, } from "../workshop/chatAttachments.js";
|
|
16
22
|
import { readWorkshopOpenModel, resolveWorkshopModel, } from "../workshop/modelSettings.js";
|
|
17
23
|
import { createWorkshopSession, getWorkshopSession, listWorkshopSessions, resolveWorkshopSessionStoreRoot, updateWorkshopSessionActiveBuildId, } from "../workshop/sessionStore.js";
|
|
18
24
|
import { createWorkshopBuild, focusUnboundWorkshopBuild, getWorkshopBuild, listWorkshopBuilds, listWorkshopPickerRows, resolvePickerCatalogPipelines, resolveWorkshopBuildStoreRoot, updateWorkshopBuild, WorkshopBuildStoreError, } from "../workshop/buildStore.js";
|
|
19
|
-
import {
|
|
25
|
+
import { resolveAgentPort } from "../operatorAgent/index.js";
|
|
26
|
+
import { listPipelinesForContext } from "../config/browseCatalog.js";
|
|
27
|
+
import { catalogContextFromStageflow } from "../config/resolveCatalogContext.js";
|
|
20
28
|
import { listModelsMultiProject, listPipelinesMultiProject, listTasksMultiProject, } from "../config/multiProjectCatalog.js";
|
|
21
|
-
import { catalogPathErrorBody, CatalogPathError, resolveCatalogRelativePath, resolveCatalogStartInput, resolveWritableCatalogRoot, } from "../config/catalogRelativePath.js";
|
|
29
|
+
import { catalogPathErrorBody, CatalogPathError, resolveCatalogRelativePath, resolveCatalogStartInput, resolveDefaultWriteRoot, resolveWritableCatalogRoot, } from "../config/catalogRelativePath.js";
|
|
22
30
|
import { listExtensions } from "../config/listExtensions.js";
|
|
23
31
|
import { listSkills } from "../config/listSkills.js";
|
|
32
|
+
import { findTaskInCatalogMulti } from "../config/findTaskInCatalog.js";
|
|
33
|
+
import { buildSkillUsageIndex } from "../config/skillUsageIndex.js";
|
|
34
|
+
import { installSkillFromCommand, parseInstallSkillBody, } from "../config/installSkillRequest.js";
|
|
35
|
+
import { validateCatalog } from "../config/validateCatalog.js";
|
|
24
36
|
import { artifactMediaType, classifyArtifactContent, readRunArtifact, readRunArtifactBytes, } from "../mcp/readArtifact.js";
|
|
25
37
|
import { getRunDiff, listCheckoutChanges, readCheckoutFileBytes, } from "../mcp/checkoutTools.js";
|
|
26
38
|
import { readStageVerificationHistory } from "../runstore/verificationHistory.js";
|
|
@@ -39,17 +51,25 @@ import { isTaskFile } from "../runtime/taskInput.js";
|
|
|
39
51
|
import { findTokenShapedField, tokenRejectedPayload, } from "../runtime/startPayload.js";
|
|
40
52
|
import { parseAskOperatorAnswer } from "../tools/askOperator.js";
|
|
41
53
|
import { handleEmailRoutes } from "./emailRoutes.js";
|
|
54
|
+
import { handleChannelRoutes } from "./channelRoutes.js";
|
|
42
55
|
import { bootstrapStageflowHost, } from "./bootstrap.js";
|
|
43
56
|
import { assertAllowedHttpAccess, isTrustedLocalHttpRequest, resolveAllowedHosts, } from "./allowedHosts.js";
|
|
44
57
|
import { enforceBearerAuth, loadControlTokens, requiredScopeFor, } from "./controlToken.js";
|
|
45
58
|
import { createHttpHost, DEFAULT_PORT, json, } from "./createHttpHost.js";
|
|
46
|
-
import { handleApiHealth } from "./healthSurfaces.js";
|
|
59
|
+
import { handleApiHealth, listCatalogRootsForApi } from "./healthSurfaces.js";
|
|
47
60
|
import { mapRetryStageFailure, mapStartFailure, mapStoreLookupError, } from "./operatorResults.js";
|
|
48
61
|
import { installShutdownController, makeDrainableHostFromOptional, } from "./shutdown.js";
|
|
49
62
|
import { writeAudit } from "../logging/audit.js";
|
|
50
63
|
import { logger as rootLogger } from "../logging/logger.js";
|
|
51
64
|
import { callerIdFromRequestAuth, getRequestAuth, requestAuthFromBearer, runWithRequestAuth, } from "./requestAuthContext.js";
|
|
52
65
|
const auditLog = rootLogger.child({ component: "audit" });
|
|
66
|
+
function draftCatalogRootWire(root) {
|
|
67
|
+
return {
|
|
68
|
+
project_root: root.project_root,
|
|
69
|
+
root_kind: root.kind,
|
|
70
|
+
read_only: root.read_only,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
53
73
|
function textPlain(res, status, body) {
|
|
54
74
|
res.writeHead(status, {
|
|
55
75
|
"Content-Type": "text/plain; charset=utf-8",
|
|
@@ -137,7 +157,10 @@ async function serveStatic(res, uiDistDir, urlPath) {
|
|
|
137
157
|
function isCredentialMutatingApi(method, pathname) {
|
|
138
158
|
if (method !== "POST")
|
|
139
159
|
return false;
|
|
140
|
-
return (
|
|
160
|
+
return (isLiveViewInputPath(method, pathname) ||
|
|
161
|
+
isLiveViewDialogPath(method, pathname) ||
|
|
162
|
+
isLiveViewReopenTabPath(method, pathname) ||
|
|
163
|
+
/^\/api\/providers\/[^/]+\/login$/.test(pathname) ||
|
|
141
164
|
/^\/api\/providers\/[^/]+\/login\/[^/]+\/answer$/.test(pathname) ||
|
|
142
165
|
/^\/api\/providers\/[^/]+\/login\/[^/]+\/cancel$/.test(pathname) ||
|
|
143
166
|
/^\/api\/providers\/[^/]+\/logout$/.test(pathname));
|
|
@@ -149,18 +172,11 @@ export function defaultUiDistDir() {
|
|
|
149
172
|
}
|
|
150
173
|
return path.resolve(here, "../ui");
|
|
151
174
|
}
|
|
152
|
-
/**
|
|
153
|
-
* The full operator-console route surface: REST API (runs, stages,
|
|
154
|
-
* catalog, settings, providers) plus, when `uiDistDir` is given, static
|
|
155
|
-
* console UI file serving. Shared by `startUiServer` (browser-facing) and
|
|
156
|
-
* `startMcpServer` (headless global-service daemon) — both need the REST
|
|
157
|
-
* API; only the former needs the UI files.
|
|
158
|
-
*/
|
|
159
175
|
export function createOperatorRoutes(deps) {
|
|
160
176
|
const { manager, store, cwd, agentDir, rootDir, providerAuthContext, uiDistDir, workshopOperatorHost, } = deps;
|
|
161
177
|
const workshopChatRegistry = deps.workshopChatRegistry ??
|
|
162
178
|
new WorkshopChatSessionRegistry(workshopOperatorHost ??
|
|
163
|
-
|
|
179
|
+
resolveAgentPort({
|
|
164
180
|
cwd,
|
|
165
181
|
projectRoot: cwd,
|
|
166
182
|
}));
|
|
@@ -168,7 +184,11 @@ export function createOperatorRoutes(deps) {
|
|
|
168
184
|
const workshopBuildStoreRoot = () => resolveWorkshopBuildStoreRoot();
|
|
169
185
|
const allowedHosts = deps.allowedHosts ?? resolveAllowedHosts();
|
|
170
186
|
const controlTokens = deps.controlTokens ?? loadControlTokens();
|
|
171
|
-
|
|
187
|
+
const liveView = createLiveViewRoutes({ ...deps.liveView, store, controlTokens });
|
|
188
|
+
liveView.attach(manager);
|
|
189
|
+
const handler = async ({ req, res, url, pathname, method, boot }) => {
|
|
190
|
+
if (isLiveViewPath(pathname))
|
|
191
|
+
applyLiveViewHeaders(res);
|
|
172
192
|
if (boot.serveBlocked !== undefined && pathname.startsWith("/api/")) {
|
|
173
193
|
json(res, 503, {
|
|
174
194
|
error: boot.serveBlocked.reason,
|
|
@@ -193,6 +213,18 @@ export function createOperatorRoutes(deps) {
|
|
|
193
213
|
return handleOperatorRequest();
|
|
194
214
|
async function handleOperatorRequest() {
|
|
195
215
|
try {
|
|
216
|
+
if (await liveView.handle(req, res))
|
|
217
|
+
return true;
|
|
218
|
+
if (pathname === "/api/connections" || pathname.startsWith("/api/connections/")) {
|
|
219
|
+
if (!assertAllowedHttpAccess({ entries: [] }, req, res, { requireOrigin: method !== "GET" }))
|
|
220
|
+
return true;
|
|
221
|
+
if (!boot.channels) {
|
|
222
|
+
json(res, 503, { code: "CHANNEL_CONNECTION_FAILED" });
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
if (await handleChannelRoutes(req, method, pathname, boot.channels, res, json))
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
196
228
|
if (pathname.startsWith("/api/email/")) {
|
|
197
229
|
if (!assertAllowedHttpAccess({ entries: [] }, req, res, { requireOrigin: ["POST", "PATCH", "DELETE"].includes(method) }))
|
|
198
230
|
return true;
|
|
@@ -1044,6 +1076,356 @@ export function createOperatorRoutes(deps) {
|
|
|
1044
1076
|
});
|
|
1045
1077
|
return true;
|
|
1046
1078
|
}
|
|
1079
|
+
if (method === "GET" && pathname === "/api/catalog/roots") {
|
|
1080
|
+
const roots = await listCatalogRootsForApi({ store, bootCwd: cwd });
|
|
1081
|
+
json(res, 200, { roots });
|
|
1082
|
+
return true;
|
|
1083
|
+
}
|
|
1084
|
+
if (method === "POST" && pathname === "/api/catalog/validate") {
|
|
1085
|
+
let body;
|
|
1086
|
+
try {
|
|
1087
|
+
body = await readJsonBody(req);
|
|
1088
|
+
}
|
|
1089
|
+
catch {
|
|
1090
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
1091
|
+
return true;
|
|
1092
|
+
}
|
|
1093
|
+
if (body !== null && typeof body === "object" && !Array.isArray(body)) {
|
|
1094
|
+
const record = body;
|
|
1095
|
+
let pipeline = typeof record.pipeline === "string" ? record.pipeline : undefined;
|
|
1096
|
+
let task = typeof record.task === "string" ? record.task : undefined;
|
|
1097
|
+
let stage = typeof record.stage === "string" ? record.stage : undefined;
|
|
1098
|
+
const strict = record.strict === true;
|
|
1099
|
+
const projectRoot = typeof record.project_root === "string" && record.project_root.trim()
|
|
1100
|
+
? record.project_root.trim()
|
|
1101
|
+
: undefined;
|
|
1102
|
+
const scopedCount = [pipeline, task, stage].filter((v) => v !== undefined).length;
|
|
1103
|
+
if (scopedCount > 1) {
|
|
1104
|
+
json(res, 400, {
|
|
1105
|
+
error: "Use at most one of pipeline, task, or stage",
|
|
1106
|
+
});
|
|
1107
|
+
return true;
|
|
1108
|
+
}
|
|
1109
|
+
let validateCwd = cwd;
|
|
1110
|
+
if (pipeline !== undefined || task !== undefined || stage !== undefined) {
|
|
1111
|
+
try {
|
|
1112
|
+
const { wireRoot, roots } = await resolveCatalogStartInput({ store, bootCwd: cwd }, projectRoot);
|
|
1113
|
+
validateCwd = wireRoot.path;
|
|
1114
|
+
if (pipeline !== undefined) {
|
|
1115
|
+
pipeline = resolveCatalogRelativePath({
|
|
1116
|
+
inputPath: pipeline.trim(),
|
|
1117
|
+
projectRoot: wireRoot.project_root,
|
|
1118
|
+
roots,
|
|
1119
|
+
fieldName: "pipeline",
|
|
1120
|
+
}).absolutePath;
|
|
1121
|
+
}
|
|
1122
|
+
if (task !== undefined) {
|
|
1123
|
+
task = resolveCatalogRelativePath({
|
|
1124
|
+
inputPath: task.trim(),
|
|
1125
|
+
projectRoot: wireRoot.project_root,
|
|
1126
|
+
roots,
|
|
1127
|
+
fieldName: "task",
|
|
1128
|
+
}).absolutePath;
|
|
1129
|
+
}
|
|
1130
|
+
if (stage !== undefined) {
|
|
1131
|
+
stage = resolveCatalogRelativePath({
|
|
1132
|
+
inputPath: stage.trim(),
|
|
1133
|
+
projectRoot: wireRoot.project_root,
|
|
1134
|
+
roots,
|
|
1135
|
+
fieldName: "stage",
|
|
1136
|
+
}).absolutePath;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
catch (err) {
|
|
1140
|
+
if (err instanceof CatalogPathError) {
|
|
1141
|
+
json(res, 400, catalogPathErrorBody(err));
|
|
1142
|
+
return true;
|
|
1143
|
+
}
|
|
1144
|
+
throw err;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
const scope = pipeline
|
|
1148
|
+
? "pipeline"
|
|
1149
|
+
: task
|
|
1150
|
+
? "task"
|
|
1151
|
+
: stage
|
|
1152
|
+
? "stage"
|
|
1153
|
+
: "full";
|
|
1154
|
+
const stageContent = typeof record.content === "string" ? record.content : undefined;
|
|
1155
|
+
if (stageContent !== undefined && stage === undefined) {
|
|
1156
|
+
json(res, 400, {
|
|
1157
|
+
error: "content is only supported with stage",
|
|
1158
|
+
});
|
|
1159
|
+
return true;
|
|
1160
|
+
}
|
|
1161
|
+
const result = await validateCatalog({
|
|
1162
|
+
scope,
|
|
1163
|
+
cwd: validateCwd,
|
|
1164
|
+
...(pipeline !== undefined ||
|
|
1165
|
+
task !== undefined ||
|
|
1166
|
+
stage !== undefined
|
|
1167
|
+
? { projectRoot: validateCwd }
|
|
1168
|
+
: {}),
|
|
1169
|
+
pipeline,
|
|
1170
|
+
task,
|
|
1171
|
+
stage,
|
|
1172
|
+
...(stageContent !== undefined ? { stageContent } : {}),
|
|
1173
|
+
strict,
|
|
1174
|
+
});
|
|
1175
|
+
json(res, 200, result);
|
|
1176
|
+
return true;
|
|
1177
|
+
}
|
|
1178
|
+
json(res, 400, { error: "Request body must be an object" });
|
|
1179
|
+
return true;
|
|
1180
|
+
}
|
|
1181
|
+
if (method === "GET" && pathname === "/api/catalog/file") {
|
|
1182
|
+
const relPath = url.searchParams.get("path");
|
|
1183
|
+
if (relPath === null || relPath.length === 0) {
|
|
1184
|
+
json(res, 400, { error: "path query parameter is required" });
|
|
1185
|
+
return true;
|
|
1186
|
+
}
|
|
1187
|
+
const projectRoot = url.searchParams.get("project_root") ?? undefined;
|
|
1188
|
+
try {
|
|
1189
|
+
const { roots } = await resolveCatalogStartInput({ store, bootCwd: cwd }, projectRoot);
|
|
1190
|
+
const result = await readCatalogFileForHttp({
|
|
1191
|
+
inputPath: relPath,
|
|
1192
|
+
projectRoot,
|
|
1193
|
+
roots,
|
|
1194
|
+
fieldName: "path",
|
|
1195
|
+
});
|
|
1196
|
+
if (!result.ok) {
|
|
1197
|
+
json(res, result.status, { error: result.error, code: result.code });
|
|
1198
|
+
return true;
|
|
1199
|
+
}
|
|
1200
|
+
json(res, 200, { path: result.path, content: result.content });
|
|
1201
|
+
return true;
|
|
1202
|
+
}
|
|
1203
|
+
catch (err) {
|
|
1204
|
+
if (err instanceof CatalogPathError) {
|
|
1205
|
+
json(res, 400, catalogPathErrorBody(err));
|
|
1206
|
+
return true;
|
|
1207
|
+
}
|
|
1208
|
+
throw err;
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
if (method === "PUT" && pathname === "/api/catalog/file") {
|
|
1212
|
+
let body;
|
|
1213
|
+
try {
|
|
1214
|
+
body = await readJsonBody(req);
|
|
1215
|
+
}
|
|
1216
|
+
catch {
|
|
1217
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
1218
|
+
return true;
|
|
1219
|
+
}
|
|
1220
|
+
if (body === null || typeof body !== "object" || Array.isArray(body)) {
|
|
1221
|
+
json(res, 400, { error: "Request body must be an object" });
|
|
1222
|
+
return true;
|
|
1223
|
+
}
|
|
1224
|
+
const record = body;
|
|
1225
|
+
const relPath = typeof record.path === "string" ? record.path.trim() : "";
|
|
1226
|
+
if (!relPath) {
|
|
1227
|
+
json(res, 400, { error: "path is required" });
|
|
1228
|
+
return true;
|
|
1229
|
+
}
|
|
1230
|
+
if (typeof record.content !== "string") {
|
|
1231
|
+
json(res, 400, { error: "content must be a string" });
|
|
1232
|
+
return true;
|
|
1233
|
+
}
|
|
1234
|
+
const writeRoot = typeof record.project_root === "string"
|
|
1235
|
+
? record.project_root
|
|
1236
|
+
: undefined;
|
|
1237
|
+
try {
|
|
1238
|
+
const { wireRoot, roots } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1239
|
+
const result = await writeCatalogFileForHttp({
|
|
1240
|
+
inputPath: relPath,
|
|
1241
|
+
content: record.content,
|
|
1242
|
+
projectRoot: wireRoot.project_root,
|
|
1243
|
+
roots,
|
|
1244
|
+
fieldName: "path",
|
|
1245
|
+
});
|
|
1246
|
+
if (!result.ok) {
|
|
1247
|
+
json(res, result.status, {
|
|
1248
|
+
error: result.error,
|
|
1249
|
+
code: result.code,
|
|
1250
|
+
...(result.findings !== undefined
|
|
1251
|
+
? { findings: result.findings }
|
|
1252
|
+
: {}),
|
|
1253
|
+
});
|
|
1254
|
+
return true;
|
|
1255
|
+
}
|
|
1256
|
+
json(res, 200, { path: result.path, content: result.content });
|
|
1257
|
+
return true;
|
|
1258
|
+
}
|
|
1259
|
+
catch (err) {
|
|
1260
|
+
if (err instanceof CatalogPathError) {
|
|
1261
|
+
json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
|
|
1262
|
+
return true;
|
|
1263
|
+
}
|
|
1264
|
+
throw err;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
if (method === "POST" && pathname === "/api/tasks/import") {
|
|
1268
|
+
let body;
|
|
1269
|
+
try {
|
|
1270
|
+
body = await readJsonBody(req);
|
|
1271
|
+
}
|
|
1272
|
+
catch {
|
|
1273
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
1274
|
+
return true;
|
|
1275
|
+
}
|
|
1276
|
+
const parsed = parseImportTaskBody(body);
|
|
1277
|
+
if ("ok" in parsed) {
|
|
1278
|
+
json(res, parsed.status, { error: parsed.error });
|
|
1279
|
+
return true;
|
|
1280
|
+
}
|
|
1281
|
+
const writeRoot = body !== null &&
|
|
1282
|
+
typeof body === "object" &&
|
|
1283
|
+
!Array.isArray(body) &&
|
|
1284
|
+
typeof body.project_root === "string"
|
|
1285
|
+
? body.project_root
|
|
1286
|
+
: undefined;
|
|
1287
|
+
let taskWriteRoot;
|
|
1288
|
+
try {
|
|
1289
|
+
const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1290
|
+
taskWriteRoot = selected.path;
|
|
1291
|
+
}
|
|
1292
|
+
catch (err) {
|
|
1293
|
+
if (err instanceof CatalogPathError) {
|
|
1294
|
+
json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
|
|
1295
|
+
return true;
|
|
1296
|
+
}
|
|
1297
|
+
throw err;
|
|
1298
|
+
}
|
|
1299
|
+
const stageflowCtx = await resolveStageflowContext(taskWriteRoot);
|
|
1300
|
+
if (stageflowCtx.manifestStatus !== "ok") {
|
|
1301
|
+
json(res, 400, {
|
|
1302
|
+
error: "Project root not found; initialize stageflow.yaml",
|
|
1303
|
+
});
|
|
1304
|
+
return true;
|
|
1305
|
+
}
|
|
1306
|
+
const result = await importTaskFromIssue(stageflowCtx.projectRoot, parsed);
|
|
1307
|
+
if (!result.ok) {
|
|
1308
|
+
json(res, result.status, { error: result.error });
|
|
1309
|
+
return true;
|
|
1310
|
+
}
|
|
1311
|
+
json(res, 201, { task: result.task });
|
|
1312
|
+
return true;
|
|
1313
|
+
}
|
|
1314
|
+
if (method === "POST" && pathname === "/api/tasks") {
|
|
1315
|
+
let body;
|
|
1316
|
+
try {
|
|
1317
|
+
body = await readJsonBody(req);
|
|
1318
|
+
}
|
|
1319
|
+
catch {
|
|
1320
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
1321
|
+
return true;
|
|
1322
|
+
}
|
|
1323
|
+
const writeRoot = body !== null &&
|
|
1324
|
+
typeof body === "object" &&
|
|
1325
|
+
!Array.isArray(body) &&
|
|
1326
|
+
typeof body.project_root === "string"
|
|
1327
|
+
? body.project_root
|
|
1328
|
+
: undefined;
|
|
1329
|
+
let taskWriteRoot;
|
|
1330
|
+
try {
|
|
1331
|
+
const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1332
|
+
taskWriteRoot = selected.path;
|
|
1333
|
+
}
|
|
1334
|
+
catch (err) {
|
|
1335
|
+
if (err instanceof CatalogPathError) {
|
|
1336
|
+
json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
|
|
1337
|
+
return true;
|
|
1338
|
+
}
|
|
1339
|
+
throw err;
|
|
1340
|
+
}
|
|
1341
|
+
const parsed = parseCreateTaskBody(body);
|
|
1342
|
+
if ("ok" in parsed) {
|
|
1343
|
+
json(res, parsed.status, { error: parsed.error });
|
|
1344
|
+
return true;
|
|
1345
|
+
}
|
|
1346
|
+
const stageflowCtx = await resolveStageflowContext(taskWriteRoot);
|
|
1347
|
+
if (stageflowCtx.manifestStatus !== "ok") {
|
|
1348
|
+
json(res, 400, {
|
|
1349
|
+
error: "Project root not found; initialize stageflow.yaml",
|
|
1350
|
+
});
|
|
1351
|
+
return true;
|
|
1352
|
+
}
|
|
1353
|
+
const result = await createTask(stageflowCtx.projectRoot, parsed);
|
|
1354
|
+
if (!result.ok) {
|
|
1355
|
+
json(res, result.status, { error: result.error });
|
|
1356
|
+
return true;
|
|
1357
|
+
}
|
|
1358
|
+
json(res, 201, { task: result.task });
|
|
1359
|
+
return true;
|
|
1360
|
+
}
|
|
1361
|
+
const taskDetailMatch = pathname.match(/^\/api\/tasks\/([^/]+)$/);
|
|
1362
|
+
if (method === "GET" && taskDetailMatch) {
|
|
1363
|
+
const taskId = decodeURIComponent(taskDetailMatch[1] ?? "");
|
|
1364
|
+
const projectRootFilter = url.searchParams.get("project_root") ?? undefined;
|
|
1365
|
+
const lookup = await findTaskInCatalogMulti({
|
|
1366
|
+
store,
|
|
1367
|
+
bootCwd: cwd,
|
|
1368
|
+
taskId,
|
|
1369
|
+
projectRootFilter,
|
|
1370
|
+
});
|
|
1371
|
+
if (!lookup.ok) {
|
|
1372
|
+
if (lookup.status === 409) {
|
|
1373
|
+
json(res, 409, {
|
|
1374
|
+
error: lookup.error,
|
|
1375
|
+
code: lookup.code,
|
|
1376
|
+
project_roots: lookup.project_roots,
|
|
1377
|
+
});
|
|
1378
|
+
return true;
|
|
1379
|
+
}
|
|
1380
|
+
if (lookup.status === 400) {
|
|
1381
|
+
json(res, 400, {
|
|
1382
|
+
error: lookup.error,
|
|
1383
|
+
code: lookup.code,
|
|
1384
|
+
});
|
|
1385
|
+
return true;
|
|
1386
|
+
}
|
|
1387
|
+
json(res, 404, { error: lookup.error });
|
|
1388
|
+
return true;
|
|
1389
|
+
}
|
|
1390
|
+
json(res, 200, { task: lookup.task });
|
|
1391
|
+
return true;
|
|
1392
|
+
}
|
|
1393
|
+
if (method === "PUT" && taskDetailMatch) {
|
|
1394
|
+
const taskId = decodeURIComponent(taskDetailMatch[1] ?? "");
|
|
1395
|
+
const projectRootFilter = url.searchParams.get("project_root") ?? undefined;
|
|
1396
|
+
let body;
|
|
1397
|
+
try {
|
|
1398
|
+
body = await readJsonBody(req);
|
|
1399
|
+
}
|
|
1400
|
+
catch {
|
|
1401
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
1402
|
+
return true;
|
|
1403
|
+
}
|
|
1404
|
+
const parsed = parseUpdateTaskBody(body);
|
|
1405
|
+
if ("ok" in parsed) {
|
|
1406
|
+
json(res, parsed.status, { error: parsed.error });
|
|
1407
|
+
return true;
|
|
1408
|
+
}
|
|
1409
|
+
const result = await updateTask({
|
|
1410
|
+
store,
|
|
1411
|
+
bootCwd: cwd,
|
|
1412
|
+
taskId,
|
|
1413
|
+
input: parsed,
|
|
1414
|
+
projectRootFilter,
|
|
1415
|
+
});
|
|
1416
|
+
if (!result.ok) {
|
|
1417
|
+
json(res, result.status, {
|
|
1418
|
+
error: result.error,
|
|
1419
|
+
...(result.code !== undefined ? { code: result.code } : {}),
|
|
1420
|
+
...(result.project_roots !== undefined
|
|
1421
|
+
? { project_roots: result.project_roots }
|
|
1422
|
+
: {}),
|
|
1423
|
+
});
|
|
1424
|
+
return true;
|
|
1425
|
+
}
|
|
1426
|
+
json(res, 200, { task: result.task });
|
|
1427
|
+
return true;
|
|
1428
|
+
}
|
|
1047
1429
|
if (method === "GET" && pathname === "/api/tasks") {
|
|
1048
1430
|
const filter = url.searchParams.get("project_root") ?? undefined;
|
|
1049
1431
|
const result = await listTasksMultiProject({
|
|
@@ -1210,19 +1592,17 @@ export function createOperatorRoutes(deps) {
|
|
|
1210
1592
|
typeof body.project_root === "string"
|
|
1211
1593
|
? body.project_root
|
|
1212
1594
|
: undefined;
|
|
1213
|
-
let validateRoot
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
return true;
|
|
1223
|
-
}
|
|
1224
|
-
throw err;
|
|
1595
|
+
let validateRoot;
|
|
1596
|
+
try {
|
|
1597
|
+
const { wireRoot: selected } = await resolveDefaultWriteRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1598
|
+
validateRoot = selected.path;
|
|
1599
|
+
}
|
|
1600
|
+
catch (err) {
|
|
1601
|
+
if (err instanceof CatalogPathError) {
|
|
1602
|
+
json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
|
|
1603
|
+
return true;
|
|
1225
1604
|
}
|
|
1605
|
+
throw err;
|
|
1226
1606
|
}
|
|
1227
1607
|
const ctx = await resolveStageflowContext(validateRoot);
|
|
1228
1608
|
const result = await validateDraftPackage(draftParsed, {
|
|
@@ -1249,9 +1629,11 @@ export function createOperatorRoutes(deps) {
|
|
|
1249
1629
|
? body.project_root
|
|
1250
1630
|
: undefined;
|
|
1251
1631
|
let draftWriteRoot;
|
|
1632
|
+
let draftWriteWireRoot;
|
|
1252
1633
|
try {
|
|
1253
1634
|
const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1254
1635
|
draftWriteRoot = selected.path;
|
|
1636
|
+
draftWriteWireRoot = selected;
|
|
1255
1637
|
}
|
|
1256
1638
|
catch (err) {
|
|
1257
1639
|
if (err instanceof CatalogPathError) {
|
|
@@ -1266,13 +1648,15 @@ export function createOperatorRoutes(deps) {
|
|
|
1266
1648
|
return true;
|
|
1267
1649
|
}
|
|
1268
1650
|
const ctx = await resolveStageflowContext(draftWriteRoot);
|
|
1269
|
-
if (
|
|
1651
|
+
if (ctx.manifestStatus !== "ok") {
|
|
1270
1652
|
json(res, 400, {
|
|
1271
|
-
error: "Project root not found; initialize stageflow.yaml
|
|
1653
|
+
error: "Project root not found; initialize stageflow.yaml",
|
|
1272
1654
|
});
|
|
1273
1655
|
return true;
|
|
1274
1656
|
}
|
|
1275
|
-
const result = (await publishDraftPackage(ctx.projectRoot, parsed, "create"
|
|
1657
|
+
const result = (await publishDraftPackage(ctx.projectRoot, parsed, "create", {
|
|
1658
|
+
catalogRootKind: draftWriteWireRoot.kind,
|
|
1659
|
+
})).write;
|
|
1276
1660
|
if (!result.ok) {
|
|
1277
1661
|
json(res, result.status, {
|
|
1278
1662
|
error: result.error,
|
|
@@ -1284,6 +1668,8 @@ export function createOperatorRoutes(deps) {
|
|
|
1284
1668
|
pipeline: result.pipeline,
|
|
1285
1669
|
pipelinePath: result.pipelinePath,
|
|
1286
1670
|
stagePaths: result.stagePaths,
|
|
1671
|
+
catalog_root_path: ctx.projectRoot,
|
|
1672
|
+
...draftCatalogRootWire(draftWriteWireRoot),
|
|
1287
1673
|
...(result.taskPath !== undefined ? { taskPath: result.taskPath } : {}),
|
|
1288
1674
|
});
|
|
1289
1675
|
return true;
|
|
@@ -1304,9 +1690,11 @@ export function createOperatorRoutes(deps) {
|
|
|
1304
1690
|
? body.project_root
|
|
1305
1691
|
: undefined;
|
|
1306
1692
|
let draftWriteRoot;
|
|
1693
|
+
let draftWriteWireRoot;
|
|
1307
1694
|
try {
|
|
1308
1695
|
const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1309
1696
|
draftWriteRoot = selected.path;
|
|
1697
|
+
draftWriteWireRoot = selected;
|
|
1310
1698
|
}
|
|
1311
1699
|
catch (err) {
|
|
1312
1700
|
if (err instanceof CatalogPathError) {
|
|
@@ -1321,13 +1709,15 @@ export function createOperatorRoutes(deps) {
|
|
|
1321
1709
|
return true;
|
|
1322
1710
|
}
|
|
1323
1711
|
const ctx = await resolveStageflowContext(draftWriteRoot);
|
|
1324
|
-
if (
|
|
1712
|
+
if (ctx.manifestStatus !== "ok") {
|
|
1325
1713
|
json(res, 400, {
|
|
1326
|
-
error: "Project root not found; initialize stageflow.yaml
|
|
1714
|
+
error: "Project root not found; initialize stageflow.yaml",
|
|
1327
1715
|
});
|
|
1328
1716
|
return true;
|
|
1329
1717
|
}
|
|
1330
|
-
const result = (await publishDraftPackage(ctx.projectRoot, parsed, "overwrite"
|
|
1718
|
+
const result = (await publishDraftPackage(ctx.projectRoot, parsed, "overwrite", {
|
|
1719
|
+
catalogRootKind: draftWriteWireRoot.kind,
|
|
1720
|
+
})).write;
|
|
1331
1721
|
if (!result.ok) {
|
|
1332
1722
|
json(res, result.status, {
|
|
1333
1723
|
error: result.error,
|
|
@@ -1339,10 +1729,70 @@ export function createOperatorRoutes(deps) {
|
|
|
1339
1729
|
pipeline: result.pipeline,
|
|
1340
1730
|
pipelinePath: result.pipelinePath,
|
|
1341
1731
|
stagePaths: result.stagePaths,
|
|
1732
|
+
catalog_root_path: ctx.projectRoot,
|
|
1733
|
+
...draftCatalogRootWire(draftWriteWireRoot),
|
|
1342
1734
|
...(result.taskPath !== undefined ? { taskPath: result.taskPath } : {}),
|
|
1343
1735
|
});
|
|
1344
1736
|
return true;
|
|
1345
1737
|
}
|
|
1738
|
+
if (method === "POST" && pathname === "/api/drafts/plan") {
|
|
1739
|
+
let body;
|
|
1740
|
+
try {
|
|
1741
|
+
body = await readJsonBody(req);
|
|
1742
|
+
}
|
|
1743
|
+
catch {
|
|
1744
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
1745
|
+
return true;
|
|
1746
|
+
}
|
|
1747
|
+
const writeRoot = body !== null &&
|
|
1748
|
+
typeof body === "object" &&
|
|
1749
|
+
!Array.isArray(body) &&
|
|
1750
|
+
typeof body.project_root === "string"
|
|
1751
|
+
? body.project_root
|
|
1752
|
+
: undefined;
|
|
1753
|
+
let draftWriteRoot;
|
|
1754
|
+
try {
|
|
1755
|
+
const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1756
|
+
draftWriteRoot = selected.path;
|
|
1757
|
+
}
|
|
1758
|
+
catch (err) {
|
|
1759
|
+
if (err instanceof CatalogPathError) {
|
|
1760
|
+
json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
|
|
1761
|
+
return true;
|
|
1762
|
+
}
|
|
1763
|
+
throw err;
|
|
1764
|
+
}
|
|
1765
|
+
const parsed = parsePlanDraftPackageBody(body);
|
|
1766
|
+
if ("ok" in parsed) {
|
|
1767
|
+
json(res, parsed.status, { error: parsed.error });
|
|
1768
|
+
return true;
|
|
1769
|
+
}
|
|
1770
|
+
const ctx = await resolveStageflowContext(draftWriteRoot);
|
|
1771
|
+
if (ctx.manifestStatus !== "ok") {
|
|
1772
|
+
json(res, 400, {
|
|
1773
|
+
error: "Project root not found; initialize stageflow.yaml",
|
|
1774
|
+
});
|
|
1775
|
+
return true;
|
|
1776
|
+
}
|
|
1777
|
+
const catalogPipelines = parsed.mode === "create"
|
|
1778
|
+
? await listPipelinesForContext(catalogContextFromStageflow(ctx))
|
|
1779
|
+
: [];
|
|
1780
|
+
const plan = await planDraftPackage(ctx.projectRoot, parsed, {
|
|
1781
|
+
mode: parsed.mode,
|
|
1782
|
+
catalogPipelines,
|
|
1783
|
+
});
|
|
1784
|
+
if (!plan.ok) {
|
|
1785
|
+
json(res, plan.status, { error: plan.error });
|
|
1786
|
+
return true;
|
|
1787
|
+
}
|
|
1788
|
+
json(res, 200, {
|
|
1789
|
+
pipelinePath: plan.pipelinePath,
|
|
1790
|
+
directory: plan.directory,
|
|
1791
|
+
files: plan.files,
|
|
1792
|
+
pipelineIdTaken: plan.pipelineIdTaken,
|
|
1793
|
+
});
|
|
1794
|
+
return true;
|
|
1795
|
+
}
|
|
1346
1796
|
if (method === "POST" && pathname === "/api/drafts/open") {
|
|
1347
1797
|
let body;
|
|
1348
1798
|
try {
|
|
@@ -1412,6 +1862,7 @@ export function createOperatorRoutes(deps) {
|
|
|
1412
1862
|
draft: result.draft,
|
|
1413
1863
|
destination: result.destination,
|
|
1414
1864
|
pipelinePath: result.pipelinePath,
|
|
1865
|
+
...draftCatalogRootWire(wireRoot),
|
|
1415
1866
|
...(result.taskPath !== undefined ? { taskPath: result.taskPath } : {}),
|
|
1416
1867
|
});
|
|
1417
1868
|
return true;
|
|
@@ -1481,23 +1932,8 @@ export function createOperatorRoutes(deps) {
|
|
|
1481
1932
|
const keyParam = url.searchParams.get("key");
|
|
1482
1933
|
const key = workshopAutosaveSlotKey(keyParam);
|
|
1483
1934
|
const writeRoot = url.searchParams.get("project_root") ?? undefined;
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
if (writeRoot) {
|
|
1487
|
-
const { wireRoot } = await resolveCatalogStartInput({ store, bootCwd: cwd }, writeRoot);
|
|
1488
|
-
const ctx = await resolveStageflowContext(wireRoot.path);
|
|
1489
|
-
storeRoot = resolveWorkshopAutosaveStoreRoot({
|
|
1490
|
-
projectRoot: ctx.projectRoot,
|
|
1491
|
-
isGitProject: ctx.isGitProject,
|
|
1492
|
-
});
|
|
1493
|
-
}
|
|
1494
|
-
else {
|
|
1495
|
-
const ctx = await resolveStageflowContext(cwd);
|
|
1496
|
-
storeRoot = resolveWorkshopAutosaveStoreRoot({
|
|
1497
|
-
projectRoot: ctx.isGitProject ? ctx.projectRoot : null,
|
|
1498
|
-
isGitProject: ctx.isGitProject,
|
|
1499
|
-
});
|
|
1500
|
-
}
|
|
1935
|
+
try {
|
|
1936
|
+
await resolveDefaultWriteRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1501
1937
|
}
|
|
1502
1938
|
catch (err) {
|
|
1503
1939
|
if (err instanceof CatalogPathError) {
|
|
@@ -1506,6 +1942,7 @@ export function createOperatorRoutes(deps) {
|
|
|
1506
1942
|
}
|
|
1507
1943
|
throw err;
|
|
1508
1944
|
}
|
|
1945
|
+
const storeRoot = resolveWorkshopAutosaveStoreRoot();
|
|
1509
1946
|
const record = readWorkshopAutosave(storeRoot, key);
|
|
1510
1947
|
json(res, 200, { key, autosave: record });
|
|
1511
1948
|
return true;
|
|
@@ -1528,23 +1965,8 @@ export function createOperatorRoutes(deps) {
|
|
|
1528
1965
|
const writeRoot = typeof body.project_root === "string"
|
|
1529
1966
|
? body.project_root
|
|
1530
1967
|
: undefined;
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
if (writeRoot) {
|
|
1534
|
-
const { wireRoot } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1535
|
-
const ctx = await resolveStageflowContext(wireRoot.path);
|
|
1536
|
-
storeRoot = resolveWorkshopAutosaveStoreRoot({
|
|
1537
|
-
projectRoot: ctx.projectRoot,
|
|
1538
|
-
isGitProject: ctx.isGitProject,
|
|
1539
|
-
});
|
|
1540
|
-
}
|
|
1541
|
-
else {
|
|
1542
|
-
const ctx = await resolveStageflowContext(cwd);
|
|
1543
|
-
storeRoot = resolveWorkshopAutosaveStoreRoot({
|
|
1544
|
-
projectRoot: ctx.isGitProject ? ctx.projectRoot : null,
|
|
1545
|
-
isGitProject: ctx.isGitProject,
|
|
1546
|
-
});
|
|
1547
|
-
}
|
|
1968
|
+
try {
|
|
1969
|
+
await resolveDefaultWriteRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1548
1970
|
}
|
|
1549
1971
|
catch (err) {
|
|
1550
1972
|
if (err instanceof CatalogPathError) {
|
|
@@ -1553,6 +1975,7 @@ export function createOperatorRoutes(deps) {
|
|
|
1553
1975
|
}
|
|
1554
1976
|
throw err;
|
|
1555
1977
|
}
|
|
1978
|
+
const storeRoot = resolveWorkshopAutosaveStoreRoot();
|
|
1556
1979
|
const candidate = {
|
|
1557
1980
|
version: 1,
|
|
1558
1981
|
key,
|
|
@@ -1583,6 +2006,9 @@ export function createOperatorRoutes(deps) {
|
|
|
1583
2006
|
diskFingerprints: body.diskFingerprints,
|
|
1584
2007
|
}
|
|
1585
2008
|
: {}),
|
|
2009
|
+
...(body.projectRoot === null || typeof body.projectRoot === "string"
|
|
2010
|
+
? { projectRoot: body.projectRoot }
|
|
2011
|
+
: {}),
|
|
1586
2012
|
};
|
|
1587
2013
|
const parsed = parseWorkshopAutosaveRecord(candidate);
|
|
1588
2014
|
if (!parsed) {
|
|
@@ -1611,23 +2037,8 @@ export function createOperatorRoutes(deps) {
|
|
|
1611
2037
|
const writeRoot = typeof body.project_root === "string"
|
|
1612
2038
|
? body.project_root
|
|
1613
2039
|
: undefined;
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
if (writeRoot) {
|
|
1617
|
-
const { wireRoot } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1618
|
-
const ctx = await resolveStageflowContext(wireRoot.path);
|
|
1619
|
-
storeRoot = resolveWorkshopAutosaveStoreRoot({
|
|
1620
|
-
projectRoot: ctx.projectRoot,
|
|
1621
|
-
isGitProject: ctx.isGitProject,
|
|
1622
|
-
});
|
|
1623
|
-
}
|
|
1624
|
-
else {
|
|
1625
|
-
const ctx = await resolveStageflowContext(cwd);
|
|
1626
|
-
storeRoot = resolveWorkshopAutosaveStoreRoot({
|
|
1627
|
-
projectRoot: ctx.isGitProject ? ctx.projectRoot : null,
|
|
1628
|
-
isGitProject: ctx.isGitProject,
|
|
1629
|
-
});
|
|
1630
|
-
}
|
|
2040
|
+
try {
|
|
2041
|
+
await resolveDefaultWriteRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1631
2042
|
}
|
|
1632
2043
|
catch (err) {
|
|
1633
2044
|
if (err instanceof CatalogPathError) {
|
|
@@ -1636,6 +2047,7 @@ export function createOperatorRoutes(deps) {
|
|
|
1636
2047
|
}
|
|
1637
2048
|
throw err;
|
|
1638
2049
|
}
|
|
2050
|
+
const storeRoot = resolveWorkshopAutosaveStoreRoot();
|
|
1639
2051
|
const cleared = clearWorkshopAutosave(storeRoot, key);
|
|
1640
2052
|
json(res, 200, { key, cleared });
|
|
1641
2053
|
return true;
|
|
@@ -2064,22 +2476,52 @@ export function createOperatorRoutes(deps) {
|
|
|
2064
2476
|
json(res, draftParsed.status, { error: draftParsed.error });
|
|
2065
2477
|
return true;
|
|
2066
2478
|
}
|
|
2479
|
+
const attachmentsParsed = parseWorkshopChatAttachments(body.attachments);
|
|
2480
|
+
if (!attachmentsParsed.ok) {
|
|
2481
|
+
json(res, 400, {
|
|
2482
|
+
error: attachmentsParsed.error,
|
|
2483
|
+
code: "workshop_attachment_invalid",
|
|
2484
|
+
});
|
|
2485
|
+
return true;
|
|
2486
|
+
}
|
|
2067
2487
|
const settingsDefault = await readWorkshopOpenModel(cwd);
|
|
2068
2488
|
const accept = String(req.headers.accept ?? "");
|
|
2069
2489
|
const wantsStream = body.stream === true ||
|
|
2070
2490
|
accept.includes("application/x-ndjson") ||
|
|
2071
2491
|
accept.includes("text/event-stream");
|
|
2492
|
+
const projectRootWire = typeof body.project_root === "string"
|
|
2493
|
+
? body.project_root.trim()
|
|
2494
|
+
: typeof body.projectRoot === "string"
|
|
2495
|
+
? body.projectRoot.trim()
|
|
2496
|
+
: "";
|
|
2497
|
+
let chatProjectRoot;
|
|
2498
|
+
if (projectRootWire) {
|
|
2499
|
+
try {
|
|
2500
|
+
const { wireRoot } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, projectRootWire);
|
|
2501
|
+
chatProjectRoot = wireRoot.path;
|
|
2502
|
+
}
|
|
2503
|
+
catch (err) {
|
|
2504
|
+
if (err instanceof CatalogPathError) {
|
|
2505
|
+
json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
|
|
2506
|
+
return true;
|
|
2507
|
+
}
|
|
2508
|
+
throw err;
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2072
2511
|
const chatTurnBase = {
|
|
2073
2512
|
sessionId: body.sessionId,
|
|
2074
2513
|
draft: draftParsed,
|
|
2075
2514
|
message: body.message,
|
|
2076
2515
|
autoApply: body.autoApply === true,
|
|
2516
|
+
attachments: attachmentsParsed.attachments,
|
|
2517
|
+
context: parseWorkshopChatContext(body.context),
|
|
2077
2518
|
model: typeof body.model === "string" || body.model === null
|
|
2078
2519
|
? body.model
|
|
2079
2520
|
: undefined,
|
|
2080
2521
|
settingsDefault: settingsDefault ?? null,
|
|
2081
2522
|
registry: workshopChatRegistry,
|
|
2082
2523
|
storeRoot: workshopSessionStoreRoot(),
|
|
2524
|
+
...(chatProjectRoot ? { projectRoot: chatProjectRoot } : {}),
|
|
2083
2525
|
};
|
|
2084
2526
|
// Fail closed on missing sessions before opening an NDJSON body.
|
|
2085
2527
|
try {
|
|
@@ -2199,7 +2641,7 @@ export function createOperatorRoutes(deps) {
|
|
|
2199
2641
|
events: [errorEvent],
|
|
2200
2642
|
draft: draftParsed,
|
|
2201
2643
|
pending: null,
|
|
2202
|
-
autoApply:
|
|
2644
|
+
autoApply: chatTurnBase.autoApply,
|
|
2203
2645
|
model: resolvedModel,
|
|
2204
2646
|
buildId: workshopChatRegistry.getPinnedBuildId(body.sessionId),
|
|
2205
2647
|
});
|
|
@@ -2336,8 +2778,94 @@ export function createOperatorRoutes(deps) {
|
|
|
2336
2778
|
})) {
|
|
2337
2779
|
return true;
|
|
2338
2780
|
}
|
|
2781
|
+
if (method === "GET" && pathname === "/api/skills/usage") {
|
|
2782
|
+
const projectRoot = url.searchParams.get("project_root") ?? undefined;
|
|
2783
|
+
let usageRoot;
|
|
2784
|
+
try {
|
|
2785
|
+
const { wireRoot } = projectRoot === undefined
|
|
2786
|
+
? await resolveWritableCatalogRoot({ store, bootCwd: cwd }, undefined)
|
|
2787
|
+
: await resolveCatalogStartInput({ store, bootCwd: cwd }, projectRoot);
|
|
2788
|
+
usageRoot = wireRoot.path;
|
|
2789
|
+
}
|
|
2790
|
+
catch (err) {
|
|
2791
|
+
if (err instanceof CatalogPathError) {
|
|
2792
|
+
json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
|
|
2793
|
+
return true;
|
|
2794
|
+
}
|
|
2795
|
+
throw err;
|
|
2796
|
+
}
|
|
2797
|
+
const usages = await buildSkillUsageIndex(usageRoot);
|
|
2798
|
+
if (usages === null) {
|
|
2799
|
+
json(res, 404, { error: "No Stageflow catalog found" });
|
|
2800
|
+
return true;
|
|
2801
|
+
}
|
|
2802
|
+
json(res, 200, { usages });
|
|
2803
|
+
return true;
|
|
2804
|
+
}
|
|
2339
2805
|
if (method === "GET" && pathname === "/api/skills") {
|
|
2340
|
-
|
|
2806
|
+
const projectRoot = url.searchParams.get("project_root") ?? undefined;
|
|
2807
|
+
try {
|
|
2808
|
+
const { wireRoot } = projectRoot === undefined
|
|
2809
|
+
? await resolveWritableCatalogRoot({ store, bootCwd: cwd }, undefined)
|
|
2810
|
+
: await resolveCatalogStartInput({ store, bootCwd: cwd }, projectRoot);
|
|
2811
|
+
json(res, 200, await listSkills({ cwd: wireRoot.path, agentDir }));
|
|
2812
|
+
}
|
|
2813
|
+
catch (err) {
|
|
2814
|
+
if (err instanceof CatalogPathError) {
|
|
2815
|
+
json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
|
|
2816
|
+
return true;
|
|
2817
|
+
}
|
|
2818
|
+
throw err;
|
|
2819
|
+
}
|
|
2820
|
+
return true;
|
|
2821
|
+
}
|
|
2822
|
+
if (method === "POST" && pathname === "/api/skills/install") {
|
|
2823
|
+
let body;
|
|
2824
|
+
try {
|
|
2825
|
+
body = await readJsonBody(req);
|
|
2826
|
+
}
|
|
2827
|
+
catch {
|
|
2828
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
2829
|
+
return true;
|
|
2830
|
+
}
|
|
2831
|
+
const parsed = parseInstallSkillBody(body);
|
|
2832
|
+
if ("ok" in parsed) {
|
|
2833
|
+
json(res, parsed.status, { error: parsed.error });
|
|
2834
|
+
return true;
|
|
2835
|
+
}
|
|
2836
|
+
let skillWriteRoot;
|
|
2837
|
+
try {
|
|
2838
|
+
const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, undefined);
|
|
2839
|
+
skillWriteRoot = selected.path;
|
|
2840
|
+
}
|
|
2841
|
+
catch (err) {
|
|
2842
|
+
if (err instanceof CatalogPathError) {
|
|
2843
|
+
json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
|
|
2844
|
+
return true;
|
|
2845
|
+
}
|
|
2846
|
+
throw err;
|
|
2847
|
+
}
|
|
2848
|
+
const stageflowCtx = await resolveStageflowContext(skillWriteRoot);
|
|
2849
|
+
if (stageflowCtx.manifestStatus !== "ok") {
|
|
2850
|
+
json(res, 400, {
|
|
2851
|
+
error: "Project root not found; initialize stageflow.yaml",
|
|
2852
|
+
});
|
|
2853
|
+
return true;
|
|
2854
|
+
}
|
|
2855
|
+
const result = await installSkillFromCommand({
|
|
2856
|
+
projectRoot: stageflowCtx.projectRoot,
|
|
2857
|
+
command: parsed.command,
|
|
2858
|
+
});
|
|
2859
|
+
if (!result.ok) {
|
|
2860
|
+
json(res, result.status ?? 400, { error: result.error });
|
|
2861
|
+
return true;
|
|
2862
|
+
}
|
|
2863
|
+
json(res, 201, {
|
|
2864
|
+
ok: true,
|
|
2865
|
+
skill_name: result.skill_name,
|
|
2866
|
+
dest_path: result.dest_path,
|
|
2867
|
+
project_root: result.project_root,
|
|
2868
|
+
});
|
|
2341
2869
|
return true;
|
|
2342
2870
|
}
|
|
2343
2871
|
if (method === "GET" && pathname === "/api/extensions") {
|
|
@@ -2444,6 +2972,7 @@ export function createOperatorRoutes(deps) {
|
|
|
2444
2972
|
}
|
|
2445
2973
|
}
|
|
2446
2974
|
};
|
|
2975
|
+
return Object.assign(handler, { dispose: () => liveView.dispose() });
|
|
2447
2976
|
}
|
|
2448
2977
|
export async function startUiServer(options) {
|
|
2449
2978
|
const host = options.host ?? "127.0.0.1";
|
|
@@ -2492,6 +3021,8 @@ export async function startUiServer(options) {
|
|
|
2492
3021
|
});
|
|
2493
3022
|
envelope.server.on("close", () => {
|
|
2494
3023
|
shutdown?.uninstall();
|
|
3024
|
+
if ("dispose" in routes)
|
|
3025
|
+
void routes.dispose();
|
|
2495
3026
|
});
|
|
2496
3027
|
return { ...envelope, shutdown };
|
|
2497
3028
|
}
|