withruntime 0.14.0 → 0.16.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 +97 -32
- package/COMPATIBILITY.md +120 -5
- package/DAYTONA.md +34 -3
- package/E2B.md +34 -5
- package/README.md +33 -0
- package/README.src.md +33 -0
- package/VERCEL.md +52 -5
- package/compatibility-lock.json +71 -111
- package/dist/blaxel/client.d.ts +11 -0
- package/dist/blaxel/client.js +28 -0
- package/dist/blaxel/errors.d.ts +65 -0
- package/dist/blaxel/errors.js +114 -0
- package/dist/blaxel/index.d.ts +10 -3
- package/dist/blaxel/index.js +23 -4
- package/dist/blaxel/preview.d.ts +29 -4
- package/dist/blaxel/preview.js +79 -5
- package/dist/blaxel/sandbox.js +1 -0
- package/dist/blaxel/session.js +4 -2
- package/dist/cli-extras.js +2 -1
- package/dist/cli-words.js +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +54 -21
- package/dist/client.d.ts +1 -1
- package/dist/daytona/client.d.ts +4 -0
- package/dist/daytona/client.js +8 -3
- package/dist/daytona/daytona.d.ts +3 -0
- package/dist/daytona/daytona.js +2 -1
- package/dist/e2b/sandbox.d.ts +6 -5
- package/dist/e2b/sandbox.js +9 -12
- package/dist/freestyle/index.d.ts +77 -5
- package/dist/freestyle/index.js +380 -48
- package/dist/identity.js +36 -18
- package/dist/index.d.ts +2 -2
- package/dist/machine.d.ts +24 -12
- package/dist/machine.js +46 -14
- package/dist/mcp.js +16 -3
- package/dist/modal/index.d.ts +15 -0
- package/dist/modal/index.js +35 -7
- package/dist/morph/index.d.ts +255 -0
- package/dist/morph/index.js +641 -0
- package/dist/network-cli.js +2 -2
- package/dist/product-names.d.ts +2 -2
- package/dist/product-names.js +2 -2
- package/dist/products/limits.d.ts +16 -2
- package/dist/products/limits.js +2 -2
- package/dist/products/mcp.d.ts +5 -0
- package/dist/products/network-products.d.ts +21 -3
- package/dist/products/network-products.js +3 -1
- package/dist/products/previews.d.ts +6 -3
- package/dist/products/previews.js +1 -1
- package/dist/snapshots.d.ts +4 -0
- package/dist/ssh.d.ts +35 -0
- package/dist/ssh.js +93 -1
- package/dist/transport.d.ts +11 -8
- package/dist/transport.js +46 -12
- package/dist/types.d.ts +24 -0
- package/dist/vercel/command.d.ts +48 -1
- package/dist/vercel/command.js +237 -6
- package/dist/vercel/sandbox-user.js +2 -1
- package/dist/vercel/sandbox.d.ts +16 -2
- package/dist/vercel/sandbox.js +45 -2
- package/package.json +6 -1
package/dist/cli.js
CHANGED
|
@@ -24,7 +24,7 @@ Usage: runtime <product> <command> [options] runtime <command> [options]
|
|
|
24
24
|
|
|
25
25
|
Start
|
|
26
26
|
sandboxes run [--keep] [create options] -- <command>
|
|
27
|
-
A fresh sandbox runs one command, then
|
|
27
|
+
A fresh sandbox runs one command, then is deleted
|
|
28
28
|
|
|
29
29
|
Account
|
|
30
30
|
login [--agent-name <name>] [--no-browser] [--wait]
|
|
@@ -88,6 +88,8 @@ Account
|
|
|
88
88
|
Connect
|
|
89
89
|
sandboxes ssh <id|name> [-- <command...>] Log in over SSH; \`runtime sandboxes ssh help\` for editors
|
|
90
90
|
sandboxes ssh config [--install] Make \`ssh <id|name>.runtime\` work (VS Code, JetBrains)
|
|
91
|
+
sandboxes code <id|name> [--editor vscode|cursor|windsurf]
|
|
92
|
+
Open it in VS Code, Cursor or Windsurf over SSH
|
|
91
93
|
sandboxes port-forward <id|name> <port|local:remote>...
|
|
92
94
|
Forward local ports to ports in a sandbox
|
|
93
95
|
|
|
@@ -122,14 +124,17 @@ const SANDBOX_HELP = `runtime sandboxes <command>
|
|
|
122
124
|
|
|
123
125
|
Every <id> below can also be the sandbox's name.
|
|
124
126
|
|
|
125
|
-
run [--keep] [create options] -- <command> A fresh sandbox runs one command, then
|
|
127
|
+
run [--keep] [create options] -- <command> A fresh sandbox runs one command, then is deleted
|
|
126
128
|
ssh <id|name> [-- <command...>] | ssh config [--install] Log in over SSH, also from VS Code and JetBrains
|
|
129
|
+
code <id|name> [--editor vscode|cursor|windsurf] [--path /workspace] [--print]
|
|
130
|
+
Open it in your editor over SSH, in one step
|
|
127
131
|
port-forward <id|name> <port|local:remote>... Forward local ports to ports in it
|
|
128
132
|
create [--name n] [--label k=v]... [--env K=V]... [--vcpu 2] [--memory 4096] [--disk 4096]
|
|
129
133
|
[--cpu shared|reserved] [--cpu-floor <thousandths>] [--max-cost <usd>] [--max-total-cost <usd>]
|
|
130
134
|
[--timeout <60-86400>] [--on-timeout pause|stop|delete] [--no-wait]
|
|
131
135
|
[--no-internet] [--allow <host>]... [--deny <host>]... [--connect <host:port>]...
|
|
132
136
|
[--image <name:tag|imageId> | --snapshot <snapshotId>] [--volume <volumeId>:/data[:snapshot]]...
|
|
137
|
+
[--spread-from <id>]...
|
|
133
138
|
[--idle-pause <seconds>] [--no-auto-wake] [--persistent] [--get-or-create]
|
|
134
139
|
Create one; prints its id
|
|
135
140
|
ls [--all] [--label k=v]... [--state running] List (live ones unless --all)
|
|
@@ -162,6 +167,7 @@ const SANDBOX_HELP = `runtime sandboxes <command>
|
|
|
162
167
|
extend <id> <seconds> Move a time limit on (one with none needs nothing)
|
|
163
168
|
update <id> [--name n] [--label k=v]... [--env K=V]... [--unset-env K]... [--idle-pause <seconds>] [--auto-wake on|off]
|
|
164
169
|
[--persistent on|off] [--max-total-cost <usd>|none]
|
|
170
|
+
[--timeout <60-86400 from now>|0] [--on-timeout pause|stop|delete]
|
|
165
171
|
Change its settings; persistent keeps it running until stopped
|
|
166
172
|
snapshot <id> [--name n] [--retention days|credit] Keep its whole machine; prints the snapshot id
|
|
167
173
|
mount <id> <s3|r2|gcs>://<bucket>[/prefix] <path> [--secret NAME] [--region r]
|
|
@@ -179,8 +185,9 @@ const SANDBOX_HELP = `runtime sandboxes <command>
|
|
|
179
185
|
Print file changes as they happen, until Ctrl-C
|
|
180
186
|
mcp catalog MCP servers a sandbox can run (licence, settings, hosts)
|
|
181
187
|
mcp <id> start <server>... [--secret SERVER.SETTING=SECRET]... [--env SERVER.NAME=value]...
|
|
182
|
-
[--option SERVER.NAME=value]... [--port 8765] [--replace]
|
|
188
|
+
[--option SERVER.NAME=value]... [--port 8765] [--replace] [--public]
|
|
183
189
|
Start them; prints each URL and the header to send
|
|
190
|
+
(--public: URLs that never expire, paid only)
|
|
184
191
|
mcp <id> [status] | mcp <id> stop Their state and fresh URLs; stop them
|
|
185
192
|
preview <id> <port> [--public] [--ttl <s>] Share a port at an HTTPS address (--public: paid only)
|
|
186
193
|
preview rotate <id> <port> Refuse every token given out for a private port so
|
|
@@ -254,7 +261,7 @@ commandAfter) {
|
|
|
254
261
|
if (arg.startsWith("--") || (arg.startsWith("-") && arg.length === 2 && !/^-\d/.test(arg))) {
|
|
255
262
|
const [typed, inline] = arg.replace(/^-+/, "").split(/=(.*)/s, 2);
|
|
256
263
|
// A plural of an option is the option once per comma: --hosts a,b is
|
|
257
|
-
// --host a --host b (13 October 2026:
|
|
264
|
+
// --host a --host b (13 October 2026: a user's first try at a secret).
|
|
258
265
|
const plural = !known.has(typed) &&
|
|
259
266
|
typed.endsWith("s") &&
|
|
260
267
|
LISTS.has(typed.slice(0, -1)) &&
|
|
@@ -428,6 +435,7 @@ const SANDBOX_MOVED = ["run", "ssh", "port-forward", "forward"];
|
|
|
428
435
|
const SANDBOX_VERBS = [
|
|
429
436
|
"run",
|
|
430
437
|
"ssh",
|
|
438
|
+
"code",
|
|
431
439
|
"port-forward",
|
|
432
440
|
"create",
|
|
433
441
|
"ls",
|
|
@@ -585,12 +593,12 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
|
|
|
585
593
|
out.error(`Warning: ${named(words.warning)}`);
|
|
586
594
|
const [first, ...remaining] = words.argv;
|
|
587
595
|
// Sandbox is one product of several, so its commands live under it
|
|
588
|
-
// (
|
|
596
|
+
// (23 September 2026): `runtime sandboxes run`, `sandbox ssh` and
|
|
589
597
|
// `sandbox port-forward`. The old top-level forms were removed, not aliased.
|
|
590
598
|
if (first && SANDBOX_MOVED.includes(first))
|
|
591
599
|
throw usage(`\`${me} ${first}\` is now \`${me} sandboxes ${first === "forward" ? "port-forward" : first}\`.`);
|
|
592
600
|
const nested = (first === "sandbox" || first === "sandboxes" || first === "sbx") &&
|
|
593
|
-
SANDBOX_MOVED.includes(remaining[0] ?? "");
|
|
601
|
+
[...SANDBOX_MOVED, "code"].includes(remaining[0] ?? "");
|
|
594
602
|
const command = nested ? remaining[0] : first;
|
|
595
603
|
const rest = nested ? remaining.slice(1) : remaining;
|
|
596
604
|
// `runtime sandboxes create --help` is how an agent asks what a command takes:
|
|
@@ -627,7 +635,7 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
|
|
|
627
635
|
: `Runtime Cloud ${VERSION}. ${start}
|
|
628
636
|
|
|
629
637
|
${me} sandboxes run -- python3 < invoice.py
|
|
630
|
-
Run a local invoice script, then
|
|
638
|
+
Run a local invoice script, then delete the sandbox
|
|
631
639
|
${me} sandboxes create A sandbox that stays; prints its id
|
|
632
640
|
${me} sandboxes exec <id> -- <command> Run in it
|
|
633
641
|
${me} help Everything else
|
|
@@ -649,24 +657,23 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
|
|
|
649
657
|
const onCapacityWait = waiting(out);
|
|
650
658
|
const sbx = await withVolumeRelease((options) => runtime.sandboxes.create(input, { ...options, onCapacityWait }), out);
|
|
651
659
|
let cleanup;
|
|
652
|
-
// SIGINT and normal completion share one
|
|
653
|
-
// across
|
|
660
|
+
// SIGINT and normal completion share one delete, with an absolute
|
|
661
|
+
// deadline across its retries, so cleanup cannot hang. A delete, not a
|
|
662
|
+
// stop: a stopped sandbox keeps its disk, billed, and an account without
|
|
663
|
+
// credit keeps at most 10, so ten runs that only stopped refused the
|
|
664
|
+
// eleventh (no_credit_kept_limit, 0685). --keep is how to keep one.
|
|
654
665
|
const stop = () => (cleanup ??= (async () => {
|
|
655
666
|
const signal = AbortSignal.timeout(30_000);
|
|
656
667
|
try {
|
|
657
|
-
await sbx.
|
|
658
|
-
if (sbx.state !== "stopped")
|
|
659
|
-
await sbx.waitFor("stopped", { timeoutSeconds: 25, signal });
|
|
660
|
-
if (sbx.state !== "stopped")
|
|
661
|
-
throw new Error(`Sandbox is still ${sbx.state}.`);
|
|
668
|
+
await sbx.delete({ signal });
|
|
662
669
|
return true;
|
|
663
670
|
}
|
|
664
671
|
catch (error) {
|
|
665
672
|
out.error(describeError(new RuntimeError({
|
|
666
673
|
code: "cleanup_failed",
|
|
667
674
|
status: 0,
|
|
668
|
-
message: `Could not confirm that sandbox ${sbx.id}
|
|
669
|
-
hint: `Check it and retry cleanup with: ${me} sandboxes
|
|
675
|
+
message: `Could not confirm that sandbox ${sbx.id} was deleted. ${error instanceof Error ? error.message : String(error)}`,
|
|
676
|
+
hint: `Check it and retry cleanup with: ${me} sandboxes rm ${sbx.id}`,
|
|
670
677
|
cause: error,
|
|
671
678
|
}), out.json));
|
|
672
679
|
return false;
|
|
@@ -1035,6 +1042,10 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
|
|
|
1035
1042
|
product = "ssh";
|
|
1036
1043
|
return (await import("./ssh.js")).sshCommand(rest, env, out, () => client(env), me);
|
|
1037
1044
|
}
|
|
1045
|
+
if (nested && command === "code") {
|
|
1046
|
+
product = "ssh";
|
|
1047
|
+
return (await import("./ssh.js")).editorCommand(rest, env, out, () => client(env), me);
|
|
1048
|
+
}
|
|
1038
1049
|
if (command === "port-forward" || command === "forward") {
|
|
1039
1050
|
product = "ssh";
|
|
1040
1051
|
return (await import("./ssh.js")).portForwardCommand(rest, out, () => client(env), me);
|
|
@@ -1852,6 +1863,8 @@ async function sandbox(argv, env, out) {
|
|
|
1852
1863
|
"auto-wake",
|
|
1853
1864
|
"persistent",
|
|
1854
1865
|
"max-total-cost",
|
|
1866
|
+
"timeout",
|
|
1867
|
+
"on-timeout",
|
|
1855
1868
|
]);
|
|
1856
1869
|
const sbx = await get(args.positional[0]);
|
|
1857
1870
|
const onOff = (name) => {
|
|
@@ -1862,6 +1875,9 @@ async function sandbox(argv, env, out) {
|
|
|
1862
1875
|
throw usage(`--${name} takes on or off.`);
|
|
1863
1876
|
return value === "on";
|
|
1864
1877
|
};
|
|
1878
|
+
const onTimeout = flag(args, "on-timeout")
|
|
1879
|
+
? oneOf(args, "on-timeout", ["pause", "stop", "delete"])
|
|
1880
|
+
: undefined;
|
|
1865
1881
|
const cost = flag(args, "max-total-cost");
|
|
1866
1882
|
if (cost !== undefined && cost !== "none" && !/^\d+(\.\d{1,6})?$/.test(cost))
|
|
1867
1883
|
throw usage("--max-total-cost takes dollars, such as 25 or 2.50, or none.");
|
|
@@ -1883,9 +1899,13 @@ async function sandbox(argv, env, out) {
|
|
|
1883
1899
|
: {
|
|
1884
1900
|
maxTotalCostMicros: cost === "none" ? null : usdMicros(args, "max-total-cost"),
|
|
1885
1901
|
}),
|
|
1902
|
+
...(integer(args, "timeout") !== undefined
|
|
1903
|
+
? { timeoutSeconds: integer(args, "timeout") }
|
|
1904
|
+
: {}),
|
|
1905
|
+
...(onTimeout === undefined ? {} : { onTimeout }),
|
|
1886
1906
|
};
|
|
1887
1907
|
if (!Object.keys(settings).length)
|
|
1888
|
-
throw usage("Say what to change: --name, --label, --env, --unset-env, --idle-pause, --auto-wake, --persistent
|
|
1908
|
+
throw usage("Say what to change: --name, --label, --env, --unset-env, --idle-pause, --auto-wake, --persistent, --max-total-cost, --timeout or --on-timeout.");
|
|
1889
1909
|
await sbx.update(settings);
|
|
1890
1910
|
print(describe(sbx.info), sbx.info);
|
|
1891
1911
|
return 0;
|
|
@@ -2191,7 +2211,7 @@ function attachment(spec) {
|
|
|
2191
2211
|
* A terminal: one line, not shown. */
|
|
2192
2212
|
/** A pasted secret as the header it becomes: no line breaks (a token copied
|
|
2193
2213
|
* from a terminal arrives wrapped), no bracketed-paste markers, no space at
|
|
2194
|
-
* either end. 13 October 2026:
|
|
2214
|
+
* either end. 13 October 2026: a user's pasted Claude token was refused as
|
|
2195
2215
|
* "not visible ASCII". A header cannot hold a line break, so none is lost. */
|
|
2196
2216
|
export function cleanSecret(raw) {
|
|
2197
2217
|
return raw
|
|
@@ -2603,8 +2623,8 @@ Making, listing and revoking keys with a key needs one an owner or admin made.
|
|
|
2603
2623
|
Every key is also at https://withruntime.com/account/keys.
|
|
2604
2624
|
`;
|
|
2605
2625
|
/* `runtime keys`: a key made from the terminal, by this machine's key with no
|
|
2606
|
-
browser (
|
|
2607
|
-
(
|
|
2626
|
+
browser (14 October 2026), or approved in the browser
|
|
2627
|
+
(23 September 2026; ARCHITECTURE.md section 10, "Keys from the
|
|
2608
2628
|
terminal"). Only the options the keys page offers. */
|
|
2609
2629
|
/* `runtime account`: the accounts this machine is connected to, and which one
|
|
2610
2630
|
every command uses (ARCHITECTURE.md section 3.9). Each is its own saved
|
|
@@ -2787,7 +2807,7 @@ async function keys(argv, env, out) {
|
|
|
2787
2807
|
dailyLimitMicros: limit === undefined ? null : dailyLimitMicros(limit),
|
|
2788
2808
|
};
|
|
2789
2809
|
/* A key on this machine makes the new one itself, with no browser: an
|
|
2790
|
-
agent with no person, a swarm's coordinator (
|
|
2810
|
+
agent with no person, a swarm's coordinator (14 October 2026). */
|
|
2791
2811
|
const saved = has(args, "browser")
|
|
2792
2812
|
? null
|
|
2793
2813
|
: env.RUNTIME_API_KEY || (await connectionStore(env).read())?.key || null;
|
|
@@ -3701,6 +3721,7 @@ const CREATE_VALUES = [
|
|
|
3701
3721
|
"connect",
|
|
3702
3722
|
"image",
|
|
3703
3723
|
"snapshot",
|
|
3724
|
+
"spread-from",
|
|
3704
3725
|
"volume",
|
|
3705
3726
|
"env",
|
|
3706
3727
|
];
|
|
@@ -3728,6 +3749,18 @@ function createInput(args) {
|
|
|
3728
3749
|
return {
|
|
3729
3750
|
...(flag(args, "image") ? { image: flag(args, "image") } : {}),
|
|
3730
3751
|
...(flag(args, "snapshot") ? { snapshot: flag(args, "snapshot") } : {}),
|
|
3752
|
+
...(args.flags.has("spread-from")
|
|
3753
|
+
? {
|
|
3754
|
+
spreadFrom: args.flags
|
|
3755
|
+
.get("spread-from")
|
|
3756
|
+
.flatMap((value) => value.split(","))
|
|
3757
|
+
.map((id) => {
|
|
3758
|
+
if (!id.trim())
|
|
3759
|
+
throw usage("--spread-from needs a non-empty value.");
|
|
3760
|
+
return id.trim();
|
|
3761
|
+
}),
|
|
3762
|
+
}
|
|
3763
|
+
: {}),
|
|
3731
3764
|
...(args.flags.has("volume") ? { volumes: args.flags.get("volume").map(attachment) } : {}),
|
|
3732
3765
|
...(has(args, "no-internet") || rules.length
|
|
3733
3766
|
? {
|
package/dist/client.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { Sandbox } from "./sandbox.js";
|
|
|
6
6
|
import { Snapshots } from "./snapshots.js";
|
|
7
7
|
import { Transport, type RequestOptions } from "./transport.js";
|
|
8
8
|
import type { CreateSandbox, DeletedSandbox, FeedbackKind, SandboxInfo, Usage } from "./types.js";
|
|
9
|
-
export type { CreateOptions } from "./machine.js";
|
|
9
|
+
export type { CreateOptions, LifecycleOptions } from "./machine.js";
|
|
10
10
|
export type RuntimeOptions = {
|
|
11
11
|
/** Default: the RUNTIME_API_KEY environment variable. */
|
|
12
12
|
apiKey?: string;
|
package/dist/daytona/client.d.ts
CHANGED
|
@@ -40,5 +40,9 @@ export interface DaytonaConfig {
|
|
|
40
40
|
export declare function pickKey(explicit?: string): string | undefined;
|
|
41
41
|
/** The Runtime client for this configuration. */
|
|
42
42
|
export declare function clientFor(config?: DaytonaConfig): Runtime;
|
|
43
|
+
/** Runtime runs in one US region: a Daytona target (the client's, or a
|
|
44
|
+
* create's own since @daytona/sdk 0.224) other than "us" is refused, and
|
|
45
|
+
* none or "" means that region. */
|
|
46
|
+
export declare function refuseTarget(target: string | undefined): void;
|
|
43
47
|
/** Test hook: forget the cached clients. */
|
|
44
48
|
export declare function resetClients(): void;
|
package/dist/daytona/client.js
CHANGED
|
@@ -21,9 +21,7 @@ const clients = new Map();
|
|
|
21
21
|
export function clientFor(config = {}) {
|
|
22
22
|
if (config.jwtToken !== undefined || (config.organizationId !== undefined && !config.apiKey))
|
|
23
23
|
throw new NotSupportedError("Daytona JWT sign-in (jwtToken, organizationId)", "Use a Runtime key: set RUNTIME_API_KEY, or run `npx withruntime login` once.");
|
|
24
|
-
|
|
25
|
-
if (target !== undefined && target !== "" && target !== "us")
|
|
26
|
-
throw new NotSupportedError(`The Daytona target "${target}"`, 'Runtime runs in one US region; remove target or use "us".');
|
|
24
|
+
refuseTarget(config.target ?? env("DAYTONA_TARGET"));
|
|
27
25
|
if (config.withruntime?.client)
|
|
28
26
|
return config.withruntime.client;
|
|
29
27
|
const key = pickKey(config.apiKey);
|
|
@@ -34,6 +32,13 @@ export function clientFor(config = {}) {
|
|
|
34
32
|
}
|
|
35
33
|
return client;
|
|
36
34
|
}
|
|
35
|
+
/** Runtime runs in one US region: a Daytona target (the client's, or a
|
|
36
|
+
* create's own since @daytona/sdk 0.224) other than "us" is refused, and
|
|
37
|
+
* none or "" means that region. */
|
|
38
|
+
export function refuseTarget(target) {
|
|
39
|
+
if (target !== undefined && target !== "" && target !== "us")
|
|
40
|
+
throw new NotSupportedError(`The Daytona target "${target}"`, 'Runtime runs in one US region; remove target or use "us".');
|
|
41
|
+
}
|
|
37
42
|
/** Test hook: forget the cached clients. */
|
|
38
43
|
export function resetClients() {
|
|
39
44
|
clients.clear();
|
|
@@ -62,6 +62,9 @@ export type CreateSandboxBaseParams = {
|
|
|
62
62
|
spot?: boolean;
|
|
63
63
|
linkedSandbox?: string;
|
|
64
64
|
secrets?: Record<string, string>;
|
|
65
|
+
/** The region for this create, over the client's target: "us" (Runtime's
|
|
66
|
+
* one region) or none; any other is refused. */
|
|
67
|
+
target?: string;
|
|
65
68
|
/** Runtime-only: an explicit client, or fields for this create. */
|
|
66
69
|
withruntime?: WithRuntime;
|
|
67
70
|
};
|
package/dist/daytona/daytona.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { clientFor, } from "./client.js";
|
|
1
|
+
import { clientFor, refuseTarget, } from "./client.js";
|
|
2
2
|
import { DaytonaError, DaytonaInvalidArgumentError, DaytonaNotFoundError, DaytonaQueueTimeoutError, guard, NotSupportedError, } from "./errors.js";
|
|
3
3
|
import { RuntimeError } from "../errors.js";
|
|
4
4
|
import { LEASE_MAX_SECONDS } from "../compat/lease.js";
|
|
@@ -433,6 +433,7 @@ function refuseCreate(params) {
|
|
|
433
433
|
gpuType !== "" &&
|
|
434
434
|
(!Array.isArray(gpuType) || gpuType.length > 0)))
|
|
435
435
|
throw new NotSupportedError("GPUs", "Runtime runs sandboxes on CPUs; remove gpu and gpuType from resources.");
|
|
436
|
+
refuseTarget(params.target);
|
|
436
437
|
if (params.volumes?.some((mount) => mount.subpath))
|
|
437
438
|
throw new NotSupportedError("Mounting part of a volume (subpath)", "Mount the whole volume.");
|
|
438
439
|
if (params.kvm)
|
package/dist/e2b/sandbox.d.ts
CHANGED
|
@@ -148,11 +148,12 @@ export declare class Sandbox {
|
|
|
148
148
|
* deleted. */
|
|
149
149
|
static kill(sandboxId: string, opts?: SandboxApiOpts): Promise<boolean>;
|
|
150
150
|
kill(opts?: Pick<ConnectionOpts, "requestTimeoutMs" | "signal">): Promise<boolean>;
|
|
151
|
-
/** Sets the sandbox to end `timeoutMs` from now, up to 24
|
|
152
|
-
* does
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
151
|
+
/** Sets the sandbox to end `timeoutMs` from now, sooner or later, up to 24
|
|
152
|
+
* hours, as E2B does: the server keeps the new time limit (an update's
|
|
153
|
+
* timeoutSeconds), and what the end does stays as created. Under a minute
|
|
154
|
+
* is a minute, Runtime's shortest limit. A sandbox with no time limit gets
|
|
155
|
+
* one; a persistent one (a pilot's, kept running by the server) has none to
|
|
156
|
+
* set, and nothing changes. */
|
|
156
157
|
static setTimeout(sandboxId: string, timeoutMs: number, opts?: SandboxApiOpts): Promise<void>;
|
|
157
158
|
setTimeout(timeoutMs: number, opts?: Pick<ConnectionOpts, "requestTimeoutMs" | "signal">): Promise<void>;
|
|
158
159
|
static getInfo(sandboxId: string, opts?: SandboxApiOpts): Promise<SandboxInfo>;
|
package/dist/e2b/sandbox.js
CHANGED
|
@@ -414,11 +414,12 @@ export class Sandbox {
|
|
|
414
414
|
throw error;
|
|
415
415
|
}
|
|
416
416
|
}
|
|
417
|
-
/** Sets the sandbox to end `timeoutMs` from now, up to 24
|
|
418
|
-
* does
|
|
419
|
-
*
|
|
420
|
-
*
|
|
421
|
-
*
|
|
417
|
+
/** Sets the sandbox to end `timeoutMs` from now, sooner or later, up to 24
|
|
418
|
+
* hours, as E2B does: the server keeps the new time limit (an update's
|
|
419
|
+
* timeoutSeconds), and what the end does stays as created. Under a minute
|
|
420
|
+
* is a minute, Runtime's shortest limit. A sandbox with no time limit gets
|
|
421
|
+
* one; a persistent one (a pilot's, kept running by the server) has none to
|
|
422
|
+
* set, and nothing changes. */
|
|
422
423
|
static async setTimeout(sandboxId, timeoutMs, opts = {}) {
|
|
423
424
|
const client = clientFor(opts);
|
|
424
425
|
const runtime = await guard("sandbox", () => client.sandboxes.get(sandboxId, request(opts)));
|
|
@@ -617,14 +618,10 @@ function secondsLater(runtime, timeoutMs) {
|
|
|
617
618
|
return (Date.now() + timeoutMs - end) / 1000;
|
|
618
619
|
}
|
|
619
620
|
async function extendTo(runtime, timeoutMs, opts) {
|
|
620
|
-
|
|
621
|
-
if (runtime.info.
|
|
621
|
+
const seconds = timeoutSeconds(timeoutMs);
|
|
622
|
+
if (runtime.info.persistent === true)
|
|
622
623
|
return;
|
|
623
|
-
|
|
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)));
|
|
624
|
+
await guard("sandbox", () => runtime.update({ timeoutSeconds: seconds }, request(opts)));
|
|
628
625
|
}
|
|
629
626
|
async function pause(runtime, opts) {
|
|
630
627
|
if (snapshotMemory("mode", opts.mode, opts.keepMemory) === false)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Runtime } from "../client.js";
|
|
2
|
-
import type { Sandbox
|
|
2
|
+
import type { Sandbox } from "../sandbox.js";
|
|
3
3
|
import type { Snapshot } from "../snapshots.js";
|
|
4
4
|
import { CompatibilityError, type ClientOptions, type Refused, type RefusedAsync } from "../compat/core.js";
|
|
5
5
|
export { CompatibilityError };
|
|
@@ -341,7 +341,30 @@ export interface PtyAttachOptions {
|
|
|
341
341
|
session: PtySessionSelector;
|
|
342
342
|
linuxUser?: string;
|
|
343
343
|
}
|
|
344
|
+
export type PtyConnectionState = "connecting" | "connected" | "reconnecting" | "closed" | "exited";
|
|
345
|
+
export interface PtyReconnectOptions {
|
|
346
|
+
/** Total time to recover one dropped connection. Default: five minutes. */
|
|
347
|
+
timeoutMs?: number;
|
|
348
|
+
/** Initial backoff, with 30% jitter. Default: 400ms. */
|
|
349
|
+
initialDelayMs?: number;
|
|
350
|
+
/** Backoff ceiling before jitter. Default: 10 seconds. */
|
|
351
|
+
maxDelayMs?: number;
|
|
352
|
+
}
|
|
344
353
|
export interface PtySessionEvents {
|
|
354
|
+
/** Reattach by itself after a dropped connection (Freestyle 0.2.18). */
|
|
355
|
+
reconnect?: boolean | PtyReconnectOptions;
|
|
356
|
+
/** Longest wait for each connection's terminal. Recovery waits at most 15
|
|
357
|
+
* seconds by default; a first connection, only when this is given. */
|
|
358
|
+
connectTimeoutMs?: number;
|
|
359
|
+
onStateChange?: (state: PtyConnectionState) => void;
|
|
360
|
+
/** Freestyle's exact replay boundaries. A Runtime terminal replays the
|
|
361
|
+
* session's output from its start on each connection without marking where
|
|
362
|
+
* the replay ends, as Freestyle's older guest agents do, so these never
|
|
363
|
+
* fire: the replay begins at the "connected" state. */
|
|
364
|
+
onReplayStart?: (info: {
|
|
365
|
+
exact: boolean;
|
|
366
|
+
}) => void;
|
|
367
|
+
onReplayEnd?: () => void;
|
|
345
368
|
onData?: (data: Uint8Array) => void;
|
|
346
369
|
onExit?: (exitCode: number) => void;
|
|
347
370
|
onClose?: (info: {
|
|
@@ -353,25 +376,37 @@ export interface PtySessionEvents {
|
|
|
353
376
|
}
|
|
354
377
|
/** A session's number: FNV-1a of its process id, 31 bits, never 0. */
|
|
355
378
|
export declare function sessionNumber(processId: string): number;
|
|
379
|
+
/** A stable handle across reattachments; input is never retried or buffered.
|
|
380
|
+
* Each connection is a Runtime terminal attached to the session's process. */
|
|
356
381
|
export declare class PtySession {
|
|
357
382
|
#private;
|
|
358
|
-
private readonly terminal;
|
|
359
383
|
private readonly sandbox;
|
|
360
384
|
private readonly processId;
|
|
361
385
|
readonly sessionId: number;
|
|
362
386
|
readonly slug: string | undefined;
|
|
363
387
|
readonly created: boolean;
|
|
364
|
-
|
|
365
|
-
|
|
388
|
+
private readonly events;
|
|
389
|
+
/** Resolves once the first connection is up. */
|
|
390
|
+
readonly ready: Promise<void>;
|
|
391
|
+
constructor(sandbox: Sandbox, processId: string, sessionId: number, slug: string | undefined, created: boolean, events: PtySessionEvents, dimensions?: {
|
|
392
|
+
cols: number;
|
|
393
|
+
rows: number;
|
|
394
|
+
});
|
|
395
|
+
/** Retry now after a network change or a spent retry budget. Never opens a new shell. */
|
|
396
|
+
reconnect(): void;
|
|
397
|
+
/** Send input once. Throws while disconnected; uncertain keystrokes are never replayed. */
|
|
366
398
|
write(data: Uint8Array | string): void;
|
|
399
|
+
/** Remembers the size and applies it again after a reattachment. */
|
|
367
400
|
resize(options: {
|
|
368
401
|
cols: number;
|
|
369
402
|
rows: number;
|
|
370
403
|
}): void;
|
|
371
404
|
signal(sig: "sigint" | "sigkill"): void;
|
|
372
|
-
/**
|
|
405
|
+
/** Cancels recovery and closes the connection; the shell keeps running and
|
|
406
|
+
* attach() finds it. */
|
|
373
407
|
detach(): void;
|
|
374
408
|
get readyState(): number;
|
|
409
|
+
get state(): PtyConnectionState;
|
|
375
410
|
}
|
|
376
411
|
export declare class VmPty {
|
|
377
412
|
private readonly get;
|
|
@@ -414,6 +449,43 @@ export declare class VmLinuxUserPty {
|
|
|
414
449
|
exitCode?: undefined;
|
|
415
450
|
}>;
|
|
416
451
|
}
|
|
452
|
+
interface XtermDisposable {
|
|
453
|
+
dispose(): void;
|
|
454
|
+
}
|
|
455
|
+
interface XtermSequence {
|
|
456
|
+
prefix?: string;
|
|
457
|
+
intermediates?: string;
|
|
458
|
+
final: string;
|
|
459
|
+
}
|
|
460
|
+
/** The xterm.js surface connectXterm uses; xterm itself is not a dependency. */
|
|
461
|
+
export interface XtermTerminal {
|
|
462
|
+
cols: number;
|
|
463
|
+
rows: number;
|
|
464
|
+
write(data: string | Uint8Array, callback?: () => void): void;
|
|
465
|
+
onData(listener: (data: string) => void): XtermDisposable;
|
|
466
|
+
onResize(listener: (size: {
|
|
467
|
+
cols: number;
|
|
468
|
+
rows: number;
|
|
469
|
+
}) => void): XtermDisposable;
|
|
470
|
+
parser: {
|
|
471
|
+
registerCsiHandler(id: XtermSequence, handler: () => boolean): XtermDisposable;
|
|
472
|
+
registerDcsHandler(id: XtermSequence, handler: () => boolean): XtermDisposable;
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
export type ConnectXtermOptions = PtyOpenOptions & PtySessionEvents & {
|
|
476
|
+
pty: VmPty | VmLinuxUserPty;
|
|
477
|
+
terminal: XtermTerminal;
|
|
478
|
+
slug: string;
|
|
479
|
+
};
|
|
480
|
+
/** Freestyle 0.2.18's connectXterm: xterm.js on a named session that
|
|
481
|
+
* reattaches by itself unless `reconnect: false`. Each connection replays the
|
|
482
|
+
* session's output from its start, so the screen is reset first and the
|
|
483
|
+
* replay's terminal queries go unanswered until it has been written (150ms
|
|
484
|
+
* quiet, at most 2 seconds). dispose() detaches; the shell keeps running. */
|
|
485
|
+
export declare function connectXterm({ pty, terminal, ...options }: ConnectXtermOptions): Promise<{
|
|
486
|
+
session: PtySession;
|
|
487
|
+
dispose(): void;
|
|
488
|
+
}>;
|
|
417
489
|
export declare class VmLinuxUser {
|
|
418
490
|
private readonly vm;
|
|
419
491
|
private readonly linuxUser;
|