@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
@@ -7,7 +7,6 @@ import { resolveTreeseedWebCachePolicy } from "../../platform/deploy-config.js";
7
7
  import {
8
8
  deleteRailwayCustomDomain,
9
9
  deleteRailwayEnvironment,
10
- deleteRailwayProject,
11
10
  deleteRailwayVolume,
12
11
  getRailwayServiceInstance,
13
12
  listRailwayCustomDomains,
@@ -23,7 +22,7 @@ import { loadCliDeployConfig, resolveWranglerBin } from "./runtime-tools.js";
23
22
  import { sdkD1MigrationsRoot } from "./runtime-paths.js";
24
23
  const DEFAULT_COMPATIBILITY_DATE = "2026-04-05";
25
24
  const DEFAULT_COMPATIBILITY_FLAGS = ["nodejs_compat"];
26
- const DEFAULT_TREESEED_MARKET_BASE_URL = "https://api.treeseed.ai";
25
+ const DEFAULT_TREESEED_MARKET_BASE_URL = "https://api.treeseed.dev";
27
26
  const GENERATED_ROOT = ".treeseed/generated";
28
27
  const STATE_ROOT = ".treeseed/state";
29
28
  const WORKTREE_METADATA_RELATIVE_PATH = ".treeseed/worktree.json";
@@ -146,6 +145,24 @@ function deriveApiDomainFromWebDomain(domain) {
146
145
  }
147
146
  return domain.startsWith("api.") ? domain : `api.${domain}`;
148
147
  }
148
+ function configuredApiConnectionDomain(deployConfig, scope) {
149
+ const apiConnection = deployConfig.connections?.api?.environments?.[scope];
150
+ const domain = apiConnection?.domain?.trim();
151
+ if (domain) {
152
+ return domain;
153
+ }
154
+ const connectionBaseUrl = primaryHost(apiConnection?.baseUrl);
155
+ if (connectionBaseUrl) {
156
+ return connectionBaseUrl;
157
+ }
158
+ const serviceDomain = deployConfig.services?.api?.environments?.[scope]?.domain?.trim();
159
+ if (serviceDomain) {
160
+ return serviceDomain;
161
+ }
162
+ return primaryHost(
163
+ deployConfig.services?.api?.environments?.[scope]?.baseUrl ?? deployConfig.services?.api?.publicBaseUrl
164
+ );
165
+ }
149
166
  function resolveConfiguredSurfaceDomain(deployConfig, target, surface) {
150
167
  if (target.kind !== "persistent") {
151
168
  return null;
@@ -155,6 +172,12 @@ function resolveConfiguredSurfaceDomain(deployConfig, target, surface) {
155
172
  if (configured) {
156
173
  return configured;
157
174
  }
175
+ if (surface === "api") {
176
+ const apiConnectionDomain = configuredApiConnectionDomain(deployConfig, scope);
177
+ if (apiConnectionDomain) {
178
+ return apiConnectionDomain;
179
+ }
180
+ }
158
181
  if (scope === "staging") {
159
182
  return deriveTreeseedStagingSurfaceDomain(
160
183
  deployConfig,
@@ -171,6 +194,14 @@ function resolveConfiguredSurfaceDomain(deployConfig, target, surface) {
171
194
  return deriveApiDomainFromWebDomain(resolveConfiguredSurfaceDomain(deployConfig, target, "web"));
172
195
  }
173
196
  function resolveConfiguredSurfaceBaseUrl(deployConfig, target, surface) {
197
+ if (surface === "api") {
198
+ const scope = scopeFromTarget(target);
199
+ const apiConnection = deployConfig.connections?.api?.environments?.[scope];
200
+ const connectionBaseUrl = apiConnection?.baseUrl ?? (apiConnection?.domain ? `https://${apiConnection.domain}` : null);
201
+ if (connectionBaseUrl) {
202
+ return connectionBaseUrl;
203
+ }
204
+ }
174
205
  const configuredDomain = resolveConfiguredSurfaceDomain(deployConfig, target, surface);
175
206
  if (configuredDomain) {
176
207
  return `https://${configuredDomain}`;
@@ -301,6 +332,7 @@ function resolveContentServingMode(deployConfig, options = {}) {
301
332
  return deployConfig.providers?.content?.serving ?? "local_collections";
302
333
  }
303
334
  function buildPublicVars(deployConfig, options = {}) {
335
+ const target = options.target ? normalizeTarget(options.target) : createPersistentDeployTarget("prod");
304
336
  const identity = resolveTreeseedResourceIdentity(deployConfig, createPersistentDeployTarget("prod"));
305
337
  const contentRuntimeProvider = deployConfig.providers?.content?.runtime ?? "team_scoped_r2_overlay";
306
338
  const contentPublishProvider = deployConfig.providers?.content?.publish ?? contentRuntimeProvider;
@@ -312,20 +344,23 @@ function buildPublicVars(deployConfig, options = {}) {
312
344
  const managedRuntime = deployConfig.runtime?.mode === "treeseed_managed";
313
345
  const workerRailway = deployConfig.services?.worker?.railway ?? {};
314
346
  const webCachePolicy = resolveTreeseedWebCachePolicy(deployConfig);
347
+ const projectDomain = target.kind === "persistent" ? resolveConfiguredSurfaceDomain(deployConfig, target, "web") : primaryHost(deployConfig.surfaces?.web?.publicBaseUrl ?? deployConfig.siteUrl);
315
348
  return {
316
349
  TREESEED_HOSTING_KIND: deployConfig.hosting?.kind ?? "self_hosted_project",
317
350
  TREESEED_HOSTING_REGISTRATION: deployConfig.hosting?.registration ?? "none",
318
351
  TREESEED_HUB_MODE: deployConfig.hub?.mode ?? "treeseed_hosted",
319
352
  TREESEED_RUNTIME_MODE: deployConfig.runtime?.mode ?? "none",
320
353
  TREESEED_RUNTIME_REGISTRATION: deployConfig.runtime?.registration ?? "none",
321
- TREESEED_CENTRAL_MARKET_API_BASE_URL: envOrNull("TREESEED_CENTRAL_MARKET_API_BASE_URL") ?? DEFAULT_TREESEED_MARKET_BASE_URL,
322
- TREESEED_MARKET_API_BASE_URL: resolveConfiguredMarketBaseUrl(deployConfig),
323
- TREESEED_CATALOG_MARKET_API_BASE_URLS: envOrNull("TREESEED_CATALOG_MARKET_API_BASE_URLS") ?? resolveConfiguredMarketBaseUrl(deployConfig),
354
+ TREESEED_CENTRAL_MARKET_API_BASE_URL: resolveConfiguredCentralMarketBaseUrl(deployConfig, target),
355
+ TREESEED_MARKET_API_BASE_URL: resolveConfiguredMarketBaseUrl(deployConfig, target),
356
+ TREESEED_API_BASE_URL: resolveConfiguredMarketBaseUrl(deployConfig, target),
357
+ TREESEED_CATALOG_MARKET_API_BASE_URLS: resolveConfiguredMarketBaseUrl(deployConfig, target) ?? envOrNull("TREESEED_CATALOG_MARKET_API_BASE_URLS"),
324
358
  TREESEED_HOSTING_TEAM_ID: contentDefaultTeamId,
359
+ TREESEED_PROJECT_DOMAINS: projectDomain ?? "",
325
360
  TREESEED_PROJECT_ID: identity.projectId,
326
- TREESEED_AGENT_EXECUTION_PROVIDER: deployConfig.providers?.agents?.execution ?? "stub",
327
- TREESEED_AGENT_REPOSITORY_PROVIDER: deployConfig.providers?.agents?.repository ?? "stub",
328
- TREESEED_AGENT_VERIFICATION_PROVIDER: deployConfig.providers?.agents?.verification ?? "stub",
361
+ TREESEED_AGENT_EXECUTION_PROVIDER: deployConfig.providers?.agents?.execution ?? "codex",
362
+ TREESEED_AGENT_REPOSITORY_PROVIDER: deployConfig.providers?.agents?.repository ?? "git",
363
+ TREESEED_AGENT_VERIFICATION_PROVIDER: deployConfig.providers?.agents?.verification ?? "local",
329
364
  TREESEED_CONTENT_RUNTIME_PROVIDER: contentRuntimeProvider,
330
365
  TREESEED_CONTENT_PUBLISH_PROVIDER: contentPublishProvider,
331
366
  TREESEED_CONTENT_SERVING_MODE: contentServingMode,
@@ -380,7 +415,7 @@ function envValue(env, key) {
380
415
  return typeof value === "string" && value.trim() ? value.trim() : null;
381
416
  }
382
417
  const LOCAL_RUNTIME_AUTH_ENV_KEYS = [
383
- "BETTER_AUTH_URL",
418
+ "TREESEED_BETTER_AUTH_URL",
384
419
  "TREESEED_SITE_URL",
385
420
  "TREESEED_AUTH_MODE",
386
421
  "TREESEED_AUTH_INTERNAL_SIGNUP",
@@ -478,15 +513,7 @@ function defaultStateFromConfig(deployConfig, target) {
478
513
  previewDatabaseId: `dryrun-${suffix}-site-data-preview`
479
514
  }
480
515
  },
481
- queues: {
482
- agentWork: {
483
- name: environmentScopedIdentityName(identity, deployConfig.cloudflare.queueName ?? "agent-work", target),
484
- dlqName: environmentScopedIdentityName(identity, deployConfig.cloudflare.dlqName ?? "agent-work-dlq", target),
485
- binding: deployConfig.cloudflare.queueBinding ?? "AGENT_WORK_QUEUE",
486
- queueId: null,
487
- dlqId: null
488
- }
489
- },
516
+ queues: {},
490
517
  pages: {
491
518
  projectName: resolveConfiguredPagesProjectName(deployConfig),
492
519
  productionBranch: deployConfig.cloudflare.pages?.productionBranch ?? "main",
@@ -655,19 +682,7 @@ function loadDeployState(tenantRoot, deployConfig, options = {}) {
655
682
  binding: defaults.d1Databases.SITE_DATA_DB.binding
656
683
  }
657
684
  },
658
- queues: {
659
- ...defaults.queues ?? {},
660
- ...persisted.queues ?? {},
661
- agentWork: {
662
- ...defaults.queues?.agentWork ?? {},
663
- ...persisted.queues?.agentWork ?? {},
664
- name: defaults.queues?.agentWork?.name ?? persisted.queues?.agentWork?.name ?? "agent-work",
665
- dlqName: defaults.queues?.agentWork?.dlqName ?? persisted.queues?.agentWork?.dlqName ?? "agent-work-dlq",
666
- binding: defaults.queues?.agentWork?.binding ?? persisted.queues?.agentWork?.binding ?? "AGENT_WORK_QUEUE",
667
- queueId: persisted.queues?.agentWork?.queueId ?? defaults.queues?.agentWork?.queueId ?? null,
668
- dlqId: persisted.queues?.agentWork?.dlqId ?? defaults.queues?.agentWork?.dlqId ?? null
669
- }
670
- },
685
+ queues: {},
671
686
  generatedSecrets: {
672
687
  ...defaults.generatedSecrets ?? {},
673
688
  ...persisted.generatedSecrets ?? {}
@@ -906,7 +921,7 @@ function runWrangler(args, { cwd, allowFailure = false, json = false, capture =
906
921
  output || `Wrangler command failed: ${args.join(" ")}`,
907
922
  "",
908
923
  "Treeseed Cloudflare authentication failed. Check that CLOUDFLARE_API_TOKEN is an account-level token scoped to the target account and domain.",
909
- "Required Cloudflare permissions: Account Cloudflare Pages edit, Account Workers Scripts edit, Account Workers KV Storage edit, Account D1 edit, Account Queues edit, Zone DNS edit."
924
+ "Required Cloudflare permissions: account Pages Write, Workers Scripts Write, Workers KV Storage Write, Workers R2 Storage Write, D1 Write, Queues Write, Turnstile Sites Write, Account Rulesets Write, and Account Rule Lists Write; target zone Zone Read, DNS Write, Cache Settings Write, and SSL and Certificates Write."
910
925
  ].join("\n"));
911
926
  }
912
927
  throw new Error(output || `Wrangler command failed: ${args.join(" ")}`);
@@ -925,7 +940,7 @@ function isWranglerAlreadyExistsError(error, matchers) {
925
940
  return matchers.some((matcher) => matcher.test(message));
926
941
  }
927
942
  function listKvNamespaces(tenantRoot, env) {
928
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
943
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
929
944
  if (!accountId) {
930
945
  return [];
931
946
  }
@@ -936,7 +951,7 @@ function listKvNamespaces(tenantRoot, env) {
936
951
  return Array.isArray(payload?.result) ? payload.result : [];
937
952
  }
938
953
  function listD1Databases(tenantRoot, env) {
939
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
954
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
940
955
  if (!accountId) {
941
956
  return [];
942
957
  }
@@ -947,7 +962,7 @@ function listD1Databases(tenantRoot, env) {
947
962
  return Array.isArray(payload?.result) ? payload.result : [];
948
963
  }
949
964
  function listQueues(tenantRoot, env) {
950
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
965
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
951
966
  if (!accountId) {
952
967
  return [];
953
968
  }
@@ -958,7 +973,7 @@ function listQueues(tenantRoot, env) {
958
973
  return Array.isArray(payload?.result) ? payload.result : [];
959
974
  }
960
975
  function listR2Buckets(tenantRoot, env) {
961
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
976
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
962
977
  if (!accountId) {
963
978
  return [];
964
979
  }
@@ -969,7 +984,7 @@ function listR2Buckets(tenantRoot, env) {
969
984
  return Array.isArray(payload?.result?.buckets) ? payload.result.buckets : [];
970
985
  }
971
986
  function listPagesProjects(tenantRoot, env) {
972
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
987
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
973
988
  if (!accountId) {
974
989
  return [];
975
990
  }
@@ -980,7 +995,7 @@ function listPagesProjects(tenantRoot, env) {
980
995
  return Array.isArray(payload?.result) ? payload.result : [];
981
996
  }
982
997
  function listTurnstileWidgets(tenantRoot, env) {
983
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
998
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
984
999
  if (!accountId) {
985
1000
  return [];
986
1001
  }
@@ -990,8 +1005,50 @@ function listTurnstileWidgets(tenantRoot, env) {
990
1005
  });
991
1006
  return Array.isArray(payload?.result) ? payload.result : [];
992
1007
  }
1008
+ function listWorkers(tenantRoot, env) {
1009
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
1010
+ if (!accountId) {
1011
+ return [];
1012
+ }
1013
+ const payload = cloudflareApiRequest(`/accounts/${encodeURIComponent(accountId)}/workers/services?per_page=100`, {
1014
+ env,
1015
+ allowFailure: true
1016
+ });
1017
+ return Array.isArray(payload?.result) ? payload.result : [];
1018
+ }
1019
+ function listDnsZones(env) {
1020
+ const payload = cloudflareApiRequest("/zones?per_page=100", {
1021
+ env,
1022
+ allowFailure: true
1023
+ });
1024
+ return Array.isArray(payload?.result) ? payload.result : [];
1025
+ }
1026
+ function listDnsRecords(zoneId, env) {
1027
+ if (!zoneId) {
1028
+ return [];
1029
+ }
1030
+ const records = [];
1031
+ let page = 1;
1032
+ let totalPages = 1;
1033
+ while (page <= totalPages && page <= 50) {
1034
+ const payload = cloudflareApiRequest(
1035
+ `/zones/${encodeURIComponent(zoneId)}/dns_records?per_page=100&page=${page}`,
1036
+ { env, allowFailure: true }
1037
+ );
1038
+ if (payload?.success === false) {
1039
+ break;
1040
+ }
1041
+ if (Array.isArray(payload?.result)) {
1042
+ records.push(...payload.result);
1043
+ }
1044
+ const reportedTotal = Number(payload?.result_info?.total_pages);
1045
+ totalPages = Number.isFinite(reportedTotal) && reportedTotal > 0 ? reportedTotal : page;
1046
+ page += 1;
1047
+ }
1048
+ return records;
1049
+ }
993
1050
  function getTurnstileWidget(env, sitekey) {
994
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
1051
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
995
1052
  if (!accountId || !sitekey) {
996
1053
  return null;
997
1054
  }
@@ -1002,7 +1059,7 @@ function getTurnstileWidget(env, sitekey) {
1002
1059
  return payload?.result ?? null;
1003
1060
  }
1004
1061
  function createTurnstileWidget(env, input) {
1005
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
1062
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
1006
1063
  if (!accountId) {
1007
1064
  throw new Error("Configure CLOUDFLARE_ACCOUNT_ID before creating Turnstile widgets.");
1008
1065
  }
@@ -1022,7 +1079,7 @@ function createTurnstileWidget(env, input) {
1022
1079
  }
1023
1080
  }
1024
1081
  function updateTurnstileWidget(env, sitekey, input) {
1025
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
1082
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
1026
1083
  if (!accountId || !sitekey) {
1027
1084
  throw new Error("Configure CLOUDFLARE_ACCOUNT_ID and sitekey before updating Turnstile widgets.");
1028
1085
  }
@@ -1125,13 +1182,10 @@ function buildProvisioningSummary(deployConfig, state, target) {
1125
1182
  formGuardKv: state.kvNamespaces.FORM_GUARD_KV,
1126
1183
  sessionKv: state.kvNamespaces.SESSION ?? null,
1127
1184
  siteDataDb: state.d1Databases.SITE_DATA_DB,
1128
- queue: state.queues?.agentWork ?? null,
1129
1185
  content: state.content ?? null,
1130
1186
  resources: {
1131
1187
  pagesProject: state.pages?.projectName ?? null,
1132
1188
  contentBucket: state.content?.bucketName ?? null,
1133
- queue: state.queues?.agentWork?.name ?? null,
1134
- dlq: state.queues?.agentWork?.dlqName ?? null,
1135
1189
  database: state.d1Databases?.SITE_DATA_DB?.databaseName ?? null,
1136
1190
  turnstileWidget: state.turnstileWidgets?.formGuard?.name ?? null,
1137
1191
  formGuardKv: state.kvNamespaces?.FORM_GUARD_KV?.name ?? null,
@@ -1199,6 +1253,13 @@ function shouldManageCloudflareWebCacheRules(deployConfig, target) {
1199
1253
  return Boolean(webTarget?.host && !webTarget.host.endsWith(".workers.dev") && !webTarget.host.endsWith(".pages.dev"));
1200
1254
  }
1201
1255
  function cloudflareApiRequest(path, { method = "GET", body, env, allowFailure = false } = {}) {
1256
+ const token = env?.TREESEED_CLOUDFLARE_API_TOKEN ?? env?.CLOUDFLARE_API_TOKEN ?? process.env.TREESEED_CLOUDFLARE_API_TOKEN ?? "";
1257
+ if (!token) {
1258
+ if (allowFailure) {
1259
+ return null;
1260
+ }
1261
+ throw new Error(`Cloudflare API token is required: ${method} ${path}`);
1262
+ }
1202
1263
  const requestScript = `import { readFileSync } from 'node:fs';
1203
1264
  import { request } from 'node:https';
1204
1265
  const input = JSON.parse(readFileSync(0, 'utf8') || '{}');
@@ -1262,9 +1323,13 @@ try {
1262
1323
  method,
1263
1324
  body,
1264
1325
  timeoutMs: 12e3,
1265
- token: env?.CLOUDFLARE_API_TOKEN ?? process.env.CLOUDFLARE_API_TOKEN ?? ""
1326
+ token
1266
1327
  });
1267
- const isTransient = (text) => /fetch failed|timed out|etimedout|econnreset|enetunreach|temporarily unavailable|aborted/iu.test(text || "");
1328
+ const isTransient = (text) => /fetch failed|timed out|etimedout|econnreset|enetunreach|temporarily unavailable|aborted|rate limit|too many requests|throttl|please wait/iu.test(text || "");
1329
+ const retryDelay = (text, currentAttempt) => {
1330
+ const base = /rate limit|too many requests|throttl|please wait/iu.test(text || "") ? 2500 : 500;
1331
+ return base * (currentAttempt + 1);
1332
+ };
1268
1333
  const formatPayloadErrors = (payload) => Array.isArray(payload?.errors) ? payload.errors.map((entry) => entry?.message ?? JSON.stringify(entry)).join("; ") : "";
1269
1334
  const summarizeChildError = (text) => {
1270
1335
  const lines = String(text || "").split("\n").map((line) => line.trim()).filter(Boolean);
@@ -1288,9 +1353,9 @@ try {
1288
1353
  }
1289
1354
  );
1290
1355
  if (response.error?.code === "ETIMEDOUT") {
1291
- if (attempt < 4) {
1356
+ if (attempt < 7) {
1292
1357
  attempt += 1;
1293
- sleepSync(500 * attempt);
1358
+ sleepSync(retryDelay("timed out", attempt));
1294
1359
  continue;
1295
1360
  }
1296
1361
  if (!allowFailure) {
@@ -1300,9 +1365,9 @@ try {
1300
1365
  }
1301
1366
  const stderr = response.stderr?.trim() || "";
1302
1367
  if (response.status !== 0) {
1303
- if (attempt < 4 && isTransient(stderr)) {
1368
+ if (attempt < 7 && isTransient(stderr)) {
1304
1369
  attempt += 1;
1305
- sleepSync(500 * attempt);
1370
+ sleepSync(retryDelay(stderr, attempt));
1306
1371
  continue;
1307
1372
  }
1308
1373
  if (!allowFailure) {
@@ -1323,9 +1388,9 @@ try {
1323
1388
  };
1324
1389
  }
1325
1390
  const details = formatPayloadErrors(parsed.payload);
1326
- if (!parsed.ok && parsed.transient && attempt < 4 && isTransient(details)) {
1391
+ if (!parsed.ok && isTransient(details) && attempt < 7) {
1327
1392
  attempt += 1;
1328
- sleepSync(500 * attempt);
1393
+ sleepSync(retryDelay(details, attempt));
1329
1394
  continue;
1330
1395
  }
1331
1396
  if (!parsed.ok && !allowFailure) {
@@ -1352,6 +1417,13 @@ function listCloudflareZoneRulesets(zoneId, env) {
1352
1417
  const result = cloudflareApiRequest(`/zones/${zoneId}/rulesets`, { env, allowFailure: true });
1353
1418
  return Array.isArray(result?.result) ? result.result : [];
1354
1419
  }
1420
+ function joinCloudflareAndExpression(clauses) {
1421
+ const parts = clauses.map((clause) => typeof clause === "string" ? clause.trim() : "").filter((clause) => clause.length > 0).map((clause) => clause.startsWith("(") && clause.endsWith(")") ? clause : `(${clause})`);
1422
+ if (parts.length === 0) {
1423
+ throw new Error("Cannot build a Cloudflare expression without predicates.");
1424
+ }
1425
+ return `(${parts.join(" and ")})`;
1426
+ }
1355
1427
  function buildTreeseedManagedCloudflareCacheRules(deployConfig, cacheTarget, kind) {
1356
1428
  if (!cacheTarget?.host) {
1357
1429
  return [];
@@ -1359,12 +1431,16 @@ function buildTreeseedManagedCloudflareCacheRules(deployConfig, cacheTarget, kin
1359
1431
  const policy = resolveTreeseedWebCachePolicy(deployConfig);
1360
1432
  const cachePolicy = kind === "web" ? policy.contentPages : policy.r2PublishedObjects;
1361
1433
  const hostExpression = `(http.host eq "${cacheTarget.host}")`;
1362
- const pathExpression = cacheTarget.pathPrefix ? `(starts_with(http.request.uri.path, "${cacheTarget.pathPrefix}/") or (http.request.uri.path eq "${cacheTarget.pathPrefix}"))` : "true";
1434
+ const pathExpression = cacheTarget.pathPrefix ? `(starts_with(http.request.uri.path, "${cacheTarget.pathPrefix}/") or (http.request.uri.path eq "${cacheTarget.pathPrefix}"))` : null;
1363
1435
  if (kind === "content") {
1364
1436
  return [
1365
1437
  {
1366
1438
  description: "treeseed-managed: cache public r2 objects",
1367
- expression: `((${hostExpression}) and (${pathExpression}) and (http.request.method in {"GET" "HEAD"}))`,
1439
+ expression: joinCloudflareAndExpression([
1440
+ hostExpression,
1441
+ pathExpression,
1442
+ '(http.request.method in {"GET" "HEAD"})'
1443
+ ]),
1368
1444
  action: "set_cache_settings",
1369
1445
  action_parameters: {
1370
1446
  cache: true,
@@ -1382,21 +1458,31 @@ function buildTreeseedManagedCloudflareCacheRules(deployConfig, cacheTarget, kin
1382
1458
  ];
1383
1459
  }
1384
1460
  const sourcePaths = policy.sourcePages.paths.map((path) => path === "/" ? "/" : path.replace(/\/+$/u, ""));
1385
- const sourcePathExpression = sourcePaths.length > 0 ? `(${sourcePaths.map((path) => `(http.request.uri.path eq "${path}")`).join(" or ")})` : "false";
1386
- const notSourcePathExpression = sourcePaths.length > 0 ? `not ${sourcePathExpression}` : "true";
1387
- return [
1461
+ const sourcePathExpression = sourcePaths.length > 0 ? `(${sourcePaths.map((path) => `(http.request.uri.path eq "${path}")`).join(" or ")})` : null;
1462
+ const notSourcePathExpression = sourcePaths.length > 0 ? `not ${sourcePathExpression}` : null;
1463
+ const rules = [
1388
1464
  {
1389
1465
  description: "treeseed-managed: bypass preview and dynamic routes",
1390
- expression: `((${hostExpression}) and ((starts_with(http.request.uri.path, "/api/")) or (http.request.uri.path eq "/api") or (starts_with(http.request.uri.path, "/auth")) or (starts_with(http.request.uri.path, "/admin")) or (starts_with(http.request.uri.path, "/app")) or (starts_with(http.request.uri.path, "/internal")) or (http.request.uri.query contains "preview=") or (http.cookie contains "treeseed-content-preview=")))`,
1466
+ expression: joinCloudflareAndExpression([
1467
+ hostExpression,
1468
+ '((starts_with(http.request.uri.path, "/api/")) or (http.request.uri.path eq "/api") or (starts_with(http.request.uri.path, "/auth")) or (starts_with(http.request.uri.path, "/admin")) or (starts_with(http.request.uri.path, "/app")) or (starts_with(http.request.uri.path, "/internal")) or (http.request.uri.query contains "preview=") or (http.cookie contains "treeseed-content-preview="))'
1469
+ ]),
1391
1470
  action: "set_cache_settings",
1392
1471
  action_parameters: {
1393
1472
  cache: false
1394
1473
  },
1395
1474
  enabled: true
1396
- },
1397
- {
1475
+ }
1476
+ ];
1477
+ if (sourcePathExpression) {
1478
+ rules.push({
1398
1479
  description: "treeseed-managed: cache source html routes",
1399
- expression: `((${hostExpression}) and (${pathExpression}) and (${sourcePathExpression}) and (http.request.method in {"GET" "HEAD"}))`,
1480
+ expression: joinCloudflareAndExpression([
1481
+ hostExpression,
1482
+ pathExpression,
1483
+ sourcePathExpression,
1484
+ '(http.request.method in {"GET" "HEAD"})'
1485
+ ]),
1400
1486
  action: "set_cache_settings",
1401
1487
  action_parameters: {
1402
1488
  cache: true,
@@ -1410,10 +1496,26 @@ function buildTreeseedManagedCloudflareCacheRules(deployConfig, cacheTarget, kin
1410
1496
  }
1411
1497
  },
1412
1498
  enabled: true
1413
- },
1499
+ });
1500
+ }
1501
+ rules.push(
1414
1502
  {
1415
1503
  description: "treeseed-managed: cache content html routes",
1416
- expression: `((${hostExpression}) and (${pathExpression}) and (${notSourcePathExpression}) and (http.request.method in {"GET" "HEAD"}) and (http.request.uri.path.extension eq "") and not (starts_with(http.request.uri.path, "/api/")) and not (http.request.uri.path eq "/api") and not (starts_with(http.request.uri.path, "/auth")) and not (starts_with(http.request.uri.path, "/admin")) and not (starts_with(http.request.uri.path, "/app")) and not (starts_with(http.request.uri.path, "/internal")) and not (http.request.uri.query contains "preview=") and not (http.cookie contains "treeseed-content-preview="))`,
1504
+ expression: joinCloudflareAndExpression([
1505
+ hostExpression,
1506
+ pathExpression,
1507
+ notSourcePathExpression,
1508
+ '(http.request.method in {"GET" "HEAD"})',
1509
+ '(http.request.uri.path.extension eq "")',
1510
+ 'not (starts_with(http.request.uri.path, "/api/"))',
1511
+ 'not (http.request.uri.path eq "/api")',
1512
+ 'not (starts_with(http.request.uri.path, "/auth"))',
1513
+ 'not (starts_with(http.request.uri.path, "/admin"))',
1514
+ 'not (starts_with(http.request.uri.path, "/app"))',
1515
+ 'not (starts_with(http.request.uri.path, "/internal"))',
1516
+ 'not (http.request.uri.query contains "preview=")',
1517
+ 'not (http.cookie contains "treeseed-content-preview=")'
1518
+ ]),
1417
1519
  action: "set_cache_settings",
1418
1520
  action_parameters: {
1419
1521
  cache: true,
@@ -1428,7 +1530,8 @@ function buildTreeseedManagedCloudflareCacheRules(deployConfig, cacheTarget, kin
1428
1530
  },
1429
1531
  enabled: true
1430
1532
  }
1431
- ];
1533
+ );
1534
+ return rules;
1432
1535
  }
1433
1536
  function reconcileCloudflareCacheRulesForTarget(role, deployConfig, state, cacheTarget, env, { dryRun = false } = {}) {
1434
1537
  const roleKey = role === "web" ? "Web" : "Content";
@@ -1482,7 +1585,7 @@ function reconcileCloudflareWebCacheRules(tenantRoot, deployConfig, state, targe
1482
1585
  return { managed: false, skipped: true, reason: "unsupported_target_or_host" };
1483
1586
  }
1484
1587
  const env = {
1485
- CLOUDFLARE_API_TOKEN: providedEnv?.CLOUDFLARE_API_TOKEN ?? process.env.CLOUDFLARE_API_TOKEN ?? ""
1588
+ CLOUDFLARE_API_TOKEN: providedEnv?.CLOUDFLARE_API_TOKEN ?? process.env.TREESEED_CLOUDFLARE_API_TOKEN ?? ""
1486
1589
  };
1487
1590
  if (!env.CLOUDFLARE_API_TOKEN) {
1488
1591
  state.webCache.webHost = resolvePublicWebCacheTarget(deployConfig)?.host ?? null;
@@ -1555,7 +1658,7 @@ function queueId(entry) {
1555
1658
  }
1556
1659
  function hasProvisionedCloudflareResources(state) {
1557
1660
  return Boolean(
1558
- state?.pages?.projectName && state?.pages?.url && state?.d1Databases?.SITE_DATA_DB?.databaseId && state?.kvNamespaces?.FORM_GUARD_KV?.id && state?.queues?.agentWork?.name && state?.content?.bucketName
1661
+ state?.pages?.projectName && state?.pages?.url && state?.d1Databases?.SITE_DATA_DB?.databaseId && state?.kvNamespaces?.FORM_GUARD_KV?.id && state?.content?.bucketName
1559
1662
  );
1560
1663
  }
1561
1664
  function absoluteUrlForPath(baseUrl, path) {
@@ -1585,14 +1688,14 @@ function purgeSourcePageCaches(tenantRoot, options = {}) {
1585
1688
  const deployConfig = loadTenantDeployConfig(tenantRoot);
1586
1689
  const state = loadDeployState(tenantRoot, deployConfig, { target });
1587
1690
  const urls = resolveSourcePagePurgeUrls(deployConfig);
1588
- if ((options.dryRun ?? false) || urls.length === 0 || !process.env.CLOUDFLARE_API_TOKEN) {
1691
+ if ((options.dryRun ?? false) || urls.length === 0 || !process.env.TREESEED_CLOUDFLARE_API_TOKEN) {
1589
1692
  recordCachePurgeResult(state.webCache.deployPurge, urls.map((url) => ({ count: url ? 1 : 0 })));
1590
1693
  writeDeployState(tenantRoot, state, { target });
1591
1694
  return { skipped: options.dryRun ?? false, urls, results: [] };
1592
1695
  }
1593
1696
  try {
1594
1697
  const results = purgeCloudflareCacheByUrls(urls, deployConfig, {
1595
- env: { CLOUDFLARE_API_TOKEN: process.env.CLOUDFLARE_API_TOKEN }
1698
+ env: { CLOUDFLARE_API_TOKEN: process.env.TREESEED_CLOUDFLARE_API_TOKEN }
1596
1699
  });
1597
1700
  recordCachePurgeResult(state.webCache.deployPurge, results);
1598
1701
  writeDeployState(tenantRoot, state, { target });
@@ -1607,14 +1710,14 @@ function purgePublishedContentCaches(tenantRoot, urls, options = {}) {
1607
1710
  const target = normalizeTarget(options.scope ?? options.target ?? "prod");
1608
1711
  const deployConfig = loadTenantDeployConfig(tenantRoot);
1609
1712
  const state = loadDeployState(tenantRoot, deployConfig, { target });
1610
- if ((options.dryRun ?? false) || !urls?.length || !process.env.CLOUDFLARE_API_TOKEN) {
1713
+ if ((options.dryRun ?? false) || !urls?.length || !process.env.TREESEED_CLOUDFLARE_API_TOKEN) {
1611
1714
  recordCachePurgeResult(state.webCache.contentPurge, (urls ?? []).map((url) => ({ count: url ? 1 : 0 })));
1612
1715
  writeDeployState(tenantRoot, state, { target });
1613
1716
  return { skipped: options.dryRun ?? false, urls: urls ?? [], results: [] };
1614
1717
  }
1615
1718
  try {
1616
1719
  const results = purgeCloudflareCacheByUrls(urls, deployConfig, {
1617
- env: { CLOUDFLARE_API_TOKEN: process.env.CLOUDFLARE_API_TOKEN }
1720
+ env: { CLOUDFLARE_API_TOKEN: process.env.TREESEED_CLOUDFLARE_API_TOKEN }
1618
1721
  });
1619
1722
  recordCachePurgeResult(state.webCache.contentPurge, results);
1620
1723
  writeDeployState(tenantRoot, state, { target });
@@ -1634,8 +1737,41 @@ function isPlaceholderAccountId(value) {
1634
1737
  function resolveConfiguredCloudflareAccountId(deployConfig) {
1635
1738
  return envOrNull("CLOUDFLARE_ACCOUNT_ID") ?? deployConfig.cloudflare.accountId;
1636
1739
  }
1637
- function resolveConfiguredMarketBaseUrl(deployConfig) {
1638
- return envOrNull("TREESEED_MARKET_API_BASE_URL") ?? envOrNull("TREESEED_CENTRAL_MARKET_API_BASE_URL") ?? deployConfig.runtime?.marketBaseUrl ?? deployConfig.hosting?.marketBaseUrl ?? DEFAULT_TREESEED_MARKET_BASE_URL;
1740
+ function normalizeConfiguredBaseUrl(value) {
1741
+ const raw = typeof value === "string" ? value.trim() : "";
1742
+ if (!raw) {
1743
+ return null;
1744
+ }
1745
+ return raw.replace(/\/+$/u, "");
1746
+ }
1747
+ function domainBaseUrl(domain) {
1748
+ const raw = typeof domain === "string" ? domain.trim() : "";
1749
+ if (!raw) {
1750
+ return null;
1751
+ }
1752
+ if (/^https?:\/\//iu.test(raw)) {
1753
+ return normalizeConfiguredBaseUrl(raw);
1754
+ }
1755
+ return `https://${raw.replace(/^\/+|\/+$/gu, "")}`;
1756
+ }
1757
+ function targetEnvironmentKey(target) {
1758
+ if (target?.kind === "persistent") {
1759
+ return target.scope;
1760
+ }
1761
+ return "staging";
1762
+ }
1763
+ function resolveConfiguredApiConnectionBaseUrl(deployConfig, target) {
1764
+ const scope = targetEnvironmentKey(target);
1765
+ if (scope === "local") {
1766
+ return normalizeConfiguredBaseUrl(deployConfig.connections?.api?.localBaseUrl) ?? normalizeConfiguredBaseUrl(deployConfig.connections?.api?.environments?.local?.baseUrl) ?? domainBaseUrl(deployConfig.connections?.api?.environments?.local?.domain);
1767
+ }
1768
+ return normalizeConfiguredBaseUrl(deployConfig.connections?.api?.environments?.[scope]?.baseUrl) ?? domainBaseUrl(deployConfig.connections?.api?.environments?.[scope]?.domain);
1769
+ }
1770
+ function resolveConfiguredMarketBaseUrl(deployConfig, target) {
1771
+ return resolveConfiguredApiConnectionBaseUrl(deployConfig, target) ?? envOrNull("TREESEED_API_BASE_URL") ?? deployConfig.runtime?.marketBaseUrl ?? deployConfig.hosting?.marketBaseUrl ?? envOrNull("TREESEED_CENTRAL_MARKET_API_BASE_URL") ?? DEFAULT_TREESEED_MARKET_BASE_URL;
1772
+ }
1773
+ function resolveConfiguredCentralMarketBaseUrl(deployConfig, target) {
1774
+ return resolveConfiguredApiConnectionBaseUrl(deployConfig, target) ?? envOrNull("TREESEED_CENTRAL_MARKET_API_BASE_URL") ?? DEFAULT_TREESEED_MARKET_BASE_URL;
1639
1775
  }
1640
1776
  function resolveConfiguredPagesProjectName(deployConfig) {
1641
1777
  return sharedDeploymentName(resolveTreeseedResourceIdentity(deployConfig, createPersistentDeployTarget("prod")));
@@ -1803,7 +1939,7 @@ function resolveExistingD1ByName(d1Databases, expectedName, current) {
1803
1939
  };
1804
1940
  }
1805
1941
  function looksLikeMissingResource(output) {
1806
- return /not found|does not exist|could(?: not|n't) find|couldnt find/i.test(output);
1942
+ return /not found|does not exist|could(?: not|n't) find|couldnt find|already deleted|deleted widget|access a deleted/i.test(output);
1807
1943
  }
1808
1944
  function deleteKvNamespace(tenantRoot, namespaceId, { env, dryRun, preview = false }) {
1809
1945
  if (!namespaceId || isPlaceholderResourceId(namespaceId)) {
@@ -1812,7 +1948,7 @@ function deleteKvNamespace(tenantRoot, namespaceId, { env, dryRun, preview = fal
1812
1948
  if (dryRun) {
1813
1949
  return { status: "planned", id: namespaceId, preview };
1814
1950
  }
1815
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
1951
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
1816
1952
  const path = accountId ? `/accounts/${encodeURIComponent(accountId)}/storage/kv/namespaces/${encodeURIComponent(namespaceId)}` : null;
1817
1953
  const deleted = deleteCloudflareApiResource(path, { env, dryRun: false, name: namespaceId, type: "kv-namespace" });
1818
1954
  return { status: deleted.status, id: namespaceId, preview };
@@ -1824,7 +1960,7 @@ function deleteTurnstileWidget(sitekey, { env, dryRun, name = null }) {
1824
1960
  if (dryRun) {
1825
1961
  return { status: "planned", sitekey, name };
1826
1962
  }
1827
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
1963
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
1828
1964
  const path = accountId ? `/accounts/${encodeURIComponent(accountId)}/challenges/widgets/${encodeURIComponent(sitekey)}` : null;
1829
1965
  let deleted;
1830
1966
  try {
@@ -1842,7 +1978,7 @@ function deleteD1Database(tenantRoot, databaseName, { env, dryRun }) {
1842
1978
  if (dryRun) {
1843
1979
  return { status: "planned", name: databaseName };
1844
1980
  }
1845
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
1981
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
1846
1982
  const database = accountId ? listD1Databases(tenantRoot, env).find((entry) => entry?.name === databaseName) : null;
1847
1983
  const databaseId = database?.uuid ?? database?.id ?? null;
1848
1984
  const path = accountId && databaseId ? `/accounts/${encodeURIComponent(accountId)}/d1/database/${encodeURIComponent(databaseId)}` : null;
@@ -1856,7 +1992,7 @@ function deleteWorker(tenantRoot, workerName, { env, dryRun, force = false }) {
1856
1992
  if (dryRun) {
1857
1993
  return { status: "planned", name: workerName };
1858
1994
  }
1859
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
1995
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
1860
1996
  const path = accountId ? `/accounts/${encodeURIComponent(accountId)}/workers/services/${encodeURIComponent(workerName)}` : null;
1861
1997
  const deleted = deleteCloudflareApiResource(path, { env, dryRun: false, name: workerName, type: "worker" });
1862
1998
  return { status: deleted.status, name: workerName };
@@ -1895,7 +2031,7 @@ function deleteQueueByName(tenantRoot, queue, { env, dryRun }) {
1895
2031
  if (dryRun) {
1896
2032
  return resourceOperation("cloudflare", "queue", name, "planned", { id });
1897
2033
  }
1898
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
2034
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
1899
2035
  if (!id && accountId) {
1900
2036
  const live = listQueues(tenantRoot, env).find((entry) => queueName(entry) === name);
1901
2037
  id = queueId(live);
@@ -1912,25 +2048,6 @@ function deleteQueueByName(tenantRoot, queue, { env, dryRun }) {
1912
2048
  }
1913
2049
  throw new Error(`Failed to delete queue ${name}: CLOUDFLARE_ACCOUNT_ID is not configured.`);
1914
2050
  }
1915
- function isLegacyTreeseedQueueName(name, scope) {
1916
- if (!name || !scope) {
1917
- return false;
1918
- }
1919
- const environmentNames = scope === "prod" ? ["prod", "production"] : [scope];
1920
- return environmentNames.some(
1921
- (environmentName) => name === `agent-work-${environmentName}` || name === `agent-work-dlq-${environmentName}`
1922
- );
1923
- }
1924
- function legacyQueueDestroyOperations(tenantRoot, queues, target, knownNames, { env, dryRun }) {
1925
- const scope = target.kind === "persistent" ? target.scope : target.branchName;
1926
- return queues.filter((queue) => {
1927
- const name = queueName(queue);
1928
- return name && !knownNames.has(name) && isLegacyTreeseedQueueName(name, scope);
1929
- }).map((queue) => {
1930
- const deleted = deleteQueueByName(tenantRoot, queue, { env, dryRun });
1931
- return { ...deleted, legacy: true };
1932
- });
1933
- }
1934
2051
  function deleteR2Bucket(tenantRoot, bucketName, { env, dryRun, deleteData }) {
1935
2052
  if (!bucketName) {
1936
2053
  return resourceOperation("cloudflare", "r2-bucket", bucketName, "missing");
@@ -1942,7 +2059,7 @@ function deleteR2Bucket(tenantRoot, bucketName, { env, dryRun, deleteData }) {
1942
2059
  return resourceOperation("cloudflare", "r2-bucket", bucketName, "planned");
1943
2060
  }
1944
2061
  const drained = drainR2Bucket(bucketName, { env });
1945
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
2062
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
1946
2063
  const path = accountId ? `/accounts/${encodeURIComponent(accountId)}/r2/buckets/${encodeURIComponent(bucketName)}` : null;
1947
2064
  const deleted = deleteCloudflareApiResource(path, { env, dryRun: false, name: bucketName, type: "r2-bucket" });
1948
2065
  return resourceOperation("cloudflare", "r2-bucket", bucketName, deleted.status, drained);
@@ -1951,15 +2068,15 @@ function r2ObjectKey(entry) {
1951
2068
  return typeof entry?.key === "string" ? entry.key : typeof entry?.name === "string" ? entry.name : "";
1952
2069
  }
1953
2070
  function listR2Objects(bucketName, { env }) {
1954
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
2071
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
1955
2072
  if (!accountId || !bucketName) {
1956
2073
  return [];
1957
2074
  }
1958
2075
  const objects = [];
1959
2076
  let cursor = "";
1960
- for (let page = 0; page < 100; page += 1) {
2077
+ for (let page = 0; page < 20; page += 1) {
1961
2078
  const payload = cloudflareApiRequest(
1962
- `/accounts/${encodeURIComponent(accountId)}/r2/buckets/${encodeURIComponent(bucketName)}/objects?per_page=1000${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}`,
2079
+ `/accounts/${encodeURIComponent(accountId)}/r2/buckets/${encodeURIComponent(bucketName)}/objects?per_page=200${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}`,
1963
2080
  { env, allowFailure: true }
1964
2081
  );
1965
2082
  if (payload?.success === false) {
@@ -1972,45 +2089,156 @@ function listR2Objects(bucketName, { env }) {
1972
2089
  break;
1973
2090
  }
1974
2091
  cursor = nextCursor;
2092
+ if (objects.length >= 200) {
2093
+ break;
2094
+ }
1975
2095
  }
1976
2096
  return objects;
1977
2097
  }
1978
2098
  function drainR2Bucket(bucketName, { env }) {
1979
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
2099
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
1980
2100
  if (!accountId || !bucketName) {
1981
- return { objectsDeleted: 0 };
2101
+ return { objectsDeleted: 0, objectsMissing: 0, objectsDeferred: 0 };
1982
2102
  }
1983
2103
  let objectsDeleted = 0;
2104
+ let objectsMissing = 0;
2105
+ let objectsDeferred = 0;
1984
2106
  for (let batch = 0; batch < 100; batch += 1) {
1985
2107
  const objects = listR2Objects(bucketName, { env });
1986
2108
  if (objects.length === 0) {
1987
2109
  break;
1988
2110
  }
1989
- let batchDeleted = 0;
1990
- for (const object of objects) {
1991
- const key = r2ObjectKey(object);
1992
- if (!key) {
2111
+ const keys = objects.map((object) => r2ObjectKey(object)).filter(Boolean);
2112
+ const deleted = deleteR2ObjectsBatch(bucketName, keys, { env });
2113
+ objectsDeleted += deleted.objectsDeleted;
2114
+ objectsMissing += deleted.objectsMissing;
2115
+ objectsDeferred += deleted.objectsDeferred;
2116
+ const batchDeleted = deleted.objectsDeleted + deleted.objectsMissing;
2117
+ if (batchDeleted === 0) {
2118
+ if (deleted.objectsDeferred > 0) {
2119
+ sleepSync(3e3);
1993
2120
  continue;
1994
2121
  }
1995
- const result = cloudflareApiRequest(
1996
- `/accounts/${encodeURIComponent(accountId)}/r2/buckets/${encodeURIComponent(bucketName)}/objects/${encodeURIComponent(key)}`,
1997
- { method: "DELETE", env, allowFailure: true }
1998
- );
1999
- if (result?.success === false) {
2000
- const message = formatCloudflareErrors(result);
2001
- if (looksLikeMissingResource(message)) {
2002
- continue;
2003
- }
2004
- throw new Error(message || `Failed to delete R2 object ${key}.`);
2005
- }
2006
- objectsDeleted += 1;
2007
- batchDeleted += 1;
2008
- }
2009
- if (batchDeleted === 0) {
2010
2122
  break;
2011
2123
  }
2124
+ if (deleted.objectsDeferred > 0) {
2125
+ sleepSync(1500);
2126
+ }
2127
+ }
2128
+ return { objectsDeleted, objectsMissing, objectsDeferred };
2129
+ }
2130
+ function deleteR2ObjectsBatch(bucketName, keys, { env }) {
2131
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
2132
+ const token = env?.CLOUDFLARE_API_TOKEN ?? process.env.TREESEED_CLOUDFLARE_API_TOKEN ?? "";
2133
+ const uniqueKeys = [...new Set((keys ?? []).filter(Boolean))];
2134
+ if (!accountId || !bucketName || uniqueKeys.length === 0) {
2135
+ return { objectsDeleted: 0, objectsMissing: 0, objectsDeferred: 0 };
2136
+ }
2137
+ const script = `
2138
+ const input = JSON.parse(await new Promise((resolve) => {
2139
+ let body = '';
2140
+ process.stdin.setEncoding('utf8');
2141
+ process.stdin.on('data', (chunk) => { body += chunk; });
2142
+ process.stdin.on('end', () => resolve(body || '{}'));
2143
+ }));
2144
+ let index = 0;
2145
+ let deleted = 0;
2146
+ let missing = 0;
2147
+ let deferred = 0;
2148
+ const failed = [];
2149
+ async function removeKey(key) {
2150
+ function encodeObjectKey(value) {
2151
+ return String(value).split('/').map((part) => encodeURIComponent(part)).join('/');
2152
+ }
2153
+ const url = 'https://api.cloudflare.com/client/v4/accounts/'
2154
+ + encodeURIComponent(input.accountId)
2155
+ + '/r2/buckets/'
2156
+ + encodeURIComponent(input.bucketName)
2157
+ + '/objects/'
2158
+ + encodeObjectKey(key);
2159
+ for (let attempt = 0; attempt < 6; attempt += 1) {
2160
+ const controller = new AbortController();
2161
+ const timeout = setTimeout(() => controller.abort(), input.timeoutMs || 15000);
2162
+ try {
2163
+ const response = await fetch(url, {
2164
+ method: 'DELETE',
2165
+ headers: { authorization: 'Bearer ' + input.token },
2166
+ signal: controller.signal,
2167
+ });
2168
+ const text = await response.text();
2169
+ let payload = {};
2170
+ try { payload = text ? JSON.parse(text) : {}; } catch { payload = { errors: [{ message: text }] }; }
2171
+ if (response.ok && payload.success !== false) {
2172
+ deleted += 1;
2173
+ return;
2174
+ }
2175
+ const message = Array.isArray(payload.errors) ? payload.errors.map((entry) => entry?.message || JSON.stringify(entry)).join('; ') : text;
2176
+ if (/not found|does not exist|deleted|missing/i.test(message || '')) {
2177
+ missing += 1;
2178
+ return;
2179
+ }
2180
+ if (response.status === 429 || /rate limit|too many requests/i.test(message || '')) {
2181
+ await new Promise((resolve) => setTimeout(resolve, 750 * (attempt + 1)));
2182
+ continue;
2183
+ }
2184
+ failed.push({ key, message: message || \`delete failed with status \${response.status}\` });
2185
+ return;
2186
+ } catch (error) {
2187
+ if (attempt < 5 && /aborted|timed out|fetch failed|econnreset/i.test(error instanceof Error ? error.message : String(error))) {
2188
+ await new Promise((resolve) => setTimeout(resolve, 750 * (attempt + 1)));
2189
+ continue;
2190
+ }
2191
+ failed.push({ key, message: error instanceof Error ? error.message : String(error) });
2192
+ return;
2193
+ } finally {
2194
+ clearTimeout(timeout);
2195
+ }
2196
+ }
2197
+ deferred += 1;
2198
+ }
2199
+ async function worker() {
2200
+ for (;;) {
2201
+ const current = index;
2202
+ index += 1;
2203
+ if (current >= input.keys.length) return;
2204
+ await removeKey(input.keys[current]);
2205
+ }
2206
+ }
2207
+ await Promise.all(Array.from({ length: Math.min(input.concurrency || 4, input.keys.length) }, () => worker()));
2208
+ process.stdout.write(JSON.stringify({ deleted, missing, deferred, failed }));
2209
+ `.trim();
2210
+ const result = spawnSync(process.execPath, ["--input-type=module", "-e", script], {
2211
+ stdio: ["pipe", "pipe", "pipe"],
2212
+ encoding: "utf8",
2213
+ env: { ...process.env, ...env ?? {} },
2214
+ input: JSON.stringify({
2215
+ accountId,
2216
+ bucketName,
2217
+ keys: uniqueKeys,
2218
+ token,
2219
+ concurrency: 4,
2220
+ timeoutMs: 12e3
2221
+ }),
2222
+ timeout: 12e4
2223
+ });
2224
+ if (result.status !== 0 || result.error) {
2225
+ throw new Error(result.stderr?.trim() || result.error?.message || `Failed to delete R2 object batch for ${bucketName}.`);
2226
+ }
2227
+ let parsed;
2228
+ try {
2229
+ parsed = JSON.parse(result.stdout || "{}");
2230
+ } catch {
2231
+ throw new Error(`R2 object batch delete returned invalid JSON for ${bucketName}.`);
2012
2232
  }
2013
- return { objectsDeleted };
2233
+ if (Array.isArray(parsed.failed) && parsed.failed.length > 0) {
2234
+ const first = parsed.failed[0];
2235
+ throw new Error(`Failed to delete ${parsed.failed.length} R2 objects from ${bucketName}: ${first?.message ?? first?.key ?? "unknown error"}`);
2236
+ }
2237
+ return {
2238
+ objectsDeleted: Number(parsed.deleted) || 0,
2239
+ objectsMissing: Number(parsed.missing) || 0,
2240
+ objectsDeferred: Number(parsed.deferred) || 0
2241
+ };
2014
2242
  }
2015
2243
  function deleteD1DatabaseForDestroy(tenantRoot, databaseName, { env, dryRun, deleteData }) {
2016
2244
  if (!deleteData) {
@@ -2023,7 +2251,7 @@ function pagesDomainName(domain) {
2023
2251
  return typeof domain?.name === "string" ? domain.name : typeof domain?.domain === "string" ? domain.domain : typeof domain?.hostname === "string" ? domain.hostname : "";
2024
2252
  }
2025
2253
  function listPagesCustomDomains(projectName, { env }) {
2026
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
2254
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
2027
2255
  if (!projectName || !accountId) {
2028
2256
  return [];
2029
2257
  }
@@ -2066,20 +2294,20 @@ function listPagesCustomDomainsWithWrangler(tenantRoot, projectName, { env }) {
2066
2294
  return [];
2067
2295
  }
2068
2296
  }
2069
- function deletePagesCustomDomains(tenantRoot, projectName, knownNames, { env, dryRun }) {
2297
+ function deletePagesCustomDomains(tenantRoot, projectName, knownNames, { env, dryRun, knownOnly = false }) {
2070
2298
  if (!projectName) {
2071
2299
  return [resourceOperation("cloudflare", "pages-custom-domain", projectName, "missing")];
2072
2300
  }
2073
2301
  const desiredNames = [...new Set((knownNames ?? []).filter(Boolean))];
2074
2302
  if (dryRun) {
2075
- return desiredNames.length > 0 ? desiredNames.map((name) => resourceOperation("cloudflare", "pages-custom-domain", name, "planned", { projectName })) : [resourceOperation("cloudflare", "pages-custom-domain", projectName, "planned", { reason: "project_delete_prerequisite" })];
2303
+ return desiredNames.length > 0 ? desiredNames.map((name) => resourceOperation("cloudflare", "pages-custom-domain", name, "planned", { projectName, knownOnly })) : [resourceOperation("cloudflare", "pages-custom-domain", projectName, knownOnly ? "skipped" : "planned", { reason: knownOnly ? "no_target_scoped_domain" : "project_delete_prerequisite" })];
2076
2304
  }
2077
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
2305
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
2078
2306
  if (!accountId) {
2079
2307
  return desiredNames.length > 0 ? desiredNames.map((name) => resourceOperation("cloudflare", "pages-custom-domain", name, "blocked", { projectName, reason: "missing_cloudflare_account_id" })) : [resourceOperation("cloudflare", "pages-custom-domain", projectName, "blocked", { reason: "missing_cloudflare_account_id" })];
2080
2308
  }
2081
- const listedNames = listPagesCustomDomains(projectName, { env }).map(pagesDomainName).filter(Boolean);
2082
- const wranglerNames = listPagesCustomDomainsWithWrangler(tenantRoot, projectName, { env });
2309
+ const listedNames = knownOnly ? [] : listPagesCustomDomains(projectName, { env }).map(pagesDomainName).filter(Boolean);
2310
+ const wranglerNames = knownOnly ? [] : listPagesCustomDomainsWithWrangler(tenantRoot, projectName, { env });
2083
2311
  const domainNames = [.../* @__PURE__ */ new Set([...desiredNames, ...listedNames, ...wranglerNames])];
2084
2312
  if (domainNames.length === 0) {
2085
2313
  return [resourceOperation("cloudflare", "pages-custom-domain", projectName, "missing", { projectName })];
@@ -2095,13 +2323,16 @@ function normalizePagesDeploymentId(deployment) {
2095
2323
  function normalizePagesDeployments(value) {
2096
2324
  return (Array.isArray(value) ? value : Array.isArray(value?.result) ? value.result : []).filter((entry) => normalizePagesDeploymentId(entry));
2097
2325
  }
2098
- function listPagesDeploymentsWithApi(projectName, { env }) {
2099
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
2326
+ function pagesDeploymentEnvironments(environment = "all") {
2327
+ return environment === "preview" ? ["preview"] : environment === "production" ? ["production"] : ["preview", "production"];
2328
+ }
2329
+ function listPagesDeploymentsWithApi(projectName, { env, environment = "all" }) {
2330
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
2100
2331
  if (!projectName || !accountId) {
2101
2332
  return [];
2102
2333
  }
2103
2334
  const deployments = [];
2104
- for (const pagesEnvironment of ["preview", "production"]) {
2335
+ for (const pagesEnvironment of pagesDeploymentEnvironments(environment)) {
2105
2336
  let page = 1;
2106
2337
  let totalPages = 1;
2107
2338
  while (page <= totalPages && page <= 50) {
@@ -2120,9 +2351,9 @@ function listPagesDeploymentsWithApi(projectName, { env }) {
2120
2351
  }
2121
2352
  return deployments;
2122
2353
  }
2123
- function listPagesDeployments(tenantRoot, projectName, { env }) {
2354
+ function listPagesDeployments(tenantRoot, projectName, { env, environment = "all" }) {
2124
2355
  const deployments = [];
2125
- for (const pagesEnvironment of ["preview", "production"]) {
2356
+ for (const pagesEnvironment of pagesDeploymentEnvironments(environment)) {
2126
2357
  const result = runWrangler(["pages", "deployment", "list", "--project-name", projectName, "--environment", pagesEnvironment, "--json"], {
2127
2358
  cwd: tenantRoot,
2128
2359
  allowFailure: true,
@@ -2141,16 +2372,16 @@ function listPagesDeployments(tenantRoot, projectName, { env }) {
2141
2372
  const byId = new Map(deployments.map((deployment) => [normalizePagesDeploymentId(deployment), deployment]));
2142
2373
  return [...byId.values()];
2143
2374
  }
2144
- return listPagesDeploymentsWithApi(projectName, { env });
2375
+ return listPagesDeploymentsWithApi(projectName, { env, environment });
2145
2376
  }
2146
- function deletePagesDeployments(tenantRoot, projectName, { env, dryRun }) {
2377
+ function deletePagesDeployments(tenantRoot, projectName, { env, dryRun, environment = "all" }) {
2147
2378
  if (!projectName) {
2148
2379
  return resourceOperation("cloudflare", "pages-deployments", projectName, "missing");
2149
2380
  }
2150
2381
  if (dryRun) {
2151
- return resourceOperation("cloudflare", "pages-deployments", projectName, "planned", { reason: "project_delete_prerequisite" });
2382
+ return resourceOperation("cloudflare", "pages-deployments", projectName, "planned", { reason: "project_delete_prerequisite", environment });
2152
2383
  }
2153
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
2384
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
2154
2385
  if (!accountId) {
2155
2386
  return resourceOperation("cloudflare", "pages-deployments", projectName, "blocked", { reason: "missing_cloudflare_account_id" });
2156
2387
  }
@@ -2158,7 +2389,7 @@ function deletePagesDeployments(tenantRoot, projectName, { env, dryRun }) {
2158
2389
  let skipped = 0;
2159
2390
  let total = 0;
2160
2391
  for (let batch = 0; batch < 100; batch += 1) {
2161
- const deployments = listPagesDeployments(tenantRoot, projectName, { env });
2392
+ const deployments = listPagesDeployments(tenantRoot, projectName, { env, environment });
2162
2393
  if (deployments.length === 0) {
2163
2394
  return resourceOperation("cloudflare", "pages-deployments", projectName, deleted > 0 ? "deleted" : "missing", {
2164
2395
  deleted,
@@ -2204,7 +2435,7 @@ function deletePagesDeployments(tenantRoot, projectName, { env, dryRun }) {
2204
2435
  });
2205
2436
  }
2206
2437
  function deletePagesProject(projectName, { env, dryRun }) {
2207
- const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
2438
+ const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
2208
2439
  if (!projectName || !accountId) {
2209
2440
  return resourceOperation("cloudflare", "pages-project", projectName, "missing");
2210
2441
  }
@@ -2286,31 +2517,31 @@ function configuredRailwayDestroyTargets(tenantRoot, deployConfig, scope) {
2286
2517
  identity = { deploymentKey: deployConfig.slug ?? deployConfig.name ?? "treeseed" };
2287
2518
  }
2288
2519
  const services = [];
2289
- for (const serviceKey of ["api", "marketOperationsRunner"]) {
2520
+ for (const serviceKey of ["api", "operationsRunner"]) {
2290
2521
  const service = deployConfig.services?.[serviceKey];
2291
2522
  if (!service || service.enabled === false || (service.provider ?? "railway") !== "railway") {
2292
2523
  continue;
2293
2524
  }
2294
- const baseServiceName = service.railway?.serviceName ?? `${identity.deploymentKey}-${serviceKey === "marketOperationsRunner" ? "market-operations-runner" : serviceKey}`;
2295
- const runnerPool = serviceKey === "marketOperationsRunner" && service.railway?.runnerPool && typeof service.railway.runnerPool === "object" ? service.railway.runnerPool : null;
2296
- const count = serviceKey === "marketOperationsRunner" ? Math.max(1, Number.parseInt(String(runnerPool?.bootstrapCount ?? 1), 10) || 1) : 1;
2525
+ const baseServiceName = service.railway?.serviceName ?? `${identity.deploymentKey}-${serviceKey === "operationsRunner" ? "operations-runner" : serviceKey}`;
2526
+ const runnerPool = serviceKey === "operationsRunner" && service.railway?.runnerPool && typeof service.railway.runnerPool === "object" ? service.railway.runnerPool : null;
2527
+ const count = serviceKey === "operationsRunner" ? Math.max(1, Number.parseInt(String(runnerPool?.bootstrapCount ?? 1), 10) || 1) : 1;
2297
2528
  for (let index = 1; index <= count; index += 1) {
2298
- const serviceName = serviceKey === "marketOperationsRunner" ? `${String(baseServiceName).replace(/-\d+$/u, "")}-${String(index).padStart(2, "0")}` : baseServiceName;
2529
+ const serviceName = serviceKey === "operationsRunner" ? `${String(baseServiceName).replace(/-\d+$/u, "").replace(/-\d{2}$/u, "")}-${String(index).padStart(2, "0")}` : baseServiceName;
2299
2530
  services.push({
2300
2531
  key: serviceKey,
2301
2532
  projectName: service.railway?.projectName ?? identity.deploymentKey,
2302
2533
  serviceName,
2303
2534
  railwayEnvironment: normalizeRailwayEnvironmentName(service.environments?.[normalizedScope]?.railwayEnvironment ?? normalizedScope),
2304
2535
  domain: service.environments?.[normalizedScope]?.domain ?? null,
2305
- volumeMountPath: serviceKey === "marketOperationsRunner" ? service.railway?.volumeMountPath ?? runnerPool?.volumeMountPath ?? "/data" : null
2536
+ volumeMountPath: serviceKey === "operationsRunner" ? service.railway?.volumeMountPath ?? runnerPool?.volumeMountPath ?? "/data" : null
2306
2537
  });
2307
2538
  }
2308
2539
  }
2309
- const marketDatabase = deployConfig.services?.marketDatabase;
2310
- if (marketDatabase?.enabled !== false && marketDatabase?.provider === "railway" && marketDatabase?.railway?.resourceType === "postgres") {
2311
- const baseName = typeof marketDatabase.railway?.serviceName === "string" && marketDatabase.railway.serviceName.trim() ? marketDatabase.railway.serviceName.trim() : `${deployConfig.slug ?? "treeseed-market"}-postgres`;
2540
+ const treeseedDatabase = deployConfig.services?.treeseedDatabase;
2541
+ if (treeseedDatabase?.enabled !== false && treeseedDatabase?.provider === "railway" && treeseedDatabase?.railway?.resourceType === "postgres") {
2542
+ const baseName = typeof treeseedDatabase.railway?.serviceName === "string" && treeseedDatabase.railway.serviceName.trim() ? treeseedDatabase.railway.serviceName.trim() : `${deployConfig.slug ?? "treeseed-market"}-postgres`;
2312
2543
  services.push({
2313
- key: "marketDatabase",
2544
+ key: "treeseedDatabase",
2314
2545
  projectName: deployConfig.services?.api?.railway?.projectName ?? identity.deploymentKey,
2315
2546
  serviceName: `${baseName.replace(/-(staging|prod|production)$/u, "")}-${normalizedScope === "prod" ? "prod" : normalizedScope}`,
2316
2547
  railwayEnvironment: normalizeRailwayEnvironmentName(normalizedScope),
@@ -2396,17 +2627,19 @@ async function destroyRailwayResources(tenantRoot, deployConfig, target, { dryRu
2396
2627
  }
2397
2628
  }
2398
2629
  }
2399
- const volumes = await listRailwayVolumes({ projectId: project.id, env });
2400
- for (const volume of volumes) {
2401
- const matchingInstance = volume.instances.find((instance) => instance.environmentId === environment?.id);
2402
- if (!matchingInstance) {
2403
- continue;
2404
- }
2405
- if (dryRun) {
2406
- operations.push(resourceOperation("railway", "volume", volume.name, "planned", { id: volume.id, projectId: project.id }));
2407
- } else {
2408
- const result = await deleteRailwayVolume({ volumeId: volume.id, env });
2409
- operations.push(resourceOperation("railway", "volume", volume.name, result.status, { id: volume.id, projectId: project.id }));
2630
+ if (deleteData) {
2631
+ const volumes = await listRailwayVolumes({ projectId: project.id, env });
2632
+ for (const volume of volumes) {
2633
+ const matchingInstance = volume.instances.find((instance) => instance.environmentId === environment?.id);
2634
+ if (!matchingInstance) {
2635
+ continue;
2636
+ }
2637
+ if (dryRun) {
2638
+ operations.push(resourceOperation("railway", "volume", volume.name, "planned", { id: volume.id, projectId: project.id }));
2639
+ } else {
2640
+ const result = await deleteRailwayVolume({ volumeId: volume.id, env });
2641
+ operations.push(resourceOperation("railway", "volume", volume.name, result.status, { id: volume.id, projectId: project.id }));
2642
+ }
2410
2643
  }
2411
2644
  }
2412
2645
  const shouldDeleteProject = shouldDeleteRailwayProjectAfterEnvironmentDestroy(project, scope, deleteData, environment?.id ?? null);
@@ -2417,11 +2650,7 @@ async function destroyRailwayResources(tenantRoot, deployConfig, target, { dryRu
2417
2650
  reason: scope === "prod" ? "prod_delete_data_cleanup" : "no_managed_persistent_environments"
2418
2651
  }));
2419
2652
  } else {
2420
- const result = await deleteRailwayProject({ projectId: project.id, env });
2421
- operations.push(resourceOperation("railway", "project", project.name, result.status, {
2422
- id: project.id,
2423
- reason: scope === "prod" ? "prod_delete_data_cleanup" : "no_managed_persistent_environments"
2424
- }));
2653
+ throw new Error("Railway project deletion is reconciler-owned. Use trsd reconcile destroy or live acceptance cleanup for project-scoped deletion.");
2425
2654
  }
2426
2655
  } else if (environment) {
2427
2656
  if (dryRun) {
@@ -2465,6 +2694,87 @@ function killPidFromFile(filePath, { dryRun }) {
2465
2694
  }
2466
2695
  return resourceOperation("local", "dev-process", String(pid), "deleted", { pidFile: filePath });
2467
2696
  }
2697
+ const LOCAL_DOCKER_RESOURCE_PATTERN = /(?:^|[-_.])(?:treeseed|treedx|treedb)(?:[-_.]|$)|(?:treeseed|treedx|treedb)/iu;
2698
+ let destroyDockerRunnerForTests = null;
2699
+ function setDestroyDockerRunnerForTests(runner) {
2700
+ destroyDockerRunnerForTests = runner;
2701
+ }
2702
+ function runDestroyDocker(args) {
2703
+ if (destroyDockerRunnerForTests) {
2704
+ return destroyDockerRunnerForTests(args);
2705
+ }
2706
+ return spawnSync("docker", args, { encoding: "utf8", stdio: "pipe" });
2707
+ }
2708
+ function dockerAvailable() {
2709
+ const result = runDestroyDocker(["info"]);
2710
+ return result.status === 0;
2711
+ }
2712
+ function dockerList(formatArgs) {
2713
+ const result = runDestroyDocker(formatArgs);
2714
+ if (result.status !== 0) {
2715
+ return [];
2716
+ }
2717
+ return result.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
2718
+ }
2719
+ function matchingDockerEntries(lines, parser) {
2720
+ return lines.map(parser).filter((entry) => entry && LOCAL_DOCKER_RESOURCE_PATTERN.test(`${entry.name} ${entry.image ?? ""}`));
2721
+ }
2722
+ function removeDockerResource(kind, id, name) {
2723
+ const args = kind === "container" ? ["rm", "-f", id] : kind === "volume" ? ["volume", "rm", "-f", id] : ["network", "rm", id];
2724
+ const result = runDestroyDocker(args);
2725
+ if (result.status === 0) {
2726
+ return resourceOperation("local", `docker-${kind}`, name, "deleted", { id });
2727
+ }
2728
+ return resourceOperation("local", `docker-${kind}`, name, "blocked", {
2729
+ id,
2730
+ reason: result.stderr?.trim() || result.stdout?.trim() || "docker_remove_failed"
2731
+ });
2732
+ }
2733
+ function dockerLocalRuntimeResourceOperations({ dryRun = false } = {}) {
2734
+ if (!dockerAvailable()) {
2735
+ return [resourceOperation("local", "docker-cleanup", "docker", "skipped", { reason: "docker_unavailable" })];
2736
+ }
2737
+ const containers = matchingDockerEntries(
2738
+ dockerList(["ps", "-a", "--format", "{{.ID}} {{.Names}} {{.Image}}"]),
2739
+ (line) => {
2740
+ const [id, name, image] = line.split(" ");
2741
+ return id && name ? { id, name, image } : null;
2742
+ }
2743
+ );
2744
+ const volumes = matchingDockerEntries(
2745
+ dockerList(["volume", "ls", "--format", "{{.Name}}"]),
2746
+ (line) => ({ id: line, name: line })
2747
+ );
2748
+ const networks = matchingDockerEntries(
2749
+ dockerList(["network", "ls", "--format", "{{.ID}} {{.Name}}"]),
2750
+ (line) => {
2751
+ const [id, name] = line.split(" ");
2752
+ return id && name ? { id, name } : null;
2753
+ }
2754
+ ).filter((entry) => !["bridge", "host", "none"].includes(entry.name));
2755
+ if (dryRun) {
2756
+ return [
2757
+ ...containers.map((entry) => resourceOperation("local", "docker-container", entry.name, "planned", { id: entry.id })),
2758
+ ...volumes.map((entry) => resourceOperation("local", "docker-volume", entry.name, "planned", { id: entry.id })),
2759
+ ...networks.map((entry) => resourceOperation("local", "docker-network", entry.name, "planned", { id: entry.id })),
2760
+ ...containers.length || volumes.length || networks.length ? [] : [resourceOperation("local", "docker-cleanup", "docker", "missing", { reason: "no_matching_resources" })]
2761
+ ];
2762
+ }
2763
+ const operations = [];
2764
+ for (const entry of containers) {
2765
+ operations.push(removeDockerResource("container", entry.id, entry.name));
2766
+ }
2767
+ for (const entry of volumes) {
2768
+ operations.push(removeDockerResource("volume", entry.id, entry.name));
2769
+ }
2770
+ for (const entry of networks) {
2771
+ operations.push(removeDockerResource("network", entry.id, entry.name));
2772
+ }
2773
+ if (!operations.length) {
2774
+ operations.push(resourceOperation("local", "docker-cleanup", "docker", "missing", { reason: "no_matching_resources" }));
2775
+ }
2776
+ return operations;
2777
+ }
2468
2778
  function destroyLocalRuntimeResources(tenantRoot, { dryRun = false, deleteData = false } = {}) {
2469
2779
  const operations = [];
2470
2780
  const pidDir = resolve(tenantRoot, ".treeseed/dev-pids");
@@ -2481,7 +2791,7 @@ function destroyLocalRuntimeResources(tenantRoot, { dryRun = false, deleteData =
2481
2791
  for (const relativePath of [
2482
2792
  ".treeseed/generated/environments/local",
2483
2793
  ".treeseed/generated/dev",
2484
- ".treeseed/market-operations-runner",
2794
+ ".treeseed/operations-runner",
2485
2795
  ".treeseed/local-capacity-provider/data"
2486
2796
  ]) {
2487
2797
  const absolutePath = resolve(tenantRoot, relativePath);
@@ -2496,11 +2806,243 @@ function destroyLocalRuntimeResources(tenantRoot, { dryRun = false, deleteData =
2496
2806
  rmSync(absolutePath, { recursive: true, force: true });
2497
2807
  operations.push(resourceOperation("local", "data-path", relativePath, "deleted"));
2498
2808
  }
2809
+ operations.push(...dockerLocalRuntimeResourceOperations({ dryRun }));
2499
2810
  } else {
2500
2811
  operations.push(resourceOperation("local", "data-path", ".treeseed/generated/environments/local", "skipped", { reason: "data_preserved" }));
2501
2812
  }
2502
2813
  return { operations };
2503
2814
  }
2815
+ function treeSeedSweepTokens(deployConfig, state) {
2816
+ const configuredHosts = [
2817
+ deployConfig.siteUrl,
2818
+ deployConfig.surfaces?.web?.publicBaseUrl,
2819
+ deployConfig.surfaces?.web?.environments?.staging?.domain,
2820
+ deployConfig.surfaces?.web?.environments?.prod?.domain,
2821
+ deployConfig.surfaces?.api?.environments?.staging?.domain,
2822
+ deployConfig.surfaces?.api?.environments?.prod?.domain,
2823
+ deployConfig.services?.api?.environments?.staging?.domain,
2824
+ deployConfig.services?.api?.environments?.prod?.domain
2825
+ ].map((value) => primaryHost(value) ?? value);
2826
+ return [...new Set([
2827
+ "treeseed",
2828
+ deployConfig.slug,
2829
+ deployConfig.name,
2830
+ state.identity?.deploymentKey,
2831
+ state.identity?.environmentKey,
2832
+ state.pages?.projectName,
2833
+ state.workerName,
2834
+ state.content?.bucketName,
2835
+ state.kvNamespaces?.FORM_GUARD_KV?.name,
2836
+ state.kvNamespaces?.SESSION?.name,
2837
+ state.d1Databases?.SITE_DATA_DB?.databaseName,
2838
+ ...configuredHosts
2839
+ ].map((value) => String(value ?? "").trim().toLowerCase()).filter((value) => value.length >= 4))];
2840
+ }
2841
+ function isProtectedAiIntegrationResource(value) {
2842
+ return /(?:^|[-_.])(?:ai-gateway|workers-ai|ai-integration|openai|anthropic)(?:[-_.]|$)/iu.test(String(value ?? ""));
2843
+ }
2844
+ function matchesTreeSeedSweep(value, tokens) {
2845
+ const normalized = String(value ?? "").trim().toLowerCase();
2846
+ if (!normalized || isProtectedAiIntegrationResource(normalized)) {
2847
+ return false;
2848
+ }
2849
+ return tokens.some((token) => normalized === token || normalized.includes(token));
2850
+ }
2851
+ function cloudflareNameCandidates(entry) {
2852
+ return [
2853
+ entry?.name,
2854
+ entry?.title,
2855
+ entry?.id,
2856
+ entry?.queue_name,
2857
+ entry?.script,
2858
+ entry?.domain,
2859
+ entry?.hostname,
2860
+ entry?.content,
2861
+ entry?.comment,
2862
+ ...Array.isArray(entry?.domains) ? entry.domains : [],
2863
+ ...Array.isArray(entry?.tags) ? entry.tags : []
2864
+ ].filter(Boolean);
2865
+ }
2866
+ function cloudflareEntryMatchesTreeSeed(entry, tokens) {
2867
+ return cloudflareNameCandidates(entry).some((candidate) => matchesTreeSeedSweep(candidate, tokens));
2868
+ }
2869
+ function deleteDnsRecord(zoneId, record, { env, dryRun }) {
2870
+ const name = record?.name ?? record?.content ?? record?.id ?? null;
2871
+ if (!zoneId || !record?.id) {
2872
+ return resourceOperation("cloudflare", "dns-record", name, "missing", { zoneId });
2873
+ }
2874
+ if (dryRun) {
2875
+ return resourceOperation("cloudflare", "dns-record", name, "planned", {
2876
+ zoneId,
2877
+ id: record.id,
2878
+ content: record.content ?? null,
2879
+ recordType: record.type ?? null
2880
+ });
2881
+ }
2882
+ return deleteCloudflareApiResource(
2883
+ `/zones/${encodeURIComponent(zoneId)}/dns_records/${encodeURIComponent(record.id)}`,
2884
+ { env, dryRun: false, name, type: "dns-record" }
2885
+ );
2886
+ }
2887
+ function sweepTreeSeedCloudflareResources(tenantRoot, deployConfig, state, { env, dryRun, deleteData }) {
2888
+ const tokens = treeSeedSweepTokens(deployConfig, state);
2889
+ const operations = [];
2890
+ const pagesProjects = listPagesProjects(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens));
2891
+ for (const project of pagesProjects) {
2892
+ const projectName = project?.name ?? project?.id ?? null;
2893
+ operations.push(...deletePagesCustomDomains(tenantRoot, projectName, [], { env, dryRun, knownOnly: false }));
2894
+ operations.push(deletePagesDeployments(tenantRoot, projectName, { env, dryRun, environment: "all" }));
2895
+ operations.push(deletePagesProject(projectName, { env, dryRun }));
2896
+ }
2897
+ for (const worker of listWorkers(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
2898
+ const name = worker?.id ?? worker?.name ?? worker?.script ?? null;
2899
+ const deleted = deleteWorker(tenantRoot, name, { env, dryRun, force: true });
2900
+ operations.push(resourceOperation("cloudflare", "worker", name, deleted.status, { ...deleted, sweep: true }));
2901
+ }
2902
+ for (const namespace of listKvNamespaces(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
2903
+ const deleted = deleteKvNamespace(tenantRoot, namespace.id, { env, dryRun });
2904
+ operations.push(resourceOperation("cloudflare", "kv-namespace", namespace.title ?? namespace.id, deleted.status, { ...deleted, sweep: true }));
2905
+ }
2906
+ for (const queue of listQueues(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
2907
+ operations.push({ ...deleteQueueByName(tenantRoot, queue, { env, dryRun }), sweep: true });
2908
+ }
2909
+ for (const database of listD1Databases(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
2910
+ const name = database?.name ?? database?.uuid ?? database?.id ?? null;
2911
+ const deleted = deleteData ? deleteD1Database(tenantRoot, name, { env, dryRun }) : null;
2912
+ operations.push(resourceOperation("cloudflare", "d1-database", name, deleteData ? deleted?.status : "skipped", {
2913
+ ...deleteData ? deleted : { reason: "data_preserved" },
2914
+ sweep: true
2915
+ }));
2916
+ }
2917
+ for (const bucket of listR2Buckets(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
2918
+ operations.push({ ...deleteR2Bucket(tenantRoot, bucket.name, { env, dryRun, deleteData }), sweep: true });
2919
+ }
2920
+ for (const widget of listTurnstileWidgets(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
2921
+ const deleted = deleteTurnstileWidget(widget.sitekey, { env, dryRun, name: widget.name });
2922
+ operations.push(resourceOperation("cloudflare", "turnstile-widget", widget.name ?? widget.sitekey, deleted.status, { ...deleted, sweep: true }));
2923
+ }
2924
+ for (const zone of listDnsZones(env)) {
2925
+ const zoneId = zone?.id ?? null;
2926
+ for (const record of listDnsRecords(zoneId, env)) {
2927
+ if (record?.type === "SOA" || record?.type === "NS") {
2928
+ continue;
2929
+ }
2930
+ if (!cloudflareEntryMatchesTreeSeed(record, tokens)) {
2931
+ continue;
2932
+ }
2933
+ operations.push({ ...deleteDnsRecord(zoneId, record, { env, dryRun }), zoneName: zone?.name ?? null, sweep: true });
2934
+ }
2935
+ }
2936
+ return operations.length > 0 ? operations : [resourceOperation("cloudflare", "treeseed-sweep", "cloudflare", "missing", { reason: "no_matching_resources" })];
2937
+ }
2938
+ function countMatchingCloudflareEntries(entries, tokens) {
2939
+ return entries.filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens)).length;
2940
+ }
2941
+ function cloudflareDestroyVerification(tenantRoot, deployConfig, state, env) {
2942
+ const tokens = treeSeedSweepTokens(deployConfig, state);
2943
+ const zoneIds = /* @__PURE__ */ new Set([
2944
+ deployConfig.cloudflare?.zoneId,
2945
+ state.webCache?.webZoneId,
2946
+ state.webCache?.contentZoneId
2947
+ ]);
2948
+ for (const zone of listDnsZones(env)) {
2949
+ if (zone?.id) {
2950
+ zoneIds.add(zone.id);
2951
+ }
2952
+ }
2953
+ const dnsRecords = [];
2954
+ for (const zoneId of [...zoneIds].filter(Boolean)) {
2955
+ dnsRecords.push(...listDnsRecords(zoneId, env));
2956
+ }
2957
+ const remaining = {
2958
+ pages: countMatchingCloudflareEntries(listPagesProjects(tenantRoot, env), tokens),
2959
+ workers: countMatchingCloudflareEntries(listWorkers(tenantRoot, env), tokens),
2960
+ kvNamespaces: countMatchingCloudflareEntries(listKvNamespaces(tenantRoot, env), tokens),
2961
+ queues: countMatchingCloudflareEntries(listQueues(tenantRoot, env), tokens),
2962
+ d1Databases: countMatchingCloudflareEntries(listD1Databases(tenantRoot, env), tokens),
2963
+ r2Buckets: countMatchingCloudflareEntries(listR2Buckets(tenantRoot, env), tokens),
2964
+ turnstileWidgets: countMatchingCloudflareEntries(listTurnstileWidgets(tenantRoot, env), tokens),
2965
+ dnsRecords: countMatchingCloudflareEntries(
2966
+ dnsRecords.filter((record) => record?.type !== "SOA" && record?.type !== "NS"),
2967
+ tokens
2968
+ )
2969
+ };
2970
+ const totalRemaining = Object.values(remaining).reduce((sum, value) => sum + value, 0);
2971
+ return {
2972
+ provider: "cloudflare",
2973
+ method: "cloudflare-api",
2974
+ status: totalRemaining === 0 ? "clean" : "remaining",
2975
+ remaining,
2976
+ totalRemaining
2977
+ };
2978
+ }
2979
+ function localDockerDestroyVerification() {
2980
+ if (!dockerAvailable()) {
2981
+ return {
2982
+ provider: "local-docker",
2983
+ method: "docker-cli",
2984
+ status: "skipped",
2985
+ reason: "docker_unavailable",
2986
+ remaining: {
2987
+ containers: 0,
2988
+ volumes: 0,
2989
+ networks: 0
2990
+ },
2991
+ totalRemaining: 0
2992
+ };
2993
+ }
2994
+ const containers = matchingDockerEntries(
2995
+ dockerList(["ps", "-a", "--format", "{{.ID}} {{.Names}} {{.Image}}"]),
2996
+ (line) => {
2997
+ const [id, name, image] = line.split(" ");
2998
+ return id && name ? { id, name, image } : null;
2999
+ }
3000
+ ).length;
3001
+ const volumes = matchingDockerEntries(
3002
+ dockerList(["volume", "ls", "--format", "{{.Name}}"]),
3003
+ (line) => ({ id: line, name: line })
3004
+ ).length;
3005
+ const networks = matchingDockerEntries(
3006
+ dockerList(["network", "ls", "--format", "{{.ID}} {{.Name}}"]),
3007
+ (line) => {
3008
+ const [id, name] = line.split(" ");
3009
+ return id && name ? { id, name } : null;
3010
+ }
3011
+ ).filter((entry) => !["bridge", "host", "none"].includes(entry.name)).length;
3012
+ const remaining = { containers, volumes, networks };
3013
+ const totalRemaining = containers + volumes + networks;
3014
+ return {
3015
+ provider: "local-docker",
3016
+ method: "docker-cli",
3017
+ status: totalRemaining === 0 ? "clean" : "remaining",
3018
+ remaining,
3019
+ totalRemaining
3020
+ };
3021
+ }
3022
+ async function sweepTreeSeedRailwayResources(deployConfig, state, { env, dryRun }) {
3023
+ if (!resolveRailwayApiToken(env)) {
3024
+ return [resourceOperation("railway", "treeseed-sweep", "railway", "blocked", { reason: "missing_railway_api_token" })];
3025
+ }
3026
+ const tokens = treeSeedSweepTokens(deployConfig, state);
3027
+ const workspace = await resolveRailwayWorkspaceContext({ env, workspace: resolveRailwayWorkspace(env) });
3028
+ const projects = await listRailwayProjects({ env, workspaceId: workspace.id });
3029
+ const operations = [];
3030
+ for (const project of projects) {
3031
+ if (project.deletedAt || !matchesTreeSeedSweep(project.name, tokens)) {
3032
+ continue;
3033
+ }
3034
+ if (dryRun) {
3035
+ operations.push(resourceOperation("railway", "project", project.name, "planned", {
3036
+ id: project.id,
3037
+ workspaceId: workspace.id,
3038
+ sweep: true
3039
+ }));
3040
+ } else {
3041
+ throw new Error("Railway project sweep deletion is reconciler-owned. Use trsd reconcile test-live --mode cleanup for isolated cleanup.");
3042
+ }
3043
+ }
3044
+ return operations.length > 0 ? operations : [resourceOperation("railway", "treeseed-sweep", "railway", "missing", { reason: "no_matching_projects" })];
3045
+ }
2504
3046
  async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
2505
3047
  const target = normalizeTarget(options.scope ?? options.target ?? "prod");
2506
3048
  const deployConfig = loadTenantDeployConfig(tenantRoot);
@@ -2512,6 +3054,8 @@ async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
2512
3054
  const dryRun = options.dryRun ?? false;
2513
3055
  const deleteData = options.deleteData === true;
2514
3056
  const force = options.force ?? false;
3057
+ const sweepTreeseed = options.sweepTreeseed === true;
3058
+ const destroysSharedWebSurface = target.kind === "persistent" && target.scope === "prod" && deleteData;
2515
3059
  const kvNamespaces = dryRun ? [] : listKvNamespaces(tenantRoot, env);
2516
3060
  const d1Databases = dryRun ? [] : listD1Databases(tenantRoot, env);
2517
3061
  const queues = dryRun ? [] : listQueues(tenantRoot, env);
@@ -2538,8 +3082,6 @@ async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
2538
3082
  state.turnstileWidgets.formGuard = resolveExistingTurnstileWidget(turnstileWidgets, state.turnstileWidgets?.formGuard);
2539
3083
  const pagesProject = pagesProjects.find((entry) => entry?.name === state.pages?.projectName);
2540
3084
  const bucket = buckets.find((entry) => entry?.name === state.content?.bucketName);
2541
- const queue = queues.find((entry) => queueName(entry) === state.queues?.agentWork?.name);
2542
- const dlq = queues.find((entry) => queueName(entry) === state.queues?.agentWork?.dlqName);
2543
3085
  const workerResult = deleteWorker(tenantRoot, state.workerName, { env, dryRun, force });
2544
3086
  const turnstileWidget = deleteTurnstileWidget(state.turnstileWidgets?.formGuard?.sitekey, {
2545
3087
  env,
@@ -2566,13 +3108,6 @@ async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
2566
3108
  return resourceOperation("cloudflare", "kv-namespace", namespace.title, result.status, { ...result, legacy: true });
2567
3109
  });
2568
3110
  const database = deleteD1DatabaseForDestroy(tenantRoot, state.d1Databases.SITE_DATA_DB.databaseName, { env, dryRun, deleteData });
2569
- const deletedQueue = deleteQueueByName(tenantRoot, queue ?? { name: state.queues?.agentWork?.name }, { env, dryRun });
2570
- const deletedDlq = state.queues?.agentWork?.dlqName ? deleteQueueByName(tenantRoot, dlq ?? { name: state.queues.agentWork.dlqName }, { env, dryRun }) : null;
2571
- const knownQueueNames = new Set([
2572
- state.queues?.agentWork?.name,
2573
- state.queues?.agentWork?.dlqName
2574
- ].filter(Boolean));
2575
- const legacyQueues = legacyQueueDestroyOperations(tenantRoot, queues, target, knownQueueNames, { env, dryRun });
2576
3111
  const r2Bucket = bucket || dryRun ? deleteR2Bucket(tenantRoot, state.content?.bucketName, { env, dryRun, deleteData }) : resourceOperation("cloudflare", "r2-bucket", state.content?.bucketName, "missing");
2577
3112
  const pageDnsNames = [
2578
3113
  state.pages?.customDomain,
@@ -2585,11 +3120,21 @@ async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
2585
3120
  ].filter(Boolean);
2586
3121
  const dnsRecords = [.../* @__PURE__ */ new Set([...pageDnsNames, ...apiDnsNames])].flatMap((name) => deleteDnsRecordsForName(deployConfig, name, { env, dryRun }));
2587
3122
  const cacheRules = deleteTreeseedCacheRules(deployConfig, state, { env, dryRun });
2588
- const pageCustomDomains = pagesProject || dryRun ? deletePagesCustomDomains(tenantRoot, state.pages?.projectName, pageDnsNames, { env, dryRun }) : [resourceOperation("cloudflare", "pages-custom-domain", state.pages?.projectName, "missing")];
2589
- const pageDeployments = pagesProject || dryRun ? deletePagesDeployments(tenantRoot, state.pages?.projectName, { env, dryRun }) : resourceOperation("cloudflare", "pages-deployments", state.pages?.projectName, "missing");
2590
- const pages = pagesProject || dryRun ? deletePagesProject(state.pages?.projectName, { env, dryRun }) : resourceOperation("cloudflare", "pages-project", state.pages?.projectName, "missing");
3123
+ const pageCustomDomains = pagesProject || dryRun ? deletePagesCustomDomains(tenantRoot, state.pages?.projectName, pageDnsNames, { env, dryRun, knownOnly: !destroysSharedWebSurface }) : [resourceOperation("cloudflare", "pages-custom-domain", state.pages?.projectName, "missing")];
3124
+ const pageDeployments = pagesProject || dryRun ? deletePagesDeployments(tenantRoot, state.pages?.projectName, {
3125
+ env,
3126
+ dryRun,
3127
+ environment: destroysSharedWebSurface ? "all" : "preview"
3128
+ }) : resourceOperation("cloudflare", "pages-deployments", state.pages?.projectName, "missing");
3129
+ const pages = destroysSharedWebSurface && (pagesProject || dryRun) ? deletePagesProject(state.pages?.projectName, { env, dryRun }) : resourceOperation("cloudflare", "pages-project", state.pages?.projectName, "skipped", {
3130
+ reason: target.scope === "prod" ? "delete_data_required" : "shared_web_surface"
3131
+ });
2591
3132
  const local = target.kind === "persistent" && target.scope === "local" ? destroyLocalRuntimeResources(tenantRoot, { dryRun, deleteData }) : { operations: [] };
2592
3133
  const railway = await destroyRailwayResources(tenantRoot, deployConfig, target, { dryRun, deleteData, env: process.env });
3134
+ const sweep = sweepTreeseed ? {
3135
+ cloudflare: sweepTreeSeedCloudflareResources(tenantRoot, deployConfig, state, { env, dryRun, deleteData }),
3136
+ railway: await sweepTreeSeedRailwayResources(deployConfig, state, { env: process.env, dryRun })
3137
+ } : { cloudflare: [], railway: [] };
2593
3138
  const operations = {
2594
3139
  cloudflare: [
2595
3140
  resourceOperation("cloudflare", "worker", state.workerName, workerResult.status, workerResult),
@@ -2600,24 +3145,31 @@ async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
2600
3145
  ...sessionPreview ? [resourceOperation("cloudflare", "kv-namespace-preview", state.kvNamespaces.SESSION.name, sessionPreview.status, sessionPreview)] : [],
2601
3146
  ...legacyKvNamespaces,
2602
3147
  database,
2603
- deletedQueue,
2604
- ...deletedDlq ? [deletedDlq] : [],
2605
- ...legacyQueues,
2606
3148
  r2Bucket,
2607
3149
  ...pageCustomDomains,
2608
3150
  pageDeployments,
2609
3151
  pages,
2610
3152
  ...dnsRecords,
2611
- ...cacheRules
3153
+ ...cacheRules,
3154
+ ...sweep.cloudflare
3155
+ ],
3156
+ railway: [
3157
+ ...railway.operations,
3158
+ ...sweep.railway
2612
3159
  ],
2613
- railway: railway.operations,
2614
3160
  local: local.operations
2615
3161
  };
3162
+ const verification = dryRun ? null : {
3163
+ cloudflare: cloudflareDestroyVerification(tenantRoot, deployConfig, state, env),
3164
+ ...target.kind === "persistent" && target.scope === "local" ? { localDocker: localDockerDestroyVerification() } : {}
3165
+ };
2616
3166
  return {
2617
3167
  target,
2618
3168
  deleteData,
3169
+ sweepTreeseed,
2619
3170
  summary: buildDestroySummary(deployConfig, state, target),
2620
- operations
3171
+ operations,
3172
+ verification
2621
3173
  };
2622
3174
  }
2623
3175
  function destroyCloudflareResources(tenantRoot, options = {}) {
@@ -2648,8 +3200,6 @@ function destroyCloudflareResources(tenantRoot, options = {}) {
2648
3200
  state.d1Databases.SITE_DATA_DB
2649
3201
  );
2650
3202
  state.turnstileWidgets.formGuard = resolveExistingTurnstileWidget(turnstileWidgets, state.turnstileWidgets?.formGuard);
2651
- const queue = queues.find((entry) => queueName(entry) === state.queues?.agentWork?.name);
2652
- const dlq = queues.find((entry) => queueName(entry) === state.queues?.agentWork?.dlqName);
2653
3203
  const bucket = buckets.find((entry) => entry?.name === state.content?.bucketName);
2654
3204
  const pagesProject = pagesProjects.find((entry) => entry?.name === state.pages?.projectName);
2655
3205
  const worker = deleteWorker(tenantRoot, state.workerName, { env, dryRun, force });
@@ -2660,13 +3210,6 @@ function destroyCloudflareResources(tenantRoot, options = {}) {
2660
3210
  });
2661
3211
  const formGuard = deleteKvNamespace(tenantRoot, state.kvNamespaces.FORM_GUARD_KV.id, { env, dryRun });
2662
3212
  const database = deleteD1DatabaseForDestroy(tenantRoot, state.d1Databases.SITE_DATA_DB.databaseName, { env, dryRun, deleteData });
2663
- const deletedQueue = deleteQueueByName(tenantRoot, queue ?? { name: state.queues?.agentWork?.name }, { env, dryRun });
2664
- const deletedDlq = state.queues?.agentWork?.dlqName ? deleteQueueByName(tenantRoot, dlq ?? { name: state.queues.agentWork.dlqName }, { env, dryRun }) : null;
2665
- const knownQueueNames = new Set([
2666
- state.queues?.agentWork?.name,
2667
- state.queues?.agentWork?.dlqName
2668
- ].filter(Boolean));
2669
- const legacyQueues = legacyQueueDestroyOperations(tenantRoot, queues, target, knownQueueNames, { env, dryRun });
2670
3213
  const r2Bucket = bucket || dryRun ? deleteR2Bucket(tenantRoot, state.content?.bucketName, { env, dryRun, deleteData }) : resourceOperation("cloudflare", "r2-bucket", state.content?.bucketName, "missing");
2671
3214
  const pages = pagesProject || dryRun ? deletePagesProject(state.pages?.projectName, { env, dryRun }) : resourceOperation("cloudflare", "pages-project", state.pages?.projectName, "missing");
2672
3215
  const operations = {
@@ -2674,9 +3217,6 @@ function destroyCloudflareResources(tenantRoot, options = {}) {
2674
3217
  turnstileWidget,
2675
3218
  formGuard,
2676
3219
  database,
2677
- queue: deletedQueue,
2678
- dlq: deletedDlq,
2679
- legacyQueues,
2680
3220
  r2Bucket,
2681
3221
  pages
2682
3222
  };
@@ -2773,61 +3313,6 @@ function provisionCloudflareResources(tenantRoot, options = {}) {
2773
3313
  current.databaseId = created.uuid;
2774
3314
  current.previewDatabaseId = created.previewDatabaseUuid ?? created.uuid;
2775
3315
  };
2776
- const ensureQueue = () => {
2777
- const current = state.queues?.agentWork;
2778
- if (!current?.name) {
2779
- return;
2780
- }
2781
- let refreshedQueues = queues;
2782
- const exists = refreshedQueues.find((entry) => queueName(entry) === current.name);
2783
- if (exists) {
2784
- current.queueId = queueId(exists);
2785
- const currentDlq = current.dlqName ? refreshedQueues.find((entry) => queueName(entry) === current.dlqName) : null;
2786
- current.dlqId = queueId(currentDlq);
2787
- return;
2788
- }
2789
- if (dryRun) {
2790
- current.queueId = `dryrun-${current.name}`;
2791
- current.dlqId = current.dlqName ? `dryrun-${current.dlqName}` : null;
2792
- return;
2793
- }
2794
- try {
2795
- runWrangler(["queues", "create", current.name], {
2796
- cwd: tenantRoot,
2797
- capture: true,
2798
- env
2799
- });
2800
- } catch (error) {
2801
- if (!isWranglerAlreadyExistsError(error, [/Queue name .* is already taken/i, /\[code:\s*11009\]/i])) {
2802
- throw error;
2803
- }
2804
- }
2805
- refreshedQueues = listQueues(tenantRoot, env);
2806
- if (current.dlqName && !refreshedQueues.find((entry) => queueName(entry) === current.dlqName)) {
2807
- try {
2808
- runWrangler(["queues", "create", current.dlqName], {
2809
- cwd: tenantRoot,
2810
- capture: true,
2811
- env
2812
- });
2813
- } catch (error) {
2814
- if (!isWranglerAlreadyExistsError(error, [/Queue name .* is already taken/i, /\[code:\s*11009\]/i])) {
2815
- throw error;
2816
- }
2817
- }
2818
- }
2819
- refreshedQueues = listQueues(tenantRoot, env);
2820
- const created = refreshedQueues.find((entry) => queueName(entry) === current.name);
2821
- if (!created) {
2822
- throw new Error(`Unable to resolve Cloudflare queue ${current.name} after reconciliation.`);
2823
- }
2824
- current.queueId = queueId(created);
2825
- const createdDlq = current.dlqName ? refreshedQueues.find((entry) => queueName(entry) === current.dlqName) : null;
2826
- if (current.dlqName && !createdDlq) {
2827
- throw new Error(`Unable to resolve Cloudflare dead-letter queue ${current.dlqName} after reconciliation.`);
2828
- }
2829
- current.dlqId = queueId(createdDlq);
2830
- };
2831
3316
  const ensureR2Bucket = () => {
2832
3317
  const bucketName = state.content?.bucketName;
2833
3318
  if (!bucketName) {
@@ -2865,7 +3350,7 @@ function provisionCloudflareResources(tenantRoot, options = {}) {
2865
3350
  const exists = pagesProjects.find((entry) => entry?.name === current.projectName);
2866
3351
  if (exists) {
2867
3352
  current.url = exists.subdomain ? `https://${exists.subdomain}` : current.url ?? `https://${current.projectName}.pages.dev`;
2868
- ensurePagesProjectCompatibility(env.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "", current.projectName, env, exists, { state, target });
3353
+ ensurePagesProjectCompatibility(env.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "", current.projectName, env, exists, { state, target });
2869
3354
  return;
2870
3355
  }
2871
3356
  if (dryRun) {
@@ -2884,12 +3369,11 @@ function provisionCloudflareResources(tenantRoot, options = {}) {
2884
3369
  capture: true,
2885
3370
  env
2886
3371
  });
2887
- ensurePagesProjectCompatibility(env.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "", current.projectName, env, null, { state, target });
3372
+ ensurePagesProjectCompatibility(env.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "", current.projectName, env, null, { state, target });
2888
3373
  current.url = `https://${current.projectName}.pages.dev`;
2889
3374
  };
2890
3375
  ensureKv("FORM_GUARD_KV");
2891
3376
  ensureD1();
2892
- ensureQueue();
2893
3377
  ensureR2Bucket();
2894
3378
  ensurePagesProject();
2895
3379
  reconcileCloudflareWebCacheRules(tenantRoot, deployConfig, state, target, { dryRun });
@@ -2979,8 +3463,6 @@ function verifyProvisionedCloudflareResources(tenantRoot, options = {}) {
2979
3463
  pages: Boolean(state.pages?.projectName && (livePages || pagesProject?.name === state.pages.projectName)),
2980
3464
  formGuardKv: Boolean(state.kvNamespaces?.FORM_GUARD_KV?.name && kvNamespaces.find((entry) => entry?.title === state.kvNamespaces.FORM_GUARD_KV.name)),
2981
3465
  d1: Boolean(state.d1Databases?.SITE_DATA_DB?.databaseName && d1Databases.find((entry) => entry?.name === state.d1Databases.SITE_DATA_DB.databaseName)),
2982
- queue: Boolean(state.queues?.agentWork?.name && queues.find((entry) => queueName(entry) === state.queues.agentWork.name)),
2983
- dlq: !state.queues?.agentWork?.dlqName || Boolean(queues.find((entry) => queueName(entry) === state.queues.agentWork.dlqName)),
2984
3466
  r2: Boolean(state.content?.bucketName && buckets.find((entry) => entry?.name === state.content.bucketName)),
2985
3467
  pagesFormGuardKvBinding: !pagesBindings.kv_namespaces?.FORM_GUARD_KV || pageBindingConfigured("kv_namespaces", "FORM_GUARD_KV", pagesBindings.kv_namespaces.FORM_GUARD_KV),
2986
3468
  pagesD1Binding: !pagesBindings.d1_databases?.SITE_DATA_DB || pageBindingConfigured("d1_databases", "SITE_DATA_DB", pagesBindings.d1_databases.SITE_DATA_DB),
@@ -2996,12 +3478,6 @@ function verifyProvisionedCloudflareResources(tenantRoot, options = {}) {
2996
3478
  state.readiness.blockers = [];
2997
3479
  state.readiness.warnings = [];
2998
3480
  state.readiness.lastValidationSummary = checks;
2999
- const liveQueue = queues.find((entry) => queueName(entry) === state.queues?.agentWork?.name);
3000
- if (state.queues?.agentWork) {
3001
- state.queues.agentWork.queueId = queueId(liveQueue) ?? state.queues.agentWork.queueId ?? null;
3002
- const liveDlq = queues.find((entry) => queueName(entry) === state.queues.agentWork.dlqName);
3003
- state.queues.agentWork.dlqId = queueId(liveDlq) ?? state.queues.agentWork.dlqId ?? null;
3004
- }
3005
3481
  if (state.pages) {
3006
3482
  const configuredWebUrl = resolveConfiguredSurfaceBaseUrl(deployConfig, target, "web");
3007
3483
  if (configuredWebUrl) {
@@ -3240,9 +3716,11 @@ export {
3240
3716
  buildProvisioningSummary,
3241
3717
  buildPublicVars,
3242
3718
  buildSecretMap,
3719
+ buildTreeseedManagedCloudflareCacheRules,
3243
3720
  buildWranglerConfigContents,
3244
3721
  cleanupDestroyedState,
3245
3722
  cloudflareApiRequest,
3723
+ cloudflareDestroyVerification,
3246
3724
  collectMissingDeployInputs,
3247
3725
  createBranchPreviewDeployTarget,
3248
3726
  createPersistentDeployTarget,
@@ -3251,17 +3729,21 @@ export {
3251
3729
  deriveTreeseedStagingSurfaceDomain,
3252
3730
  destroyCloudflareResources,
3253
3731
  destroyTreeseedEnvironmentResources,
3732
+ dockerLocalRuntimeResourceOperations,
3254
3733
  ensureGeneratedWranglerConfig,
3255
3734
  finalizeDeploymentState,
3256
3735
  getTurnstileWidget,
3257
3736
  hasProvisionedCloudflareResources,
3258
3737
  isWranglerAlreadyExistsError,
3259
3738
  listD1Databases,
3739
+ listDnsRecords,
3740
+ listDnsZones,
3260
3741
  listKvNamespaces,
3261
3742
  listPagesProjects,
3262
3743
  listQueues,
3263
3744
  listR2Buckets,
3264
3745
  listTurnstileWidgets,
3746
+ listWorkers,
3265
3747
  loadDeployState,
3266
3748
  markDeploymentInitialized,
3267
3749
  markManagedServicesInitialized,
@@ -3286,6 +3768,7 @@ export {
3286
3768
  runRemoteD1Migrations,
3287
3769
  runWrangler,
3288
3770
  scopeFromTarget,
3771
+ setDestroyDockerRunnerForTests,
3289
3772
  shouldDeleteRailwayProjectAfterEnvironmentDestroy,
3290
3773
  syncCloudflareSecrets,
3291
3774
  updateTurnstileWidget,