void 1.1.11 → 1.2.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 (233) hide show
  1. package/dist/{account-cmd-R35a4Oa8.mjs → account-cmd-CdUll_35.mjs} +5 -5
  2. package/dist/action-key-XZ9HkWAO.mjs +26 -0
  3. package/dist/{application-analysis-D7nNmNJb.mjs → application-analysis-fEqoSeMp.mjs} +11 -11
  4. package/dist/{application-routing-CZH66gXn.mjs → application-routing-DAhNP4u3.mjs} +1 -1
  5. package/dist/{ast-CPGPjb0V.mjs → ast-CsZ_q_gc.mjs} +6 -1
  6. package/dist/ast-bindings-C3yaPs1y.mjs +296 -0
  7. package/dist/{auth-link-VHmiKZmP.mjs → auth-link-C87vGXM-.mjs} +5 -5
  8. package/dist/{auth-router-O_Wrs5kg.mjs → auth-router-6I81r-ix.mjs} +3 -3
  9. package/dist/{better-auth-shared-Bv18bB-l.d.mts → better-auth-shared-DVk2DkTW.d.mts} +1 -1
  10. package/dist/{build-cmd-CMZiMn46.mjs → build-cmd-ex1g-j_e.mjs} +4 -4
  11. package/dist/{cache-TZ5Ab3d7.mjs → cache-oHzrnY3z.mjs} +4 -4
  12. package/dist/{cancel-deploy-DXoR6oC3.mjs → cancel-deploy-viAMD0Q_.mjs} +3 -3
  13. package/dist/cf-build-output-BJHuSsVN.mjs +1 -0
  14. package/dist/{cf-build-output-Bc8i3dzE.mjs → cf-build-output-BhVxldCw.mjs} +75 -10
  15. package/dist/{cf-user-agent-ChjQp2MU.mjs → cf-command-values-ByHk0xg4.mjs} +6 -16
  16. package/dist/cf-worker-settings-BECvvTVd.mjs +37 -0
  17. package/dist/cli/cli.mjs +139 -35
  18. package/dist/cli/cloudflare-operation-process.mjs +65 -18
  19. package/dist/cli/env-schema-probe.d.mts +1 -1
  20. package/dist/cli/env-schema-probe.mjs +3 -3
  21. package/dist/{client-GuSO3v_x.mjs → client-BEkdj1kx.mjs} +58 -13
  22. package/dist/client-BiRDZTSd.mjs +1 -0
  23. package/dist/{cloudflare-auth-DSvBc1Gu.mjs → cloudflare-auth-Byo_0_N9.mjs} +1 -1
  24. package/dist/{cloudflare-cmd-ZSErUl6Y.mjs → cloudflare-cmd-DMzCv12s.mjs} +5 -5
  25. package/dist/{cloudflare-config-rdFOys4X.mjs → cloudflare-config-DchHxPTo.mjs} +1 -1
  26. package/dist/{cloudflare-connect-BN-k2kD-.mjs → cloudflare-connect-CCRTVg-I.mjs} +3 -3
  27. package/dist/{cloudflare-operations-DH3GedbV.mjs → cloudflare-operations-Dh9dEHJj.mjs} +17 -7
  28. package/dist/{cloudflare-operations-Cm8lVx7W.mjs → cloudflare-operations-DsSaw2BT.mjs} +1 -1
  29. package/dist/{cloudflare-process-CZJ4viYl.mjs → cloudflare-process-8jEYIeim.mjs} +3 -2
  30. package/dist/{config-DC5llZaI.mjs → config-BIaXIkkv.mjs} +8 -3
  31. package/dist/{config-DYJV8aEH.mjs → config-BU6hMu1W.mjs} +1 -1
  32. package/dist/{config-D26zcdKy.mjs → config-DHgSEWT7.mjs} +39 -6
  33. package/dist/{config-7qoq2p9v.d.mts → config-DQfHOKh9.d.mts} +10 -1
  34. package/dist/config-entry.d.mts +1 -1
  35. package/dist/{connect-yQuLbuiJ.mjs → connect-k7R2rPVA.mjs} +6 -6
  36. package/dist/{create-project-CGOo_OL_.mjs → create-project-DTuy-l0z.mjs} +1 -1
  37. package/dist/{create-project-DEEWJX_M.mjs → create-project-DV71ol5d.mjs} +4 -4
  38. package/dist/{db-COI-yTJE.mjs → db-DVOkLH5X.mjs} +79 -23
  39. package/dist/{delete-PgN6L_9N.mjs → delete-BbP5eopu.mjs} +4 -4
  40. package/dist/{deploy-DsW-PrRQ.mjs → deploy-7H32EnUs.mjs} +220 -95
  41. package/dist/deploy-CE0Ev0Ob.mjs +2 -0
  42. package/dist/{deploy-managed-static-Dqz06S2u.mjs → deploy-managed-static-DlvcvnQT.mjs} +74 -28
  43. package/dist/{deploy-transport-nY3vLXPL.mjs → deploy-transport-BzGEj0Ge.mjs} +2 -2
  44. package/dist/deploy.d.mts +22 -1
  45. package/dist/deploy.mjs +49 -8
  46. package/dist/{dev-inbox-Rd6j_8vM.mjs → dev-inbox-D0jxye_D.mjs} +1 -1
  47. package/dist/{dispatch-BAbbn3jg.mjs → dispatch-DoUnxTNo.mjs} +15 -11
  48. package/dist/{dist-CfgIkieS.mjs → dist-B1PLt4_8.mjs} +1 -1
  49. package/dist/{dist-BVdsO7Py.mjs → dist-Cor9XGoz.mjs} +94 -200
  50. package/dist/{dist-Bn8Kodjp.mjs → dist-D15hFhP0.mjs} +1 -1
  51. package/dist/{domain-DKlNI0SR.mjs → domain-CA2wBgra.mjs} +5 -5
  52. package/dist/drizzle-9TubSZvI.mjs +1 -0
  53. package/dist/{email-BcjSyAL0.mjs → email-B9ayYQEG.mjs} +1 -1
  54. package/dist/{email-C0s3OvJk.mjs → email-DXpCc77w.mjs} +10 -10
  55. package/dist/{email-cloudflare-etpKiITL.mjs → email-cloudflare-D03g2TkS.mjs} +7 -7
  56. package/dist/{entry-B_0IhV64.mjs → entry-BEM8ZuGP.mjs} +40 -12
  57. package/dist/{env-BX2JFsgF.mjs → env-DgOQ9_8S.mjs} +6 -6
  58. package/dist/{env-public-CCdykygU.d.mts → env-public-6lqiSD2b.d.mts} +1 -0
  59. package/dist/{env-types-GRchm5dw.mjs → env-types-BicfKBhV.mjs} +1 -1
  60. package/dist/{env-validation-CuGfWRJd.mjs → env-validation-BmsdCZEk.mjs} +1 -1
  61. package/dist/{env-validation-D_KyMdab.mjs → env-validation-Dwq3PJzK.mjs} +3 -3
  62. package/dist/{fetch-DHLOuPyk.d.mts → fetch-CUX0tB78.d.mts} +14 -1
  63. package/dist/fetch-CtESUYWd.mjs +26 -0
  64. package/dist/{fetch-error-Dj3crt0e.mjs → fetch-error-Bw0n-IY-.mjs} +1 -1
  65. package/dist/{fetch-stream-PGgz4uGz.mjs → fetch-stream-BspOeTAI.mjs} +1 -1
  66. package/dist/{fetch-stream-CNxOrBdP.d.mts → fetch-stream-DfkAzZf-.d.mts} +1 -1
  67. package/dist/{gen-Bj6ZkOcI.mjs → gen-BfmcIi2g.mjs} +5 -5
  68. package/dist/gen-NrOlWZaG.mjs +1 -0
  69. package/dist/{github-cmd-DQDryS-5.mjs → github-cmd-CY9LUqLh.mjs} +4 -4
  70. package/dist/{handler-_PqY82xe.d.mts → handler-BbkSPzGF.d.mts} +91 -9
  71. package/dist/{headers-Bi4bOAwp.mjs → headers-D522zd0A.mjs} +2 -2
  72. package/dist/help-BeAFFE5C.mjs +1 -0
  73. package/dist/{help-D49SL_lN.mjs → help-CxSlPpzJ.mjs} +83 -12
  74. package/dist/{inbound-BuTfm11U.mjs → inbound-VrOLqwZY.mjs} +1 -1
  75. package/dist/index.d.mts +1 -1
  76. package/dist/index.mjs +125 -124
  77. package/dist/{info-Bxunp14X.mjs → info-7VDlhtyy.mjs} +4 -4
  78. package/dist/{init-DyTEY04s.mjs → init-DxYyOs4-.mjs} +103 -43
  79. package/dist/{link-CgOVn9EC.mjs → link-Ddmod1dZ.mjs} +5 -5
  80. package/dist/{list-CUfD9RSb.mjs → list-MogaugiO.mjs} +5 -5
  81. package/dist/{live-DJONVlQK.d.mts → live-Bt8Y_0bg.d.mts} +1 -1
  82. package/dist/{local-d1-CpaPbd_L.mjs → local-d1-BHBnKWRb.mjs} +1 -1
  83. package/dist/{log-vqY3pqwI.mjs → log-BMwmN5Ae.mjs} +2 -2
  84. package/dist/{login-BJvGW5Eu.mjs → login-Cttnagmm.mjs} +4 -4
  85. package/dist/login-DgDwlKc_.mjs +1 -0
  86. package/dist/{logs-DeizBI4j.mjs → logs-CAszUWZI.mjs} +4 -4
  87. package/dist/{migrate-8rk56vV8.mjs → migrate-DyT2jJ8y.mjs} +5 -5
  88. package/dist/migrate-n49ZeS1X.mjs +1 -0
  89. package/dist/{mime-srOklVVv.mjs → mime-BAyHPskU.mjs} +1 -1
  90. package/dist/nitro.mjs +3 -3
  91. package/dist/{node-uN8_zxJr.mjs → node-WB7c2N4L.mjs} +2 -2
  92. package/dist/operator-cmd-d2MvJOrl.mjs +1 -0
  93. package/dist/{operator-cmd-CA5QQyvE.mjs → operator-cmd-skshjMGl.mjs} +4 -4
  94. package/dist/output-BW9uK099.mjs +1 -0
  95. package/dist/{output-Ba6ZbrzY.mjs → output-BpgYODYx.mjs} +16 -4
  96. package/dist/pages/client.d.mts +2 -2
  97. package/dist/pages/client.mjs +48 -25
  98. package/dist/pages/index.d.mts +2 -2
  99. package/dist/pages/index.mjs +4 -4
  100. package/dist/pages/islands-plugin.d.mts +43 -5
  101. package/dist/pages/islands-plugin.mjs +100 -28
  102. package/dist/pages/protocol.d.mts +2 -2
  103. package/dist/pages/protocol.mjs +1 -1
  104. package/dist/pages/serialize.d.mts +2 -2
  105. package/dist/pages/serialize.mjs +1 -1
  106. package/dist/{parse-filename-CUbj-1MP.mjs → parse-filename-DioPHiR9.mjs} +1 -35
  107. package/dist/{picocolors-BTps1_gs.mjs → picocolors-BsDfhxIw.mjs} +1 -1
  108. package/dist/{platform-args-B7ke06up.mjs → platform-args-DVbVClZl.mjs} +12 -4
  109. package/dist/platform-args-kQrgxScY.mjs +1 -0
  110. package/dist/{platform-auth-config-CZaY812e.mjs → platform-auth-config-C-pm5Qzx.mjs} +6 -6
  111. package/dist/{platform-auth-protection-U9HCqoXv.mjs → platform-auth-protection-SHhvim1L.mjs} +4 -4
  112. package/dist/{platform-auth-recovery-B0BV2hfV.mjs → platform-auth-recovery-DXVWs-wA.mjs} +4 -4
  113. package/dist/{platform-cmd-CXrjk4fb.mjs → platform-cmd-D5I1beUX.mjs} +7 -7
  114. package/dist/{platform-cmd-D9lwFpcl.mjs → platform-cmd-PiNRkopU.mjs} +1 -1
  115. package/dist/{platform-domain-CXyq-68_.mjs → platform-domain-jpmBfZeb.mjs} +4 -4
  116. package/dist/{platform-lifecycle-Dz2cJsLX.mjs → platform-lifecycle-DH3Yr1qQ.mjs} +1 -1
  117. package/dist/{platform-lifecycle-D0TdyXbN.mjs → platform-lifecycle-DyKmuNY6.mjs} +405 -44
  118. package/dist/{platform-management-CnXhgxTM.mjs → platform-management--vDwSn35.mjs} +49 -5
  119. package/dist/{platform-management-BvgbusqL.mjs → platform-management-7m-PLaPJ.mjs} +1 -1
  120. package/dist/{platform-plans-config-BWn1S2Rv.mjs → platform-plans-config-CeydDSj0.mjs} +2 -2
  121. package/dist/{platform-recovery-CH_fThW6.mjs → platform-recovery-BiNpZZab.mjs} +2 -2
  122. package/dist/{platform-setup-YvN9VQXV.mjs → platform-setup-Ca53gtt0.mjs} +3 -2
  123. package/dist/{plugin-inference-Bekzjuqw.mjs → plugin-inference-DUjCd55H.mjs} +145 -218
  124. package/dist/{prepare-2XD6kh77.mjs → prepare-BcAE5Sdj.mjs} +3 -3
  125. package/dist/prepare-C4I0f--h.mjs +1 -0
  126. package/dist/{prepare-DepAGBcV.mjs → prepare-DA-17DhO.mjs} +5 -5
  127. package/dist/{prerender-render-Bfsumdkv.mjs → prerender-render-Cijv7UFo.mjs} +11 -6
  128. package/dist/prerender-render.mjs +1 -1
  129. package/dist/{project-cmd-CpLYflCH.mjs → project-cmd-DA4K5faU.mjs} +18 -18
  130. package/dist/{project-team-CTSY7W7Q.mjs → project-team-CQBNveX3.mjs} +4 -4
  131. package/dist/{project-token-Cybtlfbl.mjs → project-token-CCalFyQf.mjs} +4 -4
  132. package/dist/{project-zero-trust-DKOtqyvu.mjs → project-zero-trust-DPnkiEuT.mjs} +4 -4
  133. package/dist/{protocol-B8sI8dRD.d.mts → protocol-BBDpJz3G.d.mts} +3 -1
  134. package/dist/{protocol-RNP0u7UY.mjs → protocol-DimNxVRr.mjs} +42 -17
  135. package/dist/{provision-UcYjpTRh.mjs → provision-BFLCrnUp.mjs} +1 -1
  136. package/dist/{provision-BuifYvLc.mjs → provision-DtROpNNI.mjs} +6 -5
  137. package/dist/{requests-Ryiazz9i.mjs → requests-BpH0kImL.mjs} +3 -3
  138. package/dist/resolve-project-DD1vu8s9.mjs +1 -0
  139. package/dist/{resolve-project-WpwkCB_c.mjs → resolve-project-Xoi6eTWY.mjs} +1 -1
  140. package/dist/{rollback-Or0EV3rP.mjs → rollback-CXS1tAa-.mjs} +10 -9
  141. package/dist/{rolldown-runtime-DXIUcv95.mjs → rolldown-runtime-3h0WiCiA.mjs} +1 -1
  142. package/dist/{routing-assets-VkrKIbxE.mjs → routing-assets-DdSFxoLp.mjs} +10 -4
  143. package/dist/runtime/better-auth-mysql.d.mts +1 -1
  144. package/dist/runtime/better-auth-pg.d.mts +1 -1
  145. package/dist/runtime/better-auth.d.mts +1 -1
  146. package/dist/runtime/client-react.d.mts +2 -2
  147. package/dist/runtime/client-react.mjs +3 -3
  148. package/dist/runtime/client-solid.d.mts +2 -2
  149. package/dist/runtime/client-solid.mjs +3 -3
  150. package/dist/runtime/client-svelte.d.mts +2 -2
  151. package/dist/runtime/client-svelte.mjs +3 -3
  152. package/dist/runtime/client-vue.d.mts +2 -2
  153. package/dist/runtime/client-vue.mjs +3 -3
  154. package/dist/runtime/client.d.mts +2 -2
  155. package/dist/runtime/client.mjs +3 -3
  156. package/dist/runtime/email/testing.mjs +4 -4
  157. package/dist/runtime/email.mjs +3 -3
  158. package/dist/runtime/env-public.d.mts +1 -1
  159. package/dist/runtime/fetch-server.d.mts +8 -0
  160. package/dist/runtime/fetch-server.mjs +30 -0
  161. package/dist/runtime/fetch-stream.d.mts +2 -2
  162. package/dist/runtime/fetch-stream.mjs +2 -2
  163. package/dist/runtime/fetch.d.mts +2 -2
  164. package/dist/runtime/fetch.mjs +3 -3
  165. package/dist/runtime/handler.d.mts +1 -1
  166. package/dist/runtime/live-client.d.mts +1 -1
  167. package/dist/runtime/live.d.mts +1 -1
  168. package/dist/runtime/routing.d.mts +17 -76
  169. package/dist/runtime/routing.mjs +277 -216
  170. package/dist/runtime/validator.d.mts +1 -1
  171. package/dist/runtime/ws-server.d.mts +2 -2
  172. package/dist/runtime/ws.d.mts +1 -1
  173. package/dist/{scan-D2-FcLHs.mjs → scan-8IzYXiU_.mjs} +12 -11
  174. package/dist/{secret-CClHaFxm.mjs → secret-BbXHfLAt.mjs} +6 -6
  175. package/dist/serialize-DtR8Vwa_.d.mts +18 -0
  176. package/dist/serialize-YHvlcG3Q.mjs +31 -0
  177. package/dist/{skills-C_yRTg0g.mjs → skills-CrUO3PJ0.mjs} +1 -1
  178. package/dist/{subcommand-prompt-Bm3ZlYgA.mjs → subcommand-prompt-D9OhhbGG.mjs} +2 -2
  179. package/dist/{version-metadata-B1Bm2wSq.mjs → version-metadata-tS39iW09.mjs} +1 -1
  180. package/dist/wrangler-BLb3_luP.mjs +1 -0
  181. package/dist/{wrangler-C9XSzrju.mjs → wrangler-F96NnLyb.mjs} +13 -5
  182. package/dist/{wrapper-BOigE1xJ.mjs → wrapper-D3sZyh2C.mjs} +5 -3
  183. package/dist/{ws-CsBNxv86.d.mts → ws-BH-im_tX.d.mts} +1 -1
  184. package/docs/guide/ai.md +1 -1
  185. package/docs/guide/database/postgresql.md +1 -1
  186. package/docs/guide/database.md +26 -0
  187. package/docs/guide/deployment.md +2 -0
  188. package/docs/guide/edge/headers.md +1 -1
  189. package/docs/guide/email/domains.md +1 -1
  190. package/docs/guide/email/receiving.md +1 -1
  191. package/docs/guide/email/sending.md +3 -3
  192. package/docs/guide/pages-routing/actions-and-forms.md +1 -1
  193. package/docs/guide/pages-routing/markdown.md +3 -1
  194. package/docs/guide/platform/administration/email.md +7 -1
  195. package/docs/guide/platform/installation/ci.md +2 -0
  196. package/docs/guide/platform/installation/credentials.md +24 -6
  197. package/docs/guide/platform/installation/domains.md +16 -0
  198. package/docs/guide/platform/installation/maintenance.md +32 -2
  199. package/docs/guide/platform/installation/setup.md +1 -1
  200. package/docs/guide/self-hosted-platform.md +1 -1
  201. package/docs/guide/server-routing.md +1 -1
  202. package/docs/guide/typed-fetch.md +1 -1
  203. package/docs/integrations/cloudflare.md +2 -1
  204. package/docs/integrations/frameworks/tanstack-start.md +25 -0
  205. package/docs/reference/api/deploy.md +35 -3
  206. package/docs/reference/api/env.md +1 -1
  207. package/docs/reference/api/rewrites.md +1 -1
  208. package/docs/reference/api/types.md +1 -0
  209. package/docs/reference/cli/database.md +15 -0
  210. package/docs/reference/cli/platform-installation.md +5 -2
  211. package/docs/reference/cli/project.md +4 -2
  212. package/docs/reference/cli/setup.md +17 -0
  213. package/docs/reference/cli.md +1 -0
  214. package/docs/reference/config.md +14 -1
  215. package/docs/reference/resource-inference.md +4 -0
  216. package/package.json +23 -22
  217. package/sandbox.Dockerfile +1 -1
  218. package/schema.json +23 -3
  219. package/dist/cf-build-output-BU3hqdHu.mjs +0 -1
  220. package/dist/client-DrdeJgHR.mjs +0 -1
  221. package/dist/deploy-CxNgEYr4.mjs +0 -2
  222. package/dist/fetch-sAcwjzLO.mjs +0 -18
  223. package/dist/gen-DsCukhuw.mjs +0 -1
  224. package/dist/help-DOFygYi4.mjs +0 -1
  225. package/dist/login-alAlxw4S.mjs +0 -1
  226. package/dist/migrate-D2odrAmh.mjs +0 -1
  227. package/dist/operator-cmd-D7toMPJ9.mjs +0 -1
  228. package/dist/platform-args-B_LrqEZB.mjs +0 -1
  229. package/dist/prepare-EVEbQ9n2.mjs +0 -1
  230. package/dist/resolve-project-PnhzMtY6.mjs +0 -1
  231. package/dist/serialize-BtQyaYoC.mjs +0 -20
  232. package/dist/serialize-CfSwWfF2.d.mts +0 -10
  233. package/dist/wrangler-DAdxHEzh.mjs +0 -1
@@ -3,13 +3,13 @@ import { t as writeFileAtomic } from "./atomic-write-Dq6R0BPN.mjs";
3
3
  import { S as writePendingInstallationSecrets, a as latestPlatformWorkerCutoverGeneration, c as normalizePlatformApiUrl, d as platformRegistryDirectory, f as platformWorkerSecretUpdateIntentState, h as readPendingInstallationSecrets, m as readInstallationRecoverySecrets, n as assertPlatformInstallationSlugAvailable, o as listInstallationManifests, p as promoteInstallationSecrets, t as acquirePlatformLifecycleLock, v as resolveInstallationManifest, x as writeInstallationManifest, y as savePlatformConnection } from "./platform-registry-B9mWX9LO.mjs";
4
4
  import { t as cfAccessHeaders } from "./cf-access-BM9G0lhO.mjs";
5
5
  import { t as ensurePlatformAccessToken } from "./cf-access-CWP4avfP.mjs";
6
- import { A as confirm, H as outro, K as select, L as log, U as password, X as text$1, tt as isCancel, z as multiselect } from "./dist-BVdsO7Py.mjs";
7
- import { f as import_picocolors, l as formatUrl, m as printSuccess, n as cliCommandName, p as note, r as createSpinner, u as getErrorMessage } from "./output-Ba6ZbrzY.mjs";
8
- import { l as resolveCloudflareOperationArgv } from "./cloudflare-process-CZJ4viYl.mjs";
9
- import { o as createDefaultRunWrangler, v as resolvePinnedApiBaseUrl } from "./provision-BuifYvLc.mjs";
10
- import { t as parsePlatformArgs } from "./platform-args-B7ke06up.mjs";
11
- import { o as runCloudflareAuthCommand } from "./cloudflare-auth-DSvBc1Gu.mjs";
12
- import { a as platformManagementSetupPage, c as platformSetupLink, i as platformInstallCommandArguments, l as platformSetupPages, n as formatPlatformSetupPlan, o as platformPromptAnswer, r as openPlatformSetupPage, s as platformSetupFieldHelp, t as formatPlatformInstallCommand } from "./platform-setup-YvN9VQXV.mjs";
6
+ import { A as confirm, H as outro, K as select, L as log, U as password, X as text$1, tt as isCancel, z as multiselect } from "./dist-Cor9XGoz.mjs";
7
+ import { f as import_picocolors, l as formatUrl, m as printSuccess, n as cliCommandName, p as note, r as createSpinner, u as getErrorMessage } from "./output-BpgYODYx.mjs";
8
+ import { l as resolveCloudflareOperationArgv } from "./cloudflare-process-8jEYIeim.mjs";
9
+ import { o as createDefaultRunWrangler, v as resolvePinnedApiBaseUrl } from "./provision-DtROpNNI.mjs";
10
+ import { t as parsePlatformArgs } from "./platform-args-DVbVClZl.mjs";
11
+ import { o as runCloudflareAuthCommand } from "./cloudflare-auth-Byo_0_N9.mjs";
12
+ import { a as platformManagementSetupPage, c as platformSetupLink, i as platformInstallCommandArguments, l as platformSetupPages, n as formatPlatformSetupPlan, o as platformPromptAnswer, r as openPlatformSetupPage, s as platformSetupFieldHelp, t as formatPlatformInstallCommand } from "./platform-setup-Ca53gtt0.mjs";
13
13
  import { copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
14
14
  import { fileURLToPath } from "node:url";
15
15
  import { isRecord } from "@void/deploy-core";
@@ -19,6 +19,7 @@ import { tmpdir } from "node:os";
19
19
  import { stripVTControlCharacters } from "node:util";
20
20
  import { cloudflareCommandDiagnostic, encodeCloudflareVersionMessage, parseCloudflareVersionMessage, readCloudflareTriggerConfig, readCloudflareVersionMigrationTag, stabilizeCloudflareVersionTriggers } from "@void/deploy-cloudflare";
21
21
  import { createServer, request } from "node:http";
22
+ import { setTimeout as setTimeout$1 } from "node:timers/promises";
22
23
  import { readFile } from "node:fs/promises";
23
24
  import { PLATFORM_ADMIN_ASSETS_DIRECTORY, platformDatabaseSchemaDigest, resolvePlatformRuntime } from "@void/platform/installer";
24
25
  import { request as request$1 } from "node:https";
@@ -45,12 +46,29 @@ var PlatformWorkerRoutesChangedError = class extends Error {
45
46
  this.name = "PlatformWorkerRoutesChangedError";
46
47
  }
47
48
  };
49
+ /**
50
+ * The runtime token cannot read the shared email zone. The operator can add
51
+ * the permission to the same token, so callers keep its saved value.
52
+ */
53
+ var PlatformRuntimeTokenEmailPermissionError = class extends Error {
54
+ /** Cloudflare answered HTTP 401 at least once: it did not accept the token. */
55
+ tokenNotValid;
56
+ constructor(message, tokenNotValid, options) {
57
+ super(message, options);
58
+ this.name = "PlatformRuntimeTokenEmailPermissionError";
59
+ this.tokenNotValid = tokenNotValid;
60
+ }
61
+ };
48
62
  async function verifyPlatformRuntimeTokenPermissions(client, input) {
49
63
  try {
50
64
  await client.listHyperdriveConfigs(input.accountId);
51
65
  } catch (error) {
52
66
  throw runtimeTokenPreflightError("Account → Hyperdrive → Write", error);
53
67
  }
68
+ if (input.emailZoneId) await verifyPlatformRuntimeTokenEmailPermissions(client, {
69
+ accountId: input.accountId,
70
+ zoneId: input.emailZoneId
71
+ });
54
72
  if (!input.zoneId) return;
55
73
  const probeUrl = `https://${input.applicationDomain}/.well-known/void/runtime-token-preflight-${randomUUID()}`;
56
74
  try {
@@ -67,6 +85,37 @@ async function verifyPlatformRuntimeTokenPermissions(client, input) {
67
85
  throw runtimeTokenPreflightError("Zone → SSL and Certificates → Edit", error);
68
86
  }
69
87
  }
88
+ /**
89
+ * Shared email setup uses the runtime token after the platform pauses traffic.
90
+ * Read-only requests find a missing Email Routing permission before that. A
91
+ * read cannot prove Edit access. Email Sending is not checked, because its
92
+ * answer depends on the account plan.
93
+ */
94
+ async function verifyPlatformRuntimeTokenEmailPermissions(client, input) {
95
+ const zone = `/zones/${encodeURIComponent(input.zoneId)}`;
96
+ const probes = [
97
+ ["Zone → Zone → Read", zone],
98
+ ["Zone → Zone Settings → Edit", `${zone}/email/routing`],
99
+ ["Zone → Email Routing Rules → Edit", `${zone}/email/routing/rules?per_page=5`],
100
+ ["Account → Email Routing Addresses → Edit", `/accounts/${encodeURIComponent(input.accountId)}/email/routing/addresses?per_page=5`]
101
+ ];
102
+ const missing = [];
103
+ let denial;
104
+ let tokenNotValid = false;
105
+ for (const [permission, path] of probes) try {
106
+ await client.request(path);
107
+ } catch (error) {
108
+ if (error instanceof PlatformCloudflareApiError && (error.status === 401 || error.status === 403)) {
109
+ missing.push(permission);
110
+ denial ??= error;
111
+ tokenNotValid ||= error.status === 401;
112
+ }
113
+ }
114
+ if (denial) {
115
+ const denied = `Cloudflare denied: ${missing.join(", ")} (HTTP ${denial.status}).`;
116
+ throw new PlatformRuntimeTokenEmailPermissionError(input.savedToken ? `platform: The runtime API token saved on this machine cannot use platform email. ${denied} Void checked this token because VOID_PLATFORM_RUNTIME_CLOUDFLARE_API_TOKEN is not set. If the platform uses a different runtime token, set VOID_PLATFORM_RUNTIME_CLOUDFLARE_API_TOKEN to that token, and Void checks that token instead. Otherwise, check that the zone permissions of the saved token include the email zone ${input.zoneId}. To add a missing permission, edit the token in the Cloudflare dashboard. The token value stays the same. Then run the command again.` : `platform: The runtime API token cannot use platform email. ${denied} Check that the token is valid and that its zone permissions include the email zone ${input.zoneId}. To add a missing permission, edit the token in the Cloudflare dashboard. The token value stays the same. Or set VOID_PLATFORM_RUNTIME_CLOUDFLARE_API_TOKEN to a valid token that has these permissions. Then run the command again.`, tokenNotValid, { cause: denial });
117
+ }
118
+ }
70
119
  function runtimeTokenPreflightError(permission, cause) {
71
120
  const status = cause instanceof PlatformCloudflareApiError ? cause.status : null;
72
121
  const message = status === 401 || status === 403 ? `Cloudflare denied the runtime-token check (HTTP ${status}). Check that the token is valid, scoped to this account, and includes ${permission}.` : status === 429 ? `Cloudflare rate-limited the runtime-token check for ${permission} (HTTP 429). Retry shortly; this does not mean the token is missing a permission.` : `Could not complete the runtime-token check for ${permission}${status === null ? "" : ` (Cloudflare HTTP ${status})`}. Retry the check; this failure does not establish that a permission is missing.`;
@@ -535,6 +584,22 @@ var PlatformCloudflareClient = class PlatformCloudflareClient {
535
584
  for (const binding of settings.bindings ?? []) if (binding.type === "plain_text" && typeof binding.name === "string" && typeof binding.text === "string") bindings[binding.name] = binding.text;
536
585
  return bindings;
537
586
  }
587
+ /**
588
+ * Where the last change to a Worker came from, for example `cf_cli`, `dash`
589
+ * (Cloudflare dashboard) or `api` (Workers API). Null when the Worker does
590
+ * not exist or Cloudflare does not report it.
591
+ */
592
+ async getWorkerLastDeployedFrom(accountId, workerName) {
593
+ let service;
594
+ try {
595
+ service = await this.request(`/accounts/${encodeURIComponent(accountId)}/workers/services/${encodeURIComponent(workerName)}`);
596
+ } catch (error) {
597
+ if (error instanceof PlatformCloudflareApiError && error.status === 404) return null;
598
+ throw error;
599
+ }
600
+ const source = service?.default_environment?.script?.last_deployed_from;
601
+ return typeof source === "string" && source !== "" ? source : null;
602
+ }
538
603
  async deleteKvValue(accountId, namespaceId, key) {
539
604
  try {
540
605
  await this.request(`/accounts/${encodeURIComponent(accountId)}/storage/kv/namespaces/${encodeURIComponent(namespaceId)}/values/${encodeURIComponent(key)}`, { method: "DELETE" });
@@ -2340,8 +2405,10 @@ async function createPlatformInstallPlan(client, input, existing, options) {
2340
2405
  githubOAuth: input.authentication.providers.includes("github"),
2341
2406
  googleOAuth: input.authentication.providers.includes("google")
2342
2407
  } : {},
2343
- ...enableEmail ? { email: true } : {}
2408
+ ...enableEmail ? { email: true } : {},
2409
+ ...input.customDomains ? { customDomains: true } : {}
2344
2410
  };
2411
+ if (capabilities.customDomains && (testing || !input.dedicatedZone)) throw new Error("platform: Custom domains need a dedicated application zone. Install with a domain and --dedicated-zone.");
2345
2412
  if (capabilities.email && options?.includeEmailGateway !== false && !enableEmail) throw new Error("platform: Email-enabled installations require VOID_EMAIL_SENDER_DOMAIN and VOID_EMAIL_SHARED_ZONE_ID before deploying this runtime.");
2346
2413
  if (input.appAccess && input.appAccess !== "public" && input.controlPlaneDomain.toLowerCase().replace(/\.$/, "").endsWith(`.${input.applicationDomain.toLowerCase()}`)) throw new Error("platform: The API hostname must be outside the app Access wildcard. Use the application domain apex or workers.dev for the API, then plan again.");
2347
2414
  const deployment = createPlatformDeploymentManifest({
@@ -2384,13 +2451,20 @@ async function createPlatformInstallPlan(client, input, existing, options) {
2384
2451
  ...deployment.controlPlane.dashboardUrl ? { dashboardUrl: deployment.controlPlane.dashboardUrl } : {}
2385
2452
  }
2386
2453
  };
2387
- const comparableExistingWithoutEmailZone = existing && deployment.capabilities.email && !existing.deployment.capabilities.email ? {
2454
+ const existingWithCustomDomains = existing && deployment.capabilities.customDomains && !existing.deployment.capabilities.customDomains ? {
2388
2455
  ...existingWithDashboard,
2389
2456
  capabilities: {
2390
2457
  ...existingWithDashboard.capabilities,
2391
- email: true
2458
+ customDomains: true
2392
2459
  }
2393
2460
  } : existingWithDashboard;
2461
+ const comparableExistingWithoutEmailZone = existing && deployment.capabilities.email && !existing.deployment.capabilities.email ? {
2462
+ ...existingWithCustomDomains,
2463
+ capabilities: {
2464
+ ...existingWithCustomDomains.capabilities,
2465
+ email: true
2466
+ }
2467
+ } : existingWithCustomDomains;
2394
2468
  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.`);
2395
2469
  const names = deployment.resources;
2396
2470
  const wildcardHostname = `*.${deployment.application.baseDomain}`;
@@ -3645,8 +3719,30 @@ function createPlatformHealthProbeToken(ownershipNonce) {
3645
3719
  //#endregion
3646
3720
  //#region src/cli/platform-installer-errors.ts
3647
3721
  const ANALYTICS_ENGINE_NOT_ENABLED = 10089;
3722
+ const UPLOAD_CONFLICT = "Aborting the upload operation because of conflicts";
3723
+ /** Why the Cloudflare CLI stopped an upload, and what to do. Null for other failures. */
3724
+ function uploadConflictMessage(result, operation) {
3725
+ const output = stripVTControlCharacters(`${result.stdout}\n${result.stderr}`);
3726
+ if (!output.includes(UPLOAD_CONFLICT)) return null;
3727
+ const lines = output.split(/\r?\n/).map((line) => line.replace(/^[\s▲│]+/, "").trim());
3728
+ const outsideChange = lines.find((line) => line.includes("last updated via the script API") || line.includes("set via the Cloudflare Dashboard"));
3729
+ if (outsideChange) return `platform: The Cloudflare CLI did not ${operation}: ${outsideChange.replace(/:$/, ".")} This Worker was changed outside Void. Run the same command in a terminal. Void then lists the changes and asks you before it replaces them.`;
3730
+ const secretNames = [...(lines.find((line) => / with (?:an )?existing remote secrets?\./.test(line))?.split(" conflict")[0] ?? "").matchAll(/`[^`]+`/g)].map(([name]) => name);
3731
+ if (secretNames.length === 1) return `platform: The Cloudflare CLI did not ${operation}: the Worker has a secret with the same name as a value that Void sets: ${secretNames[0]}. Delete that secret from the Worker in the Cloudflare dashboard, then run the command again.`;
3732
+ if (secretNames.length > 1) return `platform: The Cloudflare CLI did not ${operation}: the Worker has secrets with the same names as values that Void sets: ${new Intl.ListFormat("en").format(secretNames)}. Delete those secrets from the Worker in the Cloudflare dashboard, then run the command again.`;
3733
+ const workflow = lines.findIndex((line) => line.includes("already exist and belong to different workers"));
3734
+ if (workflow !== -1) {
3735
+ const owners = lines.slice(workflow + 1);
3736
+ const end = owners.findIndex((line) => !line.startsWith("- "));
3737
+ const workflows = owners.slice(0, end === -1 ? void 0 : end);
3738
+ return `platform: The Cloudflare CLI did not ${operation}: ${[lines[workflow], ...workflows].join(" ")}. ${workflows.length > 1 ? "Other Workers own these workflows. Delete each workflow, or rename it in the configuration of the Worker that owns it." : "Another Worker owns this workflow. Delete the workflow, or rename it in the configuration of that Worker."} Then run the command again.`;
3739
+ }
3740
+ return `platform: The Cloudflare CLI did not ${operation}. It stopped the upload because of a conflict that it could not confirm without a terminal. Fix the conflict, then run the command again.`;
3741
+ }
3648
3742
  function assertCleanPlatformWrangler(result, operation) {
3649
3743
  if (result.status !== 0 || result.signal || result.error) {
3744
+ const conflict = uploadConflictMessage(result, operation);
3745
+ if (conflict) throw new Error(conflict);
3650
3746
  const diagnostic = cloudflareCommandDiagnostic({
3651
3747
  ...result,
3652
3748
  stderr: result.stderr.trim() || result.errors?.map((error) => error.message).join("\n") || ""
@@ -3675,6 +3771,36 @@ async function readActivePlatformWorkerVersion(configPath, accountId, workerName
3675
3771
  if (active.length !== 1 || typeof active[0].version_id !== "string") return null;
3676
3772
  return active[0].version_id;
3677
3773
  }
3774
+ /**
3775
+ * The active version in a Cloudflare API answer for the deployments of a
3776
+ * platform Worker. deployPlatformWorker continues only when the latest
3777
+ * deployment sends all traffic to one version (deploymentRunsOnlyVersion).
3778
+ * `split` is each other answer, for example a gradual deployment.
3779
+ */
3780
+ function readActivePlatformWorkerDeployment(response) {
3781
+ const deployments = response && typeof response === "object" && !Array.isArray(response) ? response.deployments : null;
3782
+ if (!Array.isArray(deployments)) return { kind: "malformed" };
3783
+ if (deployments.length === 0) return { kind: "split" };
3784
+ const latest = deployments[0];
3785
+ const versions = latest && typeof latest === "object" ? latest.versions : null;
3786
+ if (!Array.isArray(versions) || versions.some((version) => !version || typeof version !== "object" || typeof version.version_id !== "string" || !Number.isFinite(Number(version.percentage)))) return { kind: "malformed" };
3787
+ const [only] = versions;
3788
+ return versions.length === 1 && only && Number(only.percentage) === 100 ? {
3789
+ kind: "active",
3790
+ versionId: only.version_id
3791
+ } : { kind: "split" };
3792
+ }
3793
+ /**
3794
+ * Whether a version of a platform Worker has Void's record of its triggers.
3795
+ * This is the rule of the upgrade: it refuses a version without the record,
3796
+ * for example a version deployed outside Void.
3797
+ */
3798
+ async function platformWorkerVersionHasTriggerRecord(readCloudflare, accountId, workerName, versionId) {
3799
+ const version = await readCloudflare(`/accounts/${encodeURIComponent(accountId)}/workers/scripts/${encodeURIComponent(workerName)}/versions/${encodeURIComponent(versionId)}`);
3800
+ const annotations = version && typeof version === "object" ? version.annotations : null;
3801
+ const message = annotations && typeof annotations === "object" ? annotations["workers/message"] : null;
3802
+ return parseCloudflareVersionMessage(typeof message === "string" ? message : null)?.schema === 2;
3803
+ }
3678
3804
  async function readPlatformWorkerDeployments(configPath, accountId, workerName, runner = runPlatformWrangler) {
3679
3805
  const result = await runner({
3680
3806
  type: "deployments.list",
@@ -3753,15 +3879,15 @@ function readPlatformWorkerOwnershipMarker(config) {
3753
3879
  if (typeof marker !== "string" || marker === "") throw new Error("platform: Platform Worker config is missing its ownership marker.");
3754
3880
  return marker;
3755
3881
  }
3756
- async function readActivePlatformWorkerSnapshot(configPath, accountId, workerName, expectedOwnershipMarker, runner = runPlatformWrangler) {
3882
+ async function readActivePlatformWorkerSnapshot(configPath, accountId, workerName, expectedOwnershipMarker, runner = runPlatformWrangler, unrecordedTriggerConfig) {
3757
3883
  const readActiveVersion = () => readActivePlatformWorkerVersion(configPath, accountId, workerName, runner);
3758
3884
  const versionId = await readActiveVersion();
3759
3885
  if (!versionId) throw new Error(`platform: Existing Worker ${workerName} has no single active production version.`);
3760
3886
  return await readPlatformWorkerVersionSnapshot(configPath, accountId, workerName, expectedOwnershipMarker, versionId, runner, async () => {
3761
3887
  if (await readActiveVersion() !== versionId) throw new Error(`platform: Worker ${workerName} changed concurrently while its active version was inspected.`);
3762
- });
3888
+ }, unrecordedTriggerConfig);
3763
3889
  }
3764
- async function readPlatformWorkerVersionSnapshot(configPath, accountId, workerName, expectedOwnershipMarker, versionId, runner, validateCurrentVersion) {
3890
+ async function readPlatformWorkerVersionSnapshot(configPath, accountId, workerName, expectedOwnershipMarker, versionId, runner, validateCurrentVersion, unrecordedTriggerConfig) {
3765
3891
  const result = await runner({
3766
3892
  type: "versions.view",
3767
3893
  versionId,
@@ -3779,6 +3905,11 @@ async function readPlatformWorkerVersionSnapshot(configPath, accountId, workerNa
3779
3905
  const annotations = value.annotations;
3780
3906
  const message = annotations && typeof annotations === "object" && typeof annotations["workers/message"] === "string" ? annotations["workers/message"] ?? null : null;
3781
3907
  const metadata = parseCloudflareVersionMessage(message);
3908
+ if (metadata?.schema !== 2 && unrecordedTriggerConfig) return {
3909
+ migrationTag: readCloudflareVersionMigrationTag(value),
3910
+ triggerConfig: unrecordedTriggerConfig,
3911
+ versionId
3912
+ };
3782
3913
  if (metadata?.schema !== 2) throw new Error(`platform: Active Worker ${workerName} predates safe trigger snapshots. Refusing to upgrade because its routes, cron, and queue consumers could not be restored after a failed cutover.`);
3783
3914
  const migrationTag = readCloudflareVersionMigrationTag(value);
3784
3915
  return {
@@ -4383,7 +4514,7 @@ async function deployPlatformWorker(config, secrets, options) {
4383
4514
  }
4384
4515
  }
4385
4516
  if (options.workerExisted) {
4386
- const previous = await readActivePlatformWorkerSnapshot(configPath, accountId, workerName, readPlatformWorkerOwnershipMarker(config), readRunner);
4517
+ const previous = await readActivePlatformWorkerSnapshot(configPath, accountId, workerName, readPlatformWorkerOwnershipMarker(config), readRunner, options.replaceOutsideChanges ? previousTriggers : void 0);
4387
4518
  previousVersionId = previous.versionId;
4388
4519
  const deployments = await readPlatformWorkerDeployments(configPath, accountId, workerName, readRunner);
4389
4520
  if (!deploymentRunsOnlyVersion(deployments[0], previousVersionId)) throw new Error(`platform: Worker ${workerName} changed concurrently while its deployment lineage was inspected.`);
@@ -4559,6 +4690,37 @@ async function deployPlatformWorker(config, secrets, options) {
4559
4690
  });
4560
4691
  }
4561
4692
  }
4693
+ /**
4694
+ * Upload a version of a platform Worker that was changed outside Void, and do
4695
+ * not deploy it. The Cloudflare CLI uses this terminal: it shows what the
4696
+ * upload replaces and asks the operator. After the operator agrees, later
4697
+ * uploads of this Worker work without a terminal again.
4698
+ */
4699
+ async function uploadPlatformWorkerForReview(config, options) {
4700
+ const directory = mkdtempSync(join(tmpdir(), "void-platform-review-"));
4701
+ const workerName = String(config.name);
4702
+ const accountId = String(config.account_id);
4703
+ const readRunner = platformWorkerApiReadRunner(accountId, workerName, options.readCloudflare, options.runner);
4704
+ try {
4705
+ const { wranglerConfig } = isolatePlatformWorkerCustomDomains(config);
4706
+ const configPath = join(directory, "wrangler.json");
4707
+ const writeConfig = (value) => writeFileSync(configPath, `${JSON.stringify(value, null, 2)}\n`, { mode: 384 });
4708
+ writeConfig(withoutPlatformWorkerTrafficAttachments(wranglerConfig));
4709
+ const active = await readActivePlatformWorkerSnapshot(configPath, accountId, workerName, readPlatformWorkerOwnershipMarker(config), readRunner, readTriggerConfigFromObject(wranglerConfig));
4710
+ writeConfig(withoutCompletedPlatformWorkerMigrations(withoutPlatformWorkerTrafficAttachments(wranglerConfig), active.migrationTag));
4711
+ return await options.runner({
4712
+ type: "versions.upload",
4713
+ configPath,
4714
+ message: "Void: replace changes made outside Void",
4715
+ interactive: true
4716
+ }, accountId, directory, true);
4717
+ } finally {
4718
+ rmSync(directory, {
4719
+ force: true,
4720
+ recursive: true
4721
+ });
4722
+ }
4723
+ }
4562
4724
  async function recoverFailedPlatformWorkerDeployment(input) {
4563
4725
  const failures = [];
4564
4726
  const attempt = async (label, operation) => {
@@ -4799,6 +4961,7 @@ async function uploadInstallationWorkers(input) {
4799
4961
  preservedRoutes,
4800
4962
  previousConfig: previousWorkerConfig,
4801
4963
  previousPublicEndpoints,
4964
+ ...dependencies.replaceOutsideChanges?.includes(action.name) ? { replaceOutsideChanges: true } : {},
4802
4965
  runner: parallelWorkerUploads ? runParallelPinnedPlatformWrangler : runPinnedPlatformWrangler,
4803
4966
  workerExisted: action.action !== "create"
4804
4967
  });
@@ -4942,6 +5105,12 @@ async function verifyPlatformDeploymentExecutionDrain(apiUrl, healthProbeToken,
4942
5105
  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.");
4943
5106
  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.");
4944
5107
  }
5108
+ const sharedEmailStepPermissions = /* @__PURE__ */ new Map([
5109
+ ["routing_enable", "Zone → Zone Settings → Edit"],
5110
+ ["subaddressing_ensure", "Zone → Zone Settings → Edit"],
5111
+ ["inbound_probe", "Zone → Zone Settings → Edit"],
5112
+ ["sending_onboard", "Account → Email Sending → Edit"]
5113
+ ]);
4945
5114
  async function bootstrapPlatformSharedEmail(apiUrl, healthProbeToken, fetcher = fetch) {
4946
5115
  const deadline = Date.now() + 3e5;
4947
5116
  let operationId = null;
@@ -5024,6 +5193,9 @@ async function bootstrapPlatformSharedEmail(apiUrl, healthProbeToken, fetcher =
5024
5193
  if (typeof status?.failureDetail === "string" && safeDetails.includes(status.failureDetail)) {
5025
5194
  details.push(`${status.failureDetail}.`);
5026
5195
  if (currentStep === "inbound_probe" && status.failureDetail === mxDetail) details.push(`Enable Email Routing for ${domain} in the Cloudflare dashboard, then resume this installation.`);
5196
+ } else if (failureCode === "blocked" && currentStep && sharedEmailStepPermissions.has(currentStep)) {
5197
+ const permission = sharedEmailStepPermissions.get(currentStep);
5198
+ details.push(`This step needs ${permission} on the runtime API token. If the token does not have it, edit the token in the Cloudflare dashboard and add it. The token value stays the same. Or set VOID_PLATFORM_RUNTIME_CLOUDFLARE_API_TOKEN to a token that has it. Then resume this installation.`);
5027
5199
  }
5028
5200
  const detail = details.length ? ` ${details.join(" ")}` : "";
5029
5201
  throw new Error(`platform: Shared email bootstrap did not reach inbound readiness at ${apiUrl} (HTTP ${response.status}).${detail} Traffic remains disabled.`);
@@ -5287,7 +5459,7 @@ async function verifyInstalledPlatformAccessProtection(input) {
5287
5459
  }
5288
5460
  const existingWorkers = input.plan.actions.filter((action) => action.kind === "worker" && action.action === "update").map((action) => action.name);
5289
5461
  if (existingWorkers.length) {
5290
- const { verifyProtectionWorkerOrigins } = await import("./platform-auth-protection-U9HCqoXv.mjs");
5462
+ const { verifyProtectionWorkerOrigins } = await import("./platform-auth-protection-SHhvim1L.mjs");
5291
5463
  await verifyProtectionWorkerOrigins({
5292
5464
  request: input.client.request.bind(input.client),
5293
5465
  listWorkerDomains: input.client.listWorkerDomains.bind(input.client),
@@ -6659,21 +6831,6 @@ async function installPlatform(plan, secrets, existing, dependencies) {
6659
6831
  const recoveringCreatedWorker = interruptedCutover !== null && [...interruptedCutover.staged.values()].some((snapshot) => snapshot.previousVersionId === null);
6660
6832
  const requiresSandboxDrain = !preserveDisabled && targetSandboxDrainProtocol >= 1 && sandboxDrainState === "pending";
6661
6833
  const requiresTrafficGuard = !previousPublicEndpoints || recoveringUnsafeTraffic || createsWorker || recoveringCreatedWorker || state.manifest.usageProtocolVersion !== targetUsageProtocolVersion || runtime.privatePreviews === 1 && !state.manifest.steps.some((step) => step.key === "private-previews-initialized:v1") || authenticationProtocolTransition || shouldBootstrapSharedEmail || requiresSandboxDrain || !!state.plan.deployment.appAccess && !state.manifest.steps.some((step) => step.key === "app-access-initialized:v1");
6662
- if (requiresTrafficGuard) {
6663
- await dependencies.client.putKvValue(state.plan.account.id, requireId(ids, "routing-kv"), PLATFORM_DISABLED_KEY, PLATFORM_DISABLED_VALUE);
6664
- workerTrafficGuarded = true;
6665
- dependencies.onProgress?.("Application traffic paused for a safe platform cutover");
6666
- }
6667
- if (!preserveDisabled) {
6668
- const trafficGeneration = nextTrafficGeneration(state.manifest);
6669
- state.manifest = recordInstallationStep(state.manifest, {
6670
- completedAt: now(),
6671
- key: `traffic-enabled:${trafficGeneration}`,
6672
- resultHash: String(trafficGeneration)
6673
- }, now());
6674
- writeInstallationManifest(state.manifest);
6675
- await writeRemoteInstallationCheckpoint(dependencies.client, state.plan, state.manifest);
6676
- }
6677
6834
  const configInput = {
6678
6835
  authenticationDatabaseUncached: true,
6679
6836
  proxyHealthBinding: true,
@@ -6697,17 +6854,34 @@ async function installPlatform(plan, secrets, existing, dependencies) {
6697
6854
  runtimeEntrypoints: entrypoints,
6698
6855
  ...runtime.assets ? { runtimeApiAssetsDirectory: join(runtimeDirectory, PLATFORM_ADMIN_ASSETS_DIRECTORY) } : {}
6699
6856
  };
6700
- const workerDeploymentSecrets = {
6701
- ...state.secrets,
6702
- workerSecretUpdates: state.workerSecretUpdateIntent?.updates ?? {}
6703
- };
6704
- const requiresStagedCommit = !preserveDisabled && requiresTrafficGuard;
6705
6857
  const enabledConfigs = createPlatformWorkerConfigs({
6706
6858
  ...configInput,
6707
6859
  privatePreviews: runtime.privatePreviews === 1,
6708
6860
  publicEndpoints: !preserveDisabled,
6709
6861
  trafficState: preserveDisabled ? "staging" : "enabled"
6710
6862
  });
6863
+ const outsideChangeConfigs = workerKeys.map((key) => enabledConfigs[key]).filter((config) => dependencies.replaceOutsideChanges?.includes(String(config.name)));
6864
+ if (outsideChangeConfigs.length > 0) await dependencies.uploadOutsideChanges?.(outsideChangeConfigs);
6865
+ if (requiresTrafficGuard) {
6866
+ await dependencies.client.putKvValue(state.plan.account.id, requireId(ids, "routing-kv"), PLATFORM_DISABLED_KEY, PLATFORM_DISABLED_VALUE);
6867
+ workerTrafficGuarded = true;
6868
+ dependencies.onProgress?.("Application traffic paused for a safe platform cutover");
6869
+ }
6870
+ if (!preserveDisabled) {
6871
+ const trafficGeneration = nextTrafficGeneration(state.manifest);
6872
+ state.manifest = recordInstallationStep(state.manifest, {
6873
+ completedAt: now(),
6874
+ key: `traffic-enabled:${trafficGeneration}`,
6875
+ resultHash: String(trafficGeneration)
6876
+ }, now());
6877
+ writeInstallationManifest(state.manifest);
6878
+ await writeRemoteInstallationCheckpoint(dependencies.client, state.plan, state.manifest);
6879
+ }
6880
+ const workerDeploymentSecrets = {
6881
+ ...state.secrets,
6882
+ workerSecretUpdates: state.workerSecretUpdateIntent?.updates ?? {}
6883
+ };
6884
+ const requiresStagedCommit = !preserveDisabled && requiresTrafficGuard;
6711
6885
  const configs = requiresStagedCommit ? createPlatformWorkerConfigs({
6712
6886
  ...configInput,
6713
6887
  privatePreviews: runtime.privatePreviews === 1,
@@ -7313,6 +7487,171 @@ async function replacePendingInstallationAuthentication(input) {
7313
7487
  };
7314
7488
  }
7315
7489
  //#endregion
7490
+ //#region src/cli/platform-installer-outside-changes.ts
7491
+ const OUTSIDE_SOURCES = {
7492
+ dash: "changed in the Cloudflare dashboard",
7493
+ api: "changed in the Cloudflare API",
7494
+ unrecorded: "has an active version without a Void deployment record"
7495
+ };
7496
+ const PLATFORM_WORKER_KEYS = [
7497
+ "api",
7498
+ "proxy",
7499
+ "emailGateway",
7500
+ "tail",
7501
+ "dispatch"
7502
+ ];
7503
+ const NO_WORKER_CHANGED = "No platform Worker changed. This command did not pause application traffic.";
7504
+ /** The wait before the second and the third read of a Worker. */
7505
+ const READ_RETRY_DELAYS_MS = [250, 500];
7506
+ /** Stop before the run changes a Worker, because Void cannot tell what the run replaces. */
7507
+ function unreadablePlatformWorker(workerName, error) {
7508
+ const status = error instanceof PlatformCloudflareApiError ? error.status : null;
7509
+ const answer = status === null ? `no HTTP response: ${error instanceof Error ? error.message : String(error)}` : `HTTP ${status}`;
7510
+ return new Error([
7511
+ `platform: Void cannot read the platform Worker ${workerName} (${answer}), so it cannot find changes made outside Void.`,
7512
+ NO_WORKER_CHANGED,
7513
+ status === 401 || status === 403 ? "Make sure that the Cloudflare API token has Account → Workers Scripts → Edit for this account. Then run the command again." : "Run the command again."
7514
+ ].join(" "), { cause: error });
7515
+ }
7516
+ /**
7517
+ * Read a Worker again after a rate limit, a Cloudflare server error or a
7518
+ * network failure. Each other failure, or a third failure, stops the run.
7519
+ */
7520
+ async function readPlatformWorker(workerName, read) {
7521
+ for (let attempt = 0;; attempt++) try {
7522
+ return await read();
7523
+ } catch (error) {
7524
+ const status = error instanceof PlatformCloudflareApiError ? error.status : null;
7525
+ const wait = READ_RETRY_DELAYS_MS[attempt];
7526
+ if (wait === void 0 || status !== null && status !== 429 && status < 500) throw unreadablePlatformWorker(workerName, error);
7527
+ await setTimeout$1(wait);
7528
+ }
7529
+ }
7530
+ /** The Cloudflare CLI asks only about these changes. The others need no upload for review. */
7531
+ function needsReviewUpload(change) {
7532
+ return change.source !== "unrecorded";
7533
+ }
7534
+ /**
7535
+ * The earlier version of each Worker that the installer makes active again
7536
+ * before it deploys. A run that stopped during a Worker cutover left Void's
7537
+ * version active, and the next run first restores the earlier one. Like the
7538
+ * installer, keep Void's version while the platform changes to configurable
7539
+ * sign-in and already requires sign-in. Every Worker that this check reads
7540
+ * exists, so the installation had Workers before.
7541
+ */
7542
+ async function restoredPlatformWorkerVersions(installation, runtime) {
7543
+ const cutover = interruptedWorkerCutover(installation.manifest);
7544
+ if (!cutover || installation.manifest.deployment.authentication.configurable !== true && runtime.manifest.authenticationProtocolVersion === 1 && (await installation.readAuthenticationState?.())?.sessionsRequired === true) return /* @__PURE__ */ new Map();
7545
+ return new Map([...cutover.staged.values()].flatMap((snapshot) => snapshot.previousVersionId === null ? [] : [[snapshot.workerName, snapshot.previousVersionId]]));
7546
+ }
7547
+ /** Find the existing platform Workers that a run uploads and that were changed outside Void. */
7548
+ async function findPlatformWorkerOutsideChanges(client, plan, runtime, installation) {
7549
+ const workers = PLATFORM_WORKER_KEYS.flatMap((key) => {
7550
+ if (key === "emailGateway" && runtime.manifest.artifacts.emailGateway === void 0) return [];
7551
+ const action = plan.actions.find((entry) => entry.key === platformWorkerResourceKey(key));
7552
+ return action?.kind === "worker" && action.action !== "create" ? [{
7553
+ key,
7554
+ name: action.name
7555
+ }] : [];
7556
+ });
7557
+ const restored = workers.length > 0 ? await restoredPlatformWorkerVersions(installation, runtime) : /* @__PURE__ */ new Map();
7558
+ return (await Promise.all(workers.map(async (worker) => {
7559
+ const readCloudflare = (path) => readPlatformWorker(worker.name, () => client.request(path));
7560
+ const source = await readPlatformWorker(worker.name, () => client.getWorkerLastDeployedFrom(plan.account.id, worker.name));
7561
+ const deployments = await readPlatformWorker(worker.name, () => client.request(`/accounts/${encodeURIComponent(plan.account.id)}/workers/scripts/${encodeURIComponent(worker.name)}/deployments`).catch((error) => {
7562
+ if (error instanceof PlatformCloudflareApiError && error.status === 404) return null;
7563
+ throw error;
7564
+ }));
7565
+ if (deployments === null) return source === "dash" || source === "api" ? {
7566
+ ...worker,
7567
+ source
7568
+ } : null;
7569
+ const active = readActivePlatformWorkerDeployment(deployments);
7570
+ if (active.kind === "malformed") throw new Error(`platform: Cloudflare returned malformed deployment data for the platform Worker ${worker.name}, so Void cannot find changes made outside Void. ${NO_WORKER_CHANGED} Run the command again.`);
7571
+ if (active.kind === "split") throw new Error(`platform: The platform Worker ${worker.name} has no single version that gets 100% of traffic, for example after a gradual deployment. This command deploys a new version only when one version gets all traffic. ${NO_WORKER_CHANGED} In the Cloudflare dashboard, deploy one version of ${worker.name} to 100% of traffic. Then run this command once in a terminal, so that you can approve the dashboard change.`);
7572
+ if (source === "dash" || source === "api") return {
7573
+ ...worker,
7574
+ source
7575
+ };
7576
+ const restoredVersion = restored.get(worker.name);
7577
+ return (await Promise.all([active.versionId, ...restoredVersion === void 0 ? [] : [restoredVersion]].map((versionId) => platformWorkerVersionHasTriggerRecord(readCloudflare, plan.account.id, worker.name, versionId)))).includes(false) ? {
7578
+ ...worker,
7579
+ source: "unrecorded"
7580
+ } : null;
7581
+ }))).filter((change) => change !== null);
7582
+ }
7583
+ function listPlatformWorkerOutsideChanges(changes) {
7584
+ return changes.map((change) => `${change.name}: ${OUTSIDE_SOURCES[change.source]}`).join("\n");
7585
+ }
7586
+ /**
7587
+ * The list of Workers after a heading. The heading must be true for each
7588
+ * source: an older Void release can have deployed the active version.
7589
+ */
7590
+ function formatPlatformWorkerOutsideChanges(changes) {
7591
+ return `These platform Workers need your approval before this command replaces their configuration:\n${listPlatformWorkerOutsideChanges(changes)}`;
7592
+ }
7593
+ /**
7594
+ * Do this before a run changes a Worker or pauses traffic. When platform
7595
+ * Workers were changed outside Void, stop, or ask the operator in a terminal.
7596
+ * Returns the Workers whose outside changes the operator agreed to replace.
7597
+ * The installer uploads them later with uploadPlatformWorkerOutsideChanges.
7598
+ */
7599
+ async function reviewPlatformWorkerOutsideChanges(input) {
7600
+ const changes = await findPlatformWorkerOutsideChanges(input.client, input.plan, input.runtime, input.installation);
7601
+ if (changes.length === 0) return [];
7602
+ if (!input.interactive) throw new Error([
7603
+ `platform: ${formatPlatformWorkerOutsideChanges(changes)}`,
7604
+ NO_WORKER_CHANGED,
7605
+ "This command replaces their configuration with the Void configuration. It can ask for your approval only in a terminal.",
7606
+ `Run this command once in a terminal to review them:\n${input.command}`
7607
+ ].join("\n"));
7608
+ note([
7609
+ listPlatformWorkerOutsideChanges(changes),
7610
+ "",
7611
+ "This command replaces the configuration of these Workers with the Void configuration.",
7612
+ "It removes values that Void does not set, for example an EMAIL_FROM that you added.",
7613
+ "It resets values that Void sets, for example AUTH_REDIRECT_URI.",
7614
+ ...changes.some(needsReviewUpload) ? [
7615
+ "First, it prepares the platform resources and the database.",
7616
+ "Then, before it pauses traffic or deploys a Worker, the Cloudflare CLI asks you again for each Worker changed in the Cloudflare dashboard or API. For a dashboard change, it shows the differences.",
7617
+ "It uploads a new version of each of these Workers and does not deploy it."
7618
+ ] : []
7619
+ ].join("\n"), "Approval needed");
7620
+ const answer = await confirm({
7621
+ message: "Replace the configuration of these Workers?",
7622
+ initialValue: false
7623
+ });
7624
+ if (isCancel(answer) || typeof answer === "symbol" || !answer) throw new Error(`platform: Cancelled. ${NO_WORKER_CHANGED}`);
7625
+ return changes;
7626
+ }
7627
+ /**
7628
+ * Upload a new version of each Worker that the operator agreed to replace, and
7629
+ * do not deploy it. The installer gives the configs after it creates the
7630
+ * resources that they use, and before it pauses traffic. The Cloudflare CLI
7631
+ * uses this terminal: it shows what the upload replaces and asks the operator.
7632
+ */
7633
+ async function uploadPlatformWorkerOutsideChanges(configs, input) {
7634
+ const { client } = input;
7635
+ const reviewed = configs.filter((config) => input.changes.some((change) => change.name === config.name && needsReviewUpload(change)));
7636
+ if (reviewed.length === 0) return;
7637
+ const runner = input.runner ?? ((operation, accountId, cwd, capture) => runPlatformWranglerAtApiBase(operation, accountId, cwd, capture, client.apiBaseUrl, false, input.operatorApiToken));
7638
+ input.progress?.pause();
7639
+ try {
7640
+ for (const config of reviewed) {
7641
+ const name = String(config.name);
7642
+ log.step(`Uploading a new version of ${name} without deploying it`);
7643
+ const uploaded = await uploadPlatformWorkerForReview(config, {
7644
+ readCloudflare: (path) => client.request(path),
7645
+ runner
7646
+ });
7647
+ const source = await client.getWorkerLastDeployedFrom(String(config.account_id), name);
7648
+ if (uploaded.status !== 0 || source === "dash" || source === "api") throw new Error(`platform: The Cloudflare CLI did not replace the changes to ${name}. Its output above shows why. No deployment changed. This command did not pause application traffic.`);
7649
+ }
7650
+ } finally {
7651
+ input.progress?.resume();
7652
+ }
7653
+ }
7654
+ //#endregion
7316
7655
  //#region src/cli/platform-saved-plans.ts
7317
7656
  function planId(args) {
7318
7657
  return createHash("sha256").update(JSON.stringify([args.account, args.name])).digest("hex");
@@ -7437,14 +7776,14 @@ function resolvePlatformInstallDatabaseUrl(checkpoint, manifest = null) {
7437
7776
  }
7438
7777
  async function runPlatformLifecycleCommand(commandArgs) {
7439
7778
  if (commandArgs.subcommand === "domain") {
7440
- const { runPlatformDomainCommand } = await import("./platform-domain-CXyq-68_.mjs");
7779
+ const { runPlatformDomainCommand } = await import("./platform-domain-jpmBfZeb.mjs");
7441
7780
  await runPlatformDomainCommand(commandArgs);
7442
7781
  return;
7443
7782
  }
7444
7783
  if (commandArgs.subcommand === "discover") {
7445
7784
  const client = new PlatformCloudflareClient((await resolvePlatformCloudflareCredential()).token);
7446
7785
  const accountId = await selectAccount(await readAccounts(client), commandArgs.account);
7447
- const { recoverPlatformInstallations } = await import("./platform-recovery-CH_fThW6.mjs");
7786
+ const { recoverPlatformInstallations } = await import("./platform-recovery-BiNpZZab.mjs");
7448
7787
  const recovered = await recoverPlatformInstallations(client, accountId, void 0, commandArgs.selector);
7449
7788
  if (recovered.length === 0) {
7450
7789
  log.info(`No recoverable Void platform installations were found in ${accountId}.`);
@@ -7455,7 +7794,7 @@ async function runPlatformLifecycleCommand(commandArgs) {
7455
7794
  return;
7456
7795
  }
7457
7796
  if (commandArgs.subcommand !== "install") {
7458
- const { runInstalledPlatformCommand } = await import("./platform-management-BvgbusqL.mjs");
7797
+ const { runInstalledPlatformCommand } = await import("./platform-management-7m-PLaPJ.mjs");
7459
7798
  await runInstalledPlatformCommand(commandArgs);
7460
7799
  return;
7461
7800
  }
@@ -7483,7 +7822,7 @@ async function runPlatformLifecycleCommand(commandArgs) {
7483
7822
  if (!args.resume && process.stdin.isTTY) {
7484
7823
  const installs = resumableInstalls().filter((manifest) => !args.name || manifest.deployment.installationSlug === normalizeInstallationSlug(args.name));
7485
7824
  const manifests = listInstallationManifests();
7486
- const savedPlans = !args.applicationDomain && !args.workersDev && !args.zone && !args.displayName && !args.hyperdrive && !args.controlPlaneDomain && !args.noControlPlaneDomain && !args.authConfig && !args.loginMethods && !args.runtime && args.accessProtection === void 0 && args.appAccess === void 0 && !args.dedicatedZoneSpecified && args.dashboardUrl === void 0 ? listSavedPlatformInstallPlans().filter((saved) => (!args.name || saved.args.name === normalizeInstallationSlug(args.name)) && (!args.account || saved.args.account === args.account || saved.accountName.toLowerCase() === args.account.toLowerCase()) && !manifests.some((manifest) => manifest.status !== "uninstalled" && manifest.account.id === saved.args.account && manifest.deployment.installationSlug === saved.args.name)) : [];
7825
+ const savedPlans = !args.applicationDomain && !args.workersDev && !args.zone && !args.displayName && !args.hyperdrive && !args.controlPlaneDomain && !args.noControlPlaneDomain && !args.authConfig && !args.loginMethods && !args.runtime && args.accessProtection === void 0 && args.appAccess === void 0 && !args.dedicatedZoneSpecified && args.dashboardUrl === void 0 && !args.customDomains ? listSavedPlatformInstallPlans().filter((saved) => (!args.name || saved.args.name === normalizeInstallationSlug(args.name)) && (!args.account || saved.args.account === args.account || saved.accountName.toLowerCase() === args.account.toLowerCase()) && !manifests.some((manifest) => manifest.status !== "uninstalled" && manifest.account.id === saved.args.account && manifest.deployment.installationSlug === saved.args.name)) : [];
7487
7826
  if (installs.length > 0 || savedPlans.length > 0) {
7488
7827
  const choice = await select({
7489
7828
  message: "Continue a previous platform install?",
@@ -7631,7 +7970,7 @@ async function runPlatformLifecycleCommand(commandArgs) {
7631
7970
  progress?.stop("Read the saved installation.");
7632
7971
  progress = void 0;
7633
7972
  if (authoritative) {
7634
- const { selectAuthoritativePlatformManifest } = await import("./platform-management-BvgbusqL.mjs");
7973
+ const { selectAuthoritativePlatformManifest } = await import("./platform-management-7m-PLaPJ.mjs");
7635
7974
  const selected = selectAuthoritativePlatformManifest(existing, authoritative);
7636
7975
  authoritativeProvisionIntents = true;
7637
7976
  if (!args.plan && JSON.stringify(selected) !== JSON.stringify(existing)) writeInstallationManifest(selected);
@@ -7737,6 +8076,7 @@ async function runPlatformLifecycleCommand(commandArgs) {
7737
8076
  applicationDomain,
7738
8077
  controlPlaneDomain,
7739
8078
  ...args.dashboardUrl !== void 0 ? { dashboardUrl: args.dashboardUrl } : {},
8079
+ ...args.customDomains ? { customDomains: true } : {},
7740
8080
  dedicatedZone,
7741
8081
  displayName,
7742
8082
  ...externalHyperdrive ? { externalHyperdrive } : {},
@@ -7763,6 +8103,7 @@ async function runPlatformLifecycleCommand(commandArgs) {
7763
8103
  zoneName,
7764
8104
  controlPlaneDomain,
7765
8105
  ...args.dashboardUrl !== void 0 ? { dashboardUrl: args.dashboardUrl } : {},
8106
+ ...args.customDomains ? { customDomains: true } : {},
7766
8107
  dedicatedZone,
7767
8108
  workersDev,
7768
8109
  authConfig: args.authConfig,
@@ -7840,8 +8181,10 @@ async function runPlatformLifecycleCommand(commandArgs) {
7840
8181
  break;
7841
8182
  } catch (error) {
7842
8183
  if (!isPlatformTokenAuthorizationError(error)) throw error;
8184
+ const details = /* @__PURE__ */ new Set();
8185
+ for (let cause = error, depth = 0; depth < 5 && cause instanceof Error; depth++, cause = cause.cause) details.add(cause.message.replaceAll(token, "[redacted]"));
7843
8186
  forgetRejectedManagementToken();
7844
- log.warn("Cloudflare rejected this token. Check its permissions and account/zone scope, then paste a replacement.");
8187
+ log.warn([...details, "Check the token permissions and account/zone scope, then paste a replacement."].join("\n"));
7845
8188
  }
7846
8189
  }
7847
8190
  } else if (managementAccess) await verifyPlatformManagementAccess(client, accountId, managementAccess);
@@ -7887,6 +8230,7 @@ async function runPlatformLifecycleCommand(commandArgs) {
7887
8230
  accountId,
7888
8231
  applicationDomain,
7889
8232
  customDomains: plan.deployment.capabilities.customDomains,
8233
+ ...plan.deployment.capabilities.email && plan.deployment.emailSharedZoneId ? { emailZoneId: plan.deployment.emailSharedZoneId } : {},
7890
8234
  zoneId: plan.zone?.id ?? null
7891
8235
  });
7892
8236
  checkingRuntimePermissions = false;
@@ -7895,7 +8239,7 @@ async function runPlatformLifecycleCommand(commandArgs) {
7895
8239
  plan = await verifyPlatformAiGatewayPlan(client, planInput, existing, plan, authoritativeProvisionIntents);
7896
8240
  }
7897
8241
  } catch (error) {
7898
- if (credentialSetup && token === values.cloudflareApiToken && isPlatformTokenAuthorizationError(error, checkingRuntimePermissions ? void 0 : "/ai-gateway/")) {
8242
+ if (credentialSetup && token === values.cloudflareApiToken && !(error instanceof PlatformRuntimeTokenEmailPermissionError) && isPlatformTokenAuthorizationError(error, checkingRuntimePermissions ? void 0 : "/ai-gateway/")) {
7899
8243
  delete values.cloudflareApiToken;
7900
8244
  writePendingInstallationSecrets(setupManifest, values);
7901
8245
  rejectedSavedRuntimeToken = true;
@@ -7990,6 +8334,14 @@ async function runPlatformLifecycleCommand(commandArgs) {
7990
8334
  title: "About these keys",
7991
8335
  url: "https://void.cloud/guide/platform/installation/credentials#signing-and-encryption-keys"
7992
8336
  }));
8337
+ const outsideChanges = await reviewPlatformWorkerOutsideChanges({
8338
+ client,
8339
+ plan,
8340
+ runtime,
8341
+ installation: { manifest: existing },
8342
+ interactive: process.stdin.isTTY === true && process.stdout.isTTY === true && !args.yes,
8343
+ command: `${cliCommandName()} platform install --resume --name ${existing.id}`
8344
+ });
7993
8345
  progress = startProgress("Installing the platform");
7994
8346
  if (credentialSetup) {
7995
8347
  saveValues({
@@ -8008,6 +8360,15 @@ async function runPlatformLifecycleCommand(commandArgs) {
8008
8360
  },
8009
8361
  client,
8010
8362
  operatorApiToken: managementApiToken || operatorCredential.token,
8363
+ ...outsideChanges.length > 0 ? {
8364
+ replaceOutsideChanges: outsideChanges.map((change) => change.name),
8365
+ uploadOutsideChanges: (configs) => uploadPlatformWorkerOutsideChanges(configs, {
8366
+ changes: outsideChanges,
8367
+ client,
8368
+ operatorApiToken: managementApiToken || operatorCredential.token,
8369
+ progress
8370
+ })
8371
+ } : {},
8011
8372
  runtime,
8012
8373
  healthCheckResumeCommand: `${cliCommandName()} platform install --resume --name ${existing.id}`,
8013
8374
  onProgress: (message) => progress?.message(message),
@@ -8049,7 +8410,7 @@ async function runPlatformLifecycleCommand(commandArgs) {
8049
8410
  })).actions.filter((action) => action.kind === "worker"),
8050
8411
  revalidateBeforeWorkerDeploy: (manifest, action) => revalidatePlatformInstallAction(client, plan, manifest, action),
8051
8412
  verifyExistingOwnership: async (manifest, databaseCheckpointExists) => {
8052
- const { verifyPlatformInstallationOwnership } = await import("./platform-management-BvgbusqL.mjs");
8413
+ const { verifyPlatformInstallationOwnership } = await import("./platform-management-7m-PLaPJ.mjs");
8053
8414
  const retainedReplacementKeys = new Set(plan.actions.filter((action) => {
8054
8415
  const previous = manifest.resources.find((resource) => resource.key === action.key);
8055
8416
  return action.action === "create" && previous && previous.name !== action.name;
@@ -8206,4 +8567,4 @@ async function promptSecret(message, masked = true, validate, placeholder) {
8206
8567
  return value;
8207
8568
  }
8208
8569
  //#endregion
8209
- export { matchesRecordedR2Fingerprint as A, activatePlatformAccessCredentials as B, platformTrafficAttachmentWorker as C, formatPlatformInstallPlan as D, dnsRecordConfigHash as E, platformDatabaseNameFromOwnerUrl as F, PlatformCloudflareClient as G, parsePlatformAccessSetupSpec as H, turnOffInstalledProjectZeroTrust as I, hyperdriveOriginMatches as J, hyperdriveOriginConfigHash as K, assertPlatformOperatorCredentialCanApplyPlan as L, r2BucketConfigHash as M, resourceMarkerProblem as N, hasProvisionIntentStep as O, revalidatePlatformInstallAction as P, resolvePlatformCloudflareCredential as R, recordResource as S, createPlatformInstallPlan as T, preparePlatformAccessSetup as U, checkpointPlatformAccessResources as V, PlatformCloudflareApiError as W, verifyPlatformRuntimeTokenPermissions as Y, readPlatformDatabase as _, authenticationSetupRequestHash as a, createPlatformInstallationSecrets as b, REMOTE_INSTALLATION_MANIFEST_KEY as c, acquirePlatformDatabaseLifecycleLockConnection as d, platformDatabaseClientUrl as f, formatZeroTrustApplications as g, assertProjectZeroTrustAllowsUninstall as h, verifyPlatformAiGatewayPlan as i, platformAdoptionIntentHash as j, legacyResourceConfigHash as k, installPlatform as l, readPlatformProjectZeroTrustState as m, resolvePlatformInstallDatabaseUrl as n, readLoginReplacementFile as o, readPlatformProjectSlugs as p, hyperdriveOriginFromConnectionString as q, runPlatformLifecycleCommand as r, replacePendingInstallationAuthentication as s, assertNoninteractivePlatformMutationAcknowledged as t, acquirePlatformDatabaseLifecycleLock as u, loadPlatformRuntime as v, recordPlatformWorkerTraffic as w, resolveBootstrapGitHubId as x, createPlatformHealthProbeToken as y, verifyPlatformCloudflareCredential as z };
8570
+ export { hyperdriveOriginMatches as $, dnsRecordConfigHash as A, turnOffInstalledProjectZeroTrust as B, createPlatformHealthProbeToken as C, platformTrafficAttachmentWorker as D, recordResource as E, platformAdoptionIntentHash as F, checkpointPlatformAccessResources as G, resolvePlatformCloudflareCredential as H, r2BucketConfigHash as I, PlatformCloudflareApiError as J, parsePlatformAccessSetupSpec as K, resourceMarkerProblem as L, hasProvisionIntentStep as M, legacyResourceConfigHash as N, recordPlatformWorkerTraffic as O, matchesRecordedR2Fingerprint as P, hyperdriveOriginFromConnectionString as Q, revalidatePlatformInstallAction as R, loadPlatformRuntime as S, resolveBootstrapGitHubId as T, verifyPlatformCloudflareCredential as U, assertPlatformOperatorCredentialCanApplyPlan as V, activatePlatformAccessCredentials as W, PlatformRuntimeTokenEmailPermissionError as X, PlatformCloudflareClient as Y, hyperdriveOriginConfigHash as Z, readPlatformProjectSlugs as _, findPlatformWorkerOutsideChanges as a, formatZeroTrustApplications as b, uploadPlatformWorkerOutsideChanges as c, replacePendingInstallationAuthentication as d, verifyPlatformRuntimeTokenEmailPermissions as et, REMOTE_INSTALLATION_MANIFEST_KEY as f, platformDatabaseClientUrl as g, acquirePlatformDatabaseLifecycleLockConnection as h, verifyPlatformAiGatewayPlan as i, formatPlatformInstallPlan as j, createPlatformInstallPlan as k, authenticationSetupRequestHash as l, acquirePlatformDatabaseLifecycleLock as m, resolvePlatformInstallDatabaseUrl as n, formatPlatformWorkerOutsideChanges as o, installPlatform as p, preparePlatformAccessSetup as q, runPlatformLifecycleCommand as r, reviewPlatformWorkerOutsideChanges as s, assertNoninteractivePlatformMutationAcknowledged as t, verifyPlatformRuntimeTokenPermissions as tt, readLoginReplacementFile as u, readPlatformProjectZeroTrustState as v, createPlatformInstallationSecrets as w, readPlatformDatabase as x, assertProjectZeroTrustAllowsUninstall as y, platformDatabaseNameFromOwnerUrl as z };