stageflow 0.25.0 → 0.30.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 (681) hide show
  1. package/README.md +14 -3
  2. package/builtin-skills/browser/SKILL.md +42 -0
  3. package/dist/a2a/contracts.d.ts +1 -0
  4. package/dist/a2a/contracts.js +1 -0
  5. package/dist/a2a/registry.d.ts +2 -0
  6. package/dist/a2a/registry.js +20 -1
  7. package/dist/a2a/server.d.ts +7 -0
  8. package/dist/a2a/server.js +5 -2
  9. package/dist/a2a/service.d.ts +1 -1
  10. package/dist/a2a/service.js +57 -8
  11. package/dist/a2a/store.d.ts +8 -5
  12. package/dist/a2a/store.js +24 -67
  13. package/dist/agent/activity.d.ts +3 -0
  14. package/dist/agent/bootProviderConfig.d.ts +21 -0
  15. package/dist/agent/bootProviderConfig.js +74 -0
  16. package/dist/agent/claudeAdapter.js +2 -2
  17. package/dist/agent/cursorProvider.d.ts +2 -0
  18. package/dist/agent/cursorProvider.js +30 -5
  19. package/dist/agent/fakeAgent.d.ts +1 -0
  20. package/dist/agent/fakeAgent.js +13 -0
  21. package/dist/agent/piAdapter.d.ts +11 -24
  22. package/dist/agent/piAdapter.js +107 -50
  23. package/dist/agent/piIsolatedMcp.d.ts +2 -0
  24. package/dist/agent/piIsolatedMcp.js +10 -2
  25. package/dist/agent/piSessionFactory.d.ts +38 -0
  26. package/dist/agent/piSessionFactory.js +60 -0
  27. package/dist/agent/port.d.ts +8 -0
  28. package/dist/agent/streamLogRedact.d.ts +7 -1
  29. package/dist/agent/streamLogRedact.js +26 -11
  30. package/dist/browser/anchor.d.ts +27 -0
  31. package/dist/browser/anchor.js +127 -0
  32. package/dist/browser/auditSink.d.ts +38 -0
  33. package/dist/browser/auditSink.js +34 -0
  34. package/dist/browser/browserHost.d.ts +86 -0
  35. package/dist/browser/browserHost.js +7 -0
  36. package/dist/browser/browserSweep.d.ts +14 -0
  37. package/dist/browser/browserSweep.js +54 -0
  38. package/dist/browser/browserTeardown.d.ts +56 -0
  39. package/dist/browser/browserTeardown.js +235 -0
  40. package/dist/browser/fakeRemoteBrowserHost.d.ts +2 -0
  41. package/dist/browser/fakeRemoteBrowserHost.js +34 -0
  42. package/dist/browser/gateHandoff.d.ts +25 -0
  43. package/dist/browser/gateHandoff.js +83 -0
  44. package/dist/browser/humanLogin.d.ts +18 -0
  45. package/dist/browser/humanLogin.js +34 -0
  46. package/dist/browser/keyProvider.d.ts +10 -0
  47. package/dist/browser/keyProvider.js +39 -0
  48. package/dist/browser/localBrowserHost.d.ts +10 -0
  49. package/dist/browser/localBrowserHost.js +152 -0
  50. package/dist/browser/localProfileLock.d.ts +2 -0
  51. package/dist/browser/localProfileLock.js +218 -0
  52. package/dist/browser/localProfileStore.d.ts +5 -0
  53. package/dist/browser/localProfileStore.js +91 -0
  54. package/dist/browser/loginCheck.d.ts +53 -0
  55. package/dist/browser/loginCheck.js +147 -0
  56. package/dist/browser/memoryProfileLock.d.ts +2 -0
  57. package/dist/browser/memoryProfileLock.js +53 -0
  58. package/dist/browser/memoryProfileStore.d.ts +2 -0
  59. package/dist/browser/memoryProfileStore.js +31 -0
  60. package/dist/browser/navigationAudit.d.ts +16 -0
  61. package/dist/browser/navigationAudit.js +53 -0
  62. package/dist/browser/persistedEnv.d.ts +3 -0
  63. package/dist/browser/persistedEnv.js +15 -0
  64. package/dist/browser/profileLock.d.ts +40 -0
  65. package/dist/browser/profileLock.js +10 -0
  66. package/dist/browser/profileStore.d.ts +23 -0
  67. package/dist/browser/profileStore.js +39 -0
  68. package/dist/browser/runLiveness.d.ts +13 -0
  69. package/dist/browser/runLiveness.js +39 -0
  70. package/dist/browser/sitePolicy.d.ts +10 -0
  71. package/dist/browser/sitePolicy.js +61 -0
  72. package/dist/browser/stageBrowserEnv.d.ts +22 -0
  73. package/dist/browser/stageBrowserEnv.js +147 -0
  74. package/dist/browser/stageProfileLock.d.ts +17 -0
  75. package/dist/browser/stageProfileLock.js +39 -0
  76. package/dist/cli/artifactCommand.js +9 -16
  77. package/dist/cli/backupCommand.d.ts +9 -0
  78. package/dist/cli/backupCommand.js +111 -0
  79. package/dist/cli/browserCommand.d.ts +44 -0
  80. package/dist/cli/browserCommand.js +472 -0
  81. package/dist/cli/debugRunCommand.d.ts +10 -0
  82. package/dist/cli/debugRunCommand.js +109 -0
  83. package/dist/cli/doctorCommand.d.ts +32 -0
  84. package/dist/cli/doctorCommand.js +453 -0
  85. package/dist/cli/envelopeCommand.js +9 -2
  86. package/dist/cli/exportAllCommand.d.ts +55 -0
  87. package/dist/cli/exportAllCommand.js +218 -0
  88. package/dist/cli/exportRunCommand.js +23 -21
  89. package/dist/cli/hostClient.d.ts +41 -3
  90. package/dist/cli/hostClient.js +129 -14
  91. package/dist/cli/initCommand.d.ts +1 -0
  92. package/dist/cli/initCommand.js +23 -7
  93. package/dist/cli/initTemplates.d.ts +5 -2
  94. package/dist/cli/initTemplates.js +32 -2
  95. package/dist/cli/resolveSafeOutPath.d.ts +2 -0
  96. package/dist/cli/resolveSafeOutPath.js +40 -0
  97. package/dist/cli/restoreCommand.d.ts +10 -0
  98. package/dist/cli/restoreCommand.js +90 -0
  99. package/dist/cli/runCommand.d.ts +3 -2
  100. package/dist/cli/runCommand.js +93 -11
  101. package/dist/cli/runOutput.d.ts +4 -0
  102. package/dist/cli/runOutput.js +33 -7
  103. package/dist/cli/runStageCommand.d.ts +1 -0
  104. package/dist/cli/runStageCommand.js +44 -25
  105. package/dist/cli/runsCommand.d.ts +1 -1
  106. package/dist/cli/runsCommand.js +168 -10
  107. package/dist/cli/triggerCommand.d.ts +12 -0
  108. package/dist/cli/triggerCommand.js +183 -0
  109. package/dist/cli/validateCommand.d.ts +1 -1
  110. package/dist/cli/validateCommand.js +23 -6
  111. package/dist/cli.js +202 -32
  112. package/dist/config/browseCatalog.d.ts +1 -0
  113. package/dist/config/browseCatalog.js +5 -4
  114. package/dist/config/builtinSkills.d.ts +4 -0
  115. package/dist/config/builtinSkills.js +23 -0
  116. package/dist/config/catalogRelativePath.d.ts +63 -0
  117. package/dist/config/catalogRelativePath.js +126 -0
  118. package/dist/config/configOrigin.d.ts +27 -0
  119. package/dist/config/configOrigin.js +59 -0
  120. package/dist/config/createTrigger.d.ts +27 -0
  121. package/dist/config/createTrigger.js +232 -0
  122. package/dist/config/draftPackage.d.ts +117 -0
  123. package/dist/config/draftPackage.js +550 -0
  124. package/dist/config/ensureCatalogScanRoot.d.ts +16 -0
  125. package/dist/config/ensureCatalogScanRoot.js +73 -0
  126. package/dist/config/hostConfig.d.ts +57 -0
  127. package/dist/config/hostConfig.js +360 -0
  128. package/dist/config/listSkills.d.ts +26 -0
  129. package/dist/config/listSkills.js +206 -5
  130. package/dist/config/loadOutcome.d.ts +7 -1
  131. package/dist/config/loadPipeline.js +39 -2
  132. package/dist/config/loadStage.js +25 -0
  133. package/dist/config/loadStageflowManifest.js +25 -0
  134. package/dist/config/loadTask.d.ts +2 -0
  135. package/dist/config/loadTask.js +57 -18
  136. package/dist/config/loadTrigger.d.ts +9 -0
  137. package/dist/config/loadTrigger.js +120 -0
  138. package/dist/config/materializeStageModels.js +5 -1
  139. package/dist/config/mergePipelineIncludes.d.ts +3 -0
  140. package/dist/config/mergePipelineIncludes.js +19 -0
  141. package/dist/config/migrateYaml.js +6 -11
  142. package/dist/config/multiProjectCatalog.d.ts +32 -0
  143. package/dist/config/multiProjectCatalog.js +150 -0
  144. package/dist/config/normalizePipelineStageEntry.js +27 -1
  145. package/dist/config/parseCompletionContract.js +6 -0
  146. package/dist/config/pipelineStageKeys.js +3 -0
  147. package/dist/config/publishDraftPackage.d.ts +7 -0
  148. package/dist/config/publishDraftPackage.js +25 -0
  149. package/dist/config/resolveCatalogContext.js +1 -1
  150. package/dist/config/resolveCatalogRoots.d.ts +27 -0
  151. package/dist/config/resolveCatalogRoots.js +76 -0
  152. package/dist/config/resolveModel.d.ts +7 -1
  153. package/dist/config/resolveModel.js +11 -1
  154. package/dist/config/resolvePipelineDag.js +1 -1
  155. package/dist/config/resolveStageMcpServers.d.ts +9 -1
  156. package/dist/config/resolveStageMcpServers.js +36 -9
  157. package/dist/config/scanCatalogPaths.d.ts +1 -1
  158. package/dist/config/scanCatalogPaths.js +6 -2
  159. package/dist/config/secretFromEnvOrFile.d.ts +9 -0
  160. package/dist/config/secretFromEnvOrFile.js +34 -0
  161. package/dist/config/seededCatalog.d.ts +11 -0
  162. package/dist/config/seededCatalog.js +29 -0
  163. package/dist/config/stageBrowser.d.ts +12 -0
  164. package/dist/config/stageBrowser.js +118 -0
  165. package/dist/config/toolRequires.d.ts +24 -0
  166. package/dist/config/toolRequires.js +115 -0
  167. package/dist/config/triggerCatalog.d.ts +10 -0
  168. package/dist/config/triggerCatalog.js +37 -0
  169. package/dist/config/validateCatalog.d.ts +17 -3
  170. package/dist/config/validateCatalog.js +223 -3
  171. package/dist/config/validateCompletionContract.js +8 -0
  172. package/dist/diagnostics/checks.d.ts +39 -0
  173. package/dist/diagnostics/checks.js +135 -0
  174. package/dist/envelope/preEmitChecks.d.ts +1 -1
  175. package/dist/envelope/preEmitChecks.js +8 -0
  176. package/dist/errors/codes.d.ts +14 -0
  177. package/dist/errors/codes.js +59 -0
  178. package/dist/git/cache.d.ts +14 -0
  179. package/dist/git/cache.js +150 -0
  180. package/dist/git/credentials.d.ts +9 -0
  181. package/dist/git/credentials.js +88 -0
  182. package/dist/git/errors.d.ts +20 -0
  183. package/dist/git/errors.js +94 -0
  184. package/dist/git/exec.d.ts +16 -0
  185. package/dist/git/exec.js +135 -0
  186. package/dist/git/operations.d.ts +29 -0
  187. package/dist/git/operations.js +153 -0
  188. package/dist/git/version.d.ts +1 -0
  189. package/dist/git/version.js +10 -0
  190. package/dist/logging/audit.d.ts +16 -0
  191. package/dist/logging/audit.js +19 -0
  192. package/dist/logging/commonWords.d.ts +2 -0
  193. package/dist/logging/commonWords.js +13 -0
  194. package/dist/logging/logger.d.ts +49 -0
  195. package/dist/logging/logger.js +193 -0
  196. package/dist/logging/namedSecrets.d.ts +6 -0
  197. package/dist/logging/namedSecrets.js +58 -0
  198. package/dist/logging/redact.d.ts +17 -0
  199. package/dist/logging/redact.js +105 -0
  200. package/dist/mcp/catalogTools.js +492 -81
  201. package/dist/mcp/checkoutTools.d.ts +52 -0
  202. package/dist/mcp/checkoutTools.js +332 -0
  203. package/dist/mcp/controlTools.js +182 -6
  204. package/dist/mcp/deps.d.ts +3 -0
  205. package/dist/mcp/getStartedTool.d.ts +3 -0
  206. package/dist/mcp/getStartedTool.js +62 -0
  207. package/dist/mcp/readArtifact.d.ts +11 -0
  208. package/dist/mcp/readArtifact.js +46 -30
  209. package/dist/mcp/server.js +2 -0
  210. package/dist/mcp/tools.js +6 -0
  211. package/dist/mcp/triggerTools.d.ts +3 -0
  212. package/dist/mcp/triggerTools.js +176 -0
  213. package/dist/net/proxy.d.ts +14 -0
  214. package/dist/net/proxy.js +78 -0
  215. package/dist/operatorAgent/docsRetrieval.d.ts +40 -0
  216. package/dist/operatorAgent/docsRetrieval.js +269 -0
  217. package/dist/operatorAgent/draftContext.d.ts +16 -0
  218. package/dist/operatorAgent/draftContext.js +68 -0
  219. package/dist/operatorAgent/fakeHost.d.ts +39 -0
  220. package/dist/operatorAgent/fakeHost.js +92 -0
  221. package/dist/operatorAgent/host.d.ts +40 -0
  222. package/dist/operatorAgent/host.js +179 -0
  223. package/dist/operatorAgent/index.d.ts +9 -0
  224. package/dist/operatorAgent/index.js +8 -0
  225. package/dist/operatorAgent/piModel.d.ts +80 -0
  226. package/dist/operatorAgent/piModel.js +528 -0
  227. package/dist/operatorAgent/profiles/workshopAuthor.d.ts +23 -0
  228. package/dist/operatorAgent/profiles/workshopAuthor.js +338 -0
  229. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +6 -0
  230. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +103 -0
  231. package/dist/operatorAgent/profiles/workshopAuthorTools.d.ts +57 -0
  232. package/dist/operatorAgent/profiles/workshopAuthorTools.js +623 -0
  233. package/dist/operatorAgent/proposals.d.ts +32 -0
  234. package/dist/operatorAgent/proposals.js +144 -0
  235. package/dist/operatorAgent/types.d.ts +128 -0
  236. package/dist/operatorAgent/types.js +1 -0
  237. package/dist/package-meta.d.ts +2 -1
  238. package/dist/package-meta.js +2 -1
  239. package/dist/preflight/bash.d.ts +8 -0
  240. package/dist/preflight/bash.js +34 -0
  241. package/dist/preflight/claudeRoot.d.ts +11 -0
  242. package/dist/preflight/claudeRoot.js +23 -0
  243. package/dist/preflight/mcpCommands.d.ts +8 -0
  244. package/dist/preflight/mcpCommands.js +36 -0
  245. package/dist/preflight/pipelinePreflight.d.ts +40 -0
  246. package/dist/preflight/pipelinePreflight.js +192 -0
  247. package/dist/preflight/tls.d.ts +6 -0
  248. package/dist/preflight/tls.js +18 -0
  249. package/dist/preflight/toolchain.d.ts +45 -0
  250. package/dist/preflight/toolchain.js +219 -0
  251. package/dist/project/findManifestRoot.d.ts +3 -0
  252. package/dist/project/findManifestRoot.js +35 -0
  253. package/dist/project/findProjectRoot.js +3 -4
  254. package/dist/project/globalHome.d.ts +7 -0
  255. package/dist/project/globalHome.js +63 -2
  256. package/dist/project/resolveProjectContext.js +2 -2
  257. package/dist/project/resolveStageflowContext.d.ts +7 -0
  258. package/dist/project/resolveStageflowContext.js +46 -30
  259. package/dist/projection/projectRun.d.ts +2 -1
  260. package/dist/projection/projectRun.js +1 -0
  261. package/dist/runstore/assertTmpdir.d.ts +1 -0
  262. package/dist/runstore/assertTmpdir.js +25 -0
  263. package/dist/runstore/backup.d.ts +52 -0
  264. package/dist/runstore/backup.js +292 -0
  265. package/dist/runstore/createStore.d.ts +18 -1
  266. package/dist/runstore/createStore.js +64 -5
  267. package/dist/runstore/debugBundle.d.ts +60 -0
  268. package/dist/runstore/debugBundle.js +143 -0
  269. package/dist/runstore/diskUsage.d.ts +50 -0
  270. package/dist/runstore/diskUsage.js +214 -0
  271. package/dist/runstore/exportRunPayload.d.ts +8 -0
  272. package/dist/runstore/exportRunPayload.js +23 -0
  273. package/dist/runstore/normalizeCatalogPath.d.ts +1 -0
  274. package/dist/runstore/normalizeCatalogPath.js +10 -0
  275. package/dist/runstore/paths.d.ts +5 -0
  276. package/dist/runstore/paths.js +145 -1
  277. package/dist/runstore/port.d.ts +139 -13
  278. package/dist/runstore/port.js +8 -1
  279. package/dist/runstore/restore.d.ts +81 -0
  280. package/dist/runstore/restore.js +355 -0
  281. package/dist/runstore/retention.d.ts +27 -0
  282. package/dist/runstore/retention.js +74 -0
  283. package/dist/runstore/runManifest.d.ts +146 -0
  284. package/dist/runstore/runManifest.js +288 -0
  285. package/dist/runstore/runProjection.d.ts +3 -1
  286. package/dist/runstore/runProjection.js +63 -1
  287. package/dist/runstore/sqlite/SqliteRunStore.d.ts +35 -3
  288. package/dist/runstore/sqlite/SqliteRunStore.js +442 -219
  289. package/dist/runstore/sqlite/applyStorePragmas.d.ts +39 -0
  290. package/dist/runstore/sqlite/applyStorePragmas.js +145 -0
  291. package/dist/runstore/sqlite/migrations/001-baseline.d.ts +7 -0
  292. package/dist/runstore/sqlite/migrations/001-baseline.js +10 -0
  293. package/dist/runstore/sqlite/migrations/002-repository-binding.d.ts +7 -0
  294. package/dist/runstore/sqlite/migrations/002-repository-binding.js +29 -0
  295. package/dist/runstore/sqlite/migrations/003-run-lifecycle.d.ts +7 -0
  296. package/dist/runstore/sqlite/migrations/003-run-lifecycle.js +28 -0
  297. package/dist/runstore/sqlite/migrations/004-auto-resume-count.d.ts +7 -0
  298. package/dist/runstore/sqlite/migrations/004-auto-resume-count.js +19 -0
  299. package/dist/runstore/sqlite/migrations/005-config-origins.d.ts +7 -0
  300. package/dist/runstore/sqlite/migrations/005-config-origins.js +19 -0
  301. package/dist/runstore/sqlite/migrations/006-pipeline-body-and-caller.d.ts +7 -0
  302. package/dist/runstore/sqlite/migrations/006-pipeline-body-and-caller.js +28 -0
  303. package/dist/runstore/sqlite/migrations/007-projects-registry.d.ts +7 -0
  304. package/dist/runstore/sqlite/migrations/007-projects-registry.js +31 -0
  305. package/dist/runstore/sqlite/migrations/008-triggers-table.d.ts +7 -0
  306. package/dist/runstore/sqlite/migrations/008-triggers-table.js +19 -0
  307. package/dist/runstore/sqlite/migrations/009-trigger-next-run.d.ts +7 -0
  308. package/dist/runstore/sqlite/migrations/009-trigger-next-run.js +19 -0
  309. package/dist/runstore/sqlite/migrations/010-trigger-adapter-state.d.ts +7 -0
  310. package/dist/runstore/sqlite/migrations/010-trigger-adapter-state.js +17 -0
  311. package/dist/runstore/sqlite/migrations/index.d.ts +21 -0
  312. package/dist/runstore/sqlite/migrations/index.js +78 -0
  313. package/dist/runstore/sqlite/schema.d.ts +20 -1
  314. package/dist/runstore/sqlite/schema.js +277 -1
  315. package/dist/runstore/sqlite/storeOpenError.d.ts +6 -0
  316. package/dist/runstore/sqlite/storeOpenError.js +11 -0
  317. package/dist/runstore/sqlite/storeSchemaError.d.ts +5 -0
  318. package/dist/runstore/sqlite/storeSchemaError.js +8 -0
  319. package/dist/runstore/stageExecution.js +3 -0
  320. package/dist/runstore/storeFilesystem.d.ts +32 -0
  321. package/dist/runstore/storeFilesystem.js +148 -0
  322. package/dist/runstore/trackProjection.js +2 -0
  323. package/dist/runstore/workspaceLayout.d.ts +13 -0
  324. package/dist/runstore/workspaceLayout.js +76 -0
  325. package/dist/runtime/answerResume.d.ts +1 -0
  326. package/dist/runtime/answerResume.js +3 -1
  327. package/dist/runtime/completionCheckRunner.d.ts +15 -1
  328. package/dist/runtime/completionCheckRunner.js +67 -7
  329. package/dist/runtime/containerLimits.d.ts +16 -0
  330. package/dist/runtime/containerLimits.js +84 -0
  331. package/dist/runtime/credentialBinding.d.ts +2 -0
  332. package/dist/runtime/credentialBinding.js +3 -0
  333. package/dist/runtime/credentialMaterialisation.d.ts +28 -0
  334. package/dist/runtime/credentialMaterialisation.js +128 -0
  335. package/dist/runtime/emailSource.d.ts +72 -0
  336. package/dist/runtime/emailSource.js +304 -0
  337. package/dist/runtime/gitCheckoutCapability.js +7 -13
  338. package/dist/runtime/githubPollSource.d.ts +86 -0
  339. package/dist/runtime/githubPollSource.js +284 -0
  340. package/dist/runtime/pipelineRunner.d.ts +40 -1
  341. package/dist/runtime/pipelineRunner.js +181 -17
  342. package/dist/runtime/pipelineScheduler.d.ts +17 -0
  343. package/dist/runtime/pipelineScheduler.js +220 -28
  344. package/dist/runtime/reloadRunCatalog.js +6 -1
  345. package/dist/runtime/repositoryMaterialize.d.ts +47 -0
  346. package/dist/runtime/repositoryMaterialize.js +235 -0
  347. package/dist/runtime/resumeTimedOut.d.ts +1 -1
  348. package/dist/runtime/resumeTimedOut.js +5 -2
  349. package/dist/runtime/runChangeBus.js +9 -0
  350. package/dist/runtime/runDeletion.d.ts +7 -0
  351. package/dist/runtime/runDeletion.js +37 -0
  352. package/dist/runtime/runManager.d.ts +169 -4
  353. package/dist/runtime/runManager.js +1748 -83
  354. package/dist/runtime/runRetentionSweep.d.ts +31 -0
  355. package/dist/runtime/runRetentionSweep.js +328 -0
  356. package/dist/runtime/runRetryCoordinator.d.ts +4 -0
  357. package/dist/runtime/runRetryCoordinator.js +20 -2
  358. package/dist/runtime/runSkills.d.ts +40 -0
  359. package/dist/runtime/runSkills.js +173 -0
  360. package/dist/runtime/scheduleSource.d.ts +38 -0
  361. package/dist/runtime/scheduleSource.js +119 -0
  362. package/dist/runtime/settingsFile.d.ts +5 -0
  363. package/dist/runtime/settingsFile.js +61 -54
  364. package/dist/runtime/stageAttemptBootstrap.d.ts +5 -1
  365. package/dist/runtime/stageAttemptBootstrap.js +250 -27
  366. package/dist/runtime/stageCacheEnv.d.ts +6 -0
  367. package/dist/runtime/stageCacheEnv.js +34 -0
  368. package/dist/runtime/stageConcurrency.d.ts +2 -1
  369. package/dist/runtime/stageConcurrency.js +4 -2
  370. package/dist/runtime/stageEnvironment.d.ts +38 -0
  371. package/dist/runtime/stageEnvironment.js +194 -0
  372. package/dist/runtime/stageHitl.d.ts +1 -0
  373. package/dist/runtime/stageHitl.js +5 -1
  374. package/dist/runtime/stageProcessLauncher.d.ts +20 -0
  375. package/dist/runtime/stageProcessLauncher.js +236 -41
  376. package/dist/runtime/stageRecovery.d.ts +3 -1
  377. package/dist/runtime/stageRecovery.js +19 -4
  378. package/dist/runtime/stageRoots.d.ts +38 -1
  379. package/dist/runtime/stageRoots.js +118 -0
  380. package/dist/runtime/stageRunner.d.ts +6 -0
  381. package/dist/runtime/stageRunner.js +32 -6
  382. package/dist/runtime/stageSecretDecl.d.ts +9 -0
  383. package/dist/runtime/stageSecretDecl.js +102 -0
  384. package/dist/runtime/stageSecrets.d.ts +40 -0
  385. package/dist/runtime/stageSecrets.js +181 -0
  386. package/dist/runtime/stageStreamLog.d.ts +1 -0
  387. package/dist/runtime/stageStreamLog.js +2 -1
  388. package/dist/runtime/stageWorker.d.ts +3 -2
  389. package/dist/runtime/stageWorker.js +58 -10
  390. package/dist/runtime/stageWorkerProtocol.d.ts +5 -0
  391. package/dist/runtime/stageWorkerProtocol.js +8 -0
  392. package/dist/runtime/startPayload.d.ts +32 -0
  393. package/dist/runtime/startPayload.js +59 -0
  394. package/dist/runtime/taskInput.js +14 -5
  395. package/dist/runtime/triggerCatalog.d.ts +6 -0
  396. package/dist/runtime/triggerCatalog.js +24 -0
  397. package/dist/runtime/triggerEventMatch.d.ts +2 -0
  398. package/dist/runtime/triggerEventMatch.js +6 -0
  399. package/dist/runtime/triggerPort.d.ts +14 -0
  400. package/dist/runtime/triggerPort.js +1 -0
  401. package/dist/runtime/triggerRunner.d.ts +17 -0
  402. package/dist/runtime/triggerRunner.js +89 -0
  403. package/dist/runtime/verifiedStageExecution.d.ts +8 -0
  404. package/dist/runtime/verifiedStageExecution.js +16 -1
  405. package/dist/runtime/webhookSignature.d.ts +1 -0
  406. package/dist/runtime/webhookSignature.js +12 -0
  407. package/dist/runtime/workspaceBinding.d.ts +20 -0
  408. package/dist/runtime/workspaceBinding.js +60 -0
  409. package/dist/server/allowedHosts.d.ts +19 -0
  410. package/dist/server/allowedHosts.js +179 -0
  411. package/dist/server/bootstrap.d.ts +42 -2
  412. package/dist/server/bootstrap.js +273 -5
  413. package/dist/server/controlToken.d.ts +47 -0
  414. package/dist/server/controlToken.js +254 -0
  415. package/dist/server/createHttpHost.d.ts +11 -2
  416. package/dist/server/createHttpHost.js +106 -10
  417. package/dist/server/ensureGlobalService.d.ts +5 -1
  418. package/dist/server/ensureGlobalService.js +35 -1
  419. package/dist/server/healthSurfaces.d.ts +6 -0
  420. package/dist/server/healthSurfaces.js +123 -0
  421. package/dist/server/http.d.ts +27 -1
  422. package/dist/server/http.js +2254 -477
  423. package/dist/server/listenHost.d.ts +5 -0
  424. package/dist/server/listenHost.js +56 -0
  425. package/dist/server/mcpHost.d.ts +10 -1
  426. package/dist/server/mcpHost.js +36 -6
  427. package/dist/server/operatorResults.d.ts +2 -2
  428. package/dist/server/operatorResults.js +40 -29
  429. package/dist/server/requestAuthContext.d.ts +12 -0
  430. package/dist/server/requestAuthContext.js +21 -0
  431. package/dist/server/shutdown.d.ts +69 -0
  432. package/dist/server/shutdown.js +219 -0
  433. package/dist/server/triggerRoutes.d.ts +15 -0
  434. package/dist/server/triggerRoutes.js +268 -0
  435. package/dist/tools/askOperator.d.ts +3 -2
  436. package/dist/types/completion.d.ts +3 -0
  437. package/dist/types/pipeline.d.ts +10 -1
  438. package/dist/types/preEmitCheck.d.ts +7 -0
  439. package/dist/types/stage.d.ts +22 -0
  440. package/dist/types/stageflowManifest.d.ts +3 -0
  441. package/dist/types/task.d.ts +8 -0
  442. package/dist/types/trigger.d.ts +18 -0
  443. package/dist/types/trigger.js +1 -0
  444. package/dist/ui/assets/{index-CSsDued1.css → index-Bv4p1aPK.css} +1 -1
  445. package/dist/ui/assets/index-CntB4bZR.js +133 -0
  446. package/dist/ui/index.html +2 -2
  447. package/dist/workshop/autosave.d.ts +58 -0
  448. package/dist/workshop/autosave.js +222 -0
  449. package/dist/workshop/buildStore.d.ts +79 -0
  450. package/dist/workshop/buildStore.js +326 -0
  451. package/dist/workshop/chatTurn.d.ts +232 -0
  452. package/dist/workshop/chatTurn.js +475 -0
  453. package/dist/workshop/modelSettings.d.ts +18 -0
  454. package/dist/workshop/modelSettings.js +44 -0
  455. package/dist/workshop/sessionStore.d.ts +61 -0
  456. package/dist/workshop/sessionStore.js +254 -0
  457. package/dist/workshop/workshopPin.d.ts +72 -0
  458. package/dist/workshop/workshopPin.js +193 -0
  459. package/examples/README.md +99 -0
  460. package/examples/archify-on-pr/README.md +213 -0
  461. package/examples/archify-on-pr/archify-on-pr.pipeline.yaml +19 -0
  462. package/examples/archify-on-pr/archify-on-pr.task.yaml +19 -0
  463. package/examples/archify-on-pr/author-diagrams.yaml +106 -0
  464. package/examples/archify-on-pr/detect-changes.yaml +89 -0
  465. package/examples/browser-session/README.md +77 -0
  466. package/examples/browser-session/browser-session.pipeline.yaml +50 -0
  467. package/examples/browser-session/browser-session.task.yaml +6 -0
  468. package/examples/browser-session/check-login.yaml +22 -0
  469. package/examples/browser-session/fixture-server.mjs +64 -0
  470. package/examples/browser-session/human-login.yaml +31 -0
  471. package/examples/browser-session/merge.yaml +36 -0
  472. package/examples/browser-session/work-a.yaml +41 -0
  473. package/examples/browser-session/work-b.yaml +41 -0
  474. package/examples/ci-validate/README.md +35 -0
  475. package/examples/ci-validate/check.yaml +13 -0
  476. package/examples/ci-validate/ci-demo.pipeline.yaml +4 -0
  477. package/examples/ci-validate/validate.sh +5 -0
  478. package/examples/compliance-review-demo/README.md +125 -0
  479. package/examples/compliance-review-demo/compliance-review-demo.pipeline.yaml +29 -0
  480. package/examples/compliance-review-demo/connect-account.yaml +47 -0
  481. package/examples/compliance-review-demo/map-to-control.yaml +123 -0
  482. package/examples/compliance-review-demo/poll-bucket-config.yaml +103 -0
  483. package/examples/compliance-review-demo/publish-report.yaml +79 -0
  484. package/examples/compliance-review-demo/review-verdict.yaml +122 -0
  485. package/examples/compliance-review-demo/run-compliance-check.yaml +96 -0
  486. package/examples/compliance-review-demo/store-evidence.yaml +143 -0
  487. package/examples/conditional-fork/README.md +89 -0
  488. package/examples/conditional-fork/branch-a.yaml +13 -0
  489. package/examples/conditional-fork/branch-b.yaml +13 -0
  490. package/examples/conditional-fork/decide.yaml +35 -0
  491. package/examples/conditional-fork/fork-demo.pipeline.yaml +20 -0
  492. package/examples/conditional-fork/fork-demo.task.yaml +8 -0
  493. package/examples/context7-mcp/README.md +42 -0
  494. package/examples/context7-mcp/context7-mcp.pipeline.yaml +15 -0
  495. package/examples/context7-mcp/context7-mcp.task.yaml +4 -0
  496. package/examples/context7-mcp/fetch-docs.yaml +52 -0
  497. package/examples/context7-mcp/resolve-library.yaml +31 -0
  498. package/examples/context7-mcp/write-brief.yaml +52 -0
  499. package/examples/feature-loop/README.md +104 -0
  500. package/examples/feature-loop/address-feedback.yaml +148 -0
  501. package/examples/feature-loop/align.yaml +116 -0
  502. package/examples/feature-loop/decompose.yaml +86 -0
  503. package/examples/feature-loop/default-model-hierarchy.task.yaml +58 -0
  504. package/examples/feature-loop/feature-loop.pipeline.yaml +140 -0
  505. package/examples/feature-loop/feature-loop.task.yaml +25 -0
  506. package/examples/feature-loop/implement.yaml +128 -0
  507. package/examples/feature-loop/plan.yaml +111 -0
  508. package/examples/feature-loop/publish.yaml +167 -0
  509. package/examples/feature-loop/review.yaml +146 -0
  510. package/examples/feature-loop/verify.yaml +106 -0
  511. package/examples/feedback-loop/README.md +72 -0
  512. package/examples/feedback-loop/VERIFY.md +208 -0
  513. package/examples/feedback-loop/continue-only.task.yaml +4 -0
  514. package/examples/feedback-loop/exhaust-require-continue.task.yaml +6 -0
  515. package/examples/feedback-loop/exhaust-wait-human.task.yaml +11 -0
  516. package/examples/feedback-loop/feedback-loop-wait-human.pipeline.yaml +23 -0
  517. package/examples/feedback-loop/feedback-loop.pipeline.yaml +23 -0
  518. package/examples/feedback-loop/feedback-loop.task.yaml +4 -0
  519. package/examples/feedback-loop/implement.yaml +14 -0
  520. package/examples/feedback-loop/plan.yaml +14 -0
  521. package/examples/feedback-loop/review.yaml +15 -0
  522. package/examples/feedback-loop/send-back-once.task.yaml +6 -0
  523. package/examples/feedback-loop/submit.yaml +12 -0
  524. package/examples/generic-fan-in/README.md +151 -0
  525. package/examples/generic-fan-in/clarify.yaml +18 -0
  526. package/examples/generic-fan-in/generic-fan-in-accepted.pipeline.yaml +19 -0
  527. package/examples/generic-fan-in/generic-fan-in-accepted.task.yaml +8 -0
  528. package/examples/generic-fan-in/generic-fan-in.pipeline.yaml +18 -0
  529. package/examples/generic-fan-in/generic-fan-in.task.yaml +9 -0
  530. package/examples/generic-fan-in/research.yaml +45 -0
  531. package/examples/generic-fan-in/synthesize.yaml +66 -0
  532. package/examples/generic-fan-in/validation.yaml +45 -0
  533. package/examples/github-release/README.md +78 -0
  534. package/examples/github-release/draft-release-notes.yaml +64 -0
  535. package/examples/github-release/github-release.pipeline.yaml +11 -0
  536. package/examples/github-release/github-release.task.yaml +13 -0
  537. package/examples/github-release/publish-github-release.yaml +64 -0
  538. package/examples/hello-world/README.md +28 -0
  539. package/examples/hello-world/hello.pipeline.yaml +4 -0
  540. package/examples/hello-world/my-task.task.yaml +6 -0
  541. package/examples/hello-world/research.yaml +18 -0
  542. package/examples/mcp-hitl-tour/README.md +113 -0
  543. package/examples/mcp-hitl-tour/draft-angle.yaml +33 -0
  544. package/examples/mcp-hitl-tour/draft-short.yaml +32 -0
  545. package/examples/mcp-hitl-tour/emit-angles.yaml +45 -0
  546. package/examples/mcp-hitl-tour/gather.yaml +31 -0
  547. package/examples/mcp-hitl-tour/intake.yaml +62 -0
  548. package/examples/mcp-hitl-tour/mcp-hitl-tour-exhaust.task.yaml +13 -0
  549. package/examples/mcp-hitl-tour/mcp-hitl-tour.pipeline.yaml +54 -0
  550. package/examples/mcp-hitl-tour/mcp-hitl-tour.task.yaml +16 -0
  551. package/examples/mcp-hitl-tour/polish.yaml +33 -0
  552. package/examples/mcp-hitl-tour/review.yaml +17 -0
  553. package/examples/mcp-hitl-tour/ship.yaml +25 -0
  554. package/examples/oss-issue-contribution/README.md +218 -0
  555. package/examples/oss-issue-contribution/oss-address-review-feedback.yaml +117 -0
  556. package/examples/oss-issue-contribution/oss-approve-contribution.yaml +181 -0
  557. package/examples/oss-issue-contribution/oss-approve-plan.yaml +111 -0
  558. package/examples/oss-issue-contribution/oss-explain-fix.yaml +108 -0
  559. package/examples/oss-issue-contribution/oss-explain-issue.yaml +114 -0
  560. package/examples/oss-issue-contribution/oss-implement-source-fix.yaml +171 -0
  561. package/examples/oss-issue-contribution/oss-investigate-area.yaml +116 -0
  562. package/examples/oss-issue-contribution/oss-issue-contribution.pipeline.yaml +125 -0
  563. package/examples/oss-issue-contribution/oss-issue-intake.yaml +106 -0
  564. package/examples/oss-issue-contribution/oss-plan-investigation.yaml +112 -0
  565. package/examples/oss-issue-contribution/oss-plan-review.yaml +109 -0
  566. package/examples/oss-issue-contribution/oss-reproduce-issue.yaml +140 -0
  567. package/examples/oss-issue-contribution/oss-review-change.yaml +107 -0
  568. package/examples/oss-issue-contribution/oss-verify-fix.yaml +130 -0
  569. package/examples/oss-issue-contribution/oss-write-regression-test.yaml +135 -0
  570. package/examples/oss-issue-contribution/pi-7629.task.yaml +79 -0
  571. package/examples/oss-issue-contribution/pi-8919.task.yaml +75 -0
  572. package/examples/oss-issue-contribution/pydantic-ai-7971.task.yaml +53 -0
  573. package/examples/oss-quick-fix/README.md +65 -0
  574. package/examples/oss-quick-fix/implement-fix.yaml +84 -0
  575. package/examples/oss-quick-fix/oss-quick-fix.pipeline.yaml +35 -0
  576. package/examples/oss-quick-fix/oss-quick-fix.task.yaml +19 -0
  577. package/examples/oss-quick-fix/ship-fix.yaml +81 -0
  578. package/examples/oss-quick-fix/understand-issue.yaml +67 -0
  579. package/examples/oss-quick-fix/verify-fix.yaml +73 -0
  580. package/examples/plan-review/README.md +37 -0
  581. package/examples/plan-review/plan-review-followup.yaml +13 -0
  582. package/examples/plan-review/plan-review.pipeline.yaml +9 -0
  583. package/examples/plan-review/plan-review.task.yaml +9 -0
  584. package/examples/plan-review/plan-review.yaml +25 -0
  585. package/examples/playwright-mcp/README.md +39 -0
  586. package/examples/playwright-mcp/playwright-mcp.pipeline.yaml +5 -0
  587. package/examples/playwright-mcp/playwright-mcp.task.yaml +4 -0
  588. package/examples/playwright-mcp/screenshot.yaml +40 -0
  589. package/examples/retry-tour/README.md +75 -0
  590. package/examples/retry-tour/retry-tour.pipeline.yaml +122 -0
  591. package/examples/retry-tour/retry-tour.task.yaml +9 -0
  592. package/examples/route-if-tour/README.md +202 -0
  593. package/examples/route-if-tour/route-if-tour.pipeline.yaml +950 -0
  594. package/examples/route-if-tour/route-if-tour.task.yaml +4 -0
  595. package/examples/route-wiring-smoke-test/01-core-routing.pipeline.yaml +168 -0
  596. package/examples/route-wiring-smoke-test/02-on-gating.pipeline.yaml +44 -0
  597. package/examples/route-wiring-smoke-test/03-two-entry-points.pipeline.yaml +28 -0
  598. package/examples/route-wiring-smoke-test/04-entry-false-equivalent-to-omitted.pipeline.yaml +27 -0
  599. package/examples/route-wiring-smoke-test/05-fork-choice.pipeline.yaml +110 -0
  600. package/examples/route-wiring-smoke-test/06-fan-out-fan-in.pipeline.yaml +82 -0
  601. package/examples/route-wiring-smoke-test/07-loop-human-decision.pipeline.yaml +82 -0
  602. package/examples/route-wiring-smoke-test/08-skip-fallback-and-multi-on-gate.pipeline.yaml +77 -0
  603. package/examples/route-wiring-smoke-test/09-multi-loop-targets.pipeline.yaml +102 -0
  604. package/examples/route-wiring-smoke-test/10-uses-dialect-fork.pipeline.yaml +23 -0
  605. package/examples/route-wiring-smoke-test/11-sequential-io-handoff.pipeline.yaml +37 -0
  606. package/examples/route-wiring-smoke-test/12-complex-io-schemas.pipeline.yaml +138 -0
  607. package/examples/route-wiring-smoke-test/13-ref-io-handoff.pipeline.yaml +84 -0
  608. package/examples/route-wiring-smoke-test/14-if-eq-gating.pipeline.yaml +50 -0
  609. package/examples/route-wiring-smoke-test/15-if-all-gated.pipeline.yaml +37 -0
  610. package/examples/route-wiring-smoke-test/16-if-composition.pipeline.yaml +65 -0
  611. package/examples/route-wiring-smoke-test/17-if-join.pipeline.yaml +80 -0
  612. package/examples/route-wiring-smoke-test/README.md +377 -0
  613. package/examples/route-wiring-smoke-test/rejected/01-reject-legacy-needs.pipeline.yaml +26 -0
  614. package/examples/route-wiring-smoke-test/rejected/02-reject-legacy-fork.pipeline.yaml +25 -0
  615. package/examples/route-wiring-smoke-test/rejected/03-reject-legacy-feedback-loop.pipeline.yaml +30 -0
  616. package/examples/route-wiring-smoke-test/rejected/04-reject-missing-entry.pipeline.yaml +24 -0
  617. package/examples/route-wiring-smoke-test/rejected/05-reject-unreachable.pipeline.yaml +35 -0
  618. package/examples/route-wiring-smoke-test/rejected/06-reject-allow-none-without-route-select.pipeline.yaml +37 -0
  619. package/examples/route-wiring-smoke-test/rejected/07-reject-cycle.pipeline.yaml +27 -0
  620. package/examples/route-wiring-smoke-test/rejected/08-reject-loop-non-ancestor.pipeline.yaml +42 -0
  621. package/examples/route-wiring-smoke-test/rejected/09-reject-route-select-leaf.pipeline.yaml +26 -0
  622. package/examples/route-wiring-smoke-test/rejected/12-reject-loop-replay-unsafe.pipeline.yaml +55 -0
  623. package/examples/route-wiring-smoke-test/rejected/13-reject-multiple-loop-entries.pipeline.yaml +51 -0
  624. package/examples/route-wiring-smoke-test/rejected/14-reject-duplicate-stage-id.pipeline.yaml +35 -0
  625. package/examples/route-wiring-smoke-test/rejected/15-reject-unknown-route-target.pipeline.yaml +15 -0
  626. package/examples/route-wiring-smoke-test/rejected/16-reject-malformed-route-item.pipeline.yaml +15 -0
  627. package/examples/route-wiring-smoke-test/rejected/17-reject-io-incompatible.pipeline.yaml +33 -0
  628. package/examples/route-wiring-smoke-test/rejected/18-reject-nested-io.pipeline.yaml +51 -0
  629. package/examples/route-wiring-smoke-test/rejected/19-reject-array-item-io.pipeline.yaml +55 -0
  630. package/examples/route-wiring-smoke-test/rejected/20-reject-closed-io.pipeline.yaml +36 -0
  631. package/examples/route-wiring-smoke-test/rejected/21-reject-ref-io.pipeline.yaml +42 -0
  632. package/examples/route-wiring-smoke-test/rejected/22-reject-if-unknown-field.pipeline.yaml +33 -0
  633. package/examples/route-wiring-smoke-test/rejected/23-reject-if-optional-field.pipeline.yaml +32 -0
  634. package/examples/route-wiring-smoke-test/rejected/24-reject-if-empty-all.pipeline.yaml +31 -0
  635. package/examples/route-wiring-smoke-test/rejected/25-reject-if-gt-on-string.pipeline.yaml +33 -0
  636. package/examples/route-wiring-smoke-test/rejected/26-reject-if-optional-nested.pipeline.yaml +36 -0
  637. package/examples/route-wiring-smoke-test/rejected/27-reject-if-on-loop.pipeline.yaml +63 -0
  638. package/examples/route-wiring-smoke-test/rejected/29-reject-if-on-failed.pipeline.yaml +46 -0
  639. package/examples/route-wiring-smoke-test/smoke-test.task.yaml +4 -0
  640. package/examples/route-wiring-smoke-test/stages/branch-a.yaml +12 -0
  641. package/examples/route-wiring-smoke-test/stages/branch-b.yaml +12 -0
  642. package/examples/route-wiring-smoke-test/stages/decide.yaml +12 -0
  643. package/examples/route-wiring-smoke-test/stages/finish.yaml +12 -0
  644. package/examples/ship-feature/README.md +190 -0
  645. package/examples/ship-feature/feat-address-feedback.yaml +119 -0
  646. package/examples/ship-feature/feat-implement.yaml +180 -0
  647. package/examples/ship-feature/feat-plan.yaml +120 -0
  648. package/examples/ship-feature/feat-review.yaml +109 -0
  649. package/examples/ship-feature/feat-ship.yaml +203 -0
  650. package/examples/ship-feature/ship-feature.pipeline.yaml +44 -0
  651. package/examples/ship-feature/ship-feature.task.yaml +78 -0
  652. package/examples/stage-mcp/README.md +145 -0
  653. package/examples/stage-mcp/echo-mcp.mjs +20 -0
  654. package/examples/stage-mcp/stage-mcp-dead.pipeline.yaml +5 -0
  655. package/examples/stage-mcp/stage-mcp-dead.task.yaml +3 -0
  656. package/examples/stage-mcp/stage-mcp-missing-var.pipeline.yaml +5 -0
  657. package/examples/stage-mcp/stage-mcp-missing-var.task.yaml +3 -0
  658. package/examples/stage-mcp/stage-mcp.pipeline.yaml +5 -0
  659. package/examples/stage-mcp/stage-mcp.task.yaml +4 -0
  660. package/examples/stage-mcp/use-echo.yaml +20 -0
  661. package/examples/stageflow.yaml +51 -0
  662. package/examples/verify-tour/README.md +80 -0
  663. package/examples/verify-tour/verify-tour.pipeline.yaml +162 -0
  664. package/examples/verify-tour/verify-tour.task.yaml +11 -0
  665. package/package.json +9 -1
  666. package/skills/stageflow/SKILL.md +6 -6
  667. package/skills/stageflow/references/control-surface.md +13 -5
  668. package/skills/stageflow/scripts/detect-host.mjs +14 -1
  669. package/skills/stageflow/scripts/detect-host.test.mjs +29 -1
  670. package/skills/stageflow-author/SKILL.md +4 -3
  671. package/skills/stageflow-author/references/catalog-mapping.md +16 -0
  672. package/skills/stageflow-author/references/catalog-write-conventions.md +1 -1
  673. package/skills/stageflow-author/references/stage-prompt-template.md +4 -4
  674. package/skills/stageflow-run/SKILL.md +22 -12
  675. package/skills/stageflow-run/references/mcp-call.md +9 -1
  676. package/skills/stageflow-run/references/task-and-pipeline-selection.md +19 -6
  677. package/skills/stageflow-run/scripts/mcp-call.mjs +46 -6
  678. package/skills/stageflow-run/scripts/mcp-call.test.mjs +64 -0
  679. package/skills/stageflow-session-capture/references/catalog-authoring.md +2 -0
  680. package/skills/stageflow-setup/SKILL.md +51 -8
  681. package/dist/ui/assets/index-Da5EHU6M.js +0 -119
@@ -0,0 +1,254 @@
1
+ import { createHash, timingSafeEqual } from "node:crypto";
2
+ import { readSecretFromEnvOrFile } from "../config/secretFromEnvOrFile.js";
3
+ import { isLoopbackHostname } from "./allowedHosts.js";
4
+ const CONTROL_TOKEN_PREFIX = "STAGEFLOW_CONTROL_TOKEN_";
5
+ const DEFAULT_CALLER_ID = "default";
6
+ function digest(value) {
7
+ return createHash("sha256").update(value).digest();
8
+ }
9
+ function validateToken(token, label) {
10
+ if (!token || /\s/.test(token) || token.length < 32) {
11
+ throw new Error(`${label} requires a token of at least 32 characters with no whitespace`);
12
+ }
13
+ return token;
14
+ }
15
+ function normalizeCallerId(name) {
16
+ return name.toLowerCase();
17
+ }
18
+ /** Parse named drive callers from STAGEFLOW_CONTROL_TOKEN_<NAME> / _FILE. */
19
+ export function listNamedControlTokenEnvKeys(env = process.env) {
20
+ const out = [];
21
+ const seen = new Set();
22
+ for (const key of Object.keys(env)) {
23
+ if (!key.startsWith(CONTROL_TOKEN_PREFIX))
24
+ continue;
25
+ let namePart;
26
+ let isFile = false;
27
+ if (key.endsWith("_FILE")) {
28
+ namePart = key.slice(CONTROL_TOKEN_PREFIX.length, -"_FILE".length);
29
+ isFile = true;
30
+ }
31
+ else {
32
+ namePart = key.slice(CONTROL_TOKEN_PREFIX.length);
33
+ }
34
+ if (namePart === "" || namePart === "FILE")
35
+ continue;
36
+ const caller_id = normalizeCallerId(namePart);
37
+ const dedupe = `${caller_id}:${isFile ? "file" : "plain"}`;
38
+ if (seen.has(dedupe))
39
+ continue;
40
+ seen.add(dedupe);
41
+ out.push({ envKey: key, caller_id, isFile });
42
+ }
43
+ return out;
44
+ }
45
+ function assertUniqueDigests(entries) {
46
+ const byHex = new Map();
47
+ for (const entry of entries) {
48
+ const hex = entry.digest.toString("hex");
49
+ const prior = byHex.get(hex);
50
+ if (prior !== undefined) {
51
+ throw new Error(`Duplicate control token digests for ${prior} and ${entry.label}`);
52
+ }
53
+ byHex.set(hex, entry.label);
54
+ }
55
+ }
56
+ export function loadControlTokens(env = process.env) {
57
+ const driveRaw = readSecretFromEnvOrFile(env, "STAGEFLOW_CONTROL_TOKEN");
58
+ const readRaw = readSecretFromEnvOrFile(env, "STAGEFLOW_READ_TOKEN");
59
+ const callerBases = new Map();
60
+ for (const named of listNamedControlTokenEnvKeys(env)) {
61
+ const base = named.isFile
62
+ ? named.envKey.slice(0, -"_FILE".length)
63
+ : named.envKey;
64
+ if (!callerBases.has(named.caller_id)) {
65
+ callerBases.set(named.caller_id, base);
66
+ }
67
+ }
68
+ const namedDrive = [];
69
+ for (const [caller_id, base] of callerBases) {
70
+ const raw = readSecretFromEnvOrFile(env, base);
71
+ if (raw === undefined)
72
+ continue;
73
+ namedDrive.push({
74
+ caller_id,
75
+ digest: digest(validateToken(raw, base)),
76
+ });
77
+ }
78
+ const driveDigest = driveRaw
79
+ ? digest(validateToken(driveRaw, "STAGEFLOW_CONTROL_TOKEN"))
80
+ : undefined;
81
+ const readDigest = readRaw
82
+ ? digest(validateToken(readRaw, "STAGEFLOW_READ_TOKEN"))
83
+ : undefined;
84
+ const uniqueness = [];
85
+ if (driveDigest) {
86
+ uniqueness.push({ label: "STAGEFLOW_CONTROL_TOKEN", digest: driveDigest });
87
+ }
88
+ if (readDigest) {
89
+ uniqueness.push({ label: "STAGEFLOW_READ_TOKEN", digest: readDigest });
90
+ }
91
+ for (const named of namedDrive) {
92
+ uniqueness.push({
93
+ label: `STAGEFLOW_CONTROL_TOKEN_${named.caller_id}`,
94
+ digest: named.digest,
95
+ });
96
+ }
97
+ assertUniqueDigests(uniqueness);
98
+ return { driveDigest, readDigest, namedDrive };
99
+ }
100
+ /** Plain bearer for CLI→host calls. Drive preferred; read only for GET/HEAD. */
101
+ export function resolveClientBearerToken(method, env = process.env) {
102
+ const driveRaw = readSecretFromEnvOrFile(env, "STAGEFLOW_CONTROL_TOKEN");
103
+ if (driveRaw !== undefined) {
104
+ return validateToken(driveRaw, "STAGEFLOW_CONTROL_TOKEN");
105
+ }
106
+ if (method === "GET" || method === "HEAD") {
107
+ const readRaw = readSecretFromEnvOrFile(env, "STAGEFLOW_READ_TOKEN");
108
+ if (readRaw !== undefined) {
109
+ return validateToken(readRaw, "STAGEFLOW_READ_TOKEN");
110
+ }
111
+ }
112
+ return undefined;
113
+ }
114
+ export function clientAuthorizationHeaders(method, env = process.env) {
115
+ const token = resolveClientBearerToken(method, env);
116
+ if (token === undefined)
117
+ return {};
118
+ return { Authorization: `Bearer ${token}` };
119
+ }
120
+ export function hasDriveToken(tokens) {
121
+ return tokens.driveDigest !== undefined || tokens.namedDrive.length > 0;
122
+ }
123
+ export function hasAnyToken(tokens) {
124
+ return (tokens.driveDigest !== undefined ||
125
+ tokens.readDigest !== undefined ||
126
+ tokens.namedDrive.length > 0);
127
+ }
128
+ export function isLoopbackBind(bind) {
129
+ const normalized = bind.startsWith("[") && bind.endsWith("]")
130
+ ? bind.slice(1, -1)
131
+ : bind;
132
+ return isLoopbackHostname(normalized);
133
+ }
134
+ export function refuseBindMessage(bind) {
135
+ return `Refusing to start: Stageflow is configured to bind ${bind}, which is reachable from outside this
136
+ machine, but no control token is set.
137
+
138
+ Anyone who can reach this port could start a pipeline run, and pipeline stages execute arbitrary
139
+ shell commands with this process's environment — including provider API keys.
140
+
141
+ Set one of:
142
+ STAGEFLOW_CONTROL_TOKEN=<at least 32 characters>
143
+ STAGEFLOW_CONTROL_TOKEN_FILE=/path/to/secret
144
+ STAGEFLOW_CONTROL_TOKEN_<NAME>=<at least 32 characters>
145
+
146
+ Or bind to loopback instead:
147
+ --host 127.0.0.1 (or unset STAGEFLOW_BIND)`;
148
+ }
149
+ export function assertBindAllowed(bind, tokens) {
150
+ if (!isLoopbackBind(bind) && !hasDriveToken(tokens)) {
151
+ throw new BindRefusedError(refuseBindMessage(bind));
152
+ }
153
+ }
154
+ export class BindRefusedError extends Error {
155
+ exitCode = 1;
156
+ constructor(message) {
157
+ super(message);
158
+ this.name = "BindRefusedError";
159
+ }
160
+ }
161
+ /**
162
+ * `POST /api/triggers/:id/webhook` is exempt from bearer auth: an external
163
+ * webhook sender can't present Stageflow's own control token, and the route
164
+ * authenticates the request itself via per-trigger HMAC signature
165
+ * verification instead. It still goes through `assertAllowedHttpAccess`
166
+ * (loopback/allowed-hosts gating) the same as every other `/api/*` route —
167
+ * only the bearer-token requirement is carved out here.
168
+ */
169
+ function isTriggerWebhookRoute(method, pathname) {
170
+ return method === "POST" && /^\/api\/triggers\/[^/]+\/webhook$/.test(pathname);
171
+ }
172
+ export function requiredScopeFor(method, pathname) {
173
+ if (pathname === "/mcp")
174
+ return "drive";
175
+ if (!pathname.startsWith("/api/"))
176
+ return null;
177
+ if (isTriggerWebhookRoute(method, pathname))
178
+ return null;
179
+ if (pathname === "/api/backup" || pathname.startsWith("/api/backup/")) {
180
+ return "drive";
181
+ }
182
+ if (method === "GET" || method === "HEAD")
183
+ return "read";
184
+ return "drive";
185
+ }
186
+ /**
187
+ * Digest once; timingSafeEqual against every loaded digest (no early-return oracle).
188
+ * Named control tokens are drive-only; READ stays the singular global token.
189
+ */
190
+ export function authenticateBearer(tokens, authorization) {
191
+ const match = authorization?.match(/^Bearer ([^\s]+)$/i);
192
+ if (!match)
193
+ return undefined;
194
+ const hash = digest(match[1]);
195
+ const candidates = [];
196
+ if (tokens.driveDigest) {
197
+ candidates.push({
198
+ scope: "drive",
199
+ caller_id: DEFAULT_CALLER_ID,
200
+ digest: tokens.driveDigest,
201
+ });
202
+ }
203
+ if (tokens.readDigest) {
204
+ candidates.push({
205
+ scope: "read",
206
+ caller_id: DEFAULT_CALLER_ID,
207
+ digest: tokens.readDigest,
208
+ });
209
+ }
210
+ for (const named of tokens.namedDrive) {
211
+ candidates.push({
212
+ scope: "drive",
213
+ caller_id: named.caller_id,
214
+ digest: named.digest,
215
+ });
216
+ }
217
+ let matched;
218
+ for (const candidate of candidates) {
219
+ if (timingSafeEqual(candidate.digest, hash)) {
220
+ matched = { scope: candidate.scope, caller_id: candidate.caller_id };
221
+ }
222
+ }
223
+ return matched;
224
+ }
225
+ function scopeSatisfies(granted, required) {
226
+ if (required === "read")
227
+ return granted === "read" || granted === "drive";
228
+ return granted === "drive";
229
+ }
230
+ export function enforceBearerAuth(tokens, req, res, required) {
231
+ if (!hasAnyToken(tokens))
232
+ return { ok: true, auth: undefined };
233
+ const granted = authenticateBearer(tokens, req.headers.authorization);
234
+ if (granted === undefined) {
235
+ const payload = JSON.stringify({ error: "Unauthorized" });
236
+ res.writeHead(401, {
237
+ "Content-Type": "application/json; charset=utf-8",
238
+ "Content-Length": Buffer.byteLength(payload),
239
+ "WWW-Authenticate": "Bearer",
240
+ });
241
+ res.end(payload);
242
+ return { ok: false };
243
+ }
244
+ if (!scopeSatisfies(granted.scope, required)) {
245
+ const payload = JSON.stringify({ error: "Forbidden" });
246
+ res.writeHead(403, {
247
+ "Content-Type": "application/json; charset=utf-8",
248
+ "Content-Length": Buffer.byteLength(payload),
249
+ });
250
+ res.end(payload);
251
+ return { ok: false };
252
+ }
253
+ return { ok: true, auth: granted };
254
+ }
@@ -3,7 +3,11 @@ import type { RunStore } from "../runstore/port.js";
3
3
  import type { RunManager } from "../runtime/runManager.js";
4
4
  import type { RunChangeBus } from "../runtime/runChangeBus.js";
5
5
  import type { StageflowHostBootstrap } from "./bootstrap.js";
6
+ import { type AllowedHosts } from "./allowedHosts.js";
7
+ import { requiredScopeFor, type ControlTokens } from "./controlToken.js";
6
8
  export declare const DEFAULT_PORT = 3847;
9
+ export declare const DEFAULT_REQUEST_TIMEOUT_MS = 60000;
10
+ export declare const DEFAULT_MAX_CONNECTIONS = 256;
7
11
  export declare function json(res: ServerResponse, status: number, body: unknown): void;
8
12
  export type HttpHostRouteContext = {
9
13
  req: IncomingMessage;
@@ -19,6 +23,10 @@ export type CreateHttpHostOptions = {
19
23
  host: string;
20
24
  port: number;
21
25
  routes: (ctx: HttpHostRouteContext) => Promise<boolean | void>;
26
+ allowedHosts?: AllowedHosts;
27
+ controlTokens?: ControlTokens;
28
+ requestTimeoutMs?: number;
29
+ maxConnections?: number;
22
30
  };
23
31
  export type HttpHostEnvelope = {
24
32
  server: Server;
@@ -26,9 +34,10 @@ export type HttpHostEnvelope = {
26
34
  host: string;
27
35
  url: string;
28
36
  mcpUrl: string;
29
- manager: RunManager;
30
- store: RunStore;
37
+ manager?: RunManager;
38
+ store?: RunStore;
31
39
  runChangeBus: RunChangeBus;
32
40
  mcpStateless: boolean;
33
41
  };
34
42
  export declare function createHttpHost(options: CreateHttpHostOptions): Promise<HttpHostEnvelope>;
43
+ export { requiredScopeFor };
@@ -1,6 +1,14 @@
1
1
  import { createServer, } from "node:http";
2
- import { localhostHostValidation, localhostOriginValidation, } from "@modelcontextprotocol/node";
2
+ import { logger as rootLogger } from "../logging/logger.js";
3
+ import { assertAllowedHttpAccess, resolveAllowedHosts, } from "./allowedHosts.js";
4
+ import { enforceBearerAuth, loadControlTokens, requiredScopeFor, } from "./controlToken.js";
5
+ import { requestAuthFromBearer, runWithRequestAuth, } from "./requestAuthContext.js";
6
+ import { advertisedHost } from "./listenHost.js";
7
+ import { handleLivez, handleReadyz } from "./healthSurfaces.js";
3
8
  export const DEFAULT_PORT = 3847;
9
+ export const DEFAULT_REQUEST_TIMEOUT_MS = 60_000;
10
+ export const DEFAULT_MAX_CONNECTIONS = 256;
11
+ const log = rootLogger.child({ component: "http" });
4
12
  export function json(res, status, body) {
5
13
  const payload = JSON.stringify(body);
6
14
  res.writeHead(status, {
@@ -9,31 +17,101 @@ export function json(res, status, body) {
9
17
  });
10
18
  res.end(payload);
11
19
  }
20
+ function resolveRequestTimeoutMs(env = process.env) {
21
+ const raw = env.STAGEFLOW_REQUEST_TIMEOUT_MS;
22
+ if (raw === undefined || raw.trim() === "")
23
+ return DEFAULT_REQUEST_TIMEOUT_MS;
24
+ const n = Number(raw);
25
+ if (!Number.isFinite(n) || n < 0)
26
+ return DEFAULT_REQUEST_TIMEOUT_MS;
27
+ return n;
28
+ }
29
+ function resolveMaxConnections(env = process.env) {
30
+ const raw = env.STAGEFLOW_MAX_CONNECTIONS;
31
+ if (raw === undefined || raw.trim() === "")
32
+ return DEFAULT_MAX_CONNECTIONS;
33
+ const n = Number(raw);
34
+ if (!Number.isFinite(n) || n <= 0)
35
+ return DEFAULT_MAX_CONNECTIONS;
36
+ return Math.floor(n);
37
+ }
12
38
  export async function createHttpHost(options) {
13
39
  const { boot, host, port, routes } = options;
14
- const validateMcpHost = localhostHostValidation();
15
- const validateMcpOrigin = localhostOriginValidation();
40
+ const allowedHosts = options.allowedHosts ?? resolveAllowedHosts();
41
+ const controlTokens = options.controlTokens ?? loadControlTokens();
42
+ const requestTimeoutMs = options.requestTimeoutMs ?? resolveRequestTimeoutMs();
43
+ const maxConnections = options.maxConnections ?? resolveMaxConnections();
16
44
  const server = createServer(async (req, res) => {
17
45
  const method = req.method ?? "GET";
18
- const url = new URL(req.url ?? "/", `http://${host}:${port}`);
46
+ const url = new URL(req.url ?? "/", `http://${advertisedHost(host)}:${port}`);
19
47
  const pathname = url.pathname;
48
+ if ((pathname === "/livez" || pathname === "/readyz") &&
49
+ (method === "GET" || method === "HEAD")) {
50
+ if (!assertAllowedHttpAccess(allowedHosts, req, res))
51
+ return;
52
+ if (pathname === "/livez") {
53
+ handleLivez(req, res);
54
+ return;
55
+ }
56
+ await handleReadyz(req, res, boot);
57
+ return;
58
+ }
20
59
  if (pathname === "/api/a2a/status" && method === "GET") {
60
+ if (!assertAllowedHttpAccess(allowedHosts, req, res))
61
+ return;
62
+ if (boot.serveBlocked !== undefined) {
63
+ json(res, 503, {
64
+ error: boot.serveBlocked.reason,
65
+ code: boot.serveBlocked.code,
66
+ });
67
+ return;
68
+ }
69
+ const a2aAuth = enforceBearerAuth(controlTokens, req, res, "read");
70
+ if (!a2aAuth.ok)
71
+ return;
21
72
  json(res, 200, boot.a2a?.status ?? { state: "disabled" });
22
73
  return;
23
74
  }
75
+ if (boot.serveBlocked !== undefined) {
76
+ const a2aOwned = pathname === "/a2a" ||
77
+ pathname === "/.well-known/agent-card.json" ||
78
+ pathname.startsWith("/a2a/contracts/") ||
79
+ pathname.startsWith("/a2a/artifacts/");
80
+ if (a2aOwned) {
81
+ if (!assertAllowedHttpAccess(allowedHosts, req, res))
82
+ return;
83
+ json(res, 503, {
84
+ error: boot.serveBlocked.reason,
85
+ code: boot.serveBlocked.code,
86
+ });
87
+ return;
88
+ }
89
+ }
24
90
  if (await boot.a2a?.handle(req, res, pathname))
25
91
  return;
26
92
  if (pathname === "/mcp") {
27
- if (!validateMcpHost(req, res) || !validateMcpOrigin(req, res)) {
93
+ if (!assertAllowedHttpAccess(allowedHosts, req, res))
94
+ return;
95
+ if (boot.serveBlocked !== undefined) {
96
+ json(res, 503, {
97
+ error: boot.serveBlocked.reason,
98
+ code: boot.serveBlocked.code,
99
+ });
28
100
  return;
29
101
  }
102
+ const mcpAuth = enforceBearerAuth(controlTokens, req, res, "drive");
103
+ if (!mcpAuth.ok)
104
+ return;
105
+ res.setTimeout(0);
30
106
  try {
31
- await boot.mcpHandler.handle(req, res);
107
+ await runWithRequestAuth(requestAuthFromBearer(mcpAuth.auth, "mcp"), () => boot.mcpHandler.handle(req, res));
32
108
  }
33
109
  catch (err) {
110
+ const message = err instanceof Error ? err.message : String(err);
111
+ log.error("mcp.handler_error", message);
34
112
  if (!res.headersSent) {
35
113
  res.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
36
- res.end(err instanceof Error ? err.message : String(err));
114
+ res.end(message);
37
115
  }
38
116
  }
39
117
  return;
@@ -51,26 +129,44 @@ export async function createHttpHost(options) {
51
129
  return;
52
130
  json(res, 404, { error: "Not found" });
53
131
  });
54
- server.requestTimeout = 0;
132
+ // requestTimeout bounds receiving the request body (408); idle socket timeout
133
+ // stays 0 so MCP SSE can outlive requestTimeout.
134
+ server.requestTimeout = requestTimeoutMs;
135
+ server.maxConnections = maxConnections;
55
136
  server.on("close", () => {
137
+ boot.stopGcInterval();
138
+ boot.stopScheduleSource();
139
+ boot.stopGithubPollSource();
140
+ boot.stopEmailSource();
56
141
  void boot.mcpHandler.close();
57
142
  });
58
143
  await new Promise((resolve, reject) => {
59
144
  server.listen(port, host, () => resolve());
60
145
  server.on("error", reject);
61
146
  });
147
+ server.on("error", (err) => {
148
+ log.error("host.server_error", err instanceof Error ? err.message : String(err));
149
+ });
62
150
  const address = server.address();
63
151
  const boundPort = address && typeof address !== "string" ? address.port : port;
64
- const url = `http://${host}:${boundPort}`;
152
+ const publicHost = advertisedHost(host);
153
+ const url = `http://${publicHost}:${boundPort}`;
154
+ const mcpUrl = `${url}/mcp`;
155
+ log.info("host.listening", `listening on ${url}`, {
156
+ host,
157
+ port: boundPort,
158
+ mcp_url: mcpUrl,
159
+ });
65
160
  return {
66
161
  server,
67
162
  port: boundPort,
68
163
  host,
69
164
  url,
70
- mcpUrl: `${url}/mcp`,
165
+ mcpUrl,
71
166
  manager: boot.manager,
72
167
  store: boot.store,
73
168
  runChangeBus: boot.runChangeBus,
74
169
  mcpStateless: boot.mcpStateless,
75
170
  };
76
171
  }
172
+ export { requiredScopeFor };
@@ -11,12 +11,15 @@ export type ServiceProbeResult = "up" | "unreachable" | "unhealthy";
11
11
  export declare function hostBaseUrl(): string;
12
12
  export declare function probeGlobalServiceDetailed(): Promise<ServiceProbeResult>;
13
13
  export declare function defaultProbeHost(): Promise<HostProbeResult>;
14
+ export declare const STAGEFLOW_NO_AUTOSTART = "STAGEFLOW_NO_AUTOSTART";
15
+ /** Truthy when set and not empty / `0` / `false` (case-insensitive). */
16
+ export declare function isNoAutostartEnabled(env?: NodeJS.ProcessEnv): boolean;
14
17
  export type EnsureGlobalServiceResult = {
15
18
  ok: true;
16
19
  alreadyRunning: boolean;
17
20
  } | {
18
21
  ok: false;
19
- reason: "port_occupied" | "spawn_failed" | "timed_out";
22
+ reason: "port_occupied" | "spawn_failed" | "timed_out" | "autostart_disabled" | "bind_refused";
20
23
  message: string;
21
24
  };
22
25
  export interface EnsureGlobalServiceOptions {
@@ -26,6 +29,7 @@ export interface EnsureGlobalServiceOptions {
26
29
  spawnFn?: (command: string, args: string[], options: Parameters<typeof spawnDefault>[2]) => ChildProcess;
27
30
  pollIntervalMs?: number;
28
31
  timeoutMs?: number;
32
+ env?: NodeJS.ProcessEnv;
29
33
  }
30
34
  /**
31
35
  * Health-probe-then-spawn: makes sure a global Stageflow service is
@@ -3,7 +3,9 @@ import { openSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { ensureGlobalHome } from "../project/globalHome.js";
6
+ import { assertBindAllowed, BindRefusedError, loadControlTokens, } from "./controlToken.js";
6
7
  import { DEFAULT_PORT } from "./createHttpHost.js";
8
+ import { resolveListenHost } from "./listenHost.js";
7
9
  /**
8
10
  * The well-known port the global service listens on. Overridable via
9
11
  * STAGEFLOW_SERVICE_PORT — real usage never sets this (there's exactly one
@@ -46,7 +48,7 @@ export async function probeGlobalServiceDetailed() {
46
48
  try {
47
49
  let res;
48
50
  try {
49
- res = await fetch(`${hostBaseUrl()}/api/health`, { signal: ac.signal });
51
+ res = await fetch(`${hostBaseUrl()}/livez`, { signal: ac.signal });
50
52
  }
51
53
  catch {
52
54
  return "unreachable";
@@ -70,6 +72,15 @@ export async function defaultProbeHost() {
70
72
  }
71
73
  const DEFAULT_POLL_INTERVAL_MS = 200;
72
74
  const DEFAULT_TIMEOUT_MS = Number(process.env.STAGEFLOW_AUTOSTART_TIMEOUT_MS) || 10_000;
75
+ export const STAGEFLOW_NO_AUTOSTART = "STAGEFLOW_NO_AUTOSTART";
76
+ /** Truthy when set and not empty / `0` / `false` (case-insensitive). */
77
+ export function isNoAutostartEnabled(env = process.env) {
78
+ const raw = env[STAGEFLOW_NO_AUTOSTART];
79
+ if (raw === undefined || raw.trim() === "")
80
+ return false;
81
+ const normalized = raw.trim().toLowerCase();
82
+ return normalized !== "0" && normalized !== "false";
83
+ }
73
84
  function sleep(ms) {
74
85
  return new Promise((resolve) => setTimeout(resolve, ms));
75
86
  }
@@ -83,6 +94,7 @@ export async function ensureGlobalService(options = {}) {
83
94
  const spawnFn = options.spawnFn ?? spawnDefault;
84
95
  const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
85
96
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
97
+ const env = options.env ?? process.env;
86
98
  const initial = await probeHost();
87
99
  if (initial === "up") {
88
100
  return { ok: true, alreadyRunning: true };
@@ -94,6 +106,28 @@ export async function ensureGlobalService(options = {}) {
94
106
  message: `Port ${resolveServicePort()} is already in use by a process that isn't the Stageflow service (its health check didn't return valid health JSON). Free the port and try again.`,
95
107
  };
96
108
  }
109
+ if (isNoAutostartEnabled(env)) {
110
+ const url = hostBaseUrl();
111
+ return {
112
+ ok: false,
113
+ reason: "autostart_disabled",
114
+ message: `No Stageflow Host is answering at ${url}. Autostart is disabled (STAGEFLOW_NO_AUTOSTART). Start the Host with \`sf mcp\`, or in Docker check that the container's entrypoint is running.`,
115
+ };
116
+ }
117
+ try {
118
+ const bind = resolveListenHost({ env });
119
+ assertBindAllowed(bind, loadControlTokens(env));
120
+ }
121
+ catch (err) {
122
+ if (err instanceof BindRefusedError) {
123
+ return {
124
+ ok: false,
125
+ reason: "bind_refused",
126
+ message: err.message,
127
+ };
128
+ }
129
+ throw err;
130
+ }
97
131
  const cliEntry = options.cliEntry ?? process.argv[1] ?? "";
98
132
  const globalHome = ensureGlobalHome();
99
133
  const logPath = path.join(globalHome, "service.log");
@@ -0,0 +1,6 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import type { StageflowHostBootstrap } from "./bootstrap.js";
3
+ export declare function handleLivez(_req: IncomingMessage, res: ServerResponse): void;
4
+ export declare function handleReadyz(_req: IncomingMessage, res: ServerResponse, boot: StageflowHostBootstrap): Promise<void>;
5
+ export declare function buildRichHealthPayload(boot: StageflowHostBootstrap): Promise<Record<string, unknown>>;
6
+ export declare function handleApiHealth(_req: IncomingMessage, res: ServerResponse, boot: StageflowHostBootstrap): Promise<void>;
@@ -0,0 +1,123 @@
1
+ import { getAuthStatus, } from "../agent/providerAuth.js";
2
+ import { PACKAGE_VERSION, BUILD_SHA } from "../package-meta.js";
3
+ import { globalStageflowHome } from "../project/globalHome.js";
4
+ import { redactHostConfig } from "../config/hostConfig.js";
5
+ import { resolveCatalogRoots } from "../config/resolveCatalogRoots.js";
6
+ import { json } from "./createHttpHost.js";
7
+ import { livezBody, probeGitVersion, readSchemaHealth, runReadyzChecks, } from "../diagnostics/checks.js";
8
+ import { buildEgressHealth } from "../net/proxy.js";
9
+ import { toolchainHealthMap } from "../preflight/toolchain.js";
10
+ function bootProvidersSnapshot(providerBoot) {
11
+ return providerBoot
12
+ ? {
13
+ configured: providerBoot.configured,
14
+ failures: providerBoot.failures,
15
+ }
16
+ : { configured: [], failures: [] };
17
+ }
18
+ async function providersLiveSummary(cwd, authCtx) {
19
+ const note = "Live auth inspect — prefer list_providers / sf providers status. boot_providers is Host boot env only and may be empty when CLI credentials are configured.";
20
+ if (authCtx === undefined) {
21
+ return { configured: [], note };
22
+ }
23
+ try {
24
+ const status = await getAuthStatus(cwd, undefined, authCtx);
25
+ const rows = Array.isArray(status) ? status : [status];
26
+ return {
27
+ configured: rows.filter((r) => r.configured).map((r) => r.providerId),
28
+ note,
29
+ };
30
+ }
31
+ catch {
32
+ return { configured: [], note };
33
+ }
34
+ }
35
+ export function handleLivez(_req, res) {
36
+ json(res, 200, livezBody());
37
+ }
38
+ export async function handleReadyz(_req, res, boot) {
39
+ if (boot.serveBlocked !== undefined) {
40
+ json(res, 503, {
41
+ ready: false,
42
+ code: boot.serveBlocked.code,
43
+ error: boot.serveBlocked.reason,
44
+ checks: {
45
+ store_openable: false,
46
+ home_writable: false,
47
+ migrations_complete: false,
48
+ git_present: false,
49
+ },
50
+ });
51
+ return;
52
+ }
53
+ if (boot.store === undefined) {
54
+ json(res, 503, {
55
+ ready: false,
56
+ error: "store unavailable",
57
+ checks: {
58
+ store_openable: false,
59
+ home_writable: false,
60
+ migrations_complete: false,
61
+ git_present: false,
62
+ },
63
+ });
64
+ return;
65
+ }
66
+ const result = await runReadyzChecks({
67
+ store: boot.store,
68
+ homeDir: globalStageflowHome(),
69
+ });
70
+ json(res, result.ready ? 200 : 503, result);
71
+ }
72
+ export async function buildRichHealthPayload(boot) {
73
+ if (boot.manager === undefined || boot.store === undefined) {
74
+ throw new Error("buildRichHealthPayload requires store and manager");
75
+ }
76
+ const capacity = await boot.manager.getHealthWithDisk();
77
+ const gitVersion = await probeGitVersion();
78
+ const schema = readSchemaHealth(boot.store);
79
+ const catalogRoots = await resolveCatalogRoots({
80
+ store: boot.store,
81
+ bootCwd: boot.cwd,
82
+ });
83
+ const boot_providers = bootProvidersSnapshot(boot.providerBoot);
84
+ const providers_live = await providersLiveSummary(boot.cwd, boot.providerAuthContext);
85
+ const payload = {
86
+ ...capacity,
87
+ capacity: {
88
+ global: {
89
+ activeRunIds: capacity.activeRunIds,
90
+ activeCount: capacity.activeCount,
91
+ maxConcurrent: capacity.maxConcurrent,
92
+ slotsAvailable: capacity.slotsAvailable,
93
+ },
94
+ per_project: boot.manager.getPerProjectCapacity(),
95
+ },
96
+ version: PACKAGE_VERSION,
97
+ build_sha: BUILD_SHA,
98
+ stageflow_home: globalStageflowHome(),
99
+ schema,
100
+ git_version: gitVersion ?? null,
101
+ toolchain: toolchainHealthMap(),
102
+ store_filesystem: boot.storeFilesystem ?? null,
103
+ egress: buildEgressHealth(),
104
+ catalog_roots: catalogRoots.map((r) => ({
105
+ project_root: r.project_root,
106
+ kind: r.kind,
107
+ read_only: r.read_only,
108
+ })),
109
+ boot_providers,
110
+ providers_live,
111
+ providers: boot_providers,
112
+ };
113
+ if (boot.hostConfig) {
114
+ payload.config = redactHostConfig(boot.hostConfig);
115
+ }
116
+ if (boot.a2a?.status) {
117
+ payload.a2a = boot.a2a.status;
118
+ }
119
+ return payload;
120
+ }
121
+ export async function handleApiHealth(_req, res, boot) {
122
+ json(res, 200, await buildRichHealthPayload(boot));
123
+ }