@vercel/sandbox 0.0.21 → 0.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +14 -0
- package/dist/api-client/api-client.d.ts +18 -1
- package/dist/api-client/api-client.js +22 -4
- package/dist/api-client/base-client.js +1 -0
- package/dist/api-client/validators.d.ts +87 -0
- package/dist/api-client/validators.js +4 -1
- package/dist/command.d.ts +26 -7
- package/dist/command.js +22 -10
- package/dist/sandbox.d.ts +60 -6
- package/dist/sandbox.js +57 -10
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/api-client/api-client.ts +53 -6
- package/src/api-client/base-client.ts +1 -0
- package/src/api-client/validators.ts +4 -0
- package/src/command.ts +26 -10
- package/src/sandbox.test.ts +25 -0
- package/src/sandbox.ts +93 -11
- package/src/version.ts +1 -1
package/dist/sandbox.js
CHANGED
|
@@ -24,6 +24,12 @@ class Sandbox {
|
|
|
24
24
|
get status() {
|
|
25
25
|
return this.sandbox.status;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* The timeout of the sandbox in milliseconds.
|
|
29
|
+
*/
|
|
30
|
+
get timeout() {
|
|
31
|
+
return this.sandbox.timeout;
|
|
32
|
+
}
|
|
27
33
|
/**
|
|
28
34
|
* Allow to get a list of sandboxes for a team narrowed to the given params.
|
|
29
35
|
* It returns both the sandboxes and the pagination metadata to allow getting
|
|
@@ -35,7 +41,10 @@ class Sandbox {
|
|
|
35
41
|
teamId: credentials.teamId,
|
|
36
42
|
token: credentials.token,
|
|
37
43
|
});
|
|
38
|
-
return client.listSandboxes(
|
|
44
|
+
return client.listSandboxes({
|
|
45
|
+
...params,
|
|
46
|
+
signal: params.signal,
|
|
47
|
+
});
|
|
39
48
|
}
|
|
40
49
|
/**
|
|
41
50
|
* Create a new sandbox.
|
|
@@ -57,6 +66,7 @@ class Sandbox {
|
|
|
57
66
|
timeout: params?.timeout,
|
|
58
67
|
resources: params?.resources,
|
|
59
68
|
runtime: params?.runtime,
|
|
69
|
+
signal: params?.signal,
|
|
60
70
|
...privateParams,
|
|
61
71
|
});
|
|
62
72
|
return new Sandbox({
|
|
@@ -79,6 +89,7 @@ class Sandbox {
|
|
|
79
89
|
});
|
|
80
90
|
const sandbox = await client.getSandbox({
|
|
81
91
|
sandboxId: params.sandboxId,
|
|
92
|
+
signal: params.signal,
|
|
82
93
|
});
|
|
83
94
|
return new Sandbox({
|
|
84
95
|
client,
|
|
@@ -102,12 +113,15 @@ class Sandbox {
|
|
|
102
113
|
* Get a previously run command by its ID.
|
|
103
114
|
*
|
|
104
115
|
* @param cmdId - ID of the command to retrieve
|
|
116
|
+
* @param opts - Optional parameters.
|
|
117
|
+
* @param opts.signal - An AbortSignal to cancel the operation.
|
|
105
118
|
* @returns A {@link Command} instance representing the command
|
|
106
119
|
*/
|
|
107
|
-
async getCommand(cmdId) {
|
|
120
|
+
async getCommand(cmdId, opts) {
|
|
108
121
|
const command = await this.client.getCommand({
|
|
109
122
|
sandboxId: this.sandbox.id,
|
|
110
123
|
cmdId,
|
|
124
|
+
signal: opts?.signal,
|
|
111
125
|
});
|
|
112
126
|
return new command_1.Command({
|
|
113
127
|
client: this.client,
|
|
@@ -115,9 +129,9 @@ class Sandbox {
|
|
|
115
129
|
cmd: command.json.command,
|
|
116
130
|
});
|
|
117
131
|
}
|
|
118
|
-
async runCommand(commandOrParams, args) {
|
|
132
|
+
async runCommand(commandOrParams, args, opts) {
|
|
119
133
|
return typeof commandOrParams === "string"
|
|
120
|
-
? this._runCommand({ cmd: commandOrParams, args })
|
|
134
|
+
? this._runCommand({ cmd: commandOrParams, args, signal: opts?.signal })
|
|
121
135
|
: this._runCommand(commandOrParams);
|
|
122
136
|
}
|
|
123
137
|
/**
|
|
@@ -135,6 +149,7 @@ class Sandbox {
|
|
|
135
149
|
cwd: params.cwd,
|
|
136
150
|
env: params.env ?? {},
|
|
137
151
|
sudo: params.sudo ?? false,
|
|
152
|
+
signal: params.signal,
|
|
138
153
|
});
|
|
139
154
|
const command = new command_1.Command({
|
|
140
155
|
client: this.client,
|
|
@@ -143,7 +158,7 @@ class Sandbox {
|
|
|
143
158
|
});
|
|
144
159
|
if (params.stdout || params.stderr) {
|
|
145
160
|
(async () => {
|
|
146
|
-
for await (const log of command.logs()) {
|
|
161
|
+
for await (const log of command.logs({ signal: params.signal })) {
|
|
147
162
|
if (log.stream === "stdout") {
|
|
148
163
|
params.stdout?.write(log.data);
|
|
149
164
|
}
|
|
@@ -153,30 +168,36 @@ class Sandbox {
|
|
|
153
168
|
}
|
|
154
169
|
})();
|
|
155
170
|
}
|
|
156
|
-
return params.detached ? command : command.wait();
|
|
171
|
+
return params.detached ? command : command.wait({ signal: params.signal });
|
|
157
172
|
}
|
|
158
173
|
/**
|
|
159
174
|
* Create a directory in the filesystem of this sandbox.
|
|
160
175
|
*
|
|
161
176
|
* @param path - Path of the directory to create
|
|
177
|
+
* @param opts - Optional parameters.
|
|
178
|
+
* @param opts.signal - An AbortSignal to cancel the operation.
|
|
162
179
|
*/
|
|
163
|
-
async mkDir(path) {
|
|
180
|
+
async mkDir(path, opts) {
|
|
164
181
|
await this.client.mkDir({
|
|
165
182
|
sandboxId: this.sandbox.id,
|
|
166
183
|
path: path,
|
|
184
|
+
signal: opts?.signal,
|
|
167
185
|
});
|
|
168
186
|
}
|
|
169
187
|
/**
|
|
170
188
|
* Read a file from the filesystem of this sandbox.
|
|
171
189
|
*
|
|
172
190
|
* @param file - File to read, with path and optional cwd
|
|
191
|
+
* @param opts - Optional parameters.
|
|
192
|
+
* @param opts.signal - An AbortSignal to cancel the operation.
|
|
173
193
|
* @returns A promise that resolves to a ReadableStream containing the file contents
|
|
174
194
|
*/
|
|
175
|
-
async readFile(file) {
|
|
195
|
+
async readFile(file, opts) {
|
|
176
196
|
return this.client.readFile({
|
|
177
197
|
sandboxId: this.sandbox.id,
|
|
178
198
|
path: file.path,
|
|
179
199
|
cwd: file.cwd,
|
|
200
|
+
signal: opts?.signal,
|
|
180
201
|
});
|
|
181
202
|
}
|
|
182
203
|
/**
|
|
@@ -185,14 +206,17 @@ class Sandbox {
|
|
|
185
206
|
* Writes files using the `vercel-sandbox` user.
|
|
186
207
|
*
|
|
187
208
|
* @param files - Array of files with path and stream/buffer contents
|
|
209
|
+
* @param opts - Optional parameters.
|
|
210
|
+
* @param opts.signal - An AbortSignal to cancel the operation.
|
|
188
211
|
* @returns A promise that resolves when the files are written
|
|
189
212
|
*/
|
|
190
|
-
async writeFiles(files) {
|
|
213
|
+
async writeFiles(files, opts) {
|
|
191
214
|
return this.client.writeFiles({
|
|
192
215
|
sandboxId: this.sandbox.id,
|
|
193
216
|
cwd: this.sandbox.cwd,
|
|
194
217
|
extractDir: "/",
|
|
195
218
|
files: files,
|
|
219
|
+
signal: opts?.signal,
|
|
196
220
|
});
|
|
197
221
|
}
|
|
198
222
|
/**
|
|
@@ -214,12 +238,35 @@ class Sandbox {
|
|
|
214
238
|
/**
|
|
215
239
|
* Stop the sandbox.
|
|
216
240
|
*
|
|
241
|
+
* @param opts - Optional parameters.
|
|
242
|
+
* @param opts.signal - An AbortSignal to cancel the operation.
|
|
217
243
|
* @returns A promise that resolves when the sandbox is stopped
|
|
218
244
|
*/
|
|
219
|
-
async stop() {
|
|
245
|
+
async stop(opts) {
|
|
220
246
|
await this.client.stopSandbox({
|
|
221
247
|
sandboxId: this.sandbox.id,
|
|
248
|
+
signal: opts?.signal,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Extend the timeout of the sandbox by the specified duration.
|
|
253
|
+
*
|
|
254
|
+
* This allows you to extend the lifetime of a sandbox up until the maximum
|
|
255
|
+
* execution timeout for your plan.
|
|
256
|
+
*
|
|
257
|
+
* @param duration - The duration in milliseconds to extend the timeout by
|
|
258
|
+
* @param opts - Optional parameters.
|
|
259
|
+
* @param opts.signal - An AbortSignal to cancel the operation.
|
|
260
|
+
* @returns A promise that resolves when the timeout is extended
|
|
261
|
+
*/
|
|
262
|
+
async extendTimeout(duration, opts) {
|
|
263
|
+
const response = await this.client.extendTimeout({
|
|
264
|
+
sandboxId: this.sandbox.id,
|
|
265
|
+
duration,
|
|
266
|
+
signal: opts?.signal,
|
|
222
267
|
});
|
|
268
|
+
// Update the internal sandbox metadata with the new timeout value
|
|
269
|
+
this.sandbox = response.json.sandbox;
|
|
223
270
|
}
|
|
224
271
|
}
|
|
225
272
|
exports.Sandbox = Sandbox;
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.0.
|
|
1
|
+
export declare const VERSION = "0.0.23";
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
EmptyResponse,
|
|
13
13
|
LogLine,
|
|
14
14
|
SandboxesResponse,
|
|
15
|
+
ExtendTimeoutResponse,
|
|
15
16
|
} from "./validators";
|
|
16
17
|
import { APIError } from "./api-error";
|
|
17
18
|
import { FileWriter } from "./file-writer";
|
|
@@ -71,10 +72,12 @@ export class APIClient extends BaseClient {
|
|
|
71
72
|
});
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
async getSandbox(params: { sandboxId: string }) {
|
|
75
|
+
async getSandbox(params: { sandboxId: string; signal?: AbortSignal }) {
|
|
75
76
|
return parseOrThrow(
|
|
76
77
|
SandboxAndRoutesResponse,
|
|
77
|
-
await this.request(`/v1/sandboxes/${params.sandboxId}
|
|
78
|
+
await this.request(`/v1/sandboxes/${params.sandboxId}`, {
|
|
79
|
+
signal: params.signal,
|
|
80
|
+
}),
|
|
78
81
|
);
|
|
79
82
|
}
|
|
80
83
|
|
|
@@ -95,6 +98,7 @@ export class APIClient extends BaseClient {
|
|
|
95
98
|
timeout?: number;
|
|
96
99
|
resources?: { vcpus: number };
|
|
97
100
|
runtime?: "node22" | "python3.13" | (string & {});
|
|
101
|
+
signal?: AbortSignal;
|
|
98
102
|
}>,
|
|
99
103
|
) {
|
|
100
104
|
const privateParams = getPrivateParams(params);
|
|
@@ -111,6 +115,7 @@ export class APIClient extends BaseClient {
|
|
|
111
115
|
runtime: params.runtime,
|
|
112
116
|
...privateParams,
|
|
113
117
|
}),
|
|
118
|
+
signal: params.signal,
|
|
114
119
|
}),
|
|
115
120
|
);
|
|
116
121
|
}
|
|
@@ -122,6 +127,7 @@ export class APIClient extends BaseClient {
|
|
|
122
127
|
args: string[];
|
|
123
128
|
env: Record<string, string>;
|
|
124
129
|
sudo: boolean;
|
|
130
|
+
signal?: AbortSignal;
|
|
125
131
|
}) {
|
|
126
132
|
return parseOrThrow(
|
|
127
133
|
CommandResponse,
|
|
@@ -134,6 +140,7 @@ export class APIClient extends BaseClient {
|
|
|
134
140
|
env: params.env,
|
|
135
141
|
sudo: params.sudo,
|
|
136
142
|
}),
|
|
143
|
+
signal: params.signal,
|
|
137
144
|
}),
|
|
138
145
|
);
|
|
139
146
|
}
|
|
@@ -142,44 +149,58 @@ export class APIClient extends BaseClient {
|
|
|
142
149
|
sandboxId: string;
|
|
143
150
|
cmdId: string;
|
|
144
151
|
wait: true;
|
|
152
|
+
signal?: AbortSignal;
|
|
145
153
|
}): Promise<Parsed<z.infer<typeof CommandFinishedResponse>>>;
|
|
146
154
|
async getCommand(params: {
|
|
147
155
|
sandboxId: string;
|
|
148
156
|
cmdId: string;
|
|
149
157
|
wait?: boolean;
|
|
158
|
+
signal?: AbortSignal;
|
|
150
159
|
}): Promise<Parsed<z.infer<typeof CommandResponse>>>;
|
|
151
160
|
async getCommand(params: {
|
|
152
161
|
sandboxId: string;
|
|
153
162
|
cmdId: string;
|
|
154
163
|
wait?: boolean;
|
|
164
|
+
signal?: AbortSignal;
|
|
155
165
|
}) {
|
|
156
166
|
return params.wait
|
|
157
167
|
? parseOrThrow(
|
|
158
168
|
CommandFinishedResponse,
|
|
159
169
|
await this.request(
|
|
160
170
|
`/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`,
|
|
161
|
-
{ query: { wait: "true" } },
|
|
171
|
+
{ signal: params.signal, query: { wait: "true" } },
|
|
162
172
|
),
|
|
163
173
|
)
|
|
164
174
|
: parseOrThrow(
|
|
165
175
|
CommandResponse,
|
|
166
176
|
await this.request(
|
|
167
177
|
`/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`,
|
|
178
|
+
{ signal: params.signal },
|
|
168
179
|
),
|
|
169
180
|
);
|
|
170
181
|
}
|
|
171
182
|
|
|
172
|
-
async mkDir(params: {
|
|
183
|
+
async mkDir(params: {
|
|
184
|
+
sandboxId: string;
|
|
185
|
+
path: string;
|
|
186
|
+
cwd?: string;
|
|
187
|
+
signal?: AbortSignal;
|
|
188
|
+
}) {
|
|
173
189
|
return parseOrThrow(
|
|
174
190
|
EmptyResponse,
|
|
175
191
|
await this.request(`/v1/sandboxes/${params.sandboxId}/fs/mkdir`, {
|
|
176
192
|
method: "POST",
|
|
177
193
|
body: JSON.stringify({ path: params.path, cwd: params.cwd }),
|
|
194
|
+
signal: params.signal,
|
|
178
195
|
}),
|
|
179
196
|
);
|
|
180
197
|
}
|
|
181
198
|
|
|
182
|
-
getFileWriter(params: {
|
|
199
|
+
getFileWriter(params: {
|
|
200
|
+
sandboxId: string;
|
|
201
|
+
extractDir: string;
|
|
202
|
+
signal?: AbortSignal;
|
|
203
|
+
}) {
|
|
183
204
|
const writer = new FileWriter();
|
|
184
205
|
return {
|
|
185
206
|
response: (async () => {
|
|
@@ -190,6 +211,7 @@ export class APIClient extends BaseClient {
|
|
|
190
211
|
"x-cwd": params.extractDir,
|
|
191
212
|
},
|
|
192
213
|
body: await consumeReadable(writer.readable),
|
|
214
|
+
signal: params.signal,
|
|
193
215
|
});
|
|
194
216
|
})(),
|
|
195
217
|
writer,
|
|
@@ -217,6 +239,7 @@ export class APIClient extends BaseClient {
|
|
|
217
239
|
* @example 1540095775951
|
|
218
240
|
*/
|
|
219
241
|
until?: number | Date;
|
|
242
|
+
signal?: AbortSignal;
|
|
220
243
|
}) {
|
|
221
244
|
return parseOrThrow(
|
|
222
245
|
SandboxesResponse,
|
|
@@ -234,6 +257,7 @@ export class APIClient extends BaseClient {
|
|
|
234
257
|
: params.until?.getTime(),
|
|
235
258
|
},
|
|
236
259
|
method: "GET",
|
|
260
|
+
signal: params.signal,
|
|
237
261
|
}),
|
|
238
262
|
);
|
|
239
263
|
}
|
|
@@ -243,10 +267,12 @@ export class APIClient extends BaseClient {
|
|
|
243
267
|
cwd: string;
|
|
244
268
|
files: { path: string; content: Buffer }[];
|
|
245
269
|
extractDir: string;
|
|
270
|
+
signal?: AbortSignal;
|
|
246
271
|
}) {
|
|
247
272
|
const { writer, response } = this.getFileWriter({
|
|
248
273
|
sandboxId: params.sandboxId,
|
|
249
274
|
extractDir: params.extractDir,
|
|
275
|
+
signal: params.signal,
|
|
250
276
|
});
|
|
251
277
|
|
|
252
278
|
for (const file of params.files) {
|
|
@@ -268,12 +294,14 @@ export class APIClient extends BaseClient {
|
|
|
268
294
|
sandboxId: string;
|
|
269
295
|
path: string;
|
|
270
296
|
cwd?: string;
|
|
297
|
+
signal?: AbortSignal;
|
|
271
298
|
}): Promise<NodeJS.ReadableStream | null> {
|
|
272
299
|
const response = await this.request(
|
|
273
300
|
`/v1/sandboxes/${params.sandboxId}/fs/read`,
|
|
274
301
|
{
|
|
275
302
|
method: "POST",
|
|
276
303
|
body: JSON.stringify({ path: params.path, cwd: params.cwd }),
|
|
304
|
+
signal: params.signal,
|
|
277
305
|
},
|
|
278
306
|
);
|
|
279
307
|
|
|
@@ -292,6 +320,7 @@ export class APIClient extends BaseClient {
|
|
|
292
320
|
sandboxId: string;
|
|
293
321
|
commandId: string;
|
|
294
322
|
signal: number;
|
|
323
|
+
abortSignal?: AbortSignal;
|
|
295
324
|
}) {
|
|
296
325
|
return parseOrThrow(
|
|
297
326
|
CommandResponse,
|
|
@@ -300,6 +329,7 @@ export class APIClient extends BaseClient {
|
|
|
300
329
|
{
|
|
301
330
|
method: "POST",
|
|
302
331
|
body: JSON.stringify({ signal: params.signal }),
|
|
332
|
+
signal: params.abortSignal,
|
|
303
333
|
},
|
|
304
334
|
),
|
|
305
335
|
);
|
|
@@ -356,11 +386,28 @@ export class APIClient extends BaseClient {
|
|
|
356
386
|
|
|
357
387
|
async stopSandbox(params: {
|
|
358
388
|
sandboxId: string;
|
|
389
|
+
signal?: AbortSignal;
|
|
359
390
|
}): Promise<Parsed<z.infer<typeof SandboxResponse>>> {
|
|
360
391
|
const url = `/v1/sandboxes/${params.sandboxId}/stop`;
|
|
361
392
|
return parseOrThrow(
|
|
362
393
|
SandboxResponse,
|
|
363
|
-
await this.request(url, { method: "POST" }),
|
|
394
|
+
await this.request(url, { method: "POST", signal: params.signal }),
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async extendTimeout(params: {
|
|
399
|
+
sandboxId: string;
|
|
400
|
+
duration: number;
|
|
401
|
+
signal?: AbortSignal;
|
|
402
|
+
}): Promise<Parsed<z.infer<typeof ExtendTimeoutResponse>>> {
|
|
403
|
+
const url = `/v1/sandboxes/${params.sandboxId}/extend-timeout`;
|
|
404
|
+
return parseOrThrow(
|
|
405
|
+
ExtendTimeoutResponse,
|
|
406
|
+
await this.request(url, {
|
|
407
|
+
method: "POST",
|
|
408
|
+
body: JSON.stringify({ duration: params.duration }),
|
|
409
|
+
signal: params.signal,
|
|
410
|
+
}),
|
|
364
411
|
);
|
|
365
412
|
}
|
|
366
413
|
}
|
package/src/command.ts
CHANGED
|
@@ -112,13 +112,18 @@ export class Command {
|
|
|
112
112
|
* }
|
|
113
113
|
* ```
|
|
114
114
|
*
|
|
115
|
+
* @param params - Optional parameters.
|
|
116
|
+
* @param params.signal - An AbortSignal to cancel waiting.
|
|
115
117
|
* @returns A {@link CommandFinished} instance with populated exit code.
|
|
116
118
|
*/
|
|
117
|
-
async wait() {
|
|
119
|
+
async wait(params?: { signal?: AbortSignal }) {
|
|
120
|
+
params?.signal?.throwIfAborted();
|
|
121
|
+
|
|
118
122
|
const command = await this.client.getCommand({
|
|
119
123
|
sandboxId: this.sandboxId,
|
|
120
124
|
cmdId: this.cmd.id,
|
|
121
125
|
wait: true,
|
|
126
|
+
signal: params?.signal,
|
|
122
127
|
});
|
|
123
128
|
|
|
124
129
|
return new CommandFinished({
|
|
@@ -136,11 +141,16 @@ export class Command {
|
|
|
136
141
|
* not output valid Unicode.
|
|
137
142
|
*
|
|
138
143
|
* @param stream - The output stream to read: "stdout", "stderr", or "both".
|
|
144
|
+
* @param opts - Optional parameters.
|
|
145
|
+
* @param opts.signal - An AbortSignal to cancel output streaming.
|
|
139
146
|
* @returns The output of the specified stream(s) as a string.
|
|
140
147
|
*/
|
|
141
|
-
async output(
|
|
148
|
+
async output(
|
|
149
|
+
stream: "stdout" | "stderr" | "both" = "both",
|
|
150
|
+
opts?: { signal?: AbortSignal },
|
|
151
|
+
) {
|
|
142
152
|
let data = "";
|
|
143
|
-
for await (const log of this.logs()) {
|
|
153
|
+
for await (const log of this.logs({ signal: opts?.signal })) {
|
|
144
154
|
if (stream === "both" || log.stream === stream) {
|
|
145
155
|
data += log.data;
|
|
146
156
|
}
|
|
@@ -154,10 +164,12 @@ export class Command {
|
|
|
154
164
|
* NOTE: This may throw string conversion errors if the command does
|
|
155
165
|
* not output valid Unicode.
|
|
156
166
|
*
|
|
167
|
+
* @param opts - Optional parameters.
|
|
168
|
+
* @param opts.signal - An AbortSignal to cancel output streaming.
|
|
157
169
|
* @returns The standard output of the command.
|
|
158
170
|
*/
|
|
159
|
-
async stdout() {
|
|
160
|
-
return this.output("stdout");
|
|
171
|
+
async stdout(opts?: { signal?: AbortSignal }) {
|
|
172
|
+
return this.output("stdout", opts);
|
|
161
173
|
}
|
|
162
174
|
|
|
163
175
|
/**
|
|
@@ -166,24 +178,28 @@ export class Command {
|
|
|
166
178
|
* NOTE: This may throw string conversion errors if the command does
|
|
167
179
|
* not output valid Unicode.
|
|
168
180
|
*
|
|
181
|
+
* @param opts - Optional parameters.
|
|
182
|
+
* @param opts.signal - An AbortSignal to cancel output streaming.
|
|
169
183
|
* @returns The standard error output of the command.
|
|
170
184
|
*/
|
|
171
|
-
async stderr() {
|
|
172
|
-
return this.output("stderr");
|
|
185
|
+
async stderr(opts?: { signal?: AbortSignal }) {
|
|
186
|
+
return this.output("stderr", opts);
|
|
173
187
|
}
|
|
174
188
|
|
|
175
189
|
/**
|
|
176
190
|
* Kill a running command in a sandbox.
|
|
177
191
|
*
|
|
178
|
-
* @param
|
|
179
|
-
*
|
|
192
|
+
* @param signal - The signal to send the running process. Defaults to SIGTERM.
|
|
193
|
+
* @param opts - Optional parameters.
|
|
194
|
+
* @param opts.abortSignal - An AbortSignal to cancel the kill operation.
|
|
180
195
|
* @returns Promise<void>.
|
|
181
196
|
*/
|
|
182
|
-
async kill(signal?: Signal) {
|
|
197
|
+
async kill(signal?: Signal, opts?: { abortSignal?: AbortSignal }) {
|
|
183
198
|
await this.client.killCommand({
|
|
184
199
|
sandboxId: this.sandboxId,
|
|
185
200
|
commandId: this.cmd.id,
|
|
186
201
|
signal: resolveSignal(signal ?? "SIGTERM"),
|
|
202
|
+
abortSignal: opts?.abortSignal,
|
|
187
203
|
});
|
|
188
204
|
}
|
|
189
205
|
}
|
package/src/sandbox.test.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { it, beforeEach, afterEach, expect } from "vitest";
|
|
2
2
|
import { consumeReadable } from "./utils/consume-readable";
|
|
3
3
|
import { Sandbox } from "./sandbox";
|
|
4
|
+
import { APIError } from "./api-client/api-error";
|
|
5
|
+
import ms from "ms";
|
|
4
6
|
|
|
5
7
|
const PORTS = [3000, 4000];
|
|
6
8
|
let sandbox: Sandbox;
|
|
@@ -64,3 +66,26 @@ for (const port of ports) {
|
|
|
64
66
|
|
|
65
67
|
await server.kill();
|
|
66
68
|
});
|
|
69
|
+
|
|
70
|
+
it("allows extending the sandbox timeout", async () => {
|
|
71
|
+
const originalTimeout = sandbox.timeout;
|
|
72
|
+
const extensionDuration = ms("5m");
|
|
73
|
+
|
|
74
|
+
await sandbox.extendTimeout(extensionDuration);
|
|
75
|
+
expect(sandbox.timeout).toEqual(originalTimeout + extensionDuration);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("raises an error when the timeout cannot be updated", async () => {
|
|
79
|
+
try {
|
|
80
|
+
await sandbox.extendTimeout(ms("5d"));
|
|
81
|
+
expect.fail("Expected extendTimeout to throw an error");
|
|
82
|
+
} catch (error) {
|
|
83
|
+
expect(error).toBeInstanceOf(APIError);
|
|
84
|
+
expect(error).toMatchObject({
|
|
85
|
+
response: { status: 400 },
|
|
86
|
+
json: {
|
|
87
|
+
error: { code: "sandbox_timeout_invalid" },
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
});
|