@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
@@ -1,8 +1,7 @@
1
1
  import { request as httpsRequest } from "node:https";
2
+ import { resolveTreeseedRailwayApiToken } from "../../service-credentials.js";
2
3
  const DEFAULT_RAILWAY_API_URL = "https://backboard.railway.com/graphql/v2";
3
4
  const DEFAULT_RAILWAY_WORKSPACE = "knowledge-coop";
4
- const RAILWAY_POSTGRES_TEMPLATE_ID = "b55da7dc-09be-4140-bc65-1284d15d349c";
5
- const RAILWAY_POSTGRES_TEMPLATE_SERVICE_ID = "b55da7dc-09be-4140-bc65-1284b15d349b";
6
5
  function normalizeRailwayEnvironmentName(value) {
7
6
  const normalized = typeof value === "string" ? value.trim() : "";
8
7
  if (!normalized) {
@@ -18,7 +17,7 @@ function isUsableRailwayToken(value) {
18
17
  return typeof value === "string" && value.trim().length >= 8;
19
18
  }
20
19
  function resolveRailwayApiToken(env = process.env) {
21
- const token = configuredEnvValue(env, "RAILWAY_API_TOKEN");
20
+ const token = resolveTreeseedRailwayApiToken(env);
22
21
  return isUsableRailwayToken(token) ? token : "";
23
22
  }
24
23
  function resolveRailwayApiUrl(env = process.env) {
@@ -113,15 +112,36 @@ function normalizeProject(node) {
113
112
  if (!id || !name) {
114
113
  return null;
115
114
  }
115
+ const services = /* @__PURE__ */ new Map();
116
+ for (const service of normalizeConnectionNodes(node.services, normalizeService)) {
117
+ services.set(service.id, service);
118
+ }
119
+ for (const environment of normalizeConnectionNodes(node.environments, (entry) => entry)) {
120
+ for (const instance of normalizeConnectionNodes(environment.serviceInstances, (entry) => entry)) {
121
+ const serviceId = railwayConnectionLabel(instance.serviceId);
122
+ const serviceName = railwayConnectionLabel(instance.serviceName);
123
+ if (serviceId && serviceName) {
124
+ services.set(serviceId, { id: serviceId, name: serviceName });
125
+ }
126
+ }
127
+ }
116
128
  return {
117
129
  id,
118
130
  name,
119
131
  workspaceId: railwayConnectionLabel(node.workspaceId) || null,
120
132
  deletedAt: railwayConnectionLabel(node.deletedAt) || null,
121
133
  environments: normalizeConnectionNodes(node.environments, normalizeEnvironment),
122
- services: normalizeConnectionNodes(node.services, normalizeService)
134
+ services: [...services.values()]
123
135
  };
124
136
  }
137
+ function normalizeServiceInstanceService(node) {
138
+ const id = railwayConnectionLabel(node.serviceId);
139
+ const name = railwayConnectionLabel(node.serviceName);
140
+ if (!id || !name) {
141
+ return null;
142
+ }
143
+ return { id, name };
144
+ }
125
145
  function normalizeVariableMap(value) {
126
146
  if (!value) {
127
147
  return {};
@@ -195,6 +215,31 @@ function normalizeRailwayCustomDomain(node) {
195
215
  dnsRecords
196
216
  };
197
217
  }
218
+ function normalizeRailwayDomain(node, kind = "service") {
219
+ if (!node || typeof node !== "object") {
220
+ return null;
221
+ }
222
+ const record = node;
223
+ const id = railwayConnectionLabel(record.id) || railwayConnectionLabel(record.domain);
224
+ const domain = railwayConnectionLabel(record.domain);
225
+ if (!id || !domain) {
226
+ return null;
227
+ }
228
+ return {
229
+ id,
230
+ domain,
231
+ kind,
232
+ environmentId: railwayConnectionLabel(record.environmentId),
233
+ serviceId: railwayConnectionLabel(record.serviceId),
234
+ targetPort: normalizeRailwayNumber(record.targetPort)
235
+ };
236
+ }
237
+ function normalizeRailwayDomainList(value, kind) {
238
+ if (!Array.isArray(value)) {
239
+ return [];
240
+ }
241
+ return value.map((entry) => normalizeRailwayDomain(entry, kind)).filter(Boolean);
242
+ }
198
243
  function normalizeRailwayVolumeInstance(node) {
199
244
  const id = railwayConnectionLabel(node.id);
200
245
  if (!id) {
@@ -208,13 +253,15 @@ function normalizeRailwayVolumeInstance(node) {
208
253
  environmentId: railwayConnectionLabel(node.environmentId) || railwayConnectionLabel(node.environment?.id) || null,
209
254
  mountPath: railwayConnectionLabel(node.mountPath) || railwayConnectionLabel(node.mount_path) || null,
210
255
  state: railwayConnectionLabel(node.state) || null,
256
+ isPendingDeletion: node.isPendingDeletion === true || node.pendingDeletion === true,
257
+ deletedAt: railwayConnectionLabel(node.deletedAt) || null,
211
258
  sizeGb,
212
259
  usedGb
213
260
  };
214
261
  }
215
262
  function isActiveRailwayVolumeInstance(instance) {
216
263
  const state = String(instance.state ?? "READY").toUpperCase();
217
- return state !== "DELETING" && state !== "DELETED";
264
+ return !instance.isPendingDeletion && !(typeof instance.deletedAt === "string" && instance.deletedAt.trim()) && state !== "DELETING" && state !== "DELETED";
218
265
  }
219
266
  function normalizeVolumeInstances(value) {
220
267
  const direct = Array.isArray(value) ? value : null;
@@ -277,6 +324,13 @@ function collectRailwayVolumes(value, seen = /* @__PURE__ */ new Set()) {
277
324
  }
278
325
  return [...byId.values()];
279
326
  }
327
+ function railwayApiTimeoutMs(env, explicitTimeoutMs) {
328
+ if (typeof explicitTimeoutMs === "number" && Number.isFinite(explicitTimeoutMs) && explicitTimeoutMs > 0) {
329
+ return explicitTimeoutMs;
330
+ }
331
+ const configured = Number.parseInt(String(env.TREESEED_RAILWAY_API_TIMEOUT_MS ?? "").trim(), 10);
332
+ return Number.isFinite(configured) && configured > 0 ? configured : 12e4;
333
+ }
280
334
  async function railwayGraphqlRequest({
281
335
  query,
282
336
  variables,
@@ -284,19 +338,20 @@ async function railwayGraphqlRequest({
284
338
  apiToken,
285
339
  apiUrl,
286
340
  fetchImpl = fetch,
287
- timeoutMs = 3e4,
288
- retries = 8
341
+ timeoutMs,
342
+ retries = 4
289
343
  }) {
290
344
  const token = apiToken || resolveRailwayApiToken(env);
291
345
  if (!token) {
292
- throw new Error("Configure RAILWAY_API_TOKEN before invoking Railway APIs.");
346
+ throw new Error("Configure TREESEED_RAILWAY_API_TOKEN before invoking Railway APIs.");
293
347
  }
348
+ const requestTimeoutMs = railwayApiTimeoutMs(env, timeoutMs);
294
349
  let attempt = 0;
295
350
  for (; ; ) {
296
351
  const controller = new AbortController();
297
352
  let timer = null;
298
353
  try {
299
- const response = fetchImpl === fetch ? await railwayGraphqlHttpsRequest(apiUrl || resolveRailwayApiUrl(env), token, { query, variables }, timeoutMs) : await Promise.race([
354
+ const response = fetchImpl === fetch ? await railwayGraphqlHttpsRequest(apiUrl || resolveRailwayApiUrl(env), token, { query, variables }, requestTimeoutMs) : await Promise.race([
300
355
  fetchImpl(apiUrl || resolveRailwayApiUrl(env), {
301
356
  method: "POST",
302
357
  headers: {
@@ -314,8 +369,8 @@ async function railwayGraphqlRequest({
314
369
  new Promise((_, reject) => {
315
370
  timer = setTimeout(() => {
316
371
  controller.abort();
317
- reject(markRailwayTransientError(new Error(`Railway API request timed out after ${timeoutMs}ms.`)));
318
- }, timeoutMs);
372
+ reject(markRailwayTransientError(new Error(`Railway API request timed out after ${requestTimeoutMs}ms.`)));
373
+ }, requestTimeoutMs);
319
374
  })
320
375
  ]);
321
376
  const payload = response.payload;
@@ -349,7 +404,24 @@ async function railwayGraphqlRequest({
349
404
  async function railwayGraphqlHttpsRequest(url, token, body, timeoutMs) {
350
405
  const rawBody = JSON.stringify(body);
351
406
  return new Promise((resolve, reject) => {
352
- const req = httpsRequest(url, {
407
+ let settled = false;
408
+ let req = null;
409
+ const hardTimeout = setTimeout(() => {
410
+ if (settled) return;
411
+ settled = true;
412
+ const error = markRailwayTransientError(new Error(`Railway API request timed out after ${timeoutMs}ms.`));
413
+ if (req) {
414
+ req.destroy(error);
415
+ }
416
+ reject(error);
417
+ }, timeoutMs);
418
+ const finish = (callback) => {
419
+ if (settled) return;
420
+ settled = true;
421
+ clearTimeout(hardTimeout);
422
+ callback();
423
+ };
424
+ req = httpsRequest(url, {
353
425
  method: "POST",
354
426
  headers: {
355
427
  authorization: `Bearer ${token}`,
@@ -371,18 +443,21 @@ async function railwayGraphqlHttpsRequest(url, token, body, timeoutMs) {
371
443
  }
372
444
  const status = res.statusCode ?? 0;
373
445
  const retryAfterHeader = res.headers["retry-after"];
374
- resolve({
446
+ finish(() => resolve({
375
447
  ok: status >= 200 && status < 300,
376
448
  status,
377
449
  payload,
378
450
  retryAfter: Array.isArray(retryAfterHeader) ? retryAfterHeader[0] ?? null : retryAfterHeader ?? null
379
- });
451
+ }));
380
452
  });
381
453
  });
382
454
  req.on("timeout", () => {
383
- req.destroy(markRailwayTransientError(new Error(`Railway API request timed out after ${timeoutMs}ms.`)));
455
+ const error = markRailwayTransientError(new Error(`Railway API request timed out after ${timeoutMs}ms.`));
456
+ if (!settled) {
457
+ req?.destroy(error);
458
+ }
384
459
  });
385
- req.on("error", reject);
460
+ req.on("error", (error) => finish(() => reject(error)));
386
461
  req.write(rawBody);
387
462
  req.end();
388
463
  });
@@ -637,25 +712,130 @@ query TreeseedRailwayProjectEnvironments($projectId: String!) {
637
712
  });
638
713
  return normalizeConnectionNodes(payload.data?.project ? payload.data.project.environments : null, normalizeEnvironment);
639
714
  }
715
+ async function listRailwayEnvironmentServices({
716
+ environmentId,
717
+ env = process.env,
718
+ fetchImpl = fetch
719
+ }) {
720
+ const payload = await railwayGraphqlRequest({
721
+ query: `
722
+ query TreeseedRailwayEnvironmentServices($environmentId: String!) {
723
+ environment(id: $environmentId) {
724
+ id
725
+ name
726
+ serviceInstances(first: 100) {
727
+ edges {
728
+ node {
729
+ id
730
+ serviceId
731
+ serviceName
732
+ environmentId
733
+ }
734
+ }
735
+ }
736
+ }
737
+ }
738
+ `.trim(),
739
+ variables: { environmentId },
740
+ env,
741
+ fetchImpl
742
+ });
743
+ return normalizeConnectionNodes(
744
+ payload.data?.environment ? payload.data.environment.serviceInstances : null,
745
+ normalizeServiceInstanceService
746
+ );
747
+ }
640
748
  async function ensureRailwayService({
641
749
  projectId,
642
750
  serviceName,
643
751
  serviceId,
752
+ environmentId,
753
+ imageRef,
754
+ sourceRepo,
755
+ sourceBranch,
644
756
  env = process.env,
645
757
  fetchImpl = fetch
646
758
  }) {
647
759
  const services = await listRailwayServices({ projectId, env, fetchImpl });
648
760
  const desiredServiceName = railwayConnectionLabel(serviceName);
649
761
  const desiredServiceId = railwayConnectionLabel(serviceId);
650
- const existing = services.find(
762
+ let existing = services.find(
651
763
  (service2) => desiredServiceId && service2.id === desiredServiceId || desiredServiceName && service2.name === desiredServiceName
652
764
  ) ?? null;
765
+ if (!existing && environmentId) {
766
+ const environmentServices = await listRailwayEnvironmentServices({ environmentId, env, fetchImpl }).catch(() => []);
767
+ existing = environmentServices.find(
768
+ (service2) => desiredServiceId && service2.id === desiredServiceId || desiredServiceName && service2.name === desiredServiceName
769
+ ) ?? null;
770
+ }
653
771
  if (existing) {
772
+ const desiredImageRef = railwayConnectionLabel(imageRef);
773
+ const desiredSourceRepo = railwayConnectionLabel(sourceRepo);
774
+ if (desiredSourceRepo) {
775
+ try {
776
+ await updateRailwayServiceGitSource({
777
+ serviceId: existing.id,
778
+ sourceRepo: desiredSourceRepo,
779
+ sourceBranch,
780
+ env,
781
+ fetchImpl
782
+ });
783
+ } catch (error) {
784
+ if (!looksLikeRailwayImageSourceUpdateUnsupported(error)) {
785
+ throw error;
786
+ }
787
+ throw new Error(
788
+ `Railway Git source update for existing service ${existing.name} (${existing.id}) is unsupported; refusing to delete and recreate an existing service. Repair the service in place or use a provider-supported source update.`
789
+ );
790
+ }
791
+ return { service: existing, created: false };
792
+ }
793
+ if (desiredImageRef) {
794
+ try {
795
+ await updateRailwayServiceImageSource({
796
+ serviceId: existing.id,
797
+ imageRef: desiredImageRef,
798
+ env,
799
+ fetchImpl
800
+ });
801
+ } catch (error) {
802
+ if (!looksLikeRailwayImageSourceUpdateUnsupported(error)) {
803
+ throw error;
804
+ }
805
+ throw new Error(
806
+ `Railway image source update for existing service ${existing.name} (${existing.id}) is unsupported; refusing to delete and recreate an existing service. Repair the service in place or use a provider-supported image source update.`
807
+ );
808
+ }
809
+ }
654
810
  return { service: existing, created: false };
655
811
  }
656
812
  if (!desiredServiceName) {
657
813
  throw new Error("Railway service creation requires a service name.");
658
814
  }
815
+ const service = await createRailwayImageService({
816
+ projectId,
817
+ environmentId,
818
+ serviceName: desiredServiceName,
819
+ imageRef,
820
+ sourceRepo,
821
+ sourceBranch,
822
+ env,
823
+ fetchImpl
824
+ });
825
+ return { service, created: true };
826
+ }
827
+ async function createRailwayImageService({
828
+ projectId,
829
+ serviceName,
830
+ environmentId,
831
+ imageRef,
832
+ sourceRepo,
833
+ sourceBranch,
834
+ env = process.env,
835
+ fetchImpl = fetch
836
+ }) {
837
+ const desiredSourceRepo = railwayConnectionLabel(sourceRepo);
838
+ const desiredImageRef = railwayConnectionLabel(imageRef);
659
839
  const created = await railwayGraphqlRequest({
660
840
  query: `
661
841
  mutation TreeseedRailwayServiceCreate($input: ServiceCreateInput!) {
@@ -668,7 +848,18 @@ mutation TreeseedRailwayServiceCreate($input: ServiceCreateInput!) {
668
848
  variables: {
669
849
  input: {
670
850
  projectId,
671
- name: desiredServiceName
851
+ name: serviceName,
852
+ ...railwayConnectionLabel(environmentId) ? { environmentId: railwayConnectionLabel(environmentId) } : {},
853
+ ...desiredSourceRepo ? {
854
+ source: {
855
+ repo: desiredSourceRepo
856
+ },
857
+ ...railwayConnectionLabel(sourceBranch) ? { branch: railwayConnectionLabel(sourceBranch) } : {}
858
+ } : desiredImageRef ? {
859
+ source: {
860
+ image: desiredImageRef
861
+ }
862
+ } : {}
672
863
  }
673
864
  },
674
865
  env,
@@ -676,9 +867,219 @@ mutation TreeseedRailwayServiceCreate($input: ServiceCreateInput!) {
676
867
  });
677
868
  const service = created.data?.serviceCreate ? normalizeService(created.data.serviceCreate) : null;
678
869
  if (!service) {
679
- throw new Error(`Railway service create did not return a usable service for ${desiredServiceName}.`);
870
+ throw new Error(`Railway service create did not return a usable service for ${serviceName}.`);
680
871
  }
681
- return { service, created: true };
872
+ return service;
873
+ }
874
+ function looksLikeRailwayImageSourceUpdateUnsupported(error) {
875
+ const message = error instanceof Error ? error.message : String(error ?? "");
876
+ return /Problem processing request|source|image|ServiceUpdateInput/iu.test(message);
877
+ }
878
+ async function updateRailwayServiceImageSource({
879
+ serviceId,
880
+ imageRef,
881
+ env = process.env,
882
+ fetchImpl = fetch
883
+ }) {
884
+ const desiredImage = railwayConnectionLabel(imageRef);
885
+ if (!serviceId || !desiredImage) {
886
+ throw new Error("Railway service image source update requires a service id and image reference.");
887
+ }
888
+ const payload = await railwayGraphqlRequest({
889
+ query: `
890
+ mutation TreeseedRailwayServiceImageSourceUpdate($id: String!, $input: ServiceConnectInput!) {
891
+ serviceConnect(id: $id, input: $input) {
892
+ id
893
+ name
894
+ }
895
+ }
896
+ `.trim(),
897
+ variables: {
898
+ id: serviceId,
899
+ input: {
900
+ image: desiredImage
901
+ }
902
+ },
903
+ env,
904
+ fetchImpl
905
+ });
906
+ const service = payload.data?.serviceConnect ? normalizeService(payload.data.serviceConnect) : null;
907
+ if (!service) {
908
+ throw new Error(`Railway service image source update did not return a usable service for ${serviceId}.`);
909
+ }
910
+ return service;
911
+ }
912
+ async function updateRailwayServiceGitSource({
913
+ serviceId,
914
+ sourceRepo,
915
+ sourceBranch,
916
+ env = process.env,
917
+ fetchImpl = fetch
918
+ }) {
919
+ const desiredRepo = railwayConnectionLabel(sourceRepo);
920
+ if (!serviceId || !desiredRepo) {
921
+ throw new Error("Railway service Git source update requires a service id and repository slug.");
922
+ }
923
+ const connect = async (repo) => {
924
+ const payload2 = await railwayGraphqlRequest({
925
+ query: `
926
+ mutation TreeseedRailwayServiceGitSourceUpdate($id: String!, $input: ServiceConnectInput!) {
927
+ serviceConnect(id: $id, input: $input) {
928
+ id
929
+ name
930
+ }
931
+ }
932
+ `.trim(),
933
+ variables: {
934
+ id: serviceId,
935
+ input: {
936
+ repo,
937
+ ...railwayConnectionLabel(sourceBranch) ? { branch: railwayConnectionLabel(sourceBranch) } : {}
938
+ }
939
+ },
940
+ env,
941
+ fetchImpl
942
+ });
943
+ return payload2;
944
+ };
945
+ let payload;
946
+ try {
947
+ payload = await connect(desiredRepo);
948
+ } catch (error) {
949
+ const message = error instanceof Error ? error.message : String(error ?? "");
950
+ if (!/User does not have access to the repo/iu.test(message) || /^https?:\/\//iu.test(desiredRepo)) {
951
+ throw error;
952
+ }
953
+ payload = await connect(`https://github.com/${desiredRepo}`);
954
+ }
955
+ const service = payload.data?.serviceConnect ? normalizeService(payload.data.serviceConnect) : null;
956
+ if (!service) {
957
+ throw new Error(`Railway service Git source update did not return a usable service for ${serviceId}.`);
958
+ }
959
+ return service;
960
+ }
961
+ async function ensureRailwayGeneratedServiceDomain({
962
+ projectId,
963
+ environmentId,
964
+ serviceId,
965
+ targetPort,
966
+ env = process.env,
967
+ fetchImpl = fetch
968
+ }) {
969
+ const domains = await listRailwayServiceDomains({ projectId, environmentId, serviceId, env, fetchImpl });
970
+ const existing = domains.find((domain2) => domain2.kind === "service") ?? domains.find((domain2) => domain2.domain.endsWith(".railway.app")) ?? null;
971
+ if (existing) {
972
+ return { domain: existing, created: false };
973
+ }
974
+ const query = `
975
+ mutation TreeseedRailwayServiceDomainCreate($input: ServiceDomainCreateInput!) {
976
+ serviceDomainCreate(input: $input) {
977
+ id
978
+ domain
979
+ serviceId
980
+ environmentId
981
+ targetPort
982
+ }
983
+ }
984
+ `.trim();
985
+ const inputWithProject = {
986
+ projectId,
987
+ environmentId,
988
+ serviceId,
989
+ ...Number.isFinite(Number(targetPort)) ? { targetPort: Number(targetPort) } : {}
990
+ };
991
+ const createPayload = async (input) => railwayGraphqlRequest({
992
+ query,
993
+ variables: {
994
+ input: {
995
+ ...input
996
+ }
997
+ },
998
+ env,
999
+ fetchImpl
1000
+ });
1001
+ let payload;
1002
+ try {
1003
+ payload = await createPayload(inputWithProject);
1004
+ } catch (error) {
1005
+ const message = error instanceof Error ? error.message : String(error ?? "");
1006
+ if (!/Problem processing request|projectId|not defined by type/iu.test(message)) {
1007
+ throw error;
1008
+ }
1009
+ payload = await createPayload({
1010
+ environmentId,
1011
+ serviceId,
1012
+ ...Number.isFinite(Number(targetPort)) ? { targetPort: Number(targetPort) } : {}
1013
+ });
1014
+ }
1015
+ const domain = normalizeRailwayDomain(payload.data?.serviceDomainCreate);
1016
+ if (!domain) {
1017
+ throw new Error("Railway service domain create did not return a usable domain.");
1018
+ }
1019
+ return { domain, created: true };
1020
+ }
1021
+ async function listRailwayServiceDomains({
1022
+ projectId,
1023
+ environmentId,
1024
+ serviceId,
1025
+ env = process.env,
1026
+ fetchImpl = fetch
1027
+ }) {
1028
+ const payload = await railwayGraphqlRequest({
1029
+ query: `
1030
+ query TreeseedRailwayServiceDomains($projectId: String!, $environmentId: String!, $serviceId: String!) {
1031
+ domains(projectId: $projectId, environmentId: $environmentId, serviceId: $serviceId) {
1032
+ serviceDomains {
1033
+ id
1034
+ domain
1035
+ serviceId
1036
+ environmentId
1037
+ targetPort
1038
+ }
1039
+ customDomains {
1040
+ id
1041
+ domain
1042
+ serviceId
1043
+ environmentId
1044
+ targetPort
1045
+ }
1046
+ }
1047
+ }
1048
+ `.trim(),
1049
+ variables: { projectId, environmentId, serviceId },
1050
+ env,
1051
+ fetchImpl
1052
+ });
1053
+ const domains = payload.data?.domains && typeof payload.data.domains === "object" ? payload.data.domains : {};
1054
+ return [
1055
+ ...normalizeRailwayDomainList(domains.serviceDomains, "service"),
1056
+ ...normalizeRailwayDomainList(domains.customDomains, "custom")
1057
+ ];
1058
+ }
1059
+ async function deployRailwayServiceInstance({
1060
+ serviceId,
1061
+ environmentId,
1062
+ env = process.env,
1063
+ fetchImpl = fetch
1064
+ }) {
1065
+ const payload = await railwayGraphqlRequest({
1066
+ query: `
1067
+ mutation TreeseedRailwayServiceInstanceDeploy($serviceId: String!, $environmentId: String!) {
1068
+ serviceInstanceDeployV2(serviceId: $serviceId, environmentId: $environmentId)
1069
+ }
1070
+ `.trim(),
1071
+ variables: { serviceId, environmentId },
1072
+ env,
1073
+ fetchImpl
1074
+ });
1075
+ const value = payload.data?.serviceInstanceDeployV2;
1076
+ if (typeof value === "string" && value.trim()) {
1077
+ return { deploymentId: value.trim() };
1078
+ }
1079
+ if (value && typeof value === "object") {
1080
+ return { deploymentId: railwayConnectionLabel(value.id) || null };
1081
+ }
1082
+ return { deploymentId: null };
682
1083
  }
683
1084
  async function updateRailwayServiceName({
684
1085
  serviceId,
@@ -717,43 +1118,221 @@ async function ensureRailwayPostgresService({
717
1118
  environmentId,
718
1119
  serviceName,
719
1120
  env = process.env,
720
- fetchImpl = fetch
1121
+ fetchImpl = fetch,
1122
+ maxAttempts = 40
721
1123
  }) {
722
1124
  const desiredServiceName = railwayConnectionLabel(serviceName);
723
1125
  if (!desiredServiceName) {
724
1126
  throw new Error("Railway Postgres service creation requires a service name.");
725
1127
  }
726
1128
  const services = await listRailwayServices({ projectId, env, fetchImpl });
727
- const existing = services.find((service2) => service2.name === desiredServiceName || service2.id === desiredServiceName) ?? null;
1129
+ const existing = services.find((service) => service.name === desiredServiceName || service.id === desiredServiceName) ?? null;
728
1130
  if (existing) {
729
- return { service: existing, created: false };
730
- }
731
- const created = await railwayGraphqlRequest({
1131
+ const proof = await inspectRailwayPostgresService({ projectId, environmentId, serviceId: existing.id, env, fetchImpl });
1132
+ if (proof.ok) {
1133
+ return { service: existing, created: false, proof };
1134
+ }
1135
+ throw new Error(
1136
+ `Railway Postgres service ${existing.name} (${existing.id}) failed proof; refusing to delete and recreate an existing service. Repair the existing database service in place and rerun reconciliation.`
1137
+ );
1138
+ }
1139
+ const template = await getRailwayTemplateByCode({ code: "postgres", env, fetchImpl });
1140
+ await deployRailwayTemplate({
1141
+ templateId: template.id,
1142
+ serializedConfig: template.serializedConfig,
1143
+ projectId,
1144
+ environmentId,
1145
+ env,
1146
+ fetchImpl
1147
+ });
1148
+ const settled = await waitForRailwayPostgresTemplateService({
1149
+ projectId,
1150
+ environmentId,
1151
+ desiredServiceName,
1152
+ env,
1153
+ fetchImpl,
1154
+ maxAttempts
1155
+ });
1156
+ return { service: settled.service, created: true, proof: settled.proof };
1157
+ }
1158
+ async function getRailwayTemplateByCode({
1159
+ code,
1160
+ env = process.env,
1161
+ fetchImpl = fetch
1162
+ }) {
1163
+ const payload = await railwayGraphqlRequest({
732
1164
  query: `
733
- mutation TreeseedRailwayPostgresServiceCreate($input: ServiceCreateInput!) {
734
- serviceCreate(input: $input) {
1165
+ query TreeseedRailwayTemplate($code: String!) {
1166
+ template(code: $code) {
735
1167
  id
1168
+ code
736
1169
  name
1170
+ serializedConfig
1171
+ }
1172
+ }
1173
+ `.trim(),
1174
+ variables: { code },
1175
+ env,
1176
+ fetchImpl,
1177
+ timeoutMs: 15e3,
1178
+ retries: 1
1179
+ });
1180
+ const template = payload.data?.template;
1181
+ const id = railwayConnectionLabel(template?.id);
1182
+ if (!id || !template || typeof template !== "object") {
1183
+ throw new Error(`Railway Postgres template "${code}" was not found through the Railway API.`);
1184
+ }
1185
+ return {
1186
+ id,
1187
+ code: railwayConnectionLabel(template.code) || null,
1188
+ name: railwayConnectionLabel(template.name) || null,
1189
+ serializedConfig: normalizeTemplateSerializedConfig(template.serializedConfig)
1190
+ };
1191
+ }
1192
+ function normalizeTemplateSerializedConfig(value) {
1193
+ if (typeof value === "string") {
1194
+ try {
1195
+ const parsed = JSON.parse(value);
1196
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
1197
+ } catch {
1198
+ return {};
1199
+ }
1200
+ }
1201
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
1202
+ }
1203
+ async function deployRailwayTemplate({
1204
+ templateId,
1205
+ serializedConfig,
1206
+ projectId,
1207
+ environmentId,
1208
+ env = process.env,
1209
+ fetchImpl = fetch
1210
+ }) {
1211
+ const workspace = await resolveRailwayWorkspaceContext({ env, fetchImpl });
1212
+ const payload = await railwayGraphqlRequest({
1213
+ query: `
1214
+ mutation TreeseedRailwayTemplateDeploy($input: TemplateDeployV2Input!) {
1215
+ templateDeployV2(input: $input) {
1216
+ projectId
1217
+ workflowId
737
1218
  }
738
1219
  }
739
1220
  `.trim(),
740
1221
  variables: {
741
1222
  input: {
1223
+ templateId,
1224
+ serializedConfig,
742
1225
  projectId,
743
1226
  environmentId,
744
- name: desiredServiceName,
745
- templateId: RAILWAY_POSTGRES_TEMPLATE_ID,
746
- templateServiceId: RAILWAY_POSTGRES_TEMPLATE_SERVICE_ID
1227
+ workspaceId: workspace.id
747
1228
  }
748
1229
  },
749
1230
  env,
750
- fetchImpl
1231
+ fetchImpl,
1232
+ timeoutMs: 2e4,
1233
+ retries: 1
751
1234
  });
752
- const service = created.data?.serviceCreate ? normalizeService(created.data.serviceCreate) : null;
753
- if (!service) {
754
- throw new Error(`Railway Postgres service create did not return a usable service for ${desiredServiceName}.`);
1235
+ if (!payload.data?.templateDeployV2?.projectId) {
1236
+ throw new Error("Railway Postgres template deployment did not return a project id.");
755
1237
  }
756
- return { service, created: true };
1238
+ return payload.data.templateDeployV2;
1239
+ }
1240
+ async function waitForRailwayPostgresTemplateService({
1241
+ projectId,
1242
+ environmentId,
1243
+ desiredServiceName,
1244
+ env = process.env,
1245
+ fetchImpl = fetch,
1246
+ maxAttempts = 40
1247
+ }) {
1248
+ let lastProof = null;
1249
+ for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
1250
+ const services = await listRailwayServices({ projectId, env, fetchImpl });
1251
+ for (const service of services) {
1252
+ const proof = await inspectRailwayPostgresService({
1253
+ projectId,
1254
+ environmentId,
1255
+ serviceId: service.id,
1256
+ env,
1257
+ fetchImpl
1258
+ });
1259
+ if (proof.ok) {
1260
+ const renamed = service.name === desiredServiceName ? service : await updateRailwayServiceName({ serviceId: service.id, name: desiredServiceName, env, fetchImpl });
1261
+ return { service: renamed, proof };
1262
+ }
1263
+ lastProof = proof;
1264
+ }
1265
+ await new Promise((resolve) => setTimeout(resolve, 3e3));
1266
+ }
1267
+ throw new Error(`Railway Postgres template deployment did not produce a managed PostgreSQL service named ${desiredServiceName}. Last proof: ${lastProof?.message ?? "no candidate service observed"}`);
1268
+ }
1269
+ async function inspectRailwayPostgresService({
1270
+ projectId,
1271
+ environmentId,
1272
+ serviceId,
1273
+ env = process.env,
1274
+ fetchImpl = fetch
1275
+ }) {
1276
+ const variables = await listRailwayVariables({ projectId, environmentId, serviceId, env, fetchImpl }).catch(() => ({}));
1277
+ const hasConnectionVars = typeof variables.DATABASE_URL === "string" && typeof variables.PGHOST === "string" && typeof variables.PGUSER === "string" && typeof variables.PGPASSWORD === "string" && typeof variables.PGDATABASE === "string";
1278
+ const volumes = await listRailwayVolumes({ projectId, env, fetchImpl }).catch(() => []);
1279
+ const volume = volumes.find((candidate) => candidate.instances.some(
1280
+ (instance) => instance.serviceId === serviceId && instance.environmentId === environmentId && instance.mountPath === "/var/lib/postgresql/data"
1281
+ )) ?? null;
1282
+ const deployment = await inspectRailwayServiceDeploymentHealth({ serviceId, environmentId, env, fetchImpl }).catch((error) => ({
1283
+ ok: false,
1284
+ status: null,
1285
+ message: error instanceof Error ? error.message : String(error ?? "Unable to inspect PostgreSQL deployment health.")
1286
+ }));
1287
+ return {
1288
+ ok: hasConnectionVars && Boolean(volume) && deployment.ok,
1289
+ variableKeys: Object.keys(variables).sort(),
1290
+ volumeId: volume?.id ?? null,
1291
+ deploymentStatus: deployment.status,
1292
+ message: hasConnectionVars ? volume ? deployment.ok ? "Railway managed PostgreSQL markers are present and the deployment is healthy." : `Railway managed PostgreSQL markers are present, but deployment health is not ready. ${deployment.message}` : "PostgreSQL connection variables exist, but the managed data volume is missing." : "PostgreSQL connection variables are missing."
1293
+ };
1294
+ }
1295
+ async function inspectRailwayServiceDeploymentHealth({
1296
+ serviceId,
1297
+ environmentId,
1298
+ env = process.env,
1299
+ fetchImpl = fetch
1300
+ }) {
1301
+ const payload = await railwayGraphqlRequest({
1302
+ query: `
1303
+ query TreeseedRailwayServiceDeploymentHealth($serviceId: String!, $environmentId: String!) {
1304
+ serviceInstance(serviceId: $serviceId, environmentId: $environmentId) {
1305
+ latestDeployment {
1306
+ status
1307
+ deploymentStopped
1308
+ meta
1309
+ instances {
1310
+ status
1311
+ }
1312
+ }
1313
+ }
1314
+ }
1315
+ `.trim(),
1316
+ variables: { serviceId, environmentId },
1317
+ env,
1318
+ fetchImpl
1319
+ });
1320
+ const deployment = payload.data?.serviceInstance?.latestDeployment;
1321
+ const status = railwayConnectionLabel(deployment?.status)?.toUpperCase() ?? null;
1322
+ const instanceStatuses = Array.isArray(deployment?.instances) ? deployment.instances.map((instance) => railwayConnectionLabel(instance?.status)?.toUpperCase()).filter(Boolean) : [];
1323
+ const stopped = deployment?.deploymentStopped === true;
1324
+ const ok = status === "SUCCESS" && !stopped && (instanceStatuses.length === 0 || instanceStatuses.some((candidate) => candidate === "RUNNING"));
1325
+ return {
1326
+ ok,
1327
+ status,
1328
+ branch: railwayConnectionLabel(deployment?.meta?.branch) || null,
1329
+ repo: railwayConnectionLabel(deployment?.meta?.repo) || null,
1330
+ rootDirectory: railwayConnectionLabel(deployment?.meta?.rootDirectory) || null,
1331
+ commitHash: railwayConnectionLabel(deployment?.meta?.commitHash) || null,
1332
+ requiredMountPath: railwayConnectionLabel(deployment?.meta?.serviceManifest?.deploy?.requiredMountPath) || null,
1333
+ volumeMounts: Array.isArray(deployment?.meta?.volumeMounts) ? deployment.meta.volumeMounts.map((entry) => railwayConnectionLabel(entry)).filter(Boolean) : [],
1334
+ message: ok ? "Deployment is healthy." : `Latest deployment status is ${status ?? "unknown"}${stopped ? " and stopped" : ""}${instanceStatuses.length ? `; instances=${instanceStatuses.join(",")}` : ""}.`
1335
+ };
757
1336
  }
758
1337
  async function listRailwayServices({
759
1338
  projectId,
@@ -791,6 +1370,8 @@ async function getRailwayServiceInstance({
791
1370
  const legacySummary = {
792
1371
  id: null,
793
1372
  buildCommand: null,
1373
+ dockerfilePath: null,
1374
+ railwayConfigFile: null,
794
1375
  startCommand: null,
795
1376
  cronSchedule: null,
796
1377
  rootDirectory: null,
@@ -809,6 +1390,8 @@ query TreeseedRailwayServiceInstance($serviceId: String!, $environmentId: String
809
1390
  serviceInstance(serviceId: $serviceId, environmentId: $environmentId) {
810
1391
  id
811
1392
  buildCommand
1393
+ dockerfilePath
1394
+ railwayConfigFile
812
1395
  startCommand
813
1396
  cronSchedule
814
1397
  rootDirectory
@@ -826,6 +1409,8 @@ query TreeseedRailwayServiceInstance($serviceId: String!, $environmentId: String
826
1409
  return {
827
1410
  id: railwayConnectionLabel(instance?.id) || null,
828
1411
  buildCommand: railwayConnectionLabel(instance?.buildCommand) || null,
1412
+ dockerfilePath: railwayConnectionLabel(instance?.dockerfilePath) || null,
1413
+ railwayConfigFile: railwayConnectionLabel(instance?.railwayConfigFile) || null,
829
1414
  startCommand: railwayConnectionLabel(instance?.startCommand) || null,
830
1415
  cronSchedule: railwayConnectionLabel(instance?.cronSchedule) || null,
831
1416
  rootDirectory: railwayConnectionLabel(instance?.rootDirectory) || null,
@@ -846,6 +1431,8 @@ query TreeseedRailwayServiceInstanceLegacy($serviceId: String!, $environmentId:
846
1431
  serviceInstance(serviceId: $serviceId, environmentId: $environmentId) {
847
1432
  id
848
1433
  buildCommand
1434
+ dockerfilePath
1435
+ railwayConfigFile
849
1436
  startCommand
850
1437
  cronSchedule
851
1438
  rootDirectory
@@ -861,6 +1448,8 @@ query TreeseedRailwayServiceInstanceLegacy($serviceId: String!, $environmentId:
861
1448
  ...legacySummary,
862
1449
  id: railwayConnectionLabel(instance?.id) || null,
863
1450
  buildCommand: railwayConnectionLabel(instance?.buildCommand) || null,
1451
+ dockerfilePath: railwayConnectionLabel(instance?.dockerfilePath) || null,
1452
+ railwayConfigFile: railwayConnectionLabel(instance?.railwayConfigFile) || null,
864
1453
  startCommand: railwayConnectionLabel(instance?.startCommand) || null,
865
1454
  cronSchedule: railwayConnectionLabel(instance?.cronSchedule) || null,
866
1455
  rootDirectory: railwayConnectionLabel(instance?.rootDirectory) || null
@@ -876,6 +1465,8 @@ async function ensureRailwayServiceInstanceConfiguration({
876
1465
  serviceId,
877
1466
  environmentId,
878
1467
  buildCommand,
1468
+ dockerfilePath,
1469
+ railwayConfigFile,
879
1470
  startCommand,
880
1471
  cronSchedule,
881
1472
  rootDirectory,
@@ -884,6 +1475,7 @@ async function ensureRailwayServiceInstanceConfiguration({
884
1475
  healthcheckIntervalSeconds,
885
1476
  restartPolicy,
886
1477
  runtimeMode,
1478
+ deploymentRegion,
887
1479
  env = process.env,
888
1480
  fetchImpl = fetch,
889
1481
  settleAttempts = 60,
@@ -899,8 +1491,12 @@ async function ensureRailwayServiceInstanceConfiguration({
899
1491
  if (!current.id) {
900
1492
  return { instance: current, updated: false };
901
1493
  }
1494
+ const desiredRuntimeMode = railwayConnectionLabel(runtimeMode) === "service" ? "replicated" : railwayConnectionLabel(runtimeMode) || null;
1495
+ const desiredDeploymentRegion = railwayConnectionLabel(deploymentRegion) || null;
902
1496
  const desired = {
903
1497
  buildCommand: railwayConnectionLabel(buildCommand) || null,
1498
+ dockerfilePath: railwayConnectionLabel(dockerfilePath) || null,
1499
+ railwayConfigFile: railwayConnectionLabel(railwayConfigFile) || null,
904
1500
  startCommand: railwayConnectionLabel(startCommand) || null,
905
1501
  cronSchedule: railwayConnectionLabel(cronSchedule) || null,
906
1502
  rootDirectory: railwayConnectionLabel(rootDirectory) || null,
@@ -908,10 +1504,11 @@ async function ensureRailwayServiceInstanceConfiguration({
908
1504
  healthcheckTimeoutSeconds: normalizeRailwayNumber(healthcheckTimeoutSeconds),
909
1505
  healthcheckIntervalSeconds: normalizeRailwayNumber(healthcheckIntervalSeconds),
910
1506
  restartPolicy: railwayConnectionLabel(restartPolicy) || null,
911
- runtimeMode: railwayConnectionLabel(runtimeMode) || null,
912
- sleepApplication: railwayConnectionLabel(runtimeMode) === "serverless" ? true : railwayConnectionLabel(runtimeMode) === "replicated" ? false : null
1507
+ runtimeMode: desiredRuntimeMode,
1508
+ deploymentRegion: desiredDeploymentRegion,
1509
+ sleepApplication: desiredRuntimeMode === "serverless" ? true : desiredRuntimeMode === "replicated" ? false : null
913
1510
  };
914
- const needsRuntimeConfig = desired.healthcheckPath !== null || desired.healthcheckTimeoutSeconds !== null || desired.runtimeMode !== null;
1511
+ const needsRuntimeConfig = desired.healthcheckPath !== null || desired.healthcheckTimeoutSeconds !== null || desired.runtimeMode !== null || desired.deploymentRegion !== null;
915
1512
  if (needsRuntimeConfig && current.runtimeConfigSupported !== true) {
916
1513
  throw new Error("Railway service instance runtime settings are unsupported by the current Railway API schema.");
917
1514
  }
@@ -921,7 +1518,7 @@ async function ensureRailwayServiceInstanceConfiguration({
921
1518
  if (desired.restartPolicy !== null) {
922
1519
  throw new Error("Railway service instance restart policies are unsupported by the current Railway API schema.");
923
1520
  }
924
- const drifted = desired.buildCommand !== null && desired.buildCommand !== current.buildCommand || desired.startCommand !== null && desired.startCommand !== current.startCommand || desired.cronSchedule !== null && desired.cronSchedule !== current.cronSchedule || desired.rootDirectory !== null && desired.rootDirectory !== current.rootDirectory || desired.healthcheckPath !== null && desired.healthcheckPath !== current.healthcheckPath || desired.healthcheckTimeoutSeconds !== null && desired.healthcheckTimeoutSeconds !== current.healthcheckTimeoutSeconds || desired.runtimeMode !== null && desired.runtimeMode !== current.runtimeMode;
1521
+ const drifted = desired.buildCommand !== null && desired.buildCommand !== current.buildCommand || desired.dockerfilePath !== null && desired.dockerfilePath !== current.dockerfilePath || desired.railwayConfigFile !== null && desired.railwayConfigFile !== current.railwayConfigFile || desired.startCommand !== null && desired.startCommand !== current.startCommand || desired.cronSchedule !== null && desired.cronSchedule !== current.cronSchedule || desired.rootDirectory !== null && desired.rootDirectory !== current.rootDirectory || desired.healthcheckPath !== null && desired.healthcheckPath !== current.healthcheckPath || desired.healthcheckTimeoutSeconds !== null && desired.healthcheckTimeoutSeconds !== current.healthcheckTimeoutSeconds || desired.runtimeMode !== null && desired.runtimeMode !== current.runtimeMode || desired.deploymentRegion !== null;
925
1522
  if (!drifted) {
926
1523
  return { instance: current, updated: false };
927
1524
  }
@@ -942,12 +1539,19 @@ mutation TreeseedRailwayServiceInstanceUpdateLegacy($serviceId: String!, $enviro
942
1539
  environmentId,
943
1540
  input: {
944
1541
  ...desired.buildCommand !== null ? { buildCommand: desired.buildCommand } : {},
1542
+ ...desired.dockerfilePath !== null ? { dockerfilePath: desired.dockerfilePath } : {},
1543
+ ...desired.railwayConfigFile !== null ? { railwayConfigFile: desired.railwayConfigFile } : {},
945
1544
  ...desired.startCommand !== null ? { startCommand: desired.startCommand } : {},
946
1545
  ...desired.cronSchedule !== null ? { cronSchedule: desired.cronSchedule } : {},
947
1546
  ...desired.rootDirectory !== null ? { rootDirectory: desired.rootDirectory } : {},
948
1547
  ...desired.healthcheckPath !== null ? { healthcheckPath: desired.healthcheckPath } : {},
949
1548
  ...desired.healthcheckTimeoutSeconds !== null ? { healthcheckTimeout: desired.healthcheckTimeoutSeconds } : {},
950
- ...desired.sleepApplication !== null ? { sleepApplication: desired.sleepApplication } : {}
1549
+ ...desired.sleepApplication !== null ? { sleepApplication: desired.sleepApplication } : {},
1550
+ ...desired.deploymentRegion !== null ? {
1551
+ multiRegionConfig: {
1552
+ [desired.deploymentRegion]: { numReplicas: 1 }
1553
+ }
1554
+ } : {}
951
1555
  }
952
1556
  },
953
1557
  env,
@@ -1041,6 +1645,21 @@ mutation TreeseedRailwayVariableCollectionUpsert($input: VariableCollectionUpser
1041
1645
  replace: false,
1042
1646
  skipDeploys: true
1043
1647
  };
1648
+ const upsertOne = (key, value) => railwayGraphqlRequest({
1649
+ query,
1650
+ variables: {
1651
+ input: {
1652
+ projectId,
1653
+ environmentId,
1654
+ serviceId: serviceId || null,
1655
+ variables: { [key]: value },
1656
+ replace: false,
1657
+ skipDeploys: true
1658
+ }
1659
+ },
1660
+ env,
1661
+ fetchImpl
1662
+ });
1044
1663
  try {
1045
1664
  await railwayGraphqlRequest({
1046
1665
  query,
@@ -1054,23 +1673,29 @@ mutation TreeseedRailwayVariableCollectionUpsert($input: VariableCollectionUpser
1054
1673
  throw error;
1055
1674
  }
1056
1675
  for (const [key, value] of Object.entries(variables)) {
1057
- await railwayGraphqlRequest({
1058
- query,
1059
- variables: {
1060
- input: {
1061
- projectId,
1062
- environmentId,
1063
- serviceId: serviceId || null,
1064
- variables: { [key]: value },
1065
- replace: false,
1066
- skipDeploys: true
1067
- }
1068
- },
1069
- env,
1070
- fetchImpl
1071
- });
1676
+ await upsertOne(key, value);
1677
+ }
1678
+ }
1679
+ const observed = await listRailwayVariables({ projectId, environmentId, serviceId, env, fetchImpl }).catch(() => ({}));
1680
+ const missing = Object.keys(variables).filter((key) => observed[key] == null);
1681
+ for (const key of missing) {
1682
+ await upsertOne(key, variables[key]);
1683
+ }
1684
+ let retried = missing.length > 0 ? await listRailwayVariables({ projectId, environmentId, serviceId, env, fetchImpl }).catch(() => ({})) : observed;
1685
+ let stillMissing = Object.keys(variables).filter((key) => retried[key] == null);
1686
+ for (let attempt = 0; stillMissing.length > 0 && attempt < 12; attempt += 1) {
1687
+ await new Promise((resolve) => setTimeout(resolve, 2500));
1688
+ retried = await listRailwayVariables({ projectId, environmentId, serviceId, env, fetchImpl }).catch(() => ({}));
1689
+ stillMissing = Object.keys(variables).filter((key) => retried[key] == null);
1690
+ if (stillMissing.length > 0 && attempt === 5) {
1691
+ for (const key of stillMissing) {
1692
+ await upsertOne(key, variables[key]);
1693
+ }
1072
1694
  }
1073
1695
  }
1696
+ if (stillMissing.length > 0) {
1697
+ throw new Error(`Railway variable upsert did not persist: ${stillMissing.join(", ")}.`);
1698
+ }
1074
1699
  }
1075
1700
  async function listRailwayVolumes({
1076
1701
  projectId,
@@ -1095,6 +1720,8 @@ query TreeseedRailwayVolumeList($projectId: String!) {
1095
1720
  environmentId
1096
1721
  mountPath
1097
1722
  state
1723
+ isPendingDeletion
1724
+ deletedAt
1098
1725
  }
1099
1726
  }
1100
1727
  }
@@ -1129,15 +1756,17 @@ mutation TreeseedRailwayVolumeCreate($input: VolumeCreateInput!) {
1129
1756
  projectId
1130
1757
  volumeInstances {
1131
1758
  edges {
1132
- node {
1133
- id
1134
- serviceId
1135
- environmentId
1136
- mountPath
1137
- state
1759
+ node {
1760
+ id
1761
+ serviceId
1762
+ environmentId
1763
+ mountPath
1764
+ state
1765
+ isPendingDeletion
1766
+ deletedAt
1767
+ }
1138
1768
  }
1139
1769
  }
1140
- }
1141
1770
  }
1142
1771
  }
1143
1772
  `.trim();
@@ -1193,15 +1822,17 @@ mutation TreeseedRailwayVolumeUpdate($volumeId: String!, $input: VolumeUpdateInp
1193
1822
  projectId
1194
1823
  volumeInstances {
1195
1824
  edges {
1196
- node {
1197
- id
1198
- serviceId
1199
- environmentId
1200
- mountPath
1201
- state
1825
+ node {
1826
+ id
1827
+ serviceId
1828
+ environmentId
1829
+ mountPath
1830
+ state
1831
+ isPendingDeletion
1832
+ deletedAt
1833
+ }
1202
1834
  }
1203
1835
  }
1204
- }
1205
1836
  }
1206
1837
  }
1207
1838
  `.trim();
@@ -1233,7 +1864,7 @@ mutation TreeseedRailwayVolumeInstanceUpdate($volumeId: String!, $input: VolumeI
1233
1864
  variables: {
1234
1865
  volumeId,
1235
1866
  input: {
1236
- ...serviceId ? { serviceId } : {},
1867
+ ...serviceId !== void 0 ? { serviceId } : {},
1237
1868
  mountPath
1238
1869
  }
1239
1870
  },
@@ -1248,7 +1879,9 @@ async function ensureRailwayServiceVolume({
1248
1879
  name,
1249
1880
  mountPath,
1250
1881
  env = process.env,
1251
- fetchImpl = fetch
1882
+ fetchImpl = fetch,
1883
+ settleAttempts = 24,
1884
+ settleDelayMs = 5e3
1252
1885
  }) {
1253
1886
  if (!mountPath.startsWith("/")) {
1254
1887
  throw new Error(`Railway volume mount path must be absolute: ${mountPath}`);
@@ -1258,8 +1891,25 @@ async function ensureRailwayServiceVolume({
1258
1891
  ...candidate,
1259
1892
  instances: candidate.instances.filter(isActiveRailwayVolumeInstance)
1260
1893
  })).filter((candidate) => candidate.instances.length > 0);
1261
- let volume = findRailwayVolumeForService(volumes, serviceId, environmentId) ?? activeVolumes.find(
1894
+ const exactVolume = activeVolumes.find(
1895
+ (candidate) => candidate.name === name && candidate.instances.some(
1896
+ (instance2) => instance2.serviceId === serviceId && instance2.environmentId === environmentId && instance2.mountPath === mountPath
1897
+ )
1898
+ ) ?? null;
1899
+ if (exactVolume) {
1900
+ return {
1901
+ volume: exactVolume,
1902
+ instance: exactVolume.instances.find(
1903
+ (instance2) => instance2.serviceId === serviceId && instance2.environmentId === environmentId && instance2.mountPath === mountPath
1904
+ ) ?? null,
1905
+ created: false,
1906
+ updated: false
1907
+ };
1908
+ }
1909
+ let volume = findRailwayVolumeForService(volumes, serviceId, environmentId) ?? findRailwayVolumeForService(volumes, serviceId) ?? activeVolumes.find(
1262
1910
  (candidate) => candidate.name === name && candidate.instances.some((instance2) => instance2.environmentId === environmentId)
1911
+ ) ?? activeVolumes.find(
1912
+ (candidate) => candidate.name === name && (candidate.instances.length === 0 || candidate.instances.some((instance2) => instance2.environmentId === environmentId))
1263
1913
  ) ?? null;
1264
1914
  let created = false;
1265
1915
  let updated = false;
@@ -1282,7 +1932,13 @@ async function ensureRailwayServiceVolume({
1282
1932
  for (let attempt = 0; attempt < 8; attempt += 1) {
1283
1933
  await new Promise((resolve) => setTimeout(resolve, 1500));
1284
1934
  const refreshed = await listRailwayVolumes({ projectId, env, fetchImpl });
1285
- const existing = findRailwayVolumeForService(refreshed, serviceId, environmentId);
1935
+ const activeRefreshed = refreshed.map((candidate) => ({
1936
+ ...candidate,
1937
+ instances: candidate.instances.filter(isActiveRailwayVolumeInstance)
1938
+ })).filter((candidate) => candidate.instances.length > 0);
1939
+ const existing = findRailwayVolumeForService(refreshed, serviceId, environmentId) ?? findRailwayVolumeForService(refreshed, serviceId) ?? activeRefreshed.find(
1940
+ (candidate) => candidate.name === name && (candidate.instances.length === 0 || candidate.instances.some((instance2) => instance2.environmentId === environmentId))
1941
+ ) ?? findSoleActiveRailwayVolumeForEnvironment(refreshed, environmentId);
1286
1942
  if (existing) {
1287
1943
  return existing;
1288
1944
  }
@@ -1295,7 +1951,9 @@ async function ensureRailwayServiceVolume({
1295
1951
  if (!volume) {
1296
1952
  volume = await createReplacementVolume();
1297
1953
  }
1298
- const desiredNameInUse = volumes.some((candidate) => candidate.id !== volume?.id && candidate.name === name);
1954
+ const desiredNameInUse = volumes.some(
1955
+ (candidate) => candidate.id !== volume?.id && candidate.name === name && candidate.instances.some(isActiveRailwayVolumeInstance)
1956
+ );
1299
1957
  if (volume.name && volume.name !== name && !desiredNameInUse) {
1300
1958
  try {
1301
1959
  volume = await updateRailwayVolumeName({ volumeId: volume.id, name, env, fetchImpl }) ?? { ...volume, name };
@@ -1313,10 +1971,33 @@ async function ensureRailwayServiceVolume({
1313
1971
  await updateRailwayVolumeInstanceMountPath({ volumeId: volume.id, serviceId, mountPath, env, fetchImpl });
1314
1972
  volume = await listRailwayVolumes({ projectId, env, fetchImpl }).then((refreshed) => refreshed.find((candidate) => candidate.id === volume?.id) ?? volume);
1315
1973
  } catch (error) {
1316
- if (!looksLikeRailwayMissingResource(error)) {
1974
+ if (looksLikeRailwayVolumeCreateRace(error)) {
1975
+ volume = await listRailwayVolumes({ projectId, env, fetchImpl }).then(
1976
+ (refreshed) => findRailwayVolumeForService(refreshed, serviceId, environmentId) ?? volume
1977
+ );
1978
+ } else if (!looksLikeRailwayMissingResource(error)) {
1317
1979
  throw error;
1980
+ } else {
1981
+ volume = await createReplacementVolume();
1982
+ }
1983
+ }
1984
+ instance = volume.instances.find((entry) => entry.serviceId === serviceId && entry.environmentId === environmentId) ?? null;
1985
+ updated = true;
1986
+ }
1987
+ if (!instance) {
1988
+ try {
1989
+ await updateRailwayVolumeInstanceMountPath({ volumeId: volume.id, serviceId, mountPath, env, fetchImpl });
1990
+ volume = await listRailwayVolumes({ projectId, env, fetchImpl }).then((refreshed) => refreshed.find((candidate) => candidate.id === volume?.id) ?? volume);
1991
+ } catch (error) {
1992
+ if (looksLikeRailwayVolumeCreateRace(error)) {
1993
+ volume = await listRailwayVolumes({ projectId, env, fetchImpl }).then(
1994
+ (refreshed) => findRailwayVolumeForService(refreshed, serviceId, environmentId) ?? volume
1995
+ );
1996
+ } else if (!looksLikeRailwayMissingResource(error)) {
1997
+ throw error;
1998
+ } else {
1999
+ volume = await createReplacementVolume();
1318
2000
  }
1319
- volume = await createReplacementVolume();
1320
2001
  }
1321
2002
  instance = volume.instances.find((entry) => entry.serviceId === serviceId && entry.environmentId === environmentId) ?? null;
1322
2003
  updated = true;
@@ -1337,18 +2018,77 @@ async function ensureRailwayServiceVolume({
1337
2018
  }
1338
2019
  updated = true;
1339
2020
  }
2021
+ const settled = await waitForRailwayVolumeMount({
2022
+ projectId,
2023
+ volume,
2024
+ serviceId,
2025
+ environmentId,
2026
+ mountPath,
2027
+ env,
2028
+ fetchImpl,
2029
+ settleAttempts,
2030
+ settleDelayMs
2031
+ });
2032
+ if (settled) {
2033
+ volume = settled.volume;
2034
+ instance = settled.instance;
2035
+ }
2036
+ if (!instance || instance.serviceId !== serviceId || instance.environmentId !== environmentId || instance.mountPath !== mountPath) {
2037
+ throw new Error(`Railway API volume reconciliation did not observe ${name} mounted on service ${serviceId} at ${mountPath}.`);
2038
+ }
1340
2039
  return { volume, instance, created, updated };
1341
2040
  }
2041
+ async function waitForRailwayVolumeMount({
2042
+ projectId,
2043
+ volume,
2044
+ serviceId,
2045
+ environmentId,
2046
+ mountPath,
2047
+ env,
2048
+ fetchImpl,
2049
+ settleAttempts,
2050
+ settleDelayMs
2051
+ }) {
2052
+ const mountedInstance = (candidate) => candidate.instances.find(
2053
+ (entry) => entry.serviceId === serviceId && entry.environmentId === environmentId && entry.mountPath === mountPath && isActiveRailwayVolumeInstance(entry)
2054
+ ) ?? null;
2055
+ let instance = mountedInstance(volume);
2056
+ if (instance) {
2057
+ return { volume, instance };
2058
+ }
2059
+ for (let attempt = 0; attempt < settleAttempts; attempt += 1) {
2060
+ await new Promise((resolve) => setTimeout(resolve, settleDelayMs));
2061
+ const refreshed = await listRailwayVolumes({ projectId, env, fetchImpl });
2062
+ const refreshedVolume = refreshed.find((candidate) => candidate.id === volume.id) ?? findRailwayVolumeForService(refreshed, serviceId, environmentId) ?? null;
2063
+ if (!refreshedVolume) {
2064
+ continue;
2065
+ }
2066
+ instance = mountedInstance(refreshedVolume);
2067
+ if (instance) {
2068
+ return { volume: refreshedVolume, instance };
2069
+ }
2070
+ }
2071
+ return null;
2072
+ }
1342
2073
  function findRailwayVolumeForService(volumes, serviceId, environmentId) {
1343
2074
  return volumes.find(
1344
2075
  (candidate) => candidate.instances.some(
1345
- (instance) => instance.serviceId === serviceId && instance.environmentId === environmentId && isActiveRailwayVolumeInstance(instance)
2076
+ (instance) => instance.serviceId === serviceId && (!environmentId || instance.environmentId === environmentId) && isActiveRailwayVolumeInstance(instance)
1346
2077
  )
1347
2078
  ) ?? null;
1348
2079
  }
2080
+ function findSoleActiveRailwayVolumeForEnvironment(volumes, environmentId) {
2081
+ const active = volumes.map((candidate) => ({
2082
+ ...candidate,
2083
+ instances: candidate.instances.filter(
2084
+ (instance) => instance.environmentId === environmentId && isActiveRailwayVolumeInstance(instance)
2085
+ )
2086
+ })).filter((candidate) => candidate.instances.length > 0);
2087
+ return active.length === 1 ? active[0] : null;
2088
+ }
1349
2089
  function looksLikeRailwayVolumeCreateRace(error) {
1350
2090
  const message = error instanceof Error ? error.message : String(error ?? "");
1351
- return /would have \d+ volumes attached|can only have one volume|not authorized/iu.test(message);
2091
+ return /already has a volume attached|would have \d+ volumes attached|can only have one volume|volume named .* already exists|already exists in this project|not authorized/iu.test(message);
1352
2092
  }
1353
2093
  async function listRailwayCustomDomains({
1354
2094
  projectId,
@@ -1463,6 +2203,10 @@ function looksLikeRailwayMissingResource(error) {
1463
2203
  const message = error instanceof Error ? error.message : String(error ?? "");
1464
2204
  return /not found|does not exist|could not find|unknown|invalid .*id/iu.test(message);
1465
2205
  }
2206
+ function looksLikeRailwayOperationInProgress(error) {
2207
+ const message = error instanceof Error ? error.message : String(error ?? "");
2208
+ return /operation is already in progress/iu.test(message);
2209
+ }
1466
2210
  async function railwayDeleteMutation({
1467
2211
  query,
1468
2212
  variables,
@@ -1470,20 +2214,32 @@ async function railwayDeleteMutation({
1470
2214
  fetchImpl,
1471
2215
  missingResult
1472
2216
  }) {
1473
- try {
1474
- await railwayGraphqlRequest({
1475
- query,
1476
- variables,
1477
- env,
1478
- fetchImpl
1479
- });
1480
- return { status: "deleted" };
1481
- } catch (error) {
1482
- if (looksLikeRailwayMissingResource(error)) {
1483
- return missingResult;
2217
+ let lastError = null;
2218
+ for (let attempt = 0; attempt < 6; attempt += 1) {
2219
+ try {
2220
+ const payload = await railwayGraphqlRequest({
2221
+ query,
2222
+ variables,
2223
+ env,
2224
+ fetchImpl
2225
+ });
2226
+ const mutationResult = Object.values(payload.data ?? {})[0];
2227
+ if (mutationResult === false || mutationResult == null) {
2228
+ throw new Error("Railway delete mutation returned no successful deletion result.");
2229
+ }
2230
+ return { status: "deleted" };
2231
+ } catch (error) {
2232
+ if (looksLikeRailwayMissingResource(error)) {
2233
+ return missingResult;
2234
+ }
2235
+ if (!looksLikeRailwayOperationInProgress(error) || attempt >= 5) {
2236
+ throw error;
2237
+ }
2238
+ lastError = error;
2239
+ await new Promise((resolve) => setTimeout(resolve, 2500 * (attempt + 1)));
1484
2240
  }
1485
- throw error;
1486
2241
  }
2242
+ throw lastError instanceof Error ? lastError : new Error(String(lastError ?? "Railway delete mutation did not complete."));
1487
2243
  }
1488
2244
  async function deleteRailwayCustomDomain({
1489
2245
  domainId,
@@ -1506,6 +2262,27 @@ mutation TreeseedRailwayCustomDomainDelete($id: String!) {
1506
2262
  missingResult: { status: "missing", id: domainId }
1507
2263
  });
1508
2264
  }
2265
+ async function deleteRailwayService({
2266
+ serviceId,
2267
+ env = process.env,
2268
+ fetchImpl = fetch
2269
+ }) {
2270
+ if (!railwayConnectionLabel(serviceId)) {
2271
+ return { status: "missing", id: serviceId };
2272
+ }
2273
+ const mutation = configuredEnvValue(env, "TREESEED_RAILWAY_SERVICE_DELETE_MUTATION") || `
2274
+ mutation TreeseedRailwayServiceDelete($id: String!) {
2275
+ serviceDelete(id: $id)
2276
+ }
2277
+ `.trim();
2278
+ return railwayDeleteMutation({
2279
+ query: mutation,
2280
+ variables: { id: serviceId },
2281
+ env,
2282
+ fetchImpl,
2283
+ missingResult: { status: "missing", id: serviceId }
2284
+ });
2285
+ }
1509
2286
  async function deleteRailwayVolume({
1510
2287
  volumeId,
1511
2288
  env = process.env,
@@ -1514,18 +2291,55 @@ async function deleteRailwayVolume({
1514
2291
  if (!railwayConnectionLabel(volumeId)) {
1515
2292
  return { status: "missing", id: volumeId };
1516
2293
  }
1517
- const mutation = configuredEnvValue(env, "TREESEED_RAILWAY_VOLUME_DELETE_MUTATION") || `
2294
+ const configuredMutation = configuredEnvValue(env, "TREESEED_RAILWAY_VOLUME_DELETE_MUTATION");
2295
+ if (configuredMutation) {
2296
+ return railwayDeleteMutation({
2297
+ query: configuredMutation,
2298
+ variables: { volumeId, id: volumeId },
2299
+ env,
2300
+ fetchImpl,
2301
+ missingResult: { status: "missing", id: volumeId }
2302
+ });
2303
+ }
2304
+ const primaryMutation = `
1518
2305
  mutation TreeseedRailwayVolumeDelete($volumeId: String!) {
1519
2306
  volumeDelete(volumeId: $volumeId)
1520
2307
  }
1521
2308
  `.trim();
1522
- return railwayDeleteMutation({
1523
- query: mutation,
2309
+ const fallbackMutation = `
2310
+ mutation TreeseedRailwayVolumeDeleteById($id: String!) {
2311
+ volumeDelete(volumeId: $id)
2312
+ }
2313
+ `.trim();
2314
+ const primary = await railwayDeleteMutation({
2315
+ query: primaryMutation,
1524
2316
  variables: { volumeId },
1525
2317
  env,
1526
2318
  fetchImpl,
1527
2319
  missingResult: { status: "missing", id: volumeId }
2320
+ }).catch((error) => {
2321
+ if (looksLikeRailwayVolumeDeleteShapeUnsupported(error)) {
2322
+ return null;
2323
+ }
2324
+ throw error;
1528
2325
  });
2326
+ const fallback = await railwayDeleteMutation({
2327
+ query: fallbackMutation,
2328
+ variables: { id: volumeId },
2329
+ env,
2330
+ fetchImpl,
2331
+ missingResult: { status: "missing", id: volumeId }
2332
+ }).catch((error) => {
2333
+ if (looksLikeRailwayVolumeDeleteShapeUnsupported(error) && primary) {
2334
+ return primary;
2335
+ }
2336
+ throw error;
2337
+ });
2338
+ return fallback ?? primary ?? { status: "deleted" };
2339
+ }
2340
+ function looksLikeRailwayVolumeDeleteShapeUnsupported(error) {
2341
+ const message = error instanceof Error ? error.message : String(error ?? "");
2342
+ return /Unknown argument|Cannot query field|Unknown field|Field .* is not defined|volumeDelete.*argument|Problem processing request/iu.test(message);
1529
2343
  }
1530
2344
  async function deleteRailwayEnvironment({
1531
2345
  environmentId,
@@ -1573,9 +2387,12 @@ export {
1573
2387
  deleteRailwayCustomDomain,
1574
2388
  deleteRailwayEnvironment,
1575
2389
  deleteRailwayProject,
2390
+ deleteRailwayService,
1576
2391
  deleteRailwayVolume,
2392
+ deployRailwayServiceInstance,
1577
2393
  ensureRailwayCustomDomain,
1578
2394
  ensureRailwayEnvironment,
2395
+ ensureRailwayGeneratedServiceDomain,
1579
2396
  ensureRailwayPostgresService,
1580
2397
  ensureRailwayProject,
1581
2398
  ensureRailwayService,
@@ -1584,10 +2401,13 @@ export {
1584
2401
  getRailwayAuthProfile,
1585
2402
  getRailwayProject,
1586
2403
  getRailwayServiceInstance,
2404
+ inspectRailwayServiceDeploymentHealth,
1587
2405
  isUsableRailwayToken,
1588
2406
  listRailwayCustomDomains,
2407
+ listRailwayEnvironmentServices,
1589
2408
  listRailwayEnvironments,
1590
2409
  listRailwayProjects,
2410
+ listRailwayServiceDomains,
1591
2411
  listRailwayServices,
1592
2412
  listRailwayVariables,
1593
2413
  listRailwayVolumes,
@@ -1597,6 +2417,8 @@ export {
1597
2417
  resolveRailwayApiUrl,
1598
2418
  resolveRailwayWorkspace,
1599
2419
  resolveRailwayWorkspaceContext,
2420
+ updateRailwayServiceGitSource,
2421
+ updateRailwayServiceImageSource,
1600
2422
  updateRailwayServiceName,
1601
2423
  upsertRailwayVariables
1602
2424
  };