@vercel/sandbox 2.0.0-beta.2 → 2.0.0-beta.20

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 (208) hide show
  1. package/README.md +48 -1
  2. package/dist/_virtual/rolldown_runtime.cjs +29 -0
  3. package/dist/api-client/api-client.cjs +445 -0
  4. package/dist/api-client/api-client.cjs.map +1 -0
  5. package/dist/api-client/api-client.d.cts +6235 -0
  6. package/dist/api-client/api-client.d.ts +6229 -706
  7. package/dist/api-client/api-client.js +436 -472
  8. package/dist/api-client/api-client.js.map +1 -1
  9. package/dist/api-client/api-error.cjs +32 -0
  10. package/dist/api-client/api-error.cjs.map +1 -0
  11. package/dist/api-client/api-error.d.cts +29 -0
  12. package/dist/api-client/api-error.d.ts +21 -16
  13. package/dist/api-client/api-error.js +28 -32
  14. package/dist/api-client/api-error.js.map +1 -1
  15. package/dist/api-client/base-client.cjs +126 -0
  16. package/dist/api-client/base-client.cjs.map +1 -0
  17. package/dist/api-client/base-client.d.cts +38 -0
  18. package/dist/api-client/base-client.d.ts +31 -36
  19. package/dist/api-client/base-client.js +114 -118
  20. package/dist/api-client/base-client.js.map +1 -1
  21. package/dist/api-client/file-writer.cjs +62 -0
  22. package/dist/api-client/file-writer.cjs.map +1 -0
  23. package/dist/api-client/file-writer.d.cts +66 -0
  24. package/dist/api-client/file-writer.d.ts +56 -42
  25. package/dist/api-client/file-writer.js +57 -61
  26. package/dist/api-client/file-writer.js.map +1 -1
  27. package/dist/api-client/index.cjs +2 -0
  28. package/dist/api-client/index.d.ts +2 -2
  29. package/dist/api-client/index.js +4 -21
  30. package/dist/api-client/validators.cjs +229 -0
  31. package/dist/api-client/validators.cjs.map +1 -0
  32. package/dist/api-client/validators.d.cts +26885 -0
  33. package/dist/api-client/validators.d.ts +26732 -3706
  34. package/dist/api-client/validators.js +198 -191
  35. package/dist/api-client/validators.js.map +1 -1
  36. package/dist/api-client/with-retry.cjs +89 -0
  37. package/dist/api-client/with-retry.cjs.map +1 -0
  38. package/dist/api-client/with-retry.d.cts +10 -0
  39. package/dist/api-client/with-retry.d.ts +9 -13
  40. package/dist/api-client/with-retry.js +81 -102
  41. package/dist/api-client/with-retry.js.map +1 -1
  42. package/dist/auth/api.cjs +29 -0
  43. package/dist/auth/api.cjs.map +1 -0
  44. package/dist/auth/api.js +26 -25
  45. package/dist/auth/api.js.map +1 -1
  46. package/dist/auth/error.cjs +13 -0
  47. package/dist/auth/error.cjs.map +1 -0
  48. package/dist/auth/error.js +11 -11
  49. package/dist/auth/error.js.map +1 -1
  50. package/dist/auth/file.cjs +64 -0
  51. package/dist/auth/file.cjs.map +1 -0
  52. package/dist/auth/file.d.cts +26 -0
  53. package/dist/auth/file.d.ts +19 -15
  54. package/dist/auth/file.js +49 -64
  55. package/dist/auth/file.js.map +1 -1
  56. package/dist/auth/index.cjs +11 -0
  57. package/dist/auth/index.d.cts +5 -0
  58. package/dist/auth/index.d.ts +5 -6
  59. package/dist/auth/index.js +6 -27
  60. package/dist/auth/linked-project.cjs +38 -0
  61. package/dist/auth/linked-project.cjs.map +1 -0
  62. package/dist/auth/linked-project.js +30 -64
  63. package/dist/auth/linked-project.js.map +1 -1
  64. package/dist/auth/oauth.cjs +205 -0
  65. package/dist/auth/oauth.cjs.map +1 -0
  66. package/dist/auth/oauth.d.cts +135 -0
  67. package/dist/auth/oauth.d.ts +113 -109
  68. package/dist/auth/oauth.js +185 -252
  69. package/dist/auth/oauth.js.map +1 -1
  70. package/dist/auth/poll-for-token.cjs +82 -0
  71. package/dist/auth/poll-for-token.cjs.map +1 -0
  72. package/dist/auth/poll-for-token.d.cts +28 -0
  73. package/dist/auth/poll-for-token.d.ts +23 -15
  74. package/dist/auth/poll-for-token.js +79 -64
  75. package/dist/auth/poll-for-token.js.map +1 -1
  76. package/dist/auth/project.cjs +178 -0
  77. package/dist/auth/project.cjs.map +1 -0
  78. package/dist/auth/project.d.cts +40 -0
  79. package/dist/auth/project.d.ts +19 -19
  80. package/dist/auth/project.js +169 -72
  81. package/dist/auth/project.js.map +1 -1
  82. package/dist/auth/zod.cjs +22 -0
  83. package/dist/auth/zod.cjs.map +1 -0
  84. package/dist/auth/zod.js +18 -17
  85. package/dist/auth/zod.js.map +1 -1
  86. package/dist/command.cjs +328 -0
  87. package/dist/command.cjs.map +1 -0
  88. package/dist/command.d.cts +289 -0
  89. package/dist/command.d.ts +265 -171
  90. package/dist/command.js +323 -226
  91. package/dist/command.js.map +1 -1
  92. package/dist/constants.d.cts +5 -0
  93. package/dist/constants.d.ts +5 -1
  94. package/dist/filesystem.cjs +499 -0
  95. package/dist/filesystem.cjs.map +1 -0
  96. package/dist/filesystem.d.cts +258 -0
  97. package/dist/filesystem.d.ts +258 -0
  98. package/dist/filesystem.js +497 -0
  99. package/dist/filesystem.js.map +1 -0
  100. package/dist/index.cjs +15 -0
  101. package/dist/index.d.cts +8 -0
  102. package/dist/index.d.ts +8 -6
  103. package/dist/index.js +8 -17
  104. package/dist/network-policy.d.cts +156 -0
  105. package/dist/network-policy.d.ts +88 -28
  106. package/dist/sandbox.cjs +816 -0
  107. package/dist/sandbox.cjs.map +1 -0
  108. package/dist/sandbox.d.cts +2847 -0
  109. package/dist/sandbox.d.ts +2834 -628
  110. package/dist/sandbox.js +808 -557
  111. package/dist/sandbox.js.map +1 -1
  112. package/dist/session.cjs +527 -0
  113. package/dist/session.cjs.map +1 -0
  114. package/dist/session.d.cts +410 -0
  115. package/dist/session.d.ts +403 -368
  116. package/dist/session.js +524 -489
  117. package/dist/session.js.map +1 -1
  118. package/dist/snapshot.cjs +204 -0
  119. package/dist/snapshot.cjs.map +1 -0
  120. package/dist/snapshot.d.cts +161 -0
  121. package/dist/snapshot.d.ts +152 -92
  122. package/dist/snapshot.js +201 -114
  123. package/dist/snapshot.js.map +1 -1
  124. package/dist/utils/array.cjs +17 -0
  125. package/dist/utils/array.cjs.map +1 -0
  126. package/dist/utils/array.js +12 -15
  127. package/dist/utils/array.js.map +1 -1
  128. package/dist/utils/consume-readable.cjs +18 -0
  129. package/dist/utils/consume-readable.cjs.map +1 -0
  130. package/dist/utils/consume-readable.js +13 -12
  131. package/dist/utils/consume-readable.js.map +1 -1
  132. package/dist/utils/decode-base64-url.cjs +15 -0
  133. package/dist/utils/decode-base64-url.cjs.map +1 -0
  134. package/dist/utils/decode-base64-url.js +10 -9
  135. package/dist/utils/decode-base64-url.js.map +1 -1
  136. package/dist/utils/dev-credentials.cjs +142 -0
  137. package/dist/utils/dev-credentials.cjs.map +1 -0
  138. package/dist/utils/dev-credentials.js +126 -184
  139. package/dist/utils/dev-credentials.js.map +1 -1
  140. package/dist/utils/get-credentials.cjs +123 -0
  141. package/dist/utils/get-credentials.cjs.map +1 -0
  142. package/dist/utils/get-credentials.d.cts +21 -0
  143. package/dist/utils/get-credentials.d.ts +19 -61
  144. package/dist/utils/get-credentials.js +106 -140
  145. package/dist/utils/get-credentials.js.map +1 -1
  146. package/dist/utils/log.cjs +25 -0
  147. package/dist/utils/log.cjs.map +1 -0
  148. package/dist/utils/log.js +15 -17
  149. package/dist/utils/log.js.map +1 -1
  150. package/dist/utils/network-policy.cjs +49 -0
  151. package/dist/utils/network-policy.cjs.map +1 -0
  152. package/dist/utils/network-policy.js +42 -77
  153. package/dist/utils/network-policy.js.map +1 -1
  154. package/dist/utils/normalizePath.cjs +27 -0
  155. package/dist/utils/normalizePath.cjs.map +1 -0
  156. package/dist/utils/normalizePath.js +21 -28
  157. package/dist/utils/normalizePath.js.map +1 -1
  158. package/dist/utils/paginator.cjs +41 -0
  159. package/dist/utils/paginator.cjs.map +1 -0
  160. package/dist/utils/paginator.d.cts +16 -0
  161. package/dist/utils/paginator.d.ts +16 -0
  162. package/dist/utils/paginator.js +40 -0
  163. package/dist/utils/paginator.js.map +1 -0
  164. package/dist/utils/resolveSignal.cjs +20 -0
  165. package/dist/utils/resolveSignal.cjs.map +1 -0
  166. package/dist/utils/resolveSignal.d.cts +15 -0
  167. package/dist/utils/resolveSignal.d.ts +12 -10
  168. package/dist/utils/resolveSignal.js +14 -17
  169. package/dist/utils/resolveSignal.js.map +1 -1
  170. package/dist/utils/sandbox-snapshot.cjs +14 -0
  171. package/dist/utils/sandbox-snapshot.cjs.map +1 -0
  172. package/dist/utils/sandbox-snapshot.d.cts +10 -0
  173. package/dist/utils/sandbox-snapshot.d.ts +11 -0
  174. package/dist/utils/sandbox-snapshot.js +14 -0
  175. package/dist/utils/sandbox-snapshot.js.map +1 -0
  176. package/dist/utils/types.cjs +13 -0
  177. package/dist/utils/types.cjs.map +1 -0
  178. package/dist/utils/types.d.cts +11 -0
  179. package/dist/utils/types.d.ts +5 -7
  180. package/dist/utils/types.js +8 -8
  181. package/dist/utils/types.js.map +1 -1
  182. package/dist/version.cjs +7 -0
  183. package/dist/version.cjs.map +1 -0
  184. package/dist/version.js +5 -5
  185. package/dist/version.js.map +1 -1
  186. package/package.json +23 -3
  187. package/dist/api-client/index.js.map +0 -1
  188. package/dist/auth/api.d.ts +0 -6
  189. package/dist/auth/error.d.ts +0 -11
  190. package/dist/auth/index.js.map +0 -1
  191. package/dist/auth/linked-project.d.ts +0 -10
  192. package/dist/auth/zod.d.ts +0 -5
  193. package/dist/constants.js +0 -3
  194. package/dist/constants.js.map +0 -1
  195. package/dist/index.js.map +0 -1
  196. package/dist/network-policy.js +0 -3
  197. package/dist/network-policy.js.map +0 -1
  198. package/dist/utils/array.d.ts +0 -9
  199. package/dist/utils/consume-readable.d.ts +0 -5
  200. package/dist/utils/convert-sandbox.d.ts +0 -6
  201. package/dist/utils/convert-sandbox.js +0 -14
  202. package/dist/utils/convert-sandbox.js.map +0 -1
  203. package/dist/utils/decode-base64-url.d.ts +0 -7
  204. package/dist/utils/dev-credentials.d.ts +0 -37
  205. package/dist/utils/log.d.ts +0 -2
  206. package/dist/utils/network-policy.d.ts +0 -7
  207. package/dist/utils/normalizePath.d.ts +0 -17
  208. package/dist/version.d.ts +0 -1
package/dist/sandbox.js CHANGED
@@ -1,564 +1,815 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Sandbox = void 0;
4
- const api_client_1 = require("./api-client");
5
- const api_error_1 = require("./api-client/api-error");
6
- const get_credentials_1 = require("./utils/get-credentials");
7
- const types_1 = require("./utils/types");
8
- const session_1 = require("./session");
9
- const network_policy_1 = require("./utils/network-policy");
10
- const promises_1 = require("node:timers/promises");
1
+ import { APIError } from "./api-client/api-error.js";
2
+ import { fromAPINetworkPolicy } from "./utils/network-policy.js";
3
+ import { getPrivateParams } from "./utils/types.js";
4
+ import { APIClient } from "./api-client/api-client.js";
5
+ import "./api-client/index.js";
6
+ import { getCredentials } from "./utils/get-credentials.js";
7
+ import { attachPaginator } from "./utils/paginator.js";
8
+ import { Session } from "./session.js";
9
+ import { FileSystem } from "./filesystem.js";
10
+ import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from "@workflow/serde";
11
+ import { setTimeout } from "node:timers/promises";
12
+
13
+ //#region src/sandbox.ts
11
14
  function isSandboxStoppedError(err) {
12
- return err instanceof api_error_1.APIError && err.response.status === 410;
15
+ return err instanceof APIError && err.response.status === 410;
16
+ }
17
+ function isNotFoundError(err) {
18
+ return err instanceof APIError && err.response.status === 404;
19
+ }
20
+ function isSnapshotNotFoundError(err) {
21
+ return err instanceof APIError && err.response.status === 410 && err.json?.error?.code === "snapshot_not_found";
13
22
  }
14
23
  function isSandboxStoppingError(err) {
15
- return (err instanceof api_error_1.APIError &&
16
- err.response.status === 422 &&
17
- err.json?.error?.code === "sandbox_stopping");
24
+ return err instanceof APIError && err.response.status === 422 && err.json?.error?.code === "sandbox_stopping";
18
25
  }
19
- /**
20
- * A Sandbox is a persistent, isolated Linux MicroVMs to run commands in.
21
- * Use {@link Sandbox.create} or {@link Sandbox.get} to construct.
22
- * @hideconstructor
23
- */
24
- class Sandbox {
25
- /**
26
- * The name of this sandbox.
27
- */
28
- get name() {
29
- return this.namedSandbox.name;
30
- }
31
- /**
32
- * Routes from ports to subdomains.
33
- * @hidden
34
- */
35
- get routes() {
36
- return this.session.routes;
37
- }
38
- /**
39
- * Whether the sandbox persists the state.
40
- */
41
- get persistent() {
42
- return this.namedSandbox.persistent;
43
- }
44
- /**
45
- * The region this sandbox runs in.
46
- */
47
- get region() {
48
- return this.namedSandbox.region;
49
- }
50
- /**
51
- * Number of virtual CPUs allocated.
52
- */
53
- get vcpus() {
54
- return this.namedSandbox.vcpus;
55
- }
56
- /**
57
- * Memory allocated in MB.
58
- */
59
- get memory() {
60
- return this.namedSandbox.memory;
61
- }
62
- /** Runtime identifier (e.g. "node24", "python3.13"). */
63
- get runtime() {
64
- return this.namedSandbox.runtime;
65
- }
66
- /**
67
- * Cumulative egress bytes across all sessions.
68
- */
69
- get totalEgressBytes() {
70
- return this.namedSandbox.totalEgressBytes;
71
- }
72
- /**
73
- * Cumulative ingress bytes across all sessions.
74
- */
75
- get totalIngressBytes() {
76
- return this.namedSandbox.totalIngressBytes;
77
- }
78
- /**
79
- * Cumulative active CPU duration in milliseconds across all sessions.
80
- */
81
- get totalActiveCpuDurationMs() {
82
- return this.namedSandbox.totalActiveCpuDurationMs;
83
- }
84
- /**
85
- * Cumulative wall-clock duration in milliseconds across all sessions.
86
- */
87
- get totalDurationMs() {
88
- return this.namedSandbox.totalDurationMs;
89
- }
90
- /**
91
- * When this sandbox was last updated.
92
- */
93
- get updatedAt() {
94
- return new Date(this.namedSandbox.updatedAt);
95
- }
96
- /**
97
- * When this sandbox was created.
98
- */
99
- get createdAt() {
100
- return new Date(this.namedSandbox.createdAt);
101
- }
102
- /**
103
- * Interactive port.
104
- */
105
- get interactivePort() {
106
- return this.session.interactivePort;
107
- }
108
- /**
109
- * The status of the current session.
110
- */
111
- get status() {
112
- return this.session.status;
113
- }
114
- /**
115
- * The default timeout of this sandbox in milliseconds.
116
- */
117
- get timeout() {
118
- return this.namedSandbox.timeout;
119
- }
120
- /**
121
- * The default network policy of this sandbox.
122
- */
123
- get networkPolicy() {
124
- return this.namedSandbox.networkPolicy
125
- ? (0, network_policy_1.fromAPINetworkPolicy)(this.namedSandbox.networkPolicy)
126
- : undefined;
127
- }
128
- /**
129
- * If the session was created from a snapshot, the ID of that snapshot.
130
- */
131
- get sourceSnapshotId() {
132
- return this.session.sourceSnapshotId;
133
- }
134
- /**
135
- * The current snapshot ID of this sandbox, if any.
136
- */
137
- get currentSnapshotId() {
138
- return this.namedSandbox.currentSnapshotId;
139
- }
140
- /**
141
- * The amount of CPU used by the session. Only reported once the VM is stopped.
142
- */
143
- get activeCpuUsageMs() {
144
- return this.session.activeCpuUsageMs;
145
- }
146
- /**
147
- * The amount of network data used by the session. Only reported once the VM is stopped.
148
- */
149
- get networkTransfer() {
150
- return this.session.networkTransfer;
151
- }
152
- /**
153
- * Allow to get a list of sandboxes for a team narrowed to the given params.
154
- * It returns both the sandboxes and the pagination metadata to allow getting
155
- * the next page of results.
156
- */
157
- static async list(params) {
158
- const credentials = await (0, get_credentials_1.getCredentials)(params);
159
- const client = new api_client_1.APIClient({
160
- teamId: credentials.teamId,
161
- token: credentials.token,
162
- fetch: params?.fetch,
163
- });
164
- return client.listNamedSandboxes({
165
- ...credentials,
166
- ...params,
167
- });
168
- }
169
- /**
170
- * Create a new sandbox.
171
- *
172
- * @param params - Creation parameters and optional credentials.
173
- * @returns A promise resolving to the created {@link Sandbox}.
174
- * @example
175
- * <caption>Create a sandbox and drop it in the end of the block</caption>
176
- * async function fn() {
177
- * await using const sandbox = await Sandbox.create();
178
- * // Sandbox automatically stopped at the end of the lexical scope
179
- * }
180
- */
181
- static async create(params) {
182
- const credentials = await (0, get_credentials_1.getCredentials)(params);
183
- const client = new api_client_1.APIClient({
184
- teamId: credentials.teamId,
185
- token: credentials.token,
186
- fetch: params?.fetch,
187
- });
188
- const privateParams = (0, types_1.getPrivateParams)(params);
189
- const response = await client.createSandbox({
190
- source: params?.source,
191
- projectId: credentials.projectId,
192
- ports: params?.ports ?? [],
193
- timeout: params?.timeout,
194
- resources: params?.resources,
195
- runtime: params && "runtime" in params ? params?.runtime : undefined,
196
- networkPolicy: params?.networkPolicy,
197
- env: params?.env,
198
- signal: params?.signal,
199
- name: params?.name,
200
- persistent: params?.persistent,
201
- ...privateParams,
202
- });
203
- return new DisposableSandbox({
204
- client,
205
- session: response.json.sandbox,
206
- namedSandbox: response.json.namedSandbox,
207
- routes: response.json.routes,
208
- projectId: credentials.projectId,
209
- });
210
- }
211
- /**
212
- * Retrieve an existing sandbox and resume its session.
213
- *
214
- * @param params - Get parameters and optional credentials.
215
- * @returns A promise resolving to the {@link Sandbox}.
216
- */
217
- static async get(params) {
218
- const credentials = await (0, get_credentials_1.getCredentials)(params);
219
- const client = new api_client_1.APIClient({
220
- teamId: credentials.teamId,
221
- token: credentials.token,
222
- fetch: params.fetch,
223
- });
224
- const response = await client.getNamedSandbox({
225
- name: params.name,
226
- projectId: credentials.projectId,
227
- resume: params.resume,
228
- signal: params.signal,
229
- });
230
- return new Sandbox({
231
- client,
232
- session: response.json.sandbox,
233
- namedSandbox: response.json.namedSandbox,
234
- routes: response.json.routes,
235
- projectId: credentials.projectId,
236
- });
237
- }
238
- constructor({ client, routes, session, namedSandbox, projectId, }) {
239
- this.client = client;
240
- this.session = new session_1.Session({ client, routes, session });
241
- this.namedSandbox = namedSandbox;
242
- this.projectId = projectId;
243
- }
244
- /**
245
- * Get the current session (the running VM) for this sandbox.
246
- *
247
- * @returns The {@link Session} instance.
248
- */
249
- currentSession() {
250
- return this.session;
251
- }
252
- /**
253
- * Resume this sandbox by creating a new session via `getNamedSandbox`.
254
- */
255
- async resume(signal) {
256
- const response = await this.client.getNamedSandbox({
257
- name: this.namedSandbox.name,
258
- projectId: this.projectId,
259
- resume: true,
260
- signal,
261
- });
262
- this.session = new session_1.Session({
263
- client: this.client,
264
- routes: response.json.routes,
265
- session: response.json.sandbox,
266
- });
267
- }
268
- /**
269
- * Poll until the current session reaches a terminal state, then resume.
270
- */
271
- async waitForStopAndResume(signal) {
272
- const pollingInterval = 500;
273
- let status = this.session.status;
274
- while (status === "stopping" || status === "snapshotting") {
275
- await (0, promises_1.setTimeout)(pollingInterval, undefined, { signal });
276
- const poll = await this.client.getSandbox({
277
- sandboxId: this.session.sessionId,
278
- signal,
279
- });
280
- this.session = new session_1.Session({
281
- client: this.client,
282
- routes: poll.json.routes,
283
- session: poll.json.sandbox,
284
- });
285
- status = poll.json.sandbox.status;
286
- }
287
- await this.resume(signal);
288
- }
289
- /**
290
- * Execute `fn`, and if the session is stopped/stopping, resume and retry.
291
- */
292
- async withResume(fn, signal) {
293
- try {
294
- return await fn();
295
- }
296
- catch (err) {
297
- if (isSandboxStoppedError(err)) {
298
- await this.resume(signal);
299
- return fn();
300
- }
301
- if (isSandboxStoppingError(err)) {
302
- await this.waitForStopAndResume(signal);
303
- return fn();
304
- }
305
- throw err;
306
- }
307
- }
308
- async runCommand(commandOrParams, args, opts) {
309
- const signal = typeof commandOrParams === "string" ? opts?.signal : commandOrParams.signal;
310
- return this.withResume(() => this.session.runCommand(commandOrParams, args, opts), signal);
311
- }
312
- /**
313
- * Internal helper to start a command in the sandbox.
314
- *
315
- * @param params - Command execution parameters.
316
- * @returns A {@link Command} or {@link CommandFinished}, depending on `detached`.
317
- * @internal
318
- */
319
- async getCommand(cmdId, opts) {
320
- return this.withResume(() => this.session.getCommand(cmdId, opts), opts?.signal);
321
- }
322
- /**
323
- * Create a directory in the filesystem of this sandbox.
324
- *
325
- * @param path - Path of the directory to create
326
- * @param opts - Optional parameters.
327
- * @param opts.signal - An AbortSignal to cancel the operation.
328
- */
329
- async mkDir(path, opts) {
330
- return this.withResume(() => this.session.mkDir(path, opts), opts?.signal);
331
- }
332
- /**
333
- * Read a file from the filesystem of this sandbox as a stream.
334
- *
335
- * @param file - File to read, with path and optional cwd
336
- * @param opts - Optional parameters.
337
- * @param opts.signal - An AbortSignal to cancel the operation.
338
- * @returns A promise that resolves to a ReadableStream containing the file contents, or null if file not found
339
- */
340
- async readFile(file, opts) {
341
- return this.withResume(() => this.session.readFile(file, opts), opts?.signal);
342
- }
343
- /**
344
- * Read a file from the filesystem of this sandbox as a Buffer.
345
- *
346
- * @param file - File to read, with path and optional cwd
347
- * @param opts - Optional parameters.
348
- * @param opts.signal - An AbortSignal to cancel the operation.
349
- * @returns A promise that resolves to the file contents as a Buffer, or null if file not found
350
- */
351
- async readFileToBuffer(file, opts) {
352
- return this.withResume(() => this.session.readFileToBuffer(file, opts), opts?.signal);
353
- }
354
- /**
355
- * Download a file from the sandbox to the local filesystem.
356
- *
357
- * @param src - Source file on the sandbox, with path and optional cwd
358
- * @param dst - Destination file on the local machine, with path and optional cwd
359
- * @param opts - Optional parameters.
360
- * @param opts.mkdirRecursive - If true, create parent directories for the destination if they don't exist.
361
- * @param opts.signal - An AbortSignal to cancel the operation.
362
- * @returns The absolute path to the written file, or null if the source file was not found
363
- */
364
- async downloadFile(src, dst, opts) {
365
- return this.withResume(() => this.session.downloadFile(src, dst, opts), opts?.signal);
366
- }
367
- /**
368
- * Write files to the filesystem of this sandbox.
369
- * Defaults to writing to /vercel/sandbox unless an absolute path is specified.
370
- * Writes files using the `vercel-sandbox` user.
371
- *
372
- * @param files - Array of files with path and stream/buffer contents
373
- * @param opts - Optional parameters.
374
- * @param opts.signal - An AbortSignal to cancel the operation.
375
- * @returns A promise that resolves when the files are written
376
- */
377
- async writeFiles(files, opts) {
378
- return this.withResume(() => this.session.writeFiles(files, opts), opts?.signal);
379
- }
380
- /**
381
- * Get the public domain of a port of this sandbox.
382
- *
383
- * @param p - Port number to resolve
384
- * @returns A full domain (e.g. `https://subdomain.vercel.run`)
385
- * @throws If the port has no associated route
386
- */
387
- domain(p) {
388
- return this.session.domain(p);
389
- }
390
- /**
391
- * Stop the sandbox.
392
- *
393
- * @param opts - Optional parameters.
394
- * @param opts.signal - An AbortSignal to cancel the operation.
395
- * @param opts.blocking - If true, poll until the sandbox has fully stopped and return the final state.
396
- * @returns The sandbox metadata at the time the stop was acknowledged, or after fully stopped if `blocking` is true.
397
- */
398
- async stop(opts) {
399
- return this.session.stop(opts);
400
- }
401
- /**
402
- * Update the network policy for this sandbox.
403
- *
404
- * @deprecated Use {@link Sandbox.update} instead.
405
- *
406
- * @param networkPolicy - The new network policy to apply.
407
- * @param opts - Optional parameters.
408
- * @param opts.signal - An AbortSignal to cancel the operation.
409
- * @returns A promise that resolves when the network policy is updated.
410
- *
411
- * @example
412
- * // Restrict to specific domains
413
- * await sandbox.updateNetworkPolicy({
414
- * allow: ["*.npmjs.org", "github.com"],
415
- * });
416
- *
417
- * @example
418
- * // Inject credentials with per-domain transformers
419
- * await sandbox.updateNetworkPolicy({
420
- * allow: {
421
- * "ai-gateway.vercel.sh": [{
422
- * transform: [{
423
- * headers: { authorization: "Bearer ..." }
424
- * }]
425
- * }],
426
- * "*": []
427
- * }
428
- * });
429
- *
430
- * @example
431
- * // Deny all network access
432
- * await sandbox.updateNetworkPolicy("deny-all");
433
- */
434
- async updateNetworkPolicy(networkPolicy, opts) {
435
- await this.withResume(() => this.session.update({ networkPolicy: networkPolicy }, opts), opts?.signal);
436
- return this.session.networkPolicy;
437
- }
438
- /**
439
- * Extend the timeout of the sandbox by the specified duration.
440
- *
441
- * This allows you to extend the lifetime of a sandbox up until the maximum
442
- * execution timeout for your plan.
443
- *
444
- * @param duration - The duration in milliseconds to extend the timeout by
445
- * @param opts - Optional parameters.
446
- * @param opts.signal - An AbortSignal to cancel the operation.
447
- * @returns A promise that resolves when the timeout is extended
448
- *
449
- * @example
450
- * const sandbox = await Sandbox.create({ timeout: ms('10m') });
451
- * // Extends timeout by 5 minutes, to a total of 15 minutes.
452
- * await sandbox.extendTimeout(ms('5m'));
453
- */
454
- async extendTimeout(duration, opts) {
455
- return this.withResume(() => this.session.extendTimeout(duration, opts), opts?.signal);
456
- }
457
- /**
458
- * Create a snapshot from this currently running sandbox. New sandboxes can
459
- * then be created from this snapshot using {@link Sandbox.createFromSnapshot}.
460
- *
461
- * Note: this sandbox will be stopped as part of the snapshot creation process.
462
- *
463
- * @param opts - Optional parameters.
464
- * @param opts.expiration - Optional expiration time in milliseconds. Use 0 for no expiration at all.
465
- * @param opts.signal - An AbortSignal to cancel the operation.
466
- * @returns A promise that resolves to the Snapshot instance
467
- */
468
- async snapshot(opts) {
469
- return this.withResume(() => this.session.snapshot(opts), opts?.signal);
470
- }
471
- /**
472
- * Update the named sandbox configuration.
473
- *
474
- * @param params - Fields to update.
475
- * @param opts - Optional abort signal.
476
- */
477
- async update(params, opts) {
478
- // Update the sandbox config. This config will be used on the next session.
479
- const response = await this.client.updateNamedSandbox({
480
- name: this.namedSandbox.name,
481
- projectId: this.projectId,
482
- persistent: params.persistent,
483
- resources: params.resources,
484
- timeout: params.timeout,
485
- networkPolicy: params.networkPolicy,
486
- signal: opts?.signal,
487
- });
488
- this.namedSandbox = response.json.namedSandbox;
489
- // Update the current session config. This only applies to network policy.
490
- if (params.networkPolicy) {
491
- try {
492
- return await this.session.update({ networkPolicy: params.networkPolicy }, opts);
493
- }
494
- catch (err) {
495
- if (isSandboxStoppedError(err) || isSandboxStoppingError(err)) {
496
- return;
497
- }
498
- throw err;
499
- }
500
- }
501
- }
502
- /**
503
- * Delete this named sandbox.
504
- *
505
- * After deletion the instance becomes inert — all further API calls will
506
- * throw immediately.
507
- */
508
- async delete(opts) {
509
- await this.client.deleteNamedSandbox({
510
- name: this.namedSandbox.name,
511
- projectId: this.projectId,
512
- preserveSnapshots: opts?.preserveSnapshots,
513
- signal: opts?.signal,
514
- });
515
- }
516
- /**
517
- * List sessions (VMs) that have been created for this named sandbox.
518
- *
519
- * @param params - Optional pagination parameters.
520
- * @returns The list of sessions and pagination metadata.
521
- */
522
- async listSessions(params) {
523
- return this.client.listSandboxes({
524
- projectId: this.projectId,
525
- name: this.namedSandbox.name,
526
- limit: params?.limit,
527
- since: params?.since,
528
- until: params?.until,
529
- signal: params?.signal,
530
- });
531
- }
532
- /**
533
- * List snapshots that belong to this named sandbox.
534
- *
535
- * @param params - Optional pagination parameters.
536
- * @returns The list of snapshots and pagination metadata.
537
- */
538
- async listSnapshots(params) {
539
- return this.client.listSnapshots({
540
- projectId: this.projectId,
541
- name: this.namedSandbox.name,
542
- limit: params?.limit,
543
- since: params?.since,
544
- until: params?.until,
545
- signal: params?.signal,
546
- });
547
- }
26
+ function isSandboxSnapshottingError(err) {
27
+ return err instanceof APIError && err.response.status === 422 && err.json?.error?.code === "sandbox_snapshotting";
548
28
  }
549
- exports.Sandbox = Sandbox;
550
29
  /**
551
- * A {@link Sandbox} that can automatically be disposed using a `await using` statement.
552
- *
553
- * @example
554
- * {
555
- * await using const sandbox = await Sandbox.create();
556
- * }
557
- * // Sandbox is automatically stopped here
558
- */
559
- class DisposableSandbox extends Sandbox {
560
- async [Symbol.asyncDispose]() {
561
- await this.stop();
562
- }
563
- }
30
+ * A Sandbox is a persistent, isolated Linux MicroVMs to run commands in.
31
+ * Use {@link Sandbox.create} or {@link Sandbox.get} to construct.
32
+ * @hideconstructor
33
+ */
34
+ var Sandbox = class Sandbox {
35
+ /**
36
+ * Lazily resolve credentials and construct an API client.
37
+ * @internal
38
+ */
39
+ async ensureClient() {
40
+ "use step";
41
+ if (this._client) return this._client;
42
+ const credentials = await getCredentials();
43
+ this._client = new APIClient({
44
+ teamId: credentials.teamId,
45
+ token: credentials.token
46
+ });
47
+ return this._client;
48
+ }
49
+ /**
50
+ * The name of this sandbox.
51
+ */
52
+ get name() {
53
+ return this.sandbox.name;
54
+ }
55
+ /**
56
+ * Routes from ports to subdomains.
57
+ * @hidden
58
+ */
59
+ get routes() {
60
+ return this.currentSession().routes;
61
+ }
62
+ /**
63
+ * Whether the sandbox persists the state.
64
+ */
65
+ get persistent() {
66
+ return this.sandbox.persistent;
67
+ }
68
+ /**
69
+ * The region this sandbox runs in.
70
+ */
71
+ get region() {
72
+ return this.sandbox.region;
73
+ }
74
+ /**
75
+ * Number of virtual CPUs allocated.
76
+ */
77
+ get vcpus() {
78
+ return this.sandbox.vcpus;
79
+ }
80
+ /**
81
+ * Memory allocated in MB.
82
+ */
83
+ get memory() {
84
+ return this.sandbox.memory;
85
+ }
86
+ /** Runtime identifier (e.g. "node24", "python3.13"). */
87
+ get runtime() {
88
+ return this.sandbox.runtime;
89
+ }
90
+ /**
91
+ * Cumulative egress bytes across all sessions.
92
+ */
93
+ get totalEgressBytes() {
94
+ return this.sandbox.totalEgressBytes;
95
+ }
96
+ /**
97
+ * Cumulative ingress bytes across all sessions.
98
+ */
99
+ get totalIngressBytes() {
100
+ return this.sandbox.totalIngressBytes;
101
+ }
102
+ /**
103
+ * Cumulative active CPU duration in milliseconds across all sessions.
104
+ */
105
+ get totalActiveCpuDurationMs() {
106
+ return this.sandbox.totalActiveCpuDurationMs;
107
+ }
108
+ /**
109
+ * Cumulative wall-clock duration in milliseconds across all sessions.
110
+ */
111
+ get totalDurationMs() {
112
+ return this.sandbox.totalDurationMs;
113
+ }
114
+ /**
115
+ * When this sandbox was last updated.
116
+ */
117
+ get updatedAt() {
118
+ return new Date(this.sandbox.updatedAt);
119
+ }
120
+ /**
121
+ * When the sandbox status was last updated.
122
+ */
123
+ get statusUpdatedAt() {
124
+ return this.sandbox.statusUpdatedAt ? new Date(this.sandbox.statusUpdatedAt) : void 0;
125
+ }
126
+ /**
127
+ * When this sandbox was created.
128
+ */
129
+ get createdAt() {
130
+ return new Date(this.sandbox.createdAt);
131
+ }
132
+ /**
133
+ * Interactive port.
134
+ */
135
+ get interactivePort() {
136
+ return this.currentSession().interactivePort;
137
+ }
138
+ /**
139
+ * The status of the current session.
140
+ */
141
+ get status() {
142
+ return this.currentSession().status;
143
+ }
144
+ /**
145
+ * The default timeout of this sandbox in milliseconds.
146
+ */
147
+ get timeout() {
148
+ return this.sandbox.timeout;
149
+ }
150
+ /**
151
+ * Key-value tags attached to the sandbox.
152
+ */
153
+ get tags() {
154
+ return this.sandbox.tags;
155
+ }
156
+ /**
157
+ * The default network policy of this sandbox.
158
+ */
159
+ get networkPolicy() {
160
+ return this.sandbox.networkPolicy ? fromAPINetworkPolicy(this.sandbox.networkPolicy) : void 0;
161
+ }
162
+ /**
163
+ * If the session was created from a snapshot, the ID of that snapshot.
164
+ */
165
+ get sourceSnapshotId() {
166
+ return this.currentSession().sourceSnapshotId;
167
+ }
168
+ /**
169
+ * The current snapshot ID of this sandbox, if any.
170
+ */
171
+ get currentSnapshotId() {
172
+ return this.sandbox.currentSnapshotId;
173
+ }
174
+ /**
175
+ * The default snapshot expiration in milliseconds, if set.
176
+ */
177
+ get snapshotExpiration() {
178
+ return this.sandbox.snapshotExpiration;
179
+ }
180
+ /**
181
+ * The amount of CPU used by the session. Only reported once the VM is stopped.
182
+ */
183
+ get activeCpuUsageMs() {
184
+ return this.currentSession().activeCpuUsageMs;
185
+ }
186
+ /**
187
+ * The amount of network data used by the session. Only reported once the VM is stopped.
188
+ */
189
+ get networkTransfer() {
190
+ return this.currentSession().networkTransfer;
191
+ }
192
+ /**
193
+ * Allow to get a list of sandboxes for a team narrowed to the given params.
194
+ * It returns both the sandboxes and the pagination metadata to allow getting
195
+ * the next page of results.
196
+ *
197
+ * The returned object is async-iterable to auto-paginate through all pages:
198
+ *
199
+ * ```ts
200
+ * const result = await Sandbox.list({ namePrefix: "ci-" });
201
+ * for await (const sandbox of result) { ... }
202
+ * // or: await result.toArray();
203
+ * // or: for await (const page of result.pages()) { ... }
204
+ * ```
205
+ */
206
+ static async list(params) {
207
+ "use step";
208
+ const credentials = await getCredentials(params);
209
+ const client = new APIClient({
210
+ teamId: credentials.teamId,
211
+ token: credentials.token,
212
+ fetch: params?.fetch
213
+ });
214
+ const fetchPage = async (cursor) => {
215
+ return (await client.listSandboxes({
216
+ ...credentials,
217
+ ...params,
218
+ ...cursor !== void 0 && { cursor }
219
+ })).json;
220
+ };
221
+ return attachPaginator(await fetchPage(params?.cursor), {
222
+ itemsKey: "sandboxes",
223
+ fetchNext: fetchPage,
224
+ signal: params?.signal
225
+ });
226
+ }
227
+ /**
228
+ * Serialize a Sandbox instance to plain data for @workflow/serde.
229
+ *
230
+ * @param instance - The Sandbox instance to serialize
231
+ * @returns A plain object containing sandbox metadata and routes
232
+ */
233
+ static [WORKFLOW_SERIALIZE](instance) {
234
+ return {
235
+ metadata: instance.session?._sessionSnapshot,
236
+ routes: instance.session?.routes ?? [],
237
+ sandboxMetadata: instance.sandbox,
238
+ projectId: instance.projectId
239
+ };
240
+ }
241
+ /**
242
+ * Deserialize a Sandbox from serialized snapshot data.
243
+ *
244
+ * The deserialized instance uses the serialized metadata synchronously and
245
+ * lazily creates an API client only when methods perform API requests.
246
+ *
247
+ * @param data - The serialized sandbox data
248
+ * @returns The reconstructed Sandbox instance
249
+ */
250
+ static [WORKFLOW_DESERIALIZE](data) {
251
+ const sandbox = new Sandbox({
252
+ sandbox: data.sandboxMetadata,
253
+ routes: data.routes,
254
+ projectId: data.projectId
255
+ });
256
+ if (data.metadata) sandbox.session = new Session({
257
+ routes: data.routes,
258
+ snapshot: data.metadata
259
+ });
260
+ return sandbox;
261
+ }
262
+ /**
263
+ * Create a new sandbox.
264
+ *
265
+ * @param params - Creation parameters and optional credentials.
266
+ * @returns A promise resolving to the created {@link Sandbox}.
267
+ * @example
268
+ * <caption>Create a sandbox with default options</caption>
269
+ * const sandbox = await Sandbox.create();
270
+ *
271
+ * @example
272
+ * <caption>Create a sandbox and drop it in the end of the block</caption>
273
+ * async function fn() {
274
+ * await using const sandbox = await Sandbox.create();
275
+ * // Sandbox automatically stopped at the end of the lexical scope
276
+ * }
277
+ */
278
+ static async create(params) {
279
+ "use step";
280
+ const credentials = await getCredentials(params);
281
+ const client = new APIClient({
282
+ teamId: credentials.teamId,
283
+ token: credentials.token,
284
+ fetch: params?.fetch
285
+ });
286
+ const privateParams = getPrivateParams(params);
287
+ const response = await client.createSandbox({
288
+ source: params?.source,
289
+ projectId: credentials.projectId,
290
+ ports: params?.ports ?? [],
291
+ timeout: params?.timeout,
292
+ resources: params?.resources,
293
+ runtime: params && "runtime" in params ? params?.runtime : void 0,
294
+ networkPolicy: params?.networkPolicy,
295
+ env: params?.env,
296
+ tags: params?.tags,
297
+ snapshotExpiration: params?.snapshotExpiration,
298
+ signal: params?.signal,
299
+ name: params?.name,
300
+ persistent: params?.persistent,
301
+ ...privateParams
302
+ });
303
+ return new DisposableSandbox({
304
+ client,
305
+ session: response.json.session,
306
+ sandbox: response.json.sandbox,
307
+ routes: response.json.routes,
308
+ projectId: credentials.projectId,
309
+ onResume: params?.onResume
310
+ });
311
+ }
312
+ /**
313
+ * Retrieve an existing sandbox and resume its session.
314
+ *
315
+ * @param params - Get parameters and optional credentials.
316
+ * @returns A promise resolving to the {@link Sandbox}.
317
+ */
318
+ static async get(params) {
319
+ "use step";
320
+ const credentials = await getCredentials(params);
321
+ const client = new APIClient({
322
+ teamId: credentials.teamId,
323
+ token: credentials.token,
324
+ fetch: params.fetch
325
+ });
326
+ const privateParams = getPrivateParams(params);
327
+ const response = await client.getSandbox({
328
+ name: params.name,
329
+ projectId: credentials.projectId,
330
+ resume: params.resume,
331
+ signal: params.signal,
332
+ ...privateParams
333
+ });
334
+ const sandbox = new Sandbox({
335
+ client,
336
+ session: response.json.session,
337
+ sandbox: response.json.sandbox,
338
+ routes: response.json.routes,
339
+ projectId: credentials.projectId,
340
+ onResume: params.onResume
341
+ });
342
+ if (response.json.resumed && params.onResume) await params.onResume(sandbox);
343
+ return sandbox;
344
+ }
345
+ /**
346
+ * Retrieve an existing named sandbox, or create a new one if none exists.
347
+ *
348
+ * If `name` is omitted, this always creates a new sandbox and fires
349
+ * `onCreate`. If `name` is provided, it first tries {@link Sandbox.get};
350
+ * on `not_found` it creates a new sandbox with that name; on
351
+ * `snapshot_not_found` it deletes the stale named sandbox and creates
352
+ * a fresh one with the same name.
353
+ *
354
+ * @param params - Get/create parameters plus an optional `onCreate` hook.
355
+ * @returns A promise resolving to the {@link Sandbox}.
356
+ *
357
+ * @example
358
+ * <caption>Idempotent named sandbox with one-time setup</caption>
359
+ * const sandbox = await Sandbox.getOrCreate({
360
+ * name: "my-workspace",
361
+ * onCreate: async (sbx) => {
362
+ * await sbx.writeFiles([
363
+ * { path: "README.md", content: Buffer.from("# Hello") },
364
+ * ]);
365
+ * },
366
+ * });
367
+ *
368
+ * @example
369
+ * <caption>Unnamed — always creates</caption>
370
+ * const sandbox = await Sandbox.getOrCreate({
371
+ * onCreate: async (sbx) => {
372
+ * await sbx.runCommand("npm", ["install"]);
373
+ * },
374
+ * });
375
+ */
376
+ static async getOrCreate(params) {
377
+ "use step";
378
+ if (!params?.name) {
379
+ const sandbox = await Sandbox.create(params);
380
+ if (params?.onCreate) await params.onCreate(sandbox);
381
+ return sandbox;
382
+ }
383
+ try {
384
+ return await Sandbox.get(params);
385
+ } catch (err) {
386
+ if (isNotFoundError(err)) {
387
+ const sandbox = await Sandbox.create(params);
388
+ if (params.onCreate) await params.onCreate(sandbox);
389
+ return sandbox;
390
+ }
391
+ if (isSnapshotNotFoundError(err)) {
392
+ const credentials = await getCredentials(params);
393
+ const client = new APIClient({
394
+ teamId: credentials.teamId,
395
+ token: credentials.token,
396
+ fetch: params.fetch
397
+ });
398
+ const privateParams = getPrivateParams(params);
399
+ try {
400
+ await client.deleteSandbox({
401
+ name: params.name,
402
+ projectId: credentials.projectId,
403
+ signal: params.signal,
404
+ ...privateParams
405
+ });
406
+ } catch (deleteErr) {
407
+ if (!isNotFoundError(deleteErr)) throw deleteErr;
408
+ }
409
+ const sandbox = await Sandbox.create(params);
410
+ if (params.onCreate) await params.onCreate(sandbox);
411
+ return sandbox;
412
+ }
413
+ throw err;
414
+ }
415
+ }
416
+ /**
417
+ * Create a new Sandbox instance.
418
+ *
419
+ * @param params.client - Optional API client. If not provided, will be lazily created using global credentials.
420
+ * @param params.routes - Port-to-subdomain mappings for exposed ports
421
+ * @param params.sandbox - Sandbox snapshot metadata
422
+ */
423
+ constructor({ client, routes, session, sandbox, projectId, onResume }) {
424
+ this._client = null;
425
+ this.resumePromise = null;
426
+ this._client = client ?? null;
427
+ if (session) this.session = new Session({
428
+ client,
429
+ routes,
430
+ session
431
+ });
432
+ this.sandbox = sandbox;
433
+ this.projectId = projectId ?? "";
434
+ this.onResume = onResume;
435
+ this.fs = new FileSystem(this);
436
+ }
437
+ /**
438
+ * Get the current session (the running VM) for this sandbox.
439
+ *
440
+ * @returns The {@link Session} instance.
441
+ */
442
+ currentSession() {
443
+ if (!this.session) throw new Error("No active session. Run a command or call resume first.");
444
+ return this.session;
445
+ }
446
+ /**
447
+ * Resume this sandbox by creating a new session via `getSandbox`.
448
+ */
449
+ async resume(signal) {
450
+ if (!this.resumePromise) this.resumePromise = this.doResume(signal).finally(() => {
451
+ this.resumePromise = null;
452
+ });
453
+ return this.resumePromise;
454
+ }
455
+ async doResume(signal) {
456
+ const client = await this.ensureClient();
457
+ const response = await client.getSandbox({
458
+ name: this.sandbox.name,
459
+ projectId: this.projectId,
460
+ resume: true,
461
+ signal
462
+ });
463
+ this.session = new Session({
464
+ client,
465
+ routes: response.json.routes,
466
+ session: response.json.session
467
+ });
468
+ if (this.onResume && response.json.resumed) await this.onResume(this);
469
+ }
470
+ /**
471
+ * Poll until the current session reaches a terminal state, then resume.
472
+ */
473
+ async waitForStopAndResume(signal) {
474
+ "use step";
475
+ const client = await this.ensureClient();
476
+ const pollingInterval = 500;
477
+ let status = this.session.status;
478
+ while (status === "stopping" || status === "snapshotting") {
479
+ await setTimeout(pollingInterval, void 0, { signal });
480
+ const poll = await client.getSession({
481
+ sessionId: this.session.sessionId,
482
+ signal
483
+ });
484
+ this.session = new Session({
485
+ client,
486
+ routes: poll.json.routes,
487
+ session: poll.json.session
488
+ });
489
+ status = poll.json.session.status;
490
+ }
491
+ await this.resume(signal);
492
+ }
493
+ /**
494
+ * Execute `fn`, and if the session is stopped/stopping/snapshotting, resume and retry.
495
+ */
496
+ async withResume(fn, signal) {
497
+ if (!this.session) await this.resume(signal);
498
+ try {
499
+ return await fn();
500
+ } catch (err) {
501
+ if (isSandboxStoppedError(err)) {
502
+ await this.resume(signal);
503
+ return fn();
504
+ }
505
+ if (isSandboxStoppingError(err) || isSandboxSnapshottingError(err)) {
506
+ await this.waitForStopAndResume(signal);
507
+ return fn();
508
+ }
509
+ throw err;
510
+ }
511
+ }
512
+ async runCommand(commandOrParams, args, opts) {
513
+ "use step";
514
+ const signal = typeof commandOrParams === "string" ? opts?.signal : commandOrParams.signal;
515
+ return this.withResume(() => this.session.runCommand(commandOrParams, args, opts), signal);
516
+ }
517
+ /**
518
+ * Internal helper to start a command in the sandbox.
519
+ *
520
+ * @param params - Command execution parameters.
521
+ * @returns A {@link Command} or {@link CommandFinished}, depending on `detached`.
522
+ * @internal
523
+ */
524
+ async getCommand(cmdId, opts) {
525
+ "use step";
526
+ return this.withResume(() => this.session.getCommand(cmdId, opts), opts?.signal);
527
+ }
528
+ /**
529
+ * Create a directory in the filesystem of this sandbox.
530
+ *
531
+ * @param path - Path of the directory to create
532
+ * @param opts - Optional parameters.
533
+ * @param opts.signal - An AbortSignal to cancel the operation.
534
+ */
535
+ async mkDir(path, opts) {
536
+ "use step";
537
+ return this.withResume(() => this.session.mkDir(path, opts), opts?.signal);
538
+ }
539
+ /**
540
+ * Read a file from the filesystem of this sandbox as a stream.
541
+ *
542
+ * @param file - File to read, with path and optional cwd
543
+ * @param opts - Optional parameters.
544
+ * @param opts.signal - An AbortSignal to cancel the operation.
545
+ * @returns A promise that resolves to a ReadableStream containing the file contents, or null if file not found
546
+ */
547
+ async readFile(file, opts) {
548
+ "use step";
549
+ return this.withResume(() => this.session.readFile(file, opts), opts?.signal);
550
+ }
551
+ /**
552
+ * Read a file from the filesystem of this sandbox as a Buffer.
553
+ *
554
+ * @param file - File to read, with path and optional cwd
555
+ * @param opts - Optional parameters.
556
+ * @param opts.signal - An AbortSignal to cancel the operation.
557
+ * @returns A promise that resolves to the file contents as a Buffer, or null if file not found
558
+ */
559
+ async readFileToBuffer(file, opts) {
560
+ "use step";
561
+ return this.withResume(() => this.session.readFileToBuffer(file, opts), opts?.signal);
562
+ }
563
+ /**
564
+ * Download a file from the sandbox to the local filesystem.
565
+ *
566
+ * @param src - Source file on the sandbox, with path and optional cwd
567
+ * @param dst - Destination file on the local machine, with path and optional cwd
568
+ * @param opts - Optional parameters.
569
+ * @param opts.mkdirRecursive - If true, create parent directories for the destination if they don't exist.
570
+ * @param opts.signal - An AbortSignal to cancel the operation.
571
+ * @returns The absolute path to the written file, or null if the source file was not found
572
+ */
573
+ async downloadFile(src, dst, opts) {
574
+ "use step";
575
+ return this.withResume(() => this.session.downloadFile(src, dst, opts), opts?.signal);
576
+ }
577
+ /**
578
+ * Write files to the filesystem of this sandbox.
579
+ * Defaults to writing to /vercel/sandbox unless an absolute path is specified.
580
+ * Writes files using the `vercel-sandbox` user.
581
+ *
582
+ * @param files - Array of files with path, content, and optional mode (permissions)
583
+ * @param opts - Optional parameters.
584
+ * @param opts.signal - An AbortSignal to cancel the operation.
585
+ * @returns A promise that resolves when the files are written
586
+ *
587
+ * @example
588
+ * // Write an executable script
589
+ * await sandbox.writeFiles([
590
+ * { path: "/usr/local/bin/myscript", content: "#!/bin/bash\necho hello", mode: 0o755 }
591
+ * ]);
592
+ */
593
+ async writeFiles(files, opts) {
594
+ "use step";
595
+ return this.withResume(() => this.session.writeFiles(files, opts), opts?.signal);
596
+ }
597
+ /**
598
+ * Get the public domain of a port of this sandbox.
599
+ *
600
+ * @param p - Port number to resolve
601
+ * @returns A full domain (e.g. `https://subdomain.vercel.run`)
602
+ * @throws If the port has no associated route
603
+ */
604
+ domain(p) {
605
+ return this.currentSession().domain(p);
606
+ }
607
+ /**
608
+ * Stop the sandbox.
609
+ *
610
+ * @param opts - Optional parameters.
611
+ * @param opts.signal - An AbortSignal to cancel the operation.
612
+ * @returns The final session state after stopping, with optional snapshot metadata.
613
+ */
614
+ async stop(opts) {
615
+ "use step";
616
+ if (!this.session) throw new Error("No active session to stop.");
617
+ const { session, sandbox, snapshot } = await this.session.stop(opts);
618
+ if (sandbox) this.sandbox = sandbox;
619
+ return Object.assign(session, { snapshot });
620
+ }
621
+ /**
622
+ * Update the network policy for this sandbox.
623
+ *
624
+ * @deprecated Use {@link Sandbox.update} instead.
625
+ *
626
+ * @param networkPolicy - The new network policy to apply.
627
+ * @param opts - Optional parameters.
628
+ * @param opts.signal - An AbortSignal to cancel the operation.
629
+ * @returns A promise that resolves when the network policy is updated.
630
+ *
631
+ * @example
632
+ * // Restrict to specific domains
633
+ * await sandbox.updateNetworkPolicy({
634
+ * allow: ["*.npmjs.org", "github.com"],
635
+ * });
636
+ *
637
+ * @example
638
+ * // Inject credentials with per-domain transformers
639
+ * await sandbox.updateNetworkPolicy({
640
+ * allow: {
641
+ * "ai-gateway.vercel.sh": [{
642
+ * transform: [{
643
+ * headers: { authorization: "Bearer ..." }
644
+ * }]
645
+ * }],
646
+ * "*": []
647
+ * }
648
+ * });
649
+ *
650
+ * @example
651
+ * // Deny all network access
652
+ * await sandbox.updateNetworkPolicy("deny-all");
653
+ */
654
+ async updateNetworkPolicy(networkPolicy, opts) {
655
+ "use step";
656
+ await this.withResume(() => this.session.update({ networkPolicy }, opts), opts?.signal);
657
+ return this.session.networkPolicy;
658
+ }
659
+ /**
660
+ * Extend the timeout of the sandbox by the specified duration.
661
+ *
662
+ * This allows you to extend the lifetime of a sandbox up until the maximum
663
+ * execution timeout for your plan.
664
+ *
665
+ * @param duration - The duration in milliseconds to extend the timeout by
666
+ * @param opts - Optional parameters.
667
+ * @param opts.signal - An AbortSignal to cancel the operation.
668
+ * @returns A promise that resolves when the timeout is extended
669
+ *
670
+ * @example
671
+ * const sandbox = await Sandbox.create({ timeout: ms('10m') });
672
+ * // Extends timeout by 5 minutes, to a total of 15 minutes.
673
+ * await sandbox.extendTimeout(ms('5m'));
674
+ */
675
+ async extendTimeout(duration, opts) {
676
+ "use step";
677
+ return this.withResume(() => this.session.extendTimeout(duration, opts), opts?.signal);
678
+ }
679
+ /**
680
+ * Create a snapshot from this currently running sandbox. New sandboxes can
681
+ * then be created from this snapshot using {@link Sandbox.createFromSnapshot}.
682
+ *
683
+ * Note: this sandbox will be stopped as part of the snapshot creation process.
684
+ *
685
+ * @param opts - Optional parameters.
686
+ * @param opts.expiration - Optional expiration time in milliseconds. Use 0 for no expiration at all.
687
+ * @param opts.signal - An AbortSignal to cancel the operation.
688
+ * @returns A promise that resolves to the Snapshot instance
689
+ */
690
+ async snapshot(opts) {
691
+ "use step";
692
+ return this.withResume(() => this.session.snapshot(opts), opts?.signal);
693
+ }
694
+ /**
695
+ * Update the sandbox configuration.
696
+ *
697
+ * When `ports` is provided, it is treated as the full desired port list:
698
+ * any currently exposed port omitted from the array will be deregistered.
699
+ *
700
+ * @param params - Fields to update.
701
+ * @param opts - Optional abort signal.
702
+ */
703
+ async update(params, opts) {
704
+ "use step";
705
+ const client = await this.ensureClient();
706
+ let resources;
707
+ if (params.resources?.vcpus) resources = {
708
+ vcpus: params.resources.vcpus,
709
+ memory: params.resources.vcpus * 2048
710
+ };
711
+ const response = await client.updateSandbox({
712
+ name: this.sandbox.name,
713
+ projectId: this.projectId,
714
+ persistent: params.persistent,
715
+ resources,
716
+ timeout: params.timeout,
717
+ networkPolicy: params.networkPolicy,
718
+ tags: params.tags,
719
+ ports: params.ports,
720
+ snapshotExpiration: params.snapshotExpiration,
721
+ currentSnapshotId: params.currentSnapshotId,
722
+ signal: opts?.signal
723
+ });
724
+ this.sandbox = response.json.sandbox;
725
+ if (params.ports !== void 0 && response.json.routes) this.session?.updateRoutes(response.json.routes);
726
+ if (params.networkPolicy) try {
727
+ return await this.session?.update({ networkPolicy: params.networkPolicy }, opts);
728
+ } catch (err) {
729
+ if (isSandboxStoppedError(err) || isSandboxStoppingError(err)) return;
730
+ throw err;
731
+ }
732
+ }
733
+ /**
734
+ * Delete this sandbox.
735
+ *
736
+ * After deletion the instance becomes inert — all further API calls will
737
+ * throw immediately.
738
+ */
739
+ async delete(opts) {
740
+ "use step";
741
+ await (await this.ensureClient()).deleteSandbox({
742
+ name: this.sandbox.name,
743
+ projectId: this.projectId,
744
+ signal: opts?.signal
745
+ });
746
+ }
747
+ /**
748
+ * List sessions (VMs) that have been created for this sandbox.
749
+ *
750
+ * @param params - Optional pagination parameters.
751
+ * @returns The list of sessions and pagination metadata.
752
+ */
753
+ async listSessions(params) {
754
+ "use step";
755
+ const client = await this.ensureClient();
756
+ const fetchPage = async (cursor) => {
757
+ return (await client.listSessions({
758
+ projectId: this.projectId,
759
+ name: this.sandbox.name,
760
+ limit: params?.limit,
761
+ cursor,
762
+ sortOrder: params?.sortOrder,
763
+ signal: params?.signal
764
+ })).json;
765
+ };
766
+ return attachPaginator(await fetchPage(params?.cursor), {
767
+ itemsKey: "sessions",
768
+ fetchNext: fetchPage,
769
+ signal: params?.signal
770
+ });
771
+ }
772
+ /**
773
+ * List snapshots that belong to this sandbox.
774
+ *
775
+ * @param params - Optional pagination parameters.
776
+ * @returns The list of snapshots and pagination metadata.
777
+ */
778
+ async listSnapshots(params) {
779
+ "use step";
780
+ const client = await this.ensureClient();
781
+ const fetchPage = async (cursor) => {
782
+ return (await client.listSnapshots({
783
+ projectId: this.projectId,
784
+ name: this.sandbox.name,
785
+ limit: params?.limit,
786
+ cursor,
787
+ sortOrder: params?.sortOrder,
788
+ signal: params?.signal
789
+ })).json;
790
+ };
791
+ return attachPaginator(await fetchPage(params?.cursor), {
792
+ itemsKey: "snapshots",
793
+ fetchNext: fetchPage,
794
+ signal: params?.signal
795
+ });
796
+ }
797
+ };
798
+ /**
799
+ * A {@link Sandbox} that can automatically be disposed using a `await using` statement.
800
+ *
801
+ * @example
802
+ * {
803
+ * await using const sandbox = await Sandbox.create();
804
+ * }
805
+ * // Sandbox is automatically stopped here
806
+ */
807
+ var DisposableSandbox = class extends Sandbox {
808
+ async [Symbol.asyncDispose]() {
809
+ await this.stop();
810
+ }
811
+ };
812
+
813
+ //#endregion
814
+ export { Sandbox };
564
815
  //# sourceMappingURL=sandbox.js.map