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.
- package/dist/{account-cmd-R35a4Oa8.mjs → account-cmd-CdUll_35.mjs} +5 -5
- package/dist/action-key-XZ9HkWAO.mjs +26 -0
- package/dist/{application-analysis-D7nNmNJb.mjs → application-analysis-fEqoSeMp.mjs} +11 -11
- package/dist/{application-routing-CZH66gXn.mjs → application-routing-DAhNP4u3.mjs} +1 -1
- package/dist/{ast-CPGPjb0V.mjs → ast-CsZ_q_gc.mjs} +6 -1
- package/dist/ast-bindings-C3yaPs1y.mjs +296 -0
- package/dist/{auth-link-VHmiKZmP.mjs → auth-link-C87vGXM-.mjs} +5 -5
- package/dist/{auth-router-O_Wrs5kg.mjs → auth-router-6I81r-ix.mjs} +3 -3
- package/dist/{better-auth-shared-Bv18bB-l.d.mts → better-auth-shared-DVk2DkTW.d.mts} +1 -1
- package/dist/{build-cmd-CMZiMn46.mjs → build-cmd-ex1g-j_e.mjs} +4 -4
- package/dist/{cache-TZ5Ab3d7.mjs → cache-oHzrnY3z.mjs} +4 -4
- package/dist/{cancel-deploy-DXoR6oC3.mjs → cancel-deploy-viAMD0Q_.mjs} +3 -3
- package/dist/cf-build-output-BJHuSsVN.mjs +1 -0
- package/dist/{cf-build-output-Bc8i3dzE.mjs → cf-build-output-BhVxldCw.mjs} +75 -10
- package/dist/{cf-user-agent-ChjQp2MU.mjs → cf-command-values-ByHk0xg4.mjs} +6 -16
- package/dist/cf-worker-settings-BECvvTVd.mjs +37 -0
- package/dist/cli/cli.mjs +139 -35
- package/dist/cli/cloudflare-operation-process.mjs +65 -18
- package/dist/cli/env-schema-probe.d.mts +1 -1
- package/dist/cli/env-schema-probe.mjs +3 -3
- package/dist/{client-GuSO3v_x.mjs → client-BEkdj1kx.mjs} +58 -13
- package/dist/client-BiRDZTSd.mjs +1 -0
- package/dist/{cloudflare-auth-DSvBc1Gu.mjs → cloudflare-auth-Byo_0_N9.mjs} +1 -1
- package/dist/{cloudflare-cmd-ZSErUl6Y.mjs → cloudflare-cmd-DMzCv12s.mjs} +5 -5
- package/dist/{cloudflare-config-rdFOys4X.mjs → cloudflare-config-DchHxPTo.mjs} +1 -1
- package/dist/{cloudflare-connect-BN-k2kD-.mjs → cloudflare-connect-CCRTVg-I.mjs} +3 -3
- package/dist/{cloudflare-operations-DH3GedbV.mjs → cloudflare-operations-Dh9dEHJj.mjs} +17 -7
- package/dist/{cloudflare-operations-Cm8lVx7W.mjs → cloudflare-operations-DsSaw2BT.mjs} +1 -1
- package/dist/{cloudflare-process-CZJ4viYl.mjs → cloudflare-process-8jEYIeim.mjs} +3 -2
- package/dist/{config-DC5llZaI.mjs → config-BIaXIkkv.mjs} +8 -3
- package/dist/{config-DYJV8aEH.mjs → config-BU6hMu1W.mjs} +1 -1
- package/dist/{config-D26zcdKy.mjs → config-DHgSEWT7.mjs} +39 -6
- package/dist/{config-7qoq2p9v.d.mts → config-DQfHOKh9.d.mts} +10 -1
- package/dist/config-entry.d.mts +1 -1
- package/dist/{connect-yQuLbuiJ.mjs → connect-k7R2rPVA.mjs} +6 -6
- package/dist/{create-project-CGOo_OL_.mjs → create-project-DTuy-l0z.mjs} +1 -1
- package/dist/{create-project-DEEWJX_M.mjs → create-project-DV71ol5d.mjs} +4 -4
- package/dist/{db-COI-yTJE.mjs → db-DVOkLH5X.mjs} +79 -23
- package/dist/{delete-PgN6L_9N.mjs → delete-BbP5eopu.mjs} +4 -4
- package/dist/{deploy-DsW-PrRQ.mjs → deploy-7H32EnUs.mjs} +220 -95
- package/dist/deploy-CE0Ev0Ob.mjs +2 -0
- package/dist/{deploy-managed-static-Dqz06S2u.mjs → deploy-managed-static-DlvcvnQT.mjs} +74 -28
- package/dist/{deploy-transport-nY3vLXPL.mjs → deploy-transport-BzGEj0Ge.mjs} +2 -2
- package/dist/deploy.d.mts +22 -1
- package/dist/deploy.mjs +49 -8
- package/dist/{dev-inbox-Rd6j_8vM.mjs → dev-inbox-D0jxye_D.mjs} +1 -1
- package/dist/{dispatch-BAbbn3jg.mjs → dispatch-DoUnxTNo.mjs} +15 -11
- package/dist/{dist-CfgIkieS.mjs → dist-B1PLt4_8.mjs} +1 -1
- package/dist/{dist-BVdsO7Py.mjs → dist-Cor9XGoz.mjs} +94 -200
- package/dist/{dist-Bn8Kodjp.mjs → dist-D15hFhP0.mjs} +1 -1
- package/dist/{domain-DKlNI0SR.mjs → domain-CA2wBgra.mjs} +5 -5
- package/dist/drizzle-9TubSZvI.mjs +1 -0
- package/dist/{email-BcjSyAL0.mjs → email-B9ayYQEG.mjs} +1 -1
- package/dist/{email-C0s3OvJk.mjs → email-DXpCc77w.mjs} +10 -10
- package/dist/{email-cloudflare-etpKiITL.mjs → email-cloudflare-D03g2TkS.mjs} +7 -7
- package/dist/{entry-B_0IhV64.mjs → entry-BEM8ZuGP.mjs} +40 -12
- package/dist/{env-BX2JFsgF.mjs → env-DgOQ9_8S.mjs} +6 -6
- package/dist/{env-public-CCdykygU.d.mts → env-public-6lqiSD2b.d.mts} +1 -0
- package/dist/{env-types-GRchm5dw.mjs → env-types-BicfKBhV.mjs} +1 -1
- package/dist/{env-validation-CuGfWRJd.mjs → env-validation-BmsdCZEk.mjs} +1 -1
- package/dist/{env-validation-D_KyMdab.mjs → env-validation-Dwq3PJzK.mjs} +3 -3
- package/dist/{fetch-DHLOuPyk.d.mts → fetch-CUX0tB78.d.mts} +14 -1
- package/dist/fetch-CtESUYWd.mjs +26 -0
- package/dist/{fetch-error-Dj3crt0e.mjs → fetch-error-Bw0n-IY-.mjs} +1 -1
- package/dist/{fetch-stream-PGgz4uGz.mjs → fetch-stream-BspOeTAI.mjs} +1 -1
- package/dist/{fetch-stream-CNxOrBdP.d.mts → fetch-stream-DfkAzZf-.d.mts} +1 -1
- package/dist/{gen-Bj6ZkOcI.mjs → gen-BfmcIi2g.mjs} +5 -5
- package/dist/gen-NrOlWZaG.mjs +1 -0
- package/dist/{github-cmd-DQDryS-5.mjs → github-cmd-CY9LUqLh.mjs} +4 -4
- package/dist/{handler-_PqY82xe.d.mts → handler-BbkSPzGF.d.mts} +91 -9
- package/dist/{headers-Bi4bOAwp.mjs → headers-D522zd0A.mjs} +2 -2
- package/dist/help-BeAFFE5C.mjs +1 -0
- package/dist/{help-D49SL_lN.mjs → help-CxSlPpzJ.mjs} +83 -12
- package/dist/{inbound-BuTfm11U.mjs → inbound-VrOLqwZY.mjs} +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +125 -124
- package/dist/{info-Bxunp14X.mjs → info-7VDlhtyy.mjs} +4 -4
- package/dist/{init-DyTEY04s.mjs → init-DxYyOs4-.mjs} +103 -43
- package/dist/{link-CgOVn9EC.mjs → link-Ddmod1dZ.mjs} +5 -5
- package/dist/{list-CUfD9RSb.mjs → list-MogaugiO.mjs} +5 -5
- package/dist/{live-DJONVlQK.d.mts → live-Bt8Y_0bg.d.mts} +1 -1
- package/dist/{local-d1-CpaPbd_L.mjs → local-d1-BHBnKWRb.mjs} +1 -1
- package/dist/{log-vqY3pqwI.mjs → log-BMwmN5Ae.mjs} +2 -2
- package/dist/{login-BJvGW5Eu.mjs → login-Cttnagmm.mjs} +4 -4
- package/dist/login-DgDwlKc_.mjs +1 -0
- package/dist/{logs-DeizBI4j.mjs → logs-CAszUWZI.mjs} +4 -4
- package/dist/{migrate-8rk56vV8.mjs → migrate-DyT2jJ8y.mjs} +5 -5
- package/dist/migrate-n49ZeS1X.mjs +1 -0
- package/dist/{mime-srOklVVv.mjs → mime-BAyHPskU.mjs} +1 -1
- package/dist/nitro.mjs +3 -3
- package/dist/{node-uN8_zxJr.mjs → node-WB7c2N4L.mjs} +2 -2
- package/dist/operator-cmd-d2MvJOrl.mjs +1 -0
- package/dist/{operator-cmd-CA5QQyvE.mjs → operator-cmd-skshjMGl.mjs} +4 -4
- package/dist/output-BW9uK099.mjs +1 -0
- package/dist/{output-Ba6ZbrzY.mjs → output-BpgYODYx.mjs} +16 -4
- package/dist/pages/client.d.mts +2 -2
- package/dist/pages/client.mjs +48 -25
- package/dist/pages/index.d.mts +2 -2
- package/dist/pages/index.mjs +4 -4
- package/dist/pages/islands-plugin.d.mts +43 -5
- package/dist/pages/islands-plugin.mjs +100 -28
- package/dist/pages/protocol.d.mts +2 -2
- package/dist/pages/protocol.mjs +1 -1
- package/dist/pages/serialize.d.mts +2 -2
- package/dist/pages/serialize.mjs +1 -1
- package/dist/{parse-filename-CUbj-1MP.mjs → parse-filename-DioPHiR9.mjs} +1 -35
- package/dist/{picocolors-BTps1_gs.mjs → picocolors-BsDfhxIw.mjs} +1 -1
- package/dist/{platform-args-B7ke06up.mjs → platform-args-DVbVClZl.mjs} +12 -4
- package/dist/platform-args-kQrgxScY.mjs +1 -0
- package/dist/{platform-auth-config-CZaY812e.mjs → platform-auth-config-C-pm5Qzx.mjs} +6 -6
- package/dist/{platform-auth-protection-U9HCqoXv.mjs → platform-auth-protection-SHhvim1L.mjs} +4 -4
- package/dist/{platform-auth-recovery-B0BV2hfV.mjs → platform-auth-recovery-DXVWs-wA.mjs} +4 -4
- package/dist/{platform-cmd-CXrjk4fb.mjs → platform-cmd-D5I1beUX.mjs} +7 -7
- package/dist/{platform-cmd-D9lwFpcl.mjs → platform-cmd-PiNRkopU.mjs} +1 -1
- package/dist/{platform-domain-CXyq-68_.mjs → platform-domain-jpmBfZeb.mjs} +4 -4
- package/dist/{platform-lifecycle-Dz2cJsLX.mjs → platform-lifecycle-DH3Yr1qQ.mjs} +1 -1
- package/dist/{platform-lifecycle-D0TdyXbN.mjs → platform-lifecycle-DyKmuNY6.mjs} +405 -44
- package/dist/{platform-management-CnXhgxTM.mjs → platform-management--vDwSn35.mjs} +49 -5
- package/dist/{platform-management-BvgbusqL.mjs → platform-management-7m-PLaPJ.mjs} +1 -1
- package/dist/{platform-plans-config-BWn1S2Rv.mjs → platform-plans-config-CeydDSj0.mjs} +2 -2
- package/dist/{platform-recovery-CH_fThW6.mjs → platform-recovery-BiNpZZab.mjs} +2 -2
- package/dist/{platform-setup-YvN9VQXV.mjs → platform-setup-Ca53gtt0.mjs} +3 -2
- package/dist/{plugin-inference-Bekzjuqw.mjs → plugin-inference-DUjCd55H.mjs} +145 -218
- package/dist/{prepare-2XD6kh77.mjs → prepare-BcAE5Sdj.mjs} +3 -3
- package/dist/prepare-C4I0f--h.mjs +1 -0
- package/dist/{prepare-DepAGBcV.mjs → prepare-DA-17DhO.mjs} +5 -5
- package/dist/{prerender-render-Bfsumdkv.mjs → prerender-render-Cijv7UFo.mjs} +11 -6
- package/dist/prerender-render.mjs +1 -1
- package/dist/{project-cmd-CpLYflCH.mjs → project-cmd-DA4K5faU.mjs} +18 -18
- package/dist/{project-team-CTSY7W7Q.mjs → project-team-CQBNveX3.mjs} +4 -4
- package/dist/{project-token-Cybtlfbl.mjs → project-token-CCalFyQf.mjs} +4 -4
- package/dist/{project-zero-trust-DKOtqyvu.mjs → project-zero-trust-DPnkiEuT.mjs} +4 -4
- package/dist/{protocol-B8sI8dRD.d.mts → protocol-BBDpJz3G.d.mts} +3 -1
- package/dist/{protocol-RNP0u7UY.mjs → protocol-DimNxVRr.mjs} +42 -17
- package/dist/{provision-UcYjpTRh.mjs → provision-BFLCrnUp.mjs} +1 -1
- package/dist/{provision-BuifYvLc.mjs → provision-DtROpNNI.mjs} +6 -5
- package/dist/{requests-Ryiazz9i.mjs → requests-BpH0kImL.mjs} +3 -3
- package/dist/resolve-project-DD1vu8s9.mjs +1 -0
- package/dist/{resolve-project-WpwkCB_c.mjs → resolve-project-Xoi6eTWY.mjs} +1 -1
- package/dist/{rollback-Or0EV3rP.mjs → rollback-CXS1tAa-.mjs} +10 -9
- package/dist/{rolldown-runtime-DXIUcv95.mjs → rolldown-runtime-3h0WiCiA.mjs} +1 -1
- package/dist/{routing-assets-VkrKIbxE.mjs → routing-assets-DdSFxoLp.mjs} +10 -4
- package/dist/runtime/better-auth-mysql.d.mts +1 -1
- package/dist/runtime/better-auth-pg.d.mts +1 -1
- package/dist/runtime/better-auth.d.mts +1 -1
- package/dist/runtime/client-react.d.mts +2 -2
- package/dist/runtime/client-react.mjs +3 -3
- package/dist/runtime/client-solid.d.mts +2 -2
- package/dist/runtime/client-solid.mjs +3 -3
- package/dist/runtime/client-svelte.d.mts +2 -2
- package/dist/runtime/client-svelte.mjs +3 -3
- package/dist/runtime/client-vue.d.mts +2 -2
- package/dist/runtime/client-vue.mjs +3 -3
- package/dist/runtime/client.d.mts +2 -2
- package/dist/runtime/client.mjs +3 -3
- package/dist/runtime/email/testing.mjs +4 -4
- package/dist/runtime/email.mjs +3 -3
- package/dist/runtime/env-public.d.mts +1 -1
- package/dist/runtime/fetch-server.d.mts +8 -0
- package/dist/runtime/fetch-server.mjs +30 -0
- package/dist/runtime/fetch-stream.d.mts +2 -2
- package/dist/runtime/fetch-stream.mjs +2 -2
- package/dist/runtime/fetch.d.mts +2 -2
- package/dist/runtime/fetch.mjs +3 -3
- package/dist/runtime/handler.d.mts +1 -1
- package/dist/runtime/live-client.d.mts +1 -1
- package/dist/runtime/live.d.mts +1 -1
- package/dist/runtime/routing.d.mts +17 -76
- package/dist/runtime/routing.mjs +277 -216
- package/dist/runtime/validator.d.mts +1 -1
- package/dist/runtime/ws-server.d.mts +2 -2
- package/dist/runtime/ws.d.mts +1 -1
- package/dist/{scan-D2-FcLHs.mjs → scan-8IzYXiU_.mjs} +12 -11
- package/dist/{secret-CClHaFxm.mjs → secret-BbXHfLAt.mjs} +6 -6
- package/dist/serialize-DtR8Vwa_.d.mts +18 -0
- package/dist/serialize-YHvlcG3Q.mjs +31 -0
- package/dist/{skills-C_yRTg0g.mjs → skills-CrUO3PJ0.mjs} +1 -1
- package/dist/{subcommand-prompt-Bm3ZlYgA.mjs → subcommand-prompt-D9OhhbGG.mjs} +2 -2
- package/dist/{version-metadata-B1Bm2wSq.mjs → version-metadata-tS39iW09.mjs} +1 -1
- package/dist/wrangler-BLb3_luP.mjs +1 -0
- package/dist/{wrangler-C9XSzrju.mjs → wrangler-F96NnLyb.mjs} +13 -5
- package/dist/{wrapper-BOigE1xJ.mjs → wrapper-D3sZyh2C.mjs} +5 -3
- package/dist/{ws-CsBNxv86.d.mts → ws-BH-im_tX.d.mts} +1 -1
- package/docs/guide/ai.md +1 -1
- package/docs/guide/database/postgresql.md +1 -1
- package/docs/guide/database.md +26 -0
- package/docs/guide/deployment.md +2 -0
- package/docs/guide/edge/headers.md +1 -1
- package/docs/guide/email/domains.md +1 -1
- package/docs/guide/email/receiving.md +1 -1
- package/docs/guide/email/sending.md +3 -3
- package/docs/guide/pages-routing/actions-and-forms.md +1 -1
- package/docs/guide/pages-routing/markdown.md +3 -1
- package/docs/guide/platform/administration/email.md +7 -1
- package/docs/guide/platform/installation/ci.md +2 -0
- package/docs/guide/platform/installation/credentials.md +24 -6
- package/docs/guide/platform/installation/domains.md +16 -0
- package/docs/guide/platform/installation/maintenance.md +32 -2
- package/docs/guide/platform/installation/setup.md +1 -1
- package/docs/guide/self-hosted-platform.md +1 -1
- package/docs/guide/server-routing.md +1 -1
- package/docs/guide/typed-fetch.md +1 -1
- package/docs/integrations/cloudflare.md +2 -1
- package/docs/integrations/frameworks/tanstack-start.md +25 -0
- package/docs/reference/api/deploy.md +35 -3
- package/docs/reference/api/env.md +1 -1
- package/docs/reference/api/rewrites.md +1 -1
- package/docs/reference/api/types.md +1 -0
- package/docs/reference/cli/database.md +15 -0
- package/docs/reference/cli/platform-installation.md +5 -2
- package/docs/reference/cli/project.md +4 -2
- package/docs/reference/cli/setup.md +17 -0
- package/docs/reference/cli.md +1 -0
- package/docs/reference/config.md +14 -1
- package/docs/reference/resource-inference.md +4 -0
- package/package.json +23 -22
- package/sandbox.Dockerfile +1 -1
- package/schema.json +23 -3
- package/dist/cf-build-output-BU3hqdHu.mjs +0 -1
- package/dist/client-DrdeJgHR.mjs +0 -1
- package/dist/deploy-CxNgEYr4.mjs +0 -2
- package/dist/fetch-sAcwjzLO.mjs +0 -18
- package/dist/gen-DsCukhuw.mjs +0 -1
- package/dist/help-DOFygYi4.mjs +0 -1
- package/dist/login-alAlxw4S.mjs +0 -1
- package/dist/migrate-D2odrAmh.mjs +0 -1
- package/dist/operator-cmd-D7toMPJ9.mjs +0 -1
- package/dist/platform-args-B_LrqEZB.mjs +0 -1
- package/dist/prepare-EVEbQ9n2.mjs +0 -1
- package/dist/resolve-project-PnhzMtY6.mjs +0 -1
- package/dist/serialize-BtQyaYoC.mjs +0 -20
- package/dist/serialize-CfSwWfF2.d.mts +0 -10
- 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-
|
|
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-
|
|
8
|
-
import { l as resolveCloudflareOperationArgv } from "./cloudflare-process-
|
|
9
|
-
import { o as createDefaultRunWrangler, v as resolvePinnedApiBaseUrl } from "./provision-
|
|
10
|
-
import { t as parsePlatformArgs } from "./platform-args-
|
|
11
|
-
import { o as runCloudflareAuthCommand } from "./cloudflare-auth-
|
|
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-
|
|
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
|
|
2454
|
+
const existingWithCustomDomains = existing && deployment.capabilities.customDomains && !existing.deployment.capabilities.customDomains ? {
|
|
2388
2455
|
...existingWithDashboard,
|
|
2389
2456
|
capabilities: {
|
|
2390
2457
|
...existingWithDashboard.capabilities,
|
|
2391
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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("
|
|
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-
|
|
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 {
|
|
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 };
|