stageflow 0.33.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 +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 +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/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.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/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/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/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/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/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/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/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 +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 +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 +1 -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
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { parseDocument } from "yaml";
|
|
3
|
+
import { findCatalogTrigger, loadCatalogTriggers, triggerStoreId, } from "./triggerCatalog.js";
|
|
4
|
+
import { resolveCatalogRoots, findCatalogRoot } from "./resolveCatalogRoots.js";
|
|
5
|
+
import { loadTriggerFromYamlOutcome } from "./loadTrigger.js";
|
|
6
|
+
import { catalogContextFromStageflow } from "./resolveCatalogContext.js";
|
|
7
|
+
import { getCatalogScanPaths } from "./browseCatalog.js";
|
|
8
|
+
import { resolveStageflowContext } from "../project/resolveStageflowContext.js";
|
|
9
|
+
import { parseEventField, parseScheduleField, scheduleCronError, triggerReferenceError, } from "./createTrigger.js";
|
|
10
|
+
export function parsePatchTriggerBody(body) {
|
|
11
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
12
|
+
return { ok: false, status: 400, error: "Request body must be an object" };
|
|
13
|
+
}
|
|
14
|
+
const record = body;
|
|
15
|
+
const keys = Object.keys(record);
|
|
16
|
+
if (keys.length !== 1 || keys[0] !== "enabled") {
|
|
17
|
+
return {
|
|
18
|
+
ok: false,
|
|
19
|
+
status: 400,
|
|
20
|
+
error: "Only enabled is allowed in the request body",
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
if (typeof record.enabled !== "boolean") {
|
|
24
|
+
return { ok: false, status: 400, error: "enabled must be a boolean" };
|
|
25
|
+
}
|
|
26
|
+
return { enabled: record.enabled };
|
|
27
|
+
}
|
|
28
|
+
function isPlainObject(value) {
|
|
29
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
30
|
+
}
|
|
31
|
+
export function parsePutTriggerBody(body, triggerId) {
|
|
32
|
+
if (!isPlainObject(body)) {
|
|
33
|
+
return { ok: false, status: 400, error: "Request body must be an object" };
|
|
34
|
+
}
|
|
35
|
+
if ("id" in body && body.id !== triggerId) {
|
|
36
|
+
return { ok: false, status: 400, error: "id cannot be changed" };
|
|
37
|
+
}
|
|
38
|
+
if (typeof body.pipeline !== "string" || !body.pipeline.trim()) {
|
|
39
|
+
return { ok: false, status: 400, error: "pipeline is required" };
|
|
40
|
+
}
|
|
41
|
+
if (body.task !== undefined &&
|
|
42
|
+
body.task !== null &&
|
|
43
|
+
(typeof body.task !== "string" || !body.task.trim())) {
|
|
44
|
+
return { ok: false, status: 400, error: "task must be a non-empty string" };
|
|
45
|
+
}
|
|
46
|
+
if (body.kind !== "manual" && body.kind !== "schedule" && body.kind !== "event") {
|
|
47
|
+
return {
|
|
48
|
+
ok: false,
|
|
49
|
+
status: 400,
|
|
50
|
+
error: "kind must be one of: manual, schedule, event",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const scheduleRaw = body.kind === "schedule" ? (body.schedule === null ? undefined : body.schedule) : undefined;
|
|
54
|
+
const eventRaw = body.kind === "event" ? (body.event === null ? undefined : body.event) : undefined;
|
|
55
|
+
const schedule = parseScheduleField(scheduleRaw);
|
|
56
|
+
if (!schedule.ok) {
|
|
57
|
+
return { ok: false, status: 400, error: schedule.message };
|
|
58
|
+
}
|
|
59
|
+
const event = parseEventField(eventRaw);
|
|
60
|
+
if (!event.ok) {
|
|
61
|
+
return { ok: false, status: 400, error: event.message };
|
|
62
|
+
}
|
|
63
|
+
if (body.kind === "schedule" && schedule.value === undefined) {
|
|
64
|
+
return { ok: false, status: 400, error: "schedule.cron is required for kind=schedule" };
|
|
65
|
+
}
|
|
66
|
+
if (body.kind === "event" && event.value === undefined) {
|
|
67
|
+
return { ok: false, status: 400, error: "event.source is required for kind=event" };
|
|
68
|
+
}
|
|
69
|
+
if (typeof body.enabled !== "boolean") {
|
|
70
|
+
return { ok: false, status: 400, error: "enabled must be a boolean" };
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
pipeline: body.pipeline,
|
|
74
|
+
...(typeof body.task === "string" ? { task: body.task } : {}),
|
|
75
|
+
kind: body.kind,
|
|
76
|
+
...(schedule.value !== undefined ? { schedule: schedule.value } : {}),
|
|
77
|
+
...(event.value !== undefined ? { event: event.value } : {}),
|
|
78
|
+
enabled: body.enabled,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function projectRootFilterFromBody(body) {
|
|
82
|
+
if (!body || typeof body !== "object" || Array.isArray(body))
|
|
83
|
+
return undefined;
|
|
84
|
+
const wire = body.project_root;
|
|
85
|
+
return typeof wire === "string" && wire.trim().length > 0 ? wire.trim() : undefined;
|
|
86
|
+
}
|
|
87
|
+
async function locateWritableTrigger(cwd, store, triggerId, projectRootFilter) {
|
|
88
|
+
const located = await findCatalogTrigger({
|
|
89
|
+
store,
|
|
90
|
+
bootCwd: cwd,
|
|
91
|
+
triggerId,
|
|
92
|
+
projectRootFilter,
|
|
93
|
+
});
|
|
94
|
+
if (!located.ok) {
|
|
95
|
+
return {
|
|
96
|
+
ok: false,
|
|
97
|
+
status: located.status,
|
|
98
|
+
error: located.reason,
|
|
99
|
+
...(located.status === 409 ? { project_roots: located.project_roots } : {}),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const roots = await resolveCatalogRoots({ store, bootCwd: cwd });
|
|
103
|
+
const root = findCatalogRoot(roots, located.match.project_root);
|
|
104
|
+
if (root?.read_only) {
|
|
105
|
+
return { ok: false, status: 403, error: "catalog_root_read_only" };
|
|
106
|
+
}
|
|
107
|
+
return { ok: true, match: located.match };
|
|
108
|
+
}
|
|
109
|
+
export async function updateTriggerEnabled(cwd, store, triggerId, enabled, options = {}) {
|
|
110
|
+
const located = await locateWritableTrigger(cwd, store, triggerId, options.projectRootFilter);
|
|
111
|
+
if (!located.ok) {
|
|
112
|
+
return located;
|
|
113
|
+
}
|
|
114
|
+
const { match } = located;
|
|
115
|
+
const yamlText = await readFile(match.path, "utf8");
|
|
116
|
+
const doc = parseDocument(yamlText);
|
|
117
|
+
if (!doc.contents || typeof doc.contents !== "object") {
|
|
118
|
+
return { ok: false, status: 500, error: "Trigger YAML must be an object" };
|
|
119
|
+
}
|
|
120
|
+
doc.set("enabled", enabled);
|
|
121
|
+
const patched = doc.toString();
|
|
122
|
+
const loadOutcome = loadTriggerFromYamlOutcome(patched, match.path);
|
|
123
|
+
if (!loadOutcome.ok) {
|
|
124
|
+
return {
|
|
125
|
+
ok: false,
|
|
126
|
+
status: 400,
|
|
127
|
+
error: loadOutcome.issues[0]?.message ?? "Invalid trigger after patch",
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
await writeFile(match.path, patched, "utf8");
|
|
131
|
+
await store.upsertTrigger({
|
|
132
|
+
id: triggerStoreId(match.project_root, loadOutcome.value.id),
|
|
133
|
+
definitionRef: match.definition_ref,
|
|
134
|
+
enabled: loadOutcome.value.enabled,
|
|
135
|
+
});
|
|
136
|
+
const items = await loadCatalogTriggers({ store, bootCwd: cwd });
|
|
137
|
+
const item = items.find((row) => row.id === triggerId && row.project_root === match.project_root);
|
|
138
|
+
if (!item) {
|
|
139
|
+
return { ok: false, status: 500, error: "Trigger updated but not listed" };
|
|
140
|
+
}
|
|
141
|
+
return { ok: true, trigger: item };
|
|
142
|
+
}
|
|
143
|
+
export async function updateTriggerDefinition(cwd, store, triggerId, input, options = {}) {
|
|
144
|
+
const located = await locateWritableTrigger(cwd, store, triggerId, options.projectRootFilter);
|
|
145
|
+
if (!located.ok) {
|
|
146
|
+
return located;
|
|
147
|
+
}
|
|
148
|
+
const { match } = located;
|
|
149
|
+
const ctx = catalogContextFromStageflow(await resolveStageflowContext(match.rootPath));
|
|
150
|
+
const scanPaths = await getCatalogScanPaths(ctx);
|
|
151
|
+
if (!scanPaths) {
|
|
152
|
+
return { ok: false, status: 404, error: "No Stageflow catalog found" };
|
|
153
|
+
}
|
|
154
|
+
const refError = await triggerReferenceError(scanPaths, input.pipeline, input.task);
|
|
155
|
+
if (refError) {
|
|
156
|
+
return { ok: false, status: 422, error: refError };
|
|
157
|
+
}
|
|
158
|
+
if (input.kind === "schedule") {
|
|
159
|
+
if (!input.schedule) {
|
|
160
|
+
return { ok: false, status: 422, error: "schedule.cron is required for kind=schedule" };
|
|
161
|
+
}
|
|
162
|
+
const cronError = scheduleCronError(input.schedule);
|
|
163
|
+
if (cronError) {
|
|
164
|
+
return { ok: false, status: 422, error: cronError };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const yamlText = await readFile(match.path, "utf8");
|
|
168
|
+
const doc = parseDocument(yamlText);
|
|
169
|
+
if (!doc.contents || typeof doc.contents !== "object") {
|
|
170
|
+
return { ok: false, status: 500, error: "Trigger YAML must be an object" };
|
|
171
|
+
}
|
|
172
|
+
doc.set("pipeline", input.pipeline);
|
|
173
|
+
if (input.task !== undefined)
|
|
174
|
+
doc.set("task", input.task);
|
|
175
|
+
else
|
|
176
|
+
doc.delete("task");
|
|
177
|
+
doc.set("kind", input.kind);
|
|
178
|
+
if (input.kind === "schedule" && input.schedule) {
|
|
179
|
+
doc.delete("event");
|
|
180
|
+
doc.set("schedule", {
|
|
181
|
+
cron: input.schedule.cron,
|
|
182
|
+
...(input.schedule.timezone !== undefined ? { timezone: input.schedule.timezone } : {}),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
else if (input.kind === "event" && input.event) {
|
|
186
|
+
doc.delete("schedule");
|
|
187
|
+
doc.set("event", {
|
|
188
|
+
source: input.event.source,
|
|
189
|
+
...(input.event.match !== undefined ? { match: input.event.match } : {}),
|
|
190
|
+
...(input.event.config !== undefined ? { config: input.event.config } : {}),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
doc.delete("schedule");
|
|
195
|
+
doc.delete("event");
|
|
196
|
+
}
|
|
197
|
+
doc.set("enabled", input.enabled);
|
|
198
|
+
const patched = doc.toString();
|
|
199
|
+
const loadOutcome = loadTriggerFromYamlOutcome(patched, match.path);
|
|
200
|
+
if (!loadOutcome.ok) {
|
|
201
|
+
return {
|
|
202
|
+
ok: false,
|
|
203
|
+
status: 400,
|
|
204
|
+
error: loadOutcome.issues[0]?.message ?? "Invalid trigger after update",
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
await writeFile(match.path, patched, "utf8");
|
|
208
|
+
await store.upsertTrigger({
|
|
209
|
+
id: triggerStoreId(match.project_root, loadOutcome.value.id),
|
|
210
|
+
definitionRef: match.definition_ref,
|
|
211
|
+
enabled: loadOutcome.value.enabled,
|
|
212
|
+
});
|
|
213
|
+
const items = await loadCatalogTriggers({ store, bootCwd: cwd });
|
|
214
|
+
const item = items.find((row) => row.id === triggerId && row.project_root === match.project_root);
|
|
215
|
+
if (!item) {
|
|
216
|
+
return { ok: false, status: 500, error: "Trigger updated but not listed" };
|
|
217
|
+
}
|
|
218
|
+
return { ok: true, trigger: item };
|
|
219
|
+
}
|
|
220
|
+
export { projectRootFilterFromBody };
|
|
@@ -3,7 +3,7 @@ import type { TriggerFile } from "../types/trigger.js";
|
|
|
3
3
|
import type { LoadIssue } from "./loadOutcome.js";
|
|
4
4
|
export type ValidationSeverity = "error" | "warning";
|
|
5
5
|
export type ValidationCategory = "pipeline" | "stage" | "catalog" | "task" | "trigger";
|
|
6
|
-
export type ValidationFindingCode = "pipeline.invalid_shape" | "pipeline.dag_error" | "pipeline.missing_stage" | "pipeline.load_error" | "pipeline.string_stage_ref" | "pipeline.stage_uses_inline_conflict" | "pipeline.stage_missing_body" | "pipeline.stage_id_mismatch" | "pipeline.invalid_completion" | "catalog.path_case_mismatch" | "pipeline.invalid_recovery" | "pipeline.include_cycle" | "pipeline.include_invalid" | "pipeline.include_duplicate_stage" | "pipeline.invalid_agent" | "pipeline.invalid_model" | "pipeline.invalid_verify" | "pipeline.io_incompatible" | "pipeline.model_applies" | "pipeline.route_if_invalid" | "pipeline.route_all_gated" | "pipeline.invalid_requires" | "pipeline.requires_conflict" | "stage.invalid_shape" | "stage.invalid_model" | "stage.missing_model" | "stage.invalid_payload_schema" | "stage.invalid_clone_input_schema" | "stage.unresolved_schema_ref" | "stage.invalid_clone_actions" | "stage.invalid_gate_kinds" | "stage.invalid_pre_emit_checks" | "stage.invalid_timeout_ms" | "stage.invalid_skill" | "stage.invalid_mcp" | "stage.invalid_browser" | "stage.invalid_secrets" | "stage.unknown_secret" | "stage.denied_secret" | "stage.invalid_requires" | "stage.invalid_agent" | "stage.invalid_io" | "stage.load_error" | "stage.id_filename_mismatch" | "task.invalid_shape" | "task.load_error" | "task.entry_input_unmet" | "task.binding_conflict" | "task.repository_ref_required" | "task.ref_without_repository" | "task.repository_invalid" | "trigger.invalid_shape" | "trigger.load_error" | "trigger.unknown_pipeline" | "trigger.unknown_task" | "catalog.duplicate_pipeline_id" | "catalog.manifest_missing" | "catalog.manifest_invalid" | "catalog.empty_catalog" | "catalog.manifest_load_error" | "catalog.invalid_mcp" | "catalog.mcp_command_missing" | "catalog.mixed_yaml_dialect" | "catalog.legacy_yaml" | "catalog.stageflow_home_absolute_path";
|
|
6
|
+
export type ValidationFindingCode = "pipeline.invalid_shape" | "pipeline.dag_error" | "pipeline.missing_stage" | "pipeline.load_error" | "pipeline.string_stage_ref" | "pipeline.stage_uses_inline_conflict" | "pipeline.stage_missing_body" | "pipeline.stage_id_mismatch" | "pipeline.stage_wrapper_skill_rejected" | "pipeline.invalid_completion" | "catalog.path_case_mismatch" | "pipeline.invalid_recovery" | "pipeline.include_cycle" | "pipeline.include_invalid" | "pipeline.include_duplicate_stage" | "pipeline.invalid_agent" | "pipeline.invalid_model" | "pipeline.invalid_verify" | "pipeline.io_incompatible" | "pipeline.model_applies" | "pipeline.route_if_invalid" | "pipeline.route_all_gated" | "pipeline.invalid_requires" | "pipeline.requires_conflict" | "stage.invalid_shape" | "stage.invalid_model" | "stage.missing_model" | "stage.invalid_payload_schema" | "stage.invalid_clone_input_schema" | "stage.unresolved_schema_ref" | "stage.invalid_clone_actions" | "stage.invalid_gate_kinds" | "stage.invalid_pre_emit_checks" | "stage.invalid_timeout_ms" | "stage.invalid_skill" | "stage.invalid_mcp" | "stage.invalid_browser" | "stage.invalid_secrets" | "stage.unknown_secret" | "stage.denied_secret" | "stage.invalid_requires" | "stage.invalid_agent" | "stage.invalid_io" | "stage.load_error" | "stage.id_filename_mismatch" | "task.invalid_shape" | "task.load_error" | "task.entry_input_unmet" | "task.binding_conflict" | "task.repository_ref_required" | "task.ref_without_repository" | "task.repository_invalid" | "trigger.invalid_shape" | "trigger.load_error" | "trigger.unknown_pipeline" | "trigger.unknown_task" | "catalog.duplicate_pipeline_id" | "catalog.manifest_missing" | "catalog.manifest_invalid" | "catalog.empty_catalog" | "catalog.manifest_load_error" | "catalog.invalid_mcp" | "catalog.mcp_command_missing" | "catalog.mixed_yaml_dialect" | "catalog.legacy_yaml" | "catalog.stageflow_home_absolute_path";
|
|
7
7
|
export type ValidationFinding = {
|
|
8
8
|
severity: ValidationSeverity;
|
|
9
9
|
code: ValidationFindingCode | string;
|
|
@@ -12,8 +12,10 @@ export type ValidationFinding = {
|
|
|
12
12
|
category: ValidationCategory;
|
|
13
13
|
pipelineId?: string;
|
|
14
14
|
stageId?: string;
|
|
15
|
+
line?: number;
|
|
16
|
+
column?: number;
|
|
15
17
|
};
|
|
16
|
-
export type ValidationScope = "full" | "pipeline" | "task" | "trigger";
|
|
18
|
+
export type ValidationScope = "full" | "pipeline" | "task" | "trigger" | "stage";
|
|
17
19
|
export type ValidateCatalogOptions = {
|
|
18
20
|
cwd?: string;
|
|
19
21
|
projectRoot?: string;
|
|
@@ -21,6 +23,8 @@ export type ValidateCatalogOptions = {
|
|
|
21
23
|
pipeline?: string;
|
|
22
24
|
task?: string;
|
|
23
25
|
trigger?: string;
|
|
26
|
+
stage?: string;
|
|
27
|
+
stageContent?: string;
|
|
24
28
|
strict?: boolean;
|
|
25
29
|
};
|
|
26
30
|
export type ValidationResult = {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
1
3
|
import path from "node:path";
|
|
2
4
|
import { globalStageflowHome } from "../project/globalHome.js";
|
|
3
5
|
import { INLINE_PIPELINE_PATH, loadPipelineFromObjectOutcome, loadPipelineOutcome, resolvePipelinePath, } from "./loadPipeline.js";
|
|
@@ -122,12 +124,21 @@ function findingCatalog(cwd, absPath, message, code, severity) {
|
|
|
122
124
|
category: "catalog",
|
|
123
125
|
}, severity);
|
|
124
126
|
}
|
|
127
|
+
function withIssueLocation(finding, issue) {
|
|
128
|
+
if (issue.line === undefined && issue.column === undefined)
|
|
129
|
+
return finding;
|
|
130
|
+
return {
|
|
131
|
+
...finding,
|
|
132
|
+
...(issue.line !== undefined ? { line: issue.line } : {}),
|
|
133
|
+
...(issue.column !== undefined ? { column: issue.column } : {}),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
125
136
|
export function findingsFromLoadIssues(cwd, absPath, issues) {
|
|
126
137
|
return issues.flatMap((issue) => {
|
|
127
138
|
if (issue.category === "pipeline") {
|
|
128
139
|
if (issue.code === "pipeline.model_applies" || issue.code === "pipeline.route_all_gated") {
|
|
129
140
|
return [
|
|
130
|
-
baseFinding({
|
|
141
|
+
withIssueLocation(baseFinding({
|
|
131
142
|
cwd,
|
|
132
143
|
absPath,
|
|
133
144
|
message: issue.message,
|
|
@@ -135,26 +146,26 @@ export function findingsFromLoadIssues(cwd, absPath, issues) {
|
|
|
135
146
|
category: "pipeline",
|
|
136
147
|
pipelineId: issue.pipelineId,
|
|
137
148
|
stageId: issue.stageId,
|
|
138
|
-
}, "warning"),
|
|
149
|
+
}, "warning"), issue),
|
|
139
150
|
];
|
|
140
151
|
}
|
|
141
152
|
return [
|
|
142
|
-
findingPipelineError(cwd, absPath, issue.message, issue.code, issue.pipelineId, issue.stageId),
|
|
153
|
+
withIssueLocation(findingPipelineError(cwd, absPath, issue.message, issue.code, issue.pipelineId, issue.stageId), issue),
|
|
143
154
|
];
|
|
144
155
|
}
|
|
145
156
|
if (issue.category === "stage") {
|
|
146
157
|
return [
|
|
147
|
-
findingStageError(cwd, absPath, issue.message, issue.code, issue.stageId),
|
|
158
|
+
withIssueLocation(findingStageError(cwd, absPath, issue.message, issue.code, issue.stageId), issue),
|
|
148
159
|
];
|
|
149
160
|
}
|
|
150
161
|
if (issue.category === "task") {
|
|
151
162
|
return [
|
|
152
|
-
findingTaskError(cwd, absPath, issue.message, issue.code),
|
|
163
|
+
withIssueLocation(findingTaskError(cwd, absPath, issue.message, issue.code), issue),
|
|
153
164
|
];
|
|
154
165
|
}
|
|
155
166
|
if (issue.category === "trigger") {
|
|
156
167
|
return [
|
|
157
|
-
findingTriggerError(cwd, absPath, issue.message, issue.code),
|
|
168
|
+
withIssueLocation(findingTriggerError(cwd, absPath, issue.message, issue.code), issue),
|
|
158
169
|
];
|
|
159
170
|
}
|
|
160
171
|
if (issue.category === "catalog") {
|
|
@@ -164,7 +175,7 @@ export function findingsFromLoadIssues(cwd, absPath, issues) {
|
|
|
164
175
|
code === "catalog.legacy_yaml"
|
|
165
176
|
? "warning"
|
|
166
177
|
: "error";
|
|
167
|
-
return [findingCatalog(cwd, absPath, issue.message, code, severity)];
|
|
178
|
+
return [withIssueLocation(findingCatalog(cwd, absPath, issue.message, code, severity), issue)];
|
|
168
179
|
}
|
|
169
180
|
return [];
|
|
170
181
|
});
|
|
@@ -615,6 +626,30 @@ async function validateSingleTask(invocationCwd, taskArg) {
|
|
|
615
626
|
}
|
|
616
627
|
return [];
|
|
617
628
|
}
|
|
629
|
+
async function validateSingleStage(invocationCwd, stageArg, stageContent) {
|
|
630
|
+
const ctx = await resolveCatalogContext(invocationCwd);
|
|
631
|
+
const relCwd = ctx.projectRoot ?? invocationCwd;
|
|
632
|
+
const stagePath = path.resolve(invocationCwd, stageArg);
|
|
633
|
+
if (stageContent === undefined) {
|
|
634
|
+
return validateStageFile(relCwd, stagePath);
|
|
635
|
+
}
|
|
636
|
+
const tmpDir = await mkdtemp(path.join(tmpdir(), "sf-stage-validate-"));
|
|
637
|
+
const tmpPath = path.join(tmpDir, path.basename(stagePath));
|
|
638
|
+
try {
|
|
639
|
+
await writeFile(tmpPath, stageContent, "utf8");
|
|
640
|
+
const findings = await validateStageFile(relCwd, tmpPath);
|
|
641
|
+
const displayPath = relPath(relCwd, stagePath);
|
|
642
|
+
const tmpRel = relPath(relCwd, tmpPath);
|
|
643
|
+
return findings.map((finding) => finding.path === tmpRel ||
|
|
644
|
+
finding.path === tmpPath ||
|
|
645
|
+
finding.path.includes(tmpDir)
|
|
646
|
+
? { ...finding, path: displayPath }
|
|
647
|
+
: finding);
|
|
648
|
+
}
|
|
649
|
+
finally {
|
|
650
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
651
|
+
}
|
|
652
|
+
}
|
|
618
653
|
async function validateSingleTrigger(invocationCwd, triggerArg) {
|
|
619
654
|
const ctx = await resolveCatalogContext(invocationCwd);
|
|
620
655
|
const relCwd = ctx.projectRoot ?? invocationCwd;
|
|
@@ -647,6 +682,9 @@ export async function validateCatalog(options) {
|
|
|
647
682
|
if (options.scope === "trigger" && !options.trigger) {
|
|
648
683
|
throw new Error("validateCatalog: trigger is required when scope is \"trigger\"");
|
|
649
684
|
}
|
|
685
|
+
if (options.scope === "stage" && !options.stage) {
|
|
686
|
+
throw new Error("validateCatalog: stage is required when scope is \"stage\"");
|
|
687
|
+
}
|
|
650
688
|
const allFindings = [];
|
|
651
689
|
if (options.scope === "pipeline") {
|
|
652
690
|
return validatePipeline(options.pipeline, {
|
|
@@ -662,6 +700,9 @@ export async function validateCatalog(options) {
|
|
|
662
700
|
else if (options.scope === "trigger") {
|
|
663
701
|
allFindings.push(...(await validateSingleTrigger(cwd, options.trigger)));
|
|
664
702
|
}
|
|
703
|
+
else if (options.scope === "stage") {
|
|
704
|
+
allFindings.push(...(await validateSingleStage(cwd, options.stage, options.stageContent)));
|
|
705
|
+
}
|
|
665
706
|
else {
|
|
666
707
|
allFindings.push(...(await validateManifestAll(cwd)));
|
|
667
708
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export declare function normalizePipelineCatalogFilename(pipelineId: string, filename?: string | null): string;
|
|
2
|
+
export declare function taskIdFromDraftTask(task: {
|
|
3
|
+
filename?: string;
|
|
4
|
+
body?: Record<string, unknown>;
|
|
5
|
+
}): string;
|
|
6
|
+
export declare function normalizeTaskCatalogFilename(taskId: string, filename?: string | null): string;
|
|
7
|
+
export type DraftTaskArtifact = {
|
|
8
|
+
filename: string;
|
|
9
|
+
body: Record<string, unknown>;
|
|
10
|
+
};
|
|
11
|
+
export type DraftPackageFilenames = {
|
|
12
|
+
pipeline: {
|
|
13
|
+
id: string;
|
|
14
|
+
};
|
|
15
|
+
task?: DraftTaskArtifact;
|
|
16
|
+
};
|
|
17
|
+
export type CatalogYamlKind = "pipeline" | "task" | "other";
|
|
18
|
+
export declare function classifyCatalogYamlDocument(raw: Record<string, unknown>): CatalogYamlKind;
|
|
19
|
+
export declare function legacyBareCatalogFilenames(entityId: string): string[];
|
|
20
|
+
export declare function isMisnamedCatalogBasename(basename: string): boolean;
|
|
21
|
+
export declare function canonicalCatalogBasenameForDocument(raw: Record<string, unknown>, sourceBasename?: string): string | null;
|
|
22
|
+
export type WorkshopCatalogRepairMove = {
|
|
23
|
+
from: string;
|
|
24
|
+
to: string;
|
|
25
|
+
};
|
|
26
|
+
export type WorkshopCatalogRepairResult = {
|
|
27
|
+
renamed: WorkshopCatalogRepairMove[];
|
|
28
|
+
removed: string[];
|
|
29
|
+
};
|
|
30
|
+
export declare function reconcileWorkshopCatalogFilenames(pipelineId: string, taskId?: string | null, canonicalPipelineBasename?: string, canonicalTaskBasename?: string | null): {
|
|
31
|
+
renameCandidates: WorkshopCatalogRepairMove[];
|
|
32
|
+
removeCandidates: string[];
|
|
33
|
+
};
|
|
34
|
+
export declare function normalizeDraftCatalogFilenames<T extends DraftPackageFilenames>(draft: T, pipelineFilename?: string | null): {
|
|
35
|
+
draft: T;
|
|
36
|
+
pipelineFilename: string;
|
|
37
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
const PIPELINE_SUFFIX = ".pipeline.yaml";
|
|
2
|
+
const TASK_SUFFIX = ".task.yaml";
|
|
3
|
+
function basenameOnly(name) {
|
|
4
|
+
const trimmed = name.trim();
|
|
5
|
+
const parts = trimmed.split(/[\\/]/);
|
|
6
|
+
return parts[parts.length - 1]?.trim() || trimmed;
|
|
7
|
+
}
|
|
8
|
+
export function normalizePipelineCatalogFilename(pipelineId, filename) {
|
|
9
|
+
const id = pipelineId.trim() || "untitled";
|
|
10
|
+
const base = filename?.trim() ? basenameOnly(filename) : "";
|
|
11
|
+
if (base.endsWith(PIPELINE_SUFFIX)) {
|
|
12
|
+
const stem = base.slice(0, -PIPELINE_SUFFIX.length);
|
|
13
|
+
if (stem === id)
|
|
14
|
+
return base;
|
|
15
|
+
}
|
|
16
|
+
return `${id}${PIPELINE_SUFFIX}`;
|
|
17
|
+
}
|
|
18
|
+
export function taskIdFromDraftTask(task) {
|
|
19
|
+
const bodyId = task.body?.id;
|
|
20
|
+
if (typeof bodyId === "string" && bodyId.trim())
|
|
21
|
+
return bodyId.trim();
|
|
22
|
+
const base = task.filename?.trim() ? basenameOnly(task.filename) : "";
|
|
23
|
+
if (!base)
|
|
24
|
+
return "task";
|
|
25
|
+
return (base
|
|
26
|
+
.replace(/\.task\.ya?ml$/i, "")
|
|
27
|
+
.replace(/\.ya?ml$/i, "")
|
|
28
|
+
.trim() || "task");
|
|
29
|
+
}
|
|
30
|
+
export function normalizeTaskCatalogFilename(taskId, filename) {
|
|
31
|
+
const id = taskId.trim() || "task";
|
|
32
|
+
const base = filename?.trim() ? basenameOnly(filename) : "";
|
|
33
|
+
if (base.endsWith(TASK_SUFFIX)) {
|
|
34
|
+
const stem = base.slice(0, -TASK_SUFFIX.length);
|
|
35
|
+
if (stem === id)
|
|
36
|
+
return base;
|
|
37
|
+
}
|
|
38
|
+
return `${id}${TASK_SUFFIX}`;
|
|
39
|
+
}
|
|
40
|
+
const CATALOG_KIND_SUFFIX_PATTERN = /\.(pipeline|task|trigger)\.ya?ml$/i;
|
|
41
|
+
const BARE_YAML_PATTERN = /\.ya?ml$/i;
|
|
42
|
+
export function classifyCatalogYamlDocument(raw) {
|
|
43
|
+
if (Array.isArray(raw.stages))
|
|
44
|
+
return "pipeline";
|
|
45
|
+
if (typeof raw.goal === "string" || typeof raw.pipeline === "string") {
|
|
46
|
+
return "task";
|
|
47
|
+
}
|
|
48
|
+
return "other";
|
|
49
|
+
}
|
|
50
|
+
export function legacyBareCatalogFilenames(entityId) {
|
|
51
|
+
const id = entityId.trim();
|
|
52
|
+
if (!id)
|
|
53
|
+
return [];
|
|
54
|
+
return [`${id}.yaml`, `${id}.yml`];
|
|
55
|
+
}
|
|
56
|
+
export function isMisnamedCatalogBasename(basename) {
|
|
57
|
+
const name = basename.trim();
|
|
58
|
+
if (!BARE_YAML_PATTERN.test(name))
|
|
59
|
+
return false;
|
|
60
|
+
if (!CATALOG_KIND_SUFFIX_PATTERN.test(name))
|
|
61
|
+
return true;
|
|
62
|
+
return /\.(pipeline|task)\.yml$/i.test(name);
|
|
63
|
+
}
|
|
64
|
+
export function canonicalCatalogBasenameForDocument(raw, sourceBasename) {
|
|
65
|
+
const kind = classifyCatalogYamlDocument(raw);
|
|
66
|
+
const id = raw.id;
|
|
67
|
+
if (typeof id !== "string" || !id.trim())
|
|
68
|
+
return null;
|
|
69
|
+
if (kind === "pipeline")
|
|
70
|
+
return normalizePipelineCatalogFilename(id);
|
|
71
|
+
if (kind === "task")
|
|
72
|
+
return normalizeTaskCatalogFilename(id);
|
|
73
|
+
if (sourceBasename && CATALOG_KIND_SUFFIX_PATTERN.test(sourceBasename)) {
|
|
74
|
+
const stem = sourceBasename.replace(/\.ya?ml$/i, "");
|
|
75
|
+
const kindSuffix = stem.match(/\.(pipeline|task|trigger)$/i)?.[1]?.toLowerCase();
|
|
76
|
+
if (kindSuffix === "pipeline") {
|
|
77
|
+
return normalizePipelineCatalogFilename(id);
|
|
78
|
+
}
|
|
79
|
+
if (kindSuffix === "task") {
|
|
80
|
+
return normalizeTaskCatalogFilename(id);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
export function reconcileWorkshopCatalogFilenames(pipelineId, taskId, canonicalPipelineBasename, canonicalTaskBasename) {
|
|
86
|
+
const renameCandidates = [];
|
|
87
|
+
const removeCandidates = [];
|
|
88
|
+
const pipelineCanonical = canonicalPipelineBasename?.trim() ||
|
|
89
|
+
normalizePipelineCatalogFilename(pipelineId);
|
|
90
|
+
const taskCanonical = taskId?.trim()
|
|
91
|
+
? canonicalTaskBasename?.trim() ||
|
|
92
|
+
normalizeTaskCatalogFilename(taskId)
|
|
93
|
+
: null;
|
|
94
|
+
for (const legacy of legacyBareCatalogFilenames(pipelineId)) {
|
|
95
|
+
if (legacy === pipelineCanonical)
|
|
96
|
+
continue;
|
|
97
|
+
removeCandidates.push(legacy);
|
|
98
|
+
}
|
|
99
|
+
if (taskId?.trim()) {
|
|
100
|
+
for (const legacy of legacyBareCatalogFilenames(taskId)) {
|
|
101
|
+
if (legacy === taskCanonical || legacy === pipelineCanonical)
|
|
102
|
+
continue;
|
|
103
|
+
removeCandidates.push(legacy);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return { renameCandidates, removeCandidates };
|
|
107
|
+
}
|
|
108
|
+
export function normalizeDraftCatalogFilenames(draft, pipelineFilename) {
|
|
109
|
+
const pipelineFilenameOut = normalizePipelineCatalogFilename(draft.pipeline.id, pipelineFilename);
|
|
110
|
+
if (!draft.task) {
|
|
111
|
+
return { draft, pipelineFilename: pipelineFilenameOut };
|
|
112
|
+
}
|
|
113
|
+
const taskId = taskIdFromDraftTask(draft.task);
|
|
114
|
+
const taskFilename = normalizeTaskCatalogFilename(taskId, draft.task.filename);
|
|
115
|
+
if (taskFilename === draft.task.filename) {
|
|
116
|
+
return { draft, pipelineFilename: pipelineFilenameOut };
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
draft: {
|
|
120
|
+
...draft,
|
|
121
|
+
task: { ...draft.task, filename: taskFilename },
|
|
122
|
+
},
|
|
123
|
+
pipelineFilename: pipelineFilenameOut,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type WorkshopCatalogRepairResult } from "./workshopCatalogFilenames.js";
|
|
2
|
+
export declare function repairMisnamedWorkshopCatalogFiles(packageDirectory: string): Promise<WorkshopCatalogRepairResult>;
|
|
3
|
+
export declare function reconcileWorkshopCatalogPackageDir(packageDirectory: string, pipelineId: string, taskId?: string | null, options?: {
|
|
4
|
+
pipelineBasename?: string;
|
|
5
|
+
taskBasename?: string | null;
|
|
6
|
+
}): Promise<WorkshopCatalogRepairResult>;
|
|
7
|
+
export declare function repairWorkshopCatalogTree(projectRoot: string, relativeDirectories: string[]): Promise<WorkshopCatalogRepairResult>;
|