stageflow 0.32.0 → 0.35.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 +4 -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 +50 -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/fakeAgent.js +0 -6
- package/dist/agent/piAdapter.d.ts +2 -2
- package/dist/agent/piAdapter.js +116 -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 +2 -2
- package/dist/agent/providerAuth.d.ts +6 -0
- package/dist/agent/providerAuth.js +86 -17
- package/dist/agent/{resolveAgentPort.d.ts → resolveStagePort.d.ts} +8 -6
- package/dist/agent/{resolveAgentPort.js → resolveStagePort.js} +13 -9
- 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/cli/browserCommand.d.ts +3 -1
- package/dist/cli/browserCommand.js +25 -20
- 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 +41 -0
- package/dist/config/draftPackage.js +216 -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 +113 -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/listConfig.d.ts +0 -16
- package/dist/config/listConfig.js +0 -3
- package/dist/config/loadOutcome.d.ts +11 -6
- package/dist/config/loadPipeline.js +0 -1
- package/dist/config/loadStage.js +2 -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.js +3 -0
- package/dist/config/normalizePipelineStageEntry.js +10 -1
- 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 +6 -2
- package/dist/config/validateCatalog.js +48 -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/hosted/containment.d.ts +3 -0
- package/dist/hosted/containment.js +36 -0
- package/dist/hosted/errors.d.ts +21 -0
- package/dist/hosted/errors.js +30 -0
- package/dist/hosted/packageSkills.d.ts +3 -0
- package/dist/hosted/packageSkills.js +80 -0
- package/dist/hosted/runtime.d.ts +12 -0
- package/dist/hosted/runtime.js +596 -0
- package/dist/hosted/sequencer.d.ts +28 -0
- package/dist/hosted/sequencer.js +129 -0
- package/dist/hosted/translate.d.ts +3 -0
- package/dist/hosted/translate.js +43 -0
- package/dist/hosted/types.d.ts +108 -0
- package/dist/hosted/types.js +10 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.js +6 -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/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/projection/projectRun.d.ts +0 -1
- package/dist/projection/projectRun.js +0 -1
- 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/containerLimits.d.ts +0 -3
- package/dist/runtime/containerLimits.js +0 -5
- package/dist/runtime/credentialBinding.d.ts +3 -0
- package/dist/runtime/credentialBinding.js +23 -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/pipelineScheduler.d.ts +2 -2
- package/dist/runtime/pipelineScheduler.js +3 -0
- 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 +95 -30
- package/dist/runtime/runRetentionSweep.d.ts +0 -2
- package/dist/runtime/runRetentionSweep.js +0 -8
- 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 +10 -1
- package/dist/runtime/stageProcessLauncher.js +2 -2
- package/dist/runtime/stageRoots.js +12 -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 +7 -2
- package/dist/server/bootstrap.js +15 -1
- 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 +605 -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/readEmail.d.ts +3 -3
- package/dist/tools/writeStageArtifact.d.ts +9 -0
- package/dist/tools/writeStageArtifact.js +2 -47
- package/dist/types/pipeline.d.ts +1 -1
- package/dist/types/stage.d.ts +1 -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-BoFPkKFg.js +256 -0
- package/dist/ui/assets/{index-Bv4p1aPK.css → index-CIPbbBUi.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/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 +9 -1
- 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";
|
|
@@ -43,13 +55,20 @@ import { bootstrapStageflowHost, } from "./bootstrap.js";
|
|
|
43
55
|
import { assertAllowedHttpAccess, isTrustedLocalHttpRequest, resolveAllowedHosts, } from "./allowedHosts.js";
|
|
44
56
|
import { enforceBearerAuth, loadControlTokens, requiredScopeFor, } from "./controlToken.js";
|
|
45
57
|
import { createHttpHost, DEFAULT_PORT, json, } from "./createHttpHost.js";
|
|
46
|
-
import { handleApiHealth } from "./healthSurfaces.js";
|
|
58
|
+
import { handleApiHealth, listCatalogRootsForApi } from "./healthSurfaces.js";
|
|
47
59
|
import { mapRetryStageFailure, mapStartFailure, mapStoreLookupError, } from "./operatorResults.js";
|
|
48
60
|
import { installShutdownController, makeDrainableHostFromOptional, } from "./shutdown.js";
|
|
49
61
|
import { writeAudit } from "../logging/audit.js";
|
|
50
62
|
import { logger as rootLogger } from "../logging/logger.js";
|
|
51
63
|
import { callerIdFromRequestAuth, getRequestAuth, requestAuthFromBearer, runWithRequestAuth, } from "./requestAuthContext.js";
|
|
52
64
|
const auditLog = rootLogger.child({ component: "audit" });
|
|
65
|
+
function draftCatalogRootWire(root) {
|
|
66
|
+
return {
|
|
67
|
+
project_root: root.project_root,
|
|
68
|
+
root_kind: root.kind,
|
|
69
|
+
read_only: root.read_only,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
53
72
|
function textPlain(res, status, body) {
|
|
54
73
|
res.writeHead(status, {
|
|
55
74
|
"Content-Type": "text/plain; charset=utf-8",
|
|
@@ -137,7 +156,10 @@ async function serveStatic(res, uiDistDir, urlPath) {
|
|
|
137
156
|
function isCredentialMutatingApi(method, pathname) {
|
|
138
157
|
if (method !== "POST")
|
|
139
158
|
return false;
|
|
140
|
-
return (
|
|
159
|
+
return (isLiveViewInputPath(method, pathname) ||
|
|
160
|
+
isLiveViewDialogPath(method, pathname) ||
|
|
161
|
+
isLiveViewReopenTabPath(method, pathname) ||
|
|
162
|
+
/^\/api\/providers\/[^/]+\/login$/.test(pathname) ||
|
|
141
163
|
/^\/api\/providers\/[^/]+\/login\/[^/]+\/answer$/.test(pathname) ||
|
|
142
164
|
/^\/api\/providers\/[^/]+\/login\/[^/]+\/cancel$/.test(pathname) ||
|
|
143
165
|
/^\/api\/providers\/[^/]+\/logout$/.test(pathname));
|
|
@@ -149,18 +171,11 @@ export function defaultUiDistDir() {
|
|
|
149
171
|
}
|
|
150
172
|
return path.resolve(here, "../ui");
|
|
151
173
|
}
|
|
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
174
|
export function createOperatorRoutes(deps) {
|
|
160
175
|
const { manager, store, cwd, agentDir, rootDir, providerAuthContext, uiDistDir, workshopOperatorHost, } = deps;
|
|
161
176
|
const workshopChatRegistry = deps.workshopChatRegistry ??
|
|
162
177
|
new WorkshopChatSessionRegistry(workshopOperatorHost ??
|
|
163
|
-
|
|
178
|
+
resolveAgentPort({
|
|
164
179
|
cwd,
|
|
165
180
|
projectRoot: cwd,
|
|
166
181
|
}));
|
|
@@ -168,7 +183,11 @@ export function createOperatorRoutes(deps) {
|
|
|
168
183
|
const workshopBuildStoreRoot = () => resolveWorkshopBuildStoreRoot();
|
|
169
184
|
const allowedHosts = deps.allowedHosts ?? resolveAllowedHosts();
|
|
170
185
|
const controlTokens = deps.controlTokens ?? loadControlTokens();
|
|
171
|
-
|
|
186
|
+
const liveView = createLiveViewRoutes({ ...deps.liveView, store, controlTokens });
|
|
187
|
+
liveView.attach(manager);
|
|
188
|
+
const handler = async ({ req, res, url, pathname, method, boot }) => {
|
|
189
|
+
if (isLiveViewPath(pathname))
|
|
190
|
+
applyLiveViewHeaders(res);
|
|
172
191
|
if (boot.serveBlocked !== undefined && pathname.startsWith("/api/")) {
|
|
173
192
|
json(res, 503, {
|
|
174
193
|
error: boot.serveBlocked.reason,
|
|
@@ -193,6 +212,8 @@ export function createOperatorRoutes(deps) {
|
|
|
193
212
|
return handleOperatorRequest();
|
|
194
213
|
async function handleOperatorRequest() {
|
|
195
214
|
try {
|
|
215
|
+
if (await liveView.handle(req, res))
|
|
216
|
+
return true;
|
|
196
217
|
if (pathname.startsWith("/api/email/")) {
|
|
197
218
|
if (!assertAllowedHttpAccess({ entries: [] }, req, res, { requireOrigin: ["POST", "PATCH", "DELETE"].includes(method) }))
|
|
198
219
|
return true;
|
|
@@ -1044,6 +1065,356 @@ export function createOperatorRoutes(deps) {
|
|
|
1044
1065
|
});
|
|
1045
1066
|
return true;
|
|
1046
1067
|
}
|
|
1068
|
+
if (method === "GET" && pathname === "/api/catalog/roots") {
|
|
1069
|
+
const roots = await listCatalogRootsForApi({ store, bootCwd: cwd });
|
|
1070
|
+
json(res, 200, { roots });
|
|
1071
|
+
return true;
|
|
1072
|
+
}
|
|
1073
|
+
if (method === "POST" && pathname === "/api/catalog/validate") {
|
|
1074
|
+
let body;
|
|
1075
|
+
try {
|
|
1076
|
+
body = await readJsonBody(req);
|
|
1077
|
+
}
|
|
1078
|
+
catch {
|
|
1079
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
1080
|
+
return true;
|
|
1081
|
+
}
|
|
1082
|
+
if (body !== null && typeof body === "object" && !Array.isArray(body)) {
|
|
1083
|
+
const record = body;
|
|
1084
|
+
let pipeline = typeof record.pipeline === "string" ? record.pipeline : undefined;
|
|
1085
|
+
let task = typeof record.task === "string" ? record.task : undefined;
|
|
1086
|
+
let stage = typeof record.stage === "string" ? record.stage : undefined;
|
|
1087
|
+
const strict = record.strict === true;
|
|
1088
|
+
const projectRoot = typeof record.project_root === "string" && record.project_root.trim()
|
|
1089
|
+
? record.project_root.trim()
|
|
1090
|
+
: undefined;
|
|
1091
|
+
const scopedCount = [pipeline, task, stage].filter((v) => v !== undefined).length;
|
|
1092
|
+
if (scopedCount > 1) {
|
|
1093
|
+
json(res, 400, {
|
|
1094
|
+
error: "Use at most one of pipeline, task, or stage",
|
|
1095
|
+
});
|
|
1096
|
+
return true;
|
|
1097
|
+
}
|
|
1098
|
+
let validateCwd = cwd;
|
|
1099
|
+
if (pipeline !== undefined || task !== undefined || stage !== undefined) {
|
|
1100
|
+
try {
|
|
1101
|
+
const { wireRoot, roots } = await resolveCatalogStartInput({ store, bootCwd: cwd }, projectRoot);
|
|
1102
|
+
validateCwd = wireRoot.path;
|
|
1103
|
+
if (pipeline !== undefined) {
|
|
1104
|
+
pipeline = resolveCatalogRelativePath({
|
|
1105
|
+
inputPath: pipeline.trim(),
|
|
1106
|
+
projectRoot: wireRoot.project_root,
|
|
1107
|
+
roots,
|
|
1108
|
+
fieldName: "pipeline",
|
|
1109
|
+
}).absolutePath;
|
|
1110
|
+
}
|
|
1111
|
+
if (task !== undefined) {
|
|
1112
|
+
task = resolveCatalogRelativePath({
|
|
1113
|
+
inputPath: task.trim(),
|
|
1114
|
+
projectRoot: wireRoot.project_root,
|
|
1115
|
+
roots,
|
|
1116
|
+
fieldName: "task",
|
|
1117
|
+
}).absolutePath;
|
|
1118
|
+
}
|
|
1119
|
+
if (stage !== undefined) {
|
|
1120
|
+
stage = resolveCatalogRelativePath({
|
|
1121
|
+
inputPath: stage.trim(),
|
|
1122
|
+
projectRoot: wireRoot.project_root,
|
|
1123
|
+
roots,
|
|
1124
|
+
fieldName: "stage",
|
|
1125
|
+
}).absolutePath;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
catch (err) {
|
|
1129
|
+
if (err instanceof CatalogPathError) {
|
|
1130
|
+
json(res, 400, catalogPathErrorBody(err));
|
|
1131
|
+
return true;
|
|
1132
|
+
}
|
|
1133
|
+
throw err;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
const scope = pipeline
|
|
1137
|
+
? "pipeline"
|
|
1138
|
+
: task
|
|
1139
|
+
? "task"
|
|
1140
|
+
: stage
|
|
1141
|
+
? "stage"
|
|
1142
|
+
: "full";
|
|
1143
|
+
const stageContent = typeof record.content === "string" ? record.content : undefined;
|
|
1144
|
+
if (stageContent !== undefined && stage === undefined) {
|
|
1145
|
+
json(res, 400, {
|
|
1146
|
+
error: "content is only supported with stage",
|
|
1147
|
+
});
|
|
1148
|
+
return true;
|
|
1149
|
+
}
|
|
1150
|
+
const result = await validateCatalog({
|
|
1151
|
+
scope,
|
|
1152
|
+
cwd: validateCwd,
|
|
1153
|
+
...(pipeline !== undefined ||
|
|
1154
|
+
task !== undefined ||
|
|
1155
|
+
stage !== undefined
|
|
1156
|
+
? { projectRoot: validateCwd }
|
|
1157
|
+
: {}),
|
|
1158
|
+
pipeline,
|
|
1159
|
+
task,
|
|
1160
|
+
stage,
|
|
1161
|
+
...(stageContent !== undefined ? { stageContent } : {}),
|
|
1162
|
+
strict,
|
|
1163
|
+
});
|
|
1164
|
+
json(res, 200, result);
|
|
1165
|
+
return true;
|
|
1166
|
+
}
|
|
1167
|
+
json(res, 400, { error: "Request body must be an object" });
|
|
1168
|
+
return true;
|
|
1169
|
+
}
|
|
1170
|
+
if (method === "GET" && pathname === "/api/catalog/file") {
|
|
1171
|
+
const relPath = url.searchParams.get("path");
|
|
1172
|
+
if (relPath === null || relPath.length === 0) {
|
|
1173
|
+
json(res, 400, { error: "path query parameter is required" });
|
|
1174
|
+
return true;
|
|
1175
|
+
}
|
|
1176
|
+
const projectRoot = url.searchParams.get("project_root") ?? undefined;
|
|
1177
|
+
try {
|
|
1178
|
+
const { roots } = await resolveCatalogStartInput({ store, bootCwd: cwd }, projectRoot);
|
|
1179
|
+
const result = await readCatalogFileForHttp({
|
|
1180
|
+
inputPath: relPath,
|
|
1181
|
+
projectRoot,
|
|
1182
|
+
roots,
|
|
1183
|
+
fieldName: "path",
|
|
1184
|
+
});
|
|
1185
|
+
if (!result.ok) {
|
|
1186
|
+
json(res, result.status, { error: result.error, code: result.code });
|
|
1187
|
+
return true;
|
|
1188
|
+
}
|
|
1189
|
+
json(res, 200, { path: result.path, content: result.content });
|
|
1190
|
+
return true;
|
|
1191
|
+
}
|
|
1192
|
+
catch (err) {
|
|
1193
|
+
if (err instanceof CatalogPathError) {
|
|
1194
|
+
json(res, 400, catalogPathErrorBody(err));
|
|
1195
|
+
return true;
|
|
1196
|
+
}
|
|
1197
|
+
throw err;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
if (method === "PUT" && pathname === "/api/catalog/file") {
|
|
1201
|
+
let body;
|
|
1202
|
+
try {
|
|
1203
|
+
body = await readJsonBody(req);
|
|
1204
|
+
}
|
|
1205
|
+
catch {
|
|
1206
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
1207
|
+
return true;
|
|
1208
|
+
}
|
|
1209
|
+
if (body === null || typeof body !== "object" || Array.isArray(body)) {
|
|
1210
|
+
json(res, 400, { error: "Request body must be an object" });
|
|
1211
|
+
return true;
|
|
1212
|
+
}
|
|
1213
|
+
const record = body;
|
|
1214
|
+
const relPath = typeof record.path === "string" ? record.path.trim() : "";
|
|
1215
|
+
if (!relPath) {
|
|
1216
|
+
json(res, 400, { error: "path is required" });
|
|
1217
|
+
return true;
|
|
1218
|
+
}
|
|
1219
|
+
if (typeof record.content !== "string") {
|
|
1220
|
+
json(res, 400, { error: "content must be a string" });
|
|
1221
|
+
return true;
|
|
1222
|
+
}
|
|
1223
|
+
const writeRoot = typeof record.project_root === "string"
|
|
1224
|
+
? record.project_root
|
|
1225
|
+
: undefined;
|
|
1226
|
+
try {
|
|
1227
|
+
const { wireRoot, roots } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1228
|
+
const result = await writeCatalogFileForHttp({
|
|
1229
|
+
inputPath: relPath,
|
|
1230
|
+
content: record.content,
|
|
1231
|
+
projectRoot: wireRoot.project_root,
|
|
1232
|
+
roots,
|
|
1233
|
+
fieldName: "path",
|
|
1234
|
+
});
|
|
1235
|
+
if (!result.ok) {
|
|
1236
|
+
json(res, result.status, {
|
|
1237
|
+
error: result.error,
|
|
1238
|
+
code: result.code,
|
|
1239
|
+
...(result.findings !== undefined
|
|
1240
|
+
? { findings: result.findings }
|
|
1241
|
+
: {}),
|
|
1242
|
+
});
|
|
1243
|
+
return true;
|
|
1244
|
+
}
|
|
1245
|
+
json(res, 200, { path: result.path, content: result.content });
|
|
1246
|
+
return true;
|
|
1247
|
+
}
|
|
1248
|
+
catch (err) {
|
|
1249
|
+
if (err instanceof CatalogPathError) {
|
|
1250
|
+
json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
|
|
1251
|
+
return true;
|
|
1252
|
+
}
|
|
1253
|
+
throw err;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
if (method === "POST" && pathname === "/api/tasks/import") {
|
|
1257
|
+
let body;
|
|
1258
|
+
try {
|
|
1259
|
+
body = await readJsonBody(req);
|
|
1260
|
+
}
|
|
1261
|
+
catch {
|
|
1262
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
1263
|
+
return true;
|
|
1264
|
+
}
|
|
1265
|
+
const parsed = parseImportTaskBody(body);
|
|
1266
|
+
if ("ok" in parsed) {
|
|
1267
|
+
json(res, parsed.status, { error: parsed.error });
|
|
1268
|
+
return true;
|
|
1269
|
+
}
|
|
1270
|
+
const writeRoot = body !== null &&
|
|
1271
|
+
typeof body === "object" &&
|
|
1272
|
+
!Array.isArray(body) &&
|
|
1273
|
+
typeof body.project_root === "string"
|
|
1274
|
+
? body.project_root
|
|
1275
|
+
: undefined;
|
|
1276
|
+
let taskWriteRoot;
|
|
1277
|
+
try {
|
|
1278
|
+
const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1279
|
+
taskWriteRoot = selected.path;
|
|
1280
|
+
}
|
|
1281
|
+
catch (err) {
|
|
1282
|
+
if (err instanceof CatalogPathError) {
|
|
1283
|
+
json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
|
|
1284
|
+
return true;
|
|
1285
|
+
}
|
|
1286
|
+
throw err;
|
|
1287
|
+
}
|
|
1288
|
+
const stageflowCtx = await resolveStageflowContext(taskWriteRoot);
|
|
1289
|
+
if (stageflowCtx.manifestStatus !== "ok") {
|
|
1290
|
+
json(res, 400, {
|
|
1291
|
+
error: "Project root not found; initialize stageflow.yaml",
|
|
1292
|
+
});
|
|
1293
|
+
return true;
|
|
1294
|
+
}
|
|
1295
|
+
const result = await importTaskFromIssue(stageflowCtx.projectRoot, parsed);
|
|
1296
|
+
if (!result.ok) {
|
|
1297
|
+
json(res, result.status, { error: result.error });
|
|
1298
|
+
return true;
|
|
1299
|
+
}
|
|
1300
|
+
json(res, 201, { task: result.task });
|
|
1301
|
+
return true;
|
|
1302
|
+
}
|
|
1303
|
+
if (method === "POST" && pathname === "/api/tasks") {
|
|
1304
|
+
let body;
|
|
1305
|
+
try {
|
|
1306
|
+
body = await readJsonBody(req);
|
|
1307
|
+
}
|
|
1308
|
+
catch {
|
|
1309
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
1310
|
+
return true;
|
|
1311
|
+
}
|
|
1312
|
+
const writeRoot = body !== null &&
|
|
1313
|
+
typeof body === "object" &&
|
|
1314
|
+
!Array.isArray(body) &&
|
|
1315
|
+
typeof body.project_root === "string"
|
|
1316
|
+
? body.project_root
|
|
1317
|
+
: undefined;
|
|
1318
|
+
let taskWriteRoot;
|
|
1319
|
+
try {
|
|
1320
|
+
const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1321
|
+
taskWriteRoot = selected.path;
|
|
1322
|
+
}
|
|
1323
|
+
catch (err) {
|
|
1324
|
+
if (err instanceof CatalogPathError) {
|
|
1325
|
+
json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
|
|
1326
|
+
return true;
|
|
1327
|
+
}
|
|
1328
|
+
throw err;
|
|
1329
|
+
}
|
|
1330
|
+
const parsed = parseCreateTaskBody(body);
|
|
1331
|
+
if ("ok" in parsed) {
|
|
1332
|
+
json(res, parsed.status, { error: parsed.error });
|
|
1333
|
+
return true;
|
|
1334
|
+
}
|
|
1335
|
+
const stageflowCtx = await resolveStageflowContext(taskWriteRoot);
|
|
1336
|
+
if (stageflowCtx.manifestStatus !== "ok") {
|
|
1337
|
+
json(res, 400, {
|
|
1338
|
+
error: "Project root not found; initialize stageflow.yaml",
|
|
1339
|
+
});
|
|
1340
|
+
return true;
|
|
1341
|
+
}
|
|
1342
|
+
const result = await createTask(stageflowCtx.projectRoot, parsed);
|
|
1343
|
+
if (!result.ok) {
|
|
1344
|
+
json(res, result.status, { error: result.error });
|
|
1345
|
+
return true;
|
|
1346
|
+
}
|
|
1347
|
+
json(res, 201, { task: result.task });
|
|
1348
|
+
return true;
|
|
1349
|
+
}
|
|
1350
|
+
const taskDetailMatch = pathname.match(/^\/api\/tasks\/([^/]+)$/);
|
|
1351
|
+
if (method === "GET" && taskDetailMatch) {
|
|
1352
|
+
const taskId = decodeURIComponent(taskDetailMatch[1] ?? "");
|
|
1353
|
+
const projectRootFilter = url.searchParams.get("project_root") ?? undefined;
|
|
1354
|
+
const lookup = await findTaskInCatalogMulti({
|
|
1355
|
+
store,
|
|
1356
|
+
bootCwd: cwd,
|
|
1357
|
+
taskId,
|
|
1358
|
+
projectRootFilter,
|
|
1359
|
+
});
|
|
1360
|
+
if (!lookup.ok) {
|
|
1361
|
+
if (lookup.status === 409) {
|
|
1362
|
+
json(res, 409, {
|
|
1363
|
+
error: lookup.error,
|
|
1364
|
+
code: lookup.code,
|
|
1365
|
+
project_roots: lookup.project_roots,
|
|
1366
|
+
});
|
|
1367
|
+
return true;
|
|
1368
|
+
}
|
|
1369
|
+
if (lookup.status === 400) {
|
|
1370
|
+
json(res, 400, {
|
|
1371
|
+
error: lookup.error,
|
|
1372
|
+
code: lookup.code,
|
|
1373
|
+
});
|
|
1374
|
+
return true;
|
|
1375
|
+
}
|
|
1376
|
+
json(res, 404, { error: lookup.error });
|
|
1377
|
+
return true;
|
|
1378
|
+
}
|
|
1379
|
+
json(res, 200, { task: lookup.task });
|
|
1380
|
+
return true;
|
|
1381
|
+
}
|
|
1382
|
+
if (method === "PUT" && taskDetailMatch) {
|
|
1383
|
+
const taskId = decodeURIComponent(taskDetailMatch[1] ?? "");
|
|
1384
|
+
const projectRootFilter = url.searchParams.get("project_root") ?? undefined;
|
|
1385
|
+
let body;
|
|
1386
|
+
try {
|
|
1387
|
+
body = await readJsonBody(req);
|
|
1388
|
+
}
|
|
1389
|
+
catch {
|
|
1390
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
1391
|
+
return true;
|
|
1392
|
+
}
|
|
1393
|
+
const parsed = parseUpdateTaskBody(body);
|
|
1394
|
+
if ("ok" in parsed) {
|
|
1395
|
+
json(res, parsed.status, { error: parsed.error });
|
|
1396
|
+
return true;
|
|
1397
|
+
}
|
|
1398
|
+
const result = await updateTask({
|
|
1399
|
+
store,
|
|
1400
|
+
bootCwd: cwd,
|
|
1401
|
+
taskId,
|
|
1402
|
+
input: parsed,
|
|
1403
|
+
projectRootFilter,
|
|
1404
|
+
});
|
|
1405
|
+
if (!result.ok) {
|
|
1406
|
+
json(res, result.status, {
|
|
1407
|
+
error: result.error,
|
|
1408
|
+
...(result.code !== undefined ? { code: result.code } : {}),
|
|
1409
|
+
...(result.project_roots !== undefined
|
|
1410
|
+
? { project_roots: result.project_roots }
|
|
1411
|
+
: {}),
|
|
1412
|
+
});
|
|
1413
|
+
return true;
|
|
1414
|
+
}
|
|
1415
|
+
json(res, 200, { task: result.task });
|
|
1416
|
+
return true;
|
|
1417
|
+
}
|
|
1047
1418
|
if (method === "GET" && pathname === "/api/tasks") {
|
|
1048
1419
|
const filter = url.searchParams.get("project_root") ?? undefined;
|
|
1049
1420
|
const result = await listTasksMultiProject({
|
|
@@ -1210,19 +1581,17 @@ export function createOperatorRoutes(deps) {
|
|
|
1210
1581
|
typeof body.project_root === "string"
|
|
1211
1582
|
? body.project_root
|
|
1212
1583
|
: undefined;
|
|
1213
|
-
let validateRoot
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
return true;
|
|
1223
|
-
}
|
|
1224
|
-
throw err;
|
|
1584
|
+
let validateRoot;
|
|
1585
|
+
try {
|
|
1586
|
+
const { wireRoot: selected } = await resolveDefaultWriteRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1587
|
+
validateRoot = selected.path;
|
|
1588
|
+
}
|
|
1589
|
+
catch (err) {
|
|
1590
|
+
if (err instanceof CatalogPathError) {
|
|
1591
|
+
json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
|
|
1592
|
+
return true;
|
|
1225
1593
|
}
|
|
1594
|
+
throw err;
|
|
1226
1595
|
}
|
|
1227
1596
|
const ctx = await resolveStageflowContext(validateRoot);
|
|
1228
1597
|
const result = await validateDraftPackage(draftParsed, {
|
|
@@ -1249,9 +1618,11 @@ export function createOperatorRoutes(deps) {
|
|
|
1249
1618
|
? body.project_root
|
|
1250
1619
|
: undefined;
|
|
1251
1620
|
let draftWriteRoot;
|
|
1621
|
+
let draftWriteWireRoot;
|
|
1252
1622
|
try {
|
|
1253
1623
|
const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1254
1624
|
draftWriteRoot = selected.path;
|
|
1625
|
+
draftWriteWireRoot = selected;
|
|
1255
1626
|
}
|
|
1256
1627
|
catch (err) {
|
|
1257
1628
|
if (err instanceof CatalogPathError) {
|
|
@@ -1266,13 +1637,15 @@ export function createOperatorRoutes(deps) {
|
|
|
1266
1637
|
return true;
|
|
1267
1638
|
}
|
|
1268
1639
|
const ctx = await resolveStageflowContext(draftWriteRoot);
|
|
1269
|
-
if (
|
|
1640
|
+
if (ctx.manifestStatus !== "ok") {
|
|
1270
1641
|
json(res, 400, {
|
|
1271
|
-
error: "Project root not found; initialize stageflow.yaml
|
|
1642
|
+
error: "Project root not found; initialize stageflow.yaml",
|
|
1272
1643
|
});
|
|
1273
1644
|
return true;
|
|
1274
1645
|
}
|
|
1275
|
-
const result = (await publishDraftPackage(ctx.projectRoot, parsed, "create"
|
|
1646
|
+
const result = (await publishDraftPackage(ctx.projectRoot, parsed, "create", {
|
|
1647
|
+
catalogRootKind: draftWriteWireRoot.kind,
|
|
1648
|
+
})).write;
|
|
1276
1649
|
if (!result.ok) {
|
|
1277
1650
|
json(res, result.status, {
|
|
1278
1651
|
error: result.error,
|
|
@@ -1284,6 +1657,8 @@ export function createOperatorRoutes(deps) {
|
|
|
1284
1657
|
pipeline: result.pipeline,
|
|
1285
1658
|
pipelinePath: result.pipelinePath,
|
|
1286
1659
|
stagePaths: result.stagePaths,
|
|
1660
|
+
catalog_root_path: ctx.projectRoot,
|
|
1661
|
+
...draftCatalogRootWire(draftWriteWireRoot),
|
|
1287
1662
|
...(result.taskPath !== undefined ? { taskPath: result.taskPath } : {}),
|
|
1288
1663
|
});
|
|
1289
1664
|
return true;
|
|
@@ -1304,9 +1679,11 @@ export function createOperatorRoutes(deps) {
|
|
|
1304
1679
|
? body.project_root
|
|
1305
1680
|
: undefined;
|
|
1306
1681
|
let draftWriteRoot;
|
|
1682
|
+
let draftWriteWireRoot;
|
|
1307
1683
|
try {
|
|
1308
1684
|
const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1309
1685
|
draftWriteRoot = selected.path;
|
|
1686
|
+
draftWriteWireRoot = selected;
|
|
1310
1687
|
}
|
|
1311
1688
|
catch (err) {
|
|
1312
1689
|
if (err instanceof CatalogPathError) {
|
|
@@ -1321,13 +1698,15 @@ export function createOperatorRoutes(deps) {
|
|
|
1321
1698
|
return true;
|
|
1322
1699
|
}
|
|
1323
1700
|
const ctx = await resolveStageflowContext(draftWriteRoot);
|
|
1324
|
-
if (
|
|
1701
|
+
if (ctx.manifestStatus !== "ok") {
|
|
1325
1702
|
json(res, 400, {
|
|
1326
|
-
error: "Project root not found; initialize stageflow.yaml
|
|
1703
|
+
error: "Project root not found; initialize stageflow.yaml",
|
|
1327
1704
|
});
|
|
1328
1705
|
return true;
|
|
1329
1706
|
}
|
|
1330
|
-
const result = (await publishDraftPackage(ctx.projectRoot, parsed, "overwrite"
|
|
1707
|
+
const result = (await publishDraftPackage(ctx.projectRoot, parsed, "overwrite", {
|
|
1708
|
+
catalogRootKind: draftWriteWireRoot.kind,
|
|
1709
|
+
})).write;
|
|
1331
1710
|
if (!result.ok) {
|
|
1332
1711
|
json(res, result.status, {
|
|
1333
1712
|
error: result.error,
|
|
@@ -1339,10 +1718,70 @@ export function createOperatorRoutes(deps) {
|
|
|
1339
1718
|
pipeline: result.pipeline,
|
|
1340
1719
|
pipelinePath: result.pipelinePath,
|
|
1341
1720
|
stagePaths: result.stagePaths,
|
|
1721
|
+
catalog_root_path: ctx.projectRoot,
|
|
1722
|
+
...draftCatalogRootWire(draftWriteWireRoot),
|
|
1342
1723
|
...(result.taskPath !== undefined ? { taskPath: result.taskPath } : {}),
|
|
1343
1724
|
});
|
|
1344
1725
|
return true;
|
|
1345
1726
|
}
|
|
1727
|
+
if (method === "POST" && pathname === "/api/drafts/plan") {
|
|
1728
|
+
let body;
|
|
1729
|
+
try {
|
|
1730
|
+
body = await readJsonBody(req);
|
|
1731
|
+
}
|
|
1732
|
+
catch {
|
|
1733
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
1734
|
+
return true;
|
|
1735
|
+
}
|
|
1736
|
+
const writeRoot = body !== null &&
|
|
1737
|
+
typeof body === "object" &&
|
|
1738
|
+
!Array.isArray(body) &&
|
|
1739
|
+
typeof body.project_root === "string"
|
|
1740
|
+
? body.project_root
|
|
1741
|
+
: undefined;
|
|
1742
|
+
let draftWriteRoot;
|
|
1743
|
+
try {
|
|
1744
|
+
const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1745
|
+
draftWriteRoot = selected.path;
|
|
1746
|
+
}
|
|
1747
|
+
catch (err) {
|
|
1748
|
+
if (err instanceof CatalogPathError) {
|
|
1749
|
+
json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
|
|
1750
|
+
return true;
|
|
1751
|
+
}
|
|
1752
|
+
throw err;
|
|
1753
|
+
}
|
|
1754
|
+
const parsed = parsePlanDraftPackageBody(body);
|
|
1755
|
+
if ("ok" in parsed) {
|
|
1756
|
+
json(res, parsed.status, { error: parsed.error });
|
|
1757
|
+
return true;
|
|
1758
|
+
}
|
|
1759
|
+
const ctx = await resolveStageflowContext(draftWriteRoot);
|
|
1760
|
+
if (ctx.manifestStatus !== "ok") {
|
|
1761
|
+
json(res, 400, {
|
|
1762
|
+
error: "Project root not found; initialize stageflow.yaml",
|
|
1763
|
+
});
|
|
1764
|
+
return true;
|
|
1765
|
+
}
|
|
1766
|
+
const catalogPipelines = parsed.mode === "create"
|
|
1767
|
+
? await listPipelinesForContext(catalogContextFromStageflow(ctx))
|
|
1768
|
+
: [];
|
|
1769
|
+
const plan = await planDraftPackage(ctx.projectRoot, parsed, {
|
|
1770
|
+
mode: parsed.mode,
|
|
1771
|
+
catalogPipelines,
|
|
1772
|
+
});
|
|
1773
|
+
if (!plan.ok) {
|
|
1774
|
+
json(res, plan.status, { error: plan.error });
|
|
1775
|
+
return true;
|
|
1776
|
+
}
|
|
1777
|
+
json(res, 200, {
|
|
1778
|
+
pipelinePath: plan.pipelinePath,
|
|
1779
|
+
directory: plan.directory,
|
|
1780
|
+
files: plan.files,
|
|
1781
|
+
pipelineIdTaken: plan.pipelineIdTaken,
|
|
1782
|
+
});
|
|
1783
|
+
return true;
|
|
1784
|
+
}
|
|
1346
1785
|
if (method === "POST" && pathname === "/api/drafts/open") {
|
|
1347
1786
|
let body;
|
|
1348
1787
|
try {
|
|
@@ -1412,6 +1851,7 @@ export function createOperatorRoutes(deps) {
|
|
|
1412
1851
|
draft: result.draft,
|
|
1413
1852
|
destination: result.destination,
|
|
1414
1853
|
pipelinePath: result.pipelinePath,
|
|
1854
|
+
...draftCatalogRootWire(wireRoot),
|
|
1415
1855
|
...(result.taskPath !== undefined ? { taskPath: result.taskPath } : {}),
|
|
1416
1856
|
});
|
|
1417
1857
|
return true;
|
|
@@ -1481,23 +1921,8 @@ export function createOperatorRoutes(deps) {
|
|
|
1481
1921
|
const keyParam = url.searchParams.get("key");
|
|
1482
1922
|
const key = workshopAutosaveSlotKey(keyParam);
|
|
1483
1923
|
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
|
-
}
|
|
1924
|
+
try {
|
|
1925
|
+
await resolveDefaultWriteRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1501
1926
|
}
|
|
1502
1927
|
catch (err) {
|
|
1503
1928
|
if (err instanceof CatalogPathError) {
|
|
@@ -1506,6 +1931,7 @@ export function createOperatorRoutes(deps) {
|
|
|
1506
1931
|
}
|
|
1507
1932
|
throw err;
|
|
1508
1933
|
}
|
|
1934
|
+
const storeRoot = resolveWorkshopAutosaveStoreRoot();
|
|
1509
1935
|
const record = readWorkshopAutosave(storeRoot, key);
|
|
1510
1936
|
json(res, 200, { key, autosave: record });
|
|
1511
1937
|
return true;
|
|
@@ -1528,23 +1954,8 @@ export function createOperatorRoutes(deps) {
|
|
|
1528
1954
|
const writeRoot = typeof body.project_root === "string"
|
|
1529
1955
|
? body.project_root
|
|
1530
1956
|
: 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
|
-
}
|
|
1957
|
+
try {
|
|
1958
|
+
await resolveDefaultWriteRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1548
1959
|
}
|
|
1549
1960
|
catch (err) {
|
|
1550
1961
|
if (err instanceof CatalogPathError) {
|
|
@@ -1553,6 +1964,7 @@ export function createOperatorRoutes(deps) {
|
|
|
1553
1964
|
}
|
|
1554
1965
|
throw err;
|
|
1555
1966
|
}
|
|
1967
|
+
const storeRoot = resolveWorkshopAutosaveStoreRoot();
|
|
1556
1968
|
const candidate = {
|
|
1557
1969
|
version: 1,
|
|
1558
1970
|
key,
|
|
@@ -1583,6 +1995,9 @@ export function createOperatorRoutes(deps) {
|
|
|
1583
1995
|
diskFingerprints: body.diskFingerprints,
|
|
1584
1996
|
}
|
|
1585
1997
|
: {}),
|
|
1998
|
+
...(body.projectRoot === null || typeof body.projectRoot === "string"
|
|
1999
|
+
? { projectRoot: body.projectRoot }
|
|
2000
|
+
: {}),
|
|
1586
2001
|
};
|
|
1587
2002
|
const parsed = parseWorkshopAutosaveRecord(candidate);
|
|
1588
2003
|
if (!parsed) {
|
|
@@ -1611,23 +2026,8 @@ export function createOperatorRoutes(deps) {
|
|
|
1611
2026
|
const writeRoot = typeof body.project_root === "string"
|
|
1612
2027
|
? body.project_root
|
|
1613
2028
|
: 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
|
-
}
|
|
2029
|
+
try {
|
|
2030
|
+
await resolveDefaultWriteRoot({ store, bootCwd: cwd }, writeRoot);
|
|
1631
2031
|
}
|
|
1632
2032
|
catch (err) {
|
|
1633
2033
|
if (err instanceof CatalogPathError) {
|
|
@@ -1636,6 +2036,7 @@ export function createOperatorRoutes(deps) {
|
|
|
1636
2036
|
}
|
|
1637
2037
|
throw err;
|
|
1638
2038
|
}
|
|
2039
|
+
const storeRoot = resolveWorkshopAutosaveStoreRoot();
|
|
1639
2040
|
const cleared = clearWorkshopAutosave(storeRoot, key);
|
|
1640
2041
|
json(res, 200, { key, cleared });
|
|
1641
2042
|
return true;
|
|
@@ -2064,22 +2465,52 @@ export function createOperatorRoutes(deps) {
|
|
|
2064
2465
|
json(res, draftParsed.status, { error: draftParsed.error });
|
|
2065
2466
|
return true;
|
|
2066
2467
|
}
|
|
2468
|
+
const attachmentsParsed = parseWorkshopChatAttachments(body.attachments);
|
|
2469
|
+
if (!attachmentsParsed.ok) {
|
|
2470
|
+
json(res, 400, {
|
|
2471
|
+
error: attachmentsParsed.error,
|
|
2472
|
+
code: "workshop_attachment_invalid",
|
|
2473
|
+
});
|
|
2474
|
+
return true;
|
|
2475
|
+
}
|
|
2067
2476
|
const settingsDefault = await readWorkshopOpenModel(cwd);
|
|
2068
2477
|
const accept = String(req.headers.accept ?? "");
|
|
2069
2478
|
const wantsStream = body.stream === true ||
|
|
2070
2479
|
accept.includes("application/x-ndjson") ||
|
|
2071
2480
|
accept.includes("text/event-stream");
|
|
2481
|
+
const projectRootWire = typeof body.project_root === "string"
|
|
2482
|
+
? body.project_root.trim()
|
|
2483
|
+
: typeof body.projectRoot === "string"
|
|
2484
|
+
? body.projectRoot.trim()
|
|
2485
|
+
: "";
|
|
2486
|
+
let chatProjectRoot;
|
|
2487
|
+
if (projectRootWire) {
|
|
2488
|
+
try {
|
|
2489
|
+
const { wireRoot } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, projectRootWire);
|
|
2490
|
+
chatProjectRoot = wireRoot.path;
|
|
2491
|
+
}
|
|
2492
|
+
catch (err) {
|
|
2493
|
+
if (err instanceof CatalogPathError) {
|
|
2494
|
+
json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
|
|
2495
|
+
return true;
|
|
2496
|
+
}
|
|
2497
|
+
throw err;
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2072
2500
|
const chatTurnBase = {
|
|
2073
2501
|
sessionId: body.sessionId,
|
|
2074
2502
|
draft: draftParsed,
|
|
2075
2503
|
message: body.message,
|
|
2076
2504
|
autoApply: body.autoApply === true,
|
|
2505
|
+
attachments: attachmentsParsed.attachments,
|
|
2506
|
+
context: parseWorkshopChatContext(body.context),
|
|
2077
2507
|
model: typeof body.model === "string" || body.model === null
|
|
2078
2508
|
? body.model
|
|
2079
2509
|
: undefined,
|
|
2080
2510
|
settingsDefault: settingsDefault ?? null,
|
|
2081
2511
|
registry: workshopChatRegistry,
|
|
2082
2512
|
storeRoot: workshopSessionStoreRoot(),
|
|
2513
|
+
...(chatProjectRoot ? { projectRoot: chatProjectRoot } : {}),
|
|
2083
2514
|
};
|
|
2084
2515
|
// Fail closed on missing sessions before opening an NDJSON body.
|
|
2085
2516
|
try {
|
|
@@ -2199,7 +2630,7 @@ export function createOperatorRoutes(deps) {
|
|
|
2199
2630
|
events: [errorEvent],
|
|
2200
2631
|
draft: draftParsed,
|
|
2201
2632
|
pending: null,
|
|
2202
|
-
autoApply:
|
|
2633
|
+
autoApply: chatTurnBase.autoApply,
|
|
2203
2634
|
model: resolvedModel,
|
|
2204
2635
|
buildId: workshopChatRegistry.getPinnedBuildId(body.sessionId),
|
|
2205
2636
|
});
|
|
@@ -2336,8 +2767,94 @@ export function createOperatorRoutes(deps) {
|
|
|
2336
2767
|
})) {
|
|
2337
2768
|
return true;
|
|
2338
2769
|
}
|
|
2770
|
+
if (method === "GET" && pathname === "/api/skills/usage") {
|
|
2771
|
+
const projectRoot = url.searchParams.get("project_root") ?? undefined;
|
|
2772
|
+
let usageRoot;
|
|
2773
|
+
try {
|
|
2774
|
+
const { wireRoot } = projectRoot === undefined
|
|
2775
|
+
? await resolveWritableCatalogRoot({ store, bootCwd: cwd }, undefined)
|
|
2776
|
+
: await resolveCatalogStartInput({ store, bootCwd: cwd }, projectRoot);
|
|
2777
|
+
usageRoot = wireRoot.path;
|
|
2778
|
+
}
|
|
2779
|
+
catch (err) {
|
|
2780
|
+
if (err instanceof CatalogPathError) {
|
|
2781
|
+
json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
|
|
2782
|
+
return true;
|
|
2783
|
+
}
|
|
2784
|
+
throw err;
|
|
2785
|
+
}
|
|
2786
|
+
const usages = await buildSkillUsageIndex(usageRoot);
|
|
2787
|
+
if (usages === null) {
|
|
2788
|
+
json(res, 404, { error: "No Stageflow catalog found" });
|
|
2789
|
+
return true;
|
|
2790
|
+
}
|
|
2791
|
+
json(res, 200, { usages });
|
|
2792
|
+
return true;
|
|
2793
|
+
}
|
|
2339
2794
|
if (method === "GET" && pathname === "/api/skills") {
|
|
2340
|
-
|
|
2795
|
+
const projectRoot = url.searchParams.get("project_root") ?? undefined;
|
|
2796
|
+
try {
|
|
2797
|
+
const { wireRoot } = projectRoot === undefined
|
|
2798
|
+
? await resolveWritableCatalogRoot({ store, bootCwd: cwd }, undefined)
|
|
2799
|
+
: await resolveCatalogStartInput({ store, bootCwd: cwd }, projectRoot);
|
|
2800
|
+
json(res, 200, await listSkills({ cwd: wireRoot.path, agentDir }));
|
|
2801
|
+
}
|
|
2802
|
+
catch (err) {
|
|
2803
|
+
if (err instanceof CatalogPathError) {
|
|
2804
|
+
json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
|
|
2805
|
+
return true;
|
|
2806
|
+
}
|
|
2807
|
+
throw err;
|
|
2808
|
+
}
|
|
2809
|
+
return true;
|
|
2810
|
+
}
|
|
2811
|
+
if (method === "POST" && pathname === "/api/skills/install") {
|
|
2812
|
+
let body;
|
|
2813
|
+
try {
|
|
2814
|
+
body = await readJsonBody(req);
|
|
2815
|
+
}
|
|
2816
|
+
catch {
|
|
2817
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
2818
|
+
return true;
|
|
2819
|
+
}
|
|
2820
|
+
const parsed = parseInstallSkillBody(body);
|
|
2821
|
+
if ("ok" in parsed) {
|
|
2822
|
+
json(res, parsed.status, { error: parsed.error });
|
|
2823
|
+
return true;
|
|
2824
|
+
}
|
|
2825
|
+
let skillWriteRoot;
|
|
2826
|
+
try {
|
|
2827
|
+
const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, undefined);
|
|
2828
|
+
skillWriteRoot = selected.path;
|
|
2829
|
+
}
|
|
2830
|
+
catch (err) {
|
|
2831
|
+
if (err instanceof CatalogPathError) {
|
|
2832
|
+
json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
|
|
2833
|
+
return true;
|
|
2834
|
+
}
|
|
2835
|
+
throw err;
|
|
2836
|
+
}
|
|
2837
|
+
const stageflowCtx = await resolveStageflowContext(skillWriteRoot);
|
|
2838
|
+
if (stageflowCtx.manifestStatus !== "ok") {
|
|
2839
|
+
json(res, 400, {
|
|
2840
|
+
error: "Project root not found; initialize stageflow.yaml",
|
|
2841
|
+
});
|
|
2842
|
+
return true;
|
|
2843
|
+
}
|
|
2844
|
+
const result = await installSkillFromCommand({
|
|
2845
|
+
projectRoot: stageflowCtx.projectRoot,
|
|
2846
|
+
command: parsed.command,
|
|
2847
|
+
});
|
|
2848
|
+
if (!result.ok) {
|
|
2849
|
+
json(res, result.status ?? 400, { error: result.error });
|
|
2850
|
+
return true;
|
|
2851
|
+
}
|
|
2852
|
+
json(res, 201, {
|
|
2853
|
+
ok: true,
|
|
2854
|
+
skill_name: result.skill_name,
|
|
2855
|
+
dest_path: result.dest_path,
|
|
2856
|
+
project_root: result.project_root,
|
|
2857
|
+
});
|
|
2341
2858
|
return true;
|
|
2342
2859
|
}
|
|
2343
2860
|
if (method === "GET" && pathname === "/api/extensions") {
|
|
@@ -2444,6 +2961,7 @@ export function createOperatorRoutes(deps) {
|
|
|
2444
2961
|
}
|
|
2445
2962
|
}
|
|
2446
2963
|
};
|
|
2964
|
+
return Object.assign(handler, { dispose: () => liveView.dispose() });
|
|
2447
2965
|
}
|
|
2448
2966
|
export async function startUiServer(options) {
|
|
2449
2967
|
const host = options.host ?? "127.0.0.1";
|
|
@@ -2492,6 +3010,8 @@ export async function startUiServer(options) {
|
|
|
2492
3010
|
});
|
|
2493
3011
|
envelope.server.on("close", () => {
|
|
2494
3012
|
shutdown?.uninstall();
|
|
3013
|
+
if ("dispose" in routes)
|
|
3014
|
+
void routes.dispose();
|
|
2495
3015
|
});
|
|
2496
3016
|
return { ...envelope, shutdown };
|
|
2497
3017
|
}
|