void 0.22.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/README.md +5 -1
  2. package/dist/{account-cmd-CjyxcGkM.mjs → account-cmd-C84Ee8cO.mjs} +4 -4
  3. package/dist/{scan-ClYmX3sa.mjs → application-analysis-BVsVqc11.mjs} +135 -18
  4. package/dist/application-routing-B7QEjqSR.mjs +27 -0
  5. package/dist/{auth-CZuiVsFh.mjs → auth-CCjt0hcq.mjs} +1 -1
  6. package/dist/{auth-DLNN0D3Z.mjs → auth-CSkdO2Bb.mjs} +4 -47
  7. package/dist/{auth-link-BLO4ptzo.mjs → auth-link-CioEg6uY.mjs} +4 -4
  8. package/dist/{auth-router-ZUg9MF_U.mjs → auth-router-BsR981d4.mjs} +4 -4
  9. package/dist/{better-auth-shared-rsBGBvWJ.mjs → better-auth-shared-hy6RPh9W.mjs} +13 -2
  10. package/dist/{build-cmd-BxR5FROK.mjs → build-cmd-LzvNJORH.mjs} +17 -5
  11. package/dist/{cache-D0sWhgKI.mjs → cache-C4MvnrMH.mjs} +2 -2
  12. package/dist/{cancel-deploy-D4NUqFbi.mjs → cancel-deploy-abUxpP2n.mjs} +2 -2
  13. package/dist/{cf-build-output-BJ6yGEIS.mjs → cf-build-output-BPqOT964.mjs} +2 -1
  14. package/dist/cf-build-output-_0HNWysu.mjs +2 -0
  15. package/dist/cli/cli.mjs +80 -455
  16. package/dist/cli/{cf-compat.mjs → cloudflare-operation-process.mjs} +442 -421
  17. package/dist/cli/env-schema-probe.d.mts +2 -1
  18. package/dist/cli/env-schema-probe.mjs +3 -3
  19. package/dist/client-Cu7jWiF1.mjs +2 -0
  20. package/dist/{client-BTZ3XkrB.mjs → client-RV8NVeB8.mjs} +140 -21
  21. package/dist/{cloudflare-auth-DQkqoMYa.mjs → cloudflare-auth-Qdc7tw8F.mjs} +41 -38
  22. package/dist/{cloudflare-cmd-BKlfGHAy.mjs → cloudflare-cmd-BcrVyTmJ.mjs} +5 -5
  23. package/dist/cloudflare-config-Bktvwtpf.mjs +182 -0
  24. package/dist/{cloudflare-connect-Ctmrw579.mjs → cloudflare-connect-B8uPZ4nx.mjs} +3 -3
  25. package/dist/{cloudflare-operations-BT6OWFBk.mjs → cloudflare-operations-AiWashgg.mjs} +1 -1
  26. package/dist/{cloudflare-operations-B9tzgjmf.mjs → cloudflare-operations-fxHb-byx.mjs} +116 -172
  27. package/dist/{preset-UHj9ARyP.mjs → cloudflare-process-B-wekeR6.mjs} +93 -6
  28. package/dist/{config-VavjpDnp.d.mts → config-BMHb8RCj.d.mts} +1 -0
  29. package/dist/config-C_XRIPx2.mjs +89 -0
  30. package/dist/config-entry.d.mts +1 -1
  31. package/dist/{connect-Dg-WkW-C.mjs → connect-Bd4kJd9U.mjs} +5 -5
  32. package/dist/{create-project-DdoqwFLF.mjs → create-project-Boczwj5r.mjs} +1 -1
  33. package/dist/{create-project-l9J7pbtm.mjs → create-project-bMf6ffLZ.mjs} +2 -2
  34. package/dist/{db-gp2sCXyN.mjs → db-8uG64XLl.mjs} +21 -21
  35. package/dist/{delete-TTedbH6B.mjs → delete-NvbiyeJf.mjs} +2 -2
  36. package/dist/{deploy-CH-o2CaZ.mjs → deploy-DzAIwqNU.mjs} +908 -582
  37. package/dist/{deploy-B19L2Bra.mjs → deploy-bjXdFCtn.mjs} +1 -1
  38. package/dist/{domain-JRF59P_r.mjs → domain-y5Tvydvo.mjs} +3 -3
  39. package/dist/{email-Bo6G9LOZ.mjs → email-B3umsW75.mjs} +13 -28
  40. package/dist/{env-BYOrWQnv.mjs → env-BS6qYHDb.mjs} +4 -4
  41. package/dist/{env-public-BxU_0yTL.d.mts → env-public-BX_r8HR6.d.mts} +1 -1
  42. package/dist/{env-validation-BB4GkLxn.mjs → env-validation-BPn7vk-V.mjs} +18 -71
  43. package/dist/{env-validation-BXge7uyK.mjs → env-validation-DbTg7-ar.mjs} +1 -1
  44. package/dist/{fetch-CXDChK7B.mjs → fetch-BIZJh7vR.mjs} +2 -2
  45. package/dist/{fetch-stream-AOByI7Ki.mjs → fetch-stream-IvCYKyQL.mjs} +24 -4
  46. package/dist/{gen-sCtlCOdA.mjs → gen-B580--vC.mjs} +2 -2
  47. package/dist/gen-BVaUUumi.mjs +2 -0
  48. package/dist/{github-cmd-0-7PexDq.mjs → github-cmd-v45BdfKX.mjs} +2 -2
  49. package/dist/{handler-HEcZsaij.d.mts → handler-CZ4nAylQ.d.mts} +1 -1
  50. package/dist/help-DofyZuY7.mjs +2 -0
  51. package/dist/{help-GKtwl07I.mjs → help-daGKjXGk.mjs} +229 -747
  52. package/dist/index.d.mts +1 -1
  53. package/dist/index.mjs +288 -1502
  54. package/dist/info-B97bTX9N.mjs +113 -0
  55. package/dist/{init-FZx3Elvz.mjs → init-Bvy7zrBo.mjs} +46 -15
  56. package/dist/limits-Bq5LG8Id.d.mts +27 -0
  57. package/dist/limits-Cjuk2VPm.mjs +68 -0
  58. package/dist/{link-BrQfb_CU.mjs → link-CDqqCjFl.mjs} +3 -3
  59. package/dist/{list-D44jmIAM.mjs → list-CZj0dzKY.mjs} +3 -3
  60. package/dist/{live-CKJlvlNp.d.mts → live-Chw1eIMv.d.mts} +1 -1
  61. package/dist/{local-d1-Bg9OzEEO.mjs → local-d1-2CMnpuW_.mjs} +2 -2
  62. package/dist/login-DYt_An22.mjs +2 -0
  63. package/dist/{login-CAsAsQ-_.mjs → login-UZKFM_u7.mjs} +3 -3
  64. package/dist/{logs-BjKvFnVM.mjs → logs-CUZ6t9t3.mjs} +3 -3
  65. package/dist/migrate-8_2u55MD.mjs +2 -0
  66. package/dist/{migrate-BPITvDJN.mjs → migrate-DHul7PRV.mjs} +2 -1
  67. package/dist/{node-Dt7z256D.mjs → node-BkyRWRx8.mjs} +1 -1
  68. package/dist/operator-args-CLgKGlwU.mjs +690 -0
  69. package/dist/{operator-cmd-BK5CCiT9.mjs → operator-cmd-DBA6dl0m.mjs} +68 -22
  70. package/dist/pages/client.d.mts +34 -2
  71. package/dist/pages/client.mjs +59 -3
  72. package/dist/pages/index.d.mts +1 -1
  73. package/dist/pages/index.mjs +1 -1
  74. package/dist/pages/islands-plugin.mjs +1 -1
  75. package/dist/pages/protocol.d.mts +2 -2
  76. package/dist/pages/protocol.mjs +2 -308
  77. package/dist/{parse-filename-DioPHiR9.mjs → parse-filename-CUbj-1MP.mjs} +35 -1
  78. package/dist/plan-D1Q5rf-r.mjs +2 -0
  79. package/dist/plan-NPpwZ_kc.mjs +58 -0
  80. package/dist/platform-args-BJdRtlLq.mjs +506 -0
  81. package/dist/platform-args-D3RXyR6h.mjs +2 -0
  82. package/dist/{platform-auth-config-BlN8xTdD.mjs → platform-auth-config-B56E9YP1.mjs} +3 -3
  83. package/dist/{platform-auth-protection-7d5aV2Jg.mjs → platform-auth-protection-BDWO_aER.mjs} +2 -2
  84. package/dist/{platform-auth-recovery-Dxij8ZbR.mjs → platform-auth-recovery-1gg4CSRe.mjs} +3 -3
  85. package/dist/{platform-cmd-E0FuL212.mjs → platform-cmd-Bt-1w6pY.mjs} +1 -1
  86. package/dist/{platform-cmd-B3hwKrFK.mjs → platform-cmd-DMcQStSc.mjs} +24 -3
  87. package/dist/{platform-domain-D6Xcy9ZX.mjs → platform-domain-BNkcz0OB.mjs} +2 -2
  88. package/dist/{platform-lifecycle-k0E0xoxx.mjs → platform-lifecycle-9OyBALhH.mjs} +327 -215
  89. package/dist/{platform-lifecycle-DMh_qrry.mjs → platform-lifecycle-BE6C_jxh.mjs} +1 -1
  90. package/dist/{platform-management-Brz_BDiT.mjs → platform-management-CjwLVQwN.mjs} +6 -3
  91. package/dist/{platform-management-DOtss0BN.mjs → platform-management-CnyTdcWX.mjs} +1 -1
  92. package/dist/platform-plans-config-BNGKGr4P.mjs +359 -0
  93. package/dist/{platform-recovery-BvtcXON5.mjs → platform-recovery-D6KSpuFm.mjs} +2 -2
  94. package/dist/{plugin-inference-BMfKRSqE.mjs → plugin-inference-CXWnn79A.mjs} +174 -64
  95. package/dist/{prepare-C-6YZyyg.mjs → prepare-B5Mkic5u.mjs} +1 -1
  96. package/dist/{prepare-CRhJbVrG.mjs → prepare-DOsL0CC9.mjs} +4 -20
  97. package/dist/prepare-cgvDMtSb.mjs +2 -0
  98. package/dist/{project-cmd-BZLCqOqw.mjs → project-cmd-CNzrqvBv.mjs} +16 -16
  99. package/dist/{project-team-BvgM0WoX.mjs → project-team-DQOWPfQT.mjs} +2 -2
  100. package/dist/{project-token-l5DqK6Az.mjs → project-token-CTDYU0Cc.mjs} +2 -2
  101. package/dist/{project-zero-trust-v6Mvpp8y.mjs → project-zero-trust-BNAkW-_0.mjs} +2 -2
  102. package/dist/{protocol-BBa6cstI.d.mts → protocol-CjF_iI9X.d.mts} +2 -2
  103. package/dist/protocol-U7bfjHmA.mjs +331 -0
  104. package/dist/{provision-Fr9pRqce.mjs → provision-C4ORE7G7.mjs} +1 -1
  105. package/dist/{provision-C4IGqkBf.mjs → provision-CJFgTZY8.mjs} +89 -198
  106. package/dist/{requests-DN-BbNiM.mjs → requests-MhxYnau8.mjs} +2 -2
  107. package/dist/resource-name-C7LVpcRm.mjs +11 -0
  108. package/dist/{rollback-D7rzSaZM.mjs → rollback-CJ6iSDoU.mjs} +3 -3
  109. package/dist/route-url-CG7U-cRN.mjs +15 -0
  110. package/dist/{runner-B8wXwWlo.mjs → runner-CXA9Fh8h.mjs} +1 -1
  111. package/dist/{runner-p-dMs2UN.mjs → runner-Ol0TNjk6.mjs} +2 -2
  112. package/dist/runtime/ai.d.mts +12 -8
  113. package/dist/runtime/ai.mjs +84 -17
  114. package/dist/runtime/better-auth-mysql.mjs +1 -1
  115. package/dist/runtime/better-auth-pg.mjs +1 -1
  116. package/dist/runtime/better-auth.mjs +1 -1
  117. package/dist/runtime/client-react.mjs +2 -2
  118. package/dist/runtime/client-solid.mjs +2 -2
  119. package/dist/runtime/client-svelte.mjs +2 -2
  120. package/dist/runtime/client-vue.mjs +2 -2
  121. package/dist/runtime/client.mjs +2 -2
  122. package/dist/runtime/durable.d.mts +3 -1
  123. package/dist/runtime/durable.mjs +4 -1
  124. package/dist/runtime/email/testing.mjs +1 -1
  125. package/dist/runtime/env-public.d.mts +1 -1
  126. package/dist/runtime/fetch-stream.mjs +1 -1
  127. package/dist/runtime/fetch.mjs +1 -1
  128. package/dist/runtime/handler.d.mts +1 -1
  129. package/dist/runtime/kv.mjs +0 -1
  130. package/dist/runtime/limits.d.mts +2 -0
  131. package/dist/runtime/limits.mjs +2 -0
  132. package/dist/runtime/live-client.d.mts +1 -1
  133. package/dist/runtime/live-server.mjs +25 -16
  134. package/dist/runtime/live.d.mts +1 -1
  135. package/dist/runtime/migration-handler.mjs +62 -42
  136. package/dist/runtime/route-url.d.mts +4 -0
  137. package/dist/runtime/route-url.mjs +2 -0
  138. package/dist/runtime/routing.d.mts +180 -0
  139. package/dist/runtime/routing.mjs +1082 -0
  140. package/dist/runtime/sandbox-container.d.mts +1 -1
  141. package/dist/runtime/sandbox-container.mjs +1 -1
  142. package/dist/runtime/sandbox.d.mts +3 -3
  143. package/dist/runtime/sandbox.mjs +75 -45
  144. package/dist/runtime/sse.mjs +1 -1
  145. package/dist/runtime/validator.d.mts +1 -1
  146. package/dist/runtime/ws-server.d.mts +4 -2
  147. package/dist/runtime/ws-server.mjs +27 -2
  148. package/dist/runtime/ws.d.mts +2 -2
  149. package/dist/runtime/ws.mjs +8 -6
  150. package/dist/{sandbox-qpNBT8a3.d.mts → sandbox-XZAqzFlG.d.mts} +11 -8
  151. package/dist/{sandbox-container-DdNEBfCc.d.mts → sandbox-container-Bo0eiFKz.d.mts} +2 -1
  152. package/dist/{sandbox-container-4fdqLnyb.mjs → sandbox-container-C6ItmVuN.mjs} +4 -2
  153. package/dist/{scan-4tfN-PSn.mjs → scan-C7okrLyM.mjs} +4 -35
  154. package/dist/{secret-BOtOl_cb.mjs → secret-Dli5fP0B.mjs} +4 -4
  155. package/dist/{sse-BaC1jXko.mjs → sse-CQNaDFFV.mjs} +6 -3
  156. package/dist/validate-Dq_L3s0S.mjs +2 -0
  157. package/dist/{validate-CIUwFpjB.mjs → validate-ctOrgiS3.mjs} +2 -1
  158. package/dist/{wrangler-DQF1vKyf.mjs → wrangler-7K-bW_DL.mjs} +8 -239
  159. package/dist/{ws-BoY7vQML.d.mts → ws-CL1w7GXU.d.mts} +13 -2
  160. package/package.json +15 -8
  161. package/skills/migrate-vite-cloudflare-to-void/SKILL.md +34 -157
  162. package/skills/void/SKILL.md +50 -135
  163. package/skills/void/docs/guide/ai.md +94 -84
  164. package/skills/void/docs/guide/app-types.md +3 -32
  165. package/skills/void/docs/guide/auth.md +12 -116
  166. package/skills/void/docs/guide/database/d1.md +9 -54
  167. package/skills/void/docs/guide/database/mysql.md +1 -1
  168. package/skills/void/docs/guide/database/postgresql.md +5 -26
  169. package/skills/void/docs/guide/database.md +23 -75
  170. package/skills/void/docs/guide/deployment.md +27 -113
  171. package/skills/void/docs/guide/durable-state.md +43 -18
  172. package/skills/void/docs/guide/edge/headers.md +3 -47
  173. package/skills/void/docs/guide/edge/prerendering.md +5 -20
  174. package/skills/void/docs/guide/edge/redirects.md +11 -64
  175. package/skills/void/docs/guide/edge/revalidation.md +5 -18
  176. package/skills/void/docs/guide/edge/rewrites.md +56 -284
  177. package/skills/void/docs/guide/edge/static-assets.md +22 -72
  178. package/skills/void/docs/guide/email/domains.md +112 -0
  179. package/skills/void/docs/guide/email/receiving.md +139 -0
  180. package/skills/void/docs/guide/email/sending.md +231 -0
  181. package/skills/void/docs/guide/email.md +13 -619
  182. package/skills/void/docs/guide/env-migration.md +11 -11
  183. package/skills/void/docs/guide/env-vars.md +9 -29
  184. package/skills/void/docs/guide/index.md +0 -15
  185. package/skills/void/docs/guide/jobs.md +3 -18
  186. package/skills/void/docs/guide/kv.md +5 -11
  187. package/skills/void/docs/guide/live.md +5 -56
  188. package/skills/void/docs/guide/pages-routing/actions-and-forms.md +78 -125
  189. package/skills/void/docs/guide/pages-routing/head.md +10 -10
  190. package/skills/void/docs/guide/pages-routing/islands.md +6 -36
  191. package/skills/void/docs/guide/pages-routing/layouts.md +6 -128
  192. package/skills/void/docs/guide/pages-routing/loaders.md +3 -19
  193. package/skills/void/docs/guide/pages-routing/markdown.md +13 -171
  194. package/skills/void/docs/guide/pages-routing/overview.md +7 -17
  195. package/skills/void/docs/guide/pages-routing/view-transitions.md +1 -1
  196. package/skills/void/docs/guide/platform/administration/access.md +1 -4
  197. package/skills/void/docs/guide/platform/administration/email.md +35 -8
  198. package/skills/void/docs/guide/platform/administration/operations.md +26 -4
  199. package/skills/void/docs/guide/platform/administration/plans.md +126 -0
  200. package/skills/void/docs/guide/platform/administration/projects.md +5 -2
  201. package/skills/void/docs/guide/platform/administration/zero-trust.md +23 -152
  202. package/skills/void/docs/guide/platform/development/runtime.md +3 -13
  203. package/skills/void/docs/guide/platform/development/schema-ci.md +0 -58
  204. package/skills/void/docs/guide/platform/installation/credentials.md +6 -4
  205. package/skills/void/docs/guide/platform/installation/domains.md +30 -2
  206. package/skills/void/docs/guide/platform/installation/first-deployment.md +2 -0
  207. package/skills/void/docs/guide/platform/installation/maintenance.md +3 -1
  208. package/skills/void/docs/guide/platform/installation/prerequisites.md +20 -15
  209. package/skills/void/docs/guide/platform/installation/setup.md +9 -5
  210. package/skills/void/docs/guide/platform-administration.md +1 -0
  211. package/skills/void/docs/guide/queues.md +7 -9
  212. package/skills/void/docs/guide/quickstart.md +38 -37
  213. package/skills/void/docs/guide/remote-dev.md +4 -9
  214. package/skills/void/docs/guide/sandboxes.md +29 -21
  215. package/skills/void/docs/guide/server-routing.md +9 -72
  216. package/skills/void/docs/guide/sse.md +4 -18
  217. package/skills/void/docs/guide/ssg.md +3 -15
  218. package/skills/void/docs/guide/ssr.md +14 -62
  219. package/skills/void/docs/guide/storage.md +9 -4
  220. package/skills/void/docs/guide/type-safety.md +3 -14
  221. package/skills/void/docs/guide/typed-fetch.md +3 -7
  222. package/skills/void/docs/guide/websockets.md +68 -40
  223. package/skills/void/docs/integrations/agents.md +3 -3
  224. package/skills/void/docs/integrations/cloudflare.md +85 -316
  225. package/skills/void/docs/integrations/frameworks/analog.md +5 -64
  226. package/skills/void/docs/integrations/frameworks/astro.md +4 -73
  227. package/skills/void/docs/integrations/frameworks/nuxt.md +5 -62
  228. package/skills/void/docs/integrations/frameworks/overview.md +11 -54
  229. package/skills/void/docs/integrations/frameworks/react-router.md +5 -60
  230. package/skills/void/docs/integrations/frameworks/sveltekit.md +6 -65
  231. package/skills/void/docs/integrations/frameworks/tanstack-start.md +4 -62
  232. package/skills/void/docs/integrations/nodejs-bun-deno.md +5 -69
  233. package/skills/void/docs/reference/api/auth.md +156 -0
  234. package/skills/void/docs/reference/api/client.md +87 -0
  235. package/skills/void/docs/reference/api/database.md +95 -0
  236. package/skills/void/docs/reference/api/durable.md +46 -0
  237. package/skills/void/docs/reference/api/env.md +50 -0
  238. package/skills/void/docs/reference/api/handlers.md +254 -0
  239. package/skills/void/docs/reference/api/pages.md +241 -0
  240. package/skills/void/docs/reference/api/plugin.md +39 -0
  241. package/skills/void/docs/reference/api/resources.md +109 -0
  242. package/skills/void/docs/reference/api/rewrites.md +76 -0
  243. package/skills/void/docs/reference/api/types.md +92 -0
  244. package/skills/void/docs/reference/api.md +56 -1218
  245. package/skills/void/docs/reference/cli/auth.md +88 -0
  246. package/skills/void/docs/reference/cli/database.md +128 -0
  247. package/skills/void/docs/reference/cli/deploy.md +85 -0
  248. package/skills/void/docs/reference/cli/domains.md +41 -0
  249. package/skills/void/docs/reference/cli/email.md +129 -0
  250. package/skills/void/docs/reference/cli/generate.md +116 -0
  251. package/skills/void/docs/reference/cli/github.md +189 -0
  252. package/skills/void/docs/reference/cli/platform-config.md +92 -0
  253. package/skills/void/docs/reference/cli/platform-email.md +81 -0
  254. package/skills/void/docs/reference/cli/platform-installation.md +127 -0
  255. package/skills/void/docs/reference/cli/platform-operations.md +90 -0
  256. package/skills/void/docs/reference/cli/platform-users.md +89 -0
  257. package/skills/void/docs/reference/cli/platform-zero-trust.md +45 -0
  258. package/skills/void/docs/reference/cli/platform.md +70 -0
  259. package/skills/void/docs/reference/cli/project.md +214 -0
  260. package/skills/void/docs/reference/cli/secrets.md +76 -0
  261. package/skills/void/docs/reference/cli/setup.md +70 -0
  262. package/skills/void/docs/reference/cli.md +32 -1682
  263. package/skills/void/docs/reference/config.md +12 -18
  264. package/skills/void/docs/reference/resource-inference.md +3 -58
  265. package/skills/void/docs/reference/structure.md +14 -41
  266. package/dist/canonical-json-DuDiiUsQ.mjs +0 -13
  267. package/dist/client-Czz8o5jP.mjs +0 -2
  268. package/dist/gen-CNJ62MM7.mjs +0 -2
  269. package/dist/help-CmZzxUba.mjs +0 -2
  270. package/dist/login-CGcRKEoi.mjs +0 -2
  271. package/dist/migrate-CYfbKkXh.mjs +0 -2
  272. package/dist/plan-BEZ8VJW0.mjs +0 -256
  273. package/dist/plan-DpuOr14e.mjs +0 -2
  274. package/dist/prepare-Bm3iq-u4.mjs +0 -2
  275. package/dist/validate-EKmJWxmy.mjs +0 -2
  276. /package/dist/cli/{cf-compat.d.mts → cloudflare-operation-process.d.mts} +0 -0
@@ -2,20 +2,20 @@ import { n as __toESM } from "./rolldown-runtime-DXIUcv95.mjs";
2
2
  import { a as join, n as dirname, u as resolve } from "./dist-BVqXeR_h.mjs";
3
3
  import { t as require_picocolors } from "./picocolors-BTps1_gs.mjs";
4
4
  import { t as cfAccessHeaders } from "./cf-access-DRsQRe6k.mjs";
5
- import { d as openBrowser } from "./auth-DLNN0D3Z.mjs";
5
+ import { d as openBrowser } from "./auth-CSkdO2Bb.mjs";
6
6
  import { a as listInstallationManifests, d as readInstallationRecoverySecrets, f as readPendingInstallationSecrets, g as savePlatformConnection, h as resolveInstallationManifest, i as latestPlatformWorkerCutoverGeneration, l as platformWorkerSecretUpdateIntentState, n as assertPlatformInstallationSlugAvailable, s as normalizePlatformApiUrl, t as acquirePlatformLifecycleLock, u as promoteInstallationSecrets, v as writeInstallationManifest, y as writePendingInstallationSecrets } from "./platform-registry-QBa4DK32.mjs";
7
7
  import { n as ensurePlatformAccessToken } from "./cf-access-P0iZnu0q.mjs";
8
8
  import { B as text$1, E as confirm, F as password, G as settings, I as select, K as u$1, M as multiselect, N as note, O as intro, P as outro, R as symbol, U as CANCEL_SYMBOL, W as isCancel, g as S_PASSWORD_MASK, i as S_BAR_END, j as log, r as S_BAR } from "./dist-CTBk70IR.mjs";
9
- import { o as resolveWranglerArgv } from "./preset-UHj9ARyP.mjs";
10
- import { s as runCloudflareAuthCommand } from "./cloudflare-auth-DQkqoMYa.mjs";
11
- import { _ as resolvePinnedApiBaseUrl, o as createDefaultRunWrangler } from "./provision-C4IGqkBf.mjs";
9
+ import { c as resolveCloudflareOperationArgv } from "./cloudflare-process-B-wekeR6.mjs";
10
+ import { o as runCloudflareAuthCommand } from "./cloudflare-auth-Qdc7tw8F.mjs";
11
+ import { _ as resolvePinnedApiBaseUrl, o as createDefaultRunWrangler } from "./provision-CJFgTZY8.mjs";
12
12
  import { c as import_picocolors$1, i as createSpinner, n as cliCommandName, r as cliTitle } from "./output-Dm_A4Tbv.mjs";
13
13
  import { copyFileSync, cpSync, existsSync, lstatSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
14
14
  import { fileURLToPath } from "node:url";
15
15
  import { createHash, createHmac, randomBytes, randomUUID } from "node:crypto";
16
16
  import { tmpdir } from "node:os";
17
17
  import { execFile } from "node:child_process";
18
- import { PLATFORM_AUTH_SETUP_PROOF_PREFIX, PLATFORM_DISABLED_KEY, PLATFORM_DISABLED_VALUE, PLATFORM_HEALTH_HOSTNAME_LABEL, PLATFORM_LOGIN_KINDS, PLATFORM_R2_OWNERSHIP_MARKER_KEY, PLATFORM_REMOTE_INSTALLATION_MANIFEST_KEY, PLATFORM_SANDBOX_DRAIN_KEY, createApplicationRoutes, createPlatformDeploymentManifest, createPlatformInstallationManifest, createPlatformResourceNames, createPlatformResourceOwnershipMarker, createPlatformWorkerConfigs, normalizeApplicationDomain, normalizeInstallationSlug, parsePlatformAccessProtection, parsePlatformAdmissionPolicy, parsePlatformAuthenticationSetup, parsePlatformInstallationManifest, parsePlatformLoginConfiguration, parsePlatformResourceOwnershipMarker, recordInstallationResource, recordInstallationStep, setInstallationDesiredState, setInstallationEndpoints, transitionInstallation } from "@void/platform";
18
+ import { PLATFORM_AUTH_SETUP_PROOF_PREFIX, PLATFORM_DISABLED_KEY, PLATFORM_DISABLED_VALUE, PLATFORM_HEALTH_HOSTNAME_LABEL, PLATFORM_LOGIN_KINDS, PLATFORM_R2_OWNERSHIP_MARKER_KEY, PLATFORM_REMOTE_INSTALLATION_MANIFEST_KEY, createApplicationRoutes, createPlatformDeploymentManifest, createPlatformInstallationManifest, createPlatformResourceNames, createPlatformResourceOwnershipMarker, createPlatformWorkerConfigs, normalizeApplicationDomain, normalizeInstallationSlug, normalizePlatformDashboardUrl, parsePlatformAccessProtection, parsePlatformAdmissionPolicy, parsePlatformAuthenticationSetup, parsePlatformInstallationManifest, parsePlatformLoginConfiguration, parsePlatformResourceOwnershipMarker, platformDashboardUrl, recordInstallationResource, recordInstallationStep, setInstallationDesiredState, setInstallationEndpoints, transitionInstallation } from "@void/platform";
19
19
  import { stripVTControlCharacters } from "node:util";
20
20
  import { encodeCloudflareVersionMessage, parseCloudflareVersionMessage, readCloudflareTriggerConfig, readCloudflareVersionMigrationTag, stabilizeCloudflareVersionTriggers } from "@void/deploy-cloudflare";
21
21
  import { readFile } from "node:fs/promises";
@@ -717,6 +717,14 @@ function text(value, name) {
717
717
  if (typeof value !== "string" || !value || value.length > 8192) throw new Error(`platform: Cloudflare Access did not return ${name}.`);
718
718
  return value;
719
719
  }
720
+ function platformAccessIdentityProvider(provider) {
721
+ const id = text(provider.id, "identity provider ID");
722
+ const name = provider.name;
723
+ return {
724
+ id,
725
+ name: name == null || typeof name === "string" && !name.trim() ? id : text(name, "identity provider name")
726
+ };
727
+ }
720
728
  function ids(value) {
721
729
  if (!Array.isArray(value)) return [];
722
730
  return value.map((entry) => typeof entry === "string" ? entry : text(object(entry, "policy").id, "policy ID"));
@@ -846,10 +854,7 @@ async function inspectPlatformAccess(client, input) {
846
854
  audience: "validation"
847
855
  }]
848
856
  });
849
- const identityProviders = (await client.list("identity_providers")).map((provider) => ({
850
- id: text(provider.id, "identity provider ID"),
851
- name: text(provider.name ?? provider.id, "identity provider name")
852
- }));
857
+ const identityProviders = (await client.list("identity_providers")).map(platformAccessIdentityProvider);
853
858
  if (!input.identityProviderIds.length || !input.policyIds.length || input.identityProviderIds.length > 16 || input.policyIds.length > 16 || new Set(input.identityProviderIds).size !== input.identityProviderIds.length || new Set(input.policyIds).size !== input.policyIds.length) throw new Error("platform: Select explicit identity providers and company policies for Access.");
854
859
  if (input.identityProviderIds.some((id) => !identityProviders.some((provider) => provider.id === id))) throw new Error("platform: A selected Access identity provider is unavailable in this account.");
855
860
  const policies = await Promise.all(input.policyIds.map(async (id) => object(await client.get(`policies/${encodeURIComponent(id)}`), "Access policy")));
@@ -1277,9 +1282,9 @@ async function preparePlatformAccessSetup(input) {
1277
1282
  const identityProviderIds = spec.identityProviderIds ?? (process.stdin.isTTY ? answer$1(await multiselect({
1278
1283
  message: "Company identity providers:",
1279
1284
  required: true,
1280
- options: providers.filter((provider) => typeof provider.id === "string").map((provider) => ({
1281
- value: String(provider.id),
1282
- label: stripVTControlCharacters(String(provider.name ?? provider.id))
1285
+ options: providers.filter((provider) => typeof provider.id === "string").map(platformAccessIdentityProvider).map((provider) => ({
1286
+ value: provider.id,
1287
+ label: stripVTControlCharacters(provider.name)
1283
1288
  }))
1284
1289
  })) : []);
1285
1290
  const policyIds = spec.policyIds ?? (process.stdin.isTTY ? answer$1(await multiselect({
@@ -1417,9 +1422,9 @@ async function resolvePlatformCloudflareCredentialWithInput(readInput, dependenc
1417
1422
  input,
1418
1423
  credential: result.credential
1419
1424
  };
1420
- const login = await runWrangler(["login"], false);
1425
+ const login = await runWrangler({ type: "auth.login" }, false);
1421
1426
  assertNotCancelled();
1422
- if (login.status !== 0) throw new Error("platform: Cloudflare browser login did not complete.");
1427
+ if (login.status !== 0 || login.signal || login.error) throw new Error("platform: Cloudflare browser login did not complete.");
1423
1428
  const credential = await readWranglerOAuthCredential(runWrangler);
1424
1429
  assertNotCancelled();
1425
1430
  if (!credential) throw new Error("platform: Cloudflare sign-in completed without a usable OAuth credential.");
@@ -1442,15 +1447,11 @@ async function readExistingCredential(dependencies, runWrangler) {
1442
1447
  return await readWranglerOAuthCredential(runWrangler);
1443
1448
  }
1444
1449
  async function readWranglerOAuthCredential(runWrangler = (args) => runCloudflareAuthCommand(process.cwd(), args)) {
1445
- const result = await runWrangler([
1446
- "auth",
1447
- "token",
1448
- "--json"
1449
- ], true);
1450
- if (result.status !== 0) return null;
1450
+ const result = await runWrangler({ type: "auth.token" }, true);
1451
+ if (result.status !== 0 || result.signal || result.error) return null;
1451
1452
  try {
1452
- const parsed = JSON.parse(result.stdout);
1453
- if (parsed.type !== "oauth" || typeof parsed.token !== "string" || !parsed.token.trim()) return null;
1453
+ const parsed = result.data;
1454
+ if (!parsed || parsed.type !== "oauth" || typeof parsed.token !== "string" || !parsed.token.trim()) return null;
1454
1455
  return {
1455
1456
  source: "oauth",
1456
1457
  token: parsed.token.trim()
@@ -1490,6 +1491,7 @@ async function verifyInstalledPlatformAccessProtection(input) {
1490
1491
  if ([
1491
1492
  input.plan.apiUrl,
1492
1493
  input.plan.proxyUrl,
1494
+ ...input.dashboardUrl ? [input.dashboardUrl] : [],
1493
1495
  ...input.manifest.endpoints.dashboardUrl ? [input.manifest.endpoints.dashboardUrl] : []
1494
1496
  ].map((value) => new URL(value).origin).some((origin) => !origins.includes(origin)) || protection.applications.some((entry) => entry.audience !== app.aud || !platformAccessPublicDestinations(app).includes(new URL(entry.origin).hostname))) throw new Error("platform: Access audiences and complete origin coverage must match the active platform protection. Restore the current Access checkpoint or repair its applications.");
1495
1497
  for (const other of await access.list("apps")) {
@@ -1505,7 +1507,7 @@ async function verifyInstalledPlatformAccessProtection(input) {
1505
1507
  }
1506
1508
  const existingWorkers = input.plan.actions.filter((action) => action.kind === "worker" && action.action === "update").map((action) => action.name);
1507
1509
  if (existingWorkers.length) {
1508
- const { verifyProtectionWorkerOrigins } = await import("./platform-auth-protection-7d5aV2Jg.mjs");
1510
+ const { verifyProtectionWorkerOrigins } = await import("./platform-auth-protection-BDWO_aER.mjs");
1509
1511
  await verifyProtectionWorkerOrigins({
1510
1512
  request: input.client.request.bind(input.client),
1511
1513
  listWorkerDomains: input.client.listWorkerDomains.bind(input.client),
@@ -1598,6 +1600,7 @@ async function verifyPlatformAuthenticationDatabase(client, accountId, resource,
1598
1600
  //#endregion
1599
1601
  //#region src/cli/platform-plan.ts
1600
1602
  async function createPlatformInstallPlan(client, input, existing, options) {
1603
+ const dashboardUrl = input.dashboardUrl !== void 0 ? normalizePlatformDashboardUrl(input.dashboardUrl) : existing ? existing.deployment.controlPlane.dashboardUrl ?? (existing.endpoints.dashboardUrl ? normalizePlatformDashboardUrl(existing.endpoints.dashboardUrl) : null) ?? platformDashboardUrl(existing.deployment) : null;
1601
1604
  const account = resolveAccount(await client.listAccounts(), input.accountId);
1602
1605
  if (existing && existing.account.id !== account.id) throw new Error(`platform: Refusing to resume installation ${existing.id} in a different Cloudflare account.`);
1603
1606
  const providedEmailSharedZoneId = input.emailSharedZoneId?.trim();
@@ -1646,7 +1649,8 @@ async function createPlatformInstallPlan(client, input, existing, options) {
1646
1649
  },
1647
1650
  controlPlane: {
1648
1651
  apiHostname: input.controlPlaneDomain || null,
1649
- dashboardHostname: null,
1652
+ dashboardHostname: dashboardUrl ? new URL(dashboardUrl).hostname : null,
1653
+ ...dashboardUrl ? { dashboardUrl } : {},
1650
1654
  proxyHostname: null
1651
1655
  },
1652
1656
  displayName: input.displayName,
@@ -1664,13 +1668,21 @@ async function createPlatformInstallPlan(client, input, existing, options) {
1664
1668
  sandbox: true
1665
1669
  }
1666
1670
  } : existingWithoutEmailZone;
1667
- const comparableExistingWithoutEmailZone = existing && deployment.capabilities.email && !existing.deployment.capabilities.email ? {
1671
+ const existingWithDashboard = {
1668
1672
  ...existingWithSandbox,
1673
+ controlPlane: {
1674
+ ...existingWithSandbox.controlPlane,
1675
+ dashboardHostname: deployment.controlPlane.dashboardHostname,
1676
+ ...deployment.controlPlane.dashboardUrl ? { dashboardUrl: deployment.controlPlane.dashboardUrl } : {}
1677
+ }
1678
+ };
1679
+ const comparableExistingWithoutEmailZone = existing && deployment.capabilities.email && !existing.deployment.capabilities.email ? {
1680
+ ...existingWithDashboard,
1669
1681
  capabilities: {
1670
- ...existingWithSandbox.capabilities,
1682
+ ...existingWithDashboard.capabilities,
1671
1683
  email: true
1672
1684
  }
1673
- } : existingWithSandbox;
1685
+ } : existingWithDashboard;
1674
1686
  if (existing && (JSON.stringify(comparableExistingWithoutEmailZone) !== JSON.stringify(deploymentWithoutEmailZone) || recordedEmailZone !== void 0 && recordedEmailZone !== configuredEmailZone || recordedSenderDomain !== void 0 && recordedSenderDomain !== configuredSenderDomain)) throw new Error(`platform: --resume inputs do not match installation ${existing.id}. Use its recorded configuration.`);
1675
1687
  const names = deployment.resources;
1676
1688
  const wildcardHostname = `*.${deployment.application.baseDomain}`;
@@ -1694,7 +1706,7 @@ async function createPlatformInstallPlan(client, input, existing, options) {
1694
1706
  }
1695
1707
  throw error;
1696
1708
  });
1697
- const [workersSubdomain, aiGateways, routingKv, isrKv, legacyKv, r2Buckets, usageQueues, cronQueues, dispatchNamespaces, hyperdrives, workers] = await Promise.all([
1709
+ const [workersSubdomain, aiGateways, routingKv, isrKv, legacyKv, r2Buckets, usageQueues, cronQueues, deploymentQueues, dispatchNamespaces, hyperdrives, workers] = await Promise.all([
1698
1710
  Promise.resolve(accountSubdomain),
1699
1711
  gatewayInventory,
1700
1712
  client.listKvNamespaces(account.id, names.routingKv),
@@ -1703,6 +1715,7 @@ async function createPlatformInstallPlan(client, input, existing, options) {
1703
1715
  client.listR2Buckets(account.id),
1704
1716
  client.listQueues(account.id, names.usageQueue),
1705
1717
  client.listQueues(account.id, names.cronQueue),
1718
+ options?.deploymentExecution ?? existing?.resources.some((resource) => resource.key === "deployment-queue") ? client.listQueues(account.id, names.deploymentQueue) : Promise.resolve([]),
1706
1719
  client.listDispatchNamespaces(account.id),
1707
1720
  client.listHyperdriveConfigs(account.id),
1708
1721
  client.listWorkerNames(account.id)
@@ -1737,11 +1750,13 @@ async function createPlatformInstallPlan(client, input, existing, options) {
1737
1750
  const expectedPatterns = new Set(createApplicationRoutes(deployment.application).map((route) => route.pattern));
1738
1751
  const expectedWorker = deployment.resources.dispatchWorker;
1739
1752
  const ownedWorker = intentProofs.has("dispatch-worker") || existing?.resources.some((resource) => resource.key === "dispatch-worker" && resource.kind === "worker" && resource.name === expectedWorker && resource.state === "ready");
1740
- const conflicts = (await client.listWorkerRoutes(zone.id)).filter((route) => expectedPatterns.has(route.pattern) && (route.script !== expectedWorker || !ownedWorker));
1753
+ const [workerRoutes, workerDomains] = await Promise.all([client.listWorkerRoutes(zone.id), client.listWorkerDomains(account.id)]);
1754
+ const conflicts = workerRoutes.filter((route) => expectedPatterns.has(route.pattern) && (route.script !== expectedWorker || !ownedWorker));
1741
1755
  if (conflicts.length > 0) {
1742
1756
  const conflict = conflicts[0];
1743
1757
  throw new Error(`platform: Application route ${JSON.stringify(conflict.pattern)} is already assigned to ${JSON.stringify(conflict.script ?? "no Worker")}. Choose another application domain.`);
1744
1758
  }
1759
+ assertApplicationRoutesPreserveWorkerDomains([...expectedPatterns], expectedWorker, ownedWorker === true, zone.name, workerRoutes, workerDomains);
1745
1760
  }
1746
1761
  if (deployment.controlPlane.apiHostname) {
1747
1762
  const apiHostname = deployment.controlPlane.apiHostname;
@@ -1784,6 +1799,7 @@ async function createPlatformInstallPlan(client, input, existing, options) {
1784
1799
  addNamedPlan(actions, existing, "assets-bucket", "r2-bucket", names.assetsBucket, r2Buckets.filter((resource) => resource.name === names.assetsBucket), "reuse", options, remoteOwnership, intentProofs, ambiguousResources);
1785
1800
  addNamedPlan(actions, existing, "usage-queue", "queue", names.usageQueue, usageQueues, "reuse", options, remoteOwnership);
1786
1801
  addNamedPlan(actions, existing, "cron-queue", "queue", names.cronQueue, cronQueues, "reuse", options, remoteOwnership);
1802
+ if (options?.deploymentExecution ?? existing?.resources.some((resource) => resource.key === "deployment-queue")) addNamedPlan(actions, existing, "deployment-queue", "queue", names.deploymentQueue, deploymentQueues, "reuse", options, remoteOwnership);
1787
1803
  addNamedPlan(actions, existing, "dispatch-namespace", "dispatch-namespace", names.dispatchNamespace, dispatchNamespaces.filter((resource) => resource.name === names.dispatchNamespace), "reuse", options, remoteOwnership);
1788
1804
  const recordedHyperdrive = existing?.resources.find((resource) => resource.key === "hyperdrive");
1789
1805
  const externalHyperdriveId = input.externalHyperdrive?.id || (recordedHyperdrive?.ownership === "adopted" ? recordedHyperdrive.id : "");
@@ -1861,7 +1877,8 @@ async function revalidatePlatformInstallAction(client, original, manifest, actio
1861
1877
  }, manifest, {
1862
1878
  ...options,
1863
1879
  includeEmailGateway: original.actions.some((entry) => entry.key === "email-gateway-worker"),
1864
- enableSandbox: original.deployment.capabilities.sandbox
1880
+ enableSandbox: original.deployment.capabilities.sandbox,
1881
+ deploymentExecution: original.actions.some((entry) => entry.key === "deployment-queue")
1865
1882
  })).actions.find((entry) => entry.key === action.key);
1866
1883
  if (!current || current.kind !== action.kind || current.name !== action.name || current.action !== action.action) throw new Error(`platform: Cloudflare changed after the installation plan was approved for ${action.kind} ${JSON.stringify(action.name)}. Review a new plan before retrying.`);
1867
1884
  if (action.kind === "worker" && action.action === "update") {
@@ -2055,6 +2072,7 @@ const RESOURCE_NAME_KEY_BY_ACTION = {
2055
2072
  "api-worker": "apiWorker",
2056
2073
  "assets-bucket": "assetsBucket",
2057
2074
  "cron-queue": "cronQueue",
2075
+ "deployment-queue": "deploymentQueue",
2058
2076
  "dispatch-namespace": "dispatchNamespace",
2059
2077
  "dispatch-worker": "dispatchWorker",
2060
2078
  hyperdrive: "hyperdrive",
@@ -2066,6 +2084,33 @@ const RESOURCE_NAME_KEY_BY_ACTION = {
2066
2084
  "tail-worker": "tailWorker",
2067
2085
  "usage-queue": "usageQueue"
2068
2086
  };
2087
+ function assertApplicationRoutesPreserveWorkerDomains(patterns, dispatchWorker, ownedDispatchWorker, zoneName, workerRoutes, workerDomains) {
2088
+ const plannedRoutes = patterns.map(parseWorkerRoutePattern);
2089
+ const existingRoutes = workerRoutes.map((route) => ({
2090
+ ...parseWorkerRoutePattern(route.pattern),
2091
+ script: route.script
2092
+ }));
2093
+ for (const domain of workerDomains) {
2094
+ const hostname = domain.hostname.toLowerCase();
2095
+ if (!(domain.zoneName ? domain.zoneName.toLowerCase() === zoneName.toLowerCase() : hostname === zoneName.toLowerCase() || hostname.endsWith(`.${zoneName.toLowerCase()}`)) || domain.service === dispatchWorker && ownedDispatchWorker) continue;
2096
+ for (const planned of plannedRoutes) {
2097
+ if (!workerRouteHostMatches(planned.host, hostname)) continue;
2098
+ if (!["http", "https"].every((scheme) => existingRoutes.some((route) => route.script !== dispatchWorker && route.path === "/*" && (!route.scheme || route.scheme === scheme) && workerRouteHostMatches(route.host, hostname) && route.host.replace(/^\*/, "").length > planned.host.replace(/^\*/, "").length))) throw new Error(`platform: Application route ${JSON.stringify(planned.pattern)} would intercept Worker custom domain ${JSON.stringify(domain.hostname)}, currently served by ${JSON.stringify(domain.service)}. Choose another application domain, or use \`void platform install --workers-dev\` for a new installation and add a suitable domain later.`);
2099
+ }
2100
+ }
2101
+ }
2102
+ function parseWorkerRoutePattern(pattern) {
2103
+ const match = /^(?:(https?):\/\/)?([^/]+)(\/.*)?$/.exec(pattern);
2104
+ return {
2105
+ host: match?.[2]?.toLowerCase() ?? "",
2106
+ path: match?.[3] ?? "/",
2107
+ pattern,
2108
+ scheme: match?.[1] ?? null
2109
+ };
2110
+ }
2111
+ function workerRouteHostMatches(host, hostname) {
2112
+ return host.startsWith("*") ? hostname.endsWith(host.slice(1)) : host === hostname;
2113
+ }
2069
2114
  function platformProvisionIntentHash(manifest, key, kind, name, previousResourceId = null) {
2070
2115
  return createHash("sha256").update(JSON.stringify({
2071
2116
  key,
@@ -2101,10 +2146,12 @@ async function readRemoteOwnership(client, accountId, routingNamespaces, existin
2101
2146
  }
2102
2147
  function formatPlatformInstallPlan(plan) {
2103
2148
  const lines = [
2104
- `${import_picocolors$1.default.bold(`${plan.deployment.displayName} in ${plan.account.name}`)} (${plan.account.id})`,
2105
- plan.deployment.application.zoneName ? `${import_picocolors$1.default.bold("Applications:")} *.${plan.deployment.application.baseDomain}` : `${import_picocolors$1.default.bold("Applications:")} workers.dev (testing); add a domain with void platform domain set <domain>`,
2149
+ `${import_picocolors$1.default.bold(`${plan.deployment.displayName} in ${plan.account.name}`)}`,
2150
+ `${import_picocolors$1.default.bold("Account:")} ${plan.account.id}`,
2151
+ plan.deployment.application.zoneName ? `${import_picocolors$1.default.bold("Applications:")} *.${plan.deployment.application.baseDomain}` : `${import_picocolors$1.default.bold("Applications:")} workers.dev for testing. Add a domain with void platform domain set <domain>`,
2106
2152
  `${import_picocolors$1.default.bold("Control plane:")} ${plan.apiUrl}`,
2107
2153
  `${import_picocolors$1.default.bold("Resource proxy:")} ${plan.proxyUrl}`,
2154
+ ...platformDashboardUrl(plan.deployment) ? [`${import_picocolors$1.default.bold("Dashboard:")} ${platformDashboardUrl(plan.deployment)} (deploy separately)`] : [],
2108
2155
  `${import_picocolors$1.default.bold("Login methods:")} ${plan.deployment.authentication.providers.join(", ")}`,
2109
2156
  `${import_picocolors$1.default.bold("Access protection:")} ${plan.deployment.authentication.accessProtection ? "Enabled" : "Not enabled"}`,
2110
2157
  `${import_picocolors$1.default.bold("Login callback:")} ${plan.apiUrl}/auth/callback`,
@@ -2933,8 +2980,8 @@ async function installPlatform(plan, secrets, existing, dependencies) {
2933
2980
  if (plan.actions.some((action) => action.action === "inspect")) throw new Error("platform: AI Gateway access must be verified before provisioning. Provide AI Gateway: Edit on the runtime token, then retry.");
2934
2981
  const now = dependencies.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
2935
2982
  const cloudflareApiBaseUrl = dependencies.client.apiBaseUrl ?? resolvePinnedApiBaseUrl();
2936
- const runPinnedPlatformWrangler = (args, accountId, cwd, capture) => runPlatformWranglerAtApiBase(args, accountId, cwd, capture, cloudflareApiBaseUrl);
2937
- const runParallelPinnedPlatformWrangler = (args, accountId, cwd, capture) => runPlatformWranglerAtApiBase(args, accountId, cwd, capture, cloudflareApiBaseUrl, true);
2983
+ const runPinnedPlatformWrangler = (operation, accountId, cwd, capture) => runPlatformWranglerAtApiBase(operation, accountId, cwd, capture, cloudflareApiBaseUrl);
2984
+ const runParallelPinnedPlatformWrangler = (operation, accountId, cwd, capture) => runPlatformWranglerAtApiBase(operation, accountId, cwd, capture, cloudflareApiBaseUrl, true);
2938
2985
  const resolvedRuntime = dependencies.runtime ?? resolvePlatformRuntime();
2939
2986
  const runtimeDirectory = resolvedRuntime.directory;
2940
2987
  const runtime = resolvedRuntime.manifest;
@@ -3152,6 +3199,12 @@ async function installPlatform(plan, secrets, existing, dependencies) {
3152
3199
  manifest = transitionInstallation(manifest, "provisioning", now());
3153
3200
  writeInstallationManifest(manifest);
3154
3201
  }
3202
+ if (runtime.deploymentExecution !== 1 && (manifest.deploymentExecution === 1 || dependencies.databaseRollbackSource?.manifest.deploymentExecution === 1)) {
3203
+ if (!releaseDatabaseLock.checkpoint) throw new Error("platform: The database lifecycle lock cannot fence deployment admission.");
3204
+ await releaseDatabaseLock.checkpoint(manifest);
3205
+ dependencies.onProgress?.("Verifying durable deployment operations have drained");
3206
+ await (dependencies.verifyDeploymentExecutionDrain ?? verifyPlatformDeploymentExecutionDrain)(plan.apiUrl, createPlatformHealthProbeToken(manifest.ownershipNonce), manifest.id);
3207
+ }
3155
3208
  if (dependencies.prepareAuthentication) {
3156
3209
  dependencies.onProgress?.("Preparing platform authentication");
3157
3210
  try {
@@ -3203,6 +3256,7 @@ async function installPlatform(plan, secrets, existing, dependencies) {
3203
3256
  client: dependencies.client,
3204
3257
  manifest,
3205
3258
  plan,
3259
+ dashboardUrl: platformDashboardUrl(plan.deployment),
3206
3260
  protection,
3207
3261
  checkpoint: secretCheckpoint(secrets, workerSecretUpdateIntent)
3208
3262
  });
@@ -3270,11 +3324,6 @@ async function installPlatform(plan, secrets, existing, dependencies) {
3270
3324
  if (targetSandboxDrainProtocol >= 1 && !prepareSandboxDrain) throw new Error("platform: The PostgreSQL lifecycle lock cannot establish the sandbox-drain safety checkpoint.");
3271
3325
  const sandboxDrainPreparation = targetSandboxDrainProtocol >= 1 ? await prepareSandboxDrain(targetSandboxDrainProtocol >= 3 ? 3 : targetSandboxDrainProtocol >= 2 ? 2 : 0) : null;
3272
3326
  if (sandboxDrainPreparation && sandboxDrainPreparation.admissionProtocol < targetSandboxDrainProtocol) throw new Error("platform: The database did not arm the Sandbox admission protocol required by this runtime.");
3273
- const sandboxDrainRoutingKvId = requireId(Object.fromEntries(manifest.resources.map((resource) => [resource.key, resource.id])), "routing-kv");
3274
- const writeSandboxDrainMirror = async (preparation) => {
3275
- await dependencies.client.putKvValue(plan.account.id, sandboxDrainRoutingKvId, PLATFORM_SANDBOX_DRAIN_KEY, preparation.mirrorValue);
3276
- };
3277
- if (sandboxDrainPreparation) await writeSandboxDrainMirror(sandboxDrainPreparation);
3278
3327
  const sandboxDrainState = sandboxDrainPreparation?.state ?? "complete";
3279
3328
  if (targetSandboxDrainProtocol >= 1 && (manifest.safetyProtocols?.sandboxDrain ?? 0) < targetSandboxDrainProtocol) {
3280
3329
  manifest = {
@@ -3333,6 +3382,7 @@ async function installPlatform(plan, secrets, existing, dependencies) {
3333
3382
  accountId: plan.account.id,
3334
3383
  apiUrl: plan.apiUrl,
3335
3384
  deployment: plan.deployment,
3385
+ deploymentExecution: runtime.deploymentExecution,
3336
3386
  healthProbeToken: createPlatformHealthProbeToken(manifest.ownershipNonce),
3337
3387
  installationId: manifest.id,
3338
3388
  ownershipNonce: manifest.ownershipNonce,
@@ -3544,7 +3594,7 @@ async function installPlatform(plan, secrets, existing, dependencies) {
3544
3594
  } else for (const key of workerKeys) await deployOneWorker(key);
3545
3595
  manifest = setInstallationEndpoints(manifest, {
3546
3596
  apiUrl: plan.apiUrl,
3547
- dashboardUrl: null,
3597
+ dashboardUrl: platformDashboardUrl(plan.deployment),
3548
3598
  proxyUrl: plan.proxyUrl
3549
3599
  }, now());
3550
3600
  if (preserveDisabled) {
@@ -3555,6 +3605,7 @@ async function installPlatform(plan, secrets, existing, dependencies) {
3555
3605
  manifest = {
3556
3606
  ...manifest,
3557
3607
  deployment: plan.deployment,
3608
+ deploymentExecution: runtime.deploymentExecution,
3558
3609
  platformVersion: version,
3559
3610
  runtime: runtimeProvenance,
3560
3611
  updatedAt: now(),
@@ -3653,9 +3704,7 @@ async function installPlatform(plan, secrets, existing, dependencies) {
3653
3704
  zoneNameServers: zone?.nameServers ?? []
3654
3705
  };
3655
3706
  }
3656
- const completedDrain = await prepareSandboxDrain();
3657
- if (completedDrain.state !== "complete") throw new Error("platform: Sandbox drain completion changed while it was being verified. Traffic remains disabled.");
3658
- await writeSandboxDrainMirror(completedDrain);
3707
+ if ((await prepareSandboxDrain()).state !== "complete") throw new Error("platform: Sandbox drain completion changed while it was being verified. Traffic remains disabled.");
3659
3708
  }
3660
3709
  if (requiresStagedCommit) {
3661
3710
  if (!authenticationCutoverCommitted) {
@@ -3766,6 +3815,7 @@ async function installPlatform(plan, secrets, existing, dependencies) {
3766
3815
  manifest = {
3767
3816
  ...manifest,
3768
3817
  deployment: plan.deployment,
3818
+ deploymentExecution: runtime.deploymentExecution,
3769
3819
  platformVersion: version,
3770
3820
  runtime: runtimeProvenance,
3771
3821
  safetyProtocols: runtime.safetyProtocols ?? { sandboxDrain: 0 },
@@ -4404,14 +4454,6 @@ async function preparePlatformSandboxDrainConnection(client, requiredAdmissionPr
4404
4454
  return {
4405
4455
  admissionProtocol: row.admission_protocol,
4406
4456
  generation: row.generation,
4407
- mirrorValue: JSON.stringify({
4408
- version: 1,
4409
- generation: row.generation,
4410
- state: row.state,
4411
- startedAt: row.started_at,
4412
- updatedAt: row.updated_at,
4413
- ...row.completed_at ? { completedAt: row.completed_at } : {}
4414
- }),
4415
4457
  state: row.state === "complete" ? "complete" : "pending"
4416
4458
  };
4417
4459
  };
@@ -4578,11 +4620,12 @@ let cachedPlatformUploaderHash;
4578
4620
  function platformWorkerUploaderHash() {
4579
4621
  if (cachedPlatformUploaderHash !== void 0) return cachedPlatformUploaderHash;
4580
4622
  try {
4581
- const launcher = resolveWranglerArgv(process.cwd())[1];
4623
+ const launcher = resolveCloudflareOperationArgv(process.cwd())[1];
4582
4624
  if (!launcher) return cachedPlatformUploaderHash = null;
4583
4625
  const digest = createHash("sha256");
4584
4626
  digest.update("platform-uploader:v1\0");
4585
4627
  digest.update(process.version);
4628
+ digest.update(readFileSync(launcher));
4586
4629
  digest.update(readFileSync(resolve(dirname(launcher), "..", "..", "package.json")));
4587
4630
  const cfDirectory = dirname(fileURLToPath(import.meta.resolve("cf/package.json")));
4588
4631
  digest.update(readFileSync(join(cfDirectory, "package.json")));
@@ -4692,28 +4735,27 @@ function snapshotPlatformWorkerUploadInputs(config, directory) {
4692
4735
  }
4693
4736
  function platformWorkerApiReadRunner(accountId, workerName, readCloudflare, fallback) {
4694
4737
  const base = `/accounts/${encodeURIComponent(accountId)}/workers/scripts/${encodeURIComponent(workerName)}`;
4695
- return async (args, account, cwd, capture) => {
4738
+ return async (operation, account, cwd, capture) => {
4696
4739
  try {
4697
- if (args[0] === "deployments" && (args[1] === "status" || args[1] === "list")) {
4740
+ if (operation.type === "deployments.status" || operation.type === "deployments.list") {
4698
4741
  const response = await readCloudflare(`${base}/deployments`);
4699
4742
  const deployments = response && typeof response === "object" && !Array.isArray(response) ? response.deployments : null;
4700
4743
  if (!Array.isArray(deployments)) throw new Error(`platform: Cloudflare returned malformed deployment history for ${workerName}.`);
4701
4744
  return {
4702
- status: args[1] === "status" && deployments.length === 0 ? 1 : 0,
4745
+ status: operation.type === "deployments.status" && deployments.length === 0 ? 1 : 0,
4703
4746
  stderr: "",
4704
- stdout: JSON.stringify(args[1] === "status" ? deployments[0] : deployments)
4705
- };
4706
- }
4707
- if (args[0] === "versions" && args[1] === "view" && args[2]) {
4708
- const version = await readCloudflare(`${base}/versions/${encodeURIComponent(args[2])}`);
4709
- return {
4710
- status: 0,
4711
- stderr: "",
4712
- stdout: JSON.stringify(version)
4747
+ stdout: "",
4748
+ data: operation.type === "deployments.status" ? deployments[0] : deployments
4713
4749
  };
4714
4750
  }
4751
+ if (operation.type === "versions.view") return {
4752
+ status: 0,
4753
+ stderr: "",
4754
+ stdout: "",
4755
+ data: await readCloudflare(`${base}/versions/${encodeURIComponent(operation.versionId)}`)
4756
+ };
4715
4757
  } catch {}
4716
- return fallback(args, account, cwd, capture);
4758
+ return fallback(operation, account, cwd, capture);
4717
4759
  };
4718
4760
  }
4719
4761
  async function deployPlatformWorker(config, secrets, options) {
@@ -4831,27 +4873,21 @@ async function deployPlatformWorker(config, secrets, options) {
4831
4873
  };
4832
4874
  writeFileSync(configPath, `${JSON.stringify(uploadConfig, null, 2)}\n`, { mode: 384 });
4833
4875
  }
4834
- const argv = [
4835
- ...options.workerExisted ? ["versions", "upload"] : ["deploy"],
4836
- "--config",
4876
+ const operation = {
4877
+ type: options.workerExisted ? "versions.upload" : "worker.deploy",
4837
4878
  configPath,
4838
- "--strict",
4839
- "--experimental-provision=false",
4840
- "--experimental-auto-create=false",
4841
- ...!options.workerExisted ? ["--autoconfig=false"] : [],
4842
- "--message",
4843
- encodeCloudflareVersionMessage([], nextTriggers, uploadedContentHash ?? void 0)
4844
- ];
4879
+ message: encodeCloudflareVersionMessage([], nextTriggers, uploadedContentHash ?? void 0)
4880
+ };
4845
4881
  if (secrets) {
4846
4882
  const secretsPath = join(directory, "secrets.json");
4847
4883
  writeFileSync(secretsPath, JSON.stringify(secrets), { mode: 384 });
4848
- argv.push("--secrets-file", secretsPath);
4884
+ operation.secretsFilePath = secretsPath;
4849
4885
  }
4850
4886
  const releaseUploadSlot = await options.acquireUploadSlot?.();
4851
4887
  let uploaded;
4852
4888
  let uploadError;
4853
4889
  try {
4854
- uploaded = options.workerExisted ? await options.runner(argv, accountId, directory, true) : await runPlatformWranglerForConditionalCreate(argv, accountId, directory, true, workerName, options.apiBaseUrl);
4890
+ uploaded = options.workerExisted ? await options.runner(operation, accountId, directory, true) : await runPlatformWranglerForConditionalCreate(operation, accountId, directory, true, workerName, options.apiBaseUrl);
4855
4891
  assertCleanPlatformWrangler(uploaded, `${options.workerExisted ? "upload" : "bootstrap"} ${workerName}`);
4856
4892
  } catch (error) {
4857
4893
  uploadError = error;
@@ -4859,7 +4895,8 @@ async function deployPlatformWorker(config, secrets, options) {
4859
4895
  } finally {
4860
4896
  releaseUploadSlot?.(uploadError);
4861
4897
  }
4862
- uploadedVersionId = parseUploadedPlatformWorkerVersion(`${uploaded.stdout}\n${uploaded.stderr}`);
4898
+ const uploadData = uploaded.data;
4899
+ uploadedVersionId = typeof uploadData?.versionId === "string" ? uploadData.versionId : null;
4863
4900
  if (!options.workerExisted) activatedVersionId = uploadedVersionId;
4864
4901
  if (!uploadedVersionId) throw new Error(`platform: Wrangler uploaded ${workerName} without reporting its immutable version id.`);
4865
4902
  await options.onStaged(previousVersionId, uploadedVersionId, previousTriggers);
@@ -4869,16 +4906,15 @@ async function deployPlatformWorker(config, secrets, options) {
4869
4906
  const deploymentsBeforeActivation = await readPlatformWorkerDeployments(configPath, accountId, workerName, readRunner);
4870
4907
  if (deploymentsBeforeActivation[0]?.id !== previousDeploymentId || !deploymentRunsOnlyVersion(deploymentsBeforeActivation[0], previousVersionId)) throw new Error(`platform: Worker ${workerName} changed concurrently before activation; refusing to overwrite it.`);
4871
4908
  activatedVersionId = uploadedVersionId;
4872
- assertCleanPlatformWrangler(await options.runner([
4873
- "versions",
4874
- "deploy",
4875
- `${uploadedVersionId}@100`,
4876
- "--config",
4909
+ assertCleanPlatformWrangler(await options.runner({
4910
+ type: "versions.deploy",
4877
4911
  configPath,
4878
- "--message",
4879
- `Void platform activation ${uploadedVersionId}`,
4880
- "--yes"
4881
- ], accountId, directory, true), `activate ${workerName}`);
4912
+ versions: [{
4913
+ versionId: uploadedVersionId,
4914
+ percentage: 100
4915
+ }],
4916
+ message: `Void platform activation ${uploadedVersionId}`
4917
+ }, accountId, directory, true), `activate ${workerName}`);
4882
4918
  }
4883
4919
  const deploymentsAfterActivation = await readPlatformWorkerDeployments(configPath, accountId, workerName, readRunner);
4884
4920
  const lineage = classifyPlatformWorkerDeploymentLineage(deploymentsAfterActivation, uploadedVersionId, previousDeploymentId);
@@ -5072,28 +5108,24 @@ async function rollbackPlatformWorker(input) {
5072
5108
  }
5073
5109
  }
5074
5110
  function platformWorkerCommandRunner(accountId, cwd, runner) {
5075
- return (args, options) => runner(args, accountId, cwd, options.capture);
5111
+ return (operation, options) => runner(operation, accountId, cwd, options.capture);
5076
5112
  }
5077
- async function runPlatformWrangler(args, accountId, cwd, capture) {
5078
- return runPlatformWranglerAtApiBase(args, accountId, cwd, capture);
5113
+ async function runPlatformWrangler(operation, accountId, cwd, capture) {
5114
+ return runPlatformWranglerAtApiBase(operation, accountId, cwd, capture);
5079
5115
  }
5080
5116
  const PLATFORM_WRANGLER_TIMEOUT_MS = 6e5;
5081
- async function runPlatformWranglerAtApiBase(args, accountId, cwd, capture, apiBaseUrl = resolvePinnedApiBaseUrl(), parallelWorkerUpload = false) {
5117
+ async function runPlatformWranglerAtApiBase(operation, accountId, cwd, capture, apiBaseUrl = resolvePinnedApiBaseUrl(), parallelWorkerUpload = false) {
5082
5118
  const startedAt = performance.now();
5083
5119
  const result = await createDefaultRunWrangler(process.cwd(), {
5084
- launcher: resolveWranglerArgv(process.cwd()),
5120
+ launcher: resolveCloudflareOperationArgv(process.cwd()),
5085
5121
  parallelWorkerUpload,
5086
5122
  timeoutMs: PLATFORM_WRANGLER_TIMEOUT_MS
5087
- })([...args], {
5123
+ })(operation, {
5088
5124
  accountId,
5089
5125
  apiBaseUrl,
5090
- captureJson: capture,
5091
5126
  cwd
5092
5127
  });
5093
- if (process.env.VOID_PLATFORM_TIMINGS === "1") {
5094
- const operation = args[0] === "versions" || args[0] === "deployments" ? `${args[0]} ${args[1] ?? ""}` : args[0] ?? "unknown";
5095
- process.stderr.write(`[platform timing] wrangler ${operation} ${(performance.now() - startedAt).toFixed(0)} ms\n`);
5096
- }
5128
+ if (process.env.VOID_PLATFORM_TIMINGS === "1") process.stderr.write(`[platform timing] Cloudflare ${operation.type} ${(performance.now() - startedAt).toFixed(0)} ms\n`);
5097
5129
  const stdout = result.stdout || "";
5098
5130
  const stderr = result.stderr || "";
5099
5131
  if (!capture && result.status !== 0) {
@@ -5101,7 +5133,7 @@ async function runPlatformWranglerAtApiBase(args, accountId, cwd, capture, apiBa
5101
5133
  if (stderr) process.stderr.write(stderr);
5102
5134
  }
5103
5135
  return {
5104
- status: result.status,
5136
+ ...result,
5105
5137
  stderr,
5106
5138
  stdout
5107
5139
  };
@@ -5222,48 +5254,30 @@ async function withConditionalPlatformWorkerCreate(accountId, workerName, operat
5222
5254
  if (proxy.state.conflictStatus !== null) throw new Error(`platform: Worker ${workerName} was created concurrently (HTTP ${proxy.state.conflictStatus}); no existing Worker was changed. Re-run to inspect the collision.`);
5223
5255
  return result;
5224
5256
  }
5225
- async function runPlatformWranglerForConditionalCreate(args, accountId, cwd, capture, workerName, upstreamApiBaseUrl) {
5226
- return withConditionalPlatformWorkerCreate(accountId, workerName, (apiBaseUrl) => runPlatformWranglerAtApiBase(args, accountId, cwd, capture, apiBaseUrl), upstreamApiBaseUrl);
5257
+ async function runPlatformWranglerForConditionalCreate(operation, accountId, cwd, capture, workerName, upstreamApiBaseUrl) {
5258
+ return withConditionalPlatformWorkerCreate(accountId, workerName, (apiBaseUrl) => runPlatformWranglerAtApiBase(operation, accountId, cwd, capture, apiBaseUrl), upstreamApiBaseUrl);
5227
5259
  }
5228
5260
  async function readActivePlatformWorkerVersion(configPath, accountId, workerName, runner = runPlatformWrangler) {
5229
- const result = await runner([
5230
- "deployments",
5231
- "status",
5232
- "--config",
5261
+ const result = await runner({
5262
+ type: "deployments.status",
5233
5263
  configPath,
5234
- "--name",
5235
- workerName,
5236
- "--json"
5237
- ], accountId, process.cwd(), true);
5264
+ workerName
5265
+ }, accountId, process.cwd(), true);
5238
5266
  if (result.status !== 0) assertCleanPlatformWrangler(result, `read active version for ${workerName}`);
5239
- let value;
5240
- try {
5241
- value = JSON.parse(result.stdout);
5242
- } catch {
5243
- throw new Error(`platform: Wrangler returned malformed deployment status for ${workerName}.`);
5244
- }
5267
+ const value = result.data;
5245
5268
  if (!value || typeof value !== "object" || !Array.isArray(value.versions)) throw new Error(`platform: Wrangler returned malformed deployment status for ${workerName}.`);
5246
5269
  const active = value.versions.filter((entry) => entry && typeof entry === "object" && Number(entry.percentage) === 100);
5247
5270
  if (active.length !== 1 || typeof active[0].version_id !== "string") return null;
5248
5271
  return active[0].version_id;
5249
5272
  }
5250
5273
  async function readPlatformWorkerDeployments(configPath, accountId, workerName, runner = runPlatformWrangler) {
5251
- const result = await runner([
5252
- "deployments",
5253
- "list",
5254
- "--config",
5274
+ const result = await runner({
5275
+ type: "deployments.list",
5255
5276
  configPath,
5256
- "--name",
5257
- workerName,
5258
- "--json"
5259
- ], accountId, process.cwd(), true);
5277
+ workerName
5278
+ }, accountId, process.cwd(), true);
5260
5279
  if (result.status !== 0) assertCleanPlatformWrangler(result, `read deployment lineage for ${workerName}`);
5261
- let value;
5262
- try {
5263
- value = JSON.parse(result.stdout);
5264
- } catch {
5265
- throw new Error(`platform: Wrangler returned malformed deployment history for ${workerName}.`);
5266
- }
5280
+ const value = result.data;
5267
5281
  if (!Array.isArray(value)) throw new Error(`platform: Wrangler returned malformed deployment history for ${workerName}.`);
5268
5282
  return value.map((entry, index) => {
5269
5283
  if (!entry || typeof entry !== "object" || typeof entry.id !== "string" || typeof entry.created_on !== "string" || !Array.isArray(entry.versions)) throw new Error(`platform: Wrangler returned malformed deployment history for ${workerName}.`);
@@ -5298,16 +5312,15 @@ async function restorePlatformWorkerDeployment(input) {
5298
5312
  if (restorationVersions.length === 0 || restorationVersions.length > 2 || restorationVersions.some((version, index) => version.percentage > 100 || restorationVersions.findIndex((entry) => entry.versionId === version.versionId) !== index) || Math.abs(totalPercentage - 100) > .001) throw new Error(`Worker ${input.workerName} deployment ${input.restoration.id} has an unsupported traffic distribution.`);
5299
5313
  const before = await readPlatformWorkerDeployments(input.configPath, input.accountId, input.workerName, input.runner);
5300
5314
  if (before[0]?.id !== input.expectedActiveDeploymentId || !deploymentRunsOnlyVersion(before[0], input.expectedActiveVersionId) || before[1]?.id !== input.restoration.id || !samePlatformWorkerTraffic(before[1], input.restoration)) throw new Error(`Worker ${input.workerName} changed concurrently before deployment restoration; refusing to overwrite the newer deployment.`);
5301
- assertCleanPlatformWrangler(await input.runner([
5302
- "versions",
5303
- "deploy",
5304
- ...restorationVersions.map((version) => `${version.versionId}@${version.percentage}`),
5305
- "--config",
5306
- input.configPath,
5307
- "--message",
5308
- `Void platform restoration ${input.restoration.id}`,
5309
- "--yes"
5310
- ], input.accountId, input.directory, true), `restore deployment ${input.restoration.id}`);
5315
+ assertCleanPlatformWrangler(await input.runner({
5316
+ type: "versions.deploy",
5317
+ configPath: input.configPath,
5318
+ versions: restorationVersions.map((version) => ({
5319
+ versionId: version.versionId,
5320
+ percentage: version.percentage
5321
+ })),
5322
+ message: `Void platform restoration ${input.restoration.id}`
5323
+ }, input.accountId, input.directory, true), `restore deployment ${input.restoration.id}`);
5311
5324
  const after = await readPlatformWorkerDeployments(input.configPath, input.accountId, input.workerName, input.runner);
5312
5325
  if (!samePlatformWorkerTraffic(after[0], input.restoration) || after[1]?.id !== input.expectedActiveDeploymentId) throw new Error(`Worker ${input.workerName} deployment restoration was superseded or did not restore deployment ${input.restoration.id}.`);
5313
5326
  }
@@ -5363,23 +5376,14 @@ async function readActivePlatformWorkerSnapshot(configPath, accountId, workerNam
5363
5376
  });
5364
5377
  }
5365
5378
  async function readPlatformWorkerVersionSnapshot(configPath, accountId, workerName, expectedOwnershipMarker, versionId, runner, validateCurrentVersion) {
5366
- const result = await runner([
5367
- "versions",
5368
- "view",
5379
+ const result = await runner({
5380
+ type: "versions.view",
5369
5381
  versionId,
5370
- "--config",
5371
5382
  configPath,
5372
- "--name",
5373
- workerName,
5374
- "--json"
5375
- ], accountId, process.cwd(), true);
5383
+ workerName
5384
+ }, accountId, process.cwd(), true);
5376
5385
  assertCleanPlatformWrangler(result, `read rollback metadata for ${workerName}`);
5377
- let value;
5378
- try {
5379
- value = JSON.parse(result.stdout);
5380
- } catch {
5381
- throw new Error(`platform: Wrangler returned malformed version metadata for ${workerName}.`);
5382
- }
5386
+ const value = result.data;
5383
5387
  if (!value || typeof value !== "object") throw new Error(`platform: Wrangler returned malformed version metadata for ${workerName}.`);
5384
5388
  const resources = value.resources;
5385
5389
  const bindings = resources && typeof resources === "object" && !Array.isArray(resources) ? resources.bindings : null;
@@ -5405,11 +5409,8 @@ function withoutCompletedPlatformWorkerMigrations(config, activeMigrationTag) {
5405
5409
  delete result.migrations;
5406
5410
  return result;
5407
5411
  }
5408
- function parseUploadedPlatformWorkerVersion(output) {
5409
- return /(?:Worker|Current) Version ID:\s*([0-9a-f]{8}-[0-9a-f-]{27})/i.exec(output)?.[1] ?? null;
5410
- }
5411
5412
  function assertCleanPlatformWrangler(result, operation) {
5412
- if (result.status !== 0) throw new Error(`platform: Wrangler failed to ${operation} (${result.stderr.trim() || `exit ${result.status}`}).`);
5413
+ if (result.status !== 0 || result.signal || result.error) throw new Error(`platform: Wrangler failed to ${operation} (${result.stderr.trim() || `exit ${result.status}`}).`);
5413
5414
  }
5414
5415
  function cleanPlatformCommandResult() {
5415
5416
  return {
@@ -5530,18 +5531,18 @@ function parseWorkerCutoverSnapshot(value) {
5530
5531
  return null;
5531
5532
  }
5532
5533
  }
5533
- function interruptedWorkerCutover(manifest) {
5534
+ function workerCutoverGenerations(manifest) {
5534
5535
  const generations = /* @__PURE__ */ new Map();
5535
5536
  for (const step of manifest.steps) {
5536
5537
  const resolution = /^worker-cutover:(\d+):(committed|restored)$/.exec(step.key);
5537
5538
  if (resolution) {
5538
5539
  const generation = Number(resolution[1]);
5539
5540
  const entry = generations.get(generation) ?? {
5540
- resolved: false,
5541
+ resolution: null,
5541
5542
  staged: /* @__PURE__ */ new Map(),
5542
5543
  unsafeTraffic: /* @__PURE__ */ new Set()
5543
5544
  };
5544
- entry.resolved = true;
5545
+ entry.resolution = resolution[2];
5545
5546
  generations.set(generation, entry);
5546
5547
  continue;
5547
5548
  }
@@ -5549,7 +5550,7 @@ function interruptedWorkerCutover(manifest) {
5549
5550
  if (unsafeTraffic) {
5550
5551
  const generation = Number(unsafeTraffic[1]);
5551
5552
  const entry = generations.get(generation) ?? {
5552
- resolved: false,
5553
+ resolution: null,
5553
5554
  staged: /* @__PURE__ */ new Map(),
5554
5555
  unsafeTraffic: /* @__PURE__ */ new Set()
5555
5556
  };
@@ -5563,14 +5564,17 @@ function interruptedWorkerCutover(manifest) {
5563
5564
  const snapshot = parseWorkerCutoverSnapshot(step.resultHash);
5564
5565
  if (!snapshot) throw new Error(`platform: Invalid Worker cutover checkpoint ${step.key}.`);
5565
5566
  const entry = generations.get(generation) ?? {
5566
- resolved: false,
5567
+ resolution: null,
5567
5568
  staged: /* @__PURE__ */ new Map(),
5568
5569
  unsafeTraffic: /* @__PURE__ */ new Set()
5569
5570
  };
5570
5571
  entry.staged.set(staged[2], snapshot);
5571
5572
  generations.set(generation, entry);
5572
5573
  }
5573
- const unresolved = [...generations.entries()].filter(([, entry]) => !entry.resolved && entry.staged.size > 0).sort(([left], [right]) => right - left)[0];
5574
+ return generations;
5575
+ }
5576
+ function interruptedWorkerCutover(manifest) {
5577
+ const unresolved = [...workerCutoverGenerations(manifest).entries()].filter(([, entry]) => entry.resolution === null && entry.staged.size > 0).sort(([left], [right]) => right - left)[0];
5574
5578
  return unresolved ? {
5575
5579
  generation: unresolved[0],
5576
5580
  staged: unresolved[1].staged,
@@ -5584,8 +5588,8 @@ async function verifyPlatformWorkerCutoverVersions(manifest, generation, configs
5584
5588
  "tail",
5585
5589
  "dispatch"
5586
5590
  ]) {
5587
- const cutover = interruptedWorkerCutover(manifest);
5588
- if (cutover?.generation !== generation || cutover.staged.size !== workerKeys.length) throw new Error(`platform: Worker cutover generation ${generation} is missing a complete immutable-version checkpoint.`);
5591
+ const cutover = workerCutoverGenerations(manifest).get(generation);
5592
+ if (!cutover || cutover.resolution === "restored" || cutover.staged.size !== workerKeys.length) throw new Error(`platform: Worker cutover generation ${generation} is missing a complete immutable-version checkpoint.`);
5589
5593
  const directory = mkdtempSync(join(tmpdir(), "void-platform-cutover-verify-"));
5590
5594
  try {
5591
5595
  for (const key of workerKeys) {
@@ -5721,9 +5725,8 @@ async function verifyPlatformSandboxDrain(apiUrl, healthProbeToken, fetcher = fe
5721
5725
  if (status.version !== 1 || !validState || typeof status.complete !== "boolean" || !validPhase || status.generation !== null && (typeof status.generation !== "string" || status.generation.length === 0) || status.updatedAt !== null && (typeof status.updatedAt !== "string" || !Number.isFinite(Date.parse(status.updatedAt))) || status.state === "required" && (status.complete || status.generation !== null || status.updatedAt !== null || status.phase !== null) || status.state === "draining" && (status.complete || typeof status.generation !== "string" || typeof status.updatedAt !== "string" || status.phase === null) || status.state === "complete" && (!status.complete || typeof status.generation !== "string" || typeof status.updatedAt !== "string" || status.phase === null)) throw new Error("platform: Sandbox drain probe returned an invalid DB-backed protocol v1 document. Traffic remains disabled.");
5722
5726
  return status.state === "complete";
5723
5727
  }
5724
- async function bootstrapPlatformSharedEmail(apiUrl, healthProbeToken, fetcher = fetch) {
5725
- const response = await fetcher(`${apiUrl}/internal/platform/email-bootstrap`, {
5726
- method: "POST",
5728
+ async function verifyPlatformDeploymentExecutionDrain(apiUrl, healthProbeToken, installationId, fetcher = fetch) {
5729
+ const response = await fetcher(`${apiUrl}/__void/platform/deployment-execution`, {
5727
5730
  headers: {
5728
5731
  "X-Void-Platform-Probe": healthProbeToken,
5729
5732
  ...cfAccessHeaders(process.env, apiUrl)
@@ -5731,18 +5734,104 @@ async function bootstrapPlatformSharedEmail(apiUrl, healthProbeToken, fetcher =
5731
5734
  signal: AbortSignal.timeout(3e4),
5732
5735
  redirect: "manual"
5733
5736
  });
5737
+ if (!response.ok) throw new Error(`platform: Deployment execution drain probe failed (HTTP ${response.status}). Keep the current runtime until its durable deployment operations have drained.`);
5734
5738
  let value;
5735
5739
  try {
5736
5740
  value = await response.json();
5737
5741
  } catch {
5738
- throw new Error(`platform: Shared email bootstrap failed at ${apiUrl} (HTTP ${response.status}). Traffic remains disabled.`);
5742
+ throw new Error("platform: Deployment execution drain probe returned an invalid document.");
5739
5743
  }
5740
- const status = value && typeof value === "object" && !Array.isArray(value) ? value : null;
5741
- if (!response.ok || status?.version !== 1 || status.state !== "ready" || typeof status.operationId !== "string" || status.operationId.length === 0) {
5742
- const detail = status && typeof status.operationId === "string" ? ` Operation ${status.operationId}: ${typeof status.operationStatus === "string" ? status.operationStatus : "incomplete"}.` : status && typeof status.operationStatus === "string" ? ` Operation status: ${status.operationStatus}.` : "";
5744
+ const status = value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
5745
+ if (status?.protocolVersion !== 1 || status.installationId !== installationId || typeof status.admissionClosed !== "boolean" || !Number.isSafeInteger(status.pendingDeployments) || Number(status.pendingDeployments) < 0) throw new Error("platform: Deployment execution drain probe returned an invalid document.");
5746
+ if (!status.admissionClosed || status.pendingDeployments !== 0) throw new Error("platform: Durable deployment operations are still admitted or unfinished. Keep the current runtime and retry this command after they finish.");
5747
+ }
5748
+ async function bootstrapPlatformSharedEmail(apiUrl, healthProbeToken, fetcher = fetch) {
5749
+ const deadline = Date.now() + 3e5;
5750
+ let operationId = null;
5751
+ const timedOut = () => /* @__PURE__ */ new Error(`platform: Shared email bootstrap did not finish within five minutes at ${apiUrl}.${operationId ? ` Operation ${operationId} remains incomplete.` : ""} Traffic remains disabled. Retry the installation's repair or resume command to reconcile the existing email operation.`);
5752
+ const waitForProgress = async () => {
5753
+ const remaining = deadline - Date.now();
5754
+ if (remaining <= 0) throw timedOut();
5755
+ await new Promise((resolve) => setTimeout(resolve, Math.min(1e3, remaining)));
5756
+ };
5757
+ let response;
5758
+ let value;
5759
+ let status;
5760
+ for (;;) {
5761
+ const remaining = deadline - Date.now();
5762
+ if (remaining <= 0) throw timedOut();
5763
+ const signal = AbortSignal.timeout(Math.min(12e4, remaining));
5764
+ try {
5765
+ response = await fetcher(`${apiUrl}/internal/platform/email-bootstrap`, {
5766
+ method: "POST",
5767
+ headers: {
5768
+ "X-Void-Platform-Probe": healthProbeToken,
5769
+ ...cfAccessHeaders(process.env, apiUrl)
5770
+ },
5771
+ signal,
5772
+ redirect: "manual"
5773
+ });
5774
+ } catch (error) {
5775
+ if (signal.aborted || error instanceof Error && error.name === "TimeoutError") {
5776
+ await waitForProgress();
5777
+ continue;
5778
+ }
5779
+ throw new Error(`platform: Shared email bootstrap request failed at ${apiUrl}. Traffic remains disabled. Retry the installation's repair or resume command to reconcile the existing email operation.`);
5780
+ }
5781
+ try {
5782
+ value = await response.json();
5783
+ } catch (error) {
5784
+ if (signal.aborted || error instanceof Error && error.name === "TimeoutError") {
5785
+ await waitForProgress();
5786
+ continue;
5787
+ }
5788
+ throw new Error(`platform: Shared email bootstrap failed at ${apiUrl} (HTTP ${response.status}). Traffic remains disabled.`);
5789
+ }
5790
+ status = value && typeof value === "object" && !Array.isArray(value) ? value : null;
5791
+ const observedOperationId = typeof status?.operationId === "string" && /^[A-Za-z0-9_-]{1,100}$/.test(status.operationId) ? status.operationId : null;
5792
+ if (status?.version === 1 && observedOperationId) {
5793
+ if (operationId && operationId !== observedOperationId) throw new Error("platform: Shared email bootstrap operation identity changed while waiting. Traffic remains disabled. Inspect the installation before retrying.");
5794
+ operationId = observedOperationId;
5795
+ }
5796
+ if ([
5797
+ 200,
5798
+ 202,
5799
+ 503
5800
+ ].includes(response.status) && status?.version === 1 && status.state === "incomplete" && observedOperationId && (status.operationStatus === "pending" || status.operationStatus === "running") && status.failureCode == null) {
5801
+ await waitForProgress();
5802
+ continue;
5803
+ }
5804
+ break;
5805
+ }
5806
+ if (!response.ok || status?.version !== 1 || status.state !== "ready" || operationId === null || status.operationId !== operationId || status.operationStatus != null && status.operationStatus !== "completed" || status.failureCode != null) {
5807
+ const code = (value) => typeof value === "string" && /^[a-z][a-z0-9_-]{0,63}$/.test(value) ? value : null;
5808
+ const operationStatus = code(status?.operationStatus);
5809
+ const operationId = typeof status?.operationId === "string" && /^[A-Za-z0-9_-]{1,100}$/.test(status.operationId) ? status.operationId : null;
5810
+ const details = operationId ? [`Operation ${operationId}: ${operationStatus ?? "incomplete"}.`] : operationStatus ? [`Operation status: ${operationStatus}.`] : [];
5811
+ const currentStep = code(status?.currentStep);
5812
+ const failureCode = code(status?.failureCode);
5813
+ if (currentStep) details.push(`Step: ${currentStep}.`);
5814
+ if (failureCode) details.push(`Failure: ${failureCode}.`);
5815
+ let domain = null;
5816
+ if (typeof status?.domain === "string") try {
5817
+ domain = normalizeApplicationDomain(status.domain);
5818
+ } catch {}
5819
+ const inboundStatus = code(status?.inboundStatus);
5820
+ if (domain) details.push(`Domain: ${domain}${inboundStatus ? ` (inbound: ${inboundStatus})` : ""}.`);
5821
+ const mxDetail = domain ? `MX records for ${domain} do not route mail to Cloudflare Email Routing` : null;
5822
+ const safeDetails = [
5823
+ ...domain ? [mxDetail, `enabled explicit Cloudflare email rules conflict with ${domain}`] : [],
5824
+ "zone catch-all does not point to this ingress",
5825
+ "another enabled catch-all owns this zone"
5826
+ ];
5827
+ if (typeof status?.failureDetail === "string" && safeDetails.includes(status.failureDetail)) {
5828
+ details.push(`${status.failureDetail}.`);
5829
+ if (currentStep === "inbound_probe" && status.failureDetail === mxDetail) details.push(`Enable Email Routing for ${domain} in the Cloudflare dashboard, then resume this installation.`);
5830
+ }
5831
+ const detail = details.length ? ` ${details.join(" ")}` : "";
5743
5832
  throw new Error(`platform: Shared email bootstrap did not reach inbound readiness at ${apiUrl} (HTTP ${response.status}).${detail} Traffic remains disabled.`);
5744
5833
  }
5745
- return status.operationId;
5834
+ return operationId;
5746
5835
  }
5747
5836
  function createPlatformHealthProbeToken(ownershipNonce) {
5748
5837
  return createHash("sha256").update("void-platform-health-probe:v1\0").update(ownershipNonce).digest("base64url");
@@ -5753,7 +5842,7 @@ function answer(value) {
5753
5842
  if (isCancel(value) || typeof value === "symbol") throw new Error("platform: Cancelled.");
5754
5843
  return value;
5755
5844
  }
5756
- async function selectInstallationAuthentication(file, recorded) {
5845
+ async function selectInstallationAuthentication(file, recorded, choices = {}) {
5757
5846
  if (file) {
5758
5847
  const input = JSON.parse(await readFile(file, "utf8"));
5759
5848
  if (!input || typeof input !== "object" || Array.isArray(input) || !("connections" in input) || !Array.isArray(input.connections) || !input.connections.length || input.connections.length > 32) throw new Error("platform: An authentication file must define between 1 and 32 connections.");
@@ -5772,9 +5861,12 @@ async function selectInstallationAuthentication(file, recorded) {
5772
5861
  file: selected
5773
5862
  };
5774
5863
  }
5775
- if (recorded) return { authentication: recorded };
5776
- if (!process.stdin.isTTY) return { authentication: { providers: ["github"] } };
5777
- const providers = answer(await multiselect({
5864
+ if (recorded) {
5865
+ if (choices.loginMethods && JSON.stringify(choices.loginMethods) !== JSON.stringify(recorded.providers) || choices.accessProtection !== void 0 && choices.accessProtection !== !!recorded.accessProtection) throw new Error("platform: Resume with the recorded login methods and protection settings.");
5866
+ return { authentication: recorded };
5867
+ }
5868
+ if (!process.stdin.isTTY && !choices.loginMethods && choices.accessProtection === void 0) return { authentication: { providers: ["github"] } };
5869
+ const providers = choices.loginMethods ?? (!process.stdin.isTTY ? ["github"] : answer(await multiselect({
5778
5870
  message: "Platform login methods:",
5779
5871
  initialValues: ["github"],
5780
5872
  required: true,
@@ -5796,11 +5888,11 @@ async function selectInstallationAuthentication(file, recorded) {
5796
5888
  label: "Cloudflare Access login"
5797
5889
  }
5798
5890
  ]
5799
- }));
5800
- const protection = answer(await confirm({
5891
+ })));
5892
+ const protection = choices.accessProtection ?? (!process.stdin.isTTY ? false : answer(await confirm({
5801
5893
  message: "Also protect the platform UI and APIs with Cloudflare Access?",
5802
5894
  initialValue: false
5803
- }));
5895
+ })));
5804
5896
  return { authentication: {
5805
5897
  providers,
5806
5898
  ...protection || providers.length !== 1 || providers[0] !== "github" ? { configurable: true } : {},
@@ -5837,6 +5929,7 @@ async function collectInstallationAuthentication(selection, plan, checkpoint, sa
5837
5929
  const accessEntry = entries.find((entry) => entry.configuration.kind === "cloudflare-access");
5838
5930
  const prepared = access && (selection.authentication.accessProtection || accessEntry && (!selection.file || selection.file.cloudflareAccess)) ? await preparePlatformAccessSetup({
5839
5931
  plan,
5932
+ ...platformDashboardUrl(plan.deployment) ? { additionalOrigins: [platformDashboardUrl(plan.deployment)] } : {},
5840
5933
  ownershipNonce: access.ownershipNonce,
5841
5934
  protection: !!selection.authentication.accessProtection,
5842
5935
  ...accessEntry ? { login: {
@@ -6044,13 +6137,13 @@ function platformSetupFieldHelp(key) {
6044
6137
  switch (key) {
6045
6138
  case "databaseUrl": return {
6046
6139
  placeholder: "postgresql://user:pw@…/database",
6047
- description: "Stores platform users, projects, and deployments; use an empty hosted database, e.g. " + platformSetupLink({
6140
+ description: "Stores platform users, projects, and deployments. Use an empty hosted database, e.g. " + platformSetupLink({
6048
6141
  title: "Neon",
6049
6142
  url: "https://neon.com/"
6050
6143
  }) + ". " + docs("prerequisites", "platform-database", "Database setup")
6051
6144
  };
6052
- case "jwtSecret": return { description: "Signs platform login tokens. Run openssl rand -base64 32; save the key in your password manager. " + docs("credentials", "signing-and-encryption-keys", "Key setup") };
6053
- case "projectSecretKey": return { description: "Encrypts app secrets. Run openssl rand -base64 32 separately; save this key for recovery. " + docs("credentials", "signing-and-encryption-keys", "Key setup") };
6145
+ case "jwtSecret": return { description: "Signs platform login tokens. Run openssl rand -base64 32. Save the key in your password manager. " + docs("credentials", "signing-and-encryption-keys", "Key setup") };
6146
+ case "projectSecretKey": return { description: "Encrypts app secrets. Run openssl rand -base64 32 separately. Save this key for recovery. " + docs("credentials", "signing-and-encryption-keys", "Key setup") };
6054
6147
  default: return;
6055
6148
  }
6056
6149
  }
@@ -6168,8 +6261,8 @@ function platformSetupPages(plan) {
6168
6261
  type: "account"
6169
6262
  }),
6170
6263
  instructions: [
6171
- "Select Object Read & Write (the form defaults to read-only).",
6172
- "Keep Apply to all buckets in this account (including newly created buckets) selected.",
6264
+ "Select Object Read & Write.",
6265
+ "Keep Apply to all buckets in this account selected.",
6173
6266
  "Create the token, then copy the Access Key ID and Secret Access Key into the CLI."
6174
6267
  ].join("\n")
6175
6268
  }
@@ -6178,16 +6271,20 @@ function platformSetupPages(plan) {
6178
6271
  function formatPlatformSetupPlan(plan) {
6179
6272
  const pages = platformSetupPages(plan);
6180
6273
  return [
6181
- "Credentials needed (paste them into the installer; keep a password-manager copy):",
6274
+ "Credentials needed during installation:",
6275
+ "PostgreSQL database URL",
6182
6276
  platformSetupLink(pages.runtime),
6183
- pages.runtime.instructions,
6184
- "",
6185
6277
  platformSetupLink(pages.r2),
6186
- pages.r2.instructions,
6278
+ ...plan.deployment.authentication.providers.includes("github") ? [platformSetupLink(pages.github)] : [],
6279
+ ...plan.deployment.authentication.providers.filter((provider) => provider !== "github").map((provider) => (provider === "google" ? "Google OAuth" : provider === "oidc" ? "OIDC" : "Cloudflare Access OIDC") + " client ID and secret"),
6280
+ ...plan.deployment.authentication.accessProtection ? ["Cloudflare Access application and installer credentials"] : [],
6281
+ platformSetupLink({
6282
+ title: "JWT signing key and project encryption key",
6283
+ url: "https://void.cloud/guide/platform/installation/credentials#signing-and-encryption-keys"
6284
+ }),
6187
6285
  "",
6188
- ...plan.deployment.authentication.providers.includes("github") ? [platformSetupLink(pages.github), pages.github.instructions] : [],
6189
- ...plan.deployment.authentication.providers.filter((provider) => provider !== "github").map((provider) => `${provider === "google" ? "Google OAuth client" : provider === "oidc" ? "OIDC connection" : "Cloudflare Access OIDC application"}: client ID and secret; callback ${plan.apiUrl}/auth/callback.`),
6190
- ...plan.deployment.authentication.accessProtection ? ["Cloudflare Access protection: configure API and proxy origins and admitted installer/CI credentials."] : []
6286
+ "Login callback: " + plan.apiUrl + "/auth/callback",
6287
+ "Installation collects and validates missing credentials after you confirm the plan."
6191
6288
  ].join("\n");
6192
6289
  }
6193
6290
  function formatPlatformInstallCommand(options) {
@@ -6215,10 +6312,18 @@ function formatPlatformInstallCommand(options) {
6215
6312
  flag("--application-domain", options.applicationDomain);
6216
6313
  flag("--zone", options.zoneName);
6217
6314
  if (options.dedicatedZone) parts.push("--dedicated-zone");
6315
+ else parts.push("--no-dedicated-zone");
6218
6316
  }
6219
6317
  flag("--control-plane-domain", options.controlPlaneDomain);
6318
+ if (!options.controlPlaneDomain) parts.push("--no-control-plane-domain");
6220
6319
  }
6320
+ flag("--hyperdrive", options.hyperdrive ?? "");
6221
6321
  flag("--auth-config", options.authConfig ?? "");
6322
+ if (!options.authConfig && !options.resumeId && options.authentication) {
6323
+ flag("--login-methods", options.authentication.providers.join(","));
6324
+ parts.push(options.authentication.accessProtection ? "--access-protection" : "--no-access-protection");
6325
+ }
6326
+ flag("--dashboard-url", options.dashboardUrl ?? "");
6222
6327
  flag("--runtime", options.runtime);
6223
6328
  if (options.yes) parts.push("--yes");
6224
6329
  return parts.join(" ");
@@ -6278,14 +6383,14 @@ function resolvePlatformInstallDatabaseUrl(checkpoint, manifest = null) {
6278
6383
  }
6279
6384
  async function runPlatformLifecycleCommand(args) {
6280
6385
  if (args.subcommand === "domain") {
6281
- const { runPlatformDomainCommand } = await import("./platform-domain-D6Xcy9ZX.mjs");
6386
+ const { runPlatformDomainCommand } = await import("./platform-domain-BNkcz0OB.mjs");
6282
6387
  await runPlatformDomainCommand(args);
6283
6388
  return;
6284
6389
  }
6285
6390
  if (args.subcommand === "discover") {
6286
6391
  const client = new PlatformCloudflareClient((await resolvePlatformCloudflareCredential()).token);
6287
6392
  const accountId = await selectAccount(await readAccounts(client), args.account);
6288
- const { recoverPlatformInstallations } = await import("./platform-recovery-BvtcXON5.mjs");
6393
+ const { recoverPlatformInstallations } = await import("./platform-recovery-D6KSpuFm.mjs");
6289
6394
  const recovered = await recoverPlatformInstallations(client, accountId, void 0, args.selector);
6290
6395
  if (recovered.length === 0) {
6291
6396
  log.info(`No recoverable Void platform installations were found in ${accountId}.`);
@@ -6295,7 +6400,7 @@ async function runPlatformLifecycleCommand(args) {
6295
6400
  return;
6296
6401
  }
6297
6402
  if (args.subcommand !== "install") {
6298
- const { runInstalledPlatformCommand } = await import("./platform-management-DOtss0BN.mjs");
6403
+ const { runInstalledPlatformCommand } = await import("./platform-management-CnyTdcWX.mjs");
6299
6404
  await runInstalledPlatformCommand(args);
6300
6405
  return;
6301
6406
  }
@@ -6410,9 +6515,9 @@ async function runPlatformLifecycleCommand(args) {
6410
6515
  const applicationDomain = recorded?.application.baseDomain ?? (workersDev ? "" : normalizeApplicationDomain(args.applicationDomain || await promptText("Application base domain:", "example.app")));
6411
6516
  const zoneName = recorded?.application.zoneName ?? (workersDev ? "" : normalizeApplicationDomain(args.zone || await promptText("Cloudflare zone:", applicationDomain)));
6412
6517
  const displayName = recorded?.displayName ?? (args.displayName || await promptText("Platform display name:", `${installationSlug} Void`));
6413
- const controlPlaneDomain = recorded ? recorded.controlPlane.apiHostname ?? "" : args.controlPlaneDomain || (process.stdin.isTTY ? await promptText("Optional custom API hostname (leave empty for workers.dev):", "", true) : "");
6518
+ const controlPlaneDomain = recorded ? recorded.controlPlane.apiHostname ?? "" : args.controlPlaneDomain || (process.stdin.isTTY && !args.noControlPlaneDomain ? await promptText("Custom API hostname. Leave empty for workers.dev:", "", true) : "");
6414
6519
  let dedicatedZone = recorded?.application.dedicatedZone ?? args.dedicatedZone;
6415
- if (!workersDev && !recorded && !args.dedicatedZone && applicationDomain === zoneName && process.stdin.isTTY) {
6520
+ if (!workersDev && !recorded && !args.dedicatedZone && !args.dedicatedZoneSpecified && applicationDomain === zoneName && process.stdin.isTTY) {
6416
6521
  const catchAll = await confirm({
6417
6522
  message: `Dedicate all unmatched ${zoneName} traffic to Void apps?`,
6418
6523
  initialValue: false
@@ -6454,11 +6559,12 @@ async function runPlatformLifecycleCommand(args) {
6454
6559
  host: externalHyperdriveHost,
6455
6560
  user: externalHyperdriveUser
6456
6561
  } : void 0;
6457
- if (!externalHyperdrive && process.stdin.isTTY && (!existing || isPlatformSetupDraft(existing)) && !existing?.resources.some((resource) => resource.key === "hyperdrive")) {
6562
+ if (args.hyperdrive && externalHyperdrive && args.hyperdrive !== externalHyperdrive.id) throw new Error("platform: --hyperdrive conflicts with VOID_PLATFORM_HYPERDRIVE_ID.");
6563
+ if (!externalHyperdrive && args.hyperdrive !== "create" && (process.stdin.isTTY || args.hyperdrive) && (!existing || isPlatformSetupDraft(existing)) && !existing?.resources.some((resource) => resource.key === "hyperdrive")) {
6458
6564
  const suggestedName = existing?.deployment.resources.hyperdrive ?? createPlatformResourceNames(installationSlug).hyperdrive;
6459
6565
  const configurations = await client.listHyperdriveConfigs(accountId);
6460
6566
  const suggestedNameAvailable = !configurations.some((resource) => resource.name === suggestedName);
6461
- const choice = await select({
6567
+ const choice = args.hyperdrive ? "existing" : await select({
6462
6568
  message: "How should Void set up Hyperdrive?",
6463
6569
  options: [
6464
6570
  ...configurations.length > 0 ? [{
@@ -6478,12 +6584,12 @@ async function runPlatformLifecycleCommand(args) {
6478
6584
  });
6479
6585
  if (isCancel(choice)) throw new Error("platform: Cancelled.");
6480
6586
  if (choice === "setup") {
6481
- note(`Create a Hyperdrive in account ${accountId} with any unused name${suggestedNameAvailable ? ` (for example ${suggestedName})` : ""}. Point it at the same dedicated PostgreSQL database you will give Void, use a runtime database user, and disable SQL result caching. If your organization manages Hyperdrive, ask its administrator to create it. Then rerun this command and select it from the list.\nSetup: https://developers.cloudflare.com/hyperdrive/get-started/`, "Set up Hyperdrive first");
6587
+ note(`Create a Hyperdrive in account ${accountId} with any unused name${suggestedNameAvailable ? ` such as ${suggestedName}` : ""}. Point it at the same dedicated PostgreSQL database you will give Void, use a runtime database user, and disable SQL result caching. If your organization manages Hyperdrive, ask its administrator to create it. Then rerun this command and select it from the list.\nSetup: https://developers.cloudflare.com/hyperdrive/get-started/`, "Set up Hyperdrive first");
6482
6588
  outro("No Cloudflare or local installation state was changed.");
6483
6589
  return;
6484
6590
  }
6485
6591
  if (choice === "existing") {
6486
- const selectedId = await select({
6592
+ const selectedId = args.hyperdrive ?? await select({
6487
6593
  message: "Which Hyperdrive should Void use?",
6488
6594
  options: [...configurations].sort((a, b) => Number(b.name === suggestedName) - Number(a.name === suggestedName) || a.name.localeCompare(b.name) || a.id.localeCompare(b.id)).map((resource) => ({
6489
6595
  value: resource.id,
@@ -6498,7 +6604,7 @@ async function runPlatformLifecycleCommand(args) {
6498
6604
  if (live.id !== selected.id || live.name !== selected.name || !live.origin.user) throw new Error(`platform: Could not verify the database user for existing Hyperdrive ${selected.name}.`);
6499
6605
  note(`Database: ${live.origin.database}\nHost: ${live.origin.host}\nPort: ${live.origin.port}\nUser: ${live.origin.user}\nSQL result caching: ${live.cachingDisabled ? "disabled" : "enabled"}`, `Existing Hyperdrive ${live.name}`);
6500
6606
  if (!live.cachingDisabled) throw new Error(`platform: Disable SQL result caching on existing Hyperdrive ${live.name} before installing.`);
6501
- const confirmed = await confirm({
6607
+ const confirmed = args.hyperdrive ? true : await confirm({
6502
6608
  message: `Use ${live.name} for this platform?`,
6503
6609
  initialValue: true
6504
6610
  });
@@ -6519,7 +6625,7 @@ async function runPlatformLifecycleCommand(args) {
6519
6625
  progress?.stop("Read the saved installation.");
6520
6626
  progress = void 0;
6521
6627
  if (authoritative) {
6522
- const { selectAuthoritativePlatformManifest } = await import("./platform-management-DOtss0BN.mjs");
6628
+ const { selectAuthoritativePlatformManifest } = await import("./platform-management-CnyTdcWX.mjs");
6523
6629
  const selected = selectAuthoritativePlatformManifest(existing, authoritative);
6524
6630
  authoritativeProvisionIntents = true;
6525
6631
  if (!args.plan && JSON.stringify(selected) !== JSON.stringify(existing)) writeInstallationManifest(selected);
@@ -6527,12 +6633,13 @@ async function runPlatformLifecycleCommand(args) {
6527
6633
  if (selectedInstall && !isPlatformInstallInProgress(existing)) throw new Error(`platform: Installation ${existing.id} is no longer unfinished. Run \`void platform status ${existing.id}\` to inspect it.`);
6528
6634
  }
6529
6635
  }
6530
- const authenticationSelection = await selectInstallationAuthentication(args.authConfig, existing?.deployment.authentication);
6636
+ const authenticationSelection = await selectInstallationAuthentication(args.authConfig, existing?.deployment.authentication, args);
6531
6637
  const planInput = {
6532
6638
  authentication: authenticationSelection.authentication,
6533
6639
  accountId,
6534
6640
  applicationDomain,
6535
6641
  controlPlaneDomain,
6642
+ ...args.dashboardUrl !== void 0 ? { dashboardUrl: args.dashboardUrl } : {},
6536
6643
  dedicatedZone,
6537
6644
  displayName,
6538
6645
  ...externalHyperdrive ? { externalHyperdrive } : {},
@@ -6546,13 +6653,15 @@ async function runPlatformLifecycleCommand(args) {
6546
6653
  authoritativeProvisionIntents,
6547
6654
  allowUnverifiedAiGateway: true,
6548
6655
  includeEmailGateway: runtime.manifest.artifacts.emailGateway !== void 0,
6549
- enableSandbox: runtime.manifest.safetyProtocols.sandboxDrain >= 3
6656
+ enableSandbox: runtime.manifest.safetyProtocols.sandboxDrain >= 3,
6657
+ deploymentExecution: runtime.manifest.deploymentExecution === 1
6550
6658
  });
6551
6659
  note(formatPlatformInstallPlan(plan), args.plan ? "Read-only installation plan" : "Installation plan");
6552
6660
  note(formatPlatformRuntime(runtime), "Platform runtime");
6553
6661
  if (args.plan) {
6554
6662
  console.log(formatPlatformSetupPlan(plan));
6555
- console.log(`\n${existing ? "To resume this installation later:" : "To install with these settings later:"}\n${formatPlatformInstallCommand({
6663
+ outro("Plan complete. No installation changes were made.");
6664
+ console.log(`\n${import_picocolors$1.default.bold(import_picocolors$1.default.cyan(existing ? "Next step: run this command to resume" : "Next step: run this command to install"))}\n\n${import_picocolors$1.default.cyan(formatPlatformInstallCommand({
6556
6665
  commandName: cliCommandName(),
6557
6666
  accountId,
6558
6667
  installationSlug,
@@ -6560,15 +6669,16 @@ async function runPlatformLifecycleCommand(args) {
6560
6669
  applicationDomain,
6561
6670
  zoneName,
6562
6671
  controlPlaneDomain,
6672
+ ...args.dashboardUrl !== void 0 ? { dashboardUrl: args.dashboardUrl } : {},
6563
6673
  dedicatedZone,
6564
6674
  workersDev,
6565
6675
  authConfig: args.authConfig,
6676
+ authentication: authenticationSelection.authentication,
6677
+ hyperdrive: !existing || isPlatformSetupDraft(existing) ? externalHyperdrive?.id ?? "create" : void 0,
6566
6678
  runtime: args.runtime,
6567
6679
  ...existing ? { resumeId: existing.id } : {},
6568
6680
  yes: args.yes || process.stdin.isTTY !== true
6569
- })}\n`);
6570
- if (!existing && !args.authConfig && process.stdin.isTTY) console.log("Choose the same login methods when prompted.\n");
6571
- outro("No Cloudflare or local installation state was changed.");
6681
+ }))}\n`);
6572
6682
  return;
6573
6683
  }
6574
6684
  assertPlatformOperatorCredentialCanApplyPlan(operatorCredential, plan);
@@ -6744,11 +6854,12 @@ async function runPlatformLifecycleCommand(args) {
6744
6854
  authoritativeProvisionIntents: true,
6745
6855
  repairMissing: true,
6746
6856
  includeEmailGateway: runtime.manifest.artifacts.emailGateway !== void 0,
6747
- enableSandbox: runtime.manifest.safetyProtocols.sandboxDrain >= 3
6857
+ enableSandbox: runtime.manifest.safetyProtocols.sandboxDrain >= 3,
6858
+ deploymentExecution: runtime.manifest.deploymentExecution === 1
6748
6859
  })).actions.filter((action) => action.kind === "worker"),
6749
6860
  revalidateBeforeWorkerDeploy: (manifest, action) => revalidatePlatformInstallAction(client, plan, manifest, action),
6750
6861
  verifyExistingOwnership: async (manifest, databaseCheckpointExists) => {
6751
- const { verifyPlatformInstallationOwnership } = await import("./platform-management-DOtss0BN.mjs");
6862
+ const { verifyPlatformInstallationOwnership } = await import("./platform-management-CnyTdcWX.mjs");
6752
6863
  const retainedReplacementKeys = new Set(plan.actions.filter((action) => {
6753
6864
  const previous = manifest.resources.find((resource) => resource.key === action.key);
6754
6865
  return action.action === "create" && previous && previous.name !== action.name;
@@ -6760,7 +6871,7 @@ async function runPlatformLifecycleCommand(args) {
6760
6871
  });
6761
6872
  }
6762
6873
  });
6763
- progress?.stop(result.waitingForDns || result.waitingForSandboxDrain ? "Installation paused; progress is saved." : "Platform installed.");
6874
+ progress?.stop(result.waitingForDns || result.waitingForSandboxDrain ? "Installation paused. Progress is saved." : "Platform installed.");
6764
6875
  progress = void 0;
6765
6876
  if (result.waitingForDns) {
6766
6877
  note(result.zoneNameServers.length > 0 ? `Update the domain at your registrar to use:\n${result.zoneNameServers.join("\n")}\n\nThen rerun: void platform install --resume --name ${result.manifest.id}` : `Wait for ${zoneName} to become active, then rerun:\nvoid platform install --resume --name ${result.manifest.id}`, "Waiting for nameservers");
@@ -6768,13 +6879,13 @@ async function runPlatformLifecycleCommand(args) {
6768
6879
  return;
6769
6880
  }
6770
6881
  if (result.waitingForSandboxDrain) {
6771
- note("The compatible candidate runtime is live in sandbox-drain maintenance mode and application traffic remains disabled. Preview bounded work with `void platform system sandbox-drain --plan`; follow any returned `nextCursor` with `--cursor '<value>'`. Then apply with `void platform system sandbox-drain --yes` and rerun until it reports DB-backed `complete: true`. Rerun this install command afterward to enable traffic.", "Waiting for managed Sandbox drain");
6882
+ note("The compatible candidate runtime is live in sandbox-drain maintenance mode and application traffic remains disabled. Preview bounded work with `void platform system sandbox-drain --plan`. Follow any returned `nextCursor` with `--cursor '<value>'`. Then apply with `void platform system sandbox-drain --yes` and rerun until it reports DB-backed `complete: true`. Rerun this install command afterward to enable traffic.", "Waiting for managed Sandbox drain");
6772
6883
  outro(`Installation ${result.manifest.id} is safely checkpointed.`);
6773
6884
  return;
6774
6885
  }
6775
6886
  log.success(`${result.manifest.deployment.displayName} is installed at ${new URL("/admin", plan.apiUrl).href}.`);
6776
6887
  log.info(`API URL: ${plan.apiUrl}`);
6777
- if (result.emailOperationId) log.info(`Shared email routing ready (operation ${result.emailOperationId}).`);
6888
+ if (result.emailOperationId) log.info(`Shared email routing ready. Operation: ${result.emailOperationId}.`);
6778
6889
  const completedAuthCode = readInstallationRecoverySecrets(result.manifest)?.authBootstrapCode;
6779
6890
  if (result.setupPending && completedAuthCode) {
6780
6891
  log.info(`First administrator setup: ${plan.apiUrl}/setup`);
@@ -6814,7 +6925,8 @@ async function verifyPlatformAiGatewayPlan(client, input, existing, preview, aut
6814
6925
  verified = await createPlatformInstallPlan(client, input, existing, {
6815
6926
  authoritativeProvisionIntents,
6816
6927
  includeEmailGateway: preview.actions.some((entry) => entry.key === "email-gateway-worker"),
6817
- enableSandbox: preview.deployment.capabilities.sandbox
6928
+ enableSandbox: preview.deployment.capabilities.sandbox,
6929
+ deploymentExecution: preview.actions.some((entry) => entry.key === "deployment-queue")
6818
6930
  });
6819
6931
  } catch (error) {
6820
6932
  if (error instanceof PlatformCloudflareApiError && (error.status === 401 || error.status === 403) && error.requestPath?.includes("/ai-gateway/")) throw new Error("platform: The runtime token could not verify AI Gateway access. Include Account → AI Gateway → Edit for this account, then retry. No platform resources were created.", { cause: error });