@agentuity/core 2.0.11 → 2.0.12
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/dist/services/api.d.ts +1 -1
- package/dist/services/api.d.ts.map +1 -1
- package/dist/services/api.js +4 -6
- package/dist/services/api.js.map +1 -1
- package/dist/services/coder/agents.d.ts +6 -4
- package/dist/services/coder/agents.d.ts.map +1 -1
- package/dist/services/coder/api-reference.d.ts.map +1 -1
- package/dist/services/coder/api-reference.js +78 -10
- package/dist/services/coder/api-reference.js.map +1 -1
- package/dist/services/coder/client.d.ts +5 -1
- package/dist/services/coder/client.d.ts.map +1 -1
- package/dist/services/coder/client.js +8 -1
- package/dist/services/coder/client.js.map +1 -1
- package/dist/services/coder/index.d.ts +2 -2
- package/dist/services/coder/index.d.ts.map +1 -1
- package/dist/services/coder/index.js +1 -1
- package/dist/services/coder/index.js.map +1 -1
- package/dist/services/coder/protocol.d.ts +385 -15
- package/dist/services/coder/protocol.d.ts.map +1 -1
- package/dist/services/coder/protocol.js +148 -2
- package/dist/services/coder/protocol.js.map +1 -1
- package/dist/services/coder/sessions.d.ts +24 -2
- package/dist/services/coder/sessions.d.ts.map +1 -1
- package/dist/services/coder/sessions.js +10 -1
- package/dist/services/coder/sessions.js.map +1 -1
- package/dist/services/coder/sse.d.ts +2 -2
- package/dist/services/coder/sse.d.ts.map +1 -1
- package/dist/services/coder/sse.js +290 -178
- package/dist/services/coder/sse.js.map +1 -1
- package/dist/services/coder/types.d.ts +607 -42
- package/dist/services/coder/types.d.ts.map +1 -1
- package/dist/services/coder/types.js +202 -40
- package/dist/services/coder/types.js.map +1 -1
- package/dist/services/coder/websocket.d.ts +13 -1
- package/dist/services/coder/websocket.d.ts.map +1 -1
- package/dist/services/coder/websocket.js +91 -19
- package/dist/services/coder/websocket.js.map +1 -1
- package/dist/services/sandbox/api-reference.js +7 -7
- package/dist/services/sandbox/api-reference.js.map +1 -1
- package/dist/services/sandbox/client.d.ts +3 -2
- package/dist/services/sandbox/client.d.ts.map +1 -1
- package/dist/services/sandbox/client.js.map +1 -1
- package/dist/services/sandbox/create.d.ts +5 -0
- package/dist/services/sandbox/create.d.ts.map +1 -1
- package/dist/services/sandbox/create.js +8 -0
- package/dist/services/sandbox/create.js.map +1 -1
- package/dist/services/sandbox/get.d.ts +8 -4
- package/dist/services/sandbox/get.d.ts.map +1 -1
- package/dist/services/sandbox/get.js +28 -3
- package/dist/services/sandbox/get.js.map +1 -1
- package/dist/services/sandbox/getStatus.d.ts +2 -0
- package/dist/services/sandbox/getStatus.d.ts.map +1 -1
- package/dist/services/sandbox/getStatus.js +17 -1
- package/dist/services/sandbox/getStatus.js.map +1 -1
- package/dist/services/sandbox/index.d.ts +1 -1
- package/dist/services/sandbox/index.d.ts.map +1 -1
- package/dist/services/sandbox/list.d.ts +3 -0
- package/dist/services/sandbox/list.d.ts.map +1 -1
- package/dist/services/sandbox/list.js +5 -0
- package/dist/services/sandbox/list.js.map +1 -1
- package/dist/services/sandbox/pause.d.ts +17 -1
- package/dist/services/sandbox/pause.d.ts.map +1 -1
- package/dist/services/sandbox/pause.js +21 -3
- package/dist/services/sandbox/pause.js.map +1 -1
- package/dist/services/sandbox/run.d.ts +1 -0
- package/dist/services/sandbox/run.d.ts.map +1 -1
- package/dist/services/sandbox/run.js +145 -85
- package/dist/services/sandbox/run.js.map +1 -1
- package/dist/services/sandbox/types.d.ts +8 -2
- package/dist/services/sandbox/types.d.ts.map +1 -1
- package/dist/services/sandbox/types.js +10 -0
- package/dist/services/sandbox/types.js.map +1 -1
- package/dist/services/stream/namespaces.d.ts +2 -2
- package/dist/services/stream/namespaces.js +2 -2
- package/dist/services/stream/namespaces.js.map +1 -1
- package/package.json +2 -2
- package/src/services/api.ts +6 -7
- package/src/services/coder/api-reference.ts +79 -9
- package/src/services/coder/client.ts +12 -0
- package/src/services/coder/index.ts +3 -0
- package/src/services/coder/protocol.ts +166 -2
- package/src/services/coder/sessions.ts +26 -0
- package/src/services/coder/sse.ts +343 -184
- package/src/services/coder/types.ts +257 -44
- package/src/services/coder/websocket.ts +120 -21
- package/src/services/sandbox/api-reference.ts +7 -7
- package/src/services/sandbox/client.ts +4 -4
- package/src/services/sandbox/create.ts +10 -0
- package/src/services/sandbox/get.ts +32 -3
- package/src/services/sandbox/getStatus.ts +20 -1
- package/src/services/sandbox/index.ts +1 -1
- package/src/services/sandbox/list.ts +5 -0
- package/src/services/sandbox/pause.ts +38 -4
- package/src/services/sandbox/run.ts +202 -108
- package/src/services/sandbox/types.ts +15 -2
- package/src/services/stream/namespaces.ts +2 -2
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import type { Logger } from '../../logger.ts';
|
|
2
2
|
import type { Readable, Writable } from 'node:stream';
|
|
3
3
|
import { PassThrough } from 'node:stream';
|
|
4
|
+
import { finished } from 'node:stream/promises';
|
|
4
5
|
import { z } from 'zod';
|
|
5
6
|
import { APIClient, PaymentRequiredError } from '../api.ts';
|
|
6
7
|
import { sandboxCreate } from './create.ts';
|
|
7
8
|
import { sandboxDestroy } from './destroy.ts';
|
|
9
|
+
import { executionGet } from './execution.ts';
|
|
8
10
|
import { sandboxGetStatus } from './getStatus.ts';
|
|
9
11
|
import { ExecutionCancelledError, writeAndDrain } from './util.ts';
|
|
10
12
|
import { SandboxRunOptionsSchema, type SandboxRunResult } from './types.ts';
|
|
11
13
|
import { getServiceUrls } from '../config.ts';
|
|
12
14
|
|
|
13
15
|
const timingLogsEnabled = false;
|
|
16
|
+
const EXECUTION_WAIT_DURATION = '5m';
|
|
17
|
+
const TERMINAL_EXECUTION_STATUSES = new Set(['completed', 'failed', 'timeout', 'cancelled']);
|
|
14
18
|
|
|
15
19
|
/**
|
|
16
20
|
* Creates a Writable stream that captures all chunks to a buffer array
|
|
@@ -73,8 +77,19 @@ export async function sandboxRun(
|
|
|
73
77
|
let stdinStreamId: string | undefined;
|
|
74
78
|
let stdinStreamUrl: string | undefined;
|
|
75
79
|
|
|
76
|
-
//
|
|
77
|
-
|
|
80
|
+
// Handle stdin stream configuration:
|
|
81
|
+
// - If stdin is "ignore", pass it through to skip stdin handling on server
|
|
82
|
+
// - If stdin is an explicit stream ID, use it directly
|
|
83
|
+
// - If stdin readable is provided, create a stream for it
|
|
84
|
+
const stdinConfig = options.stream?.stdin;
|
|
85
|
+
if (stdinConfig === 'ignore') {
|
|
86
|
+
stdinStreamId = 'ignore';
|
|
87
|
+
logger?.debug('stdin explicitly ignored');
|
|
88
|
+
} else if (stdinConfig && stdinConfig !== 'ignore') {
|
|
89
|
+
// User provided an explicit stream ID
|
|
90
|
+
stdinStreamId = stdinConfig;
|
|
91
|
+
logger?.debug('using provided stdin stream ID: %s', stdinStreamId);
|
|
92
|
+
} else if (stdin && region && apiKey) {
|
|
78
93
|
const streamResult = await createStdinStream(region, apiKey, orgId, logger);
|
|
79
94
|
stdinStreamId = streamResult.id;
|
|
80
95
|
stdinStreamUrl = streamResult.url;
|
|
@@ -176,52 +191,45 @@ export async function sandboxRun(
|
|
|
176
191
|
}
|
|
177
192
|
}
|
|
178
193
|
|
|
179
|
-
// Wait for
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
} finally {
|
|
209
|
-
if (onAbort && signal) {
|
|
210
|
-
signal.removeEventListener('abort', onAbort);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
} else {
|
|
214
|
-
await Promise.allSettled(streamPromises);
|
|
215
|
-
}
|
|
194
|
+
// Wait for execution completion in parallel with stream consumption. The old
|
|
195
|
+
// flow waited for stream EOF first and only then started polling for the
|
|
196
|
+
// final exit code, which adds avoidable tail latency now that create returns
|
|
197
|
+
// an execution ID immediately for oneshot sandboxes.
|
|
198
|
+
let finalExecution:
|
|
199
|
+
| {
|
|
200
|
+
exitCode?: number;
|
|
201
|
+
status: string;
|
|
202
|
+
}
|
|
203
|
+
| undefined;
|
|
204
|
+
if (createResponse.executionId) {
|
|
205
|
+
logger?.debug(
|
|
206
|
+
'waiting for execution %s and %d stream(s) in parallel',
|
|
207
|
+
createResponse.executionId,
|
|
208
|
+
streamPromises.length
|
|
209
|
+
);
|
|
210
|
+
const executionPromise = waitForExecutionCompletion(
|
|
211
|
+
client,
|
|
212
|
+
createResponse.executionId,
|
|
213
|
+
orgId,
|
|
214
|
+
signal,
|
|
215
|
+
logger,
|
|
216
|
+
started
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
finalExecution = signal
|
|
220
|
+
? await raceWithAbort(executionPromise, signal, abortController, sandboxId)
|
|
221
|
+
: await executionPromise;
|
|
222
|
+
await waitForStreamsToDrain(streamPromises, signal, abortController, sandboxId);
|
|
216
223
|
} else {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
224
|
+
logger?.debug(
|
|
225
|
+
'missing executionId on create response, falling back to stream-first completion'
|
|
226
|
+
);
|
|
227
|
+
await waitForStreamsToDrain(streamPromises, signal, abortController, sandboxId);
|
|
220
228
|
}
|
|
221
229
|
|
|
222
230
|
if (timingLogsEnabled)
|
|
223
|
-
console.error(`[TIMING] +${Date.now() - started}ms:
|
|
224
|
-
logger?.debug('
|
|
231
|
+
console.error(`[TIMING] +${Date.now() - started}ms: completion wait finished`);
|
|
232
|
+
logger?.debug('completion wait finished, resolving final exit code');
|
|
225
233
|
|
|
226
234
|
// Stream EOF means the sandbox is done — hadron only closes streams after the
|
|
227
235
|
// container exits. Poll for the exit code with retries because the lifecycle
|
|
@@ -234,93 +242,60 @@ export async function sandboxRun(
|
|
|
234
242
|
// linear 1s polling interval (not exponential backoff) so we don't overshoot
|
|
235
243
|
// the window — 15 attempts × 1s = 15s total, which comfortably covers the
|
|
236
244
|
// drain + lifecycle propagation delay.
|
|
237
|
-
|
|
238
|
-
const abortAwareSleep = (ms: number): Promise<void> =>
|
|
239
|
-
new Promise((resolve, reject) => {
|
|
240
|
-
if (signal?.aborted) {
|
|
241
|
-
reject(new DOMException('Aborted', 'AbortError'));
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
const timer = setTimeout(resolve, ms);
|
|
245
|
-
signal?.addEventListener(
|
|
246
|
-
'abort',
|
|
247
|
-
() => {
|
|
248
|
-
clearTimeout(timer);
|
|
249
|
-
reject(new DOMException('Aborted', 'AbortError'));
|
|
250
|
-
},
|
|
251
|
-
{ once: true }
|
|
252
|
-
);
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
let exitCode = 0;
|
|
256
|
-
const maxStatusRetries = 15;
|
|
257
|
-
const statusPollInterval = 1000;
|
|
245
|
+
let exitCode = finalExecution?.exitCode ?? 0;
|
|
258
246
|
const statusPollStart = Date.now();
|
|
259
|
-
|
|
260
|
-
if (signal?.aborted) {
|
|
261
|
-
break;
|
|
262
|
-
}
|
|
247
|
+
if (finalExecution?.exitCode == null) {
|
|
263
248
|
try {
|
|
264
|
-
const sandboxStatus = await sandboxGetStatus(client, {
|
|
249
|
+
const sandboxStatus = await sandboxGetStatus(client, {
|
|
250
|
+
sandboxId,
|
|
251
|
+
orgId,
|
|
252
|
+
waitForStatus: ['terminated', 'failed'],
|
|
253
|
+
waitMs: 15000,
|
|
254
|
+
});
|
|
265
255
|
if (sandboxStatus.exitCode != null) {
|
|
266
256
|
exitCode = sandboxStatus.exitCode;
|
|
267
257
|
logger?.debug(
|
|
268
|
-
'[run] exit code %d found
|
|
258
|
+
'[run] exit code %d found after server-side wait (+%dms)',
|
|
269
259
|
exitCode,
|
|
270
|
-
attempt + 1,
|
|
271
|
-
maxStatusRetries,
|
|
272
260
|
Date.now() - statusPollStart
|
|
273
261
|
);
|
|
274
|
-
break;
|
|
275
262
|
} else if (sandboxStatus.status === 'failed') {
|
|
276
263
|
exitCode = 1;
|
|
277
264
|
logger?.debug(
|
|
278
|
-
'[run] sandbox failed
|
|
279
|
-
attempt + 1,
|
|
280
|
-
maxStatusRetries,
|
|
265
|
+
'[run] sandbox failed after server-side wait (+%dms)',
|
|
281
266
|
Date.now() - statusPollStart
|
|
282
267
|
);
|
|
283
|
-
break;
|
|
284
268
|
} else if (sandboxStatus.status === 'terminated') {
|
|
285
|
-
// Sandbox was destroyed. If exit code is missing, the
|
|
286
|
-
// terminated event may have overwritten it. Stop polling —
|
|
287
|
-
// no further updates will come.
|
|
288
269
|
logger?.debug(
|
|
289
|
-
'[run] sandbox terminated without exit code
|
|
290
|
-
|
|
291
|
-
|
|
270
|
+
'[run] sandbox terminated without exit code after server-side wait (+%dms)',
|
|
271
|
+
Date.now() - statusPollStart
|
|
272
|
+
);
|
|
273
|
+
} else {
|
|
274
|
+
logger?.debug(
|
|
275
|
+
'[run] sandbox status wait expired with status=%s (+%dms)',
|
|
276
|
+
sandboxStatus.status,
|
|
292
277
|
Date.now() - statusPollStart
|
|
293
278
|
);
|
|
294
|
-
break;
|
|
295
|
-
}
|
|
296
|
-
// Exit code not yet propagated — wait before next poll.
|
|
297
|
-
if (attempt < maxStatusRetries - 1) {
|
|
298
|
-
await abortAwareSleep(statusPollInterval);
|
|
299
279
|
}
|
|
300
280
|
} catch (err) {
|
|
301
|
-
if (err instanceof DOMException && err.name === 'AbortError') {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
'[run] sandboxGetStatus attempt %d/%d failed (+%dms): %s',
|
|
308
|
-
attempt + 1,
|
|
309
|
-
maxStatusRetries,
|
|
310
|
-
Date.now() - statusPollStart,
|
|
311
|
-
err
|
|
312
|
-
);
|
|
313
|
-
if (attempt < maxStatusRetries - 1) {
|
|
314
|
-
await abortAwareSleep(statusPollInterval);
|
|
281
|
+
if (!(err instanceof DOMException && err.name === 'AbortError')) {
|
|
282
|
+
logger?.debug(
|
|
283
|
+
'[run] sandboxGetStatus server-side wait failed (+%dms): %s',
|
|
284
|
+
Date.now() - statusPollStart,
|
|
285
|
+
err
|
|
286
|
+
);
|
|
315
287
|
}
|
|
316
288
|
}
|
|
317
289
|
}
|
|
318
290
|
if (exitCode === 0) {
|
|
319
|
-
|
|
320
|
-
'[run] exit code
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
291
|
+
if (finalExecution?.exitCode != null) {
|
|
292
|
+
logger?.debug('[run] using execution exit code 0 from long-poll result');
|
|
293
|
+
} else {
|
|
294
|
+
logger?.debug(
|
|
295
|
+
'[run] exit code wait finished with default 0 (+%dms)',
|
|
296
|
+
Date.now() - statusPollStart
|
|
297
|
+
);
|
|
298
|
+
}
|
|
324
299
|
}
|
|
325
300
|
|
|
326
301
|
if (timingLogsEnabled)
|
|
@@ -352,6 +327,119 @@ export async function sandboxRun(
|
|
|
352
327
|
}
|
|
353
328
|
}
|
|
354
329
|
|
|
330
|
+
async function waitForExecutionCompletion(
|
|
331
|
+
client: APIClient,
|
|
332
|
+
executionId: string,
|
|
333
|
+
orgId: string | undefined,
|
|
334
|
+
signal: AbortSignal | undefined,
|
|
335
|
+
logger: Logger | undefined,
|
|
336
|
+
started: number
|
|
337
|
+
): Promise<{ exitCode?: number; status: string }> {
|
|
338
|
+
while (true) {
|
|
339
|
+
if (signal?.aborted) {
|
|
340
|
+
throw new DOMException('Aborted', 'AbortError');
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const result = await executionGet(client, {
|
|
344
|
+
executionId,
|
|
345
|
+
orgId,
|
|
346
|
+
wait: EXECUTION_WAIT_DURATION,
|
|
347
|
+
signal,
|
|
348
|
+
});
|
|
349
|
+
logger?.debug(
|
|
350
|
+
'[run] execution wait: id=%s status=%s exit=%s +%dms',
|
|
351
|
+
executionId,
|
|
352
|
+
result.status,
|
|
353
|
+
result.exitCode ?? 'undefined',
|
|
354
|
+
Date.now() - started
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
if (TERMINAL_EXECUTION_STATUSES.has(result.status)) {
|
|
358
|
+
return {
|
|
359
|
+
exitCode: result.exitCode,
|
|
360
|
+
status: result.status,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async function waitForStreamsToDrain(
|
|
367
|
+
streamPromises: Promise<void>[],
|
|
368
|
+
signal: AbortSignal | undefined,
|
|
369
|
+
abortController: AbortController,
|
|
370
|
+
sandboxId: string
|
|
371
|
+
): Promise<void> {
|
|
372
|
+
if (streamPromises.length === 0) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (signal) {
|
|
377
|
+
let onAbort: (() => void) | undefined;
|
|
378
|
+
try {
|
|
379
|
+
await Promise.race([
|
|
380
|
+
Promise.allSettled(streamPromises).then(() => undefined),
|
|
381
|
+
new Promise<never>((_, reject) => {
|
|
382
|
+
onAbort = () => {
|
|
383
|
+
abortController.abort();
|
|
384
|
+
reject(
|
|
385
|
+
new ExecutionCancelledError({
|
|
386
|
+
message: 'Sandbox execution cancelled',
|
|
387
|
+
sandboxId,
|
|
388
|
+
})
|
|
389
|
+
);
|
|
390
|
+
};
|
|
391
|
+
if (signal.aborted) {
|
|
392
|
+
onAbort();
|
|
393
|
+
} else {
|
|
394
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
395
|
+
}
|
|
396
|
+
}),
|
|
397
|
+
]);
|
|
398
|
+
} finally {
|
|
399
|
+
if (onAbort) {
|
|
400
|
+
signal.removeEventListener('abort', onAbort);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
await Promise.allSettled(streamPromises);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async function raceWithAbort<T>(
|
|
410
|
+
promise: Promise<T>,
|
|
411
|
+
signal: AbortSignal,
|
|
412
|
+
abortController: AbortController,
|
|
413
|
+
sandboxId: string
|
|
414
|
+
): Promise<T> {
|
|
415
|
+
let onAbort: (() => void) | undefined;
|
|
416
|
+
try {
|
|
417
|
+
return await Promise.race([
|
|
418
|
+
promise,
|
|
419
|
+
new Promise<never>((_, reject) => {
|
|
420
|
+
onAbort = () => {
|
|
421
|
+
abortController.abort();
|
|
422
|
+
reject(
|
|
423
|
+
new ExecutionCancelledError({
|
|
424
|
+
message: 'Sandbox execution cancelled',
|
|
425
|
+
sandboxId,
|
|
426
|
+
})
|
|
427
|
+
);
|
|
428
|
+
};
|
|
429
|
+
if (signal.aborted) {
|
|
430
|
+
onAbort();
|
|
431
|
+
} else {
|
|
432
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
433
|
+
}
|
|
434
|
+
}),
|
|
435
|
+
]);
|
|
436
|
+
} finally {
|
|
437
|
+
if (onAbort) {
|
|
438
|
+
signal.removeEventListener('abort', onAbort);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
355
443
|
async function createStdinStream(
|
|
356
444
|
region: string,
|
|
357
445
|
apiKey: string,
|
|
@@ -526,6 +614,12 @@ async function streamUrlToWritable(
|
|
|
526
614
|
// Signal end-of-stream to the tee/pipe chain so downstream
|
|
527
615
|
// consumers (e.g. process.stdout pipe) know no more data is coming.
|
|
528
616
|
writable.end();
|
|
617
|
+
if ('once' in writable) {
|
|
618
|
+
await finished(writable as NodeJS.WritableStream).catch(() => {
|
|
619
|
+
// Ignore finish errors here; the main read/write path already
|
|
620
|
+
// reported meaningful stream errors.
|
|
621
|
+
});
|
|
622
|
+
}
|
|
529
623
|
} catch (err) {
|
|
530
624
|
if (err instanceof Error && err.name === 'AbortError') {
|
|
531
625
|
logger?.debug('[stream] aborted after %dms', Date.now() - streamStart);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { StructuredError } from '../../error.ts';
|
|
3
3
|
import { SortDirectionSchema } from '../pagination.ts';
|
|
4
|
+
import type { SandboxPauseResult } from './pause.ts';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Resource limits for a sandbox using Kubernetes-style units
|
|
@@ -274,6 +275,13 @@ export const SandboxTimeoutConfigSchema = z.object({
|
|
|
274
275
|
.string()
|
|
275
276
|
.optional()
|
|
276
277
|
.describe('Maximum execution time per command (e.g., "5m", "1h")'),
|
|
278
|
+
/** Maximum duration a sandbox can remain paused before termination (e.g., "24h", "0" for infinite) */
|
|
279
|
+
paused: z
|
|
280
|
+
.string()
|
|
281
|
+
.optional()
|
|
282
|
+
.describe(
|
|
283
|
+
'Maximum duration a sandbox can remain paused before termination (e.g., "24h", "0" for infinite)'
|
|
284
|
+
),
|
|
277
285
|
});
|
|
278
286
|
export type SandboxTimeoutConfig = z.infer<typeof SandboxTimeoutConfigSchema>;
|
|
279
287
|
|
|
@@ -399,7 +407,7 @@ export const SandboxSchema = z.object({
|
|
|
399
407
|
.describe('Set environment variables on the sandbox. Pass null to delete a variable.'),
|
|
400
408
|
/** Pause the sandbox, creating a checkpoint of its current state. */
|
|
401
409
|
pause: z
|
|
402
|
-
.custom<() => Promise<
|
|
410
|
+
.custom<() => Promise<SandboxPauseResult>>()
|
|
403
411
|
.describe('Pause the sandbox, creating a checkpoint of its current state.'),
|
|
404
412
|
/** Resume the sandbox from a paused or evacuated state. */
|
|
405
413
|
resume: z
|
|
@@ -592,6 +600,11 @@ export const SandboxInfoSchema = z.object({
|
|
|
592
600
|
idle: z.string().optional().describe('Idle timeout duration (e.g., "10m0s")'),
|
|
593
601
|
/** Execution timeout duration (e.g., "5m0s") */
|
|
594
602
|
execution: z.string().optional().describe('Execution timeout duration (e.g., "5m0s")'),
|
|
603
|
+
/** Paused timeout duration (e.g., "24h0s", "0s" for infinite) */
|
|
604
|
+
paused: z
|
|
605
|
+
.string()
|
|
606
|
+
.optional()
|
|
607
|
+
.describe('Paused timeout duration (e.g., "24h0s", "0s" for infinite)'),
|
|
595
608
|
})
|
|
596
609
|
.optional()
|
|
597
610
|
.describe('Timeout configuration for this sandbox'),
|
|
@@ -905,7 +918,7 @@ export interface SandboxService {
|
|
|
905
918
|
list(params?: ListSandboxesParams): Promise<ListSandboxesResponse>;
|
|
906
919
|
destroy(sandboxId: string): Promise<void>;
|
|
907
920
|
/** Pause a running sandbox, creating a checkpoint of its current state. */
|
|
908
|
-
pause(sandboxId: string): Promise<
|
|
921
|
+
pause(sandboxId: string): Promise<SandboxPauseResult>;
|
|
909
922
|
/** Resume a paused or evacuated sandbox from its checkpoint. */
|
|
910
923
|
resume(sandboxId: string): Promise<void>;
|
|
911
924
|
snapshot: SnapshotService;
|
|
@@ -40,11 +40,11 @@ export const StreamNamespaceEntrySchema = z.object({
|
|
|
40
40
|
chunks: z.number().describe('number of chunks'),
|
|
41
41
|
completed: z.boolean().describe('whether the stream upload is completed'),
|
|
42
42
|
size_bytes: z.number().describe('size in bytes'),
|
|
43
|
-
started_at: z.string().nullable().describe('ISO 8601 stream start timestamp'),
|
|
43
|
+
started_at: z.string().nullable().optional().describe('ISO 8601 stream start timestamp'),
|
|
44
44
|
ended_at: z.string().nullable().describe('ISO 8601 stream end timestamp'),
|
|
45
45
|
headers: z.record(z.string(), z.string()).nullable().optional().describe('stream headers'),
|
|
46
46
|
metadata: z.record(z.string(), z.string()).nullable().optional().describe('stream metadata'),
|
|
47
|
-
expires_at: z.string().nullable().describe('ISO 8601 expiration timestamp or null'),
|
|
47
|
+
expires_at: z.string().nullable().optional().describe('ISO 8601 expiration timestamp or null'),
|
|
48
48
|
url: z.string().describe('public URL to access the stream'),
|
|
49
49
|
});
|
|
50
50
|
|