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.
Files changed (109) hide show
  1. package/README.md +5 -1
  2. package/dist/{auth-W9WII-mN.mjs → auth-DPl6kck4.mjs} +46 -26
  3. package/dist/{auth-cmd-CAH62yDU.mjs → auth-cmd-CzwquNiP.mjs} +5 -4
  4. package/dist/auth-link-ElDTgF7j.mjs +28 -0
  5. package/dist/{build-cmd-CJvZvPQO.mjs → build-cmd-BpJe6boP.mjs} +3 -3
  6. package/dist/{cache-BlNeQjuP.mjs → cache-D98YTqeE.mjs} +3 -3
  7. package/dist/{cancel-deploy-CmlAZ9P6.mjs → cancel-deploy-CPbEQLMG.mjs} +3 -3
  8. package/dist/cf-access-DRsQRe6k.mjs +75 -0
  9. package/dist/cli/cli.mjs +309 -1958
  10. package/dist/cli/env-schema-probe.mjs +11 -2
  11. package/dist/client-BQBrZoCX.mjs +989 -0
  12. package/dist/{cloudflare-auth-B1QtTO1b.mjs → cloudflare-auth-6M5llVPC.mjs} +2 -2
  13. package/dist/{cloudflare-cmd-B6_OZx2V.mjs → cloudflare-cmd-4RPGN3KB.mjs} +2 -2
  14. package/dist/{cloudflare-connect-j5D4hhrG.mjs → cloudflare-connect-t1UU5svD.mjs} +2 -2
  15. package/dist/{cloudflare-operations-CPTpRW6d.mjs → cloudflare-operations-BzWnlC1_.mjs} +1 -1
  16. package/dist/{config-BQFq7QvD.mjs → config-uNGuFsI2.mjs} +1 -1
  17. package/dist/{connect-C04Wdy_h.mjs → connect-WCQZ_u3m.mjs} +6 -6
  18. package/dist/{create-project-ChGZ1DFd.mjs → create-project-D0oXA090.mjs} +7 -7
  19. package/dist/{db-D2d_mUsB.mjs → db-DJ-9qs3S.mjs} +47 -30
  20. package/dist/{delete-D8GigDk8.mjs → delete-BZ4-WaGm.mjs} +3 -3
  21. package/dist/{deploy-iXZ3F0N6.mjs → deploy-BAhhcg5q.mjs} +135 -117
  22. package/dist/{dev-inbox-DkgRWLkW.mjs → dev-inbox-P0u4tM8Y.mjs} +1 -1
  23. package/dist/{domain-B1VmoSr0.mjs → domain-BxAyhxXN.mjs} +4 -4
  24. package/dist/email-Bj7Cvdwp.mjs +795 -0
  25. package/dist/{env-D4Emu-M_.mjs → env-DBKmK4vc.mjs} +1 -0
  26. package/dist/{env-BcQzYgoG.mjs → env-DP_EErve.mjs} +5 -5
  27. package/dist/{env-validation-ENpMy6Ez.mjs → env-validation-CF6KvTRf.mjs} +3 -1
  28. package/dist/{gen-DI2YwdBM.mjs → gen-B_wPnVTK.mjs} +2 -2
  29. package/dist/{github-cmd-xItS5Zwf.mjs → github-cmd-C-z_xRrQ.mjs} +15 -21
  30. package/dist/{headers-D8QfRX9Y.mjs → headers-BAHwgHdW.mjs} +1 -1
  31. package/dist/help-CwOX-zmI.mjs +2216 -0
  32. package/dist/{inbound-afAcWeQ9.d.mts → inbound-CH5Mksyy.d.mts} +34 -48
  33. package/dist/{inbound-2d0zi2yS.mjs → inbound-aVHEUhKo.mjs} +130 -100
  34. package/dist/index.mjs +54 -17
  35. package/dist/{init-BD-9THgn.mjs → init-BGktCXgA.mjs} +11 -11
  36. package/dist/{link-RMdgjF1v.mjs → link-D2kbqhWb.mjs} +4 -4
  37. package/dist/{list-3F52R_yO.mjs → list-CvkK_G7k.mjs} +4 -4
  38. package/dist/{login-pV69H-ZO.mjs → login-WIjNc77c.mjs} +28 -10
  39. package/dist/{logs-DFHHD6wE.mjs → logs-dLUFCapG.mjs} +4 -4
  40. package/dist/{mime-BJD7d_qL.mjs → mime-D5Nmdzf7.mjs} +23 -9
  41. package/dist/{node-Dk3H2jmU.mjs → node-Ez5KW5rn.mjs} +2 -2
  42. package/dist/operator-auth-B3e08unv.mjs +52 -0
  43. package/dist/operator-client-LUZnlnYk.mjs +82 -0
  44. package/dist/{operator-cmd-DYWRbWUA.mjs → operator-cmd-CKJ7xIRs.mjs} +35 -55
  45. package/dist/{output-tFQLLj26.mjs → output-B0cfNSx5.mjs} +316 -2
  46. package/dist/pages/index.mjs +2 -2
  47. package/dist/platform-auth-config-CdVWRRJr.mjs +368 -0
  48. package/dist/platform-auth-protection-Drl0qhrn.mjs +219 -0
  49. package/dist/platform-auth-recovery-CmKEWpDo.mjs +310 -0
  50. package/dist/{platform-cmd-DxJ2FRwR.mjs → platform-cmd-5q_k56xS.mjs} +16 -6
  51. package/dist/{platform-domain-ChvbJkdy.mjs → platform-domain-4GiDlcqx.mjs} +4 -4
  52. package/dist/{platform-lifecycle-DN4MzJF_.mjs → platform-lifecycle-R9xAxvtG.mjs} +1716 -204
  53. package/dist/{platform-management-Db2PXw0B.mjs → platform-management-BfWsXHEW.mjs} +35 -7
  54. package/dist/{platform-recovery-C_YO-tIs.mjs → platform-recovery-CbK-I1FB.mjs} +6 -5
  55. package/dist/{prepare-CBetXvsN.mjs → prepare-CtDJjoOj.mjs} +2 -2
  56. package/dist/{prepare-BfJvFUtJ.mjs → prepare-blNRQvQl.mjs} +2 -2
  57. package/dist/prerender-render.d.mts +11 -0
  58. package/dist/prerender-render.mjs +111 -0
  59. package/dist/{project-cmd-Mo0V9yKS.mjs → project-cmd-CTdmnzvc.mjs} +32 -14
  60. package/dist/project-team-CGxsQe3_.mjs +132 -0
  61. package/dist/project-token-Cirx7uwZ.mjs +75 -0
  62. package/dist/{provision-Blnstcm2.mjs → provision-CSJOjjQk.mjs} +2 -0
  63. package/dist/{requests-BcKOVpRg.mjs → requests-4Nq59hOr.mjs} +3 -3
  64. package/dist/{rollback-Bx85-0xh.mjs → rollback-Dr7u0Ljx.mjs} +4 -4
  65. package/dist/runtime/ai.mjs +3 -2
  66. package/dist/runtime/email/testing.d.mts +1 -1
  67. package/dist/runtime/email/testing.mjs +3 -3
  68. package/dist/runtime/email-protocol.d.mts +15 -0
  69. package/dist/runtime/email-protocol.mjs +70 -0
  70. package/dist/runtime/email.d.mts +2 -2
  71. package/dist/runtime/email.mjs +189 -96
  72. package/dist/runtime/remote/index.mjs +54 -11
  73. package/dist/runtime/sandbox.d.mts +4 -56
  74. package/dist/runtime/sandbox.mjs +81 -220
  75. package/dist/{secret-ByhJ9AMl.mjs → secret-AcPi-FoA.mjs} +5 -5
  76. package/dist/{skills-Q46GZMO-.mjs → skills-C0RvGjeE.mjs} +1 -1
  77. package/dist/{subcommand-prompt-WfySCQ7S.mjs → subcommand-prompt-Bmyn5Rlc.mjs} +1 -1
  78. package/package.json +12 -7
  79. package/skills/void/SKILL.md +35 -4
  80. package/skills/void/docs/guide/deployment.md +16 -14
  81. package/skills/void/docs/guide/email.md +114 -120
  82. package/skills/void/docs/guide/platform/administration/access.md +163 -0
  83. package/skills/void/docs/guide/platform/administration/email.md +121 -0
  84. package/skills/void/docs/guide/platform/administration/operations.md +97 -0
  85. package/skills/void/docs/guide/platform/administration/projects.md +54 -0
  86. package/skills/void/docs/guide/platform/development/local.md +119 -0
  87. package/skills/void/docs/guide/platform/development/runtime.md +124 -0
  88. package/skills/void/docs/guide/platform/development/schema-ci.md +95 -0
  89. package/skills/void/docs/guide/platform/installation/ci.md +55 -0
  90. package/skills/void/docs/guide/platform/installation/credentials.md +80 -0
  91. package/skills/void/docs/guide/platform/installation/domains.md +68 -0
  92. package/skills/void/docs/guide/platform/installation/first-deployment.md +82 -0
  93. package/skills/void/docs/guide/platform/installation/maintenance.md +137 -0
  94. package/skills/void/docs/guide/platform/installation/prerequisites.md +86 -0
  95. package/skills/void/docs/guide/platform/installation/setup.md +169 -0
  96. package/skills/void/docs/guide/platform/installation/uninstall.md +54 -0
  97. package/skills/void/docs/guide/platform-administration.md +6 -202
  98. package/skills/void/docs/guide/platform-development.md +5 -254
  99. package/skills/void/docs/guide/project-collaboration.md +94 -0
  100. package/skills/void/docs/guide/sandboxes.md +9 -24
  101. package/skills/void/docs/guide/self-hosted-platform.md +13 -542
  102. package/skills/void/docs/reference/api.md +34 -34
  103. package/skills/void/docs/reference/cli.md +276 -31
  104. package/skills/void/docs/reference/config.md +1 -1
  105. package/skills/void/docs/reference/resource-inference.md +10 -10
  106. package/dist/cf-access-AJ1ehiFR.mjs +0 -42
  107. package/dist/cf-access-DsSsZUPr.mjs +0 -67
  108. package/dist/client-Clirrol3.mjs +0 -705
  109. package/dist/email-uKyQYUVY.mjs +0 -1016
@@ -1,6 +1,53 @@
1
- import { t as cfAccessHeaders } from "../../cf-access-AJ1ehiFR.mjs";
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
  *
@@ -34,12 +81,10 @@ var ProxyClient = class {
34
81
  body: JSON.stringify({
35
82
  ...body,
36
83
  projectId: this.projectId
37
- })
84
+ }),
85
+ redirect: "manual"
38
86
  });
39
- if (!res.ok) {
40
- const text = await res.text().catch(() => res.statusText);
41
- throw new Error(`remote: Proxy request failed with ${res.status}: '${text}'.`);
42
- }
87
+ if (!res.ok) throw await proxyError(res);
43
88
  return res;
44
89
  }
45
90
  /**
@@ -62,12 +107,10 @@ var ProxyClient = class {
62
107
  ...init.headers,
63
108
  ...this.authHeaders
64
109
  },
65
- body
110
+ body,
111
+ redirect: "manual"
66
112
  });
67
- if (!res.ok) {
68
- const text = await res.text().catch(() => res.statusText);
69
- throw new Error(`remote: Proxy request failed with ${res.status}: '${text}'.`);
70
- }
113
+ if (!res.ok) throw await proxyError(res);
71
114
  return res;
72
115
  }
73
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;
@@ -4,14 +4,7 @@ import { Sandbox, getSandbox as getSandbox$1 } from "@cloudflare/sandbox";
4
4
  //#region src/runtime/sandbox.ts
5
5
  const DEFAULT_SANDBOX_ID = "default";
6
6
  const DEFAULT_SANDBOX_BINDING = "SANDBOX";
7
- const SANDBOX_USAGE_QUEUE_BINDING = "USAGE_EVENTS_QUEUE";
8
- const SANDBOX_GATE_BINDING = "__VOID_PROXY";
9
- const HEARTBEAT_MS = 3e5;
10
- /**
11
- * Thrown by `getSandbox()` when the platform gate denies a new acquire because
12
- * the user has hit a concurrency or runtime budget cap. Surface this to the
13
- * caller so they can render a friendly error; do not retry transparently.
14
- */
7
+ const SANDBOX_SERVICE_BINDING = "__VOID_SANDBOX";
15
8
  var SandboxLimitError = class extends Error {
16
9
  name = "SandboxLimitError";
17
10
  reason;
@@ -20,7 +13,6 @@ var SandboxLimitError = class extends Error {
20
13
  this.reason = reason;
21
14
  }
22
15
  };
23
- /** Managed platform Sandbox is unavailable or its activation gate cannot be verified. */
24
16
  var SandboxUnavailableError = class extends Error {
25
17
  name = "SandboxUnavailableError";
26
18
  constructor(message = "Managed Sandbox is unavailable on this Void platform.") {
@@ -31,233 +23,102 @@ let sandboxBindingName = DEFAULT_SANDBOX_BINDING;
31
23
  function __setSandboxBindingName(bindingName) {
32
24
  sandboxBindingName = bindingName;
33
25
  }
34
- const STORAGE_KEY_START_TS = "__voidStartTs";
35
- const STORAGE_KEY_LAST_FLUSH_TS = "__voidLastFlushTs";
36
- function getUsageContext() {
37
- const runtimeEnv = getRawRuntimeEnv();
38
- return {
39
- userId: runtimeEnv.__VOID_USER_ID,
40
- projectId: runtimeEnv.__PROJECT_ID,
41
- slug: runtimeEnv.__VOID_PROJECT_SLUG
42
- };
43
- }
44
- function getSandboxInstanceId(ctx) {
45
- return ctx.id.toString();
46
- }
47
- /**
48
- * Flush the accumulated runtime delta to the usage queue. No-op when the
49
- * usage-queue binding is not bound (e.g. local dev without the platform
50
- * proxy). Errors are swallowed and logged — usage emission must never crash
51
- * the sandbox lifecycle.
52
- */
53
- async function emitRuntimeSeconds(ctx, sandboxRuntimeSeconds) {
54
- if (sandboxRuntimeSeconds <= 0) return;
55
- const queue = getRawRuntimeEnv()[SANDBOX_USAGE_QUEUE_BINDING];
56
- if (!queue) return;
57
- const event = {
58
- requests: 0,
59
- responseBytes: 0,
60
- sandboxRuntimeSeconds,
61
- sandboxInstanceId: getSandboxInstanceId(ctx),
62
- ...getUsageContext()
63
- };
64
- try {
65
- await queue.send(event);
66
- } catch (error) {
67
- console.error("[void/sandbox] failed to emit usage event:", error);
68
- }
69
- }
70
- function isManagedPlatformSandbox(runtimeEnv) {
71
- return [
72
- SANDBOX_GATE_BINDING,
73
- "__PROJECT_ID",
74
- "__VOID_PROXY_TOKEN",
75
- "__VOID_USER_ID",
76
- "__VOID_PROJECT_SLUG"
77
- ].some((binding) => runtimeEnv[binding] !== void 0);
26
+ function rethrowSandboxError(error) {
27
+ const message = error instanceof Error ? error.message : String(error);
28
+ for (const reason of ["concurrency", "runtime_budget"]) if (message.includes(`VOID_SANDBOX_${reason}`)) throw new SandboxLimitError(reason);
29
+ if (message.includes("VOID_SANDBOX_unavailable")) throw new SandboxUnavailableError();
30
+ throw error;
78
31
  }
79
- function resolveGateIdentity(runtimeEnv = getRawRuntimeEnv()) {
80
- const gate = runtimeEnv[SANDBOX_GATE_BINDING];
81
- if (!gate) return null;
82
- const projectId = runtimeEnv.__PROJECT_ID;
83
- const aiToken = runtimeEnv.__VOID_PROXY_TOKEN;
84
- if (!projectId || !aiToken) return null;
85
- return {
86
- gate,
87
- projectId,
88
- aiToken
32
+ function managedClient(capability) {
33
+ const call = async (target, operation, args) => {
34
+ try {
35
+ return decodeManagedResult(await target.invoke(operation, prepareManagedArgs(operation, args)));
36
+ } catch (error) {
37
+ rethrowSandboxError(error);
38
+ }
89
39
  };
90
- }
91
- async function acquireManagedSandbox(sandboxId, runtimeEnv = getRawRuntimeEnv()) {
92
- if (!isManagedPlatformSandbox(runtimeEnv)) return;
93
- const identity = resolveGateIdentity(runtimeEnv);
94
- if (!identity) throw new SandboxUnavailableError("Managed Sandbox activation could not verify the platform identity bindings.");
95
- let response;
96
- try {
97
- response = await identity.gate.fetch("https://gate/sandbox/acquire", {
98
- method: "POST",
99
- headers: { "content-type": "application/json" },
100
- body: JSON.stringify({
101
- projectId: identity.projectId,
102
- aiToken: identity.aiToken,
103
- sandboxId
104
- })
105
- });
106
- } catch {
107
- throw new SandboxUnavailableError("Managed Sandbox activation could not reach the platform gate.");
40
+ return new Proxy(Object.create(null), { get(_target, property) {
41
+ if (typeof property !== "string" || property === "then") return;
42
+ if (property === "tunnels") return new Proxy(Object.create(null), { get(_tunnels, method) {
43
+ if (typeof method !== "string" || method === "then") return;
44
+ return (...args) => call(capability, "tunnels", [method, ...args]);
45
+ } });
46
+ return (...args) => call(capability, property, args);
47
+ } });
48
+ function decodeManagedResult(value) {
49
+ if (Array.isArray(value)) return value.map(decodeManagedResult);
50
+ if (!value || typeof value !== "object") return value;
51
+ const record = value;
52
+ if (record.__voidSandboxType === "process") {
53
+ const { capability: processCapability, __voidSandboxType: _type, ...snapshot } = value;
54
+ return Object.assign(snapshot, {
55
+ kill: (...args) => call(processCapability, "kill", args),
56
+ getStatus: (...args) => call(processCapability, "getStatus", args),
57
+ getLogs: (...args) => call(processCapability, "getLogs", args),
58
+ waitForLog: (...args) => call(processCapability, "waitForLog", args),
59
+ waitForPort: (...args) => call(processCapability, "waitForPort", args),
60
+ waitForExit: (...args) => call(processCapability, "waitForExit", args)
61
+ });
62
+ }
63
+ if (record.__voidSandboxType === "session") {
64
+ const session = value;
65
+ return new Proxy({ id: session.id }, { get(target, property) {
66
+ if (property === "id") return target.id;
67
+ if (typeof property !== "string" || property === "then") return;
68
+ return (...args) => call(session.capability, property, args);
69
+ } });
70
+ }
71
+ return value;
108
72
  }
109
- if (response.status === 409) {
110
- const body = await response.json().catch(() => ({}));
111
- throw new SandboxLimitError(body.reason === "concurrency" || body.reason === "runtime_budget" ? body.reason : "concurrency");
73
+ function prepareManagedArgs(operation, args) {
74
+ if (operation !== "startProcess") return args;
75
+ const [command, options, ...rest] = args;
76
+ if (!options || typeof options !== "object" || Array.isArray(options)) return args;
77
+ const onStart = options.onStart;
78
+ if (typeof onStart !== "function") return args;
79
+ return [
80
+ command,
81
+ {
82
+ ...options,
83
+ onStart: (process) => onStart(decodeManagedResult(process))
84
+ },
85
+ ...rest
86
+ ];
112
87
  }
113
- if (!response.ok) throw new SandboxUnavailableError(`Managed Sandbox activation was rejected by the platform gate (HTTP ${response.status}).`);
114
- }
115
- /**
116
- * Release notification to the gate so it can drop the active lease counter.
117
- * Returns the in-flight fetch as a Promise (errors swallowed) so callers can
118
- * route it through `ctx.waitUntil()` and keep it alive past lifecycle
119
- * teardown. Failure is silent — the gate maintains its own TTL fallback.
120
- */
121
- function notifyGateRelease(ctx) {
122
- const identity = resolveGateIdentity();
123
- if (!identity) return Promise.resolve();
124
- const sandboxId = getSandboxInstanceId(ctx);
125
- return identity.gate.fetch("https://gate/sandbox/release", {
126
- method: "POST",
127
- headers: { "content-type": "application/json" },
128
- body: JSON.stringify({
129
- projectId: identity.projectId,
130
- aiToken: identity.aiToken,
131
- sandboxId
132
- })
133
- }).then(() => void 0).catch(() => {});
134
88
  }
135
89
  /**
136
- * Heartbeat-driven keepalive ping for the gate's concurrency slot. The DO's
137
- * `STALE_MS` (1h) sweep would otherwise reap slots for long-running sandbox
138
- * sessions whose `acquiredAt` ages past the threshold between acquire and
139
- * release, letting new acquires exceed `sandboxMaxConcurrentInstances`. The
140
- * 5 min heartbeat sits well inside the sweep window, so each successful
141
- * refresh resets the stale clock. Returns the in-flight fetch as a Promise
142
- * (errors swallowed) so callers can route it through `ctx.waitUntil()` and
143
- * keep the alarm path itself off the critical path — a failed refresh just
144
- * means the slot ages out naturally, which matches the pre-existing failure
145
- * mode if the gate goes down entirely.
146
- */
147
- function notifyGateRefresh(ctx) {
148
- const identity = resolveGateIdentity();
149
- if (!identity) return Promise.resolve();
150
- const sandboxId = getSandboxInstanceId(ctx);
151
- return identity.gate.fetch("https://gate/sandbox/refresh", {
152
- method: "POST",
153
- headers: { "content-type": "application/json" },
154
- body: JSON.stringify({
155
- projectId: identity.projectId,
156
- aiToken: identity.aiToken,
157
- sandboxId
158
- })
159
- }).then(() => void 0).catch(() => {});
160
- }
161
- /**
162
- * Sandbox variant that participates in Void platform metering: it records
163
- * container runtime seconds into DO storage, emits periodic heartbeats to
164
- * the usage queue, and notifies the gate when the container stops.
165
- *
166
- * Generated code from `void inference` wires user `Sandbox` subclasses to
167
- * extend this class on platform deploys so per-tenant accounting works
168
- * without user opt-in. On local dev (no usage-queue / gate bindings), all
169
- * overrides degrade to no-ops and the sandbox behaves like the upstream
170
- * `Sandbox` class.
90
+ * Retained for generated native/local Worker entries. Managed deployments host
91
+ * the platform-owned Sandbox class in a separate Worker and never use this DO.
171
92
  */
172
93
  var VoidPlatformSandbox = class extends Sandbox {
173
94
  async onStart() {
174
- await acquireManagedSandbox(getSandboxInstanceId(this.ctx), this.env);
175
- const now = Date.now();
176
- const stored = await this.ctx.storage.get([STORAGE_KEY_START_TS, STORAGE_KEY_LAST_FLUSH_TS]);
177
- const orphanedAnchor = stored.get(STORAGE_KEY_LAST_FLUSH_TS) ?? stored.get(STORAGE_KEY_START_TS);
178
- if (orphanedAnchor !== void 0 && orphanedAnchor < now) {
179
- const sandboxRuntimeSeconds = (now - orphanedAnchor) / 1e3;
180
- await emitRuntimeSeconds(this.ctx, sandboxRuntimeSeconds);
181
- await this.ctx.storage.delete([STORAGE_KEY_START_TS, STORAGE_KEY_LAST_FLUSH_TS]);
182
- }
183
- try {
184
- await super.onStart();
185
- } catch (err) {
186
- this.ctx.waitUntil(notifyGateRelease(this.ctx));
187
- throw err;
188
- }
189
- const startedAt = Date.now();
190
- await this.ctx.storage.put({
191
- [STORAGE_KEY_START_TS]: startedAt,
192
- [STORAGE_KEY_LAST_FLUSH_TS]: startedAt
193
- });
194
- await this.ctx.storage.setAlarm(startedAt + HEARTBEAT_MS);
195
- }
196
- async alarm(alarmProps) {
197
- await super.alarm(alarmProps);
198
- const lastFlush = await this.ctx.storage.get(STORAGE_KEY_LAST_FLUSH_TS);
199
- if (typeof lastFlush !== "number") return;
200
- const now = Date.now();
201
- const sandboxRuntimeSeconds = Math.max(0, (now - lastFlush) / 1e3);
202
- await emitRuntimeSeconds(this.ctx, sandboxRuntimeSeconds);
203
- this.ctx.waitUntil(notifyGateRefresh(this.ctx));
204
- await Promise.all([this.ctx.storage.put(STORAGE_KEY_LAST_FLUSH_TS, now), this.ctx.storage.setAlarm(now + HEARTBEAT_MS)]);
205
- }
206
- async onStop() {
207
- await this.flushFinal();
208
- this.ctx.waitUntil(notifyGateRelease(this.ctx));
209
- await super.onStop();
210
- }
211
- async onActivityExpired() {
212
- await this.flushFinal();
213
- this.ctx.waitUntil(notifyGateRelease(this.ctx));
214
- await super.onActivityExpired();
215
- }
216
- async flushFinal() {
217
- const lastFlush = await this.ctx.storage.get(STORAGE_KEY_LAST_FLUSH_TS);
218
- if (typeof lastFlush === "number") {
219
- const sandboxRuntimeSeconds = Math.max(0, (Date.now() - lastFlush) / 1e3);
220
- await emitRuntimeSeconds(this.ctx, sandboxRuntimeSeconds);
221
- }
222
- await this.ctx.storage.delete(STORAGE_KEY_START_TS);
223
- await this.ctx.storage.delete(STORAGE_KEY_LAST_FLUSH_TS);
95
+ const env = this.env;
96
+ if (env.__PROJECT_ID !== void 0 || env.__VOID_PROXY !== void 0) throw new SandboxUnavailableError("Managed Sandboxes require the platform Sandbox controller. Redeploy this application.");
97
+ await super.onStart();
224
98
  }
225
99
  };
226
- /**
227
- * Resolve the platform-provided sandbox stub for the given id, optionally
228
- * coordinating with the platform gate to enforce per-user concurrency and
229
- * runtime budgets.
230
- *
231
- * When the gate binding (`__VOID_PROXY` by default) is present AND the
232
- * platform's `__PROJECT_ID` + `__VOID_PROXY_TOKEN` identity vars are
233
- * available, we POST to `https://gate/sandbox/acquire` with the HMAC-signed
234
- * body the shared proxy auth middleware expects. A `409` response means the
235
- * user has exceeded a budget — we throw `SandboxLimitError` so the caller
236
- * can render a clear message. Any other non-OK status (or a thrown transport
237
- * error from the service binding) fails closed before returning a usable
238
- * managed stub.
239
- *
240
- * When all reserved platform identity bindings are absent, this is a native
241
- * Cloudflare or local deployment and returns the upstream stub directly.
242
- *
243
- * The stub is constructed BEFORE calling the gate so we can pass its opaque,
244
- * namespace-specific Durable Object id (`stub.id.toString()`) as `sandboxId`.
245
- * The same id is available as `ctx.id.toString()` inside lifecycle hooks, so
246
- * acquire, heartbeat, and release use one globally unique instance identity
247
- * even when separate projects or deployments both request the name `default`.
248
- * Constructing the stub does NOT start a container — that only happens on
249
- * the first method call against the stub.
250
- */
100
+ /** Resolve an isolated Sandbox on the current deployment. Starts on first use. */
251
101
  async function getSandbox(id = DEFAULT_SANDBOX_ID, options) {
252
102
  const { binding = sandboxBindingName, ...sandboxOptions } = options ?? {};
253
- const namespace = requireRuntimeBinding(binding);
254
- const stub = getSandbox$1(namespace, id, {
103
+ const runtimeEnv = getRawRuntimeEnv();
104
+ const service = runtimeEnv[SANDBOX_SERVICE_BINDING];
105
+ if (service) {
106
+ if (binding !== sandboxBindingName) throw new SandboxUnavailableError(`Managed Sandbox binding ${binding} is unavailable.`);
107
+ try {
108
+ return managedClient(await service.getSandbox(id, {
109
+ normalizeId: true,
110
+ ...sandboxOptions
111
+ }));
112
+ } catch (error) {
113
+ rethrowSandboxError(error);
114
+ }
115
+ }
116
+ if (runtimeEnv.__PROJECT_ID !== void 0 || runtimeEnv.__VOID_PROXY !== void 0) throw new SandboxUnavailableError("This deployment has no managed Sandbox controller. Redeploy this application.");
117
+ return getSandbox$1(requireRuntimeBinding(binding), id, {
255
118
  normalizeId: true,
256
119
  ...sandboxOptions
257
120
  });
258
- await acquireManagedSandbox(stub.id.toString());
259
- return stub;
260
121
  }
261
122
  const sandbox = { get: getSandbox };
262
123
  //#endregion
263
- export { DEFAULT_SANDBOX_BINDING, DEFAULT_SANDBOX_ID, HEARTBEAT_MS, SANDBOX_GATE_BINDING, SANDBOX_USAGE_QUEUE_BINDING, Sandbox, SandboxLimitError, SandboxUnavailableError, VoidPlatformSandbox, __setSandboxBindingName, getSandbox, sandbox };
124
+ export { DEFAULT_SANDBOX_BINDING, DEFAULT_SANDBOX_ID, SANDBOX_SERVICE_BINDING, Sandbox, SandboxLimitError, SandboxUnavailableError, VoidPlatformSandbox, __setSandboxBindingName, getSandbox, sandbox };
@@ -1,7 +1,7 @@
1
- import { A as password, C as cancel, D as log, E as intro, F as isCancel, O as note, k as outro, r as cliTitle, w as confirm } from "./output-tFQLLj26.mjs";
2
- import { b as readDeploymentPlatform, c as getToken, x as readProjectConfig } from "./auth-W9WII-mN.mjs";
3
- import { m as validateSecretValues } from "./env-validation-ENpMy6Ez.mjs";
4
- import { n as PlatformClient } from "./client-Clirrol3.mjs";
1
+ import { A as outro, C as cancel, D as log, E as intro, I as isCancel, j as password, k as note, r as cliTitle, w as confirm } from "./output-B0cfNSx5.mjs";
2
+ import { S as readProjectConfig, l as getToken, x as readDeploymentPlatform } from "./auth-DPl6kck4.mjs";
3
+ import { m as validateSecretValues } from "./env-validation-CF6KvTRf.mjs";
4
+ import { n as PlatformClient } from "./client-BQBrZoCX.mjs";
5
5
  import { r as resolveProjectBySlug, t as getRequestedProjectSlug } from "./resolve-project--Vxawf7z.mjs";
6
6
  import { readFileSync } from "node:fs";
7
7
  //#region src/cli/secret.ts
@@ -76,7 +76,7 @@ async function runSecretCommand(root, args) {
76
76
  }
77
77
  async function runCloudflareSecretCommand(root, args) {
78
78
  if (args.projectName) throw new Error("secret: --project selects a Void platform project and is not used for direct Cloudflare apps.");
79
- const { deleteCloudflareSecret, listCloudflareSecrets, putCloudflareSecret, resolveCloudflareOperationContext, syncCloudflareSecrets } = await import("./cloudflare-operations-CPTpRW6d.mjs").then((n) => n.t);
79
+ const { deleteCloudflareSecret, listCloudflareSecrets, putCloudflareSecret, resolveCloudflareOperationContext, syncCloudflareSecrets } = await import("./cloudflare-operations-BzWnlC1_.mjs").then((n) => n.t);
80
80
  const context = resolveCloudflareOperationContext(root);
81
81
  if (args.action === "list") {
82
82
  const secrets = await listCloudflareSecrets(context);
@@ -1,5 +1,5 @@
1
1
  import { c as relative, o as join } from "./dist-BrsS7cai.mjs";
2
- import { D as log, p as getPackageDir } from "./output-tFQLLj26.mjs";
2
+ import { D as log, p as getPackageDir } from "./output-B0cfNSx5.mjs";
3
3
  import { existsSync, mkdirSync, readFileSync, readdirSync, readlinkSync, symlinkSync } from "node:fs";
4
4
  //#region src/cli/skills.ts
5
5
  function parseSkills(skillsDir) {
@@ -1,4 +1,4 @@
1
- import { R as wrapAnsi, S as S_STEP_SUBMIT, _ as S_CHECKBOX_INACTIVE, b as S_RADIO_INACTIVE, c as import_picocolors, g as S_CHECKBOX_ACTIVE, h as S_BAR_END, m as S_BAR, x as S_STEP_ACTIVE, y as S_RADIO_ACTIVE } from "./output-tFQLLj26.mjs";
1
+ import { S as S_STEP_SUBMIT, _ as S_CHECKBOX_INACTIVE, b as S_RADIO_INACTIVE, c as import_picocolors, g as S_CHECKBOX_ACTIVE, h as S_BAR_END, m as S_BAR, x as S_STEP_ACTIVE, y as S_RADIO_ACTIVE, z as wrapAnsi } from "./output-B0cfNSx5.mjs";
2
2
  //#region src/cli/subcommand-prompt.ts
3
3
  function promptSubcommand(command, subcommands) {
4
4
  return new Promise((resolve) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "void",
3
- "version": "0.20.1",
3
+ "version": "0.20.3",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/voidzero-dev/void.git",
@@ -218,6 +218,11 @@
218
218
  "import": "./dist/runtime/email.mjs",
219
219
  "require": "./dist/runtime/email.mjs"
220
220
  },
221
+ "./_email-protocol": {
222
+ "types": "./dist/runtime/email-protocol.d.mts",
223
+ "import": "./dist/runtime/email-protocol.mjs",
224
+ "require": "./dist/runtime/email-protocol.mjs"
225
+ },
221
226
  "./email/testing": {
222
227
  "types": "./dist/runtime/email/testing.d.mts",
223
228
  "import": "./dist/runtime/email/testing.mjs",
@@ -313,11 +318,11 @@
313
318
  "@cloudflare/vite-plugin": "1.54.7",
314
319
  "@cloudflare/workers-types": "^4.20260702.1",
315
320
  "@napi-rs/keyring": "2.0.0",
316
- "@void/deploy-cloudflare": "0.20.1",
317
- "@void/deploy-core": "0.20.1",
318
- "@void/edge": "0.20.1",
319
- "@void/isr": "0.20.1",
320
- "@void/platform": "0.20.1",
321
+ "@void/deploy-cloudflare": "0.20.3",
322
+ "@void/deploy-core": "0.20.3",
323
+ "@void/edge": "0.20.3",
324
+ "@void/isr": "0.20.3",
325
+ "@void/platform": "0.20.3",
321
326
  "better-auth": "^1.7.3",
322
327
  "better-sqlite3": "^13.0.3",
323
328
  "blake3-jit": "^1.1.0",
@@ -363,7 +368,7 @@
363
368
  "zod": "^4.6.1"
364
369
  },
365
370
  "peerDependencies": {
366
- "@void/md": "0.20.1",
371
+ "@void/md": "0.20.3",
367
372
  "arktype": ">=2.0.0",
368
373
  "valibot": ">=1.0.0-beta.7",
369
374
  "vite": "^8.0.0",