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.
Files changed (335) hide show
  1. package/README.md +4 -3
  2. package/dist/agent/adapterSpill.d.ts +14 -0
  3. package/dist/agent/adapterSpill.js +83 -0
  4. package/dist/agent/agentBackend.d.ts +2 -2
  5. package/dist/agent/agentBackend.js +2 -2
  6. package/dist/agent/claudeAdapter.d.ts +13 -2
  7. package/dist/agent/claudeAdapter.js +50 -6
  8. package/dist/agent/cursorProvider.d.ts +3 -0
  9. package/dist/agent/cursorProvider.js +23 -6
  10. package/dist/agent/fakeAgent.d.ts +3 -3
  11. package/dist/agent/fakeAgent.js +0 -6
  12. package/dist/agent/piAdapter.d.ts +2 -2
  13. package/dist/agent/piAdapter.js +116 -15
  14. package/dist/agent/piIsolatedMcp.d.ts +1 -0
  15. package/dist/agent/piIsolatedMcp.js +19 -1
  16. package/dist/agent/piIsolatedMcpProbe.d.ts +11 -0
  17. package/dist/agent/piIsolatedMcpProbe.js +20 -0
  18. package/dist/agent/port.d.ts +2 -2
  19. package/dist/agent/providerAuth.d.ts +6 -0
  20. package/dist/agent/providerAuth.js +86 -17
  21. package/dist/agent/{resolveAgentPort.d.ts → resolveStagePort.d.ts} +8 -6
  22. package/dist/agent/{resolveAgentPort.js → resolveStagePort.js} +13 -9
  23. package/dist/agent/stageTurnDriver.d.ts +33 -0
  24. package/dist/agent/stageTurnDriver.js +64 -0
  25. package/dist/agent/toolOutputBudget.d.ts +46 -0
  26. package/dist/agent/toolOutputBudget.js +97 -0
  27. package/dist/agent/toolOutputBudgetExtension.d.ts +22 -0
  28. package/dist/agent/toolOutputBudgetExtension.js +141 -0
  29. package/dist/agent/toolOutputSpill.d.ts +34 -0
  30. package/dist/agent/toolOutputSpill.js +85 -0
  31. package/dist/browser/agentBrowserLiveViewRelay.d.ts +41 -0
  32. package/dist/browser/agentBrowserLiveViewRelay.js +801 -0
  33. package/dist/browser/anchor.d.ts +2 -0
  34. package/dist/browser/anchor.js +2 -1
  35. package/dist/browser/auditSink.d.ts +8 -0
  36. package/dist/browser/browserHost.d.ts +29 -0
  37. package/dist/browser/browserSweep.d.ts +4 -1
  38. package/dist/browser/browserSweep.js +7 -0
  39. package/dist/browser/browserTeardown.d.ts +1 -0
  40. package/dist/browser/browserTeardown.js +32 -2
  41. package/dist/browser/containerBrowserEndpoint.d.ts +11 -0
  42. package/dist/browser/containerBrowserEndpoint.js +57 -0
  43. package/dist/browser/containerBrowserHost.d.ts +24 -0
  44. package/dist/browser/containerBrowserHost.js +147 -0
  45. package/dist/browser/dockerSandboxOrchestrator.d.ts +26 -0
  46. package/dist/browser/dockerSandboxOrchestrator.js +142 -0
  47. package/dist/browser/fakeLiveViewRelay.d.ts +15 -0
  48. package/dist/browser/fakeLiveViewRelay.js +94 -0
  49. package/dist/browser/fakeLiveViewSource.d.ts +6 -0
  50. package/dist/browser/fakeLiveViewSource.js +16 -0
  51. package/dist/browser/fakeRemoteBrowserHost.d.ts +2 -1
  52. package/dist/browser/fakeRemoteBrowserHost.js +2 -1
  53. package/dist/browser/fakeSandboxOrchestrator.d.ts +9 -0
  54. package/dist/browser/fakeSandboxOrchestrator.js +44 -0
  55. package/dist/browser/gateHandoff.d.ts +0 -2
  56. package/dist/browser/gateHandoff.js +5 -11
  57. package/dist/browser/hostCapabilities.d.ts +23 -0
  58. package/dist/browser/hostCapabilities.js +23 -0
  59. package/dist/browser/humanLogin.d.ts +2 -2
  60. package/dist/browser/humanLogin.js +11 -2
  61. package/dist/browser/liveViewPersisted.d.ts +11 -0
  62. package/dist/browser/liveViewPersisted.js +22 -0
  63. package/dist/browser/liveViewRelay.d.ts +76 -0
  64. package/dist/browser/liveViewRelay.js +1 -0
  65. package/dist/browser/liveViewSessions.d.ts +25 -0
  66. package/dist/browser/liveViewSessions.js +139 -0
  67. package/dist/browser/liveViewSource.d.ts +25 -0
  68. package/dist/browser/liveViewSource.js +7 -0
  69. package/dist/browser/liveViewTickets.d.ts +46 -0
  70. package/dist/browser/liveViewTickets.js +122 -0
  71. package/dist/browser/localBrowserHost.d.ts +10 -0
  72. package/dist/browser/localBrowserHost.js +53 -7
  73. package/dist/browser/ownerScope.d.ts +10 -0
  74. package/dist/browser/ownerScope.js +6 -0
  75. package/dist/browser/profileStore.d.ts +5 -2
  76. package/dist/browser/profileStore.js +3 -5
  77. package/dist/browser/sandboxOrchestrator.d.ts +60 -0
  78. package/dist/browser/sandboxOrchestrator.js +8 -0
  79. package/dist/browser/sessionApiSandboxOrchestrator.d.ts +34 -0
  80. package/dist/browser/sessionApiSandboxOrchestrator.js +108 -0
  81. package/dist/browser/stageBrowserEnv.d.ts +7 -0
  82. package/dist/browser/stageBrowserEnv.js +53 -15
  83. package/dist/browser/stageHandoff.d.ts +47 -0
  84. package/dist/browser/stageHandoff.js +101 -0
  85. package/dist/browser/stageProfileLock.d.ts +1 -0
  86. package/dist/browser/stageProfileLock.js +1 -3
  87. package/dist/browser/volumeProfileStore.d.ts +7 -0
  88. package/dist/browser/volumeProfileStore.js +35 -0
  89. package/dist/cli/browserCommand.d.ts +3 -1
  90. package/dist/cli/browserCommand.js +25 -20
  91. package/dist/cli/resolveCliProjectRoot.d.ts +7 -0
  92. package/dist/cli/resolveCliProjectRoot.js +27 -0
  93. package/dist/cli/runCommand.d.ts +1 -1
  94. package/dist/cli/runCommand.js +59 -17
  95. package/dist/cli/skillsCommand.d.ts +20 -0
  96. package/dist/cli/skillsCommand.js +74 -31
  97. package/dist/cli/validateCommand.d.ts +1 -1
  98. package/dist/cli/validateCommand.js +22 -4
  99. package/dist/cli.js +4 -4
  100. package/dist/config/browseCatalog.d.ts +2 -0
  101. package/dist/config/browseCatalog.js +6 -0
  102. package/dist/config/catalogRelativePath.d.ts +4 -0
  103. package/dist/config/catalogRelativePath.js +37 -9
  104. package/dist/config/createStage.d.ts +1 -0
  105. package/dist/config/createStage.js +11 -2
  106. package/dist/config/createTask.d.ts +30 -0
  107. package/dist/config/createTask.js +161 -0
  108. package/dist/config/createTrigger.d.ts +15 -0
  109. package/dist/config/createTrigger.js +32 -25
  110. package/dist/config/draftPackage.d.ts +41 -0
  111. package/dist/config/draftPackage.js +216 -23
  112. package/dist/config/ensureCatalogScanRoot.d.ts +2 -1
  113. package/dist/config/ensureCatalogScanRoot.js +6 -2
  114. package/dist/config/findTaskInCatalog.d.ts +33 -0
  115. package/dist/config/findTaskInCatalog.js +69 -0
  116. package/dist/config/hostConfig.d.ts +21 -0
  117. package/dist/config/hostConfig.js +113 -1
  118. package/dist/config/importTask.d.ts +19 -0
  119. package/dist/config/importTask.js +133 -0
  120. package/dist/config/installSkillRequest.d.ts +40 -0
  121. package/dist/config/installSkillRequest.js +286 -0
  122. package/dist/config/listConfig.d.ts +0 -16
  123. package/dist/config/listConfig.js +0 -3
  124. package/dist/config/loadOutcome.d.ts +11 -6
  125. package/dist/config/loadPipeline.js +0 -1
  126. package/dist/config/loadStage.js +2 -0
  127. package/dist/config/loadStageflowManifest.js +4 -1
  128. package/dist/config/loadTask.js +2 -0
  129. package/dist/config/loadTrigger.js +2 -0
  130. package/dist/config/mergePipelineIncludes.js +3 -0
  131. package/dist/config/normalizePipelineStageEntry.js +10 -1
  132. package/dist/config/printTargetYaml.js +0 -1
  133. package/dist/config/publishDraftPackage.d.ts +5 -1
  134. package/dist/config/publishDraftPackage.js +4 -1
  135. package/dist/config/resolveCatalogRoots.d.ts +4 -1
  136. package/dist/config/resolveCatalogRoots.js +18 -1
  137. package/dist/config/skillUsageIndex.d.ts +6 -0
  138. package/dist/config/skillUsageIndex.js +39 -0
  139. package/dist/config/stageBrowser.js +13 -0
  140. package/dist/config/triggerAdapterStatus.d.ts +11 -0
  141. package/dist/config/triggerAdapterStatus.js +90 -0
  142. package/dist/config/triggerCatalog.d.ts +41 -2
  143. package/dist/config/triggerCatalog.js +107 -15
  144. package/dist/config/updateTask.d.ts +30 -0
  145. package/dist/config/updateTask.js +119 -0
  146. package/dist/config/updateTrigger.d.ts +38 -0
  147. package/dist/config/updateTrigger.js +220 -0
  148. package/dist/config/validateCatalog.d.ts +6 -2
  149. package/dist/config/validateCatalog.js +48 -7
  150. package/dist/config/workshopCatalogFilenames.d.ts +37 -0
  151. package/dist/config/workshopCatalogFilenames.js +125 -0
  152. package/dist/config/workshopCatalogRepair.d.ts +7 -0
  153. package/dist/config/workshopCatalogRepair.js +144 -0
  154. package/dist/config/yamlParsePosition.d.ts +4 -0
  155. package/dist/config/yamlParsePosition.js +9 -0
  156. package/dist/hosted/containment.d.ts +3 -0
  157. package/dist/hosted/containment.js +36 -0
  158. package/dist/hosted/errors.d.ts +21 -0
  159. package/dist/hosted/errors.js +30 -0
  160. package/dist/hosted/packageSkills.d.ts +3 -0
  161. package/dist/hosted/packageSkills.js +80 -0
  162. package/dist/hosted/runtime.d.ts +12 -0
  163. package/dist/hosted/runtime.js +596 -0
  164. package/dist/hosted/sequencer.d.ts +28 -0
  165. package/dist/hosted/sequencer.js +129 -0
  166. package/dist/hosted/translate.d.ts +3 -0
  167. package/dist/hosted/translate.js +43 -0
  168. package/dist/hosted/types.d.ts +108 -0
  169. package/dist/hosted/types.js +10 -0
  170. package/dist/index.d.ts +9 -1
  171. package/dist/index.js +6 -0
  172. package/dist/mcp/getStartedTool.js +15 -6
  173. package/dist/mcp/triggerTools.js +2 -8
  174. package/dist/operatorAgent/agentPort.d.ts +11 -0
  175. package/dist/operatorAgent/agentPort.js +12 -0
  176. package/dist/operatorAgent/index.d.ts +1 -0
  177. package/dist/operatorAgent/index.js +1 -0
  178. package/dist/operatorAgent/piModel.js +26 -6
  179. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +1 -1
  180. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +4 -2
  181. package/dist/operatorAgent/profiles/workshopAuthorTools.js +103 -16
  182. package/dist/package-meta.d.ts +1 -1
  183. package/dist/package-meta.js +1 -1
  184. package/dist/project/ensureProjectRootsFromEnv.d.ts +3 -0
  185. package/dist/project/ensureProjectRootsFromEnv.js +22 -0
  186. package/dist/project/globalHome.d.ts +1 -0
  187. package/dist/project/globalHome.js +18 -0
  188. package/dist/project/workspaceCatalog.d.ts +10 -0
  189. package/dist/project/workspaceCatalog.js +91 -0
  190. package/dist/projection/projectRun.d.ts +0 -1
  191. package/dist/projection/projectRun.js +0 -1
  192. package/dist/runstore/backup.js +14 -4
  193. package/dist/runstore/port.d.ts +11 -1
  194. package/dist/runstore/restore.js +5 -2
  195. package/dist/runstore/sqlite/SqliteRunStore.d.ts +8 -0
  196. package/dist/runstore/sqlite/SqliteRunStore.js +29 -9
  197. package/dist/runstore/sqlite/migrations/012-trigger-fires.d.ts +7 -0
  198. package/dist/runstore/sqlite/migrations/012-trigger-fires.js +18 -0
  199. package/dist/runstore/sqlite/migrations/index.js +2 -0
  200. package/dist/runstore/stageSnapshot.d.ts +3 -1
  201. package/dist/runstore/stageSnapshot.js +19 -0
  202. package/dist/runstore/workspaceLayout.js +27 -0
  203. package/dist/runstore/writeContainedArtifact.d.ts +1 -0
  204. package/dist/runstore/writeContainedArtifact.js +52 -0
  205. package/dist/runtime/containerLimits.d.ts +0 -3
  206. package/dist/runtime/containerLimits.js +0 -5
  207. package/dist/runtime/credentialBinding.d.ts +3 -0
  208. package/dist/runtime/credentialBinding.js +23 -6
  209. package/dist/runtime/emailSource.js +20 -28
  210. package/dist/runtime/githubPollSource.js +20 -28
  211. package/dist/runtime/pipelineRunner.d.ts +4 -4
  212. package/dist/runtime/pipelineScheduler.d.ts +2 -2
  213. package/dist/runtime/pipelineScheduler.js +3 -0
  214. package/dist/runtime/resumeReconstruct.d.ts +2 -2
  215. package/dist/runtime/resumeTimedOut.d.ts +2 -2
  216. package/dist/runtime/runManager.d.ts +18 -2
  217. package/dist/runtime/runManager.js +95 -30
  218. package/dist/runtime/runRetentionSweep.d.ts +0 -2
  219. package/dist/runtime/runRetentionSweep.js +0 -8
  220. package/dist/runtime/runRetryCoordinator.d.ts +2 -2
  221. package/dist/runtime/scheduleSource.js +15 -24
  222. package/dist/runtime/stageAttemptBootstrap.d.ts +2 -2
  223. package/dist/runtime/stageAttemptBootstrap.js +10 -1
  224. package/dist/runtime/stageProcessLauncher.js +2 -2
  225. package/dist/runtime/stageRoots.js +12 -1
  226. package/dist/runtime/stageRunner.d.ts +2 -2
  227. package/dist/runtime/stageRunner.js +8 -2
  228. package/dist/runtime/stageWorker.js +2 -2
  229. package/dist/runtime/triggerPort.d.ts +5 -3
  230. package/dist/runtime/triggerRunner.d.ts +1 -0
  231. package/dist/runtime/triggerRunner.js +27 -20
  232. package/dist/runtime/webhookAccept.d.ts +25 -0
  233. package/dist/runtime/webhookAccept.js +56 -0
  234. package/dist/server/bootstrap.d.ts +7 -2
  235. package/dist/server/bootstrap.js +15 -1
  236. package/dist/server/controlToken.d.ts +1 -1
  237. package/dist/server/controlToken.js +18 -2
  238. package/dist/server/healthSurfaces.d.ts +12 -0
  239. package/dist/server/healthSurfaces.js +27 -6
  240. package/dist/server/http.d.ts +19 -10
  241. package/dist/server/http.js +605 -85
  242. package/dist/server/liveViewPath.d.ts +9 -0
  243. package/dist/server/liveViewPath.js +8 -0
  244. package/dist/server/liveViewRoutes.d.ts +58 -0
  245. package/dist/server/liveViewRoutes.js +565 -0
  246. package/dist/server/mcpHost.d.ts +4 -2
  247. package/dist/server/mcpHost.js +2 -0
  248. package/dist/server/readCatalogFile.d.ts +29 -0
  249. package/dist/server/readCatalogFile.js +215 -0
  250. package/dist/server/triggerRoutes.d.ts +1 -0
  251. package/dist/server/triggerRoutes.js +200 -73
  252. package/dist/tools/readEmail.d.ts +3 -3
  253. package/dist/tools/writeStageArtifact.d.ts +9 -0
  254. package/dist/tools/writeStageArtifact.js +2 -47
  255. package/dist/types/pipeline.d.ts +1 -1
  256. package/dist/types/stage.d.ts +1 -1
  257. package/dist/types/stageflowManifest.d.ts +1 -1
  258. package/dist/ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  259. package/dist/ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  260. package/dist/ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  261. package/dist/ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  262. package/dist/ui/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
  263. package/dist/ui/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
  264. package/dist/ui/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
  265. package/dist/ui/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  266. package/dist/ui/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
  267. package/dist/ui/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
  268. package/dist/ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  269. package/dist/ui/assets/index-BoFPkKFg.js +256 -0
  270. package/dist/ui/assets/{index-Bv4p1aPK.css → index-CIPbbBUi.css} +1 -1
  271. package/dist/ui/index.html +4 -4
  272. package/dist/workshop/autosave.d.ts +2 -2
  273. package/dist/workshop/autosave.js +4 -6
  274. package/dist/workshop/buildStore.d.ts +2 -0
  275. package/dist/workshop/chatAttachments.d.ts +27 -0
  276. package/dist/workshop/chatAttachments.js +98 -0
  277. package/dist/workshop/chatTurn.d.ts +5 -2
  278. package/dist/workshop/chatTurn.js +22 -18
  279. package/dist/workshop/sessionMutations.d.ts +17 -0
  280. package/dist/workshop/sessionMutations.js +107 -0
  281. package/dist/workshop/sessionStore.d.ts +10 -0
  282. package/dist/workshop/sessionStore.js +52 -0
  283. package/dist/workshop/workshopPin.d.ts +4 -3
  284. package/dist/workshop/workshopPin.js +26 -6
  285. package/dist/workshop/workshopTranscriptTurn.d.ts +8 -0
  286. package/dist/workshop/workshopTranscriptTurn.js +46 -0
  287. package/examples/README.md +1 -1
  288. package/examples/archify-on-pr/README.md +1 -1
  289. package/examples/archify-on-pr/archify-on-pr.pipeline.yaml +0 -1
  290. package/examples/archify-on-pr/author-diagrams.yaml +1 -0
  291. package/examples/conditional-fork/branch-a.yaml +1 -1
  292. package/examples/conditional-fork/decide.yaml +2 -1
  293. package/examples/feature-loop/{implement.yaml → feature-implement.yaml} +1 -1
  294. package/examples/feature-loop/feature-loop.pipeline.yaml +10 -10
  295. package/examples/feature-loop/{plan.yaml → feature-plan.yaml} +1 -1
  296. package/examples/feature-loop/{review.yaml → feature-review.yaml} +1 -1
  297. package/examples/feedback-loop/{implement.yaml → feedback-implement.yaml} +1 -1
  298. package/examples/feedback-loop/feedback-loop-wait-human.pipeline.yaml +9 -9
  299. package/examples/feedback-loop/feedback-loop.pipeline.yaml +9 -9
  300. package/examples/feedback-loop/{plan.yaml → feedback-plan.yaml} +1 -1
  301. package/examples/feedback-loop/{review.yaml → feedback-review.yaml} +1 -1
  302. package/examples/generic-fan-in/{research.yaml → fan-in-research.yaml} +1 -1
  303. package/examples/generic-fan-in/generic-fan-in-accepted.pipeline.yaml +3 -3
  304. package/examples/generic-fan-in/generic-fan-in.pipeline.yaml +3 -3
  305. package/examples/hello-world/{research.yaml → hello-research.yaml} +1 -1
  306. package/examples/hello-world/hello.pipeline.yaml +2 -2
  307. package/examples/mcp-hitl-tour/{review.yaml → mcp-hitl-review.yaml} +1 -1
  308. package/examples/mcp-hitl-tour/mcp-hitl-tour.pipeline.yaml +3 -3
  309. package/examples/oss-issue-contribution/oss-explain-fix.yaml +1 -0
  310. package/examples/oss-issue-contribution/oss-explain-issue.yaml +1 -0
  311. package/examples/oss-issue-contribution/oss-issue-contribution.pipeline.yaml +0 -2
  312. package/examples/stageflow.yaml +6 -0
  313. package/examples/triggers/sample-github-pr.trigger.yaml +12 -0
  314. package/examples/triggers/sample-inbox-email.trigger.yaml +14 -0
  315. package/examples/triggers/sample-manual-hello.trigger.yaml +5 -0
  316. package/examples/triggers/sample-nightly-hello.trigger.yaml +8 -0
  317. package/examples/triggers/sample-signed-webhook.trigger.yaml +11 -0
  318. package/examples/triggers-readiness/README.md +48 -0
  319. package/examples/triggers-readiness/readiness-emit.yaml +18 -0
  320. package/examples/triggers-readiness/readiness.pipeline.yaml +4 -0
  321. package/examples/triggers-readiness/readiness.task.yaml +6 -0
  322. package/examples/triggers-readiness/stageflow.yaml +13 -0
  323. package/examples/triggers-readiness/triggers/readiness-dynamic-manual.trigger.yaml +4 -0
  324. package/examples/triggers-readiness/triggers/readiness-email.trigger.yaml +12 -0
  325. package/examples/triggers-readiness/triggers/readiness-github-pr.trigger.yaml +12 -0
  326. package/examples/triggers-readiness/triggers/readiness-manual.trigger.yaml +5 -0
  327. package/examples/triggers-readiness/triggers/readiness-schedule.trigger.yaml +8 -0
  328. package/examples/triggers-readiness/triggers/readiness-webhook.trigger.yaml +11 -0
  329. package/package.json +9 -1
  330. package/skills/stageflow/references/control-surface.md +1 -1
  331. package/skills/stageflow-author/references/catalog-write-conventions.md +2 -2
  332. package/skills/stageflow-run/SKILL.md +1 -1
  333. package/skills/stageflow-run/references/mcp-call.md +1 -1
  334. package/skills/stageflow-setup/SKILL.md +1 -1
  335. 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>;