withruntime 0.11.1 → 0.12.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 +3 -3
- package/COMPATIBILITY.md +74 -9
- package/DAYTONA.md +33 -5
- package/E2B.md +137 -48
- package/README.md +11 -9
- package/README.src.md +11 -9
- package/VERCEL.md +43 -5
- package/compatibility-lock.json +58 -49
- package/dist/billing-cli.d.ts +1 -1
- package/dist/billing-cli.js +77 -1
- package/dist/blaxel/errors.d.ts +14 -0
- package/dist/blaxel/errors.js +33 -4
- package/dist/blaxel/index.d.ts +2 -2
- package/dist/blaxel/index.js +4 -4
- package/dist/blaxel/interpreter.d.ts +2 -5
- package/dist/blaxel/sandbox.d.ts +15 -7
- package/dist/blaxel/sandbox.js +64 -9
- package/dist/blaxel/snapshot.d.ts +4 -0
- package/dist/blaxel/snapshot.js +4 -0
- package/dist/cli/logs.d.ts +21 -0
- package/dist/cli/logs.js +188 -0
- package/dist/cli/logs.test.d.ts +1 -0
- package/dist/cli/logs.test.js +272 -0
- package/dist/cli-extras.d.ts +3 -3
- package/dist/cli-extras.js +17 -12
- package/dist/cli-words.d.ts +10 -1
- package/dist/cli-words.js +18 -0
- package/dist/cli.d.ts +6 -2
- package/dist/cli.js +468 -219
- package/dist/client.d.ts +13 -63
- package/dist/client.js +35 -124
- package/dist/cloudflare/errors.d.ts +143 -0
- package/dist/cloudflare/errors.js +167 -0
- package/dist/cloudflare/index.d.ts +175 -6
- package/dist/cloudflare/index.js +416 -26
- package/dist/cloudflare/interpreter.d.ts +119 -0
- package/dist/cloudflare/interpreter.js +223 -0
- package/dist/cloudflare/streams.d.ts +38 -0
- package/dist/cloudflare/streams.js +145 -0
- package/dist/cloudflare/watch.d.ts +9 -0
- package/dist/cloudflare/watch.js +24 -0
- package/dist/codesandbox/index.d.ts +216 -28
- package/dist/codesandbox/index.js +478 -78
- package/dist/compat/core.d.ts +18 -0
- package/dist/compat/core.js +29 -0
- package/dist/compat/files.d.ts +1 -0
- package/dist/compat/files.js +25 -5
- package/dist/daytona/computer-use.d.ts +13 -4
- package/dist/daytona/computer-use.js +66 -8
- package/dist/daytona/daytona.d.ts +7 -2
- package/dist/daytona/daytona.js +31 -5
- package/dist/daytona/errors.d.ts +11 -0
- package/dist/daytona/errors.js +18 -0
- package/dist/daytona/filesystem.js +2 -1
- package/dist/daytona/image.d.ts +13 -9
- package/dist/daytona/image.js +18 -12
- package/dist/daytona/index.d.ts +1 -1
- package/dist/daytona/sandbox.d.ts +9 -3
- package/dist/daytona/sandbox.js +17 -3
- package/dist/domains/index.d.ts +9 -0
- package/dist/domains/index.js +10 -0
- package/dist/e2b/client.d.ts +15 -2
- package/dist/e2b/client.js +61 -7
- package/dist/e2b/commands.d.ts +4 -1
- package/dist/e2b/commands.js +17 -7
- package/dist/e2b/errors.d.ts +43 -2
- package/dist/e2b/errors.js +78 -6
- package/dist/e2b/filesystem.d.ts +1 -1
- package/dist/e2b/filesystem.js +82 -16
- package/dist/e2b/index.d.ts +3 -3
- package/dist/e2b/index.js +2 -2
- package/dist/e2b/sandbox.d.ts +50 -35
- package/dist/e2b/sandbox.js +228 -185
- package/dist/e2b/unsupported.d.ts +19 -0
- package/dist/e2b/unsupported.js +22 -1
- package/dist/e2b/users.d.ts +9 -0
- package/dist/e2b/users.js +22 -0
- package/dist/errors.d.ts +14 -5
- package/dist/errors.js +28 -14
- package/dist/freestyle/index.d.ts +285 -37
- package/dist/freestyle/index.js +656 -144
- package/dist/identity.d.ts +3 -0
- package/dist/identity.js +27 -0
- package/dist/images/index.d.ts +9 -0
- package/dist/images/index.js +10 -0
- package/dist/index.d.ts +1 -0
- package/dist/ips/index.d.ts +9 -0
- package/dist/ips/index.js +10 -0
- package/dist/jobs/index.d.ts +9 -0
- package/dist/jobs/index.js +10 -0
- package/dist/keys.js +2 -0
- package/dist/login.js +2 -0
- package/dist/machine.d.ts +189 -0
- package/dist/machine.js +521 -0
- package/dist/mcp.js +1 -1
- package/dist/modal/index.d.ts +339 -61
- package/dist/modal/index.js +781 -121
- package/dist/network-cli.d.ts +3 -1
- package/dist/network-cli.js +78 -36
- package/dist/networks/index.d.ts +11 -0
- package/dist/networks/index.js +10 -0
- package/dist/openai-agents/index.d.ts +1 -1
- package/dist/openai-agents/index.js +1 -1
- package/dist/ports/index.d.ts +9 -0
- package/dist/ports/index.js +10 -0
- package/dist/product-names.d.ts +52 -0
- package/dist/product-names.js +164 -0
- package/dist/product-renames.d.ts +103 -0
- package/dist/product-renames.js +189 -0
- package/dist/products/apps.d.ts +176 -0
- package/dist/products/apps.js +165 -0
- package/dist/products/billing.d.ts +41 -0
- package/dist/products/billing.js +13 -0
- package/dist/products/browser.d.ts +10 -0
- package/dist/products/browsers.d.ts +71 -0
- package/dist/products/browsers.js +102 -0
- package/dist/products/builds.d.ts +126 -0
- package/dist/products/builds.js +221 -0
- package/dist/products/index.d.ts +3 -16
- package/dist/products/index.js +3 -14
- package/dist/products/jobs.d.ts +19 -3
- package/dist/products/jobs.js +28 -12
- package/dist/products/limits.d.ts +4 -10
- package/dist/products/logs.d.ts +245 -0
- package/dist/products/logs.js +274 -0
- package/dist/products/logs.test.d.ts +1 -0
- package/dist/products/logs.test.js +343 -0
- package/dist/products/network-products.d.ts +38 -7
- package/dist/products/network-products.js +31 -18
- package/dist/products/network.d.ts +1 -1
- package/dist/products/observability.d.ts +3 -2
- package/dist/products/private-network.js +1 -1
- package/dist/products/queues.d.ts +149 -0
- package/dist/products/queues.js +213 -0
- package/dist/products/referrals.d.ts +21 -7
- package/dist/products/referrals.js +3 -3
- package/dist/products/secrets.d.ts +8 -4
- package/dist/products/secrets.js +39 -18
- package/dist/products/signed-requests.d.ts +21 -0
- package/dist/products/signed-requests.js +11 -0
- package/dist/products/streams.d.ts +73 -0
- package/dist/products/streams.js +17 -0
- package/dist/products/switching.d.ts +37 -10
- package/dist/products/switching.js +4 -3
- package/dist/products/volumes.d.ts +3 -0
- package/dist/record-cli.d.ts +23 -0
- package/dist/record-cli.js +308 -0
- package/dist/renamed-products.d.ts +38 -0
- package/dist/renamed-products.js +34 -0
- package/dist/runloop/index.d.ts +9 -2
- package/dist/runloop/index.js +29 -8
- package/dist/runloop/sdk.d.ts +131 -0
- package/dist/runloop/sdk.js +167 -0
- package/dist/sandbox.d.ts +10 -94
- package/dist/sandbox.js +22 -371
- package/dist/sandboxes/index.d.ts +11 -0
- package/dist/sandboxes/index.js +12 -0
- package/dist/snapshots/index.d.ts +9 -0
- package/dist/snapshots/index.js +11 -0
- package/dist/snapshots.d.ts +7 -4
- package/dist/sprites/index.d.ts +343 -31
- package/dist/sprites/index.js +866 -84
- package/dist/ssh.d.ts +1 -1
- package/dist/ssh.js +11 -11
- package/dist/terminal.d.ts +24 -0
- package/dist/terminal.js +32 -0
- package/dist/transport.d.ts +18 -2
- package/dist/transport.js +61 -6
- package/dist/tunnel.js +1 -1
- package/dist/types.d.ts +116 -8
- package/dist/vercel/sandbox.d.ts +1 -2
- package/dist/vercel/sandbox.js +6 -5
- package/dist/vercel/snapshot.js +10 -2
- package/dist/volumes/index.d.ts +9 -0
- package/dist/volumes/index.js +10 -0
- package/package.json +48 -2
package/dist/e2b/sandbox.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { clientFor, request, } from "./client.js";
|
|
1
|
+
import { clientFor, keySource, request, } from "./client.js";
|
|
2
2
|
import { Commands } from "./commands.js";
|
|
3
3
|
import { guard, InvalidArgumentError, NotSupportedError, PublicPreviewNotAllowedError, SandboxError, SandboxNotFoundError, TemplateError, } from "./errors.js";
|
|
4
4
|
import { Filesystem } from "./filesystem.js";
|
|
@@ -9,18 +9,26 @@ import { Pty } from "./pty.js";
|
|
|
9
9
|
/** E2B's default machine: 2 vCPU and 512 MiB (docs.e2b.dev/billing, checked
|
|
10
10
|
* 23 September 2026). Pass `runtime: { create: { memoryMiB } }` for another. */
|
|
11
11
|
export const DEFAULT_SHAPE = { vcpu: 2, memoryMiB: 512 };
|
|
12
|
-
/** E2B's default sandbox timeout, 300 s.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
/** E2B's default sandbox timeout, 300 s. A sandbox created without timeoutMs
|
|
13
|
+
* gets it, and is paused when it runs out rather than killed, since nobody
|
|
14
|
+
* asked for it to end: nothing in it is lost, and the next call wakes it. On
|
|
15
|
+
* a pilot account the server keeps every sandbox running instead (0254). */
|
|
15
16
|
export const DEFAULT_TIMEOUT_MS = 300_000;
|
|
16
|
-
/** Runtime's
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
/** Runtime's time limit bounds (MIN_TIMEOUT_SECONDS and MAX_TIMEOUT_SECONDS
|
|
18
|
+
* in the API): a minute to 24 hours, E2B's longest (its Pro plan). The server
|
|
19
|
+
* keeps the deadline: nothing in this process has to stay running for it. */
|
|
20
|
+
const MIN_TIMEOUT_SECONDS = 60;
|
|
20
21
|
const LONGEST_TIMEOUT_MS = 86_400_000;
|
|
22
|
+
/** Makes E2B's home lead to Runtime's unless the image has its own, and
|
|
23
|
+
* prints "same" when /home/user is then /workspace. */
|
|
24
|
+
const HOME_LINK = "[ -e /home/user ] || sudo ln -s /workspace /home/user || exit 1; if [ /home/user -ef /workspace ]; then echo same; fi";
|
|
25
|
+
/** The label that keeps the template a sandbox was made from, so getInfo
|
|
26
|
+
* and list name it whichever client asks; it is not shown in `metadata`. */
|
|
27
|
+
export const TEMPLATE_LABEL = "e2b-template";
|
|
21
28
|
/** E2B template names that mean "the stock environment": Runtime's default image. */
|
|
22
29
|
const STOCK_TEMPLATES = new Set(["base", "code-interpreter-v1", "code-interpreter"]);
|
|
23
30
|
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
31
|
+
const SNAPSHOT_MODES = ["full", "filesystem"];
|
|
24
32
|
let warnedShortLease = false;
|
|
25
33
|
/** E2B's own bound since 2.52.0: refused before anything happens. */
|
|
26
34
|
const MAX_FORK_COUNT = 20;
|
|
@@ -28,17 +36,17 @@ function checkForkCount(count) {
|
|
|
28
36
|
if (count !== undefined && (!Number.isInteger(count) || count < 1 || count > MAX_FORK_COUNT))
|
|
29
37
|
throw new InvalidArgumentError(`count must be an integer between 1 and ${MAX_FORK_COUNT}`);
|
|
30
38
|
}
|
|
31
|
-
/**
|
|
32
|
-
*
|
|
33
|
-
function
|
|
39
|
+
/** Runtime's time limit for `timeoutMs`, in whole seconds: at least a
|
|
40
|
+
* minute (with a warning once), at most 24 hours. */
|
|
41
|
+
function timeoutSeconds(timeoutMs) {
|
|
34
42
|
checkTimeout(timeoutMs);
|
|
35
|
-
const seconds = Math.
|
|
36
|
-
if (seconds <
|
|
43
|
+
const seconds = Math.ceil(timeoutMs / 1000);
|
|
44
|
+
if (seconds < MIN_TIMEOUT_SECONDS) {
|
|
37
45
|
if (!warnedShortLease && typeof process !== "undefined") {
|
|
38
46
|
warnedShortLease = true;
|
|
39
|
-
process.emitWarning(`timeoutMs ${timeoutMs} is under Runtime's shortest
|
|
47
|
+
process.emitWarning(`timeoutMs ${timeoutMs} is under Runtime's shortest time limit; the sandbox gets 60 s. Call kill() when done.`, { code: "RUNTIME_E2B_SHORT_TIMEOUT" });
|
|
40
48
|
}
|
|
41
|
-
return
|
|
49
|
+
return MIN_TIMEOUT_SECONDS;
|
|
42
50
|
}
|
|
43
51
|
return seconds;
|
|
44
52
|
}
|
|
@@ -48,89 +56,37 @@ function checkTimeout(timeoutMs) {
|
|
|
48
56
|
if (timeoutMs > LONGEST_TIMEOUT_MS)
|
|
49
57
|
throw new InvalidArgumentError(`timeoutMs is at most 24 hours (86_400_000 ms), as on E2B, not ${timeoutMs}.`);
|
|
50
58
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const KEEP_EVERY_MS = 5 * 60_000;
|
|
58
|
-
/** Kept under the API's hour, for the clocks and the trip. */
|
|
59
|
-
const KEEP_AHEAD_MS = (MAX_LEASE_SECONDS - 30) * 1000;
|
|
60
|
-
const keepers = new Map();
|
|
61
|
-
let warnedKeeper = false;
|
|
62
|
-
/** Keeps `runtime` until `until` (epoch ms), or stops keeping it when that
|
|
63
|
-
* is within the hour its lease can already reach. */
|
|
64
|
-
function keepUntil(runtime, until) {
|
|
65
|
-
const keeper = keepers.get(runtime.id);
|
|
66
|
-
if (until - Date.now() <= MAX_LEASE_SECONDS * 1000)
|
|
67
|
-
return stopKeeping(runtime.id);
|
|
68
|
-
if (keeper) {
|
|
69
|
-
keeper.until = until;
|
|
70
|
-
keeper.runtime = runtime;
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
const made = { until, runtime };
|
|
74
|
-
keepers.set(runtime.id, made);
|
|
75
|
-
schedule(made);
|
|
76
|
-
}
|
|
77
|
-
function stopKeeping(sandboxId) {
|
|
78
|
-
const keeper = keepers.get(sandboxId);
|
|
79
|
-
if (keeper?.timer !== undefined)
|
|
80
|
-
clearTimeout(keeper.timer);
|
|
81
|
-
keepers.delete(sandboxId);
|
|
82
|
-
}
|
|
83
|
-
function schedule(keeper) {
|
|
84
|
-
keeper.timer = setTimeout(() => void renew(keeper), KEEP_EVERY_MS);
|
|
85
|
-
// Never what keeps a Node or Bun process running.
|
|
86
|
-
keeper.timer.unref?.();
|
|
87
|
-
}
|
|
88
|
-
async function renew(keeper) {
|
|
89
|
-
const id = keeper.runtime.id;
|
|
90
|
-
if (keepers.get(id) !== keeper)
|
|
91
|
-
return;
|
|
92
|
-
try {
|
|
93
|
-
await keeper.runtime.refresh();
|
|
94
|
-
const current = state(keeper.runtime);
|
|
95
|
-
if (current === "stopped")
|
|
96
|
-
return stopKeeping(id);
|
|
97
|
-
if (current === "running") {
|
|
98
|
-
if (keeper.runtime.info.endsAt === null)
|
|
99
|
-
return stopKeeping(id);
|
|
100
|
-
const end = Date.parse(keeper.runtime.info.expiresAt);
|
|
101
|
-
const want = Math.min(keeper.until, Date.now() + KEEP_AHEAD_MS);
|
|
102
|
-
const later = Math.floor((want - end) / 1000);
|
|
103
|
-
if (later >= 1)
|
|
104
|
-
await keeper.runtime.extend(later);
|
|
105
|
-
if (Date.parse(keeper.runtime.info.expiresAt) >= keeper.until - 1000)
|
|
106
|
-
return stopKeeping(id);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
catch (error) {
|
|
110
|
-
if (!warnedKeeper && typeof process !== "undefined" && process.emitWarning) {
|
|
111
|
-
warnedKeeper = true;
|
|
112
|
-
process.emitWarning(`Could not extend sandbox ${id}'s lease toward its timeout: ${error instanceof Error ? error.message : String(error)}. Trying again in five minutes.`, { code: "RUNTIME_E2B_LEASE" });
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
if (keepers.get(id) === keeper)
|
|
116
|
-
schedule(keeper);
|
|
117
|
-
}
|
|
118
|
-
/** Test hook: renew every kept lease now, as the five-minute timer would. */
|
|
119
|
-
export async function renewLeases() {
|
|
120
|
-
await Promise.all([...keepers.values()].map((keeper) => renew(keeper)));
|
|
121
|
-
}
|
|
122
|
-
/** Test hook: the end each kept sandbox is kept until. */
|
|
123
|
-
export function keptLeases() {
|
|
124
|
-
return new Map([...keepers].map(([id, keeper]) => [id, keeper.until]));
|
|
125
|
-
}
|
|
126
|
-
function onLeaseEnd(lifecycle) {
|
|
59
|
+
/** What the server does when the time limit runs out. E2B kills: a caller
|
|
60
|
+
* that asked for an end (a timeoutMs, or onTimeout "kill") gets a delete, as
|
|
61
|
+
* E2B's kill destroys the sandbox. One that asked for nothing gets E2B's
|
|
62
|
+
* 300 s default as a pause, which keeps everything, since a sandbox nobody
|
|
63
|
+
* asked to end is never ended. */
|
|
64
|
+
function onTimeout(lifecycle, timeoutAsked) {
|
|
127
65
|
if (!lifecycle)
|
|
128
|
-
return "
|
|
66
|
+
return timeoutAsked ? "delete" : "pause";
|
|
129
67
|
const action = typeof lifecycle.onTimeout === "string" ? lifecycle.onTimeout : lifecycle.onTimeout.action;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
68
|
+
// As E2B 2.55 checks: mode (or keepMemory) belongs to a pause alone.
|
|
69
|
+
const pauseOpts = (typeof lifecycle.onTimeout === "string" ? {} : lifecycle.onTimeout);
|
|
70
|
+
if (("mode" in pauseOpts || "keepMemory" in pauseOpts) && action !== "pause")
|
|
71
|
+
throw new InvalidArgumentError("onTimeout.mode is only allowed when action is 'pause'.");
|
|
72
|
+
if (snapshotMemory("onTimeout.mode", pauseOpts.mode, pauseOpts.keepMemory) === false)
|
|
73
|
+
throw filesOnlyPause();
|
|
74
|
+
return action === "pause" ? "pause" : "delete";
|
|
75
|
+
}
|
|
76
|
+
/** E2B 2.55's reading of `mode`, or the deprecated `keepMemory`: whether
|
|
77
|
+
* memory is kept, or undefined when neither was given. Both at once, or a
|
|
78
|
+
* mode E2B does not know, is E2B's InvalidArgumentError. */
|
|
79
|
+
function snapshotMemory(field, mode, keepMemory) {
|
|
80
|
+
if (mode != null && keepMemory != null)
|
|
81
|
+
throw new InvalidArgumentError(`Pass either ${field} or the deprecated keepMemory, not both.`);
|
|
82
|
+
if (mode == null)
|
|
83
|
+
return keepMemory ?? undefined;
|
|
84
|
+
if (!SNAPSHOT_MODES.includes(mode))
|
|
85
|
+
throw new InvalidArgumentError(`${field} must be one of: ${SNAPSHOT_MODES.join(", ")} (got ${JSON.stringify(mode)}).`);
|
|
86
|
+
return mode === "full";
|
|
133
87
|
}
|
|
88
|
+
const filesOnlyPause = () => new NotSupportedError("A files-only pause (mode: 'filesystem', or keepMemory: false)", "Runtime's pause keeps memory and files; leave mode out or pass mode: 'full'. " +
|
|
89
|
+
"For a copy of the files alone, use createSnapshot({ mode: 'filesystem' }).");
|
|
134
90
|
function refuseCreate(opts) {
|
|
135
91
|
const refusals = [
|
|
136
92
|
[
|
|
@@ -154,8 +110,16 @@ function refuseCreate(opts) {
|
|
|
154
110
|
if (opts[field] !== undefined)
|
|
155
111
|
throw new NotSupportedError(feature, alternative);
|
|
156
112
|
}
|
|
113
|
+
/** The Runtime image name an E2B template name stands for: E2B's
|
|
114
|
+
* `team/name:tag` without the team, since a Runtime account's images are its
|
|
115
|
+
* own. `name`, `name:tag` and `name@version` are Runtime image names too. */
|
|
116
|
+
export function imageNameFor(template) {
|
|
117
|
+
const slash = template.lastIndexOf("/");
|
|
118
|
+
return slash < 0 ? template : template.slice(slash + 1);
|
|
119
|
+
}
|
|
157
120
|
/** Where a template sends the create: the stock image, a Runtime image by
|
|
158
|
-
* name or id, or a Runtime snapshot by id.
|
|
121
|
+
* name (with its tag or version) or id, or a Runtime snapshot by id. A name
|
|
122
|
+
* is resolved by the create itself, in the same request. */
|
|
159
123
|
async function resolveTemplate(client, template, options) {
|
|
160
124
|
options.signal?.throwIfAborted();
|
|
161
125
|
if (STOCK_TEMPLATES.has(template))
|
|
@@ -171,16 +135,36 @@ async function resolveTemplate(client, template, options) {
|
|
|
171
135
|
}
|
|
172
136
|
return { snapshot: template };
|
|
173
137
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
138
|
+
return { image: imageNameFor(template) };
|
|
139
|
+
}
|
|
140
|
+
/** The labels a create sends: E2B's metadata, and the template it was made
|
|
141
|
+
* from, while there is room for it among Runtime's 32 labels. */
|
|
142
|
+
function labelsFor(metadata, template) {
|
|
143
|
+
const labels = { ...metadata };
|
|
144
|
+
if (!STOCK_TEMPLATES.has(template) && Object.keys(labels).length < 32 && template.length <= 256)
|
|
145
|
+
labels[TEMPLATE_LABEL] = template;
|
|
146
|
+
return Object.keys(labels).length ? { labels } : {};
|
|
147
|
+
}
|
|
148
|
+
/** " in the account …", for an error a key of another account explains: the
|
|
149
|
+
* account's name and where its key came from. Nothing when the lookup fails. */
|
|
150
|
+
async function accountOf(client, opts) {
|
|
151
|
+
const me = await client.me({ timeoutMs: 5_000 }).catch(() => undefined);
|
|
152
|
+
if (!me)
|
|
153
|
+
return undefined;
|
|
154
|
+
const source = opts.runtime?.client ? "" : ` (key: ${keySource(opts.apiKey)})`;
|
|
155
|
+
return ` in the account "${me.orgName || me.orgId}"${source}`;
|
|
156
|
+
}
|
|
157
|
+
/** The create's refusal of a template no Runtime image answers to, as E2B's
|
|
158
|
+
* TemplateError, saying how to build it. */
|
|
159
|
+
function templateMissing(template, cause, account) {
|
|
160
|
+
const name = imageNameFor(template).replace(/[:@].*$/, "");
|
|
161
|
+
const error = new TemplateError(`No Runtime image is named "${imageNameFor(template)}"${account ?? ""}. E2B templates do not run on Runtime; build the same ` +
|
|
179
162
|
`environment as a Runtime image with that name and this call starts from it: ` +
|
|
180
|
-
`\`npx withruntime
|
|
181
|
-
`runtime.images.build({ name: "${
|
|
163
|
+
`\`npx withruntime images build --dockerfile e2b.Dockerfile --name ${name}\`, or ` +
|
|
164
|
+
`runtime.images.build({ name: "${name}", dockerfile }).`);
|
|
182
165
|
error.code = "template_not_found";
|
|
183
|
-
|
|
166
|
+
error.cause = cause;
|
|
167
|
+
return error;
|
|
184
168
|
}
|
|
185
169
|
function state(runtime) {
|
|
186
170
|
const current = runtime.state;
|
|
@@ -190,28 +174,57 @@ function state(runtime) {
|
|
|
190
174
|
return "stopped";
|
|
191
175
|
return "running";
|
|
192
176
|
}
|
|
193
|
-
|
|
177
|
+
/** Whether a paused sandbox is, to code written for E2B, still running:
|
|
178
|
+
* Runtime paused it itself for being idle (E2B never does) and the next
|
|
179
|
+
* call wakes it, so every E2B call on it works as on a running one. */
|
|
180
|
+
function idleAsleep(runtime) {
|
|
194
181
|
const info = runtime.info;
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
: "base";
|
|
182
|
+
return state(runtime) === "paused" && info.stopReason === "idle" && info.autoWake !== false;
|
|
183
|
+
}
|
|
184
|
+
/** The sandbox's state as E2B would say it. */
|
|
185
|
+
function e2bState(runtime) {
|
|
200
186
|
const current = state(runtime);
|
|
187
|
+
if (current === "running" || idleAsleep(runtime))
|
|
188
|
+
return "running";
|
|
189
|
+
return "paused";
|
|
190
|
+
}
|
|
191
|
+
/** E2B's endAt: when the sandbox ends by itself. One that never will (no
|
|
192
|
+
* time limit, as a persistent or a pilot's sandbox) reads 24 hours ahead, the
|
|
193
|
+
* furthest an E2B sandbox's end may be, so code that waits until the end, or
|
|
194
|
+
* extends near it, behaves as for the longest E2B sandbox: a JavaScript timer
|
|
195
|
+
* set past 24.8 days would fire at once. (5 October 2026: it read where the
|
|
196
|
+
* sandbox was paid up to, minutes ahead, so a pilot's looked about to end.) */
|
|
197
|
+
function endOf(runtime) {
|
|
198
|
+
const { endsAt, expiresAt } = runtime.info;
|
|
199
|
+
if (endsAt)
|
|
200
|
+
return new Date(endsAt);
|
|
201
|
+
if (endsAt === null && e2bState(runtime) === "running")
|
|
202
|
+
return new Date(Date.now() + LONGEST_TIMEOUT_MS);
|
|
203
|
+
return new Date(expiresAt);
|
|
204
|
+
}
|
|
205
|
+
function infoOf(runtime) {
|
|
206
|
+
const info = runtime.info;
|
|
207
|
+
const { [TEMPLATE_LABEL]: template, ...metadata } = info.labels ?? {};
|
|
208
|
+
const templateId = template ??
|
|
209
|
+
(typeof info.image === "string"
|
|
210
|
+
? info.image
|
|
211
|
+
: typeof info.snapshot === "string"
|
|
212
|
+
? info.snapshot
|
|
213
|
+
: "base");
|
|
214
|
+
const action = info.onTimeout ?? info.onLeaseEnd;
|
|
201
215
|
return {
|
|
202
216
|
sandboxId: info.id,
|
|
203
217
|
templateId,
|
|
204
218
|
...(info.name ? { name: info.name } : {}),
|
|
205
|
-
metadata
|
|
219
|
+
metadata,
|
|
206
220
|
startedAt: new Date(info.createdAt),
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
state: current === "stopped" ? "paused" : current,
|
|
221
|
+
endAt: endOf(runtime),
|
|
222
|
+
state: e2bState(runtime),
|
|
210
223
|
cpuCount: info.vcpu,
|
|
211
224
|
memoryMB: info.memoryMiB,
|
|
212
225
|
envdVersion: "runtime",
|
|
213
226
|
lifecycle: {
|
|
214
|
-
onTimeout:
|
|
227
|
+
onTimeout: action === "pause" ? "pause" : "kill",
|
|
215
228
|
autoResume: info.autoWake === true,
|
|
216
229
|
},
|
|
217
230
|
};
|
|
@@ -231,6 +244,8 @@ export class Sandbox {
|
|
|
231
244
|
pty;
|
|
232
245
|
#client;
|
|
233
246
|
#runtime;
|
|
247
|
+
#killed = false;
|
|
248
|
+
/** The /home/user link, once asked for: whether /home/user leads to /workspace. */
|
|
234
249
|
#home;
|
|
235
250
|
/** Ports shared as public previews, by port: the one share each asked for. */
|
|
236
251
|
#shares = new Map();
|
|
@@ -241,6 +256,7 @@ export class Sandbox {
|
|
|
241
256
|
this.#client = client;
|
|
242
257
|
const ctx = {
|
|
243
258
|
ensureHome: (text, options) => this.#ensureHome(text, options),
|
|
259
|
+
homeIsWorkspace: () => (this.#home ?? this.#linkHome()).catch(() => false),
|
|
244
260
|
requestTimeoutMs: this.requestTimeoutMs,
|
|
245
261
|
};
|
|
246
262
|
// A getter, so the modules see the sandbox as it is after each refresh.
|
|
@@ -283,21 +299,32 @@ export class Sandbox {
|
|
|
283
299
|
});
|
|
284
300
|
});
|
|
285
301
|
};
|
|
302
|
+
const done = (home) => wait(home.then(() => undefined));
|
|
286
303
|
if (this.#home || !text?.includes("/home/user"))
|
|
287
|
-
return
|
|
304
|
+
return done(this.#home ?? Promise.resolve(false));
|
|
305
|
+
return done(this.#linkHome(signal));
|
|
306
|
+
}
|
|
307
|
+
/** Links /home/user to /workspace unless something is already there, and
|
|
308
|
+
* says whether /home/user then leads to /workspace: an image may have its
|
|
309
|
+
* own /home/user, and E2B's paths are shown only where they are true. */
|
|
310
|
+
#linkHome(signal) {
|
|
288
311
|
const pending = (async () => {
|
|
289
|
-
const result = await guard("sandbox", () => this.#runtime.exec(
|
|
290
|
-
if (result.exitCode !== 0
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
312
|
+
const result = await guard("sandbox", () => this.#runtime.exec(HOME_LINK, { signal }));
|
|
313
|
+
if (result.exitCode !== 0) {
|
|
314
|
+
if (typeof process !== "undefined")
|
|
315
|
+
process.emitWarning(`Could not link /home/user to /workspace: ${result.stderr.trim()}`, {
|
|
316
|
+
code: "RUNTIME_E2B_HOME",
|
|
317
|
+
});
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
return result.stdout.trim() === "same";
|
|
294
321
|
})();
|
|
295
322
|
this.#home = pending;
|
|
296
323
|
pending.catch(() => {
|
|
297
324
|
if (this.#home === pending)
|
|
298
325
|
this.#home = undefined;
|
|
299
326
|
});
|
|
300
|
-
return
|
|
327
|
+
return pending;
|
|
301
328
|
}
|
|
302
329
|
static async create(templateOrOpts, maybeOpts) {
|
|
303
330
|
const opts = (typeof templateOrOpts === "string" ? maybeOpts : templateOrOpts) ?? {};
|
|
@@ -306,15 +333,15 @@ export class Sandbox {
|
|
|
306
333
|
options.signal?.throwIfAborted();
|
|
307
334
|
refuseCreate(opts);
|
|
308
335
|
const client = clientFor(opts);
|
|
309
|
-
//
|
|
310
|
-
|
|
336
|
+
// The server keeps the deadline, up to 24 hours (0381). Without one asked
|
|
337
|
+
// for, E2B's 300 s ends in a pause, never a delete.
|
|
311
338
|
const input = {
|
|
312
|
-
|
|
313
|
-
onLeaseEnd:
|
|
339
|
+
timeoutSeconds: timeoutSeconds(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS),
|
|
340
|
+
onLeaseEnd: onTimeout(opts.lifecycle, opts.timeoutMs !== undefined),
|
|
314
341
|
// With a lifecycle, E2B resumes a paused sandbox on traffic only when
|
|
315
342
|
// asked (autoResume); Runtime's automatic wake is the same thing (0093).
|
|
316
343
|
...(opts.lifecycle ? { autoWake: opts.lifecycle.autoResume === true } : {}),
|
|
317
|
-
...(opts.metadata
|
|
344
|
+
...labelsFor(opts.metadata, template),
|
|
318
345
|
// Runtime keeps them with the sandbox: every command, terminal and
|
|
319
346
|
// interpreter in it gets them, from this client or any other.
|
|
320
347
|
...(opts.envs && Object.keys(opts.envs).length ? { env: opts.envs } : {}),
|
|
@@ -329,9 +356,11 @@ export class Sandbox {
|
|
|
329
356
|
...source,
|
|
330
357
|
...opts.runtime?.create,
|
|
331
358
|
};
|
|
332
|
-
const runtime = await guard("sandbox", () => client.sandboxes.create(create, options))
|
|
333
|
-
|
|
334
|
-
|
|
359
|
+
const runtime = await guard("sandbox", () => client.sandboxes.create(create, options)).catch(async (error) => {
|
|
360
|
+
if (error.code === "image_not_found")
|
|
361
|
+
throw templateMissing(template, error, await accountOf(client, opts));
|
|
362
|
+
throw error;
|
|
363
|
+
});
|
|
335
364
|
return new this(runtime, client, opts.requestTimeoutMs);
|
|
336
365
|
}
|
|
337
366
|
/** Connects to a sandbox by id, waking it if it is paused, as E2B does. A
|
|
@@ -353,13 +382,17 @@ export class Sandbox {
|
|
|
353
382
|
return new SandboxPaginator(clientFor(opts), opts);
|
|
354
383
|
}
|
|
355
384
|
// ---- lifecycle --------------------------------------------------------
|
|
356
|
-
/**
|
|
385
|
+
/** Deletes the sandbox for good, as E2B's kill destroys it: its disk,
|
|
386
|
+
* memory and shared ports go. False when it was not found or was already
|
|
387
|
+
* deleted. */
|
|
357
388
|
static async kill(sandboxId, opts = {}) {
|
|
358
389
|
const client = clientFor(opts);
|
|
359
|
-
stopKeeping(sandboxId);
|
|
360
390
|
try {
|
|
391
|
+
// Read first: a delete answers the same however often it is sent, and
|
|
392
|
+
// E2B says false for one already gone.
|
|
361
393
|
const runtime = await guard("sandbox", () => client.sandboxes.get(sandboxId, request(opts)));
|
|
362
|
-
|
|
394
|
+
await guard("sandbox", () => runtime.delete(request(opts)));
|
|
395
|
+
return true;
|
|
363
396
|
}
|
|
364
397
|
catch (error) {
|
|
365
398
|
if (error instanceof SandboxNotFoundError)
|
|
@@ -368,9 +401,12 @@ export class Sandbox {
|
|
|
368
401
|
}
|
|
369
402
|
}
|
|
370
403
|
async kill(opts = {}) {
|
|
371
|
-
|
|
404
|
+
if (this.#killed)
|
|
405
|
+
return false;
|
|
372
406
|
try {
|
|
373
|
-
|
|
407
|
+
await guard("sandbox", () => this.#runtime.delete(request(opts)));
|
|
408
|
+
this.#killed = true;
|
|
409
|
+
return true;
|
|
374
410
|
}
|
|
375
411
|
catch (error) {
|
|
376
412
|
if (error instanceof SandboxNotFoundError)
|
|
@@ -378,10 +414,11 @@ export class Sandbox {
|
|
|
378
414
|
throw error;
|
|
379
415
|
}
|
|
380
416
|
}
|
|
381
|
-
/** Sets the sandbox to end `timeoutMs` from now, up to 24 hours
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
417
|
+
/** Sets the sandbox to end `timeoutMs` from now, up to 24 hours, as E2B
|
|
418
|
+
* does; the server keeps the deadline. Runtime cannot bring an end
|
|
419
|
+
* sooner, so that is refused. A sandbox with no time limit (one on a pilot
|
|
420
|
+
* account, kept running by the server) has no end to move, and nothing
|
|
421
|
+
* changes. */
|
|
385
422
|
static async setTimeout(sandboxId, timeoutMs, opts = {}) {
|
|
386
423
|
const client = clientFor(opts);
|
|
387
424
|
const runtime = await guard("sandbox", () => client.sandboxes.get(sandboxId, request(opts)));
|
|
@@ -399,6 +436,9 @@ export class Sandbox {
|
|
|
399
436
|
await guard("sandbox", () => this.#runtime.refresh(request(opts)));
|
|
400
437
|
return infoOf(this.#runtime);
|
|
401
438
|
}
|
|
439
|
+
/** Whether the sandbox answers calls, as E2B's health check says: true
|
|
440
|
+
* while it runs, and while Runtime has paused it for being idle, since the
|
|
441
|
+
* next call wakes it. False once paused on request, ended or deleted. */
|
|
402
442
|
async isRunning(opts = {}) {
|
|
403
443
|
try {
|
|
404
444
|
await guard("sandbox", () => this.#runtime.refresh(request(opts)));
|
|
@@ -408,7 +448,7 @@ export class Sandbox {
|
|
|
408
448
|
return false;
|
|
409
449
|
throw error;
|
|
410
450
|
}
|
|
411
|
-
return this.#runtime.state === "running";
|
|
451
|
+
return this.#runtime.state === "running" || idleAsleep(this.#runtime);
|
|
412
452
|
}
|
|
413
453
|
/** Pauses the sandbox, keeping memory and files. False when it was paused. */
|
|
414
454
|
static async pause(sandboxId, opts = {}) {
|
|
@@ -445,10 +485,17 @@ export class Sandbox {
|
|
|
445
485
|
const Kind = this.constructor;
|
|
446
486
|
return copies.map((copy) => new Kind(copy, this.#client));
|
|
447
487
|
}
|
|
448
|
-
/** Keeps the sandbox's whole machine as a Runtime snapshot
|
|
449
|
-
*
|
|
488
|
+
/** Keeps the sandbox's whole machine as a Runtime snapshot, or with
|
|
489
|
+
* `mode: "filesystem"` its disk alone, from which a sandbox boots afresh;
|
|
490
|
+
* start from it with Sandbox.create(snapshotId). */
|
|
450
491
|
async createSnapshot(opts = {}) {
|
|
451
|
-
const
|
|
492
|
+
const memory = snapshotMemory("mode", opts.mode);
|
|
493
|
+
const mode = memory === undefined ? undefined : memory ? "memory" : "disk";
|
|
494
|
+
const snapshot = await guard("sandbox", () => this.#runtime.snapshot({
|
|
495
|
+
...(opts.name ? { name: opts.name } : {}),
|
|
496
|
+
...(mode ? { mode } : {}),
|
|
497
|
+
...request(opts),
|
|
498
|
+
}));
|
|
452
499
|
return { snapshotId: snapshot.id, names: snapshot.name ? [snapshot.name] : [] };
|
|
453
500
|
}
|
|
454
501
|
static async createSnapshot(sandboxId, opts = {}) {
|
|
@@ -479,7 +526,7 @@ export class Sandbox {
|
|
|
479
526
|
getHost(port) {
|
|
480
527
|
if (!Number.isInteger(port) || port < 1 || port > 65_535)
|
|
481
528
|
throw new InvalidArgumentError(`port must be a whole number from 1 to 65535, not ${port}.`);
|
|
482
|
-
// A
|
|
529
|
+
// A sandbox without credit shares ports privately: a host alone would answer 404 or
|
|
483
530
|
// 401 to everyone. Say so now, with the address that works.
|
|
484
531
|
if (this.#runtime.info.funding === "trial")
|
|
485
532
|
throw new PublicPreviewNotAllowedError(this.sandboxId, port);
|
|
@@ -546,48 +593,42 @@ async function resume(runtime, opts) {
|
|
|
546
593
|
const current = state(runtime);
|
|
547
594
|
if (current === "stopped")
|
|
548
595
|
throw new SandboxNotFoundError(`Sandbox ${runtime.id} has ended.`);
|
|
549
|
-
const asked = opts.timeoutMs === undefined ? undefined : Date.now() + opts.timeoutMs;
|
|
550
596
|
if (current === "paused") {
|
|
551
597
|
await guard("sandbox", () => runtime.wake({
|
|
552
|
-
...(opts.timeoutMs === undefined ? {} : { timeoutSeconds:
|
|
598
|
+
...(opts.timeoutMs === undefined ? {} : { timeoutSeconds: timeoutSeconds(opts.timeoutMs) }),
|
|
553
599
|
...request(opts),
|
|
554
600
|
}));
|
|
555
601
|
}
|
|
556
602
|
else if (opts.timeoutMs !== undefined) {
|
|
557
603
|
checkTimeout(opts.timeoutMs);
|
|
558
|
-
//
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
await guard("sandbox", () => runtime.extend(Math.ceil(later / 1000), request(opts)));
|
|
564
|
-
}
|
|
565
|
-
if (asked !== undefined && asked > (keepers.get(runtime.id)?.until ?? 0))
|
|
566
|
-
keepUntil(runtime, asked);
|
|
604
|
+
// A running one's end moves later, never sooner, as E2B's connect.
|
|
605
|
+
const later = secondsLater(runtime, opts.timeoutMs);
|
|
606
|
+
if (later > 1)
|
|
607
|
+
await guard("sandbox", () => runtime.extend(Math.ceil(later), request(opts)));
|
|
608
|
+
}
|
|
567
609
|
}
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
610
|
+
/** How many seconds past its current end `now + timeoutMs` is; 0 for a
|
|
611
|
+
* sandbox with no time limit, which has no end to move. A second either way
|
|
612
|
+
* is the clocks and the trip, not a new end. */
|
|
613
|
+
function secondsLater(runtime, timeoutMs) {
|
|
614
|
+
if (runtime.info.endsAt === null)
|
|
615
|
+
return 0;
|
|
616
|
+
const end = Date.parse(runtime.info.endsAt ?? runtime.info.expiresAt);
|
|
617
|
+
return (Date.now() + timeoutMs - end) / 1000;
|
|
573
618
|
}
|
|
574
619
|
async function extendTo(runtime, timeoutMs, opts) {
|
|
575
620
|
checkTimeout(timeoutMs);
|
|
576
|
-
// No time limit: no end to move (0300).
|
|
577
621
|
if (runtime.info.endsAt === null)
|
|
578
622
|
return;
|
|
579
|
-
const
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
if (later > 1000)
|
|
585
|
-
await guard("sandbox", () => runtime.extend(Math.ceil(later / 1000), request(opts)));
|
|
586
|
-
keepUntil(runtime, asked);
|
|
623
|
+
const later = secondsLater(runtime, timeoutMs);
|
|
624
|
+
if (later < -1)
|
|
625
|
+
throw new NotSupportedError("Ending a sandbox sooner than its time limit (a shorter setTimeout)", "Runtime cannot bring a time limit forward. Call kill() when the work is done.");
|
|
626
|
+
if (later > 1)
|
|
627
|
+
await guard("sandbox", () => runtime.extend(Math.ceil(later), request(opts)));
|
|
587
628
|
}
|
|
588
629
|
async function pause(runtime, opts) {
|
|
589
|
-
if (opts.keepMemory === false)
|
|
590
|
-
throw
|
|
630
|
+
if (snapshotMemory("mode", opts.mode, opts.keepMemory) === false)
|
|
631
|
+
throw filesOnlyPause();
|
|
591
632
|
const current = state(runtime);
|
|
592
633
|
if (current === "paused")
|
|
593
634
|
return false;
|
|
@@ -609,6 +650,10 @@ export class SandboxPaginator {
|
|
|
609
650
|
#opts;
|
|
610
651
|
#pageSize;
|
|
611
652
|
#local;
|
|
653
|
+
/** One E2B state asked for alone: Runtime's states are read together and
|
|
654
|
+
* sorted here, since a sandbox Runtime paused for being idle is running
|
|
655
|
+
* to E2B. */
|
|
656
|
+
#only;
|
|
612
657
|
#page;
|
|
613
658
|
#all;
|
|
614
659
|
#offset;
|
|
@@ -627,9 +672,10 @@ export class SandboxPaginator {
|
|
|
627
672
|
query.startedAfter !== undefined ||
|
|
628
673
|
opts.order === "desc" ||
|
|
629
674
|
opts.nextToken !== undefined;
|
|
630
|
-
const
|
|
675
|
+
const asked = new Set(query.state?.length ? query.state : ["running", "paused"]);
|
|
676
|
+
this.#only = asked.size === 1 ? [...asked][0] : undefined;
|
|
631
677
|
this.#filter = {
|
|
632
|
-
state:
|
|
678
|
+
state: [...RUNNING, ...PAUSED],
|
|
633
679
|
...(query.metadata && Object.keys(query.metadata).length ? { labels: query.metadata } : {}),
|
|
634
680
|
limit: this.#local ? 100 : Math.min(this.#pageSize, 100),
|
|
635
681
|
};
|
|
@@ -658,21 +704,28 @@ export class SandboxPaginator {
|
|
|
658
704
|
: await this.#client.sandboxes.list(this.#filter, this.#request));
|
|
659
705
|
this.#page = page ?? undefined;
|
|
660
706
|
this.#hasNext = Boolean(page?.hasMore);
|
|
661
|
-
const items = (page?.data ?? []).map(infoOf);
|
|
707
|
+
const items = (page?.data ?? []).map(infoOf).filter((info) => this.#wanted(info));
|
|
662
708
|
this.#offset += items.length;
|
|
663
709
|
return items;
|
|
664
710
|
}
|
|
711
|
+
#wanted(info) {
|
|
712
|
+
return this.#only === undefined || info.state === this.#only;
|
|
713
|
+
}
|
|
665
714
|
async #everyMatch() {
|
|
666
715
|
const query = this.#opts.query ?? {};
|
|
667
716
|
const templates = query.template === undefined
|
|
668
717
|
? undefined
|
|
669
|
-
:
|
|
718
|
+
: STOCK_TEMPLATES.has(query.template)
|
|
719
|
+
? new Set(["base", ...STOCK_TEMPLATES])
|
|
720
|
+
: new Set([query.template]);
|
|
670
721
|
const after = query.startedAfter?.getTime();
|
|
671
722
|
const all = [];
|
|
672
723
|
let page = await this.#client.sandboxes.list(this.#filter, this.#request);
|
|
673
724
|
while (page) {
|
|
674
725
|
for (const one of page.data) {
|
|
675
726
|
const info = infoOf(one);
|
|
727
|
+
if (!this.#wanted(info))
|
|
728
|
+
continue;
|
|
676
729
|
if (templates && !templates.has(info.templateId))
|
|
677
730
|
continue;
|
|
678
731
|
if (after !== undefined && info.startedAt.getTime() < after)
|
|
@@ -691,16 +744,6 @@ function tokenOffset(token) {
|
|
|
691
744
|
throw new InvalidArgumentError(`"${token}" is not a nextToken this package gave; pass the paginator's own nextToken.`);
|
|
692
745
|
return Number(match[1]);
|
|
693
746
|
}
|
|
694
|
-
/** The template ids a template name stands for: "base" for the stock names,
|
|
695
|
-
* the id itself for a UUID, and every Runtime image of that name. */
|
|
696
|
-
async function templateIds(client, template, options) {
|
|
697
|
-
if (STOCK_TEMPLATES.has(template))
|
|
698
|
-
return new Set(["base"]);
|
|
699
|
-
if (UUID.test(template))
|
|
700
|
-
return new Set([template]);
|
|
701
|
-
const page = await client.images.list({ name: template, limit: 100 }, options);
|
|
702
|
-
return new Set(page.data.map((image) => image.id));
|
|
703
|
-
}
|
|
704
747
|
async function e2bMetrics(runtime, opts) {
|
|
705
748
|
const start = opts.start === undefined ? Date.now() - 15 * 60_000 : new Date(opts.start).getTime();
|
|
706
749
|
const end = opts.end === undefined ? Date.now() : new Date(opts.end).getTime();
|