withruntime 0.9.0 → 0.10.0
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/BLAXEL.md +4 -4
- package/DAYTONA.md +13 -7
- package/E2B.md +12 -8
- package/README.md +15 -7
- package/README.src.md +15 -7
- package/VERCEL.md +13 -3
- package/compatibility-lock.json +84 -8
- package/dist/blaxel/errors.js +1 -1
- package/dist/blaxel/lookup.d.ts +3 -2
- package/dist/blaxel/lookup.js +3 -2
- package/dist/blaxel/pagination.d.ts +2 -2
- package/dist/blaxel/pagination.js +45 -17
- package/dist/blaxel/preview.d.ts +2 -1
- package/dist/blaxel/preview.js +15 -13
- package/dist/blaxel/process.js +56 -10
- package/dist/blaxel/sandbox.d.ts +2 -0
- package/dist/blaxel/sandbox.js +2 -0
- package/dist/blaxel/snapshot.js +3 -5
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +189 -54
- package/dist/client.d.ts +29 -3
- package/dist/client.js +40 -11
- package/dist/cloudflare/identity.d.ts +7 -0
- package/dist/cloudflare/identity.js +13 -0
- package/dist/cloudflare/index.d.ts +9 -0
- package/dist/cloudflare/index.js +47 -9
- package/dist/cloudflare/worker.d.ts +4 -0
- package/dist/cloudflare/worker.js +166 -16
- package/dist/codesandbox/guest-service.js +57 -20
- package/dist/codesandbox/index.js +42 -8
- package/dist/compat/core.d.ts +9 -8
- package/dist/compat/core.js +60 -21
- package/dist/compat/files.js +22 -4
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +107 -38
- package/dist/daytona/daytona.js +11 -4
- package/dist/daytona/filesystem.d.ts +17 -4
- package/dist/daytona/filesystem.js +121 -38
- package/dist/daytona/index.d.ts +1 -1
- package/dist/daytona/sandbox.d.ts +1 -1
- package/dist/daytona/sandbox.js +16 -7
- package/dist/e2b/client.js +1 -1
- package/dist/e2b/commands.d.ts +30 -8
- package/dist/e2b/commands.js +142 -54
- package/dist/e2b/index.d.ts +1 -0
- package/dist/e2b/index.js +1 -0
- package/dist/e2b/pty.d.ts +24 -0
- package/dist/e2b/pty.js +80 -0
- package/dist/e2b/sandbox.d.ts +2 -1
- package/dist/e2b/sandbox.js +49 -15
- package/dist/file-source.d.ts +16 -0
- package/dist/file-source.js +162 -0
- package/dist/freestyle/index.d.ts +9 -1
- package/dist/freestyle/index.js +38 -5
- package/dist/index.d.ts +2 -1
- package/dist/login.js +16 -10
- package/dist/modal/index.js +2 -1
- package/dist/network-cli.js +111 -43
- package/dist/openai-agents/index.js +9 -45
- package/dist/page.d.ts +1 -1
- package/dist/page.js +2 -2
- package/dist/products/index.d.ts +12 -0
- package/dist/products/index.js +10 -0
- package/dist/products/observability.d.ts +44 -0
- package/dist/products/observability.js +6 -0
- package/dist/products/previews.d.ts +8 -2
- package/dist/products/previews.js +11 -3
- package/dist/products/volumes.d.ts +42 -0
- package/dist/products/volumes.js +36 -0
- package/dist/products/watch.js +61 -8
- package/dist/proxy.js +3 -1
- package/dist/runloop/index.d.ts +13 -27
- package/dist/runloop/index.js +149 -52
- package/dist/sandbox.d.ts +37 -8
- package/dist/sandbox.js +432 -68
- package/dist/snapshots.d.ts +9 -4
- package/dist/sprites/index.js +69 -33
- package/dist/tar.d.ts +3 -1
- package/dist/tar.js +140 -14
- package/dist/transport.d.ts +4 -7
- package/dist/transport.js +205 -122
- package/dist/vercel/command.d.ts +1 -1
- package/dist/vercel/command.js +3 -2
- package/dist/vercel/errors.d.ts +1 -1
- package/dist/vercel/errors.js +7 -1
- package/dist/vercel/sandbox.d.ts +8 -8
- package/dist/vercel/sandbox.js +154 -59
- package/dist/vercel/snapshot.d.ts +3 -2
- package/dist/vercel/snapshot.js +9 -5
- package/package.json +2 -1
|
@@ -8,30 +8,58 @@ export function unwrapListData(response) {
|
|
|
8
8
|
return response.data ?? [];
|
|
9
9
|
}
|
|
10
10
|
/** Blaxel's own helper, for a list fetched by `fetchPage`. */
|
|
11
|
-
export async function createPaginatedList({ response, fetchPage, mapItem, query, }) {
|
|
12
|
-
const meta = !response || Array.isArray(response)
|
|
11
|
+
export async function createPaginatedList({ response, fetchPage, mapItem, query, seenCursors, }) {
|
|
12
|
+
const meta = !response || Array.isArray(response)
|
|
13
|
+
? { hasMore: false }
|
|
14
|
+
: (response.meta ?? { hasMore: false });
|
|
13
15
|
const data = await Promise.all(unwrapListData(response).map(mapItem));
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
const cursors = new Set(seenCursors);
|
|
17
|
+
if (query?.cursor)
|
|
18
|
+
cursors.add(query.cursor);
|
|
19
|
+
return build(data, meta.nextCursor || undefined, async (cursor) => {
|
|
20
|
+
if (cursors.has(cursor))
|
|
21
|
+
throw new Error("Pagination returned a repeated cursor");
|
|
22
|
+
const nextQuery = { ...(query ?? {}), cursor };
|
|
23
|
+
const nextSeenCursors = new Set(cursors);
|
|
24
|
+
nextSeenCursors.add(cursor);
|
|
25
|
+
return createPaginatedList({
|
|
26
|
+
response: await fetchPage(nextQuery),
|
|
27
|
+
fetchPage,
|
|
28
|
+
mapItem,
|
|
29
|
+
query: nextQuery,
|
|
30
|
+
seenCursors: nextSeenCursors,
|
|
31
|
+
});
|
|
32
|
+
}, meta);
|
|
20
33
|
}
|
|
21
34
|
/** A Runtime page, each item mapped, as Blaxel's paginated list. */
|
|
22
|
-
export function paginate(page, map) {
|
|
23
|
-
return build(page.data.map(map), page.nextCursor ?? undefined, async () => {
|
|
24
|
-
|
|
25
|
-
|
|
35
|
+
export function paginate(page, map, seenCursors = new Set()) {
|
|
36
|
+
return build(page.data.map(map), page.nextCursor ?? undefined, async (cursor) => {
|
|
37
|
+
if (cursor && seenCursors.has(cursor))
|
|
38
|
+
throw new Error("Pagination returned a repeated cursor");
|
|
39
|
+
const nextSeenCursors = new Set(seenCursors);
|
|
40
|
+
if (cursor)
|
|
41
|
+
nextSeenCursors.add(cursor);
|
|
42
|
+
const next = await guard(() => page.next(cursor));
|
|
43
|
+
return next ? paginate(next, map, nextSeenCursors) : null;
|
|
26
44
|
});
|
|
27
45
|
}
|
|
28
|
-
function build(data, nextCursor, more
|
|
46
|
+
function build(data, nextCursor, more, meta = {
|
|
47
|
+
hasMore: nextCursor !== undefined,
|
|
48
|
+
...(nextCursor ? { nextCursor } : {}),
|
|
49
|
+
}) {
|
|
29
50
|
const list = {
|
|
30
51
|
data,
|
|
31
|
-
meta
|
|
32
|
-
hasMore
|
|
33
|
-
|
|
34
|
-
|
|
52
|
+
meta,
|
|
53
|
+
get hasMore() {
|
|
54
|
+
return Boolean(list.nextCursor);
|
|
55
|
+
},
|
|
56
|
+
get nextCursor() {
|
|
57
|
+
return meta.nextCursor || undefined;
|
|
58
|
+
},
|
|
59
|
+
nextPage: async () => {
|
|
60
|
+
const cursor = list.nextCursor;
|
|
61
|
+
return cursor ? more(cursor) : null;
|
|
62
|
+
},
|
|
35
63
|
async autoPagingEach(onItem) {
|
|
36
64
|
for await (const item of list)
|
|
37
65
|
if ((await onItem(item)) === false)
|
package/dist/blaxel/preview.d.ts
CHANGED
|
@@ -67,7 +67,8 @@ export declare class SandboxPreviewTokens {
|
|
|
67
67
|
constructor(preview: SandboxPreview, ctx: PreviewContext);
|
|
68
68
|
get previewName(): string;
|
|
69
69
|
get resourceName(): string;
|
|
70
|
-
/** A token
|
|
70
|
+
/** A token bounded by `expiresAt`; native signing floors milliseconds and
|
|
71
|
+
* caps it to the caller's session. Older servers refuse without a TTL fallback. */
|
|
71
72
|
create(expiresAt: Date): Promise<SandboxPreviewToken>;
|
|
72
73
|
list(): Promise<never>;
|
|
73
74
|
/** Revokes the preview's tokens. Runtime cannot revoke one token alone, so
|
package/dist/blaxel/preview.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { guard, isNotFound, NotSupportedError, responseError } from "./errors.js";
|
|
2
2
|
import { PREVIEW } from "./lookup.js";
|
|
3
|
-
/** Runtime's shortest and longest token, in seconds. */
|
|
4
|
-
const TOKEN_MIN = 60;
|
|
5
|
-
const TOKEN_MAX = 7 * 86_400;
|
|
6
3
|
function checkName(name) {
|
|
7
4
|
if (!/^[A-Za-z0-9._-]{1,48}$/.test(name))
|
|
8
5
|
throw new NotSupportedError(`The preview name "${name}"`, "Use 1 to 48 letters, digits, dots, dashes or underscores.");
|
|
@@ -56,19 +53,24 @@ export class SandboxPreviewTokens {
|
|
|
56
53
|
get resourceName() {
|
|
57
54
|
return this.#ctx.sandboxName;
|
|
58
55
|
}
|
|
59
|
-
/** A token
|
|
56
|
+
/** A token bounded by `expiresAt`; native signing floors milliseconds and
|
|
57
|
+
* caps it to the caller's session. Older servers refuse without a TTL fallback. */
|
|
60
58
|
async create(expiresAt) {
|
|
61
|
-
const
|
|
62
|
-
if (
|
|
63
|
-
throw
|
|
64
|
-
const
|
|
65
|
-
|
|
59
|
+
const requested = expiresAt.getTime();
|
|
60
|
+
if (!Number.isFinite(requested))
|
|
61
|
+
throw responseError(400, "A preview token needs a valid expiration date.");
|
|
62
|
+
const remaining = requested - Date.now();
|
|
63
|
+
if (remaining < 60_000 || remaining > 7 * 86_400_000)
|
|
64
|
+
throw new NotSupportedError("A preview token lasting less than a minute or more than a week", "Request an expiration at least one minute and at most seven days away.");
|
|
65
|
+
const got = await this.#ctx.run((runtime) => runtime.previews.get(this.#preview.spec.port, undefined, {
|
|
66
|
+
expiresAt: expiresAt.toISOString(),
|
|
67
|
+
}));
|
|
68
|
+
const actual = got.tokenExpiresAt === null ? Number.NaN : Date.parse(got.tokenExpiresAt);
|
|
69
|
+
if (!got.token || !Number.isFinite(actual) || actual > requested)
|
|
70
|
+
throw new NotSupportedError("A preview token with the requested expiration", "The server must issue a token that never outlives the requested expiration; no duration-based fallback was requested.");
|
|
66
71
|
return new SandboxPreviewToken({
|
|
67
72
|
metadata: { name: `token-${Date.now()}`, previewName: this.previewName },
|
|
68
|
-
spec: {
|
|
69
|
-
...(got.token ? { token: got.token } : {}),
|
|
70
|
-
...(got.tokenExpiresAt ? { expiresAt: got.tokenExpiresAt } : {}),
|
|
71
|
-
},
|
|
73
|
+
spec: { token: got.token, expiresAt: got.tokenExpiresAt },
|
|
72
74
|
});
|
|
73
75
|
}
|
|
74
76
|
list() {
|
package/dist/blaxel/process.js
CHANGED
|
@@ -152,14 +152,17 @@ export class SandboxProcess {
|
|
|
152
152
|
}
|
|
153
153
|
/** A call that found the sandbox paused (autoWake off) is made again once,
|
|
154
154
|
* after a wake: it never started, so it cannot run twice. */
|
|
155
|
-
async #retryPaused(work) {
|
|
155
|
+
async #retryPaused(work, signal) {
|
|
156
|
+
signal?.throwIfAborted();
|
|
156
157
|
try {
|
|
157
158
|
return await work();
|
|
158
159
|
}
|
|
159
160
|
catch (error) {
|
|
161
|
+
signal?.throwIfAborted();
|
|
160
162
|
if (codeOf(error) !== "sandbox_paused")
|
|
161
163
|
throw translate(error);
|
|
162
164
|
await this.#ctx.wake();
|
|
165
|
+
signal?.throwIfAborted();
|
|
163
166
|
return guard(work);
|
|
164
167
|
}
|
|
165
168
|
}
|
|
@@ -281,16 +284,20 @@ export class SandboxProcess {
|
|
|
281
284
|
};
|
|
282
285
|
}
|
|
283
286
|
/** A process by pid or name: the registry first, then Runtime's list. */
|
|
284
|
-
async #find(identifier) {
|
|
287
|
+
async #find(identifier, signal) {
|
|
288
|
+
signal?.throwIfAborted();
|
|
285
289
|
const runtime = await this.#ctx.live();
|
|
290
|
+
signal?.throwIfAborted();
|
|
286
291
|
const registry = this.#registry;
|
|
287
292
|
const known = registry.byName.get(identifier) ?? (registry.meta.has(identifier) ? identifier : undefined);
|
|
288
293
|
if (known !== undefined) {
|
|
289
|
-
const process = await this.#retryPaused(() => runtime.processes.get(known));
|
|
294
|
+
const process = await this.#retryPaused(() => runtime.processes.get(known, { signal }), signal);
|
|
295
|
+
signal?.throwIfAborted();
|
|
290
296
|
registry.processes.set(known, process);
|
|
291
297
|
return { id: known, info: process.info, process };
|
|
292
298
|
}
|
|
293
|
-
const all = await this.#retryPaused(() => runtime.processes.list());
|
|
299
|
+
const all = await this.#retryPaused(() => runtime.processes.list({ signal }), signal);
|
|
300
|
+
signal?.throwIfAborted();
|
|
294
301
|
let found;
|
|
295
302
|
for (const info of all) {
|
|
296
303
|
const parsed = parseProcessLine(info.command);
|
|
@@ -323,7 +330,9 @@ export class SandboxProcess {
|
|
|
323
330
|
const running = found.info.state === "running";
|
|
324
331
|
if (running && !untilExit && found.info.outputBytes <= found.info.firstOffset)
|
|
325
332
|
return out;
|
|
333
|
+
signal?.throwIfAborted();
|
|
326
334
|
const runtime = await this.#ctx.live();
|
|
335
|
+
signal?.throwIfAborted();
|
|
327
336
|
const abort = new AbortController();
|
|
328
337
|
const forward = () => abort.abort();
|
|
329
338
|
signal?.throwIfAborted();
|
|
@@ -369,9 +378,30 @@ export class SandboxProcess {
|
|
|
369
378
|
/** The process, with the output it has written so far. */
|
|
370
379
|
async get(identifier, options = {}) {
|
|
371
380
|
options.signal?.throwIfAborted();
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
381
|
+
const signal = options.signal;
|
|
382
|
+
let onAbort;
|
|
383
|
+
const interrupted = new Promise((_, reject) => {
|
|
384
|
+
onAbort = () => reject(new Error("Process read was interrupted.", { cause: signal?.reason }));
|
|
385
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
386
|
+
});
|
|
387
|
+
try {
|
|
388
|
+
const reading = (async () => {
|
|
389
|
+
const found = await this.#find(identifier, signal);
|
|
390
|
+
const out = await this.#output(found, false, signal);
|
|
391
|
+
signal?.throwIfAborted();
|
|
392
|
+
return this.#settled(this.#response(found.info, this.#registry.meta.get(found.id), out));
|
|
393
|
+
})();
|
|
394
|
+
return await Promise.race([reading, interrupted]);
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
if (signal?.aborted)
|
|
398
|
+
throw signal.reason ?? error;
|
|
399
|
+
throw error;
|
|
400
|
+
}
|
|
401
|
+
finally {
|
|
402
|
+
if (onAbort)
|
|
403
|
+
signal?.removeEventListener("abort", onAbort);
|
|
404
|
+
}
|
|
375
405
|
}
|
|
376
406
|
/** Waits for the process to end (at most `maxWait` ms, -1 for no limit) and
|
|
377
407
|
* answers it with its output. The process is never stopped by the wait. */
|
|
@@ -390,10 +420,24 @@ export class SandboxProcess {
|
|
|
390
420
|
signal?.throwIfAborted();
|
|
391
421
|
signal?.addEventListener("abort", forward, { once: true });
|
|
392
422
|
const timer = maxWait === -1 ? undefined : setTimeout(() => abort.abort(late()), maxWait);
|
|
423
|
+
let onAbort;
|
|
424
|
+
const interrupted = new Promise((_, reject) => {
|
|
425
|
+
onAbort = () => {
|
|
426
|
+
const reason = abort.signal.reason;
|
|
427
|
+
reject(reason instanceof Error
|
|
428
|
+
? reason
|
|
429
|
+
: new Error("Process wait was interrupted.", { cause: reason }));
|
|
430
|
+
};
|
|
431
|
+
abort.signal.addEventListener("abort", onAbort, { once: true });
|
|
432
|
+
});
|
|
393
433
|
try {
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
434
|
+
const result = (async () => {
|
|
435
|
+
const found = await this.#find(identifier, abort.signal);
|
|
436
|
+
const out = await this.#output(found, true, abort.signal);
|
|
437
|
+
abort.signal.throwIfAborted();
|
|
438
|
+
return await this.#settled(this.#response(found.info, this.#registry.meta.get(found.id), out));
|
|
439
|
+
})();
|
|
440
|
+
return await Promise.race([result, interrupted]);
|
|
397
441
|
}
|
|
398
442
|
catch (error) {
|
|
399
443
|
if (abort.signal.aborted)
|
|
@@ -402,6 +446,8 @@ export class SandboxProcess {
|
|
|
402
446
|
}
|
|
403
447
|
finally {
|
|
404
448
|
clearTimeout(timer);
|
|
449
|
+
if (onAbort)
|
|
450
|
+
abort.signal.removeEventListener("abort", onAbort);
|
|
405
451
|
signal?.removeEventListener("abort", forward);
|
|
406
452
|
}
|
|
407
453
|
}
|
package/dist/blaxel/sandbox.d.ts
CHANGED
|
@@ -32,6 +32,8 @@ export type SandboxForkOptions = {
|
|
|
32
32
|
/** A snapshot's id or, of this sandbox's snapshots, its name. */
|
|
33
33
|
snapshotId?: string;
|
|
34
34
|
envs?: Env[];
|
|
35
|
+
/** Blaxel's fork override; Runtime cannot enforce this override during a fork. */
|
|
36
|
+
lifecycle?: SandboxLifecycle;
|
|
35
37
|
};
|
|
36
38
|
export type SandboxArchiveOptions = {
|
|
37
39
|
wait?: boolean;
|
package/dist/blaxel/sandbox.js
CHANGED
|
@@ -750,6 +750,8 @@ export class SandboxInstance {
|
|
|
750
750
|
* (files, memory and running processes), or started from a snapshot with
|
|
751
751
|
* `snapshotId`. `envs` are added over this sandbox's. */
|
|
752
752
|
async fork(targetName, options = {}) {
|
|
753
|
+
if (options.lifecycle !== undefined)
|
|
754
|
+
throw new NotSupportedError("Overriding lifecycle during a fork", "Omit lifecycle to use the existing Runtime fork behavior.");
|
|
753
755
|
refuseApplication(options);
|
|
754
756
|
const envs = envRecord(options.envs);
|
|
755
757
|
checkEnvNames(Object.keys(envs));
|
package/dist/blaxel/snapshot.js
CHANGED
|
@@ -22,13 +22,11 @@ export function snapshotModel(snapshot, sandboxName) {
|
|
|
22
22
|
}
|
|
23
23
|
/** Envs as a record; a nameless one is refused. */
|
|
24
24
|
export function envRecord(envs) {
|
|
25
|
-
|
|
26
|
-
for (const env of envs ?? []) {
|
|
25
|
+
return Object.fromEntries((envs ?? []).map((env) => {
|
|
27
26
|
if (!env.name)
|
|
28
27
|
throw responseError(400, "every env needs a name");
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
return out;
|
|
28
|
+
return [env.name, env.value ?? ""];
|
|
29
|
+
}));
|
|
32
30
|
}
|
|
33
31
|
/** Appends envs to a sandbox's env file: they win over the ones it has. */
|
|
34
32
|
export async function appendEnvs(runtime, envs) {
|
package/dist/cli.d.ts
CHANGED