void 0.20.2 → 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.
Files changed (68) hide show
  1. package/dist/{auth-cmd-gniL2fNt.mjs → auth-cmd-CzwquNiP.mjs} +3 -3
  2. package/dist/{auth-link-NZdjCmSc.mjs → auth-link-ElDTgF7j.mjs} +3 -3
  3. package/dist/{build-cmd-sI18tX_O.mjs → build-cmd-BpJe6boP.mjs} +1 -1
  4. package/dist/{cache-IHn5MwBC.mjs → cache-D98YTqeE.mjs} +1 -1
  5. package/dist/{cancel-deploy-C5qTOdLi.mjs → cancel-deploy-CPbEQLMG.mjs} +1 -1
  6. package/dist/cli/cli.mjs +27 -26
  7. package/dist/{client-dHfSJvAN.mjs → client-BQBrZoCX.mjs} +143 -78
  8. package/dist/{connect-Bfk31O_8.mjs → connect-WCQZ_u3m.mjs} +3 -3
  9. package/dist/{create-project-Bk9Z0-Jg.mjs → create-project-D0oXA090.mjs} +2 -3
  10. package/dist/{db-BkRoptAt.mjs → db-DJ-9qs3S.mjs} +2 -2
  11. package/dist/{delete-DouASY9P.mjs → delete-BZ4-WaGm.mjs} +1 -1
  12. package/dist/{deploy-DTaWUS1S.mjs → deploy-BAhhcg5q.mjs} +17 -10
  13. package/dist/{domain-1RhhOVrC.mjs → domain-BxAyhxXN.mjs} +1 -1
  14. package/dist/{email-C-lGh51B.mjs → email-Bj7Cvdwp.mjs} +2 -2
  15. package/dist/{env-DJHsPE7Z.mjs → env-DP_EErve.mjs} +1 -1
  16. package/dist/{github-cmd-PW7ZnWTp.mjs → github-cmd-C-z_xRrQ.mjs} +13 -19
  17. package/dist/index.mjs +4 -4
  18. package/dist/{init-BWZ7q5Z4.mjs → init-BGktCXgA.mjs} +4 -4
  19. package/dist/{link-Rmvu2Wl_.mjs → link-D2kbqhWb.mjs} +2 -2
  20. package/dist/{list-DEE2S6mY.mjs → list-CvkK_G7k.mjs} +1 -1
  21. package/dist/{login-Uvferzmm.mjs → login-WIjNc77c.mjs} +2 -2
  22. package/dist/{logs-27FenuiC.mjs → logs-dLUFCapG.mjs} +1 -1
  23. package/dist/{operator-cmd-CjOTmAYE.mjs → operator-cmd-CKJ7xIRs.mjs} +2 -2
  24. package/dist/{platform-auth-config-DrbQXXiW.mjs → platform-auth-config-CdVWRRJr.mjs} +2 -2
  25. package/dist/{platform-auth-protection-Bhtvp0B_.mjs → platform-auth-protection-Drl0qhrn.mjs} +2 -2
  26. package/dist/{platform-auth-recovery-CeOKGVeJ.mjs → platform-auth-recovery-CmKEWpDo.mjs} +2 -2
  27. package/dist/{platform-cmd-BFhieCdV.mjs → platform-cmd-5q_k56xS.mjs} +3 -3
  28. package/dist/{platform-domain-C74PULqV.mjs → platform-domain-4GiDlcqx.mjs} +2 -2
  29. package/dist/{platform-lifecycle-BwAIgz-t.mjs → platform-lifecycle-R9xAxvtG.mjs} +61 -25
  30. package/dist/{platform-management-COogu_Se.mjs → platform-management-BfWsXHEW.mjs} +5 -3
  31. package/dist/{platform-recovery-ewqLefp1.mjs → platform-recovery-CbK-I1FB.mjs} +2 -2
  32. package/dist/{project-cmd-DmZK9Hxf.mjs → project-cmd-CTdmnzvc.mjs} +12 -12
  33. package/dist/{project-team-D8jOJMUJ.mjs → project-team-CGxsQe3_.mjs} +4 -2
  34. package/dist/{project-token-DA34bf-C.mjs → project-token-Cirx7uwZ.mjs} +1 -1
  35. package/dist/{requests-CUExwGQQ.mjs → requests-4Nq59hOr.mjs} +1 -1
  36. package/dist/{rollback-CDNGU1gr.mjs → rollback-Dr7u0Ljx.mjs} +1 -1
  37. package/dist/runtime/remote/index.mjs +49 -8
  38. package/dist/runtime/sandbox.d.mts +4 -56
  39. package/dist/runtime/sandbox.mjs +81 -220
  40. package/dist/{secret-Bzzi2e9E.mjs → secret-AcPi-FoA.mjs} +1 -1
  41. package/package.json +7 -7
  42. package/skills/void/SKILL.md +2 -2
  43. package/skills/void/docs/guide/deployment.md +14 -14
  44. package/skills/void/docs/guide/email.md +12 -10
  45. package/skills/void/docs/guide/platform/administration/access.md +163 -0
  46. package/skills/void/docs/guide/platform/administration/email.md +121 -0
  47. package/skills/void/docs/guide/platform/administration/operations.md +97 -0
  48. package/skills/void/docs/guide/platform/administration/projects.md +54 -0
  49. package/skills/void/docs/guide/platform/development/local.md +119 -0
  50. package/skills/void/docs/guide/platform/development/runtime.md +124 -0
  51. package/skills/void/docs/guide/platform/development/schema-ci.md +95 -0
  52. package/skills/void/docs/guide/platform/installation/ci.md +55 -0
  53. package/skills/void/docs/guide/platform/installation/credentials.md +80 -0
  54. package/skills/void/docs/guide/platform/installation/domains.md +68 -0
  55. package/skills/void/docs/guide/platform/installation/first-deployment.md +82 -0
  56. package/skills/void/docs/guide/platform/installation/maintenance.md +137 -0
  57. package/skills/void/docs/guide/platform/installation/prerequisites.md +86 -0
  58. package/skills/void/docs/guide/platform/installation/setup.md +169 -0
  59. package/skills/void/docs/guide/platform/installation/uninstall.md +54 -0
  60. package/skills/void/docs/guide/platform-administration.md +6 -414
  61. package/skills/void/docs/guide/platform-development.md +5 -316
  62. package/skills/void/docs/guide/project-collaboration.md +1 -1
  63. package/skills/void/docs/guide/sandboxes.md +9 -24
  64. package/skills/void/docs/guide/self-hosted-platform.md +11 -694
  65. package/skills/void/docs/reference/api.md +34 -34
  66. package/skills/void/docs/reference/cli.md +28 -11
  67. package/skills/void/docs/reference/config.md +1 -1
  68. package/skills/void/docs/reference/resource-inference.md +10 -10
@@ -1501,7 +1501,7 @@ async function verifyInstalledPlatformAccessProtection(input) {
1501
1501
  }
1502
1502
  const existingWorkers = input.plan.actions.filter((action) => action.kind === "worker" && action.action === "update").map((action) => action.name);
1503
1503
  if (existingWorkers.length) {
1504
- const { verifyProtectionWorkerOrigins } = await import("./platform-auth-protection-Bhtvp0B_.mjs");
1504
+ const { verifyProtectionWorkerOrigins } = await import("./platform-auth-protection-Drl0qhrn.mjs");
1505
1505
  await verifyProtectionWorkerOrigins({
1506
1506
  request: input.client.request.bind(input.client),
1507
1507
  listWorkerDomains: input.client.listWorkerDomains.bind(input.client),
@@ -1599,6 +1599,7 @@ async function createPlatformInstallPlan(client, input, existing, options) {
1599
1599
  const enableEmail = options?.includeEmailGateway !== false && !!emailSharedZoneId && !!normalizedEmailSenderDomain;
1600
1600
  const capabilities = {
1601
1601
  ...existing?.deployment.capabilities,
1602
+ ...options?.enableSandbox ? { sandbox: true } : {},
1602
1603
  ...input.authentication ? {
1603
1604
  githubOAuth: input.authentication.providers.includes("github"),
1604
1605
  googleOAuth: input.authentication.providers.includes("google")
@@ -1629,13 +1630,20 @@ async function createPlatformInstallPlan(client, input, existing, options) {
1629
1630
  });
1630
1631
  const { emailSharedZoneId: configuredEmailZone, emailSenderDomain: configuredSenderDomain, ...deploymentWithoutEmailZone } = deployment;
1631
1632
  const { emailSharedZoneId: recordedEmailZone, emailSenderDomain: recordedSenderDomain, ...existingWithoutEmailZone } = existing?.deployment ?? deployment;
1632
- const comparableExistingWithoutEmailZone = existing && deployment.capabilities.email && !existing.deployment.capabilities.email ? {
1633
+ const existingWithSandbox = existing && options?.enableSandbox ? {
1633
1634
  ...existingWithoutEmailZone,
1634
1635
  capabilities: {
1635
1636
  ...existingWithoutEmailZone.capabilities,
1636
- email: true
1637
+ sandbox: true
1637
1638
  }
1638
1639
  } : existingWithoutEmailZone;
1640
+ const comparableExistingWithoutEmailZone = existing && deployment.capabilities.email && !existing.deployment.capabilities.email ? {
1641
+ ...existingWithSandbox,
1642
+ capabilities: {
1643
+ ...existingWithSandbox.capabilities,
1644
+ email: true
1645
+ }
1646
+ } : existingWithSandbox;
1639
1647
  if (existing && (JSON.stringify(comparableExistingWithoutEmailZone) !== JSON.stringify(deploymentWithoutEmailZone) || recordedEmailZone !== void 0 && recordedEmailZone !== configuredEmailZone || recordedSenderDomain !== void 0 && recordedSenderDomain !== configuredSenderDomain)) throw new Error(`platform: --resume inputs do not match installation ${existing.id}. Use its recorded configuration.`);
1640
1648
  const names = deployment.resources;
1641
1649
  const wildcardHostname = `*.${deployment.application.baseDomain}`;
@@ -1793,7 +1801,8 @@ async function revalidatePlatformInstallAction(client, original, manifest, actio
1793
1801
  zoneName: deployment.application.zoneName
1794
1802
  }, manifest, {
1795
1803
  ...options,
1796
- includeEmailGateway: original.actions.some((entry) => entry.key === "email-gateway-worker")
1804
+ includeEmailGateway: original.actions.some((entry) => entry.key === "email-gateway-worker"),
1805
+ enableSandbox: original.deployment.capabilities.sandbox
1797
1806
  })).actions.find((entry) => entry.key === action.key);
1798
1807
  if (!current || current.kind !== action.kind || current.name !== action.name || current.action !== action.action) throw new Error(`platform: Cloudflare changed after the installation plan was approved for ${action.kind} ${JSON.stringify(action.name)}. Review a new plan before retrying.`);
1799
1808
  if (action.kind === "worker" && action.action === "update") {
@@ -2771,7 +2780,8 @@ async function installPlatform(plan, secrets, existing, dependencies) {
2771
2780
  const targetSandboxDrainProtocol = runtime.safetyProtocols?.sandboxDrain ?? 0;
2772
2781
  const prepareSandboxDrain = dependencies.prepareSandboxDrain ?? releaseDatabaseLock.prepareSandboxDrain;
2773
2782
  if (targetSandboxDrainProtocol >= 1 && !prepareSandboxDrain) throw new Error("platform: The PostgreSQL lifecycle lock cannot establish the sandbox-drain safety checkpoint.");
2774
- const sandboxDrainPreparation = targetSandboxDrainProtocol >= 1 ? await prepareSandboxDrain(targetSandboxDrainProtocol >= 2 ? 2 : 0) : null;
2783
+ const sandboxDrainPreparation = targetSandboxDrainProtocol >= 1 ? await prepareSandboxDrain(targetSandboxDrainProtocol >= 3 ? 3 : targetSandboxDrainProtocol >= 2 ? 2 : 0) : null;
2784
+ if (sandboxDrainPreparation && sandboxDrainPreparation.admissionProtocol < targetSandboxDrainProtocol) throw new Error("platform: The database did not arm the Sandbox admission protocol required by this runtime.");
2775
2785
  const sandboxDrainRoutingKvId = requireId(Object.fromEntries(manifest.resources.map((resource) => [resource.key, resource.id])), "routing-kv");
2776
2786
  const writeSandboxDrainMirror = async (preparation) => {
2777
2787
  await dependencies.client.putKvValue(plan.account.id, sandboxDrainRoutingKvId, PLATFORM_SANDBOX_DRAIN_KEY, preparation.mirrorValue);
@@ -3836,14 +3846,14 @@ async function preparePlatformSandboxDrainConnection(client, requiredAdmissionPr
3836
3846
  AND table_name = 'sandbox_drain_checkpoints'
3837
3847
  AND column_name = 'admission_protocol'
3838
3848
  ) AS "present"`)).rows[0]?.present === true;
3839
- if (requiredAdmissionProtocol === 2 && !hasAdmissionColumn) throw new Error("platform: The runtime database does not include the sandbox deployment admission barrier.");
3849
+ if (requiredAdmissionProtocol >= 2 && !hasAdmissionColumn) throw new Error("platform: The runtime database does not include the sandbox deployment admission barrier.");
3840
3850
  const readCheckpoint = () => client.query(`SELECT ${hasAdmissionColumn ? "\"admission_protocol\"" : "0 AS \"admission_protocol\""}, "generation", "state", "phase", "cursor", "started_at", "updated_at", "completed_at"
3841
3851
  FROM "sandbox_drain_checkpoints"
3842
3852
  WHERE "singleton" = true`);
3843
3853
  const classify = (row) => {
3844
3854
  if (!row) return null;
3845
3855
  const cursor = row.cursor;
3846
- if (!row.generation || row.admission_protocol !== 0 && row.admission_protocol !== 2 || !row.started_at || !row.updated_at || row.state !== "draining" && row.state !== "complete" || row.phase !== "deployments" && row.phase !== "resources" || !cursor || cursor.pass !== "1" && cursor.pass !== "2" || row.state === "complete" && (!row.completed_at || row.phase !== "resources" || cursor.pass !== "2") || row.state === "draining" && row.completed_at !== null) throw new Error("platform: The authoritative sandbox-drain database checkpoint is invalid. Traffic remains disabled.");
3856
+ if (!row.generation || row.admission_protocol !== 0 && row.admission_protocol !== 2 && row.admission_protocol !== 3 || !row.started_at || !row.updated_at || row.state !== "draining" && row.state !== "complete" || row.phase !== "deployments" && row.phase !== "resources" || !cursor || cursor.pass !== "1" && cursor.pass !== "2" || row.state === "complete" && (!row.completed_at || row.phase !== "resources" || cursor.pass !== "2") || row.state === "draining" && row.completed_at !== null) throw new Error("platform: The authoritative sandbox-drain database checkpoint is invalid. Traffic remains disabled.");
3847
3857
  return {
3848
3858
  admissionProtocol: row.admission_protocol,
3849
3859
  generation: row.generation,
@@ -3860,7 +3870,23 @@ async function preparePlatformSandboxDrainConnection(client, requiredAdmissionPr
3860
3870
  };
3861
3871
  const existingState = classify((await readCheckpoint()).rows[0]);
3862
3872
  if (existingState && (requiredAdmissionProtocol === 0 || existingState.admissionProtocol >= requiredAdmissionProtocol)) return existingState;
3863
- if (requiredAdmissionProtocol === 2) {
3873
+ if (requiredAdmissionProtocol >= 2) {
3874
+ if (existingState && existingState.admissionProtocol >= 2) {
3875
+ await client.query("BEGIN");
3876
+ try {
3877
+ await client.query("LOCK TABLE \"deployments\" IN SHARE MODE");
3878
+ await client.query(`UPDATE "sandbox_drain_checkpoints"
3879
+ SET "admission_protocol" = GREATEST("admission_protocol", $1), "updated_at" = now()
3880
+ WHERE "singleton" = true`, [requiredAdmissionProtocol]);
3881
+ const advanced = classify((await readCheckpoint()).rows[0]);
3882
+ if (!advanced || advanced.admissionProtocol < requiredAdmissionProtocol) throw new Error("platform: The Sandbox controller admission barrier could not be armed.");
3883
+ await client.query("COMMIT");
3884
+ return advanced;
3885
+ } catch (error) {
3886
+ await client.query("ROLLBACK").catch(() => void 0);
3887
+ throw error;
3888
+ }
3889
+ }
3864
3890
  const generation = randomUUID();
3865
3891
  await client.query("BEGIN");
3866
3892
  try {
@@ -3893,8 +3919,10 @@ async function preparePlatformSandboxDrainConnection(client, requiredAdmissionPr
3893
3919
  WHERE "singleton" = true
3894
3920
  AND "admission_protocol" = 2
3895
3921
  AND "generation" = $1`, [generation]);
3922
+ if (requiredAdmissionProtocol >= 3) await client.query(`UPDATE "sandbox_drain_checkpoints" SET "admission_protocol" = $1
3923
+ WHERE "singleton" = true`, [requiredAdmissionProtocol]);
3896
3924
  const armed = classify((await readCheckpoint()).rows[0]);
3897
- if (!armed || armed.admissionProtocol !== 2) throw new Error("platform: The sandbox deployment admission barrier could not be armed.");
3925
+ if (!armed || armed.admissionProtocol < requiredAdmissionProtocol) throw new Error("platform: The sandbox deployment admission barrier could not be armed.");
3898
3926
  await client.query("COMMIT");
3899
3927
  return armed;
3900
3928
  } catch (error) {
@@ -3937,12 +3965,17 @@ async function readRequiredPlatformSafetyProtocols(client) {
3937
3965
  AND table_name = 'sandbox_drain_checkpoints'
3938
3966
  AND column_name = 'admission_protocol'
3939
3967
  ) AS "present"`)).rows[0]?.present === true) {
3940
- if ((await client.query(`SELECT EXISTS(
3968
+ const admission = await client.query(`SELECT EXISTS(
3941
3969
  SELECT 1
3942
3970
  FROM "sandbox_drain_checkpoints"
3943
3971
  WHERE "singleton" = true
3944
3972
  AND "admission_protocol" >= 2
3945
- ) AS "required"`)).rows[0]?.required === true) return { sandboxDrain: 2 };
3973
+ ) AS "required", (SELECT "admission_protocol" FROM "sandbox_drain_checkpoints"
3974
+ WHERE "singleton" = true) AS "protocol"`);
3975
+ if (admission.rows[0]?.required === true) {
3976
+ if (admission.rows[0].protocol != null && admission.rows[0].protocol > 3) throw new Error("platform: The database requires a newer Sandbox controller protocol.");
3977
+ return { sandboxDrain: admission.rows[0].protocol === 3 ? 3 : 2 };
3978
+ }
3946
3979
  }
3947
3980
  return { sandboxDrain: (await client.query(`SELECT EXISTS(
3948
3981
  SELECT 1
@@ -4817,7 +4850,7 @@ var PlatformAccessBlockedError = class extends Error {
4817
4850
  "Void’s own authentication stays enabled. Keep account-wide Default-Deny on.",
4818
4851
  "",
4819
4852
  "For platforms that must remain Access-protected:",
4820
- "https://void.cloud/guide/self-hosted-platform#cloudflare-access"
4853
+ "https://void.cloud/guide/platform/installation/maintenance#cloudflare-access"
4821
4854
  ].join("\n"));
4822
4855
  }
4823
4856
  };
@@ -5199,9 +5232,9 @@ function platformSetupLink(page, hyperlinks = supportsSetupLinks()) {
5199
5232
  return hyperlinks ? "\x1B]8;;" + page.url + "\x1B\\" + page.title + "\x1B]8;;\x1B\\" : page.title + ": " + page.url;
5200
5233
  }
5201
5234
  function platformSetupFieldHelp(key) {
5202
- const docs = (section, title) => platformSetupLink({
5235
+ const docs = (page, section, title) => platformSetupLink({
5203
5236
  title,
5204
- url: "https://void.cloud/guide/self-hosted-platform#" + section
5237
+ url: `https://void.cloud/guide/platform/installation/${page}#${section}`
5205
5238
  });
5206
5239
  switch (key) {
5207
5240
  case "databaseUrl": return {
@@ -5209,10 +5242,10 @@ function platformSetupFieldHelp(key) {
5209
5242
  description: "Stores platform users, projects, and deployments; use an empty hosted database, e.g. " + platformSetupLink({
5210
5243
  title: "Neon",
5211
5244
  url: "https://neon.com/"
5212
- }) + ". " + docs("platform-database", "Database setup")
5245
+ }) + ". " + docs("prerequisites", "platform-database", "Database setup")
5213
5246
  };
5214
- case "jwtSecret": return { description: "Signs platform login tokens. Run openssl rand -base64 32; save the key in your password manager. " + docs("signing-and-encryption-keys", "Key setup") };
5215
- case "projectSecretKey": return { description: "Encrypts app secrets. Run openssl rand -base64 32 separately; save this key for recovery. " + docs("signing-and-encryption-keys", "Key setup") };
5247
+ case "jwtSecret": return { description: "Signs platform login tokens. Run openssl rand -base64 32; save the key in your password manager. " + docs("credentials", "signing-and-encryption-keys", "Key setup") };
5248
+ case "projectSecretKey": return { description: "Encrypts app secrets. Run openssl rand -base64 32 separately; save this key for recovery. " + docs("credentials", "signing-and-encryption-keys", "Key setup") };
5216
5249
  default: return;
5217
5250
  }
5218
5251
  }
@@ -5407,14 +5440,14 @@ function resolvePlatformInstallDatabaseUrl(checkpoint, manifest = null) {
5407
5440
  }
5408
5441
  async function runPlatformLifecycleCommand(args) {
5409
5442
  if (args.subcommand === "domain") {
5410
- const { runPlatformDomainCommand } = await import("./platform-domain-C74PULqV.mjs");
5443
+ const { runPlatformDomainCommand } = await import("./platform-domain-4GiDlcqx.mjs");
5411
5444
  await runPlatformDomainCommand(args);
5412
5445
  return;
5413
5446
  }
5414
5447
  if (args.subcommand === "discover") {
5415
5448
  const client = new PlatformCloudflareClient((await resolvePlatformCloudflareCredential()).token);
5416
5449
  const accountId = await selectAccount(await readAccounts(client), args.account);
5417
- const { recoverPlatformInstallations } = await import("./platform-recovery-ewqLefp1.mjs");
5450
+ const { recoverPlatformInstallations } = await import("./platform-recovery-CbK-I1FB.mjs");
5418
5451
  const recovered = await recoverPlatformInstallations(client, accountId, void 0, args.selector);
5419
5452
  if (recovered.length === 0) {
5420
5453
  log.info(`No recoverable Void platform installations were found in ${accountId}.`);
@@ -5424,7 +5457,7 @@ async function runPlatformLifecycleCommand(args) {
5424
5457
  return;
5425
5458
  }
5426
5459
  if (args.subcommand !== "install") {
5427
- const { runInstalledPlatformCommand } = await import("./platform-management-COogu_Se.mjs");
5460
+ const { runInstalledPlatformCommand } = await import("./platform-management-BfWsXHEW.mjs");
5428
5461
  await runInstalledPlatformCommand(args);
5429
5462
  return;
5430
5463
  }
@@ -5574,7 +5607,7 @@ async function runPlatformLifecycleCommand(args) {
5574
5607
  progress?.stop("Read the saved installation.");
5575
5608
  progress = void 0;
5576
5609
  if (authoritative) {
5577
- const { selectAuthoritativePlatformManifest } = await import("./platform-management-COogu_Se.mjs");
5610
+ const { selectAuthoritativePlatformManifest } = await import("./platform-management-BfWsXHEW.mjs");
5578
5611
  const selected = selectAuthoritativePlatformManifest(existing, authoritative);
5579
5612
  authoritativeProvisionIntents = true;
5580
5613
  if (!args.plan && JSON.stringify(selected) !== JSON.stringify(existing)) writeInstallationManifest(selected);
@@ -5599,7 +5632,8 @@ async function runPlatformLifecycleCommand(args) {
5599
5632
  let plan = await createPlatformInstallPlan(client, planInput, existing, {
5600
5633
  authoritativeProvisionIntents,
5601
5634
  allowUnverifiedAiGateway: true,
5602
- includeEmailGateway: runtime.manifest.artifacts.emailGateway !== void 0
5635
+ includeEmailGateway: runtime.manifest.artifacts.emailGateway !== void 0,
5636
+ enableSandbox: runtime.manifest.safetyProtocols.sandboxDrain >= 3
5603
5637
  });
5604
5638
  note(formatPlatformInstallPlan(plan), args.plan ? "Read-only installation plan" : "Installation plan");
5605
5639
  note(formatPlatformRuntime(runtime), "Platform runtime");
@@ -5780,11 +5814,12 @@ async function runPlatformLifecycleCommand(args) {
5780
5814
  replanWorkers: async (manifest) => (await createPlatformInstallPlan(client, planInput, manifest, {
5781
5815
  authoritativeProvisionIntents: true,
5782
5816
  repairMissing: true,
5783
- includeEmailGateway: runtime.manifest.artifacts.emailGateway !== void 0
5817
+ includeEmailGateway: runtime.manifest.artifacts.emailGateway !== void 0,
5818
+ enableSandbox: runtime.manifest.safetyProtocols.sandboxDrain >= 3
5784
5819
  })).actions.filter((action) => action.kind === "worker"),
5785
5820
  revalidateBeforeWorkerDeploy: (manifest, action) => revalidatePlatformInstallAction(client, plan, manifest, action),
5786
5821
  verifyExistingOwnership: async (manifest, databaseCheckpointExists) => {
5787
- const { verifyPlatformInstallationOwnership } = await import("./platform-management-COogu_Se.mjs");
5822
+ const { verifyPlatformInstallationOwnership } = await import("./platform-management-BfWsXHEW.mjs");
5788
5823
  const retainedReplacementKeys = new Set(plan.actions.filter((action) => {
5789
5824
  const previous = manifest.resources.find((resource) => resource.key === action.key);
5790
5825
  return action.action === "create" && previous && previous.name !== action.name;
@@ -5849,7 +5884,8 @@ async function verifyPlatformAiGatewayPlan(client, input, existing, preview, aut
5849
5884
  try {
5850
5885
  verified = await createPlatformInstallPlan(client, input, existing, {
5851
5886
  authoritativeProvisionIntents,
5852
- includeEmailGateway: preview.actions.some((entry) => entry.key === "email-gateway-worker")
5887
+ includeEmailGateway: preview.actions.some((entry) => entry.key === "email-gateway-worker"),
5888
+ enableSandbox: preview.deployment.capabilities.sandbox
5853
5889
  });
5854
5890
  } catch (error) {
5855
5891
  if (error instanceof PlatformCloudflareApiError && (error.status === 401 || error.status === 403) && error.requestPath?.includes("/ai-gateway/")) throw new Error("platform: The runtime token could not verify AI Gateway access. Include Account → AI Gateway → Edit for this account, then retry. No platform resources were created.", { cause: error });
@@ -1,5 +1,5 @@
1
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";
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
@@ -72,7 +72,8 @@ async function runInstalledPlatformCommand(args) {
72
72
  authoritativeProvisionIntents: databaseCheckpointExists,
73
73
  repairMissing: args.subcommand === "repair",
74
74
  allowUnverifiedAiGateway: args.plan,
75
- includeEmailGateway: runtime.manifest.artifacts.emailGateway !== void 0
75
+ includeEmailGateway: runtime.manifest.artifacts.emailGateway !== void 0,
76
+ enableSandbox: runtime.manifest.safetyProtocols.sandboxDrain >= 3
76
77
  });
77
78
  note(formatPlatformInstallPlan(plan), args.subcommand === "upgrade" ? "Upgrade plan" : args.subcommand === "rollback" ? "Rollback plan" : args.subcommand === "enable" ? "Enable plan" : "Repair plan");
78
79
  note([
@@ -144,7 +145,8 @@ async function runInstalledPlatformCommand(args) {
144
145
  replanWorkers: async (recoveryManifest) => (await createPlatformInstallPlan(client, planInput, recoveryManifest, {
145
146
  authoritativeProvisionIntents: true,
146
147
  repairMissing: true,
147
- includeEmailGateway: runtime.manifest.artifacts.emailGateway !== void 0
148
+ includeEmailGateway: runtime.manifest.artifacts.emailGateway !== void 0,
149
+ enableSandbox: runtime.manifest.safetyProtocols.sandboxDrain >= 3
148
150
  })).actions.filter((action) => action.kind === "worker"),
149
151
  revalidateBeforeWorkerDeploy: (manifest, action) => revalidatePlatformInstallAction(client, plan, manifest, action, { repairMissing: args.subcommand === "repair" })
150
152
  });
@@ -1,7 +1,7 @@
1
1
  import { J as listInstallationManifests, W as acquirePlatformLifecycleLock, at as savePlatformConnection, st as writeInstallationManifest } from "./output-B0cfNSx5.mjs";
2
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";
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 = "") {
@@ -1,6 +1,6 @@
1
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
2
  import { c as getSiteDomain, l as getToken, x as readDeploymentPlatform } from "./auth-DPl6kck4.mjs";
3
- import { n as PlatformClient } from "./client-dHfSJvAN.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();
@@ -37,24 +37,24 @@ async function runProjectsCommand(root) {
37
37
  async function runProjectCommand(root, args) {
38
38
  const cloudflare = readDeploymentPlatform(root) === "cloudflare";
39
39
  if (args.subcommand === "status") {
40
- const { runListCommand } = await import("./list-DEE2S6mY.mjs");
40
+ const { runListCommand } = await import("./list-CvkK_G7k.mjs");
41
41
  return runListCommand(root, args.name || void 0);
42
42
  }
43
43
  if (args.subcommand === "link") {
44
44
  if (cloudflare) throw new Error("project: Direct Cloudflare apps are identified by the name in wrangler.jsonc and do not need linking.");
45
- const { runLink } = await import("./link-Rmvu2Wl_.mjs");
45
+ const { runLink } = await import("./link-D2kbqhWb.mjs");
46
46
  return runLink(root, args.name || void 0);
47
47
  }
48
48
  if (args.subcommand === "list") {
49
49
  if (cloudflare) {
50
- const { runListCommand } = await import("./list-DEE2S6mY.mjs");
50
+ const { runListCommand } = await import("./list-CvkK_G7k.mjs");
51
51
  return runListCommand(root);
52
52
  }
53
53
  return runProjectsCommand(root);
54
54
  }
55
55
  if (args.subcommand === "token") {
56
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");
57
+ const { runProjectTokenCommand } = await import("./project-token-Cirx7uwZ.mjs");
58
58
  return runProjectTokenCommand(root, args);
59
59
  }
60
60
  if (args.subcommand === "team") {
@@ -65,35 +65,35 @@ async function runProjectCommand(root, args) {
65
65
  }
66
66
  const accountInvitationAction = args.action === "pending" || args.action === "accept" || args.action === "decline";
67
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");
68
+ const { runProjectTeamCommand } = await import("./project-team-CGxsQe3_.mjs");
69
69
  return runProjectTeamCommand(root, args);
70
70
  }
71
71
  if (args.subcommand === "logs") {
72
- const { runLogsCommand } = await import("./logs-27FenuiC.mjs");
72
+ const { runLogsCommand } = await import("./logs-dLUFCapG.mjs");
73
73
  return runLogsCommand(root, args.filter, args.range, args.deployment || void 0, args.level || void 0);
74
74
  }
75
75
  if (args.subcommand === "requests") {
76
76
  if (cloudflare) throw new Error("project: Historical request analytics require the Void platform. Use `void project logs` for a live Cloudflare tail.");
77
- const { runRequestsCommand } = await import("./requests-CUExwGQQ.mjs");
77
+ const { runRequestsCommand } = await import("./requests-4Nq59hOr.mjs");
78
78
  return runRequestsCommand(root, args.range, args.status || void 0);
79
79
  }
80
80
  if (args.subcommand === "rollback") {
81
- const { runRollbackCommand } = await import("./rollback-CDNGU1gr.mjs");
81
+ const { runRollbackCommand } = await import("./rollback-Dr7u0Ljx.mjs");
82
82
  return runRollbackCommand(root, args.deployId || void 0);
83
83
  }
84
84
  if (args.subcommand === "cancel") {
85
85
  if (cloudflare) throw new Error("project: Direct Cloudflare deploys activate only after readiness succeeds, so there is no remote build to cancel.");
86
- const { runCancelDeployCommand } = await import("./cancel-deploy-C5qTOdLi.mjs");
86
+ const { runCancelDeployCommand } = await import("./cancel-deploy-CPbEQLMG.mjs");
87
87
  return runCancelDeployCommand(root, args.deployId || void 0);
88
88
  }
89
89
  if (args.subcommand === "delete") {
90
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.");
91
- const { runDeleteCommand } = await import("./delete-DouASY9P.mjs");
91
+ const { runDeleteCommand } = await import("./delete-BZ4-WaGm.mjs");
92
92
  return runDeleteCommand(root, args.name || void 0, { force: args.force });
93
93
  }
94
94
  if (args.subcommand === "purge-cache") {
95
95
  if (cloudflare) throw new Error("project: Direct Cloudflare ISR cache purge is not available yet; redeploy or purge the configured KV namespace explicitly.");
96
- const { runPurgeCacheCommand } = await import("./cache-IHn5MwBC.mjs");
96
+ const { runPurgeCacheCommand } = await import("./cache-D98YTqeE.mjs");
97
97
  return runPurgeCacheCommand(root, args.projectSlug || void 0);
98
98
  }
99
99
  }
@@ -1,6 +1,6 @@
1
1
  import { A as outro, D as log, E as intro, k as note, r as cliTitle } from "./output-B0cfNSx5.mjs";
2
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";
3
+ import { n as PlatformClient, o as parsePlatformErrorBody, s as platformErrorMessage, t as PlatformApiError } from "./client-BQBrZoCX.mjs";
4
4
  import { r as resolveProjectBySlug, t as getRequestedProjectSlug } from "./resolve-project--Vxawf7z.mjs";
5
5
  //#region src/cli/project-team.ts
6
6
  function clean(value) {
@@ -42,13 +42,15 @@ async function resolveProjectId(root, client, projectSlug) {
42
42
  }
43
43
  function invitationResponseError(error, apiUrl) {
44
44
  if (!(error instanceof PlatformApiError)) return error;
45
+ if (platformErrorMessage(parsePlatformErrorBody(error.body))) return error;
45
46
  let message;
46
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.`;
47
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.`;
48
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`.";
49
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.";
50
51
  else return error;
51
- return new Error(message, { cause: error });
52
+ error.message = message;
53
+ return error;
52
54
  }
53
55
  async function runProjectTeamCommand(root, args) {
54
56
  console.log();
@@ -1,6 +1,6 @@
1
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
2
  import { S as readProjectConfig, l as getToken } from "./auth-DPl6kck4.mjs";
3
- import { n as PlatformClient } from "./client-dHfSJvAN.mjs";
3
+ import { n as PlatformClient } from "./client-BQBrZoCX.mjs";
4
4
  import { r as resolveProjectBySlug } from "./resolve-project--Vxawf7z.mjs";
5
5
  //#region src/cli/project-token.ts
6
6
  async function resolveTarget(root, args) {
@@ -1,6 +1,6 @@
1
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
2
  import { S as readProjectConfig, l as getToken } from "./auth-DPl6kck4.mjs";
3
- import { n as PlatformClient } from "./client-dHfSJvAN.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();
@@ -1,6 +1,6 @@
1
1
  import { A as outro, C as cancel, D as log, E as intro, I as isCancel, M as select, a as dim, c as import_picocolors, i as createSpinner, o as ensureUtc, r as cliTitle, w as confirm } from "./output-B0cfNSx5.mjs";
2
2
  import { S as readProjectConfig, l as getToken, x as readDeploymentPlatform } from "./auth-DPl6kck4.mjs";
3
- import { n as PlatformClient } from "./client-dHfSJvAN.mjs";
3
+ import { n as PlatformClient } from "./client-BQBrZoCX.mjs";
4
4
  //#region src/cli/rollback.ts
5
5
  async function runRollbackCommand(root, deployId) {
6
6
  console.log();
@@ -1,6 +1,53 @@
1
1
  import { t as cfAccessHeaders } from "../../cf-access-DRsQRe6k.mjs";
2
2
  import { n as serializeR2Conditional } from "../../r2-conditions-D1Wk8i7b.mjs";
3
3
  //#region src/runtime/remote/client.ts
4
+ function readableMessage(value) {
5
+ if (typeof value !== "string") return;
6
+ const text = value.replace(/(?:\u001b\]|\u009d)[\s\S]*?(?:\u0007|\u001b\\|\u009c|$)/g, "").replace(/(?:\u001b\[|\u009b)[0-?]*[ -/]*[@-~]/g, "").replace(/[\u0000-\u001f\u007f-\u009f]/g, " ").trim();
7
+ if (!text || /^[{[]/.test(text) || /<\/?[a-z!][^>]*>/i.test(text)) return;
8
+ return text.length > 500 ? `${text.slice(0, 500)}…` : text;
9
+ }
10
+ function isGenericPermissionMessage(message) {
11
+ return /^(?:(?:HTTP\s*)?(?:401|403)\s*[:-]?\s*)?(?:forbidden|unauthorized|access denied|permission denied|invalid or expired token)[.!]?$/i.test(message);
12
+ }
13
+ async function proxyError(response) {
14
+ const reader = response.body?.getReader();
15
+ const decoder = new TextDecoder();
16
+ let text = "";
17
+ let remaining = 16384;
18
+ try {
19
+ while (reader && remaining > 0) {
20
+ const { done, value } = await reader.read();
21
+ if (done) break;
22
+ text += decoder.decode(value.subarray(0, remaining), { stream: true });
23
+ remaining -= value.byteLength;
24
+ }
25
+ text += decoder.decode();
26
+ } catch {} finally {
27
+ await reader?.cancel().catch(() => {});
28
+ reader?.releaseLock();
29
+ }
30
+ let message;
31
+ let error;
32
+ try {
33
+ const body = JSON.parse(text);
34
+ if (body && typeof body === "object" && !Array.isArray(body)) {
35
+ const payload = body;
36
+ message = readableMessage(payload.message);
37
+ error = readableMessage(payload.error);
38
+ }
39
+ } catch {
40
+ if (!response.headers.get("content-type")?.includes("text/html")) error = readableMessage(text);
41
+ }
42
+ if (message && isGenericPermissionMessage(message)) message = void 0;
43
+ if (!message && error && !isGenericPermissionMessage(error)) message = error;
44
+ if (!message) {
45
+ if (response.status === 401) message = "Remote access could not be authenticated. Sign in to the connected platform again, or restart the build to obtain a fresh prerender token.";
46
+ else if (response.status === 403) message = "Remote access could not be authorized. Check the selected platform and project, then ask a project admin or owner to confirm your access. If your access is correct, retry shortly.";
47
+ else message = error ?? "The remote service returned an unreadable error. Retry the request.";
48
+ }
49
+ return /* @__PURE__ */ new Error(`remote: Proxy request failed with ${response.status}: ${message}`);
50
+ }
4
51
  /**
5
52
  * HTTP client for the remote bindings proxy (proxy.void.cloud).
6
53
  *
@@ -37,10 +84,7 @@ var ProxyClient = class {
37
84
  }),
38
85
  redirect: "manual"
39
86
  });
40
- if (!res.ok) {
41
- const text = await res.text().catch(() => res.statusText);
42
- throw new Error(`remote: Proxy request failed with ${res.status}: '${text}'.`);
43
- }
87
+ if (!res.ok) throw await proxyError(res);
44
88
  return res;
45
89
  }
46
90
  /**
@@ -66,10 +110,7 @@ var ProxyClient = class {
66
110
  body,
67
111
  redirect: "manual"
68
112
  });
69
- if (!res.ok) {
70
- const text = await res.text().catch(() => res.statusText);
71
- throw new Error(`remote: Proxy request failed with ${res.status}: '${text}'.`);
72
- }
113
+ if (!res.ok) throw await proxyError(res);
73
114
  return res;
74
115
  }
75
116
  };
@@ -3,80 +3,28 @@ import { Sandbox, SandboxOptions } from "@cloudflare/sandbox";
3
3
  //#region src/runtime/sandbox.d.ts
4
4
  export declare const DEFAULT_SANDBOX_ID = "default";
5
5
  export declare const DEFAULT_SANDBOX_BINDING = "SANDBOX";
6
- export declare const SANDBOX_USAGE_QUEUE_BINDING = "USAGE_EVENTS_QUEUE";
7
- export declare const SANDBOX_GATE_BINDING = "__VOID_PROXY";
8
- export declare const HEARTBEAT_MS: number;
6
+ export declare const SANDBOX_SERVICE_BINDING = "__VOID_SANDBOX";
9
7
  export type VoidSandboxOptions = SandboxOptions & {
10
8
  binding?: string;
11
9
  };
12
- export type SandboxUsageEvent = Readonly<{
13
- requests: 0;
14
- responseBytes: 0;
15
- userId?: string;
16
- projectId?: string;
17
- slug?: string;
18
- sandboxRuntimeSeconds: number;
19
- sandboxInstanceId: string;
20
- }>;
21
- /**
22
- * Thrown by `getSandbox()` when the platform gate denies a new acquire because
23
- * the user has hit a concurrency or runtime budget cap. Surface this to the
24
- * caller so they can render a friendly error; do not retry transparently.
25
- */
26
10
  export declare class SandboxLimitError extends Error {
27
11
  override readonly name = "SandboxLimitError";
28
12
  readonly reason: "concurrency" | "runtime_budget";
29
13
  constructor(reason: "concurrency" | "runtime_budget", message?: string);
30
14
  }
31
- /** Managed platform Sandbox is unavailable or its activation gate cannot be verified. */
32
15
  export declare class SandboxUnavailableError extends Error {
33
16
  override readonly name = "SandboxUnavailableError";
34
17
  constructor(message?: string);
35
18
  }
36
19
  export declare function __setSandboxBindingName(bindingName: string): void;
37
20
  /**
38
- * Sandbox variant that participates in Void platform metering: it records
39
- * container runtime seconds into DO storage, emits periodic heartbeats to
40
- * the usage queue, and notifies the gate when the container stops.
41
- *
42
- * Generated code from `void inference` wires user `Sandbox` subclasses to
43
- * extend this class on platform deploys so per-tenant accounting works
44
- * without user opt-in. On local dev (no usage-queue / gate bindings), all
45
- * overrides degrade to no-ops and the sandbox behaves like the upstream
46
- * `Sandbox` class.
21
+ * Retained for generated native/local Worker entries. Managed deployments host
22
+ * the platform-owned Sandbox class in a separate Worker and never use this DO.
47
23
  */
48
24
  export declare class VoidPlatformSandbox<Env = unknown> extends Sandbox<Env> {
49
25
  override onStart(): Promise<void>;
50
- override alarm(alarmProps?: AlarmInvocationInfo): Promise<void>;
51
- override onStop(): Promise<void>;
52
- override onActivityExpired(): Promise<void>;
53
- private flushFinal;
54
26
  }
55
- /**
56
- * Resolve the platform-provided sandbox stub for the given id, optionally
57
- * coordinating with the platform gate to enforce per-user concurrency and
58
- * runtime budgets.
59
- *
60
- * When the gate binding (`__VOID_PROXY` by default) is present AND the
61
- * platform's `__PROJECT_ID` + `__VOID_PROXY_TOKEN` identity vars are
62
- * available, we POST to `https://gate/sandbox/acquire` with the HMAC-signed
63
- * body the shared proxy auth middleware expects. A `409` response means the
64
- * user has exceeded a budget — we throw `SandboxLimitError` so the caller
65
- * can render a clear message. Any other non-OK status (or a thrown transport
66
- * error from the service binding) fails closed before returning a usable
67
- * managed stub.
68
- *
69
- * When all reserved platform identity bindings are absent, this is a native
70
- * Cloudflare or local deployment and returns the upstream stub directly.
71
- *
72
- * The stub is constructed BEFORE calling the gate so we can pass its opaque,
73
- * namespace-specific Durable Object id (`stub.id.toString()`) as `sandboxId`.
74
- * The same id is available as `ctx.id.toString()` inside lifecycle hooks, so
75
- * acquire, heartbeat, and release use one globally unique instance identity
76
- * even when separate projects or deployments both request the name `default`.
77
- * Constructing the stub does NOT start a container — that only happens on
78
- * the first method call against the stub.
79
- */
27
+ /** Resolve an isolated Sandbox on the current deployment. Starts on first use. */
80
28
  export declare function getSandbox<T extends Sandbox = Sandbox>(id?: string, options?: VoidSandboxOptions): Promise<T>;
81
29
  export declare const sandbox: {
82
30
  get: typeof getSandbox;