void 0.20.1 → 0.20.3
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-CAH62yDU.mjs → auth-cmd-CzwquNiP.mjs} +5 -4
- package/dist/auth-link-ElDTgF7j.mjs +28 -0
- package/dist/{build-cmd-CJvZvPQO.mjs → build-cmd-BpJe6boP.mjs} +3 -3
- package/dist/{cache-BlNeQjuP.mjs → cache-D98YTqeE.mjs} +3 -3
- package/dist/{cancel-deploy-CmlAZ9P6.mjs → cancel-deploy-CPbEQLMG.mjs} +3 -3
- package/dist/cf-access-DRsQRe6k.mjs +75 -0
- package/dist/cli/cli.mjs +309 -1958
- package/dist/cli/env-schema-probe.mjs +11 -2
- package/dist/client-BQBrZoCX.mjs +989 -0
- package/dist/{cloudflare-auth-B1QtTO1b.mjs → cloudflare-auth-6M5llVPC.mjs} +2 -2
- package/dist/{cloudflare-cmd-B6_OZx2V.mjs → cloudflare-cmd-4RPGN3KB.mjs} +2 -2
- package/dist/{cloudflare-connect-j5D4hhrG.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-C04Wdy_h.mjs → connect-WCQZ_u3m.mjs} +6 -6
- package/dist/{create-project-ChGZ1DFd.mjs → create-project-D0oXA090.mjs} +7 -7
- package/dist/{db-D2d_mUsB.mjs → db-DJ-9qs3S.mjs} +47 -30
- package/dist/{delete-D8GigDk8.mjs → delete-BZ4-WaGm.mjs} +3 -3
- package/dist/{deploy-iXZ3F0N6.mjs → deploy-BAhhcg5q.mjs} +135 -117
- package/dist/{dev-inbox-DkgRWLkW.mjs → dev-inbox-P0u4tM8Y.mjs} +1 -1
- package/dist/{domain-B1VmoSr0.mjs → domain-BxAyhxXN.mjs} +4 -4
- package/dist/email-Bj7Cvdwp.mjs +795 -0
- package/dist/{env-D4Emu-M_.mjs → env-DBKmK4vc.mjs} +1 -0
- package/dist/{env-BcQzYgoG.mjs → env-DP_EErve.mjs} +5 -5
- package/dist/{env-validation-ENpMy6Ez.mjs → env-validation-CF6KvTRf.mjs} +3 -1
- package/dist/{gen-DI2YwdBM.mjs → gen-B_wPnVTK.mjs} +2 -2
- package/dist/{github-cmd-xItS5Zwf.mjs → github-cmd-C-z_xRrQ.mjs} +15 -21
- package/dist/{headers-D8QfRX9Y.mjs → headers-BAHwgHdW.mjs} +1 -1
- package/dist/help-CwOX-zmI.mjs +2216 -0
- package/dist/{inbound-afAcWeQ9.d.mts → inbound-CH5Mksyy.d.mts} +34 -48
- package/dist/{inbound-2d0zi2yS.mjs → inbound-aVHEUhKo.mjs} +130 -100
- package/dist/index.mjs +54 -17
- package/dist/{init-BD-9THgn.mjs → init-BGktCXgA.mjs} +11 -11
- package/dist/{link-RMdgjF1v.mjs → link-D2kbqhWb.mjs} +4 -4
- package/dist/{list-3F52R_yO.mjs → list-CvkK_G7k.mjs} +4 -4
- package/dist/{login-pV69H-ZO.mjs → login-WIjNc77c.mjs} +28 -10
- package/dist/{logs-DFHHD6wE.mjs → logs-dLUFCapG.mjs} +4 -4
- package/dist/{mime-BJD7d_qL.mjs → mime-D5Nmdzf7.mjs} +23 -9
- package/dist/{node-Dk3H2jmU.mjs → node-Ez5KW5rn.mjs} +2 -2
- package/dist/operator-auth-B3e08unv.mjs +52 -0
- package/dist/operator-client-LUZnlnYk.mjs +82 -0
- package/dist/{operator-cmd-DYWRbWUA.mjs → operator-cmd-CKJ7xIRs.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-CdVWRRJr.mjs +368 -0
- package/dist/platform-auth-protection-Drl0qhrn.mjs +219 -0
- package/dist/platform-auth-recovery-CmKEWpDo.mjs +310 -0
- package/dist/{platform-cmd-DxJ2FRwR.mjs → platform-cmd-5q_k56xS.mjs} +16 -6
- package/dist/{platform-domain-ChvbJkdy.mjs → platform-domain-4GiDlcqx.mjs} +4 -4
- package/dist/{platform-lifecycle-DN4MzJF_.mjs → platform-lifecycle-R9xAxvtG.mjs} +1716 -204
- package/dist/{platform-management-Db2PXw0B.mjs → platform-management-BfWsXHEW.mjs} +35 -7
- package/dist/{platform-recovery-C_YO-tIs.mjs → platform-recovery-CbK-I1FB.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-Mo0V9yKS.mjs → project-cmd-CTdmnzvc.mjs} +32 -14
- package/dist/project-team-CGxsQe3_.mjs +132 -0
- package/dist/project-token-Cirx7uwZ.mjs +75 -0
- package/dist/{provision-Blnstcm2.mjs → provision-CSJOjjQk.mjs} +2 -0
- package/dist/{requests-BcKOVpRg.mjs → requests-4Nq59hOr.mjs} +3 -3
- package/dist/{rollback-Bx85-0xh.mjs → rollback-Dr7u0Ljx.mjs} +4 -4
- 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 +54 -11
- package/dist/runtime/sandbox.d.mts +4 -56
- package/dist/runtime/sandbox.mjs +81 -220
- package/dist/{secret-ByhJ9AMl.mjs → secret-AcPi-FoA.mjs} +5 -5
- package/dist/{skills-Q46GZMO-.mjs → skills-C0RvGjeE.mjs} +1 -1
- package/dist/{subcommand-prompt-WfySCQ7S.mjs → subcommand-prompt-Bmyn5Rlc.mjs} +1 -1
- package/package.json +12 -7
- package/skills/void/SKILL.md +35 -4
- package/skills/void/docs/guide/deployment.md +16 -14
- package/skills/void/docs/guide/email.md +114 -120
- package/skills/void/docs/guide/platform/administration/access.md +163 -0
- package/skills/void/docs/guide/platform/administration/email.md +121 -0
- package/skills/void/docs/guide/platform/administration/operations.md +97 -0
- package/skills/void/docs/guide/platform/administration/projects.md +54 -0
- package/skills/void/docs/guide/platform/development/local.md +119 -0
- package/skills/void/docs/guide/platform/development/runtime.md +124 -0
- package/skills/void/docs/guide/platform/development/schema-ci.md +95 -0
- package/skills/void/docs/guide/platform/installation/ci.md +55 -0
- package/skills/void/docs/guide/platform/installation/credentials.md +80 -0
- package/skills/void/docs/guide/platform/installation/domains.md +68 -0
- package/skills/void/docs/guide/platform/installation/first-deployment.md +82 -0
- package/skills/void/docs/guide/platform/installation/maintenance.md +137 -0
- package/skills/void/docs/guide/platform/installation/prerequisites.md +86 -0
- package/skills/void/docs/guide/platform/installation/setup.md +169 -0
- package/skills/void/docs/guide/platform/installation/uninstall.md +54 -0
- package/skills/void/docs/guide/platform-administration.md +6 -202
- package/skills/void/docs/guide/platform-development.md +5 -254
- package/skills/void/docs/guide/project-collaboration.md +94 -0
- package/skills/void/docs/guide/sandboxes.md +9 -24
- package/skills/void/docs/guide/self-hosted-platform.md +13 -542
- package/skills/void/docs/reference/api.md +34 -34
- package/skills/void/docs/reference/cli.md +276 -31
- package/skills/void/docs/reference/config.md +1 -1
- package/skills/void/docs/reference/resource-inference.md +10 -10
- package/dist/cf-access-AJ1ehiFR.mjs +0 -42
- package/dist/cf-access-DsSsZUPr.mjs +0 -67
- package/dist/client-Clirrol3.mjs +0 -705
- package/dist/email-uKyQYUVY.mjs +0 -1016
|
@@ -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-R9xAxvtG.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,9 @@ 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,
|
|
76
|
+
enableSandbox: runtime.manifest.safetyProtocols.sandboxDrain >= 3
|
|
73
77
|
});
|
|
74
78
|
note(formatPlatformInstallPlan(plan), args.subcommand === "upgrade" ? "Upgrade plan" : args.subcommand === "rollback" ? "Rollback plan" : args.subcommand === "enable" ? "Enable plan" : "Repair plan");
|
|
75
79
|
note([
|
|
@@ -103,6 +107,9 @@ async function runInstalledPlatformCommand(args) {
|
|
|
103
107
|
cloudflareApiToken: process.env.VOID_PLATFORM_RUNTIME_CLOUDFLARE_API_TOKEN || "",
|
|
104
108
|
checkpoint: recovery,
|
|
105
109
|
databaseUrl,
|
|
110
|
+
emailEnabled: plan.deployment.capabilities.email,
|
|
111
|
+
emailSigningSecret: process.env.VOID_PLATFORM_EMAIL_SIGNING_SECRET || "",
|
|
112
|
+
existingEmailEnabled: manifest.deployment.capabilities.email,
|
|
106
113
|
existingInstallation: true,
|
|
107
114
|
updateExistingWorkers: true,
|
|
108
115
|
githubClientId: process.env.VOID_PLATFORM_GITHUB_CLIENT_ID || "",
|
|
@@ -121,11 +128,13 @@ async function runInstalledPlatformCommand(args) {
|
|
|
121
128
|
customDomains: plan.deployment.capabilities.customDomains,
|
|
122
129
|
zoneId: plan.zone?.id ?? null
|
|
123
130
|
});
|
|
131
|
+
if (!databaseLock?.readAuthenticationState) throw new Error("platform: The database lifecycle lock cannot read authoritative authentication settings.");
|
|
124
132
|
const result = await installPlatform(plan, secrets, installation, {
|
|
125
133
|
acquireDatabaseLifecycleLock: async () => Object.assign(async () => {}, {
|
|
126
134
|
checkpoint: databaseLock?.checkpoint,
|
|
127
135
|
prepareDatabase: databaseLock?.prepareDatabase,
|
|
128
136
|
prepareSandboxDrain: databaseLock?.prepareSandboxDrain,
|
|
137
|
+
readAuthenticationState: databaseLock?.readAuthenticationState,
|
|
129
138
|
requiredSafetyProtocols: databaseLock?.requiredSafetyProtocols,
|
|
130
139
|
verifyDatabase: databaseLock?.verifyDatabase
|
|
131
140
|
}),
|
|
@@ -135,7 +144,9 @@ async function runInstalledPlatformCommand(args) {
|
|
|
135
144
|
runtime,
|
|
136
145
|
replanWorkers: async (recoveryManifest) => (await createPlatformInstallPlan(client, planInput, recoveryManifest, {
|
|
137
146
|
authoritativeProvisionIntents: true,
|
|
138
|
-
repairMissing: true
|
|
147
|
+
repairMissing: true,
|
|
148
|
+
includeEmailGateway: runtime.manifest.artifacts.emailGateway !== void 0,
|
|
149
|
+
enableSandbox: runtime.manifest.safetyProtocols.sandboxDrain >= 3
|
|
139
150
|
})).actions.filter((action) => action.kind === "worker"),
|
|
140
151
|
revalidateBeforeWorkerDeploy: (manifest, action) => revalidatePlatformInstallAction(client, plan, manifest, action, { repairMissing: args.subcommand === "repair" })
|
|
141
152
|
});
|
|
@@ -152,6 +163,14 @@ async function runInstalledPlatformCommand(args) {
|
|
|
152
163
|
return;
|
|
153
164
|
}
|
|
154
165
|
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}.`);
|
|
166
|
+
if (result.emailOperationId) log.info(`Shared email routing ready (operation ${result.emailOperationId}).`);
|
|
167
|
+
if (result.setupPending) {
|
|
168
|
+
const code = readInstallationRecoverySecrets(result.manifest)?.authBootstrapCode;
|
|
169
|
+
if (code) {
|
|
170
|
+
log.info(`First administrator setup: ${plan.apiUrl}/setup`);
|
|
171
|
+
console.log(`One-time administrator setup code: ${code}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
155
174
|
};
|
|
156
175
|
};
|
|
157
176
|
if (args.plan) {
|
|
@@ -189,9 +208,12 @@ async function runInstalledPlatformCommand(args) {
|
|
|
189
208
|
}
|
|
190
209
|
}
|
|
191
210
|
function assertPlatformRollbackCompatibility(installation, target, source) {
|
|
211
|
+
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
212
|
if (!installation.runtime) throw new Error("platform: The installed runtime has no artifact provenance, so post-success rollback cannot identify its compatibility manifest.");
|
|
193
213
|
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
214
|
if (target.manifestSha256 === source.manifestSha256) throw new Error("platform: The rollback target is already the installed runtime artifact.");
|
|
215
|
+
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.");
|
|
216
|
+
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
217
|
if (target.manifest.database.lineage !== source.manifest.database.lineage) throw new Error("platform: The rollback target belongs to a different database lineage.");
|
|
196
218
|
const prefix = source.manifest.database.migrations.slice(0, target.manifest.database.migrations.length);
|
|
197
219
|
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 +224,7 @@ function assertPlatformRollbackCompatibility(installation, target, source) {
|
|
|
202
224
|
function resolvePlatformRuntimeTokenToPreflight(plan, secrets) {
|
|
203
225
|
const explicitUpdate = secrets.workerSecretUpdates?.CF_API_TOKEN;
|
|
204
226
|
if (explicitUpdate) return explicitUpdate;
|
|
205
|
-
return plan.actions.some((action) => action.kind === "worker" && action.action === "create") ? secrets.cloudflareApiToken : "";
|
|
227
|
+
return plan.actions.some((action) => action.kind === "worker" && action.action === "create" && (action.key === "api-worker" || action.key === "proxy-worker")) ? secrets.cloudflareApiToken : "";
|
|
206
228
|
}
|
|
207
229
|
async function resolveInstalledPlatformManifest(selector, dependencies = {}) {
|
|
208
230
|
if (selector) return resolveInstallationManifest(selector);
|
|
@@ -275,7 +297,7 @@ async function uninstallPlatform(client, initialManifest, args, dependencies = {
|
|
|
275
297
|
const confirmAction = dependencies.confirm ?? promptForConfirmation;
|
|
276
298
|
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
299
|
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");
|
|
300
|
+
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
301
|
if (args.plan) {
|
|
280
302
|
outro("No Cloudflare or local installation state was changed.");
|
|
281
303
|
return;
|
|
@@ -397,6 +419,11 @@ async function recoverDurablyMarkedProvisionIntents(client, manifest) {
|
|
|
397
419
|
"worker",
|
|
398
420
|
manifest.deployment.resources.proxyWorker
|
|
399
421
|
],
|
|
422
|
+
[
|
|
423
|
+
"email-gateway-worker",
|
|
424
|
+
"worker",
|
|
425
|
+
manifest.deployment.resources.emailGatewayWorker
|
|
426
|
+
],
|
|
400
427
|
[
|
|
401
428
|
"tail-worker",
|
|
402
429
|
"worker",
|
|
@@ -596,7 +623,8 @@ function isPlatformManifestMonotonicExtension(earlier, later) {
|
|
|
596
623
|
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
624
|
});
|
|
598
625
|
const stepsAdvance = earlier.steps.every((step) => later.steps.some((entry) => entry.key === step.key && entry.completedAt === step.completedAt && entry.resultHash === step.resultHash));
|
|
599
|
-
|
|
626
|
+
const authenticationResourcesAdvance = (earlier.authenticationResources ?? []).every((resource) => (later.authenticationResources ?? []).some((next) => JSON.stringify(next) === JSON.stringify(resource)));
|
|
627
|
+
return resourcesAdvance && stepsAdvance && authenticationResourcesAdvance;
|
|
600
628
|
}
|
|
601
629
|
function hasNamedResource(entries, resource) {
|
|
602
630
|
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-R9xAxvtG.mjs";
|
|
4
|
+
import { isPlatformManifestMonotonicExtension, recoverDurablyMarkedProvisionIntents, verifyPlatformInstallationOwnership } from "./platform-management-BfWsXHEW.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-BQBrZoCX.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-CvkK_G7k.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-D2kbqhWb.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-CvkK_G7k.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-Cirx7uwZ.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-CGxsQe3_.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-dLUFCapG.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-4Nq59hOr.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-Dr7u0Ljx.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-CPbEQLMG.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-BZ4-WaGm.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-D98YTqeE.mjs");
|
|
79
97
|
return runPurgeCacheCommand(root, args.projectSlug || void 0);
|
|
80
98
|
}
|
|
81
99
|
}
|
|
@@ -0,0 +1,132 @@
|
|
|
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, o as parsePlatformErrorBody, s as platformErrorMessage, t as PlatformApiError } from "./client-BQBrZoCX.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
|
+
if (platformErrorMessage(parsePlatformErrorBody(error.body))) return error;
|
|
46
|
+
let message;
|
|
47
|
+
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.`;
|
|
48
|
+
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.`;
|
|
49
|
+
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`.";
|
|
50
|
+
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.";
|
|
51
|
+
else return error;
|
|
52
|
+
error.message = message;
|
|
53
|
+
return error;
|
|
54
|
+
}
|
|
55
|
+
async function runProjectTeamCommand(root, args) {
|
|
56
|
+
console.log();
|
|
57
|
+
intro(cliTitle(`project team ${args.action}`));
|
|
58
|
+
const { apiUrl, token } = args.action === "pending" || args.action === "accept" || args.action === "decline" ? getAccountCredential() : {
|
|
59
|
+
apiUrl: void 0,
|
|
60
|
+
token: getToken(root)
|
|
61
|
+
};
|
|
62
|
+
if (apiUrl) log.info(`Platform: ${apiUrl}`);
|
|
63
|
+
if (!token) {
|
|
64
|
+
log.error(apiUrl ? `Not logged in to ${apiUrl}. Run \`void connect ${apiUrl}\` to sign in.` : "Not logged in. Run `void auth login` first.");
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
const client = new PlatformClient(token, apiUrl ? { apiUrl } : { root });
|
|
68
|
+
if (args.action === "pending") {
|
|
69
|
+
const invitations = await client.listPendingProjectInvitations();
|
|
70
|
+
if (invitations.length === 0) {
|
|
71
|
+
outro("No pending project invitations.");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
note(invitationLines(invitations, true), "Pending Project Invitations");
|
|
75
|
+
outro(`${invitations.length} invitation(s)`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (args.action === "accept" || args.action === "decline") {
|
|
79
|
+
try {
|
|
80
|
+
await client.respondToProjectInvitation(args.invitationId, args.action);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
throw invitationResponseError(error, client.apiUrl);
|
|
83
|
+
}
|
|
84
|
+
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.");
|
|
85
|
+
outro(`Project invitation ${args.action === "accept" ? "accepted" : "declined"}.`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const projectId = await resolveProjectId(root, client, "projectSlug" in args ? args.projectSlug : "");
|
|
89
|
+
if (args.action === "list") {
|
|
90
|
+
const members = await client.listProjectMembers(projectId);
|
|
91
|
+
note(memberLines(members), "Project Team");
|
|
92
|
+
outro(`${members.length} member(s)`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (args.action === "invitations") {
|
|
96
|
+
const invitations = await client.listProjectInvitations(projectId);
|
|
97
|
+
if (invitations.length === 0) {
|
|
98
|
+
outro("No project invitations found.");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
note(invitationLines(invitations, false), "Project Invitations");
|
|
102
|
+
outro(`${invitations.length} invitation(s)`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (args.action === "invite") {
|
|
106
|
+
const result = await client.inviteProjectMember(projectId, args.email, args.role);
|
|
107
|
+
const delivery = result.delivery.status === "sent" ? "" : ` (email delivery: ${result.delivery.status.replace("_", " ")})`;
|
|
108
|
+
const action = result.refreshed ? "Updated invitation for" : "Invited";
|
|
109
|
+
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");
|
|
110
|
+
outro(`${action} ${clean(invitationEmail(result.invitation))} as ${args.role}${delivery}.`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (args.action === "role") {
|
|
114
|
+
await client.updateProjectMemberRole(projectId, args.userId, args.role);
|
|
115
|
+
outro(`Changed ${args.userId} to ${args.role}.`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (args.action === "remove") {
|
|
119
|
+
await client.removeProjectMember(projectId, args.userId);
|
|
120
|
+
outro(`Removed ${args.userId} from the project.`);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (args.action === "revoke") {
|
|
124
|
+
await client.revokeProjectInvitation(projectId, args.invitationId);
|
|
125
|
+
outro(`Revoked invitation ${args.invitationId}.`);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
await client.leaveProject(projectId);
|
|
129
|
+
outro("Left the project.");
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
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-BQBrZoCX.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-BQBrZoCX.mjs";
|
|
4
4
|
//#region src/cli/requests.ts
|
|
5
5
|
async function runRequestsCommand(root, range, status) {
|
|
6
6
|
console.log();
|