void 0.20.0 → 0.20.2
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/README.md +5 -1
- package/dist/{auth-W9WII-mN.mjs → auth-DPl6kck4.mjs} +46 -26
- package/dist/{auth-cmd-CYVhSNFy.mjs → auth-cmd-gniL2fNt.mjs} +5 -4
- package/dist/auth-link-NZdjCmSc.mjs +28 -0
- package/dist/{build-cmd-CkVD1uOh.mjs → build-cmd-sI18tX_O.mjs} +3 -3
- package/dist/{cache-QcUfR-ff.mjs → cache-IHn5MwBC.mjs} +3 -3
- package/dist/{cancel-deploy-DsvWKFTe.mjs → cancel-deploy-C5qTOdLi.mjs} +3 -3
- package/dist/cf-access-DRsQRe6k.mjs +75 -0
- package/dist/cli/cli.mjs +364 -1883
- package/dist/cli/env-schema-probe.mjs +11 -2
- package/dist/{client-CyCHWSO_.mjs → client-dHfSJvAN.mjs} +336 -58
- package/dist/{cloudflare-auth-DRkGe7-s.mjs → cloudflare-auth-6M5llVPC.mjs} +53 -6
- package/dist/{cloudflare-cmd-D3ME2GAe.mjs → cloudflare-cmd-4RPGN3KB.mjs} +2 -2
- package/dist/{cloudflare-connect-BivMefMA.mjs → cloudflare-connect-t1UU5svD.mjs} +2 -2
- package/dist/{cloudflare-operations-CPTpRW6d.mjs → cloudflare-operations-BzWnlC1_.mjs} +1 -1
- package/dist/{config-BQFq7QvD.mjs → config-uNGuFsI2.mjs} +1 -1
- package/dist/{connect-D9Yr-T5f.mjs → connect-Bfk31O_8.mjs} +6 -6
- package/dist/{create-project-DMV-csEm.mjs → create-project-Bk9Z0-Jg.mjs} +6 -5
- package/dist/{db-BxoUWL0F.mjs → db-BkRoptAt.mjs} +75 -48
- package/dist/{delete-iJOqxBz1.mjs → delete-DouASY9P.mjs} +3 -3
- package/dist/{deploy-CcDoeBIf.mjs → deploy-DTaWUS1S.mjs} +137 -128
- package/dist/{dev-inbox-DkgRWLkW.mjs → dev-inbox-P0u4tM8Y.mjs} +1 -1
- package/dist/{domain-gu_iaHmn.mjs → domain-1RhhOVrC.mjs} +4 -4
- package/dist/email-C-lGh51B.mjs +795 -0
- package/dist/{env-D4Emu-M_.mjs → env-DBKmK4vc.mjs} +1 -0
- package/dist/{env-DmU2To0C.mjs → env-DJHsPE7Z.mjs} +5 -5
- package/dist/{env-validation-ENpMy6Ez.mjs → env-validation-CF6KvTRf.mjs} +3 -1
- package/dist/{gen-BKw6qHIg.mjs → gen-B_wPnVTK.mjs} +3 -3
- package/dist/generate-RTK8_kK1.mjs +47 -0
- package/dist/{github-cmd-DJS5Ab-H.mjs → github-cmd-PW7ZnWTp.mjs} +3 -3
- package/dist/{headers-D8QfRX9Y.mjs → headers-BAHwgHdW.mjs} +1 -1
- package/dist/help-CwOX-zmI.mjs +2216 -0
- package/dist/{inbound-BJ70in1n.d.mts → inbound-CH5Mksyy.d.mts} +37 -42
- package/dist/{inbound-CNKxb3FY.mjs → inbound-aVHEUhKo.mjs} +143 -101
- package/dist/index.mjs +69 -30
- package/dist/{init-Dx5cgKqK.mjs → init-BWZ7q5Z4.mjs} +11 -11
- package/dist/{link-D_rm5sRH.mjs → link-Rmvu2Wl_.mjs} +4 -4
- package/dist/{list-M8XShti7.mjs → list-DEE2S6mY.mjs} +4 -4
- package/dist/{local-d1-BE8KBbMy.mjs → local-d1-D2I6Ox5F.mjs} +1 -1
- package/dist/{login-C8-UuLnp.mjs → login-Uvferzmm.mjs} +28 -10
- package/dist/{logs-DwFU7dMW.mjs → logs-27FenuiC.mjs} +4 -4
- package/dist/{mime-BJD7d_qL.mjs → mime-D5Nmdzf7.mjs} +23 -9
- package/dist/{node-BkaXcpAc.mjs → node-Ez5KW5rn.mjs} +3 -3
- package/dist/operator-auth-B3e08unv.mjs +52 -0
- package/dist/operator-client-LUZnlnYk.mjs +82 -0
- package/dist/{operator-cmd-DYWRbWUA.mjs → operator-cmd-CjOTmAYE.mjs} +35 -55
- package/dist/{output-tFQLLj26.mjs → output-B0cfNSx5.mjs} +316 -2
- package/dist/pages/index.mjs +2 -2
- package/dist/platform-auth-config-DrbQXXiW.mjs +368 -0
- package/dist/platform-auth-protection-Bhtvp0B_.mjs +219 -0
- package/dist/platform-auth-recovery-CeOKGVeJ.mjs +310 -0
- package/dist/{platform-cmd-BEOVv1PN.mjs → platform-cmd-BFhieCdV.mjs} +16 -6
- package/dist/{platform-domain-BszUfiS8.mjs → platform-domain-C74PULqV.mjs} +4 -4
- package/dist/{platform-lifecycle-CjSr6xf0.mjs → platform-lifecycle-BwAIgz-t.mjs} +1667 -191
- package/dist/{platform-management-W30iCaTL.mjs → platform-management-COogu_Se.mjs} +33 -7
- package/dist/{platform-recovery-pHHv4ZSg.mjs → platform-recovery-ewqLefp1.mjs} +6 -5
- package/dist/{prepare-CBetXvsN.mjs → prepare-CtDJjoOj.mjs} +2 -2
- package/dist/{prepare-BfJvFUtJ.mjs → prepare-blNRQvQl.mjs} +2 -2
- package/dist/prerender-render.d.mts +11 -0
- package/dist/prerender-render.mjs +111 -0
- package/dist/{project-cmd-DCpk1cDt.mjs → project-cmd-DmZK9Hxf.mjs} +32 -14
- package/dist/project-team-D8jOJMUJ.mjs +130 -0
- package/dist/project-token-DA34bf-C.mjs +75 -0
- package/dist/{provision-Blnstcm2.mjs → provision-CSJOjjQk.mjs} +2 -0
- package/dist/{requests-Dn8Vheh1.mjs → requests-CUExwGQQ.mjs} +3 -3
- package/dist/{rollback-CtlPXEBi.mjs → rollback-CDNGU1gr.mjs} +4 -4
- package/dist/{runner-mysql-CgRFl3s6.mjs → runner-mysql-7BPUNGmL.mjs} +1 -1
- package/dist/{runner-pg-DCkWPsWS.mjs → runner-pg-BkEza-dX.mjs} +1 -1
- package/dist/runtime/ai.mjs +3 -2
- package/dist/runtime/email/testing.d.mts +1 -1
- package/dist/runtime/email/testing.mjs +3 -3
- package/dist/runtime/email-protocol.d.mts +15 -0
- package/dist/runtime/email-protocol.mjs +70 -0
- package/dist/runtime/email.d.mts +2 -2
- package/dist/runtime/email.mjs +189 -96
- package/dist/runtime/remote/index.mjs +5 -3
- package/dist/{secret-BqTxGqki.mjs → secret-Bzzi2e9E.mjs} +5 -5
- package/dist/{skills-Q46GZMO-.mjs → skills-C0RvGjeE.mjs} +1 -1
- package/dist/sqlite-validation-BzKMWnO4.mjs +25 -0
- package/dist/{subcommand-prompt-WfySCQ7S.mjs → subcommand-prompt-Bmyn5Rlc.mjs} +1 -1
- package/dist/{validate-Bihr8WBi.mjs → validate-tBBN_dXH.mjs} +1 -0
- package/package.json +12 -7
- package/skills/void/SKILL.md +35 -4
- package/skills/void/docs/guide/deployment.md +2 -0
- package/skills/void/docs/guide/email.md +121 -98
- package/skills/void/docs/guide/platform-administration.md +214 -2
- package/skills/void/docs/guide/platform-development.md +93 -2
- package/skills/void/docs/guide/project-collaboration.md +94 -0
- package/skills/void/docs/guide/self-hosted-platform.md +184 -30
- package/skills/void/docs/reference/cli.md +294 -15
- package/dist/cf-access-AJ1ehiFR.mjs +0 -42
- package/dist/cf-access-DsSsZUPr.mjs +0 -67
- package/dist/email-r6DHJyAB.mjs +0 -262
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { A as outro, D as log, I as isCancel, J as listInstallationManifests, M as select, W as acquirePlatformLifecycleLock, et as readInstallationRecoverySecrets, i as createSpinner, it as resolveInstallationManifest, k as note, nt as removeInstallationRecoverySecrets, rt as removePlatformConnectionForInstallation, st as writeInstallationManifest, w as confirm } from "./output-B0cfNSx5.mjs";
|
|
2
|
+
import { C as PlatformCloudflareClient, D as verifyPlatformRuntimeTokenPermissions, E as hyperdriveOriginMatches, T as hyperdriveOriginFromConnectionString, _ as revalidatePlatformInstallAction, a as createPlatformInstallationSecrets, assertNoninteractivePlatformMutationAcknowledged, f as trackedPlatformWorkerTraffic, g as platformProvisionIntentHash, h as platformAdoptionIntentHash, m as formatPlatformInstallPlan, n as acquirePlatformDatabaseLifecycleLock, o as installPlatform, p as createPlatformInstallPlan, resolvePlatformInstallDatabaseUrl, s as loadPlatformRuntime, t as REMOTE_INSTALLATION_MANIFEST_KEY, u as resolveBootstrapGitHubId, v as assertPlatformOperatorCredentialCanApplyPlan, w as hyperdriveOriginConfigHash, y as resolvePlatformCloudflareCredential } from "./platform-lifecycle-BwAIgz-t.mjs";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import { PLATFORM_DISABLED_KEY, PLATFORM_DISABLED_VALUE, PLATFORM_R2_OWNERSHIP_MARKER_KEY, createPlatformResourceOwnershipMarker, parsePlatformInstallationManifest, parsePlatformResourceOwnershipMarker, recordInstallationResource, recordInstallationStep, setInstallationDesiredState, transitionInstallation } from "@void/platform";
|
|
5
5
|
//#region src/cli/platform-management.ts
|
|
@@ -59,6 +59,8 @@ async function runInstalledPlatformCommand(args) {
|
|
|
59
59
|
const deployment = manifest.deployment;
|
|
60
60
|
const planInput = {
|
|
61
61
|
accountId: manifest.account.id,
|
|
62
|
+
...args.subcommand === "upgrade" && process.env.VOID_EMAIL_SHARED_ZONE_ID ? { emailSharedZoneId: process.env.VOID_EMAIL_SHARED_ZONE_ID } : {},
|
|
63
|
+
...args.subcommand === "upgrade" && process.env.VOID_EMAIL_SENDER_DOMAIN ? { emailSenderDomain: process.env.VOID_EMAIL_SENDER_DOMAIN } : {},
|
|
62
64
|
applicationDomain: deployment.application.baseDomain,
|
|
63
65
|
controlPlaneDomain: deployment.controlPlane.apiHostname ?? "",
|
|
64
66
|
dedicatedZone: deployment.application.dedicatedZone,
|
|
@@ -69,7 +71,8 @@ async function runInstalledPlatformCommand(args) {
|
|
|
69
71
|
const plan = await createPlatformInstallPlan(client, planInput, manifest, {
|
|
70
72
|
authoritativeProvisionIntents: databaseCheckpointExists,
|
|
71
73
|
repairMissing: args.subcommand === "repair",
|
|
72
|
-
allowUnverifiedAiGateway: args.plan
|
|
74
|
+
allowUnverifiedAiGateway: args.plan,
|
|
75
|
+
includeEmailGateway: runtime.manifest.artifacts.emailGateway !== void 0
|
|
73
76
|
});
|
|
74
77
|
note(formatPlatformInstallPlan(plan), args.subcommand === "upgrade" ? "Upgrade plan" : args.subcommand === "rollback" ? "Rollback plan" : args.subcommand === "enable" ? "Enable plan" : "Repair plan");
|
|
75
78
|
note([
|
|
@@ -103,6 +106,9 @@ async function runInstalledPlatformCommand(args) {
|
|
|
103
106
|
cloudflareApiToken: process.env.VOID_PLATFORM_RUNTIME_CLOUDFLARE_API_TOKEN || "",
|
|
104
107
|
checkpoint: recovery,
|
|
105
108
|
databaseUrl,
|
|
109
|
+
emailEnabled: plan.deployment.capabilities.email,
|
|
110
|
+
emailSigningSecret: process.env.VOID_PLATFORM_EMAIL_SIGNING_SECRET || "",
|
|
111
|
+
existingEmailEnabled: manifest.deployment.capabilities.email,
|
|
106
112
|
existingInstallation: true,
|
|
107
113
|
updateExistingWorkers: true,
|
|
108
114
|
githubClientId: process.env.VOID_PLATFORM_GITHUB_CLIENT_ID || "",
|
|
@@ -121,11 +127,13 @@ async function runInstalledPlatformCommand(args) {
|
|
|
121
127
|
customDomains: plan.deployment.capabilities.customDomains,
|
|
122
128
|
zoneId: plan.zone?.id ?? null
|
|
123
129
|
});
|
|
130
|
+
if (!databaseLock?.readAuthenticationState) throw new Error("platform: The database lifecycle lock cannot read authoritative authentication settings.");
|
|
124
131
|
const result = await installPlatform(plan, secrets, installation, {
|
|
125
132
|
acquireDatabaseLifecycleLock: async () => Object.assign(async () => {}, {
|
|
126
133
|
checkpoint: databaseLock?.checkpoint,
|
|
127
134
|
prepareDatabase: databaseLock?.prepareDatabase,
|
|
128
135
|
prepareSandboxDrain: databaseLock?.prepareSandboxDrain,
|
|
136
|
+
readAuthenticationState: databaseLock?.readAuthenticationState,
|
|
129
137
|
requiredSafetyProtocols: databaseLock?.requiredSafetyProtocols,
|
|
130
138
|
verifyDatabase: databaseLock?.verifyDatabase
|
|
131
139
|
}),
|
|
@@ -135,7 +143,8 @@ async function runInstalledPlatformCommand(args) {
|
|
|
135
143
|
runtime,
|
|
136
144
|
replanWorkers: async (recoveryManifest) => (await createPlatformInstallPlan(client, planInput, recoveryManifest, {
|
|
137
145
|
authoritativeProvisionIntents: true,
|
|
138
|
-
repairMissing: true
|
|
146
|
+
repairMissing: true,
|
|
147
|
+
includeEmailGateway: runtime.manifest.artifacts.emailGateway !== void 0
|
|
139
148
|
})).actions.filter((action) => action.kind === "worker"),
|
|
140
149
|
revalidateBeforeWorkerDeploy: (manifest, action) => revalidatePlatformInstallAction(client, plan, manifest, action, { repairMissing: args.subcommand === "repair" })
|
|
141
150
|
});
|
|
@@ -152,6 +161,14 @@ async function runInstalledPlatformCommand(args) {
|
|
|
152
161
|
return;
|
|
153
162
|
}
|
|
154
163
|
log.success(args.subcommand === "rollback" ? `${deployment.displayName} is running compatible runtime ${runtime.manifest.platformVersion}; PostgreSQL remains at schema ${rollbackSourceRuntime.manifest.database.schemaDigest}.` : `${deployment.displayName} is healthy at ${plan.apiUrl}.`);
|
|
164
|
+
if (result.emailOperationId) log.info(`Shared email routing ready (operation ${result.emailOperationId}).`);
|
|
165
|
+
if (result.setupPending) {
|
|
166
|
+
const code = readInstallationRecoverySecrets(result.manifest)?.authBootstrapCode;
|
|
167
|
+
if (code) {
|
|
168
|
+
log.info(`First administrator setup: ${plan.apiUrl}/setup`);
|
|
169
|
+
console.log(`One-time administrator setup code: ${code}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
155
172
|
};
|
|
156
173
|
};
|
|
157
174
|
if (args.plan) {
|
|
@@ -189,9 +206,12 @@ async function runInstalledPlatformCommand(args) {
|
|
|
189
206
|
}
|
|
190
207
|
}
|
|
191
208
|
function assertPlatformRollbackCompatibility(installation, target, source) {
|
|
209
|
+
if (target.manifest.emailProtocolVersion !== 1 && (installation.deployment.capabilities.email || installation.resources.some((resource) => resource.key === "email-gateway-worker" && resource.state === "ready"))) throw new Error("platform: The rollback target predates the email gateway and cannot safely replace this installation. Use a runtime with email protocol version 1.");
|
|
192
210
|
if (!installation.runtime) throw new Error("platform: The installed runtime has no artifact provenance, so post-success rollback cannot identify its compatibility manifest.");
|
|
193
211
|
if (source.manifest.platformVersion !== installation.platformVersion || source.manifestSha256 !== installation.runtime.manifestSha256) throw new Error("platform: --from-runtime does not exactly match the installed runtime artifact digest. Supply the runtime used by the current successful deployment.");
|
|
194
212
|
if (target.manifestSha256 === source.manifestSha256) throw new Error("platform: The rollback target is already the installed runtime artifact.");
|
|
213
|
+
if ((target.manifest.authenticationProtocolVersion ?? 0) < Math.max(source.manifest.authenticationProtocolVersion ?? 0, installation.deployment.authentication.configurable ? 1 : 0)) throw new Error("platform: The rollback target cannot enforce the installed authentication configuration and session revocation protocol. Use a compatible runtime.");
|
|
214
|
+
if (Math.max(installation.usageProtocolVersion, source.manifest.usageProtocolVersion) >= 2 && target.manifest.usageProtocolVersion < 2) throw new Error("platform: The rollback target predates ownership-aware usage attribution and cannot safely account for transferred projects. Use a runtime with usage protocol version 2 or newer.");
|
|
195
215
|
if (target.manifest.database.lineage !== source.manifest.database.lineage) throw new Error("platform: The rollback target belongs to a different database lineage.");
|
|
196
216
|
const prefix = source.manifest.database.migrations.slice(0, target.manifest.database.migrations.length);
|
|
197
217
|
if (JSON.stringify(prefix) !== JSON.stringify(target.manifest.database.migrations)) throw new Error("platform: The rollback target database migrations are not an exact prefix of the installed runtime.");
|
|
@@ -202,7 +222,7 @@ function assertPlatformRollbackCompatibility(installation, target, source) {
|
|
|
202
222
|
function resolvePlatformRuntimeTokenToPreflight(plan, secrets) {
|
|
203
223
|
const explicitUpdate = secrets.workerSecretUpdates?.CF_API_TOKEN;
|
|
204
224
|
if (explicitUpdate) return explicitUpdate;
|
|
205
|
-
return plan.actions.some((action) => action.kind === "worker" && action.action === "create") ? secrets.cloudflareApiToken : "";
|
|
225
|
+
return plan.actions.some((action) => action.kind === "worker" && action.action === "create" && (action.key === "api-worker" || action.key === "proxy-worker")) ? secrets.cloudflareApiToken : "";
|
|
206
226
|
}
|
|
207
227
|
async function resolveInstalledPlatformManifest(selector, dependencies = {}) {
|
|
208
228
|
if (selector) return resolveInstallationManifest(selector);
|
|
@@ -275,7 +295,7 @@ async function uninstallPlatform(client, initialManifest, args, dependencies = {
|
|
|
275
295
|
const confirmAction = dependencies.confirm ?? promptForConfirmation;
|
|
276
296
|
if (createdZone && args.deleteZone === true) throw new Error(`platform: Refusing to delete Cloudflare zone ${JSON.stringify(createdZone.name)} automatically. A zone can contain unrelated DNS, settings, and products that the installer cannot exhaustively prove ownership of. Uninstall with --keep-zone, inspect the retained zone in Cloudflare, and delete it manually if appropriate.`);
|
|
277
297
|
const dataMessage = retainData ? "Retain installer-owned Workers, storage, dispatch, AI logs, and Hyperdrive by default." : args.purgeData ? "Delete safely identifiable installer-owned data (--purge-data); retain Workers, R2, AI Gateway, and external PostgreSQL for manual cleanup." : "Retain installer-owned data; external PostgreSQL is never deleted.";
|
|
278
|
-
note(`Disable all platform traffic through installation-owned routing storage.\nRetain DNS records, routes, and custom domains for explicit dashboard cleanup.\nDelete installer-owned Queues.\n${dataMessage}\n${createdZone ? `Keep zone ${createdZone.name} for explicit manual cleanup.` : "Never delete the adopted application zone."}`, "Uninstall plan");
|
|
298
|
+
note(`Disable all platform traffic through installation-owned routing storage.\nRetain DNS records, routes, and custom domains for explicit dashboard cleanup.\nDelete installer-owned Queues.\n${dataMessage}\n${createdZone ? `Keep zone ${createdZone.name} for explicit manual cleanup.` : "Never delete the adopted application zone."}${(manifest.authenticationResources ?? []).length ? "\nRetain Access resources for deliberate cleanup:\n" + manifest.authenticationResources.map((resource) => ` ${resource.ownership} ${resource.kind}: ${resource.name} (${resource.id}, account ${resource.accountId})`).join("\n") : ""}`, "Uninstall plan");
|
|
279
299
|
if (args.plan) {
|
|
280
300
|
outro("No Cloudflare or local installation state was changed.");
|
|
281
301
|
return;
|
|
@@ -397,6 +417,11 @@ async function recoverDurablyMarkedProvisionIntents(client, manifest) {
|
|
|
397
417
|
"worker",
|
|
398
418
|
manifest.deployment.resources.proxyWorker
|
|
399
419
|
],
|
|
420
|
+
[
|
|
421
|
+
"email-gateway-worker",
|
|
422
|
+
"worker",
|
|
423
|
+
manifest.deployment.resources.emailGatewayWorker
|
|
424
|
+
],
|
|
400
425
|
[
|
|
401
426
|
"tail-worker",
|
|
402
427
|
"worker",
|
|
@@ -596,7 +621,8 @@ function isPlatformManifestMonotonicExtension(earlier, later) {
|
|
|
596
621
|
return next?.id === resource.id && next.kind === resource.kind && next.name === resource.name && next.ownership === resource.ownership && (next.state === resource.state || resource.state === "ready" && (next.state === "deleted" || next.state === "retained"));
|
|
597
622
|
});
|
|
598
623
|
const stepsAdvance = earlier.steps.every((step) => later.steps.some((entry) => entry.key === step.key && entry.completedAt === step.completedAt && entry.resultHash === step.resultHash));
|
|
599
|
-
|
|
624
|
+
const authenticationResourcesAdvance = (earlier.authenticationResources ?? []).every((resource) => (later.authenticationResources ?? []).some((next) => JSON.stringify(next) === JSON.stringify(resource)));
|
|
625
|
+
return resourcesAdvance && stepsAdvance && authenticationResourcesAdvance;
|
|
600
626
|
}
|
|
601
627
|
function hasNamedResource(entries, resource) {
|
|
602
628
|
return entries.some((entry) => entry.id === resource.id && entry.name === resource.name);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as cfAccessHeaders } from "./cf-access-
|
|
3
|
-
import {
|
|
4
|
-
import { isPlatformManifestMonotonicExtension, recoverDurablyMarkedProvisionIntents, verifyPlatformInstallationOwnership } from "./platform-management-
|
|
1
|
+
import { J as listInstallationManifests, W as acquirePlatformLifecycleLock, at as savePlatformConnection, st as writeInstallationManifest } from "./output-B0cfNSx5.mjs";
|
|
2
|
+
import { t as cfAccessHeaders } from "./cf-access-DRsQRe6k.mjs";
|
|
3
|
+
import { p as createPlatformInstallPlan, t as REMOTE_INSTALLATION_MANIFEST_KEY } from "./platform-lifecycle-BwAIgz-t.mjs";
|
|
4
|
+
import { isPlatformManifestMonotonicExtension, recoverDurablyMarkedProvisionIntents, verifyPlatformInstallationOwnership } from "./platform-management-COogu_Se.mjs";
|
|
5
5
|
import { parsePlatformInstallationManifest, setInstallationEndpoints } from "@void/platform";
|
|
6
6
|
//#region src/cli/platform-recovery.ts
|
|
7
7
|
async function recoverPlatformInstallations(client, accountId, verifyEndpoint = verifyInstallationEndpoint, selector = "") {
|
|
@@ -90,7 +90,8 @@ async function verifyInstallationEndpoint(manifest, fetcher = fetch) {
|
|
|
90
90
|
Accept: "application/json",
|
|
91
91
|
...cfAccessHeaders(process.env, apiUrl)
|
|
92
92
|
},
|
|
93
|
-
signal: AbortSignal.timeout(15e3)
|
|
93
|
+
signal: AbortSignal.timeout(15e3),
|
|
94
|
+
redirect: "manual"
|
|
94
95
|
});
|
|
95
96
|
if (!response.ok) throw new Error(`platform: Installation ${manifest.id} is not reachable (HTTP ${response.status}).`);
|
|
96
97
|
if ((await response.json()).installationId !== manifest.id) throw new Error(`platform: Installation ${manifest.id} endpoint identity does not match.`);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { o as join } from "./dist-BrsS7cai.mjs";
|
|
2
2
|
import { i as resolveProjectPaths } from "./project-paths-SK8nMHPp.mjs";
|
|
3
3
|
import { c as loadVoidAuthConfig, r as scanRoutes, s as findVoidAuthConfig } from "./scan-BMH4rzlv.mjs";
|
|
4
|
-
import { c as getDatabaseDialect, f as readConfig } from "./config-
|
|
4
|
+
import { c as getDatabaseDialect, f as readConfig } from "./config-uNGuFsI2.mjs";
|
|
5
5
|
import { n as ensureProjectTsconfig, r as writeFileIfChanged } from "./project-tsconfig-Ql2XsSQp.mjs";
|
|
6
6
|
import { i as inferProjectBindings, n as FRAMEWORK_SCAN_DIRS, r as detectFramework } from "./plugin-inference-BDRfZngg.mjs";
|
|
7
7
|
import { i as generateVoidAuthDrizzleSchema, n as generateQueueTypes, r as generateVoidAuthDatabaseModule, s as generateDbDts, t as generateRouteTypes } from "./route-types-Da-DpyUp.mjs";
|
|
8
8
|
import { t as discoverSchema } from "./discover-xvfrgJeo.mjs";
|
|
9
9
|
import { r as generateEnvTypes, t as findEnvFile } from "./env-types-BNPhro-M.mjs";
|
|
10
|
-
import { a as loadLocalEnv } from "./env-
|
|
10
|
+
import { a as loadLocalEnv } from "./env-DBKmK4vc.mjs";
|
|
11
11
|
import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
//#region src/codegen/prepare.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { prepareProject } from "./prepare-
|
|
1
|
+
import { A as outro, D as log, E as intro, k as note, r as cliTitle } from "./output-B0cfNSx5.mjs";
|
|
2
|
+
import { prepareProject } from "./prepare-CtDJjoOj.mjs";
|
|
3
3
|
//#region src/cli/prepare.ts
|
|
4
4
|
async function runPrepareCommand(root, _args) {
|
|
5
5
|
console.log();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/prerender-render.d.ts
|
|
2
|
+
export type PrerenderResult = {
|
|
3
|
+
rendered: Array<string>;
|
|
4
|
+
failed: Array<{
|
|
5
|
+
path: string;
|
|
6
|
+
error: string;
|
|
7
|
+
}>;
|
|
8
|
+
};
|
|
9
|
+
/** Shared renderer for local Workers and the isolated Node prerender child. */
|
|
10
|
+
export declare function renderPrerenderPaths(paths: Array<string>, clientDir: string, fetchPath: (path: string) => Promise<Response>, fetchPageData?: (path: string) => Promise<Response>, onResult?: (path: string, outputFile: string, error?: string) => void): Promise<PrerenderResult>;
|
|
11
|
+
//#endregion
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { n as dirname, o as join } from "./dist-BrsS7cai.mjs";
|
|
2
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
+
//#region src/prerender-render.ts
|
|
4
|
+
function outputFileForPath(path) {
|
|
5
|
+
return path === "/" ? "index.html" : `${path.slice(1)}.html`;
|
|
6
|
+
}
|
|
7
|
+
function pageDataFileForPath(path) {
|
|
8
|
+
return path === "/" ? "_void/pages/index.json" : `_void/pages/${path.slice(1)}.json`;
|
|
9
|
+
}
|
|
10
|
+
function normalizedFile(clientDir, path) {
|
|
11
|
+
return join(clientDir, path).normalize("NFC").toLowerCase();
|
|
12
|
+
}
|
|
13
|
+
function generatedFiles(path, clientDir) {
|
|
14
|
+
return [outputFileForPath(path), pageDataFileForPath(path)].map((file) => normalizedFile(clientDir, file));
|
|
15
|
+
}
|
|
16
|
+
function requestAssetCandidates(path, clientDir) {
|
|
17
|
+
const relativePath = path === "/" ? "" : path.slice(1);
|
|
18
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
19
|
+
const addCandidates = (candidate) => {
|
|
20
|
+
candidates.add(candidate);
|
|
21
|
+
if (candidate === "" || candidate.endsWith("/")) candidates.add(`${candidate}index.html`);
|
|
22
|
+
else if (!candidate.split("/").at(-1)?.includes(".")) {
|
|
23
|
+
candidates.add(`${candidate}.html`);
|
|
24
|
+
candidates.add(`${candidate}/index.html`);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
addCandidates(relativePath);
|
|
28
|
+
try {
|
|
29
|
+
addCandidates(decodeURI(relativePath));
|
|
30
|
+
} catch {}
|
|
31
|
+
try {
|
|
32
|
+
addCandidates(decodeURIComponent(relativePath));
|
|
33
|
+
} catch {}
|
|
34
|
+
return new Set([...candidates].map((file) => normalizedFile(clientDir, file)));
|
|
35
|
+
}
|
|
36
|
+
function renderConcurrency(paths, clientDir) {
|
|
37
|
+
const outputs = paths.map((path) => generatedFiles(path, clientDir));
|
|
38
|
+
const ownerByOutput = /* @__PURE__ */ new Map();
|
|
39
|
+
for (let index = 0; index < outputs.length; index++) for (const output of outputs[index]) {
|
|
40
|
+
if (ownerByOutput.has(output)) return 1;
|
|
41
|
+
ownerByOutput.set(output, index);
|
|
42
|
+
}
|
|
43
|
+
for (const file of ownerByOutput.keys()) {
|
|
44
|
+
let parent = dirname(file);
|
|
45
|
+
while (parent !== dirname(parent)) {
|
|
46
|
+
if (ownerByOutput.has(parent)) return 1;
|
|
47
|
+
parent = dirname(parent);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
for (let index = 0; index < paths.length; index++) for (const candidate of requestAssetCandidates(paths[index], clientDir)) {
|
|
51
|
+
const owner = ownerByOutput.get(candidate);
|
|
52
|
+
if (owner !== void 0 && owner !== index) return 1;
|
|
53
|
+
}
|
|
54
|
+
return 4;
|
|
55
|
+
}
|
|
56
|
+
/** Shared renderer for local Workers and the isolated Node prerender child. */
|
|
57
|
+
async function renderPrerenderPaths(paths, clientDir, fetchPath, fetchPageData, onResult) {
|
|
58
|
+
const results = paths.map(() => ({
|
|
59
|
+
rendered: [],
|
|
60
|
+
failed: []
|
|
61
|
+
}));
|
|
62
|
+
let next = 0;
|
|
63
|
+
await Promise.all(Array.from({ length: Math.min(paths.length, renderConcurrency(paths, clientDir)) }, async () => {
|
|
64
|
+
while (next < paths.length) {
|
|
65
|
+
const index = next++;
|
|
66
|
+
const path = paths[index];
|
|
67
|
+
const result = results[index];
|
|
68
|
+
const outputFile = outputFileForPath(path);
|
|
69
|
+
try {
|
|
70
|
+
const response = await fetchPath(path);
|
|
71
|
+
const html = await response.text();
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
const error = `HTTP ${response.status}`;
|
|
74
|
+
result.failed.push({
|
|
75
|
+
path,
|
|
76
|
+
error
|
|
77
|
+
});
|
|
78
|
+
onResult?.(path, outputFile, error);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const outputPath = join(clientDir, outputFile);
|
|
82
|
+
mkdirSync(dirname(outputPath), { recursive: true });
|
|
83
|
+
writeFileSync(outputPath, html);
|
|
84
|
+
if (fetchPageData) {
|
|
85
|
+
const pageDataResponse = await fetchPageData(path);
|
|
86
|
+
if (pageDataResponse.headers.get("X-VoidPages")) {
|
|
87
|
+
const pageData = await pageDataResponse.json();
|
|
88
|
+
const pageDataPath = join(clientDir, pageDataFileForPath(path));
|
|
89
|
+
mkdirSync(dirname(pageDataPath), { recursive: true });
|
|
90
|
+
writeFileSync(pageDataPath, JSON.stringify(pageData));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
result.rendered.push(path);
|
|
94
|
+
onResult?.(path, outputFile);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
97
|
+
result.failed.push({
|
|
98
|
+
path,
|
|
99
|
+
error
|
|
100
|
+
});
|
|
101
|
+
onResult?.(path, outputFile, error);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}));
|
|
105
|
+
return {
|
|
106
|
+
rendered: results.flatMap((result) => result.rendered),
|
|
107
|
+
failed: results.flatMap((result) => result.failed)
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
export { renderPrerenderPaths };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { D as log, E as intro, a as dim, c as import_picocolors,
|
|
2
|
-
import {
|
|
3
|
-
import { n as PlatformClient } from "./client-
|
|
1
|
+
import { A as outro, D as log, E as intro, a as dim, c as import_picocolors, r as cliTitle, u as tablePrefix } from "./output-B0cfNSx5.mjs";
|
|
2
|
+
import { c as getSiteDomain, l as getToken, x as readDeploymentPlatform } from "./auth-DPl6kck4.mjs";
|
|
3
|
+
import { n as PlatformClient } from "./client-dHfSJvAN.mjs";
|
|
4
4
|
//#region src/cli/projects.ts
|
|
5
5
|
async function runProjectsCommand(root) {
|
|
6
6
|
console.log();
|
|
@@ -17,14 +17,16 @@ async function runProjectsCommand(root) {
|
|
|
17
17
|
}
|
|
18
18
|
const rows = projects.map((p) => ({
|
|
19
19
|
slug: p.slug,
|
|
20
|
+
role: p.role ?? "owner",
|
|
20
21
|
appType: p.type ?? "worker",
|
|
21
22
|
url: p.custom_domain ? `https://${p.custom_domain}` : p.url ?? `https://${p.slug}.${getSiteDomain(root)}`
|
|
22
23
|
}));
|
|
23
24
|
const slugWidth = Math.max(4, ...rows.map((r) => r.slug.length));
|
|
25
|
+
const roleWidth = Math.max(4, ...rows.map((r) => r.role.length));
|
|
24
26
|
const typeWidth = Math.max(4, ...rows.map((r) => r.appType.length));
|
|
25
27
|
const prefix = tablePrefix();
|
|
26
|
-
const header = `${prefix}${dim(`${"slug".padEnd(slugWidth)} ${"type".padEnd(typeWidth)} url`)}`;
|
|
27
|
-
const lines = rows.map((row) => `${prefix}${import_picocolors.default.green(row.slug)}${"".padEnd(slugWidth - row.slug.length)} ${row.appType.padEnd(typeWidth)} ${row.url}`);
|
|
28
|
+
const header = `${prefix}${dim(`${"slug".padEnd(slugWidth)} ${"role".padEnd(roleWidth)} ${"type".padEnd(typeWidth)} url`)}`;
|
|
29
|
+
const lines = rows.map((row) => `${prefix}${import_picocolors.default.green(row.slug)}${"".padEnd(slugWidth - row.slug.length)} ${row.role.padEnd(roleWidth)} ${row.appType.padEnd(typeWidth)} ${row.url}`);
|
|
28
30
|
console.log(prefix);
|
|
29
31
|
console.log(header);
|
|
30
32
|
for (const line of lines) console.log(line);
|
|
@@ -35,47 +37,63 @@ async function runProjectsCommand(root) {
|
|
|
35
37
|
async function runProjectCommand(root, args) {
|
|
36
38
|
const cloudflare = readDeploymentPlatform(root) === "cloudflare";
|
|
37
39
|
if (args.subcommand === "status") {
|
|
38
|
-
const { runListCommand } = await import("./list-
|
|
40
|
+
const { runListCommand } = await import("./list-DEE2S6mY.mjs");
|
|
39
41
|
return runListCommand(root, args.name || void 0);
|
|
40
42
|
}
|
|
41
43
|
if (args.subcommand === "link") {
|
|
42
44
|
if (cloudflare) throw new Error("project: Direct Cloudflare apps are identified by the name in wrangler.jsonc and do not need linking.");
|
|
43
|
-
const { runLink } = await import("./link-
|
|
45
|
+
const { runLink } = await import("./link-Rmvu2Wl_.mjs");
|
|
44
46
|
return runLink(root, args.name || void 0);
|
|
45
47
|
}
|
|
46
48
|
if (args.subcommand === "list") {
|
|
47
49
|
if (cloudflare) {
|
|
48
|
-
const { runListCommand } = await import("./list-
|
|
50
|
+
const { runListCommand } = await import("./list-DEE2S6mY.mjs");
|
|
49
51
|
return runListCommand(root);
|
|
50
52
|
}
|
|
51
53
|
return runProjectsCommand(root);
|
|
52
54
|
}
|
|
55
|
+
if (args.subcommand === "token") {
|
|
56
|
+
if (cloudflare) throw new Error("project: CI deploy credentials are available only for Void platform projects.");
|
|
57
|
+
const { runProjectTokenCommand } = await import("./project-token-DA34bf-C.mjs");
|
|
58
|
+
return runProjectTokenCommand(root, args);
|
|
59
|
+
}
|
|
60
|
+
if (args.subcommand === "team") {
|
|
61
|
+
if (args.action === "") {
|
|
62
|
+
const { printHelpPage } = await import("./help-CwOX-zmI.mjs").then((n) => n.n);
|
|
63
|
+
printHelpPage(["project", "team"]);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const accountInvitationAction = args.action === "pending" || args.action === "accept" || args.action === "decline";
|
|
67
|
+
if (cloudflare && !accountInvitationAction) throw new Error("project: Teams are available only for projects hosted on a Void platform, not direct Cloudflare deployments.");
|
|
68
|
+
const { runProjectTeamCommand } = await import("./project-team-D8jOJMUJ.mjs");
|
|
69
|
+
return runProjectTeamCommand(root, args);
|
|
70
|
+
}
|
|
53
71
|
if (args.subcommand === "logs") {
|
|
54
|
-
const { runLogsCommand } = await import("./logs-
|
|
72
|
+
const { runLogsCommand } = await import("./logs-27FenuiC.mjs");
|
|
55
73
|
return runLogsCommand(root, args.filter, args.range, args.deployment || void 0, args.level || void 0);
|
|
56
74
|
}
|
|
57
75
|
if (args.subcommand === "requests") {
|
|
58
76
|
if (cloudflare) throw new Error("project: Historical request analytics require the Void platform. Use `void project logs` for a live Cloudflare tail.");
|
|
59
|
-
const { runRequestsCommand } = await import("./requests-
|
|
77
|
+
const { runRequestsCommand } = await import("./requests-CUExwGQQ.mjs");
|
|
60
78
|
return runRequestsCommand(root, args.range, args.status || void 0);
|
|
61
79
|
}
|
|
62
80
|
if (args.subcommand === "rollback") {
|
|
63
|
-
const { runRollbackCommand } = await import("./rollback-
|
|
81
|
+
const { runRollbackCommand } = await import("./rollback-CDNGU1gr.mjs");
|
|
64
82
|
return runRollbackCommand(root, args.deployId || void 0);
|
|
65
83
|
}
|
|
66
84
|
if (args.subcommand === "cancel") {
|
|
67
85
|
if (cloudflare) throw new Error("project: Direct Cloudflare deploys activate only after readiness succeeds, so there is no remote build to cancel.");
|
|
68
|
-
const { runCancelDeployCommand } = await import("./cancel-deploy-
|
|
86
|
+
const { runCancelDeployCommand } = await import("./cancel-deploy-C5qTOdLi.mjs");
|
|
69
87
|
return runCancelDeployCommand(root, args.deployId || void 0);
|
|
70
88
|
}
|
|
71
89
|
if (args.subcommand === "delete") {
|
|
72
90
|
if (cloudflare) throw new Error("project: Refusing to delete Cloudflare resources through this command. Direct deployments are additive and existing resources may be shared.");
|
|
73
|
-
const { runDeleteCommand } = await import("./delete-
|
|
91
|
+
const { runDeleteCommand } = await import("./delete-DouASY9P.mjs");
|
|
74
92
|
return runDeleteCommand(root, args.name || void 0, { force: args.force });
|
|
75
93
|
}
|
|
76
94
|
if (args.subcommand === "purge-cache") {
|
|
77
95
|
if (cloudflare) throw new Error("project: Direct Cloudflare ISR cache purge is not available yet; redeploy or purge the configured KV namespace explicitly.");
|
|
78
|
-
const { runPurgeCacheCommand } = await import("./cache-
|
|
96
|
+
const { runPurgeCacheCommand } = await import("./cache-IHn5MwBC.mjs");
|
|
79
97
|
return runPurgeCacheCommand(root, args.projectSlug || void 0);
|
|
80
98
|
}
|
|
81
99
|
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { A as outro, D as log, E as intro, k as note, r as cliTitle } from "./output-B0cfNSx5.mjs";
|
|
2
|
+
import { S as readProjectConfig, a as getAccountCredential, l as getToken } from "./auth-DPl6kck4.mjs";
|
|
3
|
+
import { n as PlatformClient, t as PlatformApiError } from "./client-dHfSJvAN.mjs";
|
|
4
|
+
import { r as resolveProjectBySlug, t as getRequestedProjectSlug } from "./resolve-project--Vxawf7z.mjs";
|
|
5
|
+
//#region src/cli/project-team.ts
|
|
6
|
+
function clean(value) {
|
|
7
|
+
return (value ?? "—").replace(/[\u0000-\u001f\u007f]/g, " ");
|
|
8
|
+
}
|
|
9
|
+
function memberLines(members) {
|
|
10
|
+
const rows = members.map((member) => ({
|
|
11
|
+
id: member.user.id,
|
|
12
|
+
login: member.user.login ?? "—",
|
|
13
|
+
role: member.role
|
|
14
|
+
}));
|
|
15
|
+
const widths = {
|
|
16
|
+
id: Math.max(2, ...rows.map((row) => clean(row.id).length)),
|
|
17
|
+
login: Math.max(5, ...rows.map((row) => clean(row.login).length))
|
|
18
|
+
};
|
|
19
|
+
return [`${"ID".padEnd(widths.id)} ${"LOGIN".padEnd(widths.login)} ROLE`, ...rows.map((row) => `${clean(row.id).padEnd(widths.id)} ${clean(row.login).padEnd(widths.login)} ${row.role}`)].join("\n");
|
|
20
|
+
}
|
|
21
|
+
function invitationEmail(invitation) {
|
|
22
|
+
return invitation.target?.email ?? invitation.target_email ?? "—";
|
|
23
|
+
}
|
|
24
|
+
function invitationLines(invitations, pending) {
|
|
25
|
+
return invitations.map((invitation) => {
|
|
26
|
+
const project = pending ? ` ${clean(invitation.project?.slug)}` : "";
|
|
27
|
+
const target = pending ? "" : ` ${clean(invitationEmail(invitation))}`;
|
|
28
|
+
const status = pending ? "" : ` ${invitation.status ?? "pending"}`;
|
|
29
|
+
return `${clean(invitation.id)}${project}${target} ${invitation.role}${status} expires ${clean(invitation.expires_at)}`;
|
|
30
|
+
}).join("\n");
|
|
31
|
+
}
|
|
32
|
+
async function resolveProjectId(root, client, projectSlug) {
|
|
33
|
+
const requestedSlug = getRequestedProjectSlug({ projectSlug });
|
|
34
|
+
if (requestedSlug) {
|
|
35
|
+
const project = resolveProjectBySlug(await client.listProjects(), requestedSlug);
|
|
36
|
+
if (!project) throw new Error(`project: No project found with slug '${requestedSlug}'.`);
|
|
37
|
+
return project.projectId;
|
|
38
|
+
}
|
|
39
|
+
const linked = readProjectConfig(root);
|
|
40
|
+
if (!linked) throw new Error("No linked project. Run `void project link` or pass --project <slug>.");
|
|
41
|
+
return linked.projectId;
|
|
42
|
+
}
|
|
43
|
+
function invitationResponseError(error, apiUrl) {
|
|
44
|
+
if (!(error instanceof PlatformApiError)) return error;
|
|
45
|
+
let message;
|
|
46
|
+
if (error.code === "project invitation not found") message = `Invitation not found on ${apiUrl}. Check VOID_API_URL if set, connect to the platform in your invitation with \`void connect <url>\`, then run \`void project team pending\` to check the invitation ID. If it is missing, ask a project administrator for a new invitation.`;
|
|
47
|
+
else if (error.code === "forbidden") message = `Your account cannot respond to this invitation on ${apiUrl}. Sign in with the account whose registered email received the invitation. To switch accounts, set VOID_API_URL to ${apiUrl}, unset VOID_TOKEN if present, then run \`void auth logout\` and \`void auth login\`. Run \`void project team pending\` to see that account's invitations.`;
|
|
48
|
+
else if (error.code === "project invitation has expired") message = "This invitation has expired. Ask a project administrator to invite you again, then run `void project team pending`.";
|
|
49
|
+
else if (error.code === "project invitation is no longer pending") message = "This invitation has already been answered or revoked. Run `void project team pending` to see invitations awaiting your response, or ask a project administrator to check your access.";
|
|
50
|
+
else return error;
|
|
51
|
+
return new Error(message, { cause: error });
|
|
52
|
+
}
|
|
53
|
+
async function runProjectTeamCommand(root, args) {
|
|
54
|
+
console.log();
|
|
55
|
+
intro(cliTitle(`project team ${args.action}`));
|
|
56
|
+
const { apiUrl, token } = args.action === "pending" || args.action === "accept" || args.action === "decline" ? getAccountCredential() : {
|
|
57
|
+
apiUrl: void 0,
|
|
58
|
+
token: getToken(root)
|
|
59
|
+
};
|
|
60
|
+
if (apiUrl) log.info(`Platform: ${apiUrl}`);
|
|
61
|
+
if (!token) {
|
|
62
|
+
log.error(apiUrl ? `Not logged in to ${apiUrl}. Run \`void connect ${apiUrl}\` to sign in.` : "Not logged in. Run `void auth login` first.");
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
const client = new PlatformClient(token, apiUrl ? { apiUrl } : { root });
|
|
66
|
+
if (args.action === "pending") {
|
|
67
|
+
const invitations = await client.listPendingProjectInvitations();
|
|
68
|
+
if (invitations.length === 0) {
|
|
69
|
+
outro("No pending project invitations.");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
note(invitationLines(invitations, true), "Pending Project Invitations");
|
|
73
|
+
outro(`${invitations.length} invitation(s)`);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (args.action === "accept" || args.action === "decline") {
|
|
77
|
+
try {
|
|
78
|
+
await client.respondToProjectInvitation(args.invitationId, args.action);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
throw invitationResponseError(error, client.apiUrl);
|
|
81
|
+
}
|
|
82
|
+
if (args.action === "accept") log.info("Open the invited application’s directory and run `void project link` if it is not linked yet. If it is linked to another project, use a separate checkout. Accepting an invitation keeps existing project links.");
|
|
83
|
+
outro(`Project invitation ${args.action === "accept" ? "accepted" : "declined"}.`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const projectId = await resolveProjectId(root, client, "projectSlug" in args ? args.projectSlug : "");
|
|
87
|
+
if (args.action === "list") {
|
|
88
|
+
const members = await client.listProjectMembers(projectId);
|
|
89
|
+
note(memberLines(members), "Project Team");
|
|
90
|
+
outro(`${members.length} member(s)`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (args.action === "invitations") {
|
|
94
|
+
const invitations = await client.listProjectInvitations(projectId);
|
|
95
|
+
if (invitations.length === 0) {
|
|
96
|
+
outro("No project invitations found.");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
note(invitationLines(invitations, false), "Project Invitations");
|
|
100
|
+
outro(`${invitations.length} invitation(s)`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (args.action === "invite") {
|
|
104
|
+
const result = await client.inviteProjectMember(projectId, args.email, args.role);
|
|
105
|
+
const delivery = result.delivery.status === "sent" ? "" : ` (email delivery: ${result.delivery.status.replace("_", " ")})`;
|
|
106
|
+
const action = result.refreshed ? "Updated invitation for" : "Invited";
|
|
107
|
+
if (result.delivery.status !== "sent") note(`Invitation: ${clean(result.invitation.id)}\nShare these steps with the invited user:\n\nvoid connect '${client.apiUrl.replaceAll("'", "'\\''")}'\nvoid project team pending\nvoid project team accept ${clean(result.invitation.id)}`, "Invitation saved; email was not sent");
|
|
108
|
+
outro(`${action} ${clean(invitationEmail(result.invitation))} as ${args.role}${delivery}.`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (args.action === "role") {
|
|
112
|
+
await client.updateProjectMemberRole(projectId, args.userId, args.role);
|
|
113
|
+
outro(`Changed ${args.userId} to ${args.role}.`);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (args.action === "remove") {
|
|
117
|
+
await client.removeProjectMember(projectId, args.userId);
|
|
118
|
+
outro(`Removed ${args.userId} from the project.`);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (args.action === "revoke") {
|
|
122
|
+
await client.revokeProjectInvitation(projectId, args.invitationId);
|
|
123
|
+
outro(`Revoked invitation ${args.invitationId}.`);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
await client.leaveProject(projectId);
|
|
127
|
+
outro("Left the project.");
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
export { runProjectTeamCommand };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { A as outro, D as log, E as intro, a as dim, r as cliTitle, u as tablePrefix } from "./output-B0cfNSx5.mjs";
|
|
2
|
+
import { S as readProjectConfig, l as getToken } from "./auth-DPl6kck4.mjs";
|
|
3
|
+
import { n as PlatformClient } from "./client-dHfSJvAN.mjs";
|
|
4
|
+
import { r as resolveProjectBySlug } from "./resolve-project--Vxawf7z.mjs";
|
|
5
|
+
//#region src/cli/project-token.ts
|
|
6
|
+
async function resolveTarget(root, args) {
|
|
7
|
+
const humanToken = getToken(root);
|
|
8
|
+
if (!humanToken) throw new Error("project: Sign in with a human account before managing CI credentials.");
|
|
9
|
+
const client = new PlatformClient(humanToken, { root });
|
|
10
|
+
if (args.projectSlug) {
|
|
11
|
+
const resolved = resolveProjectBySlug(await client.listProjects(), args.projectSlug);
|
|
12
|
+
if (!resolved) throw new Error(`project: No project found with slug '${args.projectSlug}'.`);
|
|
13
|
+
return {
|
|
14
|
+
client,
|
|
15
|
+
projectId: resolved.projectId,
|
|
16
|
+
slug: args.projectSlug
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const linked = readProjectConfig(root);
|
|
20
|
+
if (!linked) throw new Error("project: No linked project. Pass --project <name>.");
|
|
21
|
+
return {
|
|
22
|
+
client,
|
|
23
|
+
projectId: linked.projectId,
|
|
24
|
+
slug: linked.slug
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function printIssued(args, result, apiUrl) {
|
|
28
|
+
if (args.json) {
|
|
29
|
+
console.log(JSON.stringify({
|
|
30
|
+
...result,
|
|
31
|
+
apiUrl
|
|
32
|
+
}));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
log.success(`Credential ${result.id} is valid until ${result.expiresAt}.`);
|
|
36
|
+
log.warn("Copy this token now. Void will not display it again.");
|
|
37
|
+
console.log(`\nVOID_TOKEN=${result.token}\nVOID_API_URL=${apiUrl}\n`);
|
|
38
|
+
}
|
|
39
|
+
async function runProjectTokenCommand(root, args) {
|
|
40
|
+
if (!args.json) {
|
|
41
|
+
console.log();
|
|
42
|
+
intro(cliTitle(`project token ${args.action}`));
|
|
43
|
+
}
|
|
44
|
+
const { client, projectId, slug } = await resolveTarget(root, args);
|
|
45
|
+
if (args.action === "create") printIssued(args, await client.createProjectToken(projectId, {
|
|
46
|
+
...args.name ? { name: args.name } : {},
|
|
47
|
+
...args.expiresInDays ? { expiresInDays: args.expiresInDays } : {}
|
|
48
|
+
}), client.apiUrl);
|
|
49
|
+
else if (args.action === "renew") printIssued(args, await client.renewProjectToken(projectId, args.credentialId, args.expiresInDays), client.apiUrl);
|
|
50
|
+
else if (args.action === "revoke") {
|
|
51
|
+
await client.revokeProjectToken(projectId, args.credentialId);
|
|
52
|
+
if (args.json) console.log(JSON.stringify({
|
|
53
|
+
ok: true,
|
|
54
|
+
id: args.credentialId
|
|
55
|
+
}));
|
|
56
|
+
else log.success(`Revoked ${args.credentialId}.`);
|
|
57
|
+
} else {
|
|
58
|
+
const rows = await client.listProjectTokens(projectId);
|
|
59
|
+
if (args.json) {
|
|
60
|
+
console.log(JSON.stringify(rows));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const prefix = tablePrefix();
|
|
64
|
+
console.log(prefix);
|
|
65
|
+
console.log(`${prefix}${dim("id status expires name")}`);
|
|
66
|
+
for (const row of rows) {
|
|
67
|
+
const status = row.revokedAt ? "revoked" : Date.parse(row.expiresAt) <= Date.now() ? "expired" : "active";
|
|
68
|
+
console.log(`${prefix}${row.id.padEnd(38)}${status.padEnd(9)}${row.expiresAt.slice(0, 10)} ${row.name}`);
|
|
69
|
+
}
|
|
70
|
+
if (rows.length === 0) log.info("No CI deploy credentials.");
|
|
71
|
+
}
|
|
72
|
+
if (!args.json) outro(`${slug} — project-scoped deploy access only`);
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
export { runProjectTokenCommand };
|
|
@@ -339,6 +339,8 @@ function provisionEnv(ctx) {
|
|
|
339
339
|
scrub("CF_ACCESS_CLIENT_ID");
|
|
340
340
|
scrub("CF_ACCESS_CLIENT_SECRET");
|
|
341
341
|
scrub("CF_ACCESS_TOKEN");
|
|
342
|
+
scrub("VOID_ACCESS_CREDENTIALS");
|
|
343
|
+
scrub("VOID_ACCESS_ORIGIN");
|
|
342
344
|
for (const name of TRANSPORT_REDIRECT_ENV_VARS) scrub(name);
|
|
343
345
|
for (const name of OUTPUT_FILE_ENV_VARS) scrub(name);
|
|
344
346
|
scrub("WRANGLER_LOG_SANITIZE");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { D as log, E as intro, a as dim, c as import_picocolors,
|
|
2
|
-
import {
|
|
3
|
-
import { n as PlatformClient } from "./client-
|
|
1
|
+
import { A as outro, D as log, E as intro, a as dim, c as import_picocolors, r as cliTitle } from "./output-B0cfNSx5.mjs";
|
|
2
|
+
import { S as readProjectConfig, l as getToken } from "./auth-DPl6kck4.mjs";
|
|
3
|
+
import { n as PlatformClient } from "./client-dHfSJvAN.mjs";
|
|
4
4
|
//#region src/cli/requests.ts
|
|
5
5
|
async function runRequestsCommand(root, range, status) {
|
|
6
6
|
console.log();
|