@treeseed/sdk 0.10.28 → 0.12.4

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 (509) hide show
  1. package/README.md +213 -7
  2. package/dist/agent-capacity.d.ts +916 -0
  3. package/dist/agent-capacity.js +1600 -0
  4. package/dist/agent-tools.d.ts +37 -0
  5. package/dist/agent-tools.js +301 -0
  6. package/dist/api/app.d.ts +1 -1
  7. package/dist/api/auth/d1-database.d.ts +2 -2
  8. package/dist/api/auth/d1-provider.d.ts +4 -4
  9. package/dist/api/auth/d1-store.d.ts +2 -2
  10. package/dist/api/auth/memory-provider.d.ts +1 -1
  11. package/dist/api/auth/tokens.d.ts +1 -1
  12. package/dist/api/config.d.ts +3 -1
  13. package/dist/api/config.js +34 -10
  14. package/dist/api/http.d.ts +2 -2
  15. package/dist/api/index.d.ts +10 -10
  16. package/dist/api/index.js +4 -2
  17. package/dist/api/operations-routes.d.ts +3 -3
  18. package/dist/api/operations.d.ts +1 -1
  19. package/dist/api/providers.d.ts +1 -1
  20. package/dist/api/providers.js +21 -4
  21. package/dist/api/railway.d.ts +3 -2
  22. package/dist/api/sdk-dispatch.d.ts +4 -4
  23. package/dist/api/sdk-routes.d.ts +2 -2
  24. package/dist/api/templates.d.ts +2 -2
  25. package/dist/api/types.d.ts +3 -2
  26. package/dist/capacity-provider.d.ts +118 -59
  27. package/dist/capacity-provider.js +235 -43
  28. package/dist/capacity.d.ts +2 -2
  29. package/dist/cli-tools.d.ts +1 -1
  30. package/dist/content-operations.d.ts +97 -0
  31. package/dist/content-operations.js +250 -0
  32. package/dist/content-store.d.ts +4 -4
  33. package/dist/control-plane-client.d.ts +13 -18
  34. package/dist/control-plane-client.js +8 -86
  35. package/dist/control-plane.d.ts +3 -2
  36. package/dist/control-plane.js +38 -13
  37. package/dist/copilot.d.ts +3 -0
  38. package/dist/copilot.js +16 -1
  39. package/dist/d1-http.d.ts +1 -1
  40. package/dist/d1-store.d.ts +32 -57
  41. package/dist/d1-store.js +115 -318
  42. package/dist/db/d1.d.ts +1 -232
  43. package/dist/db/index.d.ts +3 -3
  44. package/dist/db/market-schema.d.ts +50900 -11018
  45. package/dist/db/market-schema.js +2313 -467
  46. package/dist/db/node-sqlite.d.ts +1 -232
  47. package/dist/db/node-sqlite.js +7 -2
  48. package/dist/db/schema.d.ts +0 -462
  49. package/dist/db/schema.js +2 -22
  50. package/dist/dispatch.d.ts +1 -1
  51. package/dist/dispatch.js +0 -11
  52. package/dist/fixture-support.js +52 -4
  53. package/dist/governance.d.ts +133 -0
  54. package/dist/governance.js +285 -0
  55. package/dist/graph/build.d.ts +2 -2
  56. package/dist/graph/context-query-contracts.d.ts +1 -1
  57. package/dist/graph/dsl.d.ts +1 -1
  58. package/dist/graph/query.d.ts +4 -4
  59. package/dist/graph/ranking.d.ts +1 -1
  60. package/dist/graph/schema.d.ts +1 -1
  61. package/dist/graph.d.ts +11 -11
  62. package/dist/guarantees/index.d.ts +425 -0
  63. package/dist/guarantees/index.js +1146 -0
  64. package/dist/hosting/apps.d.ts +12 -0
  65. package/dist/hosting/apps.js +107 -0
  66. package/dist/hosting/builtins.d.ts +25 -0
  67. package/dist/hosting/builtins.js +602 -0
  68. package/dist/hosting/contracts.d.ts +208 -0
  69. package/dist/hosting/contracts.js +0 -0
  70. package/dist/hosting/graph.d.ts +195 -0
  71. package/dist/hosting/graph.js +1092 -0
  72. package/dist/hosting/index.d.ts +4 -0
  73. package/dist/hosting/index.js +4 -0
  74. package/dist/index.d.ts +88 -67
  75. package/dist/index.js +142 -14
  76. package/dist/local-dev/managed-dev.d.ts +85 -0
  77. package/dist/local-dev/managed-dev.js +368 -0
  78. package/dist/managed-dependencies.d.ts +9 -0
  79. package/dist/managed-dependencies.js +10 -9
  80. package/dist/market-client.d.ts +327 -9
  81. package/dist/market-client.js +464 -20
  82. package/dist/model-registry.d.ts +1 -1
  83. package/dist/model-registry.js +19 -19
  84. package/dist/operations/agent-tools.d.ts +2 -11
  85. package/dist/operations/agent-tools.js +2 -14
  86. package/dist/operations/providers/default.d.ts +1 -1
  87. package/dist/operations/providers/default.js +20 -37
  88. package/dist/operations/repository-operations.d.ts +26 -2
  89. package/dist/operations/repository-operations.js +159 -1
  90. package/dist/operations/runtime.d.ts +3 -3
  91. package/dist/operations/services/bootstrap-runner.d.ts +4 -2
  92. package/dist/operations/services/bootstrap-runner.js +22 -2
  93. package/dist/operations/services/build-warning-policy.d.ts +34 -0
  94. package/dist/operations/services/build-warning-policy.js +81 -82
  95. package/dist/operations/services/commit-message-provider.js +4 -4
  96. package/dist/operations/services/config-runtime.d.ts +83 -24
  97. package/dist/operations/services/config-runtime.js +449 -130
  98. package/dist/operations/services/deploy.d.ts +106 -624
  99. package/dist/operations/services/deploy.js +746 -263
  100. package/dist/operations/services/deployment-readiness.d.ts +30 -0
  101. package/dist/operations/services/deployment-readiness.js +206 -0
  102. package/dist/operations/services/git-remote-policy.js +12 -4
  103. package/dist/operations/services/git-runner.d.ts +66 -0
  104. package/dist/operations/services/git-runner.js +479 -0
  105. package/dist/operations/services/git-workflow.d.ts +45 -6
  106. package/dist/operations/services/git-workflow.js +251 -23
  107. package/dist/operations/services/github-actions-verification.d.ts +4 -1
  108. package/dist/operations/services/github-actions-verification.js +12 -3
  109. package/dist/operations/services/github-api.js +22 -6
  110. package/dist/operations/services/github-automation.d.ts +4 -4
  111. package/dist/operations/services/github-automation.js +19 -21
  112. package/dist/operations/services/github-credentials.d.ts +13 -0
  113. package/dist/operations/services/github-credentials.js +59 -0
  114. package/dist/operations/services/hosted-service-checks.d.ts +81 -0
  115. package/dist/operations/services/hosted-service-checks.js +478 -0
  116. package/dist/operations/services/hosting-audit.d.ts +2 -2
  117. package/dist/operations/services/hosting-audit.js +7 -7
  118. package/dist/operations/services/hub-launch.d.ts +1 -1
  119. package/dist/operations/services/hub-provider-launch.d.ts +5 -5
  120. package/dist/operations/services/hub-provider-launch.js +22 -17
  121. package/dist/operations/services/live-hosted-service-checks.d.ts +26 -0
  122. package/dist/operations/services/live-hosted-service-checks.js +463 -0
  123. package/dist/operations/services/local-dev.js +0 -1
  124. package/dist/operations/services/managed-host-security.d.ts +1 -1
  125. package/dist/operations/services/managed-host-security.js +1 -1
  126. package/dist/operations/services/managed-repositories.d.ts +37 -0
  127. package/dist/operations/services/managed-repositories.js +177 -0
  128. package/dist/operations/services/operations-runner-smoke.d.ts +31 -0
  129. package/dist/operations/services/operations-runner-smoke.js +198 -0
  130. package/dist/operations/services/package-adapters.d.ts +170 -0
  131. package/dist/operations/services/package-adapters.js +859 -0
  132. package/dist/operations/services/package-reference-policy.d.ts +13 -88
  133. package/dist/operations/services/package-reference-policy.js +96 -215
  134. package/dist/operations/services/project-host-operations.d.ts +4 -4
  135. package/dist/operations/services/project-platform.d.ts +57 -173
  136. package/dist/operations/services/project-platform.js +80 -111
  137. package/dist/operations/services/project-web-monitor.d.ts +2 -2
  138. package/dist/operations/services/project-web-monitor.js +122 -5
  139. package/dist/operations/services/railway-api.d.ts +117 -5
  140. package/dist/operations/services/railway-api.js +915 -93
  141. package/dist/operations/services/railway-deploy.d.ts +30 -112
  142. package/dist/operations/services/railway-deploy.js +521 -961
  143. package/dist/operations/services/release-history.js +3 -4
  144. package/dist/operations/services/release-proof-ledger.d.ts +13 -0
  145. package/dist/operations/services/release-proof-ledger.js +81 -0
  146. package/dist/operations/services/release-proof-planner.d.ts +48 -0
  147. package/dist/operations/services/release-proof-planner.js +195 -0
  148. package/dist/operations/services/release-proof-runner.d.ts +16 -0
  149. package/dist/operations/services/release-proof-runner.js +169 -0
  150. package/dist/operations/services/release-proof.d.ts +81 -0
  151. package/dist/operations/services/release-proof.js +79 -0
  152. package/dist/operations/services/repository-save-orchestrator.d.ts +8 -7
  153. package/dist/operations/services/repository-save-orchestrator.js +604 -212
  154. package/dist/operations/services/runtime-paths.d.ts +1 -3
  155. package/dist/operations/services/runtime-paths.js +0 -4
  156. package/dist/operations/services/runtime-tools.d.ts +1 -191
  157. package/dist/operations/services/runtime-tools.js +4 -443
  158. package/dist/operations/services/save-deploy-preflight.js +9 -1
  159. package/dist/operations/services/template-host-bindings.d.ts +2 -2
  160. package/dist/operations/services/template-host-bindings.js +3 -2
  161. package/dist/operations/services/template-registry.d.ts +6 -6
  162. package/dist/operations/services/template-registry.js +33 -14
  163. package/dist/operations/services/template-secret-sync.d.ts +3 -3
  164. package/dist/operations/services/verification-cache.d.ts +25 -0
  165. package/dist/operations/services/verification-cache.js +71 -0
  166. package/dist/operations/services/workflow-timing.d.ts +25 -0
  167. package/dist/operations/services/workflow-timing.js +79 -0
  168. package/dist/operations/services/workspace-dependency-mode.js +50 -5
  169. package/dist/operations/services/workspace-preflight.js +8 -8
  170. package/dist/operations/services/workspace-save-script.d.ts +4 -4
  171. package/dist/operations/services/workspace-save.d.ts +1 -0
  172. package/dist/operations/services/workspace-save.js +42 -19
  173. package/dist/operations/services/workspace-tools.js +42 -7
  174. package/dist/operations-registry.d.ts +1 -1
  175. package/dist/operations-registry.js +5 -6
  176. package/dist/operations-types.d.ts +1 -1
  177. package/dist/operations.d.ts +17 -11
  178. package/dist/operations.js +57 -3
  179. package/dist/platform/book-export.js +2 -0
  180. package/dist/platform/books-data.d.ts +1 -1
  181. package/dist/platform/config.d.ts +27 -0
  182. package/dist/platform/config.js +100 -0
  183. package/dist/platform/content-runtime-source.d.ts +54 -0
  184. package/dist/platform/content-runtime-source.js +169 -0
  185. package/dist/platform/contracts.d.ts +38 -2
  186. package/dist/platform/deploy-config.d.ts +1 -1
  187. package/dist/platform/deploy-config.js +80 -9
  188. package/dist/platform/deploy-runtime.d.ts +1 -1
  189. package/dist/platform/desired-state.d.ts +63 -0
  190. package/dist/platform/desired-state.js +1021 -0
  191. package/dist/platform/env.yaml +568 -45
  192. package/dist/platform/environment.d.ts +3 -2
  193. package/dist/platform/environment.js +122 -21
  194. package/dist/platform/local-content-materialization.d.ts +9 -0
  195. package/dist/platform/local-content-materialization.js +212 -0
  196. package/dist/platform/plugin.d.ts +13 -4
  197. package/dist/platform/plugins/runtime.d.ts +3 -3
  198. package/dist/platform/plugins/runtime.js +62 -1
  199. package/dist/platform/plugins.d.ts +6 -6
  200. package/dist/platform/published-content-pipeline.d.ts +3 -3
  201. package/dist/platform/published-content-pipeline.js +5 -1
  202. package/dist/platform/published-content.d.ts +4 -3
  203. package/dist/platform/site-config-schema.d.ts +1 -0
  204. package/dist/platform/site-config-schema.js +4 -1
  205. package/dist/platform/tenant/runtime-config.d.ts +152 -2
  206. package/dist/platform/tenant-config.d.ts +1 -1
  207. package/dist/platform/utils/site-config-schema.d.ts +154 -0
  208. package/dist/platform/utils/site-config-schema.js +400 -461
  209. package/dist/platform-operation-store.d.ts +1 -1
  210. package/dist/platform-operation-store.js +2 -2
  211. package/dist/platform-operations.js +1 -1
  212. package/dist/plugin-default.d.ts +1 -1
  213. package/dist/plugin-default.js +27 -12
  214. package/dist/project-import.d.ts +91 -0
  215. package/dist/project-import.js +257 -0
  216. package/dist/project-workflow.d.ts +1 -1
  217. package/dist/reconcile/bootstrap-systems.d.ts +2 -2
  218. package/dist/reconcile/bootstrap-systems.js +5 -5
  219. package/dist/reconcile/builtin-adapters.d.ts +7 -1
  220. package/dist/reconcile/builtin-adapters.js +3190 -429
  221. package/dist/reconcile/contracts.d.ts +23 -6
  222. package/dist/reconcile/desired-state.d.ts +2 -192
  223. package/dist/reconcile/desired-state.js +29 -38
  224. package/dist/reconcile/engine.d.ts +61 -395
  225. package/dist/reconcile/engine.js +229 -44
  226. package/dist/reconcile/index.d.ts +10 -8
  227. package/dist/reconcile/index.js +2 -0
  228. package/dist/reconcile/live-acceptance.d.ts +100 -0
  229. package/dist/reconcile/live-acceptance.js +2027 -0
  230. package/dist/reconcile/platform.d.ts +104 -0
  231. package/dist/reconcile/platform.js +100 -0
  232. package/dist/reconcile/providers/docker-private.d.ts +54 -0
  233. package/dist/reconcile/providers/docker-private.js +101 -0
  234. package/dist/reconcile/providers/github-private.d.ts +65 -0
  235. package/dist/reconcile/providers/github-private.js +119 -0
  236. package/dist/reconcile/providers/local-private.d.ts +28 -0
  237. package/dist/reconcile/providers/local-private.js +75 -0
  238. package/dist/reconcile/providers/railway-iac.d.ts +66 -0
  239. package/dist/reconcile/providers/railway-iac.js +275 -0
  240. package/dist/reconcile/providers/release-private.d.ts +92 -0
  241. package/dist/reconcile/providers/release-private.js +152 -0
  242. package/dist/reconcile/registry.d.ts +2 -2
  243. package/dist/reconcile/registry.js +16 -1
  244. package/dist/reconcile/state.d.ts +1 -1
  245. package/dist/reconcile/state.js +7 -44
  246. package/dist/reconcile/units.d.ts +1 -1
  247. package/dist/reconcile/units.js +9 -3
  248. package/dist/remote.d.ts +6 -6
  249. package/dist/scenes/artifacts.d.ts +16 -0
  250. package/dist/scenes/artifacts.js +61 -0
  251. package/dist/scenes/auth.d.ts +2 -0
  252. package/dist/scenes/auth.js +28 -0
  253. package/dist/scenes/base-url.d.ts +12 -0
  254. package/dist/scenes/base-url.js +123 -0
  255. package/dist/scenes/builtin-plugins.d.ts +2 -0
  256. package/dist/scenes/builtin-plugins.js +447 -0
  257. package/dist/scenes/checkpoints.d.ts +14 -0
  258. package/dist/scenes/checkpoints.js +33 -0
  259. package/dist/scenes/device-matrix.d.ts +2 -0
  260. package/dist/scenes/device-matrix.js +97 -0
  261. package/dist/scenes/devices.d.ts +11 -0
  262. package/dist/scenes/devices.js +22 -0
  263. package/dist/scenes/diagnostics.d.ts +5 -0
  264. package/dist/scenes/diagnostics.js +22 -0
  265. package/dist/scenes/diagram-providers.d.ts +2 -0
  266. package/dist/scenes/diagram-providers.js +181 -0
  267. package/dist/scenes/diagram-validation.d.ts +22 -0
  268. package/dist/scenes/diagram-validation.js +74 -0
  269. package/dist/scenes/environment.d.ts +2 -0
  270. package/dist/scenes/environment.js +65 -0
  271. package/dist/scenes/evidence-bundle.d.ts +5 -0
  272. package/dist/scenes/evidence-bundle.js +42 -0
  273. package/dist/scenes/evidence-report.d.ts +10 -0
  274. package/dist/scenes/evidence-report.js +73 -0
  275. package/dist/scenes/evidence.d.ts +16 -0
  276. package/dist/scenes/evidence.js +277 -0
  277. package/dist/scenes/index.d.ts +48 -0
  278. package/dist/scenes/index.js +312 -0
  279. package/dist/scenes/inspect.d.ts +9 -0
  280. package/dist/scenes/inspect.js +64 -0
  281. package/dist/scenes/loader.d.ts +3 -0
  282. package/dist/scenes/loader.js +38 -0
  283. package/dist/scenes/logs.d.ts +2 -0
  284. package/dist/scenes/logs.js +47 -0
  285. package/dist/scenes/operations.d.ts +3 -0
  286. package/dist/scenes/operations.js +128 -0
  287. package/dist/scenes/phase0.d.ts +9 -0
  288. package/dist/scenes/phase0.js +637 -0
  289. package/dist/scenes/planner.d.ts +12 -0
  290. package/dist/scenes/planner.js +166 -0
  291. package/dist/scenes/playwright-adapter.d.ts +2 -0
  292. package/dist/scenes/playwright-adapter.js +63 -0
  293. package/dist/scenes/plugins.d.ts +5 -0
  294. package/dist/scenes/plugins.js +115 -0
  295. package/dist/scenes/progress.d.ts +17 -0
  296. package/dist/scenes/progress.js +24 -0
  297. package/dist/scenes/publish-destinations.d.ts +12 -0
  298. package/dist/scenes/publish-destinations.js +99 -0
  299. package/dist/scenes/publish-plan-report.d.ts +10 -0
  300. package/dist/scenes/publish-plan-report.js +70 -0
  301. package/dist/scenes/publish-plan.d.ts +15 -0
  302. package/dist/scenes/publish-plan.js +248 -0
  303. package/dist/scenes/publish-redaction.d.ts +16 -0
  304. package/dist/scenes/publish-redaction.js +148 -0
  305. package/dist/scenes/publish-release-record.d.ts +13 -0
  306. package/dist/scenes/publish-release-record.js +34 -0
  307. package/dist/scenes/publish-report.d.ts +10 -0
  308. package/dist/scenes/publish-report.js +67 -0
  309. package/dist/scenes/publish.d.ts +16 -0
  310. package/dist/scenes/publish.js +298 -0
  311. package/dist/scenes/registry.d.ts +12 -0
  312. package/dist/scenes/registry.js +65 -0
  313. package/dist/scenes/remotion-adapter.d.ts +3 -0
  314. package/dist/scenes/remotion-adapter.js +94 -0
  315. package/dist/scenes/remotion-composition-registry.d.ts +2 -0
  316. package/dist/scenes/remotion-composition-registry.js +12 -0
  317. package/dist/scenes/remotion-compositions.d.ts +15 -0
  318. package/dist/scenes/remotion-compositions.js +464 -0
  319. package/dist/scenes/remotion-diagrams.d.ts +9 -0
  320. package/dist/scenes/remotion-diagrams.js +188 -0
  321. package/dist/scenes/remotion-input.d.ts +13 -0
  322. package/dist/scenes/remotion-input.js +291 -0
  323. package/dist/scenes/remotion-visuals.d.ts +29 -0
  324. package/dist/scenes/remotion-visuals.js +198 -0
  325. package/dist/scenes/render-media-assets.d.ts +9 -0
  326. package/dist/scenes/render-media-assets.js +117 -0
  327. package/dist/scenes/render-report.d.ts +10 -0
  328. package/dist/scenes/render-report.js +27 -0
  329. package/dist/scenes/render.d.ts +2 -0
  330. package/dist/scenes/render.js +218 -0
  331. package/dist/scenes/reporter.d.ts +3 -0
  332. package/dist/scenes/reporter.js +101 -0
  333. package/dist/scenes/resume.d.ts +2 -0
  334. package/dist/scenes/resume.js +79 -0
  335. package/dist/scenes/runner.d.ts +2 -0
  336. package/dist/scenes/runner.js +674 -0
  337. package/dist/scenes/schema.d.ts +6 -0
  338. package/dist/scenes/schema.js +994 -0
  339. package/dist/scenes/seed.d.ts +2 -0
  340. package/dist/scenes/seed.js +83 -0
  341. package/dist/scenes/segments.d.ts +15 -0
  342. package/dist/scenes/segments.js +79 -0
  343. package/dist/scenes/selectors.d.ts +3 -0
  344. package/dist/scenes/selectors.js +21 -0
  345. package/dist/scenes/timeline.d.ts +11 -0
  346. package/dist/scenes/timeline.js +26 -0
  347. package/dist/scenes/timeouts.d.ts +6 -0
  348. package/dist/scenes/timeouts.js +17 -0
  349. package/dist/scenes/training-captions.d.ts +14 -0
  350. package/dist/scenes/training-captions.js +98 -0
  351. package/dist/scenes/training-report.d.ts +10 -0
  352. package/dist/scenes/training-report.js +78 -0
  353. package/dist/scenes/training-transcript.d.ts +24 -0
  354. package/dist/scenes/training-transcript.js +204 -0
  355. package/dist/scenes/training.d.ts +10 -0
  356. package/dist/scenes/training.js +134 -0
  357. package/dist/scenes/types.d.ts +1872 -0
  358. package/dist/scenes/types.js +8 -0
  359. package/dist/scenes/visual-audit-contact-sheets.d.ts +6 -0
  360. package/dist/scenes/visual-audit-contact-sheets.js +119 -0
  361. package/dist/scenes/visual-audit-fixtures.d.ts +25 -0
  362. package/dist/scenes/visual-audit-fixtures.js +210 -0
  363. package/dist/scenes/visual-audit-report.d.ts +11 -0
  364. package/dist/scenes/visual-audit-report.js +85 -0
  365. package/dist/scenes/visual-audit-review.d.ts +23 -0
  366. package/dist/scenes/visual-audit-review.js +829 -0
  367. package/dist/scenes/visual-audit-routes.d.ts +13 -0
  368. package/dist/scenes/visual-audit-routes.js +308 -0
  369. package/dist/scenes/visual-audit.d.ts +3 -0
  370. package/dist/scenes/visual-audit.js +596 -0
  371. package/dist/scripts/build-dist.js +154 -40
  372. package/dist/scripts/check-build-warnings.js +42 -53
  373. package/dist/scripts/cleanup-markdown.js +1 -1
  374. package/dist/scripts/cli-test-fixtures.js +24 -28
  375. package/dist/scripts/deployment-readiness.js +20 -0
  376. package/dist/scripts/generate-treedx-openapi-types.js +186 -0
  377. package/dist/scripts/migrate-journey-csv-to-guarantees.js +140 -0
  378. package/dist/scripts/operations-runner-smoke.js +16 -0
  379. package/dist/scripts/package-tools.js +12 -1
  380. package/dist/scripts/prepare.js +3 -7
  381. package/dist/scripts/release-verify.js +6 -6
  382. package/dist/scripts/run-fixture-astro-command.js +1 -1
  383. package/dist/scripts/template-catalog.test.js +68 -82
  384. package/dist/scripts/tenant-dev.js +0 -10
  385. package/dist/scripts/tenant-workflow-action.js +10 -1
  386. package/dist/scripts/test-cloudflare-local.js +1 -1
  387. package/dist/scripts/test-scaffold.js +0 -1
  388. package/dist/scripts/test-smoke.js +3 -3
  389. package/dist/scripts/treeseed-build-dist.js +4 -7
  390. package/dist/scripts/treeseed-release-verify.js +1 -1
  391. package/dist/scripts/verify-driver.js +30 -21
  392. package/dist/scripts/workflow-commands.test.js +18 -27
  393. package/dist/scripts/workspace-command-e2e.js +1 -1
  394. package/dist/scripts/workspace-lint.js +6 -0
  395. package/dist/scripts/workspace-release-test.js +90 -1
  396. package/dist/scripts/workspace-test.js +6 -0
  397. package/dist/sdk-dispatch.d.ts +1 -1
  398. package/dist/sdk-dispatch.js +0 -16
  399. package/dist/sdk-fields.d.ts +1 -1
  400. package/dist/sdk-filters.d.ts +1 -1
  401. package/dist/sdk-types.d.ts +1352 -116
  402. package/dist/sdk-types.js +413 -2
  403. package/dist/sdk.d.ts +106 -100
  404. package/dist/sdk.js +211 -51
  405. package/dist/secrets-capability.d.ts +421 -0
  406. package/dist/secrets-capability.js +530 -0
  407. package/dist/seeds/normalize.js +1 -0
  408. package/dist/seeds/planner.js +38 -0
  409. package/dist/seeds/schema.js +293 -4
  410. package/dist/seeds/types.d.ts +60 -0
  411. package/dist/seeds/types.js +9 -1
  412. package/dist/service-credentials.d.ts +54 -0
  413. package/dist/service-credentials.js +112 -0
  414. package/dist/stores/cursor-store.d.ts +2 -2
  415. package/dist/stores/envelopes.d.ts +1 -1
  416. package/dist/stores/helpers.d.ts +1 -1
  417. package/dist/stores/lease-store.d.ts +2 -2
  418. package/dist/stores/message-store.d.ts +2 -2
  419. package/dist/stores/operational-store.d.ts +3 -26
  420. package/dist/stores/operational-store.js +0 -225
  421. package/dist/stores/project-workflow-store.d.ts +2 -2
  422. package/dist/stores/run-store.d.ts +2 -3
  423. package/dist/stores/run-store.js +0 -98
  424. package/dist/stores/subscription-store.d.ts +2 -3
  425. package/dist/stores/subscription-store.js +0 -100
  426. package/dist/template-catalog.d.ts +1 -1
  427. package/dist/template-launch-requirements.d.ts +2 -2
  428. package/dist/template-launch-requirements.js +9 -0
  429. package/dist/template-launch-ui.d.ts +2 -2
  430. package/dist/treedx/adapters.d.ts +6 -0
  431. package/dist/treedx/adapters.js +36 -0
  432. package/dist/treedx/client.d.ts +222 -0
  433. package/dist/treedx/client.js +871 -0
  434. package/dist/treedx/errors.d.ts +13 -0
  435. package/dist/treedx/errors.js +17 -0
  436. package/dist/treedx/federated-client.d.ts +27 -0
  437. package/dist/treedx/federated-client.js +158 -0
  438. package/dist/treedx/generated/openapi-types.d.ts +3558 -0
  439. package/dist/treedx/generated/openapi-types.js +0 -0
  440. package/dist/treedx/graph-adapter.d.ts +33 -0
  441. package/dist/treedx/graph-adapter.js +156 -0
  442. package/dist/treedx/index.d.ts +14 -0
  443. package/dist/treedx/index.js +48 -0
  444. package/dist/treedx/market-integration.d.ts +27 -0
  445. package/dist/treedx/market-integration.js +131 -0
  446. package/dist/treedx/ports.d.ts +166 -0
  447. package/dist/treedx/ports.js +231 -0
  448. package/dist/treedx/query-adapter.d.ts +19 -0
  449. package/dist/treedx/query-adapter.js +62 -0
  450. package/dist/treedx/registry-client.d.ts +11 -0
  451. package/dist/treedx/registry-client.js +19 -0
  452. package/dist/treedx/repository-adapter.d.ts +45 -0
  453. package/dist/treedx/repository-adapter.js +319 -0
  454. package/dist/treedx/sdk-integration.d.ts +27 -0
  455. package/dist/treedx/sdk-integration.js +63 -0
  456. package/dist/treedx/types.d.ts +1084 -0
  457. package/dist/treedx/types.js +8 -0
  458. package/dist/treedx/workspace-adapter.d.ts +27 -0
  459. package/dist/treedx/workspace-adapter.js +65 -0
  460. package/dist/treedx-backends.d.ts +220 -0
  461. package/dist/treedx-backends.js +634 -0
  462. package/dist/treedx-client.d.ts +89 -0
  463. package/dist/treedx-client.js +184 -0
  464. package/dist/treeseed/services/compose.yml +2 -3
  465. package/dist/treeseed/template-catalog/catalog.fixture.json +27 -27
  466. package/dist/types/agents.d.ts +256 -0
  467. package/dist/types/agents.js +42 -1
  468. package/dist/verification.d.ts +1 -0
  469. package/dist/verification.js +143 -14
  470. package/dist/workflow/operations.d.ts +580 -523
  471. package/dist/workflow/operations.js +2084 -1279
  472. package/dist/workflow/policy.js +19 -7
  473. package/dist/workflow/runs.d.ts +9 -3
  474. package/dist/workflow/runs.js +66 -26
  475. package/dist/workflow/session.d.ts +4 -1
  476. package/dist/workflow/session.js +50 -10
  477. package/dist/workflow/worktrees.d.ts +6 -2
  478. package/dist/workflow/worktrees.js +98 -15
  479. package/dist/workflow-state.d.ts +17 -4
  480. package/dist/workflow-state.js +68 -41
  481. package/dist/workflow-support.d.ts +26 -17
  482. package/dist/workflow-support.js +84 -24
  483. package/dist/workflow.d.ts +51 -16
  484. package/dist/workflow.js +19 -7
  485. package/drizzle/d1/0000_treeseed_d1.sql +0 -18
  486. package/drizzle/market/0000_market_control_plane.sql +1793 -1499
  487. package/drizzle/market/0004_treedx_market_integration.sql +99 -0
  488. package/drizzle/market/0005_agent_capacity_coordination.sql +145 -0
  489. package/drizzle/market/0006_provider_assignment_lifecycle.sql +9 -0
  490. package/drizzle/market/0007_agent_architecture_gap_closure.sql +157 -0
  491. package/drizzle/market/0008_agent_capacity_operational_closure.sql +27 -0
  492. package/drizzle/market/0009_agent_capacity_runtime_hardening.sql +29 -0
  493. package/drizzle/market/0010_secrets_capability_persistence.sql +130 -0
  494. package/drizzle/market/0011_github_app_adapter.sql +47 -0
  495. package/drizzle/market/0012_workday_test_runs.sql +44 -0
  496. package/drizzle/market/0013_runtime_task_lifecycle.sql +39 -0
  497. package/drizzle/market/0014_governance_pipeline.sql +189 -0
  498. package/package.json +106 -18
  499. package/scripts/{verify-driver.mjs → verify-driver.ts} +19 -4
  500. package/templates/github/deploy-web.workflow.yml +49 -12
  501. package/dist/operations/services/mailpit-runtime.d.ts +0 -20
  502. package/dist/operations/services/mailpit-runtime.js +0 -67
  503. package/dist/operations/services/release-candidate.d.ts +0 -41
  504. package/dist/operations/services/release-candidate.js +0 -616
  505. package/dist/scripts/ensure-mailpit.js +0 -48
  506. package/dist/scripts/logs-mailpit.js +0 -2
  507. package/dist/scripts/run-ts.js +0 -45
  508. package/dist/scripts/stop-mailpit.js +0 -5
  509. package/dist/scripts/treeseed-run-ts.js +0 -45
@@ -0,0 +1,1092 @@
1
+ import { loadTreeseedDeployConfig } from "../platform/deploy-config.js";
2
+ import { loadTreeseedPlugins } from "../platform/plugins/runtime.js";
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import { resolve } from "node:path";
5
+ import { parse as parseYaml } from "yaml";
6
+ import { createPersistentDeployTarget } from "../operations/services/deploy.js";
7
+ import { collectTreeseedConfigSeedValues, resolveTreeseedMachineEnvironmentValues } from "../operations/services/config-runtime.js";
8
+ import { classifyTreeseedGitMode, runTreeseedGitText } from "../operations/services/git-runner.js";
9
+ import { createTreeseedCanonicalReconcileReport } from "../reconcile/index.js";
10
+ import { reconcileTreeseedTarget } from "../reconcile/index.js";
11
+ import { discoverTreeseedApplications, findTreeseedApplication } from "./apps.js";
12
+ import {
13
+ createDefaultHostAdapters,
14
+ createDefaultHostingProfiles,
15
+ createDefaultServiceTypeAdapters,
16
+ redactSensitiveConfig,
17
+ sanitizedUnitConfig,
18
+ summarizePlacementStatus
19
+ } from "./builtins.js";
20
+ const ENVIRONMENT_NAMES = {
21
+ local: "local",
22
+ staging: "staging",
23
+ prod: "production"
24
+ };
25
+ const PLACEMENT_LABELS = {
26
+ web: "Site Hosting",
27
+ api: "API Runtime",
28
+ database: "Database",
29
+ "knowledge-library": "Knowledge Library",
30
+ "runner-capacity": "Runner Capacity",
31
+ repository: "Repository",
32
+ "content-storage": "Content Storage",
33
+ email: "Email",
34
+ operations: "Operations",
35
+ custom: "Custom"
36
+ };
37
+ function mergeRecord(...records) {
38
+ return Object.assign({}, ...records.filter(Boolean));
39
+ }
40
+ function asPluginRecord(value) {
41
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
42
+ }
43
+ function normalizeEnvironment(value) {
44
+ return value === "prod" || value === "production" ? "prod" : value === "staging" ? "staging" : "local";
45
+ }
46
+ function indexedName(baseName, index) {
47
+ return `${baseName.replace(/-\d+$/u, "").replace(/-\d{2}$/u, "")}-${String(Math.max(1, index)).padStart(2, "0")}`;
48
+ }
49
+ function publicTreeDxNodePool(config) {
50
+ const nodePool = config.publicTreeDxFederation?.railway?.nodePool ?? {};
51
+ const bootstrapCount = Math.max(1, Number.parseInt(String(nodePool.bootstrapCount ?? 1), 10) || 1);
52
+ const maxNodes = Math.max(bootstrapCount, Number.parseInt(String(nodePool.maxNodes ?? 4), 10) || 4);
53
+ return { bootstrapCount, maxNodes };
54
+ }
55
+ function resolvePublicTreeDxRoot(input) {
56
+ const candidates = [
57
+ resolve(input.tenantRoot, "packages", "treedx"),
58
+ resolve(input.tenantRoot, "..", "treedx"),
59
+ input.configRoot ? resolve(input.configRoot, "packages", "treedx") : null
60
+ ].filter((candidate) => Boolean(candidate));
61
+ return candidates.find((candidate) => existsSync(resolve(candidate, "treeseed.package.yaml")) || existsSync(resolve(candidate, ".git"))) ?? candidates[0];
62
+ }
63
+ function publicTreeDxSourcePolicy(input, config) {
64
+ const railway = config.publicTreeDxFederation?.railway ?? {};
65
+ const configuredSource = railway.source && typeof railway.source === "object" && !Array.isArray(railway.source) ? railway.source : {};
66
+ const configuredMode = typeof railway.sourceMode === "string" ? railway.sourceMode : null;
67
+ const treeDxRoot = resolvePublicTreeDxRoot(input);
68
+ const repository = typeof railway.sourceRepo === "string" ? railway.sourceRepo : typeof configuredSource.repository === "string" ? configuredSource.repository : typeof configuredSource.repo === "string" ? configuredSource.repo : readPackageRepository(treeDxRoot) ?? "treeseed-ai/treedx";
69
+ const sourceMode = configuredMode === "git" || configuredMode === "image" ? configuredMode : input.environment === "staging" ? "git" : "image";
70
+ if (sourceMode !== "git") {
71
+ return {
72
+ sourceMode: "image",
73
+ sourceRepo: null,
74
+ sourceBranch: null,
75
+ sourceCommit: null,
76
+ sourceRootDirectory: null,
77
+ image: "treeseed/treedx",
78
+ imageTagRef: "TREESEED_PUBLIC_TREEDX_IMAGE_REF"
79
+ };
80
+ }
81
+ return {
82
+ sourceMode: "git",
83
+ sourceRepo: repository,
84
+ sourceBranch: typeof railway.sourceBranch === "string" ? railway.sourceBranch : typeof configuredSource.branch === "string" ? configuredSource.branch : "staging",
85
+ sourceCommit: typeof railway.sourceCommit === "string" ? railway.sourceCommit : typeof configuredSource.commit === "string" ? configuredSource.commit : headCommitSafe(treeDxRoot) ?? headCommitSafe(input.tenantRoot),
86
+ sourceRootDirectory: typeof railway.sourceRootDirectory === "string" ? railway.sourceRootDirectory : typeof configuredSource.rootDirectory === "string" ? configuredSource.rootDirectory : ".",
87
+ image: null,
88
+ imageTagRef: null
89
+ };
90
+ }
91
+ function serviceKeyPlacement(serviceKey) {
92
+ if (serviceKey === "api") return "api";
93
+ if (serviceKey === "treeseedDatabase") return "database";
94
+ if (serviceKey === "operationsRunner") return "runner-capacity";
95
+ if (/runner|capacity/iu.test(serviceKey)) return "runner-capacity";
96
+ if (/database|postgres|db/iu.test(serviceKey)) return "database";
97
+ if (/email|smtp/iu.test(serviceKey)) return "email";
98
+ return "operations";
99
+ }
100
+ function serviceKeyType(serviceKey, service) {
101
+ if (serviceKey === "treeseedDatabase" || service.railway?.resourceType === "postgres") return "relational-database";
102
+ if (String(serviceKey).startsWith("capacityProvider")) return "capacity-provider";
103
+ if (serviceKey === "operationsRunner" || /runner/iu.test(serviceKey)) return "runner-pool";
104
+ if (Array.isArray(service.railway?.schedule) || typeof service.railway?.schedule === "string") return "scheduled-job";
105
+ if (serviceKey === "api") return "container-api";
106
+ return service.railway?.volumeMountPath ? "stateful-container" : "container-api";
107
+ }
108
+ function railwayImageRefEnvForService(serviceKey) {
109
+ if (serviceKey === "api") return "TREESEED_API_IMAGE_REF";
110
+ if (serviceKey === "operationsRunner") return "TREESEED_OPERATIONS_RUNNER_IMAGE_REF";
111
+ if (serviceKey === "capacityProviderManager") return "TREESEED_AGENT_MANAGER_IMAGE_REF";
112
+ if (serviceKey === "capacityProviderRunner") return "TREESEED_AGENT_RUNNER_IMAGE_REF";
113
+ return null;
114
+ }
115
+ function defaultRailwayImageRefForService(serviceKey, environment) {
116
+ return null;
117
+ }
118
+ function readPackageRepository(root) {
119
+ const manifestPath = resolve(root, "treeseed.package.yaml");
120
+ if (!existsSync(manifestPath)) return null;
121
+ try {
122
+ const parsed = parseYaml(readFileSync(manifestPath, "utf8"));
123
+ const repository = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed.repository : null;
124
+ return typeof repository === "string" && /^[^/\s]+\/[^/\s]+$/u.test(repository.trim()) ? repository.trim() : null;
125
+ } catch {
126
+ return null;
127
+ }
128
+ }
129
+ function headCommitSafe(root) {
130
+ try {
131
+ return runTreeseedGitText(["rev-parse", "HEAD"], {
132
+ cwd: root,
133
+ mode: classifyTreeseedGitMode(["rev-parse", "HEAD"])
134
+ }).trim();
135
+ } catch {
136
+ return null;
137
+ }
138
+ }
139
+ function resolveRailwayServiceSourceRoot(input, serviceKey, service) {
140
+ if (String(serviceKey).startsWith("capacityProvider")) {
141
+ if (service.railway?.rootDir) {
142
+ return resolve(input.tenantRoot, service.railway.rootDir);
143
+ }
144
+ const workspaceRoot = input.configRoot ?? input.tenantRoot;
145
+ const candidates = [
146
+ resolve(input.tenantRoot, "..", "agent"),
147
+ resolve(workspaceRoot, "packages", "agent"),
148
+ resolve(input.tenantRoot, "..", "..", "packages", "agent")
149
+ ];
150
+ const found = candidates.find(
151
+ (candidate) => existsSync(resolve(candidate, "treeseed.package.yaml")) || existsSync(resolve(candidate, "package.json"))
152
+ );
153
+ return found ?? resolve(workspaceRoot, "packages", "agent");
154
+ }
155
+ const rootDir = service.railway?.rootDir ?? service.rootDir ?? ".";
156
+ return resolve(input.tenantRoot, rootDir);
157
+ }
158
+ function railwaySourcePolicy(input, serviceKey, service, imageRef) {
159
+ const configuredSource = service.railway?.source && typeof service.railway.source === "object" && !Array.isArray(service.railway.source) ? service.railway.source : {};
160
+ const configuredMode = typeof service.railway?.sourceMode === "string" ? service.railway.sourceMode : null;
161
+ const repository = typeof service.railway?.sourceRepo === "string" ? service.railway.sourceRepo : typeof configuredSource.repository === "string" ? configuredSource.repository : typeof configuredSource.repo === "string" ? configuredSource.repo : readPackageRepository(resolveRailwayServiceSourceRoot(input, serviceKey, service)) ?? readPackageRepository(input.tenantRoot);
162
+ const sourceEligible = ["api", "operationsRunner", "capacityProviderManager", "capacityProviderRunner"].includes(serviceKey);
163
+ const mode = input.environment === "prod" ? "image" : configuredMode === "git" || configuredMode === "image" ? configuredMode : imageRef ? "image" : input.environment === "staging" && sourceEligible && repository ? "git" : "git";
164
+ if (mode !== "git") {
165
+ return {
166
+ sourceMode: "image",
167
+ sourceRepo: null,
168
+ sourceBranch: null,
169
+ sourceCommit: null,
170
+ sourceRootDirectory: null,
171
+ imageRef
172
+ };
173
+ }
174
+ return {
175
+ sourceMode: "git",
176
+ sourceRepo: repository,
177
+ sourceBranch: typeof service.railway?.sourceBranch === "string" ? service.railway.sourceBranch : typeof configuredSource.branch === "string" ? configuredSource.branch : input.environment === "staging" ? "staging" : null,
178
+ sourceCommit: headCommitSafe(resolveRailwayServiceSourceRoot(input, serviceKey, service)) ?? headCommitSafe(input.tenantRoot),
179
+ sourceRootDirectory: typeof service.railway?.sourceRootDirectory === "string" ? service.railway.sourceRootDirectory : typeof configuredSource.rootDirectory === "string" ? configuredSource.rootDirectory : ".",
180
+ imageRef: null
181
+ };
182
+ }
183
+ function collectPluginHostingContributions(input) {
184
+ const plugins = loadTreeseedPlugins(input.deployConfig);
185
+ const context = {
186
+ projectRoot: input.tenantRoot,
187
+ tenantConfig: void 0,
188
+ deployConfig: input.deployConfig,
189
+ pluginConfig: {}
190
+ };
191
+ const hostAdapters = {};
192
+ const serviceTypeAdapters = {};
193
+ const profiles = [];
194
+ for (const entry of plugins) {
195
+ const pluginContext = { ...context, pluginConfig: entry.config ?? {} };
196
+ const contribution = entry.plugin.hosting;
197
+ const resolved = typeof contribution === "function" ? contribution(pluginContext) : contribution;
198
+ if (!resolved || typeof resolved !== "object") continue;
199
+ Object.assign(hostAdapters, asPluginRecord(resolved.hostAdapters));
200
+ Object.assign(serviceTypeAdapters, asPluginRecord(resolved.serviceTypeAdapters));
201
+ const contributedProfiles = Array.isArray(resolved.profiles) ? resolved.profiles : [];
202
+ profiles.push(...contributedProfiles.filter(Boolean));
203
+ }
204
+ return {
205
+ hostAdapters,
206
+ serviceTypeAdapters,
207
+ profiles
208
+ };
209
+ }
210
+ function marketProjectGroup(environment, config) {
211
+ const apiService = config.services?.api && typeof config.services.api === "object" ? config.services.api : null;
212
+ const railwayProjectName = typeof apiService?.railway?.projectName === "string" && apiService.railway.projectName.trim() ? apiService.railway.projectName.trim() : void 0;
213
+ const projectName = railwayProjectName ?? config.slug ?? "treeseed-api";
214
+ return {
215
+ id: "treeseed-control-plane",
216
+ label: "Treeseed control plane",
217
+ hostId: environment === "local" ? "local-process" : "railway",
218
+ environments: {
219
+ local: { projectName: `${projectName}-local`, environmentName: "local" },
220
+ staging: { projectName, environmentName: "staging" },
221
+ prod: { projectName, environmentName: "production" }
222
+ },
223
+ metadata: { stableProjectName: projectName }
224
+ };
225
+ }
226
+ function railwayProjectGroupIdForProjectName(prefix, projectName) {
227
+ const slug = projectName.toLowerCase().replace(/[^a-z0-9-]+/gu, "-").replace(/^-+|-+$/gu, "") || "railway-project";
228
+ return `${prefix}:${slug}`;
229
+ }
230
+ function capacityProviderProjectGroupId(projectName) {
231
+ return railwayProjectGroupIdForProjectName("capacity-provider", projectName);
232
+ }
233
+ function capacityProviderProjectGroups(environment, config) {
234
+ const projectNames = /* @__PURE__ */ new Set();
235
+ for (const [serviceKey, service] of Object.entries(config.services ?? {})) {
236
+ if (!String(serviceKey).startsWith("capacityProvider")) continue;
237
+ if (!service || typeof service !== "object") continue;
238
+ const projectName = service.railway?.projectName;
239
+ if (typeof projectName === "string" && projectName.trim()) {
240
+ projectNames.add(projectName.trim());
241
+ }
242
+ }
243
+ return [...projectNames].map((projectName) => ({
244
+ id: capacityProviderProjectGroupId(projectName),
245
+ label: "Capacity provider",
246
+ hostId: environment === "local" ? "local-docker" : "railway",
247
+ environments: {
248
+ local: { projectName: `${projectName}-local`, environmentName: "local" },
249
+ staging: { projectName, environmentName: "staging" },
250
+ prod: { projectName, environmentName: "production" }
251
+ },
252
+ metadata: {
253
+ stableProjectName: projectName,
254
+ isolation: "capacity-provider"
255
+ }
256
+ }));
257
+ }
258
+ function publicTreeDxProjectGroup(environment, config) {
259
+ const apiProjectName = marketProjectGroup(environment, config).environments.staging?.projectName ?? config.slug ?? "treeseed-api";
260
+ return {
261
+ id: "public-treedx-federation",
262
+ label: "Public TreeDX federation",
263
+ hostId: environment === "local" ? "local-docker" : "railway",
264
+ environments: {
265
+ local: { projectName: `${apiProjectName}-local`, environmentName: "local" },
266
+ staging: { projectName: apiProjectName, environmentName: "staging" },
267
+ prod: { projectName: apiProjectName, environmentName: "production" }
268
+ },
269
+ metadata: {
270
+ publicFederation: true,
271
+ ownedByAppProject: "api",
272
+ isolation: "railway-service-volume-domain"
273
+ }
274
+ };
275
+ }
276
+ function privateTreeDxProjectGroup(teamId = "{teamId}") {
277
+ return {
278
+ id: "private-team-treedx",
279
+ label: "Private team TreeDX",
280
+ hostId: "railway",
281
+ environments: {
282
+ staging: { projectName: `treeseed-team-${teamId}-treedx`, environmentName: "staging" },
283
+ prod: { projectName: `treeseed-team-${teamId}-treedx`, environmentName: "production" }
284
+ },
285
+ metadata: { transferable: true, privateTeam: true }
286
+ };
287
+ }
288
+ function buildProfileFromDeployConfig(input) {
289
+ const config = input.deployConfig;
290
+ const environment = input.environment;
291
+ const services = [];
292
+ const projectGroups = [
293
+ marketProjectGroup(environment, config),
294
+ ...capacityProviderProjectGroups(environment, config),
295
+ publicTreeDxProjectGroup(environment, config),
296
+ privateTreeDxProjectGroup()
297
+ ];
298
+ const railwayImageRefEnvKeys = [...new Set(Object.keys(config.services ?? {}).map((serviceKey) => railwayImageRefEnvForService(serviceKey)).filter((value) => Boolean(value)))];
299
+ let railwayImageRefEnv = {};
300
+ try {
301
+ railwayImageRefEnv = resolveTreeseedMachineEnvironmentValues(input.configRoot ?? input.tenantRoot, environment, railwayImageRefEnvKeys);
302
+ } catch {
303
+ railwayImageRefEnv = {};
304
+ }
305
+ if (config.surfaces?.web && config.surfaces.web.enabled !== false) {
306
+ services.push({
307
+ id: "web",
308
+ label: "Site Hosting",
309
+ serviceType: "web-site",
310
+ placement: "web",
311
+ projectGroupId: environment === "local" ? void 0 : "treeseed-control-plane",
312
+ config: {
313
+ rootDir: config.surfaces?.web?.rootDir ?? ".",
314
+ publicBaseUrl: config.surfaces?.web?.environments?.[environment]?.baseUrl ?? config.surfaces?.web?.publicBaseUrl ?? null,
315
+ domain: config.surfaces?.web?.environments?.[environment]?.domain ?? null,
316
+ cache: config.surfaces?.web?.cache ?? null,
317
+ cloudflare: config.cloudflare ? {
318
+ workerName: config.cloudflare.workerName ?? null,
319
+ pages: config.cloudflare.pages ?? null
320
+ } : null
321
+ },
322
+ environments: {
323
+ local: { hostId: "local-process", config: { hotReload: true, baseUrl: config.surfaces?.web?.localBaseUrl ?? "http://127.0.0.1:4321" } },
324
+ staging: { hostId: "cloudflare", projectGroupId: "treeseed-control-plane" },
325
+ prod: { hostId: "cloudflare", projectGroupId: "treeseed-control-plane" }
326
+ }
327
+ });
328
+ }
329
+ for (const [serviceKey, serviceValue] of Object.entries(config.services ?? {})) {
330
+ const service = serviceValue;
331
+ if (!service || service.enabled === false) continue;
332
+ const serviceType = serviceKeyType(serviceKey, service);
333
+ const placement = serviceKeyPlacement(serviceKey);
334
+ const configuredRailwayProjectName = typeof service.railway?.projectName === "string" && service.railway.projectName.trim() ? service.railway.projectName.trim() : null;
335
+ const defaultProjectGroup = service.provider === "railway" || service.railway ? String(serviceKey).startsWith("capacityProvider") && configuredRailwayProjectName ? capacityProviderProjectGroupId(configuredRailwayProjectName) : "treeseed-control-plane" : void 0;
336
+ const imageRefEnv = railwayImageRefEnvForService(serviceKey);
337
+ const imageRef = service.railway?.imageRef ?? (imageRefEnv ? railwayImageRefEnv[imageRefEnv] ?? process.env[imageRefEnv] : null) ?? defaultRailwayImageRefForService(serviceKey, input.environment) ?? null;
338
+ const sourcePolicy = railwaySourcePolicy(input, serviceKey, service, imageRef);
339
+ services.push({
340
+ id: serviceKey,
341
+ label: placement === "runner-capacity" ? "Runner Capacity" : serviceKey === "api" ? "API Runtime" : serviceKey,
342
+ serviceType,
343
+ placement,
344
+ projectGroupId: defaultProjectGroup,
345
+ config: {
346
+ rootDir: service.railway?.rootDir ?? service.rootDir ?? ".",
347
+ imageRef: sourcePolicy.imageRef,
348
+ imageRefEnv: sourcePolicy.sourceMode === "image" ? imageRefEnv : null,
349
+ sourceMode: sourcePolicy.sourceMode,
350
+ sourceRepo: sourcePolicy.sourceRepo,
351
+ sourceBranch: sourcePolicy.sourceBranch,
352
+ sourceCommit: sourcePolicy.sourceCommit,
353
+ sourceRootDirectory: sourcePolicy.sourceRootDirectory,
354
+ dockerfilePath: sourcePolicy.sourceMode === "git" ? service.railway?.dockerfilePath ?? null : null,
355
+ buildCommand: sourcePolicy.imageRef ? null : service.railway?.buildCommand ?? null,
356
+ startCommand: sourcePolicy.imageRef ? null : service.railway?.startCommand ?? null,
357
+ healthcheckPath: service.railway?.healthcheckPath ?? null,
358
+ runtimeMode: service.railway?.runtimeMode ?? null,
359
+ volumeMountPath: service.railway?.volumeMountPath ?? null,
360
+ runnerPool: service.railway?.runnerPool ?? null,
361
+ resourceType: service.railway?.resourceType ?? null,
362
+ serviceName: service.railway?.serviceName ?? null,
363
+ serviceTargets: service.railway?.serviceTargets ?? null
364
+ },
365
+ secretRefs: serviceKey === "treeseedDatabase" ? ["TREESEED_DATABASE_URL"] : [],
366
+ variableRefs: serviceKey === "operationsRunner" ? ["TREESEED_PLATFORM_RUNNER_ID", "TREESEED_PLATFORM_RUNNER_DATA_DIR", "TREESEED_PLATFORM_RUNNER_ENVIRONMENT"] : [],
367
+ metadata: String(serviceKey).startsWith("capacityProvider") ? { capacityProvider: true, deployByDefault: false } : {},
368
+ environments: {
369
+ local: {
370
+ hostId: serviceType === "relational-database" || serviceType === "runner-pool" || service.railway?.volumeMountPath ? "local-docker" : "local-process",
371
+ projectGroupId: void 0,
372
+ config: service.environments?.local ?? {}
373
+ },
374
+ staging: {
375
+ hostId: service.provider ?? "railway",
376
+ projectGroupId: defaultProjectGroup,
377
+ config: service.environments?.staging ?? {}
378
+ },
379
+ prod: {
380
+ hostId: service.provider ?? "railway",
381
+ projectGroupId: defaultProjectGroup,
382
+ config: service.environments?.prod ?? {}
383
+ }
384
+ }
385
+ });
386
+ }
387
+ if (config.cloudflare?.r2) {
388
+ services.push({
389
+ id: "content-storage",
390
+ label: "Content Storage",
391
+ serviceType: "object-store",
392
+ placement: "content-storage",
393
+ config: {
394
+ bucketName: config.cloudflare.r2.bucketName ?? null,
395
+ manifestKeyTemplate: config.cloudflare.r2.manifestKeyTemplate ?? null,
396
+ previewRootTemplate: config.cloudflare.r2.previewRootTemplate ?? null
397
+ },
398
+ environments: {
399
+ local: { hostId: "local-docker" },
400
+ staging: { hostId: "cloudflare" },
401
+ prod: { hostId: "cloudflare" }
402
+ }
403
+ });
404
+ }
405
+ if (config.smtp?.enabled === true) {
406
+ services.push({
407
+ id: "email",
408
+ label: "Email",
409
+ serviceType: "email-relay",
410
+ placement: "email",
411
+ secretRefs: ["SMTP_PASSWORD"],
412
+ environments: {
413
+ local: { hostId: "smtp" },
414
+ staging: { hostId: "smtp" },
415
+ prod: { hostId: "smtp" }
416
+ }
417
+ });
418
+ }
419
+ if (config.hosting?.kind === "treeseed_control_plane") {
420
+ const treeDxNodePool = publicTreeDxNodePool(config);
421
+ const treeDxSourcePolicy = publicTreeDxSourcePolicy(input, config);
422
+ const treeDxNodeUnits = Array.from({ length: treeDxNodePool.bootstrapCount }, (_, offset) => {
423
+ const nodeIndex = offset + 1;
424
+ const serviceName = indexedName("public-treedx-node", nodeIndex);
425
+ return {
426
+ id: serviceName,
427
+ label: `Public TreeDX node ${String(nodeIndex).padStart(2, "0")}`,
428
+ serviceType: "treedx-node",
429
+ placement: "knowledge-library",
430
+ projectGroupId: "public-treedx-federation",
431
+ config: {
432
+ ...treeDxSourcePolicy.image ? { image: treeDxSourcePolicy.image } : {},
433
+ ...treeDxSourcePolicy.imageTagRef ? { imageTagRef: treeDxSourcePolicy.imageTagRef } : {},
434
+ sourceMode: treeDxSourcePolicy.sourceMode,
435
+ ...treeDxSourcePolicy.sourceRepo ? { sourceRepo: treeDxSourcePolicy.sourceRepo } : {},
436
+ ...treeDxSourcePolicy.sourceBranch ? { sourceBranch: treeDxSourcePolicy.sourceBranch } : {},
437
+ ...treeDxSourcePolicy.sourceCommit ? { sourceCommit: treeDxSourcePolicy.sourceCommit } : {},
438
+ ...treeDxSourcePolicy.sourceRootDirectory ? { sourceRootDirectory: treeDxSourcePolicy.sourceRootDirectory } : {},
439
+ serviceName,
440
+ volumeName: `${serviceName}-volume`,
441
+ volumeMountPath: "/data",
442
+ runtimeMode: "replicated",
443
+ environmentVariables: {
444
+ PORT: "4000",
445
+ TREEDX_DATA_DIR: "/data",
446
+ TREEDX_AUTH_MODE: "connected",
447
+ TREEDX_AUTH_VERIFIER: "hs256_dev",
448
+ TREEDX_ALLOW_DEV_VERIFIER_IN_PROD: "true",
449
+ TREEDX_EXEC_BACKEND: "container_sandbox",
450
+ TREEDX_FEDERATION_MODE: "connected_library",
451
+ TREEDX_JWT_AUDIENCE: "treedx-public-federation",
452
+ TREEDX_JWT_ISSUER: "https://api.treeseed.local/treedx",
453
+ TREEDX_BOOTSTRAP_TRUST_ACTOR_ID: "treeseed-api",
454
+ TREEDX_BOOTSTRAP_TRUST_TENANT_ID: "treeseed-control-plane",
455
+ TREEDX_BOOTSTRAP_TRUST_REPO_IDS: "*",
456
+ TREEDX_BOOTSTRAP_TRUST_REFS: "*",
457
+ TREEDX_BOOTSTRAP_TRUST_PATHS: "**",
458
+ TREEDX_SCOPE: "public_federation"
459
+ }
460
+ },
461
+ variableRefs: [
462
+ "PORT",
463
+ "TREEDX_DATA_DIR",
464
+ "TREEDX_AUTH_MODE",
465
+ "TREEDX_AUTH_VERIFIER",
466
+ "TREEDX_ALLOW_DEV_VERIFIER_IN_PROD",
467
+ "TREEDX_EXEC_BACKEND",
468
+ "TREEDX_FEDERATION_MODE",
469
+ "TREEDX_JWT_AUDIENCE",
470
+ "TREEDX_JWT_ISSUER",
471
+ "TREEDX_BOOTSTRAP_TRUST_ACTOR_ID",
472
+ "TREEDX_BOOTSTRAP_TRUST_TENANT_ID",
473
+ "TREEDX_BOOTSTRAP_TRUST_REPO_IDS",
474
+ "TREEDX_BOOTSTRAP_TRUST_REFS",
475
+ "TREEDX_BOOTSTRAP_TRUST_PATHS",
476
+ "TREEDX_SCOPE"
477
+ ],
478
+ secretRefs: ["TREEDX_SECRET_KEY_BASE", "TREEDX_ADMIN_TOKEN", "TREEDX_JWT_HS256_SECRET"],
479
+ environments: {
480
+ local: { hostId: "local-docker", projectGroupId: "public-treedx-federation" },
481
+ staging: { hostId: "railway", projectGroupId: "public-treedx-federation" },
482
+ prod: { hostId: "railway", projectGroupId: "public-treedx-federation" }
483
+ },
484
+ metadata: {
485
+ publicFederation: true,
486
+ defaultNode: nodeIndex === 1,
487
+ nodeIndex,
488
+ maxNodes: treeDxNodePool.maxNodes,
489
+ retainVolumeOnScaleDown: true
490
+ }
491
+ };
492
+ });
493
+ services.push(
494
+ {
495
+ id: "public-treedx-federation",
496
+ label: "Public TreeDX federation",
497
+ serviceType: "treedx-federation",
498
+ placement: "knowledge-library",
499
+ projectGroupId: "public-treedx-federation",
500
+ dependencies: treeDxNodeUnits.map((unit) => unit.id),
501
+ config: {
502
+ projectName: marketProjectGroup(environment, config).environments.staging?.projectName ?? config.slug ?? "treeseed-api",
503
+ isolation: "same API Railway project, separate service, volume, and domain",
504
+ federationMode: "connected_library",
505
+ nodePool: treeDxNodePool
506
+ },
507
+ environments: {
508
+ local: { hostId: "local-docker", projectGroupId: "public-treedx-federation" },
509
+ staging: { hostId: "railway", projectGroupId: "public-treedx-federation" },
510
+ prod: { hostId: "railway", projectGroupId: "public-treedx-federation" }
511
+ },
512
+ metadata: { publicFederation: true, nodeCount: treeDxNodePool.bootstrapCount, maxNodes: treeDxNodePool.maxNodes }
513
+ },
514
+ ...treeDxNodeUnits
515
+ );
516
+ }
517
+ return {
518
+ id: `${config.slug}-compiled`,
519
+ label: `${config.name} hosting profile`,
520
+ services,
521
+ projectGroups,
522
+ metadata: {
523
+ source: "treeseed.site.yaml",
524
+ environment
525
+ }
526
+ };
527
+ }
528
+ function assertCapabilityBinding(unit) {
529
+ const hostCapabilities = new Set(unit.host.capabilities.filter((capability) => capability.environments.includes(unit.environment)).map((capability) => capability.id));
530
+ const missing = unit.requiredCapabilities.filter((capability) => !hostCapabilities.has(capability));
531
+ if (missing.length > 0) {
532
+ throw new Error(`Hosting unit "${unit.id}" cannot bind ${unit.serviceType.id} to host "${unit.host.id}" in ${unit.environment}; missing capabilities: ${missing.join(", ")}.`);
533
+ }
534
+ }
535
+ function orderUnits(units) {
536
+ const remaining = new Map(units.map((unit) => [unit.id, unit]));
537
+ const ordered = [];
538
+ while (remaining.size > 0) {
539
+ const ready = [...remaining.values()].filter((unit) => unit.dependencies.every((dependency) => !remaining.has(dependency) || ordered.some((orderedUnit) => orderedUnit.id === dependency)));
540
+ if (ready.length === 0) {
541
+ throw new Error(`Hosting graph contains a dependency cycle: ${[...remaining.keys()].join(", ")}.`);
542
+ }
543
+ for (const unit of ready) {
544
+ remaining.delete(unit.id);
545
+ ordered.push(unit);
546
+ }
547
+ }
548
+ return ordered;
549
+ }
550
+ function createUnit(service, environment, hosts, serviceTypes, projectGroups, application) {
551
+ const serviceType = serviceTypes[service.serviceType];
552
+ if (!serviceType) {
553
+ throw new Error(`Unknown hosting service type "${service.serviceType}" for service "${service.id}".`);
554
+ }
555
+ const binding = service.environments?.[environment];
556
+ if (binding?.enabled === false) return null;
557
+ const hostId = binding?.hostId ?? serviceType.defaultHostByEnvironment?.[environment];
558
+ if (!hostId) {
559
+ throw new Error(`Hosting service "${service.id}" does not define a host binding for ${environment}.`);
560
+ }
561
+ const host = hosts[hostId];
562
+ if (!host) {
563
+ throw new Error(`Unknown hosting host "${hostId}" for service "${service.id}".`);
564
+ }
565
+ const projectGroupId = binding?.projectGroupId ?? service.projectGroupId;
566
+ const projectGroup = projectGroupId ? projectGroups[projectGroupId] ?? null : null;
567
+ const unitId = application && application.relativeRoot !== "." && service.id === "web" ? application.id : service.id;
568
+ const unit = {
569
+ id: unitId,
570
+ label: service.label,
571
+ serviceType,
572
+ placement: service.placement ?? serviceType.placement,
573
+ host,
574
+ environment,
575
+ projectGroup,
576
+ dependencies: service.dependencies ?? [],
577
+ requiredCapabilities: serviceType.requiredCapabilities,
578
+ config: redactSensitiveConfig({
579
+ ...service.config ?? {},
580
+ ...binding?.config ?? {}
581
+ }),
582
+ secretRefs: service.secretRefs ?? [],
583
+ variableRefs: service.variableRefs ?? [],
584
+ metadata: redactSensitiveConfig(service.metadata ?? {}),
585
+ application
586
+ };
587
+ assertCapabilityBinding(unit);
588
+ return unit;
589
+ }
590
+ function summarizePlacements(units) {
591
+ const grouped = /* @__PURE__ */ new Map();
592
+ for (const unit of units) {
593
+ grouped.set(unit.placement, [...grouped.get(unit.placement) ?? [], unit]);
594
+ }
595
+ return [...grouped.entries()].map(([placement, placementUnits]) => ({
596
+ placement,
597
+ label: PLACEMENT_LABELS[placement] ?? placement,
598
+ serviceIds: placementUnits.map((unit) => unit.id),
599
+ hostIds: [...new Set(placementUnits.map((unit) => unit.host.id))],
600
+ status: summarizePlacementStatus(placementUnits.map(() => "pending")),
601
+ advanced: false
602
+ }));
603
+ }
604
+ function normalizeFilterValues(values) {
605
+ return new Set((values ?? []).map((value) => value.trim()).filter(Boolean));
606
+ }
607
+ function filterHostingUnits(units, filter) {
608
+ const serviceIds = normalizeFilterValues(filter?.serviceIds);
609
+ const placements = normalizeFilterValues(filter?.placements);
610
+ const hosts = normalizeFilterValues(filter?.hosts);
611
+ const allServiceIds = new Set(units.map((unit) => unit.id));
612
+ const missingServices = [...serviceIds].filter((serviceId) => !allServiceIds.has(serviceId));
613
+ if (missingServices.length > 0) {
614
+ throw new Error(`Unknown hosting service id${missingServices.length === 1 ? "" : "s"}: ${missingServices.join(", ")}.`);
615
+ }
616
+ if (serviceIds.size === 0 && placements.size === 0 && hosts.size === 0) {
617
+ return units.filter((unit) => unit.metadata.deployByDefault !== false);
618
+ }
619
+ return units.filter((unit) => (serviceIds.size === 0 || serviceIds.has(unit.id)) && (placements.size === 0 || placements.has(unit.placement)) && (hosts.size === 0 || hosts.has(unit.host.id)));
620
+ }
621
+ function compileSingleTreeseedHostingGraph(input, application) {
622
+ const environment = normalizeEnvironment(input.environment);
623
+ const deployConfig = input.deployConfig ?? loadTreeseedDeployConfig(resolve(input.tenantRoot, "treeseed.site.yaml"));
624
+ const pluginContributions = collectPluginHostingContributions({ ...input, deployConfig, environment });
625
+ const hosts = mergeRecord(createDefaultHostAdapters(), pluginContributions.hostAdapters, input.hostAdapters);
626
+ const serviceTypes = mergeRecord(createDefaultServiceTypeAdapters(), pluginContributions.serviceTypeAdapters, input.serviceTypeAdapters);
627
+ const compiledProfile = buildProfileFromDeployConfig({ ...input, deployConfig, environment });
628
+ const profiles = [
629
+ ...createDefaultHostingProfiles(),
630
+ ...pluginContributions.profiles,
631
+ compiledProfile,
632
+ ...input.profiles ?? []
633
+ ];
634
+ const projectGroups = Object.fromEntries(
635
+ profiles.flatMap((profile) => profile.projectGroups ?? []).map((group) => [group.id, group])
636
+ );
637
+ const services = profiles.flatMap((profile) => profile.services);
638
+ const applicationInfo = application ? {
639
+ id: application.id,
640
+ root: application.root,
641
+ relativeRoot: application.relativeRoot,
642
+ configPath: application.configPath,
643
+ roles: application.roles
644
+ } : void 0;
645
+ const units = filterHostingUnits(orderUnits(services.map((service) => createUnit(service, environment, hosts, serviceTypes, projectGroups, applicationInfo)).filter((unit) => Boolean(unit))), input.filter);
646
+ return {
647
+ tenantRoot: input.tenantRoot,
648
+ environment,
649
+ deployConfig,
650
+ applications: application ? [application] : void 0,
651
+ hosts,
652
+ serviceTypes,
653
+ profiles,
654
+ projectGroups,
655
+ units,
656
+ placements: summarizePlacements(units),
657
+ warnings: []
658
+ };
659
+ }
660
+ function mergeTreeseedHostingGraphs(input, applications) {
661
+ const environment = normalizeEnvironment(input.environment);
662
+ const graphs = applications.map((application) => compileSingleTreeseedHostingGraph({
663
+ ...input,
664
+ tenantRoot: application.root,
665
+ configRoot: resolve(input.tenantRoot),
666
+ deployConfig: application.config,
667
+ filter: void 0
668
+ }, application));
669
+ const rootGraph = graphs.find((graph) => graph.tenantRoot === resolve(input.tenantRoot)) ?? graphs[0];
670
+ const hosts = mergeRecord(...graphs.map((graph) => graph.hosts), input.hostAdapters);
671
+ const serviceTypes = mergeRecord(...graphs.map((graph) => graph.serviceTypes), input.serviceTypeAdapters);
672
+ const projectGroups = mergeRecord(...graphs.map((graph) => graph.projectGroups));
673
+ const profiles = graphs.flatMap((graph) => graph.profiles);
674
+ const units = filterHostingUnits(orderUnits(graphs.flatMap((graph) => graph.units)), input.filter);
675
+ return {
676
+ tenantRoot: resolve(input.tenantRoot),
677
+ environment,
678
+ deployConfig: rootGraph.deployConfig,
679
+ applications,
680
+ hosts,
681
+ serviceTypes,
682
+ profiles,
683
+ projectGroups,
684
+ units,
685
+ placements: summarizePlacements(units),
686
+ warnings: graphs.flatMap((graph) => graph.warnings)
687
+ };
688
+ }
689
+ function compileTreeseedHostingGraph(input) {
690
+ if (input.deployConfig) {
691
+ return compileSingleTreeseedHostingGraph(input);
692
+ }
693
+ const tenantRoot = resolve(input.tenantRoot);
694
+ if (input.appId) {
695
+ const application = findTreeseedApplication(tenantRoot, input.appId);
696
+ if (!application) {
697
+ throw new Error(`Unknown Treeseed application "${input.appId}".`);
698
+ }
699
+ return compileSingleTreeseedHostingGraph({
700
+ ...input,
701
+ tenantRoot: application.root,
702
+ configRoot: tenantRoot,
703
+ deployConfig: application.config
704
+ }, application);
705
+ }
706
+ const applications = discoverTreeseedApplications(tenantRoot);
707
+ if (applications.length > 1) {
708
+ return mergeTreeseedHostingGraphs(input, applications);
709
+ }
710
+ return compileSingleTreeseedHostingGraph(input, applications[0]);
711
+ }
712
+ async function planTreeseedHostingGraph(input) {
713
+ const graph = compileTreeseedHostingGraph(input);
714
+ const units = [];
715
+ for (const unit of graph.units) {
716
+ const observed = await unit.host.refresh({ environment: graph.environment, unit, graph, dryRun: input.dryRun !== false });
717
+ const plan = await unit.host.diff({ environment: graph.environment, unit, graph, observed, dryRun: input.dryRun !== false });
718
+ const verification = await unit.host.verify({ environment: graph.environment, unit, graph, observed, dryRun: input.dryRun !== false });
719
+ units.push({ unit, observed, plan, verification });
720
+ }
721
+ return {
722
+ environment: graph.environment,
723
+ dryRun: input.dryRun !== false,
724
+ units,
725
+ placements: graph.placements,
726
+ warnings: graph.warnings
727
+ };
728
+ }
729
+ function railwayReconcileSystemsForUnits(units) {
730
+ const systems = /* @__PURE__ */ new Set();
731
+ for (const unit of units) {
732
+ if (unit.host.id !== "railway") continue;
733
+ if (unit.id === "operationsRunner") {
734
+ systems.add("api");
735
+ continue;
736
+ }
737
+ if (unit.placement === "api" || unit.placement === "knowledge-library" || unit.id === "api" || unit.serviceType.id === "container-api" || unit.serviceType.id === "treedx-node") {
738
+ systems.add("api");
739
+ }
740
+ if (unit.placement === "runner-capacity" || unit.serviceType.id === "runner-pool") {
741
+ systems.add("agents");
742
+ }
743
+ if (unit.placement === "database" && units.some((candidate) => candidate.id === "api" || candidate.placement === "api")) {
744
+ systems.add("api");
745
+ }
746
+ }
747
+ return [...systems];
748
+ }
749
+ function isInfrastructureHostedUnit(unit) {
750
+ return ["railway", "cloudflare", "cloudflare-dns", "local-docker"].includes(unit.host.id);
751
+ }
752
+ function railwayEnvForHostingApply(input, graph) {
753
+ const seedValues = collectTreeseedConfigSeedValues(input.tenantRoot, graph.environment);
754
+ return {
755
+ ...process.env,
756
+ ...seedValues
757
+ };
758
+ }
759
+ async function applyTreeseedHostingGraph(input) {
760
+ const plan = await planTreeseedHostingGraph(input);
761
+ const graph = compileTreeseedHostingGraph(input);
762
+ const selectedSystems = railwayReconcileSystemsForUnits(graph.units);
763
+ const infrastructureUnits = graph.units.filter(isInfrastructureHostedUnit);
764
+ if (plan.dryRun) {
765
+ return {
766
+ environment: plan.environment,
767
+ dryRun: true,
768
+ selectedApps: [...new Set(graph.units.map((unit) => unit.application?.id).filter((value) => Boolean(value)))],
769
+ selectedSystems,
770
+ skippedSystems: ["web", "data", "github"].filter((system) => !selectedSystems.includes(system)).map((system) => ({ system, reason: selectedSystems.length > 0 ? "Not selected by hosting app filter." : "No Railway reconciliation selected." })),
771
+ results: plan.units.map((entry) => ({
772
+ unit: entry.unit,
773
+ plan: entry.plan,
774
+ result: entry.observed,
775
+ verification: entry.verification
776
+ })),
777
+ placements: plan.placements,
778
+ warnings: plan.warnings
779
+ };
780
+ }
781
+ if (infrastructureUnits.length > 0 && selectedSystems.length === 0) {
782
+ throw new Error(`Hosting apply selected infrastructure resources but no provider reconciliation system was selected: ${infrastructureUnits.map((unit) => `${unit.id} (${unit.host.id})`).join(", ")}.`);
783
+ }
784
+ const reconcile = await reconcileTreeseedTarget({
785
+ tenantRoot: graph.tenantRoot,
786
+ target: createPersistentDeployTarget(graph.environment),
787
+ systems: selectedSystems.length > 0 ? selectedSystems : void 0,
788
+ env: railwayEnvForHostingApply(input, graph),
789
+ dryRun: plan.dryRun
790
+ });
791
+ const resultByUnit = /* @__PURE__ */ new Map();
792
+ for (const entry of reconcile.results) {
793
+ resultByUnit.set(entry.unit.unitId, entry);
794
+ resultByUnit.set(entry.unit.logicalName, entry);
795
+ }
796
+ const results = plan.units.map((entry) => {
797
+ const unit = graph.units.find((candidate) => candidate.id === entry.unit.id) ?? entry.unit;
798
+ const reconcileResult = resultByUnit.get(entry.unit.id) ?? resultByUnit.get(unit.id) ?? resultByUnit.get(unit.logicalName) ?? null;
799
+ return {
800
+ unit,
801
+ plan: entry.plan,
802
+ result: {
803
+ status: reconcileResult?.verification?.ready || plan.dryRun ? "ready" : "blocked",
804
+ locators: reconcileResult?.resourceLocators ?? {},
805
+ state: reconcileResult?.state ?? {
806
+ unitId: unit.id,
807
+ action: plan.dryRun ? "plan" : "unmatched"
808
+ },
809
+ warnings: reconcileResult?.warnings ?? []
810
+ },
811
+ verification: reconcileResult?.verification ? {
812
+ unitId: unit.id,
813
+ status: reconcileResult.verification.verified ? "ready" : "blocked",
814
+ verified: reconcileResult.verification.verified,
815
+ checks: reconcileResult.verification.checks.map((check) => ({
816
+ key: check.key,
817
+ label: check.description,
818
+ ok: check.verified,
819
+ expected: check.expected,
820
+ observed: check.observed,
821
+ issues: check.issues
822
+ })),
823
+ warnings: reconcileResult.verification.warnings
824
+ } : {
825
+ unitId: unit.id,
826
+ status: plan.dryRun ? "ready" : "blocked",
827
+ verified: plan.dryRun,
828
+ checks: plan.dryRun ? [] : [{
829
+ key: "reconcile-result",
830
+ label: "Hosting unit matched a reconcile result",
831
+ ok: false,
832
+ issues: [`No reconcile result matched hosting unit ${unit.id}.`]
833
+ }],
834
+ warnings: []
835
+ }
836
+ };
837
+ });
838
+ return {
839
+ environment: plan.environment,
840
+ dryRun: plan.dryRun,
841
+ selectedApps: [...new Set(graph.units.map((unit) => unit.application?.id).filter((value) => Boolean(value)))],
842
+ selectedSystems,
843
+ skippedSystems: ["web", "data", "github"].filter((system) => !selectedSystems.includes(system)).map((system) => ({ system, reason: selectedSystems.length > 0 ? "Not selected by hosting app filter." : "No Railway reconciliation selected." })),
844
+ transport: selectedSystems.length > 0 ? {
845
+ railway: {
846
+ reconcile: "api",
847
+ deploy: process.env.TREESEED_RAILWAY_DEPLOY_TRANSPORT === "cli-fallback" ? "cli-fallback" : "api"
848
+ }
849
+ } : void 0,
850
+ results,
851
+ placements: plan.placements,
852
+ warnings: plan.warnings
853
+ };
854
+ }
855
+ function serializeHostingUnit(unit) {
856
+ return sanitizedUnitConfig(unit);
857
+ }
858
+ function canonicalActionKind(value) {
859
+ const allowed = /* @__PURE__ */ new Set(["noop", "create", "update", "replace", "delete", "adopt", "rename", "reattach", "retain", "taint", "blocked"]);
860
+ return typeof value === "string" && allowed.has(value) ? value : "noop";
861
+ }
862
+ function canonicalHostingNode(unit, value) {
863
+ return {
864
+ id: unit.id,
865
+ provider: unit.host.id,
866
+ type: unit.serviceType.id,
867
+ owner: unit.application?.id ?? null,
868
+ environment: unit.environment,
869
+ spec: serializeHostingUnit(unit),
870
+ state: value,
871
+ locators: {
872
+ hostId: unit.host.id,
873
+ projectGroupId: unit.projectGroup?.id ?? null,
874
+ serviceTypeId: unit.serviceType.id
875
+ },
876
+ metadata: {
877
+ placement: unit.placement,
878
+ logicalName: unit.logicalName
879
+ }
880
+ };
881
+ }
882
+ function canonicalHostingDrift(unit, entries, fallbackReason) {
883
+ const rawEntries = Array.isArray(entries) ? entries : [];
884
+ if (rawEntries.length === 0) return [];
885
+ return rawEntries.map((entry, index) => ({
886
+ id: `${unit.id}:drift:${index + 1}`,
887
+ resourceId: unit.id,
888
+ severity: "blocking",
889
+ reason: typeof entry === "string" ? entry : fallbackReason,
890
+ provider: unit.host.id,
891
+ type: unit.serviceType.id,
892
+ observed: entry
893
+ }));
894
+ }
895
+ function canonicalHostingPostcondition(unit, verification) {
896
+ const issues = [
897
+ ...Array.isArray(verification.issues) ? verification.issues.map(String) : [],
898
+ ...Array.isArray(verification.checks) ? verification.checks.flatMap((check) => {
899
+ if (!check || typeof check !== "object") return [];
900
+ const maybeIssues = check.issues;
901
+ return Array.isArray(maybeIssues) ? maybeIssues.map(String) : [];
902
+ }) : []
903
+ ];
904
+ return {
905
+ id: `${unit.id}:verified`,
906
+ resourceId: unit.id,
907
+ description: `Live postconditions pass for ${unit.logicalName}.`,
908
+ source: "sdk",
909
+ required: true,
910
+ ok: verification.verified === true,
911
+ issues,
912
+ observed: verification
913
+ };
914
+ }
915
+ function hostingPlanReason(plan, prefix) {
916
+ return plan.reasons?.length ? plan.reasons.join("; ") : `${prefix} ${String(plan.action ?? "noop")}.`;
917
+ }
918
+ function canonicalHostingReportFromPlan(plan) {
919
+ const desiredGraph = plan.units.map((entry) => canonicalHostingNode(entry.unit));
920
+ const observedGraph = plan.units.map((entry) => canonicalHostingNode(entry.unit, entry.observed));
921
+ const diff = plan.units.flatMap((entry) => [
922
+ ...entry.plan.action && entry.plan.action !== "noop" ? [{
923
+ id: `${entry.unit.id}:diff`,
924
+ resourceId: entry.unit.id,
925
+ severity: canonicalActionKind(entry.plan.action) === "blocked" ? "blocking" : "info",
926
+ reason: hostingPlanReason(entry.plan, "Planned"),
927
+ provider: entry.unit.host.id,
928
+ type: entry.unit.serviceType.id,
929
+ expected: serializeHostingUnit(entry.unit),
930
+ observed: entry.observed
931
+ }] : [],
932
+ ...canonicalHostingDrift(entry.unit, entry.plan.blockedDrift, "Blocked provider drift.")
933
+ ]);
934
+ const providerLimitations = plan.units.flatMap((entry) => canonicalHostingDrift(entry.unit, entry.plan.providerLimitations, "Provider limitation."));
935
+ const actions = plan.units.map((entry) => ({
936
+ id: `${entry.unit.id}:${entry.plan.action ?? "noop"}`,
937
+ kind: canonicalActionKind(entry.plan.action),
938
+ resourceId: entry.unit.id,
939
+ reason: hostingPlanReason(entry.plan, "Planned"),
940
+ provider: entry.unit.host.id,
941
+ type: entry.unit.serviceType.id,
942
+ before: entry.observed,
943
+ after: serializeHostingUnit(entry.unit)
944
+ }));
945
+ return createTreeseedCanonicalReconcileReport({
946
+ desiredGraph,
947
+ observedGraph,
948
+ stateGraph: [],
949
+ diff,
950
+ actions,
951
+ postconditions: plan.units.map((entry) => canonicalHostingPostcondition(entry.unit, entry.verification)),
952
+ selectedResources: plan.units.map((entry) => entry.unit.id),
953
+ skippedResources: [],
954
+ blockedDrift: diff.filter((entry) => entry.severity === "blocking"),
955
+ providerLimitations,
956
+ retainedResources: plan.units.flatMap((entry) => (entry.plan.retainedResources ?? []).map((resource, index) => ({
957
+ id: `${entry.unit.id}:retained:${index + 1}`,
958
+ provider: entry.unit.host.id,
959
+ type: "retained-resource",
960
+ owner: entry.unit.application?.id ?? null,
961
+ state: resource
962
+ }))),
963
+ liveVerification: {
964
+ ok: plan.units.every((entry) => entry.verification.verified === true),
965
+ source: "hosting-plan",
966
+ issues: plan.units.filter((entry) => entry.verification.verified !== true).map((entry) => `${entry.unit.id}: verification did not pass`)
967
+ }
968
+ });
969
+ }
970
+ function canonicalHostingReportFromApplyResult(result) {
971
+ const desiredGraph = result.results.map((entry) => canonicalHostingNode(entry.unit));
972
+ const observedGraph = result.results.map((entry) => canonicalHostingNode(entry.unit, entry.result));
973
+ const diff = result.results.flatMap((entry) => [
974
+ ...entry.plan.action && entry.plan.action !== "noop" ? [{
975
+ id: `${entry.unit.id}:diff`,
976
+ resourceId: entry.unit.id,
977
+ severity: canonicalActionKind(entry.plan.action) === "blocked" ? "blocking" : "info",
978
+ reason: hostingPlanReason(entry.plan, "Applied"),
979
+ provider: entry.unit.host.id,
980
+ type: entry.unit.serviceType.id,
981
+ expected: serializeHostingUnit(entry.unit),
982
+ observed: entry.result
983
+ }] : [],
984
+ ...canonicalHostingDrift(entry.unit, entry.plan.blockedDrift, "Blocked provider drift.")
985
+ ]);
986
+ const providerLimitations = result.results.flatMap((entry) => canonicalHostingDrift(entry.unit, entry.plan.providerLimitations, "Provider limitation."));
987
+ const actions = result.results.map((entry) => ({
988
+ id: `${entry.unit.id}:${entry.plan.action ?? "noop"}`,
989
+ kind: canonicalActionKind(entry.plan.action),
990
+ resourceId: entry.unit.id,
991
+ reason: hostingPlanReason(entry.plan, "Applied"),
992
+ provider: entry.unit.host.id,
993
+ type: entry.unit.serviceType.id,
994
+ before: entry.result,
995
+ after: serializeHostingUnit(entry.unit)
996
+ }));
997
+ return createTreeseedCanonicalReconcileReport({
998
+ desiredGraph,
999
+ observedGraph,
1000
+ stateGraph: [],
1001
+ diff,
1002
+ actions,
1003
+ postconditions: result.results.map((entry) => canonicalHostingPostcondition(entry.unit, entry.verification)),
1004
+ selectedResources: result.results.map((entry) => entry.unit.id),
1005
+ skippedResources: result.skippedSystems.map((entry) => ({ id: entry.system, reason: entry.reason })),
1006
+ blockedDrift: diff.filter((entry) => entry.severity === "blocking"),
1007
+ providerLimitations,
1008
+ retainedResources: result.results.flatMap((entry) => (entry.plan.retainedResources ?? []).map((resource, index) => ({
1009
+ id: `${entry.unit.id}:retained:${index + 1}`,
1010
+ provider: entry.unit.host.id,
1011
+ type: "retained-resource",
1012
+ owner: entry.unit.application?.id ?? null,
1013
+ state: resource
1014
+ }))),
1015
+ liveVerification: {
1016
+ ok: result.results.every((entry) => entry.verification.verified === true),
1017
+ source: "hosting-apply",
1018
+ issues: result.results.filter((entry) => entry.verification.verified !== true).map((entry) => `${entry.unit.id}: verification did not pass after apply`)
1019
+ }
1020
+ });
1021
+ }
1022
+ function serializeHostingPlan(plan) {
1023
+ const selectedSystems = railwayReconcileSystemsForUnits(plan.units.map((entry) => entry.unit));
1024
+ const canonical = canonicalHostingReportFromPlan(plan);
1025
+ return {
1026
+ environment: plan.environment,
1027
+ dryRun: plan.dryRun,
1028
+ ...canonical,
1029
+ selectedApps: [...new Set(plan.units.map((entry) => entry.unit.application?.id).filter((value) => Boolean(value)))],
1030
+ selectedSystems,
1031
+ skippedSystems: ["web", "data", "github"].filter((system) => !selectedSystems.includes(system)).map((system) => ({ system, reason: selectedSystems.length > 0 ? "Not selected by hosting app filter." : "No Railway reconciliation selected." })),
1032
+ transport: selectedSystems.length > 0 ? {
1033
+ railway: {
1034
+ reconcile: "api",
1035
+ deploy: process.env.TREESEED_RAILWAY_DEPLOY_TRANSPORT === "cli-fallback" ? "cli-fallback" : "api"
1036
+ }
1037
+ } : void 0,
1038
+ placements: plan.placements,
1039
+ units: plan.units.map((entry) => ({
1040
+ unit: serializeHostingUnit(entry.unit),
1041
+ desired: serializeHostingUnit(entry.unit),
1042
+ observed: entry.observed,
1043
+ diff: entry.plan,
1044
+ actions: entry.plan.actions ?? [entry.plan.action],
1045
+ retainedResources: entry.plan.retainedResources ?? [],
1046
+ blockedDrift: entry.plan.blockedDrift ?? [],
1047
+ providerLimitations: entry.plan.providerLimitations ?? [],
1048
+ plan: entry.plan,
1049
+ verification: entry.verification
1050
+ })),
1051
+ warnings: plan.warnings
1052
+ };
1053
+ }
1054
+ function serializeHostingApplyResult(result) {
1055
+ const canonical = canonicalHostingReportFromApplyResult(result);
1056
+ return {
1057
+ environment: result.environment,
1058
+ dryRun: result.dryRun,
1059
+ ...canonical,
1060
+ selectedApps: result.selectedApps ?? [],
1061
+ selectedSystems: result.selectedSystems ?? [],
1062
+ skippedSystems: result.skippedSystems ?? [],
1063
+ transport: result.transport,
1064
+ placements: result.placements,
1065
+ results: result.results.map((entry) => ({
1066
+ unit: serializeHostingUnit(entry.unit),
1067
+ desired: serializeHostingUnit(entry.unit),
1068
+ observed: entry.result,
1069
+ diff: entry.plan,
1070
+ actions: entry.plan.actions ?? [entry.plan.action],
1071
+ retainedResources: entry.plan.retainedResources ?? [],
1072
+ blockedDrift: entry.plan.blockedDrift ?? [],
1073
+ providerLimitations: entry.plan.providerLimitations ?? [],
1074
+ plan: entry.plan,
1075
+ result: entry.result,
1076
+ verification: entry.verification
1077
+ })),
1078
+ warnings: result.warnings
1079
+ };
1080
+ }
1081
+ function hostingEnvironmentLabel(environment) {
1082
+ return ENVIRONMENT_NAMES[environment];
1083
+ }
1084
+ export {
1085
+ applyTreeseedHostingGraph,
1086
+ compileTreeseedHostingGraph,
1087
+ hostingEnvironmentLabel,
1088
+ planTreeseedHostingGraph,
1089
+ serializeHostingApplyResult,
1090
+ serializeHostingPlan,
1091
+ serializeHostingUnit
1092
+ };