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
@@ -1,6 +1,9 @@
1
+ import path from "node:path";
1
2
  import { parse as parseYaml } from "yaml";
3
+ import { CatalogPathError, resolveDefaultWriteRoot, } from "../../config/catalogRelativePath.js";
2
4
  import { defaultWorkshopPackageDirectory } from "../../config/ensureCatalogScanRoot.js";
3
5
  import { publishDraftPackage } from "../../config/publishDraftPackage.js";
6
+ import { globalStageflowHome } from "../../project/globalHome.js";
4
7
  import { emptyDraftPackage, isWorkshopDraftContext, readDraftFromContext, withDraft, } from "../draftContext.js";
5
8
  import { focusUnboundWorkshopBuild, getWorkshopBuild, listWorkshopPickerRows, resolvePickerCatalogPipelines, } from "../../workshop/buildStore.js";
6
9
  import { focusWorkshopBuildPointer, pinWorkshopBuildOnCreate, recordPinnedWorkshopSave, resolveLiveWorkshopBinding, resolvePinnedSaveDestination, } from "../../workshop/workshopPin.js";
@@ -99,10 +102,10 @@ function readWorkshopContext(ctx) {
99
102
  return raw;
100
103
  return { draft: readDraftFromContext(raw) };
101
104
  }
102
- function resolveDestination(ctx, args) {
105
+ function resolveDestination(ctx, args, catalogRootKind) {
103
106
  const directory = stringArg(args, "directory") ??
104
107
  readWorkshopContext(ctx).destination?.directory ??
105
- defaultWorkshopPackageDirectory(readDraftFromContext(ctx.getContext()).pipeline.id);
108
+ defaultWorkshopPackageDirectory(readDraftFromContext(ctx.getContext()).pipeline.id, catalogRootKind);
106
109
  const pipelineFilename = stringArg(args, "pipelineFilename") ??
107
110
  stringArg(args, "pipeline_filename") ??
108
111
  readWorkshopContext(ctx).destination?.pipelineFilename;
@@ -113,6 +116,64 @@ function resolveProjectRoot(ctx, defaultProjectRoot) {
113
116
  // (or the profile/host default passed into createSaveTool).
114
117
  return readWorkshopContext(ctx).projectRoot ?? defaultProjectRoot ?? null;
115
118
  }
119
+ async function resolveSaveWriteTarget(ctx, options) {
120
+ const bound = resolveProjectRoot(ctx, options.projectRoot);
121
+ const live = liveWorkshopBinding(ctx);
122
+ const storeRoot = live?.storeRoot ?? globalStageflowHome();
123
+ const { createRunStore } = await import("../../runstore/createStore.js");
124
+ const store = createRunStore({ rootDir: storeRoot });
125
+ try {
126
+ if (bound) {
127
+ try {
128
+ const { wireRoot } = await resolveDefaultWriteRoot({ store }, bound);
129
+ return {
130
+ ok: true,
131
+ target: {
132
+ fsRoot: wireRoot.path,
133
+ wireProjectRoot: wireRoot.project_root,
134
+ catalogRootKind: wireRoot.kind,
135
+ },
136
+ };
137
+ }
138
+ catch (err) {
139
+ if (err instanceof CatalogPathError &&
140
+ err.code === "unknown_project_root") {
141
+ const abs = path.resolve(bound);
142
+ return {
143
+ ok: true,
144
+ target: {
145
+ fsRoot: abs,
146
+ wireProjectRoot: abs,
147
+ catalogRootKind: "registered",
148
+ },
149
+ };
150
+ }
151
+ if (err instanceof CatalogPathError) {
152
+ return { ok: false, error: err.message };
153
+ }
154
+ throw err;
155
+ }
156
+ }
157
+ const { wireRoot } = await resolveDefaultWriteRoot({ store }, undefined);
158
+ return {
159
+ ok: true,
160
+ target: {
161
+ fsRoot: wireRoot.path,
162
+ wireProjectRoot: wireRoot.project_root,
163
+ catalogRootKind: wireRoot.kind,
164
+ },
165
+ };
166
+ }
167
+ catch (err) {
168
+ if (err instanceof CatalogPathError) {
169
+ return { ok: false, error: err.message };
170
+ }
171
+ throw err;
172
+ }
173
+ finally {
174
+ await store.close();
175
+ }
176
+ }
116
177
  export function buildCreateStageDraft(draft, args) {
117
178
  const existingIds = new Set(draft.pipeline.stages.map((s) => (typeof s.id === "string" ? s.id : "")));
118
179
  let stageId = stringArg(args, "id") ??
@@ -366,7 +427,7 @@ async function listPickerCatalog(storeRoot) {
366
427
  export function createWorkshopBuildTools() {
367
428
  const listBuildsTool = {
368
429
  name: "list_builds",
369
- description: "List the same workshop rows as the studio picker (open builds and unbound disk pipelines). Does not create a build. Works when no build is focused.",
430
+ description: "List the same workshop rows as the studio picker (open builds and unbound disk pipelines). Each row includes catalogListed (whether Tasks/Pipelines would list that pipeline path). Does not create a build. Works when no build is focused.",
370
431
  async handler(_args, ctx) {
371
432
  const live = liveWorkshopBinding(ctx);
372
433
  if (!live) {
@@ -374,7 +435,16 @@ export function createWorkshopBuildTools() {
374
435
  }
375
436
  try {
376
437
  const catalog = await listPickerCatalog(live.storeRoot);
377
- const rows = listWorkshopPickerRows(live.storeRoot, catalog.pipelines, catalog.roots);
438
+ const rows = listWorkshopPickerRows(live.storeRoot, catalog.pipelines, catalog.roots).map((row) => {
439
+ if (!row.projectRoot || !row.relativePath) {
440
+ return { ...row, catalogListed: false };
441
+ }
442
+ const relPath = row.relativePath;
443
+ const normalize = (value) => value.replace(/\\/g, "/").replace(/^\.\//, "");
444
+ const listed = catalog.pipelines.some((pipeline) => pipeline.project_root === row.projectRoot &&
445
+ normalize(pipeline.path) === normalize(relPath));
446
+ return { ...row, catalogListed: listed };
447
+ });
378
448
  return { ok: true, content: { rows } };
379
449
  }
380
450
  catch (err) {
@@ -556,23 +626,24 @@ export function createWorkshopAuthorMutatingTools() {
556
626
  export function createSaveTool(options = {}) {
557
627
  return {
558
628
  name: "save",
559
- description: "Validate-then-write the draft via catalog facades (createDraftPackage / overwriteDraftPackage). When directory is omitted, writes under workshop/<pipeline-id>, which the host adds to the catalog so Run can see the pipeline and task. Pass directory only when the operator names a folder. Soft undo does not reverse disk. Prefer mode auto; set allowInvalid only when the operator explicitly requests saving invalid YAML. Do not pass projectRoot — the host binds it.",
629
+ description: "Validate-then-write the draft via catalog facades (createDraftPackage / overwriteDraftPackage). When directory is omitted, writes under workshop/<pipeline-id> on registered catalogs (or <pipeline-id> on the workspace catalog). The host adds registered workshop paths to stageflow.yaml so Run can see them. Pass directory only when the operator names a folder. Soft undo does not reverse disk. Prefer mode auto; set allowInvalid only when the operator explicitly requests saving invalid YAML. Do not pass projectRoot — the host binds it.",
560
630
  async handler(args, ctx) {
561
- const pinnedDestination = resolvePinnedSaveDestination(ctx.getContext());
562
- const destination = pinnedDestination ?? resolveDestination(ctx, args);
563
- if (!destination) {
631
+ const writeTarget = await resolveSaveWriteTarget(ctx, options);
632
+ if (!writeTarget.ok) {
564
633
  return {
565
634
  ok: false,
566
635
  content: null,
567
- error: "destination is required — set context.destination or pass directory (do not invent a disk path)",
636
+ error: writeTarget.error,
568
637
  };
569
638
  }
570
- const projectRoot = resolveProjectRoot(ctx, options.projectRoot);
571
- if (!projectRoot) {
639
+ const { fsRoot, catalogRootKind, wireProjectRoot } = writeTarget.target;
640
+ const pinnedDestination = resolvePinnedSaveDestination(ctx.getContext(), fsRoot);
641
+ const destination = pinnedDestination ?? resolveDestination(ctx, args, catalogRootKind);
642
+ if (!destination) {
572
643
  return {
573
644
  ok: false,
574
645
  content: null,
575
- error: "projectRoot is required — bind it on the draft context (host-owned; do not invent from tool args)",
646
+ error: "destination is required — set context.destination or pass directory (do not invent a disk path)",
576
647
  };
577
648
  }
578
649
  const draft = readDraftFromContext(ctx.getContext());
@@ -589,7 +660,9 @@ export function createSaveTool(options = {}) {
589
660
  : {}),
590
661
  ...(allowInvalid ? { allowInvalid: true } : {}),
591
662
  };
592
- const published = await publishDraftPackage(projectRoot, input, mode);
663
+ const published = await publishDraftPackage(fsRoot, input, mode, {
664
+ catalogRootKind,
665
+ });
593
666
  const result = published.write;
594
667
  if (result.ok) {
595
668
  const current = readWorkshopContext(ctx);
@@ -602,20 +675,34 @@ export function createSaveTool(options = {}) {
602
675
  ? { pipelineFilename: destination.pipelineFilename }
603
676
  : {}),
604
677
  },
605
- projectRoot,
678
+ projectRoot: fsRoot,
606
679
  });
607
680
  recordPinnedWorkshopSave(ctx.getContext(), {
608
681
  directory: destination.directory,
609
682
  ...(destination.pipelineFilename
610
683
  ? { pipelineFilename: destination.pipelineFilename }
611
684
  : {}),
612
- projectRoot,
685
+ projectRoot: fsRoot,
613
686
  draft,
687
+ relativePath: result.pipelinePath,
614
688
  });
615
689
  }
616
690
  return {
617
691
  ok: result.ok,
618
- content: result,
692
+ content: result.ok
693
+ ? {
694
+ ...result,
695
+ published: {
696
+ project_root: wireProjectRoot,
697
+ project_root_path: fsRoot,
698
+ pipeline_path: result.pipelinePath,
699
+ stage_paths: result.stagePaths,
700
+ ...(result.taskPath !== undefined
701
+ ? { task_path: result.taskPath }
702
+ : { task_path: null }),
703
+ },
704
+ }
705
+ : result,
619
706
  ...(result.ok ? {} : { error: result.error }),
620
707
  };
621
708
  },
@@ -1,3 +1,3 @@
1
1
  export declare const PACKAGE_NAME = "stageflow";
2
- export declare const PACKAGE_VERSION = "0.32.0";
2
+ export declare const PACKAGE_VERSION = "0.35.0";
3
3
  export declare const BUILD_SHA: string;
@@ -1,3 +1,3 @@
1
1
  export const PACKAGE_NAME = "stageflow";
2
- export const PACKAGE_VERSION = "0.32.0";
2
+ export const PACKAGE_VERSION = "0.35.0";
3
3
  export const BUILD_SHA = process.env.STAGEFLOW_BUILD_SHA ?? "unknown";
@@ -0,0 +1,3 @@
1
+ import type { RunStore } from "../runstore/port.js";
2
+ export declare function parseEnsureProjectRootsEnv(env: NodeJS.ProcessEnv): string[];
3
+ export declare function ensureProjectRootsFromEnv(env: NodeJS.ProcessEnv, store: RunStore): Promise<void>;
@@ -0,0 +1,22 @@
1
+ import path from "node:path";
2
+ export function parseEnsureProjectRootsEnv(env) {
3
+ const raw = env.STAGEFLOW_ENSURE_PROJECT_ROOTS?.trim();
4
+ if (!raw)
5
+ return [];
6
+ return raw
7
+ .split(",")
8
+ .map((entry) => entry.trim())
9
+ .filter((entry) => entry.length > 0);
10
+ }
11
+ export async function ensureProjectRootsFromEnv(env, store) {
12
+ for (const entry of parseEnsureProjectRootsEnv(env)) {
13
+ const abs = path.resolve(entry);
14
+ try {
15
+ await store.ensureProject(abs);
16
+ }
17
+ catch (err) {
18
+ const message = err instanceof Error ? err.message : String(err);
19
+ console.warn(`stageflow: could not register project_root ${abs}: ${message}`);
20
+ }
21
+ }
22
+ }
@@ -5,5 +5,6 @@ export declare class StageflowHomeError extends Error {
5
5
  }
6
6
  export declare function resetGlobalStageflowHomeForTests(): void;
7
7
  export declare function globalStageflowHome(): string;
8
+ export declare function globalCredentialRoot(): string;
8
9
  export declare function assertStageflowHomeWritable(): void;
9
10
  export declare function ensureGlobalHome(): string;
@@ -10,8 +10,10 @@ export class StageflowHomeError extends Error {
10
10
  }
11
11
  }
12
12
  let memoizedHome;
13
+ let memoizedCredentialRoot;
13
14
  export function resetGlobalStageflowHomeForTests() {
14
15
  memoizedHome = undefined;
16
+ memoizedCredentialRoot = undefined;
15
17
  }
16
18
  function resolveStageflowHomeFromEnv() {
17
19
  const raw = process.env.STAGEFLOW_HOME?.trim();
@@ -30,6 +32,22 @@ export function globalStageflowHome() {
30
32
  }
31
33
  return memoizedHome;
32
34
  }
35
+ function resolveCredentialRootFromEnv() {
36
+ const raw = process.env.STAGEFLOW_CREDENTIAL_HOME?.trim();
37
+ if (raw) {
38
+ if (path.isAbsolute(raw)) {
39
+ return path.resolve(raw);
40
+ }
41
+ return path.resolve(process.cwd(), raw);
42
+ }
43
+ return path.join(os.homedir(), ".stageflow");
44
+ }
45
+ export function globalCredentialRoot() {
46
+ if (memoizedCredentialRoot === undefined) {
47
+ memoizedCredentialRoot = resolveCredentialRootFromEnv();
48
+ }
49
+ return memoizedCredentialRoot;
50
+ }
33
51
  function stageflowHomeNotWritableMessage(resolvedPath) {
34
52
  const uid = process.getuid?.() ?? 0;
35
53
  const gid = process.getgid?.() ?? 0;
@@ -0,0 +1,10 @@
1
+ export declare const WORKSPACE_CATALOG_ID = "workspace";
2
+ export type WorkspaceProvisionResult = {
3
+ path: string;
4
+ ok: boolean;
5
+ reason?: string;
6
+ };
7
+ export declare function resetWorkspaceProvisionForTests(): void;
8
+ export declare function lastWorkspaceProvisionResult(): WorkspaceProvisionResult | undefined;
9
+ export declare function workspaceCatalogPath(home?: string): string;
10
+ export declare function ensureWorkspaceCatalog(home?: string): WorkspaceProvisionResult;
@@ -0,0 +1,91 @@
1
+ import { accessSync, constants, mkdirSync, writeFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { globalStageflowHome } from "./globalHome.js";
4
+ export const WORKSPACE_CATALOG_ID = "workspace";
5
+ let lastProvisionResult;
6
+ export function resetWorkspaceProvisionForTests() {
7
+ lastProvisionResult = undefined;
8
+ }
9
+ export function lastWorkspaceProvisionResult() {
10
+ return lastProvisionResult;
11
+ }
12
+ export function workspaceCatalogPath(home) {
13
+ return path.join(home ?? globalStageflowHome(), "workspace");
14
+ }
15
+ function workspaceManifestYaml() {
16
+ return `version: 1
17
+ catalog:
18
+ pipelines:
19
+ - .
20
+ tasks:
21
+ - .
22
+ triggers:
23
+ - .
24
+ patterns:
25
+ pipeline: "*.pipeline.yaml"
26
+ task: "*.task.yaml"
27
+ trigger: "*.trigger.yaml"
28
+ `;
29
+ }
30
+ function workspaceNotWritableReason(resolvedPath) {
31
+ const uid = process.getuid?.() ?? 0;
32
+ const gid = process.getgid?.() ?? 0;
33
+ return `Workspace catalog is not writable (${resolvedPath}). Running as uid=${uid} gid=${gid}. Try: sudo chown -R ${uid}:${gid} ${path.dirname(resolvedPath)}`;
34
+ }
35
+ function isWritableDir(dir) {
36
+ try {
37
+ accessSync(dir, constants.W_OK);
38
+ return true;
39
+ }
40
+ catch (err) {
41
+ const code = err.code;
42
+ if (code === "ENOENT") {
43
+ const parent = path.dirname(dir);
44
+ if (parent === dir)
45
+ return false;
46
+ try {
47
+ accessSync(parent, constants.W_OK);
48
+ return true;
49
+ }
50
+ catch {
51
+ return false;
52
+ }
53
+ }
54
+ return false;
55
+ }
56
+ }
57
+ export function ensureWorkspaceCatalog(home) {
58
+ const wsPath = workspaceCatalogPath(home);
59
+ if (!isWritableDir(wsPath)) {
60
+ const result = {
61
+ path: wsPath,
62
+ ok: false,
63
+ reason: workspaceNotWritableReason(wsPath),
64
+ };
65
+ lastProvisionResult = result;
66
+ return result;
67
+ }
68
+ try {
69
+ mkdirSync(wsPath, { recursive: true });
70
+ const manifestPath = path.join(wsPath, "stageflow.yaml");
71
+ try {
72
+ accessSync(manifestPath, constants.F_OK);
73
+ }
74
+ catch {
75
+ writeFileSync(manifestPath, workspaceManifestYaml(), "utf8");
76
+ }
77
+ const result = { path: wsPath, ok: true };
78
+ lastProvisionResult = result;
79
+ return result;
80
+ }
81
+ catch (err) {
82
+ const message = err instanceof Error ? err.message : String(err);
83
+ const result = {
84
+ path: wsPath,
85
+ ok: false,
86
+ reason: `${workspaceNotWritableReason(wsPath)} (${message})`,
87
+ };
88
+ lastProvisionResult = result;
89
+ return result;
90
+ }
91
+ }
@@ -46,4 +46,3 @@ export type RunProjection = {
46
46
  total_cost_usd?: number;
47
47
  };
48
48
  export declare function projectRun(detail: RunDetail): RunProjection;
49
- export { projectRun as projectRunForMcp };
@@ -80,4 +80,3 @@ export function projectRun(detail) {
80
80
  })),
81
81
  };
82
82
  }
83
- export { projectRun as projectRunForMcp };
@@ -8,7 +8,8 @@ import { pipeline } from "node:stream/promises";
8
8
  import { createWriteStream } from "node:fs";
9
9
  import Database from "better-sqlite3";
10
10
  import { PACKAGE_VERSION } from "../package-meta.js";
11
- import { globalStageflowHome } from "../project/globalHome.js";
11
+ import { globalCredentialRoot, globalStageflowHome } from "../project/globalHome.js";
12
+ import { isUsableAuthFile } from "../runtime/credentialBinding.js";
12
13
  import { readFilesystemSize } from "./diskUsage.js";
13
14
  import { isInsideDir } from "./workspaceLayout.js";
14
15
  export class BackupError extends Error {
@@ -45,6 +46,14 @@ export function assertBackupOutPathAllowed(outPath, homeDir = globalStageflowHom
45
46
  }
46
47
  return resolved;
47
48
  }
49
+ function isCredentialRootDir(dir) {
50
+ return path.resolve(dir) === path.resolve(globalCredentialRoot());
51
+ }
52
+ function packAuthMember(homeDir, authPath, noCredentials) {
53
+ return (!noCredentials &&
54
+ isCredentialRootDir(homeDir) &&
55
+ isUsableAuthFile(authPath));
56
+ }
48
57
  function fingerprint(filePath) {
49
58
  if (!existsSync(filePath))
50
59
  return null;
@@ -203,7 +212,8 @@ export async function createBackup(options) {
203
212
  });
204
213
  contents.push("settings.json");
205
214
  }
206
- if (!noCredentials && existsSync(authPath)) {
215
+ const includeAuth = packAuthMember(homeDir, authPath, noCredentials);
216
+ if (includeAuth) {
207
217
  const stagedAuth = path.join(staging, "auth.json");
208
218
  copyFileSync(authPath, stagedAuth);
209
219
  chmodSync(stagedAuth, 0o600);
@@ -226,14 +236,14 @@ export async function createBackup(options) {
226
236
  schema_version: userVersion,
227
237
  contents,
228
238
  db_only: false,
229
- credentials_included: !noCredentials && existsSync(authPath),
239
+ credentials_included: includeAuth,
230
240
  a2a_artifacts_included: includeA2a,
231
241
  settings_fingerprint_before: settingsBefore,
232
242
  settings_fingerprint_after: settingsAfter,
233
243
  auth_fingerprint_before: authBefore,
234
244
  auth_fingerprint_after: authAfter,
235
245
  fingerprint_skew: fingerprintSkew,
236
- secret_warning: SECRET_WARNING,
246
+ secret_warning: includeAuth ? SECRET_WARNING : "",
237
247
  };
238
248
  const manifestPath = path.join(staging, "manifest.json");
239
249
  writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, {
@@ -99,6 +99,7 @@ export type StageSnapshot = {
99
99
  attempt_count: number;
100
100
  /** Total $ spent on this stage across every attempt; omitted when no attempt reported usage. */
101
101
  cost_usd?: number;
102
+ usage?: StageUsage;
102
103
  };
103
104
  export type StageExecution = {
104
105
  run_id: string;
@@ -470,12 +471,21 @@ export interface RunStore {
470
471
  upsertTrigger(input: UpsertTriggerInput): Promise<TriggerRecord>;
471
472
  getTrigger(id: string): Promise<TriggerRecord | null>;
472
473
  listTriggers(): Promise<TriggerRecord[]>;
473
- /** Record a fire: stamps last_fired_at/last_run_id. Throws if the trigger is missing. */
474
+ /** Record a fire: stamps last_fired_at/last_run_id and appends trigger_fires. Throws if the trigger is missing. */
474
475
  recordTriggerFired(id: string, runId: string): Promise<void>;
476
+ /** Newest first. */
477
+ listTriggerFires(triggerId: string, limit: number): Promise<Array<{
478
+ fired_at: string;
479
+ run_id: string;
480
+ }>>;
475
481
  /** Persist the next computed fire time for a schedule-kind trigger. Throws if the trigger is missing. */
476
482
  setTriggerNextRun(id: string, nextRunAt: string): Promise<void>;
477
483
  /** Read a single adapter-owned state value for a trigger, or null if unset. */
478
484
  getTriggerAdapterState(triggerId: string, key: string): Promise<string | null>;
485
+ getTriggerAdapterStateMeta(triggerId: string, key: string): Promise<{
486
+ value: string;
487
+ updated_at: string;
488
+ } | null>;
479
489
  /** Upsert a single adapter-owned state value for a trigger. */
480
490
  setTriggerAdapterState(triggerId: string, key: string, value: string): Promise<void>;
481
491
  readRun(runId: string): Promise<RunDetail>;
@@ -7,7 +7,8 @@ import { pipeline } from "node:stream/promises";
7
7
  import { createWriteStream } from "node:fs";
8
8
  import Database from "better-sqlite3";
9
9
  import { PACKAGE_VERSION } from "../package-meta.js";
10
- import { globalStageflowHome } from "../project/globalHome.js";
10
+ import { globalCredentialRoot, globalStageflowHome } from "../project/globalHome.js";
11
+ import { isUsableAuthFile } from "../runtime/credentialBinding.js";
11
12
  import { CURRENT_SCHEMA_VERSION } from "./sqlite/migrations/index.js";
12
13
  import { StoreSchemaError } from "./sqlite/storeSchemaError.js";
13
14
  import { assertStoreQuickCheck } from "./sqlite/applyStorePragmas.js";
@@ -234,7 +235,9 @@ export async function applyRestoreArchive(options) {
234
235
  if (existsSync(settingsSrc)) {
235
236
  copyFileSync(settingsSrc, path.join(homeDir, "settings.json"));
236
237
  }
237
- if (existsSync(authSrc)) {
238
+ const restoreAuth = path.resolve(homeDir) === path.resolve(globalCredentialRoot()) &&
239
+ isUsableAuthFile(authSrc);
240
+ if (restoreAuth) {
238
241
  const agentDir = path.join(homeDir, "agent");
239
242
  mkdirSync(agentDir, { recursive: true });
240
243
  try {
@@ -82,8 +82,16 @@ export declare class SqliteRunStore implements RunStore {
82
82
  getTrigger(id: string): Promise<TriggerRecord | null>;
83
83
  listTriggers(): Promise<TriggerRecord[]>;
84
84
  recordTriggerFired(id: string, runId: string): Promise<void>;
85
+ listTriggerFires(triggerId: string, limit: number): Promise<Array<{
86
+ fired_at: string;
87
+ run_id: string;
88
+ }>>;
85
89
  setTriggerNextRun(id: string, nextRunAt: string): Promise<void>;
86
90
  getTriggerAdapterState(triggerId: string, key: string): Promise<string | null>;
91
+ getTriggerAdapterStateMeta(triggerId: string, key: string): Promise<{
92
+ value: string;
93
+ updated_at: string;
94
+ } | null>;
87
95
  setTriggerAdapterState(triggerId: string, key: string, value: string): Promise<void>;
88
96
  readRun(runId: string): Promise<RunDetail>;
89
97
  createFeedbackLoop(runId: string, input: CreateFeedbackLoopInput): Promise<FeedbackLoopRecord>;
@@ -942,13 +942,29 @@ export class SqliteRunStore {
942
942
  async recordTriggerFired(id, runId) {
943
943
  await this.ready();
944
944
  const now = new Date().toISOString();
945
- const result = this.db
946
- .prepare(`UPDATE triggers SET last_fired_at = @last_fired_at, last_run_id = @last_run_id, updated_at = @updated_at
947
- WHERE id = @id`)
948
- .run({ id, last_fired_at: now, last_run_id: runId, updated_at: now });
949
- if (result.changes === 0) {
950
- throw new Error(`Trigger not found: ${id}`);
951
- }
945
+ const apply = this.db.transaction(() => {
946
+ const result = this.db
947
+ .prepare(`UPDATE triggers SET last_fired_at = @last_fired_at, last_run_id = @last_run_id, updated_at = @updated_at
948
+ WHERE id = @id`)
949
+ .run({ id, last_fired_at: now, last_run_id: runId, updated_at: now });
950
+ if (result.changes === 0) {
951
+ throw new Error(`Trigger not found: ${id}`);
952
+ }
953
+ this.db
954
+ .prepare(`INSERT INTO trigger_fires (trigger_id, run_id, fired_at)
955
+ VALUES (@trigger_id, @run_id, @fired_at)`)
956
+ .run({ trigger_id: id, run_id: runId, fired_at: now });
957
+ });
958
+ apply();
959
+ }
960
+ async listTriggerFires(triggerId, limit) {
961
+ await this.ready();
962
+ return this.db
963
+ .prepare(`SELECT fired_at, run_id FROM trigger_fires
964
+ WHERE trigger_id = ?
965
+ ORDER BY fired_at DESC, id DESC
966
+ LIMIT ?`)
967
+ .all(triggerId, limit);
952
968
  }
953
969
  async setTriggerNextRun(id, nextRunAt) {
954
970
  await this.ready();
@@ -962,11 +978,15 @@ export class SqliteRunStore {
962
978
  }
963
979
  }
964
980
  async getTriggerAdapterState(triggerId, key) {
981
+ const row = await this.getTriggerAdapterStateMeta(triggerId, key);
982
+ return row?.value ?? null;
983
+ }
984
+ async getTriggerAdapterStateMeta(triggerId, key) {
965
985
  await this.ready();
966
986
  const row = this.db
967
- .prepare(`SELECT value FROM trigger_adapter_state WHERE trigger_id = ? AND key = ?`)
987
+ .prepare(`SELECT value, updated_at FROM trigger_adapter_state WHERE trigger_id = ? AND key = ?`)
968
988
  .get(triggerId, key);
969
- return row ? row.value : null;
989
+ return row ?? null;
970
990
  }
971
991
  async setTriggerAdapterState(triggerId, key, value) {
972
992
  await this.ready();
@@ -0,0 +1,7 @@
1
+ import type Database from "better-sqlite3";
2
+ export declare const MIGRATION_012: {
3
+ version: number;
4
+ name: string;
5
+ minStageflowVersion: string;
6
+ up(db: Database.Database): void;
7
+ };
@@ -0,0 +1,18 @@
1
+ import { PACKAGE_VERSION } from "../../../package-meta.js";
2
+ export const MIGRATION_012 = {
3
+ version: 12,
4
+ name: "012_trigger_fires",
5
+ minStageflowVersion: PACKAGE_VERSION,
6
+ up(db) {
7
+ db.exec(`
8
+ CREATE TABLE IF NOT EXISTS trigger_fires (
9
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
10
+ trigger_id TEXT NOT NULL,
11
+ run_id TEXT NOT NULL,
12
+ fired_at TEXT NOT NULL
13
+ );
14
+ CREATE INDEX IF NOT EXISTS trigger_fires_trigger_fired
15
+ ON trigger_fires (trigger_id, fired_at DESC);
16
+ `);
17
+ },
18
+ };
@@ -10,6 +10,7 @@ import { MIGRATION_008 } from "./008-triggers-table.js";
10
10
  import { MIGRATION_009 } from "./009-trigger-next-run.js";
11
11
  import { MIGRATION_010 } from "./010-trigger-adapter-state.js";
12
12
  import { MIGRATION_011 } from "./011-email-dispatch-key.js";
13
+ import { MIGRATION_012 } from "./012-trigger-fires.js";
13
14
  const DEFAULT_MIGRATIONS = [
14
15
  MIGRATION_001,
15
16
  MIGRATION_002,
@@ -22,6 +23,7 @@ const DEFAULT_MIGRATIONS = [
22
23
  MIGRATION_009,
23
24
  MIGRATION_010,
24
25
  MIGRATION_011,
26
+ MIGRATION_012,
25
27
  ];
26
28
  export const CURRENT_SCHEMA_VERSION = DEFAULT_MIGRATIONS[DEFAULT_MIGRATIONS.length - 1].version;
27
29
  function throwStoreSchemaTooNew(db, version, maxKnown) {
@@ -1,2 +1,4 @@
1
- import type { RunStore, StageSnapshot } from "./port.js";
1
+ import { type StageUsage } from "../types/usage.js";
2
+ import type { RunStore, StageExecution, StageSnapshot } from "./port.js";
2
3
  export declare function buildStageSnapshotFromStore(store: RunStore, runId: string, stageId: string, workspaceDir: string): Promise<StageSnapshot>;
4
+ export declare function mergeStageExecutionUsage(executions: StageExecution[]): StageUsage | undefined;