stageflow 0.33.0 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (379) hide show
  1. package/README.md +3 -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 +56 -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/piAdapter.d.ts +2 -2
  12. package/dist/agent/piAdapter.js +125 -15
  13. package/dist/agent/piIsolatedMcp.d.ts +1 -0
  14. package/dist/agent/piIsolatedMcp.js +19 -1
  15. package/dist/agent/piIsolatedMcpProbe.d.ts +11 -0
  16. package/dist/agent/piIsolatedMcpProbe.js +20 -0
  17. package/dist/agent/port.d.ts +4 -2
  18. package/dist/agent/providerAuth.js +36 -3
  19. package/dist/agent/{resolveAgentPort.d.ts → resolveStagePort.d.ts} +8 -8
  20. package/dist/agent/{resolveAgentPort.js → resolveStagePort.js} +11 -11
  21. package/dist/agent/stageTurnDriver.d.ts +33 -0
  22. package/dist/agent/stageTurnDriver.js +64 -0
  23. package/dist/agent/toolOutputBudget.d.ts +46 -0
  24. package/dist/agent/toolOutputBudget.js +97 -0
  25. package/dist/agent/toolOutputBudgetExtension.d.ts +22 -0
  26. package/dist/agent/toolOutputBudgetExtension.js +141 -0
  27. package/dist/agent/toolOutputSpill.d.ts +34 -0
  28. package/dist/agent/toolOutputSpill.js +85 -0
  29. package/dist/browser/agentBrowserLiveViewRelay.d.ts +41 -0
  30. package/dist/browser/agentBrowserLiveViewRelay.js +801 -0
  31. package/dist/browser/anchor.d.ts +2 -0
  32. package/dist/browser/anchor.js +2 -1
  33. package/dist/browser/auditSink.d.ts +8 -0
  34. package/dist/browser/browserHost.d.ts +29 -0
  35. package/dist/browser/browserSweep.d.ts +4 -1
  36. package/dist/browser/browserSweep.js +7 -0
  37. package/dist/browser/browserTeardown.d.ts +1 -0
  38. package/dist/browser/browserTeardown.js +32 -2
  39. package/dist/browser/containerBrowserEndpoint.d.ts +11 -0
  40. package/dist/browser/containerBrowserEndpoint.js +57 -0
  41. package/dist/browser/containerBrowserHost.d.ts +24 -0
  42. package/dist/browser/containerBrowserHost.js +147 -0
  43. package/dist/browser/dockerSandboxOrchestrator.d.ts +26 -0
  44. package/dist/browser/dockerSandboxOrchestrator.js +142 -0
  45. package/dist/browser/fakeLiveViewRelay.d.ts +15 -0
  46. package/dist/browser/fakeLiveViewRelay.js +94 -0
  47. package/dist/browser/fakeLiveViewSource.d.ts +6 -0
  48. package/dist/browser/fakeLiveViewSource.js +16 -0
  49. package/dist/browser/fakeRemoteBrowserHost.d.ts +2 -1
  50. package/dist/browser/fakeRemoteBrowserHost.js +2 -1
  51. package/dist/browser/fakeSandboxOrchestrator.d.ts +9 -0
  52. package/dist/browser/fakeSandboxOrchestrator.js +44 -0
  53. package/dist/browser/gateHandoff.d.ts +0 -2
  54. package/dist/browser/gateHandoff.js +5 -11
  55. package/dist/browser/hostCapabilities.d.ts +23 -0
  56. package/dist/browser/hostCapabilities.js +23 -0
  57. package/dist/browser/humanLogin.d.ts +2 -2
  58. package/dist/browser/humanLogin.js +11 -2
  59. package/dist/browser/liveViewPersisted.d.ts +11 -0
  60. package/dist/browser/liveViewPersisted.js +22 -0
  61. package/dist/browser/liveViewRelay.d.ts +76 -0
  62. package/dist/browser/liveViewRelay.js +1 -0
  63. package/dist/browser/liveViewSessions.d.ts +25 -0
  64. package/dist/browser/liveViewSessions.js +139 -0
  65. package/dist/browser/liveViewSource.d.ts +25 -0
  66. package/dist/browser/liveViewSource.js +7 -0
  67. package/dist/browser/liveViewTickets.d.ts +46 -0
  68. package/dist/browser/liveViewTickets.js +122 -0
  69. package/dist/browser/localBrowserHost.d.ts +10 -0
  70. package/dist/browser/localBrowserHost.js +53 -7
  71. package/dist/browser/ownerScope.d.ts +10 -0
  72. package/dist/browser/ownerScope.js +6 -0
  73. package/dist/browser/profileStore.d.ts +5 -2
  74. package/dist/browser/profileStore.js +3 -5
  75. package/dist/browser/sandboxOrchestrator.d.ts +60 -0
  76. package/dist/browser/sandboxOrchestrator.js +8 -0
  77. package/dist/browser/sessionApiSandboxOrchestrator.d.ts +34 -0
  78. package/dist/browser/sessionApiSandboxOrchestrator.js +108 -0
  79. package/dist/browser/stageBrowserEnv.d.ts +7 -0
  80. package/dist/browser/stageBrowserEnv.js +53 -15
  81. package/dist/browser/stageHandoff.d.ts +47 -0
  82. package/dist/browser/stageHandoff.js +101 -0
  83. package/dist/browser/stageProfileLock.d.ts +1 -0
  84. package/dist/browser/stageProfileLock.js +1 -3
  85. package/dist/browser/volumeProfileStore.d.ts +7 -0
  86. package/dist/browser/volumeProfileStore.js +35 -0
  87. package/dist/channels/config.d.ts +11 -0
  88. package/dist/channels/config.js +95 -0
  89. package/dist/channels/configWriter.d.ts +40 -0
  90. package/dist/channels/configWriter.js +148 -0
  91. package/dist/channels/connectionEdits.d.ts +34 -0
  92. package/dist/channels/connectionEdits.js +53 -0
  93. package/dist/channels/errorInfo.d.ts +18 -0
  94. package/dist/channels/errorInfo.js +71 -0
  95. package/dist/channels/errors.d.ts +12 -0
  96. package/dist/channels/errors.js +13 -0
  97. package/dist/channels/health.d.ts +41 -0
  98. package/dist/channels/health.js +99 -0
  99. package/dist/channels/host.d.ts +55 -0
  100. package/dist/channels/host.js +185 -0
  101. package/dist/channels/http.d.ts +31 -0
  102. package/dist/channels/http.js +79 -0
  103. package/dist/channels/index.d.ts +10 -0
  104. package/dist/channels/index.js +9 -0
  105. package/dist/channels/ledger.d.ts +37 -0
  106. package/dist/channels/ledger.js +95 -0
  107. package/dist/channels/providers/agentmail.d.ts +26 -0
  108. package/dist/channels/providers/agentmail.js +81 -0
  109. package/dist/channels/providers/kit.d.ts +40 -0
  110. package/dist/channels/providers/kit.js +78 -0
  111. package/dist/channels/providers/photonImessage.d.ts +45 -0
  112. package/dist/channels/providers/photonImessage.js +152 -0
  113. package/dist/channels/providers/photonWhatsapp.d.ts +49 -0
  114. package/dist/channels/providers/photonWhatsapp.js +146 -0
  115. package/dist/channels/providers/slack.d.ts +25 -0
  116. package/dist/channels/providers/slack.js +101 -0
  117. package/dist/channels/providers/telegram.d.ts +21 -0
  118. package/dist/channels/providers/telegram.js +100 -0
  119. package/dist/channels/providers.d.ts +5 -0
  120. package/dist/channels/providers.js +22 -0
  121. package/dist/channels/registry.d.ts +54 -0
  122. package/dist/channels/registry.js +186 -0
  123. package/dist/channels/secretStore.d.ts +21 -0
  124. package/dist/channels/secretStore.js +75 -0
  125. package/dist/channels/secrets.d.ts +15 -0
  126. package/dist/channels/secrets.js +50 -0
  127. package/dist/channels/service.d.ts +54 -0
  128. package/dist/channels/service.js +307 -0
  129. package/dist/channels/stageChannels.d.ts +45 -0
  130. package/dist/channels/stageChannels.js +110 -0
  131. package/dist/channels/types.d.ts +142 -0
  132. package/dist/channels/types.js +1 -0
  133. package/dist/cli/browserCommand.d.ts +3 -1
  134. package/dist/cli/browserCommand.js +25 -20
  135. package/dist/cli/doctorCommand.js +3 -1
  136. package/dist/cli/resolveCliProjectRoot.d.ts +7 -0
  137. package/dist/cli/resolveCliProjectRoot.js +27 -0
  138. package/dist/cli/runCommand.d.ts +1 -1
  139. package/dist/cli/runCommand.js +59 -17
  140. package/dist/cli/skillsCommand.d.ts +20 -0
  141. package/dist/cli/skillsCommand.js +74 -31
  142. package/dist/cli/validateCommand.d.ts +1 -1
  143. package/dist/cli/validateCommand.js +22 -4
  144. package/dist/cli.js +4 -4
  145. package/dist/config/browseCatalog.d.ts +2 -0
  146. package/dist/config/browseCatalog.js +6 -0
  147. package/dist/config/catalogRelativePath.d.ts +4 -0
  148. package/dist/config/catalogRelativePath.js +37 -9
  149. package/dist/config/createStage.d.ts +1 -0
  150. package/dist/config/createStage.js +11 -2
  151. package/dist/config/createTask.d.ts +30 -0
  152. package/dist/config/createTask.js +161 -0
  153. package/dist/config/createTrigger.d.ts +15 -0
  154. package/dist/config/createTrigger.js +32 -25
  155. package/dist/config/draftPackage.d.ts +42 -0
  156. package/dist/config/draftPackage.js +220 -23
  157. package/dist/config/ensureCatalogScanRoot.d.ts +2 -1
  158. package/dist/config/ensureCatalogScanRoot.js +6 -2
  159. package/dist/config/findTaskInCatalog.d.ts +33 -0
  160. package/dist/config/findTaskInCatalog.js +69 -0
  161. package/dist/config/hostConfig.d.ts +21 -0
  162. package/dist/config/hostConfig.js +114 -1
  163. package/dist/config/importTask.d.ts +19 -0
  164. package/dist/config/importTask.js +133 -0
  165. package/dist/config/installSkillRequest.d.ts +40 -0
  166. package/dist/config/installSkillRequest.js +286 -0
  167. package/dist/config/loadOutcome.d.ts +11 -6
  168. package/dist/config/loadPipeline.js +27 -4
  169. package/dist/config/loadStage.js +17 -0
  170. package/dist/config/loadStageflowManifest.js +4 -1
  171. package/dist/config/loadTask.js +2 -0
  172. package/dist/config/loadTrigger.js +2 -0
  173. package/dist/config/mergePipelineIncludes.d.ts +3 -0
  174. package/dist/config/mergePipelineIncludes.js +30 -0
  175. package/dist/config/normalizePipelineStageEntry.js +10 -1
  176. package/dist/config/pipelineStageKeys.js +1 -0
  177. package/dist/config/printTargetYaml.js +0 -1
  178. package/dist/config/publishDraftPackage.d.ts +5 -1
  179. package/dist/config/publishDraftPackage.js +4 -1
  180. package/dist/config/resolveCatalogRoots.d.ts +4 -1
  181. package/dist/config/resolveCatalogRoots.js +18 -1
  182. package/dist/config/skillUsageIndex.d.ts +6 -0
  183. package/dist/config/skillUsageIndex.js +39 -0
  184. package/dist/config/stageBrowser.js +13 -0
  185. package/dist/config/triggerAdapterStatus.d.ts +11 -0
  186. package/dist/config/triggerAdapterStatus.js +90 -0
  187. package/dist/config/triggerCatalog.d.ts +41 -2
  188. package/dist/config/triggerCatalog.js +107 -15
  189. package/dist/config/updateTask.d.ts +30 -0
  190. package/dist/config/updateTask.js +119 -0
  191. package/dist/config/updateTrigger.d.ts +38 -0
  192. package/dist/config/updateTrigger.js +220 -0
  193. package/dist/config/validateCatalog.d.ts +9 -2
  194. package/dist/config/validateCatalog.js +71 -7
  195. package/dist/config/workshopCatalogFilenames.d.ts +37 -0
  196. package/dist/config/workshopCatalogFilenames.js +125 -0
  197. package/dist/config/workshopCatalogRepair.d.ts +7 -0
  198. package/dist/config/workshopCatalogRepair.js +144 -0
  199. package/dist/config/yamlParsePosition.d.ts +4 -0
  200. package/dist/config/yamlParsePosition.js +9 -0
  201. package/dist/errors/codes.d.ts +1 -1
  202. package/dist/errors/codes.js +1 -0
  203. package/dist/hosted/runtime.d.ts +2 -2
  204. package/dist/hosted/types.d.ts +1 -1
  205. package/dist/hosted/types.js +1 -1
  206. package/dist/index.d.ts +3 -2
  207. package/dist/index.js +2 -1
  208. package/dist/mcp/catalogTools.js +1 -0
  209. package/dist/mcp/getStartedTool.js +15 -6
  210. package/dist/mcp/triggerTools.js +2 -8
  211. package/dist/operatorAgent/agentPort.d.ts +11 -0
  212. package/dist/operatorAgent/agentPort.js +12 -0
  213. package/dist/operatorAgent/index.d.ts +1 -0
  214. package/dist/operatorAgent/index.js +1 -0
  215. package/dist/operatorAgent/piModel.js +26 -6
  216. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +1 -1
  217. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +4 -2
  218. package/dist/operatorAgent/profiles/workshopAuthorTools.js +103 -16
  219. package/dist/package-meta.d.ts +1 -1
  220. package/dist/package-meta.js +1 -1
  221. package/dist/preflight/channelChecks.d.ts +17 -0
  222. package/dist/preflight/channelChecks.js +106 -0
  223. package/dist/preflight/pipelinePreflight.d.ts +9 -2
  224. package/dist/preflight/pipelinePreflight.js +9 -1
  225. package/dist/project/ensureProjectRootsFromEnv.d.ts +3 -0
  226. package/dist/project/ensureProjectRootsFromEnv.js +22 -0
  227. package/dist/project/globalHome.d.ts +1 -0
  228. package/dist/project/globalHome.js +18 -0
  229. package/dist/project/workspaceCatalog.d.ts +10 -0
  230. package/dist/project/workspaceCatalog.js +91 -0
  231. package/dist/runstore/backup.js +14 -4
  232. package/dist/runstore/port.d.ts +11 -1
  233. package/dist/runstore/restore.js +5 -2
  234. package/dist/runstore/sqlite/SqliteRunStore.d.ts +8 -0
  235. package/dist/runstore/sqlite/SqliteRunStore.js +29 -9
  236. package/dist/runstore/sqlite/migrations/012-trigger-fires.d.ts +7 -0
  237. package/dist/runstore/sqlite/migrations/012-trigger-fires.js +18 -0
  238. package/dist/runstore/sqlite/migrations/index.js +2 -0
  239. package/dist/runstore/stageSnapshot.d.ts +3 -1
  240. package/dist/runstore/stageSnapshot.js +19 -0
  241. package/dist/runstore/workspaceLayout.js +27 -0
  242. package/dist/runstore/writeContainedArtifact.d.ts +1 -0
  243. package/dist/runstore/writeContainedArtifact.js +52 -0
  244. package/dist/runtime/credentialBinding.js +14 -6
  245. package/dist/runtime/emailSource.js +20 -28
  246. package/dist/runtime/githubPollSource.js +20 -28
  247. package/dist/runtime/pipelineRunner.d.ts +4 -4
  248. package/dist/runtime/pipelineRunner.js +3 -1
  249. package/dist/runtime/pipelineScheduler.d.ts +2 -2
  250. package/dist/runtime/pipelineScheduler.js +3 -0
  251. package/dist/runtime/repositoryMaterialize.d.ts +1 -1
  252. package/dist/runtime/resumeReconstruct.d.ts +2 -2
  253. package/dist/runtime/resumeTimedOut.d.ts +2 -2
  254. package/dist/runtime/runManager.d.ts +18 -2
  255. package/dist/runtime/runManager.js +99 -31
  256. package/dist/runtime/runRetryCoordinator.d.ts +2 -2
  257. package/dist/runtime/scheduleSource.js +15 -24
  258. package/dist/runtime/stageAttemptBootstrap.d.ts +2 -2
  259. package/dist/runtime/stageAttemptBootstrap.js +26 -1
  260. package/dist/runtime/stageProcessLauncher.js +33 -3
  261. package/dist/runtime/stageRoots.js +8 -1
  262. package/dist/runtime/stageRunner.d.ts +2 -2
  263. package/dist/runtime/stageRunner.js +8 -2
  264. package/dist/runtime/stageWorker.js +2 -2
  265. package/dist/runtime/triggerPort.d.ts +5 -3
  266. package/dist/runtime/triggerRunner.d.ts +1 -0
  267. package/dist/runtime/triggerRunner.js +27 -20
  268. package/dist/runtime/webhookAccept.d.ts +25 -0
  269. package/dist/runtime/webhookAccept.js +56 -0
  270. package/dist/server/bootstrap.d.ts +10 -2
  271. package/dist/server/bootstrap.js +19 -1
  272. package/dist/server/channelRoutes.d.ts +9 -0
  273. package/dist/server/channelRoutes.js +88 -0
  274. package/dist/server/controlToken.d.ts +1 -1
  275. package/dist/server/controlToken.js +18 -2
  276. package/dist/server/healthSurfaces.d.ts +12 -0
  277. package/dist/server/healthSurfaces.js +27 -6
  278. package/dist/server/http.d.ts +19 -10
  279. package/dist/server/http.js +616 -85
  280. package/dist/server/liveViewPath.d.ts +9 -0
  281. package/dist/server/liveViewPath.js +8 -0
  282. package/dist/server/liveViewRoutes.d.ts +58 -0
  283. package/dist/server/liveViewRoutes.js +565 -0
  284. package/dist/server/mcpHost.d.ts +4 -2
  285. package/dist/server/mcpHost.js +2 -0
  286. package/dist/server/readCatalogFile.d.ts +29 -0
  287. package/dist/server/readCatalogFile.js +215 -0
  288. package/dist/server/triggerRoutes.d.ts +1 -0
  289. package/dist/server/triggerRoutes.js +200 -73
  290. package/dist/tools/sendMessage.d.ts +21 -0
  291. package/dist/tools/sendMessage.js +25 -0
  292. package/dist/tools/writeStageArtifact.d.ts +9 -0
  293. package/dist/tools/writeStageArtifact.js +2 -47
  294. package/dist/types/pipeline.d.ts +5 -2
  295. package/dist/types/stage.d.ts +14 -1
  296. package/dist/types/stageflowManifest.d.ts +1 -1
  297. package/dist/ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  298. package/dist/ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  299. package/dist/ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  300. package/dist/ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  301. package/dist/ui/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
  302. package/dist/ui/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
  303. package/dist/ui/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
  304. package/dist/ui/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  305. package/dist/ui/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
  306. package/dist/ui/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
  307. package/dist/ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  308. package/dist/ui/assets/index-BOhqnPNg.js +260 -0
  309. package/dist/ui/assets/{index-Bv4p1aPK.css → index-Clv4sJpU.css} +1 -1
  310. package/dist/ui/index.html +4 -4
  311. package/dist/workshop/autosave.d.ts +2 -2
  312. package/dist/workshop/autosave.js +4 -6
  313. package/dist/workshop/buildStore.d.ts +2 -0
  314. package/dist/workshop/chatAttachments.d.ts +27 -0
  315. package/dist/workshop/chatAttachments.js +98 -0
  316. package/dist/workshop/chatTurn.d.ts +5 -2
  317. package/dist/workshop/chatTurn.js +22 -18
  318. package/dist/workshop/sessionMutations.d.ts +17 -0
  319. package/dist/workshop/sessionMutations.js +107 -0
  320. package/dist/workshop/sessionStore.d.ts +10 -0
  321. package/dist/workshop/sessionStore.js +52 -0
  322. package/dist/workshop/workshopPin.d.ts +4 -3
  323. package/dist/workshop/workshopPin.js +26 -6
  324. package/dist/workshop/workshopTranscriptTurn.d.ts +8 -0
  325. package/dist/workshop/workshopTranscriptTurn.js +46 -0
  326. package/examples/README.md +1 -1
  327. package/examples/archify-on-pr/README.md +1 -1
  328. package/examples/archify-on-pr/archify-on-pr.pipeline.yaml +0 -1
  329. package/examples/archify-on-pr/author-diagrams.yaml +1 -0
  330. package/examples/channels-smoke/README.md +12 -0
  331. package/examples/channels-smoke/channels-smoke.pipeline.yaml +11 -0
  332. package/examples/channels-smoke/channels-smoke.task.yaml +3 -0
  333. package/examples/channels-smoke/send-email.yaml +32 -0
  334. package/examples/channels-smoke/send-text.yaml +31 -0
  335. package/examples/conditional-fork/branch-a.yaml +1 -1
  336. package/examples/conditional-fork/decide.yaml +2 -1
  337. package/examples/feature-loop/{implement.yaml → feature-implement.yaml} +1 -1
  338. package/examples/feature-loop/feature-loop.pipeline.yaml +10 -10
  339. package/examples/feature-loop/{plan.yaml → feature-plan.yaml} +1 -1
  340. package/examples/feature-loop/{review.yaml → feature-review.yaml} +1 -1
  341. package/examples/feedback-loop/{implement.yaml → feedback-implement.yaml} +1 -1
  342. package/examples/feedback-loop/feedback-loop-wait-human.pipeline.yaml +9 -9
  343. package/examples/feedback-loop/feedback-loop.pipeline.yaml +9 -9
  344. package/examples/feedback-loop/{plan.yaml → feedback-plan.yaml} +1 -1
  345. package/examples/feedback-loop/{review.yaml → feedback-review.yaml} +1 -1
  346. package/examples/generic-fan-in/{research.yaml → fan-in-research.yaml} +1 -1
  347. package/examples/generic-fan-in/generic-fan-in-accepted.pipeline.yaml +3 -3
  348. package/examples/generic-fan-in/generic-fan-in.pipeline.yaml +3 -3
  349. package/examples/hello-world/{research.yaml → hello-research.yaml} +1 -1
  350. package/examples/hello-world/hello.pipeline.yaml +2 -2
  351. package/examples/mcp-hitl-tour/{review.yaml → mcp-hitl-review.yaml} +1 -1
  352. package/examples/mcp-hitl-tour/mcp-hitl-tour.pipeline.yaml +3 -3
  353. package/examples/oss-issue-contribution/oss-explain-fix.yaml +1 -0
  354. package/examples/oss-issue-contribution/oss-explain-issue.yaml +1 -0
  355. package/examples/oss-issue-contribution/oss-issue-contribution.pipeline.yaml +0 -2
  356. package/examples/stageflow.yaml +6 -0
  357. package/examples/triggers/sample-github-pr.trigger.yaml +12 -0
  358. package/examples/triggers/sample-inbox-email.trigger.yaml +14 -0
  359. package/examples/triggers/sample-manual-hello.trigger.yaml +5 -0
  360. package/examples/triggers/sample-nightly-hello.trigger.yaml +8 -0
  361. package/examples/triggers/sample-signed-webhook.trigger.yaml +11 -0
  362. package/examples/triggers-readiness/README.md +48 -0
  363. package/examples/triggers-readiness/readiness-emit.yaml +18 -0
  364. package/examples/triggers-readiness/readiness.pipeline.yaml +4 -0
  365. package/examples/triggers-readiness/readiness.task.yaml +6 -0
  366. package/examples/triggers-readiness/stageflow.yaml +13 -0
  367. package/examples/triggers-readiness/triggers/readiness-dynamic-manual.trigger.yaml +4 -0
  368. package/examples/triggers-readiness/triggers/readiness-email.trigger.yaml +12 -0
  369. package/examples/triggers-readiness/triggers/readiness-github-pr.trigger.yaml +12 -0
  370. package/examples/triggers-readiness/triggers/readiness-manual.trigger.yaml +5 -0
  371. package/examples/triggers-readiness/triggers/readiness-schedule.trigger.yaml +8 -0
  372. package/examples/triggers-readiness/triggers/readiness-webhook.trigger.yaml +11 -0
  373. package/package.json +8 -3
  374. package/skills/stageflow/references/control-surface.md +1 -1
  375. package/skills/stageflow-author/references/catalog-write-conventions.md +2 -2
  376. package/skills/stageflow-run/SKILL.md +1 -1
  377. package/skills/stageflow-run/references/mcp-call.md +1 -1
  378. package/skills/stageflow-setup/SKILL.md +1 -1
  379. package/dist/ui/assets/index-59cjF0OA.js +0 -133
@@ -6,13 +6,13 @@ import { createInterface } from "node:readline";
6
6
  import { localAuditLogPath } from "../browser/auditSink.js";
7
7
  import { closeBrowserSession, defaultBrowserRunner } from "../browser/browserTeardown.js";
8
8
  import { defaultDisplayProbe, noScreenError } from "../browser/humanLogin.js";
9
+ import { hostLaunchOptions } from "../browser/stageBrowserEnv.js";
9
10
  import { createLocalBrowserHost } from "../browser/localBrowserHost.js";
10
11
  import { createLocalProfileLock } from "../browser/localProfileLock.js";
11
12
  import { createLocalProfileStore } from "../browser/localProfileStore.js";
12
13
  import { matchLoginState, runLoginCheck } from "../browser/loginCheck.js";
13
- // TODO(multi-tenant): every `sf browser` command below uses the fixed local scope.
14
- // In a hosted service the scope must come from the signed-in user. Search for LOCAL_BROWSER_SCOPE in this file.
15
- import { InvalidProfileKeyError, LOCAL_BROWSER_SCOPE, validateProfileName, } from "../browser/profileStore.js";
14
+ import { localOwnerScope } from "../browser/ownerScope.js";
15
+ import { InvalidProfileKeyError, validateProfileName, } from "../browser/profileStore.js";
16
16
  import { cliRunLive, createRunLiveness } from "../browser/runLiveness.js";
17
17
  import { loadHostConfig } from "../config/hostConfig.js";
18
18
  import { BlockedSiteError, hostOf, isBlockedHost } from "../browser/sitePolicy.js";
@@ -41,6 +41,9 @@ const DEFAULT_LOGIN_TIMEOUT_SEC = 300;
41
41
  const LOGIN_POLL_MS = 1000;
42
42
  const LOGIN_CLOSED_AFTER_FAILED_READS = 3;
43
43
  const STAGE_ID = "browser-cli";
44
+ function scopeOf(deps) {
45
+ return deps.scope ?? localOwnerScope();
46
+ }
44
47
  class CliError extends Error {
45
48
  code;
46
49
  exit;
@@ -88,7 +91,7 @@ function defaultSessionOpen(env) {
88
91
  return false;
89
92
  }
90
93
  }
91
- async function defaultAuditLastUsed() {
94
+ async function defaultAuditLastUsed(scope) {
92
95
  const out = {};
93
96
  let text;
94
97
  try {
@@ -103,7 +106,7 @@ async function defaultAuditLastUsed() {
103
106
  try {
104
107
  const rec = JSON.parse(line);
105
108
  if (rec.event === "profile_used" &&
106
- rec.scope === LOCAL_BROWSER_SCOPE &&
109
+ rec.scope === scope &&
107
110
  typeof rec.profile === "string" &&
108
111
  typeof rec.at === "string" &&
109
112
  (out[rec.profile] === undefined || rec.at > out[rec.profile])) {
@@ -132,7 +135,7 @@ function defaultDeps() {
132
135
  log: (line) => console.log(line),
133
136
  error: (line) => console.error(line),
134
137
  store: createLocalProfileStore(),
135
- host: createLocalBrowserHost(),
138
+ host: createLocalBrowserHost(hostLaunchOptions()),
136
139
  locks: createLocalProfileLock({ isRunLive }),
137
140
  runner: defaultBrowserRunner,
138
141
  display: () => defaultDisplayProbe(),
@@ -201,17 +204,17 @@ function newOwner() {
201
204
  return { runId: `cli-${process.pid}-${randomBytes(4).toString("hex")}`, stageId: STAGE_ID };
202
205
  }
203
206
  async function withProfile(deps, name, fn, options) {
204
- const names = await deps.store.list(LOCAL_BROWSER_SCOPE);
207
+ const names = await deps.store.list(scopeOf(deps));
205
208
  if (!options.create && !names.includes(name)) {
206
209
  throw new CliError(`Profile "${name}" does not exist. Create it with sf browser login.`, "not_found", BROWSER_EXIT.error);
207
210
  }
208
211
  const owner = newOwner();
209
- const got = await deps.locks.acquire({ scope: LOCAL_BROWSER_SCOPE, name }, owner);
212
+ const got = await deps.locks.acquire({ scope: scopeOf(deps), name }, owner);
210
213
  if (got.status === "queued") {
211
214
  throw new CliError(`Profile "${name}" is in use by run ${got.holder.runId}${got.holder.stageId !== undefined ? ` stage ${got.holder.stageId}` : ""}. Try again when it finishes.`, "profile_busy", BROWSER_EXIT.busy);
212
215
  }
213
216
  try {
214
- const handle = await deps.store.open({ scope: LOCAL_BROWSER_SCOPE, name });
217
+ const handle = await deps.store.open({ scope: scopeOf(deps), name });
215
218
  return await fn(handle);
216
219
  }
217
220
  finally {
@@ -240,16 +243,18 @@ function closeOpts(deps) {
240
243
  };
241
244
  }
242
245
  async function profilesCmd(deps, p) {
243
- const names = await deps.store.list(LOCAL_BROWSER_SCOPE);
244
- const audited = await deps.auditLastUsed().catch(() => ({}));
246
+ const names = await deps.store.list(scopeOf(deps));
247
+ const audited = await deps.auditLastUsed(scopeOf(deps)).catch(() => ({}));
245
248
  const profiles = [];
246
249
  for (const name of names) {
247
250
  let last = audited[name];
248
251
  try {
249
- const handle = await deps.store.open({ scope: LOCAL_BROWSER_SCOPE, name });
250
- const iso = (await stat(handle.profileDir)).mtime.toISOString();
251
- if (last === undefined || iso > last)
252
- last = iso;
252
+ const handle = await deps.store.open({ scope: scopeOf(deps), name });
253
+ if (handle.profileDir !== undefined) {
254
+ const iso = (await stat(handle.profileDir)).mtime.toISOString();
255
+ if (last === undefined || iso > last)
256
+ last = iso;
257
+ }
253
258
  }
254
259
  catch {
255
260
  // store without a real folder
@@ -263,9 +268,9 @@ async function profilesCmd(deps, p) {
263
268
  }
264
269
  async function statusCmd(deps, p) {
265
270
  const name = requireName(p);
266
- const exists = (await deps.store.list(LOCAL_BROWSER_SCOPE)).includes(name);
271
+ const exists = (await deps.store.list(scopeOf(deps))).includes(name);
267
272
  let lock = null;
268
- const holder = await deps.locks.holder({ scope: LOCAL_BROWSER_SCOPE, name });
273
+ const holder = await deps.locks.holder({ scope: scopeOf(deps), name });
269
274
  if (holder !== undefined) {
270
275
  lock = {
271
276
  run_id: holder.runId,
@@ -275,7 +280,7 @@ async function statusCmd(deps, p) {
275
280
  }
276
281
  let sessionOpen = false;
277
282
  if (exists) {
278
- const handle = await deps.store.open({ scope: LOCAL_BROWSER_SCOPE, name });
283
+ const handle = await deps.store.open({ scope: scopeOf(deps), name });
279
284
  sessionOpen = deps.sessionOpen(await envFor(deps, handle, { profile: name }));
280
285
  }
281
286
  const doc = { name, exists, locked: lock !== null, lock, session_open: sessionOpen };
@@ -403,7 +408,7 @@ async function loginCmd(deps, p) {
403
408
  }
404
409
  async function clearCmd(deps, p) {
405
410
  const name = requireName(p);
406
- if (!(await deps.store.list(LOCAL_BROWSER_SCOPE)).includes(name)) {
411
+ if (!(await deps.store.list(scopeOf(deps))).includes(name)) {
407
412
  throw new CliError(`Profile "${name}" does not exist.`, "not_found", BROWSER_EXIT.error);
408
413
  }
409
414
  if (!p.yes) {
@@ -418,7 +423,7 @@ async function clearCmd(deps, p) {
418
423
  return withProfile(deps, name, async (handle) => {
419
424
  const env = await envFor(deps, handle, { profile: name });
420
425
  await closeBrowserSession(env, closeOpts(deps)).catch(() => undefined);
421
- await deps.store.delete({ scope: LOCAL_BROWSER_SCOPE, name });
426
+ await deps.store.delete({ scope: scopeOf(deps), name });
422
427
  emit(deps, p.json, { cleared: true, name }, [`Cleared profile "${name}".`]);
423
428
  return BROWSER_EXIT.ok;
424
429
  }, { create: false });
@@ -293,7 +293,9 @@ function preflightToDoctorChecks(preflight, strict) {
293
293
  ? `toolchain:${c.tool ?? i}`
294
294
  : c.kind === "secret"
295
295
  ? `secret:${c.secret ?? i}`
296
- : `mcp:${c.server ?? c.stageId ?? i}`;
296
+ : c.kind === "channel"
297
+ ? `channel:${c.connection ?? i}`
298
+ : `mcp:${c.server ?? c.stageId ?? i}`;
297
299
  let status = "pass";
298
300
  if (c.status === "ok") {
299
301
  status = "pass";
@@ -0,0 +1,7 @@
1
+ import type { RunStore } from "../runstore/port.js";
2
+ export type ResolvedCliProjectRoot = {
3
+ rootPath: string;
4
+ wireId: string;
5
+ };
6
+ export declare function resolveCliProjectRootLocal(flag: string): ResolvedCliProjectRoot | null;
7
+ export declare function resolveCliProjectRoot(flag: string, store: RunStore): Promise<ResolvedCliProjectRoot>;
@@ -0,0 +1,27 @@
1
+ import path from "node:path";
2
+ import { findCatalogRoot, resolveCatalogRoots } from "../config/resolveCatalogRoots.js";
3
+ import { WORKSPACE_CATALOG_ID, workspaceCatalogPath, } from "../project/workspaceCatalog.js";
4
+ export function resolveCliProjectRootLocal(flag) {
5
+ const trimmed = flag.trim();
6
+ if (trimmed.length === 0)
7
+ return null;
8
+ if (trimmed === WORKSPACE_CATALOG_ID) {
9
+ return { rootPath: workspaceCatalogPath(), wireId: WORKSPACE_CATALOG_ID };
10
+ }
11
+ if (path.isAbsolute(trimmed)) {
12
+ const resolved = path.resolve(trimmed);
13
+ return { rootPath: resolved, wireId: resolved };
14
+ }
15
+ return null;
16
+ }
17
+ export async function resolveCliProjectRoot(flag, store) {
18
+ const local = resolveCliProjectRootLocal(flag);
19
+ if (local)
20
+ return local;
21
+ const roots = await resolveCatalogRoots({ store });
22
+ const found = findCatalogRoot(roots, flag.trim());
23
+ if (found === undefined) {
24
+ throw new Error(`Unknown project_root: ${flag.trim()}`);
25
+ }
26
+ return { rootPath: found.path, wireId: found.project_root };
27
+ }
@@ -3,7 +3,7 @@ import type { StartRunResult } from "../runtime/runManager.js";
3
3
  import type { TaskFile } from "../types/task.js";
4
4
  import { type EnsureGlobalServiceResult } from "../server/ensureGlobalService.js";
5
5
  import { type CliRunReportIo } from "./runOutput.js";
6
- export declare const RUN_USAGE = "Usage:\n sf run --task <path> --pipeline <path> [--checkout <path>] [--repository <owner/repo>] [--ref <ref>] [--json] [--include stages] [--skip-gates] [--git-sha <sha>] [--ci-pr-url <url>] [--ci-job-url <url>] [--operator-cwd <path>] [--operator-agent-dir <path>]";
6
+ export declare const RUN_USAGE = "Usage:\n sf run --task <path> --pipeline <path> [--project-root <id-or-path>] [--checkout <path>] [--repository <owner/repo>] [--ref <ref>] [--json] [--include stages] [--skip-gates] [--git-sha <sha>] [--ci-pr-url <url>] [--ci-job-url <url>] [--operator-cwd <path>] [--operator-agent-dir <path>]";
7
7
  export type RunCommandIo = CliRunReportIo;
8
8
  export type StartRunFn = (input: {
9
9
  task: string | TaskFile;
@@ -2,7 +2,8 @@ import { readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { parse as parseYaml } from "yaml";
4
4
  import { coerceTaskFile } from "../config/loadTask.js";
5
- import { relativizeLocalPathForNetwork } from "../config/catalogRelativePath.js";
5
+ import { isInsideProjectRoot, relativizeLocalPathForNetwork, } from "../config/catalogRelativePath.js";
6
+ import { resolveCliProjectRootLocal } from "./resolveCliProjectRoot.js";
6
7
  import { PipelinePreflightError, PipelineValidationError } from "../runtime/pipelineRunner.js";
7
8
  import { ensureGlobalService, hostBaseUrl, } from "../server/ensureGlobalService.js";
8
9
  import { httpEnsureProject, httpStartRun, httpStoreReader } from "./hostClient.js";
@@ -10,7 +11,7 @@ import { reportCliRun, writeQueuedAdmissionLine, } from "./runOutput.js";
10
11
  import { resolveCiIdentity } from "./ciIdentity.js";
11
12
  import { exitCodeForValidation, formatValidationHuman, formatValidationJson, } from "./validateOutput.js";
12
13
  export const RUN_USAGE = `Usage:
13
- sf run --task <path> --pipeline <path> [--checkout <path>] [--repository <owner/repo>] [--ref <ref>] [--json] [--include stages] [--skip-gates] [--git-sha <sha>] [--ci-pr-url <url>] [--ci-job-url <url>] [--operator-cwd <path>] [--operator-agent-dir <path>]`;
14
+ sf run --task <path> --pipeline <path> [--project-root <id-or-path>] [--checkout <path>] [--repository <owner/repo>] [--ref <ref>] [--json] [--include stages] [--skip-gates] [--git-sha <sha>] [--ci-pr-url <url>] [--ci-job-url <url>] [--operator-cwd <path>] [--operator-agent-dir <path>]`;
14
15
  const defaultIo = {
15
16
  log: (line) => console.log(line),
16
17
  error: (line) => console.error(line),
@@ -32,6 +33,7 @@ function parseRunArgs(args) {
32
33
  let ciJobUrl;
33
34
  let operatorCwd;
34
35
  let operatorAgentDir;
36
+ let projectRoot;
35
37
  let json = false;
36
38
  let includeStages = false;
37
39
  let skipGates = false;
@@ -111,6 +113,13 @@ function parseRunArgs(args) {
111
113
  }
112
114
  operatorAgentDir = value;
113
115
  }
116
+ else if (arg === "--project-root") {
117
+ const value = args[++i];
118
+ if (value === undefined || value.length === 0) {
119
+ throw new Error("Missing value for --project-root");
120
+ }
121
+ projectRoot = value;
122
+ }
114
123
  else if (arg === "--json") {
115
124
  json = true;
116
125
  }
@@ -151,8 +160,16 @@ function parseRunArgs(args) {
151
160
  ciJobUrl,
152
161
  operatorCwd,
153
162
  operatorAgentDir,
163
+ projectRoot,
154
164
  };
155
165
  }
166
+ function resolvePathUnderCliRoot(rootPath, inputPath, label) {
167
+ const abs = path.resolve(rootPath, inputPath);
168
+ if (!isInsideProjectRoot(rootPath, abs)) {
169
+ throw new Error(`${label} ${abs} is outside project root ${rootPath}`);
170
+ }
171
+ return path.relative(rootPath, abs).replace(/\\/g, "/") || ".";
172
+ }
156
173
  async function loadTaskWithBindingOverrides(taskPath, cwd, overrides) {
157
174
  const absolute = path.resolve(cwd, taskPath);
158
175
  const yamlText = await readFile(absolute, "utf8");
@@ -167,7 +184,7 @@ async function loadTaskWithBindingOverrides(taskPath, cwd, overrides) {
167
184
  task.ref = overrides.ref;
168
185
  return task;
169
186
  }
170
- function defaultStartRun(cwd, base, ensureService) {
187
+ function defaultStartRun(cwd, base, ensureService, cliProjectRoot) {
171
188
  return async (input) => {
172
189
  const ensured = await ensureService();
173
190
  if (!ensured.ok) {
@@ -180,20 +197,34 @@ function defaultStartRun(cwd, base, ensureService) {
180
197
  : {}),
181
198
  };
182
199
  }
183
- const pipelineRef = relativizeLocalPathForNetwork(cwd, input.pipeline);
200
+ let pipelineRef;
184
201
  let task = input.task;
185
- let projectRoot = pipelineRef.project_root;
186
- if (typeof input.task === "string") {
187
- const absTask = path.resolve(cwd, input.task);
188
- const cwdAbs = path.resolve(cwd);
189
- const relTask = path.relative(cwdAbs, absTask);
190
- if (relTask.startsWith("..") || path.isAbsolute(relTask)) {
191
- task = await loadTaskWithBindingOverrides(input.task, cwd, {});
202
+ let projectRoot;
203
+ if (cliProjectRoot) {
204
+ pipelineRef = {
205
+ path: resolvePathUnderCliRoot(cliProjectRoot.rootPath, input.pipeline, "Pipeline"),
206
+ project_root: cliProjectRoot.wireId,
207
+ };
208
+ projectRoot = cliProjectRoot.wireId;
209
+ if (typeof input.task === "string") {
210
+ task = resolvePathUnderCliRoot(cliProjectRoot.rootPath, input.task, "Task");
192
211
  }
193
- else {
194
- const taskRef = relativizeLocalPathForNetwork(cwd, input.task);
195
- task = taskRef.path;
196
- projectRoot = taskRef.project_root;
212
+ }
213
+ else {
214
+ pipelineRef = relativizeLocalPathForNetwork(cwd, input.pipeline);
215
+ projectRoot = pipelineRef.project_root;
216
+ if (typeof input.task === "string") {
217
+ const absTask = path.resolve(cwd, input.task);
218
+ const cwdAbs = path.resolve(cwd);
219
+ const relTask = path.relative(cwdAbs, absTask);
220
+ if (relTask.startsWith("..") || path.isAbsolute(relTask)) {
221
+ task = await loadTaskWithBindingOverrides(input.task, cwd, {});
222
+ }
223
+ else {
224
+ const taskRef = relativizeLocalPathForNetwork(cwd, input.task);
225
+ task = taskRef.path;
226
+ projectRoot = taskRef.project_root;
227
+ }
197
228
  }
198
229
  }
199
230
  const ensuredProject = await httpEnsureProject(base, projectRoot);
@@ -253,8 +284,18 @@ export async function runRunCommand(args, options = {}) {
253
284
  if (parsed.operatorCwd !== undefined || parsed.operatorAgentDir !== undefined) {
254
285
  out.error("warning: --operator-cwd/--operator-agent-dir have no effect on `sf run` now that pipelines execute in the shared global Stageflow service; set STAGEFLOW_OPERATOR_CWD/STAGEFLOW_OPERATOR_AGENT_DIR before that service first starts instead.");
255
286
  }
287
+ let cliProjectRoot;
288
+ if (parsed.projectRoot !== undefined) {
289
+ const local = resolveCliProjectRootLocal(parsed.projectRoot);
290
+ if (!local) {
291
+ out.error(`Unknown project_root: ${parsed.projectRoot.trim()}`);
292
+ out.error(RUN_USAGE);
293
+ return 1;
294
+ }
295
+ cliProjectRoot = local;
296
+ }
256
297
  const startRun = options.startRun ??
257
- defaultStartRun(cwd, base, options.ensureService ?? (() => ensureGlobalService()));
298
+ defaultStartRun(cwd, base, options.ensureService ?? (() => ensureGlobalService()), cliProjectRoot);
258
299
  const identity = resolveCiIdentity({
259
300
  flags: {
260
301
  gitSha: parsed.gitSha,
@@ -266,8 +307,9 @@ export async function runRunCommand(args, options = {}) {
266
307
  const store = options.store ?? (parsed.includeStages ? httpStoreReader(base) : undefined);
267
308
  try {
268
309
  const hasBindingOverride = parsed.repository !== undefined || parsed.ref !== undefined;
310
+ const taskLoadRoot = cliProjectRoot?.rootPath ?? cwd;
269
311
  const taskInput = hasBindingOverride
270
- ? await loadTaskWithBindingOverrides(parsed.task, cwd, {
312
+ ? await loadTaskWithBindingOverrides(parsed.task, taskLoadRoot, {
271
313
  repository: parsed.repository,
272
314
  ref: parsed.ref,
273
315
  })
@@ -5,6 +5,26 @@ export type SkillsCommandIo = {
5
5
  fetch: typeof globalThis.fetch;
6
6
  runDoctor: (scriptPath: string, cwd: string) => number;
7
7
  };
8
+ export type InstallProjectSkillInput = {
9
+ projectRoot: string;
10
+ fromPath?: string;
11
+ fromZip?: string;
12
+ zipBytes?: Uint8Array;
13
+ skillName?: string;
14
+ checksum?: string;
15
+ io?: Partial<SkillsCommandIo>;
16
+ };
17
+ export type InstallProjectSkillResult = {
18
+ ok: true;
19
+ skillName: string;
20
+ destPath: string;
21
+ message: string;
22
+ } | {
23
+ ok: false;
24
+ error: string;
25
+ doctorFailed?: boolean;
26
+ };
27
+ export declare function installProjectSkill(input: InstallProjectSkillInput): Promise<InstallProjectSkillResult>;
8
28
  export declare function runSkillsCommand(args: string[], options?: {
9
29
  cwd?: string;
10
30
  projectRoot?: string;
@@ -291,14 +291,18 @@ async function findSkillRootBySkillMd(dir) {
291
291
  }
292
292
  return undefined;
293
293
  }
294
- async function resolveInstallSource(parsed, io) {
295
- const hasPath = parsed.fromPath !== undefined;
296
- const hasZip = parsed.fromZip !== undefined;
297
- if (hasPath === hasZip) {
298
- throw new Error("specify exactly one of --from-path or --from-zip");
299
- }
300
- if (parsed.fromPath !== undefined) {
301
- const sourceRoot = path.resolve(parsed.fromPath);
294
+ async function resolveInstallSource(input, io) {
295
+ const hasPath = input.fromPath !== undefined;
296
+ const hasZip = input.fromZip !== undefined;
297
+ const hasZipBytes = input.zipBytes !== undefined;
298
+ const sourceCount = Number(hasPath) + Number(hasZip) + Number(hasZipBytes);
299
+ if (sourceCount !== 1) {
300
+ throw new Error(hasZipBytes
301
+ ? "specify exactly one of fromPath, fromZip, or zipBytes"
302
+ : "specify exactly one of --from-path or --from-zip");
303
+ }
304
+ if (input.fromPath !== undefined) {
305
+ const sourceRoot = path.resolve(input.fromPath);
302
306
  try {
303
307
  const sourceStat = await stat(sourceRoot);
304
308
  if (!sourceStat.isDirectory()) {
@@ -308,13 +312,21 @@ async function resolveInstallSource(parsed, io) {
308
312
  catch {
309
313
  throw new Error("source path not found");
310
314
  }
311
- const skillName = parsed.skillName ?? inferSkillNameFromPath(parsed.fromPath);
315
+ const skillName = input.skillName ?? inferSkillNameFromPath(input.fromPath);
312
316
  return { sourceRoot, skillName };
313
317
  }
314
- const zipRef = parsed.fromZip;
315
- const zipBytes = await resolveZipBytes(zipRef, io);
316
- if (parsed.checksum !== undefined) {
317
- const expected = parseChecksum(parsed.checksum);
318
+ let zipBytes;
319
+ let inferredZipName;
320
+ if (input.zipBytes !== undefined) {
321
+ zipBytes = Buffer.from(input.zipBytes);
322
+ }
323
+ else {
324
+ const zipRef = input.fromZip;
325
+ zipBytes = await resolveZipBytes(zipRef, io);
326
+ inferredZipName = inferSkillNameFromZipRef(zipRef);
327
+ }
328
+ if (input.checksum !== undefined) {
329
+ const expected = parseChecksum(input.checksum);
318
330
  const actual = sha256Hex(zipBytes);
319
331
  if (actual !== expected) {
320
332
  throw new Error("checksum mismatch");
@@ -322,8 +334,9 @@ async function resolveInstallSource(parsed, io) {
322
334
  }
323
335
  const extractDir = await mkdtemp(path.join(tmpdir(), "sf-skill-zip-"));
324
336
  await extractZipBuffer(zipBytes, extractDir);
325
- const skillName = parsed.skillName ?? inferSkillNameFromZipRef(zipRef);
326
- const sourceRoot = await findSkillRoot(extractDir, skillName);
337
+ const lookupName = input.skillName ?? inferredZipName ?? "skill";
338
+ const sourceRoot = await findSkillRoot(extractDir, lookupName);
339
+ const skillName = input.skillName ?? inferredZipName ?? path.basename(sourceRoot);
327
340
  return {
328
341
  sourceRoot,
329
342
  skillName,
@@ -364,24 +377,46 @@ async function listSkills(projectRoot, io) {
364
377
  io.log(`${name}\t${version}\tbin/${name}.mjs`);
365
378
  }
366
379
  }
367
- async function installSkill(projectRoot, parsed, io) {
368
- const resolved = await resolveInstallSource(parsed, io);
380
+ export async function installProjectSkill(input) {
381
+ const io = { ...defaultIo, ...input.io };
369
382
  try {
370
- const destRoot = path.join(skillsDirForRoot(projectRoot), resolved.skillName);
371
- await copySkillTree(resolved.sourceRoot, destRoot);
372
- const doctorScript = path.join(destRoot, "bin", `${resolved.skillName}.mjs`);
373
- const doctorCode = io.runDoctor(doctorScript, projectRoot);
374
- if (doctorCode !== 0) {
375
- io.error("skill doctor failed");
376
- return 1;
383
+ const resolved = await resolveInstallSource({
384
+ fromPath: input.fromPath,
385
+ fromZip: input.fromZip,
386
+ zipBytes: input.zipBytes,
387
+ skillName: input.skillName,
388
+ checksum: input.checksum,
389
+ }, io);
390
+ try {
391
+ const destRoot = path.join(skillsDirForRoot(input.projectRoot), resolved.skillName);
392
+ await copySkillTree(resolved.sourceRoot, destRoot);
393
+ const doctorScript = path.join(destRoot, "bin", `${resolved.skillName}.mjs`);
394
+ if (await pathExists(doctorScript)) {
395
+ const doctorCode = io.runDoctor(doctorScript, input.projectRoot);
396
+ if (doctorCode !== 0) {
397
+ io.error("skill doctor failed");
398
+ return { ok: false, error: "skill doctor failed", doctorFailed: true };
399
+ }
400
+ }
401
+ const message = `Installed skill ${resolved.skillName} to ${destRoot}`;
402
+ io.log(message);
403
+ return {
404
+ ok: true,
405
+ skillName: resolved.skillName,
406
+ destPath: destRoot,
407
+ message,
408
+ };
409
+ }
410
+ finally {
411
+ if (resolved.cleanup) {
412
+ await resolved.cleanup();
413
+ }
377
414
  }
378
- io.log(`Installed skill ${resolved.skillName} to ${destRoot}`);
379
- return 0;
380
415
  }
381
- finally {
382
- if (resolved.cleanup) {
383
- await resolved.cleanup();
384
- }
416
+ catch (err) {
417
+ const error = err instanceof Error ? err.message : String(err);
418
+ io.error(error);
419
+ return { ok: false, error };
385
420
  }
386
421
  }
387
422
  export async function runSkillsCommand(args, options = {}) {
@@ -402,7 +437,15 @@ export async function runSkillsCommand(args, options = {}) {
402
437
  await listSkills(projectRoot, io);
403
438
  return 0;
404
439
  }
405
- return await installSkill(projectRoot, parsed, io);
440
+ const result = await installProjectSkill({
441
+ projectRoot,
442
+ fromPath: parsed.fromPath,
443
+ fromZip: parsed.fromZip,
444
+ skillName: parsed.skillName,
445
+ checksum: parsed.checksum,
446
+ io,
447
+ });
448
+ return result.ok ? 0 : 1;
406
449
  }
407
450
  catch (err) {
408
451
  io.error(err instanceof Error ? err.message : String(err));
@@ -1,5 +1,5 @@
1
1
  import { validateCatalog as defaultValidateCatalog, type ValidationResult } from "../config/validateCatalog.js";
2
- export declare const VALIDATE_USAGE = "Usage:\n sf validate [--pipeline <path>] [--task <path>] [--trigger <path>] [--strict] [--json]\n\n With no flags, validates all pipelines and tasks declared in stageflow.yaml (manifest-all).\n --pipeline validates one pipeline file (includes uses:/include: transitively).\n --task validates one task file.\n --trigger validates one trigger file, including its pipeline/task refs.";
2
+ export declare const VALIDATE_USAGE = "Usage:\n sf validate [--project-root <id-or-path>] [--pipeline <path>] [--task <path>] [--trigger <path>] [--strict] [--json]\n\n With no flags, validates all pipelines and tasks declared in stageflow.yaml (manifest-all).\n --pipeline validates one pipeline file (includes uses:/include: transitively).\n --task validates one task file.\n --trigger validates one trigger file, including its pipeline/task refs.";
3
3
  export type ValidateCommandIo = {
4
4
  log: (line: string) => void;
5
5
  error: (line: string) => void;
@@ -1,7 +1,8 @@
1
1
  import { validateCatalog as defaultValidateCatalog, } from "../config/validateCatalog.js";
2
2
  import { exitCodeForValidation, formatValidationHuman, formatValidationJson, } from "./validateOutput.js";
3
+ import { resolveCliProjectRootLocal } from "./resolveCliProjectRoot.js";
3
4
  export const VALIDATE_USAGE = `Usage:
4
- sf validate [--pipeline <path>] [--task <path>] [--trigger <path>] [--strict] [--json]
5
+ sf validate [--project-root <id-or-path>] [--pipeline <path>] [--task <path>] [--trigger <path>] [--strict] [--json]
5
6
 
6
7
  With no flags, validates all pipelines and tasks declared in stageflow.yaml (manifest-all).
7
8
  --pipeline validates one pipeline file (includes uses:/include: transitively).
@@ -18,6 +19,7 @@ function parseValidateArgs(args) {
18
19
  if (args[0] === "--help" || args[0] === "-h") {
19
20
  return { help: true, strict: false, json: false };
20
21
  }
22
+ let projectRoot;
21
23
  let pipeline;
22
24
  let task;
23
25
  let trigger;
@@ -29,6 +31,13 @@ function parseValidateArgs(args) {
29
31
  if (arg === "--help" || arg === "-h") {
30
32
  help = true;
31
33
  }
34
+ else if (arg === "--project-root") {
35
+ const value = args[++i];
36
+ if (value === undefined || value.length === 0) {
37
+ throw new Error("Missing value for --project-root");
38
+ }
39
+ projectRoot = value;
40
+ }
32
41
  else if (arg === "--pipeline") {
33
42
  const value = args[++i];
34
43
  if (value === undefined || value.length === 0) {
@@ -67,11 +76,10 @@ function parseValidateArgs(args) {
67
76
  if (scopedFlagCount > 1) {
68
77
  throw new Error("Use at most one of --pipeline, --task, or --trigger");
69
78
  }
70
- return { help, pipeline, task, trigger, strict, json };
79
+ return { help, projectRoot, pipeline, task, trigger, strict, json };
71
80
  }
72
81
  export async function runValidateCommand(args, options = {}) {
73
- const cwd = options.cwd ?? process.cwd();
74
- const projectRoot = options.projectRoot ?? cwd;
82
+ const shellCwd = options.cwd ?? process.cwd();
75
83
  const out = { ...defaultIo, ...options.io };
76
84
  const validateCatalogFn = options.validateCatalog ?? defaultValidateCatalog;
77
85
  try {
@@ -80,6 +88,16 @@ export async function runValidateCommand(args, options = {}) {
80
88
  out.error(VALIDATE_USAGE);
81
89
  return 0;
82
90
  }
91
+ let cwd = shellCwd;
92
+ let projectRoot = options.projectRoot ?? shellCwd;
93
+ if (parsed.projectRoot !== undefined) {
94
+ const resolved = resolveCliProjectRootLocal(parsed.projectRoot);
95
+ if (!resolved) {
96
+ throw new Error(`Unknown project_root: ${parsed.projectRoot.trim()}`);
97
+ }
98
+ cwd = resolved.rootPath;
99
+ projectRoot = resolved.rootPath;
100
+ }
83
101
  const scope = parsed.pipeline
84
102
  ? "pipeline"
85
103
  : parsed.task
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import { A2A_USAGE, runA2aCommand } from "./cli/a2aCommand.js";
4
4
  import { realpathSync } from "node:fs";
5
5
  import path from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
- import { globalAgentBackendFromManifest, resolveAgentPort } from "./agent/resolveAgentPort.js";
7
+ import { globalStageBackendFromManifest, resolveStagePort } from "./agent/resolveStagePort.js";
8
8
  import { BROWSER_USAGE, runBrowserCommand } from "./cli/browserCommand.js";
9
9
  import { ARTIFACT_USAGE, runArtifactCommand } from "./cli/artifactCommand.js";
10
10
  import { ENVELOPE_USAGE, runEnvelopeCommand, } from "./cli/envelopeCommand.js";
@@ -503,7 +503,7 @@ async function main(argv) {
503
503
  if (parsed.command === "browser") {
504
504
  return runBrowserCommand(argv.slice(3));
505
505
  }
506
- const globalAgent = globalAgentBackendFromManifest(ctx.manifest);
506
+ const globalAgent = globalStageBackendFromManifest(ctx.manifest);
507
507
  if (parsed.command === "ui") {
508
508
  const listenHost = resolveListenHost({ flag: parsed.host });
509
509
  const tokens = loadControlTokens();
@@ -512,7 +512,7 @@ async function main(argv) {
512
512
  mcpStateless: parsed.mcpStateless,
513
513
  });
514
514
  const host = await startUiServer({
515
- agent: resolveAgentPort({ global: globalAgent }),
515
+ agent: resolveStagePort({ global: globalAgent }),
516
516
  cwd: ctx.invocationCwd,
517
517
  rootDir: ctx.projectRoot,
518
518
  port: parsed.port,
@@ -547,7 +547,7 @@ async function main(argv) {
547
547
  mcpStateless: parsed.mcpStateless,
548
548
  });
549
549
  const host = await startMcpServer({
550
- agent: resolveAgentPort({ global: globalAgent }),
550
+ agent: resolveStagePort({ global: globalAgent }),
551
551
  cwd: ctx.invocationCwd,
552
552
  rootDir: ctx.projectRoot,
553
553
  port: parsed.port,
@@ -13,6 +13,8 @@ export type PipelineStageListing = {
13
13
  gate_kinds?: StageGateKind[];
14
14
  uses_path?: string;
15
15
  inline?: boolean;
16
+ model?: string;
17
+ skill?: string;
16
18
  };
17
19
  export type PipelineListing = {
18
20
  path: string;
@@ -22,6 +22,12 @@ function mapStageListing(stage, source, projectRoot) {
22
22
  else if (source?.kind === "file") {
23
23
  listing.uses_path = repoRelPath(projectRoot, source.path);
24
24
  }
25
+ if (typeof stage.model === "string" && stage.model.trim() !== "") {
26
+ listing.model = stage.model;
27
+ }
28
+ if (typeof stage.skill === "string" && stage.skill.trim() !== "") {
29
+ listing.skill = stage.skill;
30
+ }
25
31
  return listing;
26
32
  }
27
33
  function bakedModels() {
@@ -37,6 +37,10 @@ export declare function catalogPathErrorBody(err: CatalogPathError): {
37
37
  * otherwise require explicit — no invent.
38
38
  */
39
39
  export declare function selectCatalogRootForStart(roots: CatalogRoot[], projectRoot?: string): CatalogRoot;
40
+ export declare function resolveDefaultWriteRoot(options: ResolveCatalogRootsOptions, projectRoot?: string): Promise<{
41
+ roots: CatalogRoot[];
42
+ wireRoot: CatalogRoot;
43
+ }>;
40
44
  /**
41
45
  * Shared helper: resolveCatalogRoots → selectCatalogRootForStart.
42
46
  * All network-surface start-input call sites use this to avoid the hand-copy sequence.