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.
Files changed (176) hide show
  1. package/BLAXEL.md +3 -3
  2. package/COMPATIBILITY.md +74 -9
  3. package/DAYTONA.md +33 -5
  4. package/E2B.md +137 -48
  5. package/README.md +11 -9
  6. package/README.src.md +11 -9
  7. package/VERCEL.md +43 -5
  8. package/compatibility-lock.json +58 -49
  9. package/dist/billing-cli.d.ts +1 -1
  10. package/dist/billing-cli.js +77 -1
  11. package/dist/blaxel/errors.d.ts +14 -0
  12. package/dist/blaxel/errors.js +33 -4
  13. package/dist/blaxel/index.d.ts +2 -2
  14. package/dist/blaxel/index.js +4 -4
  15. package/dist/blaxel/interpreter.d.ts +2 -5
  16. package/dist/blaxel/sandbox.d.ts +15 -7
  17. package/dist/blaxel/sandbox.js +64 -9
  18. package/dist/blaxel/snapshot.d.ts +4 -0
  19. package/dist/blaxel/snapshot.js +4 -0
  20. package/dist/cli/logs.d.ts +21 -0
  21. package/dist/cli/logs.js +188 -0
  22. package/dist/cli/logs.test.d.ts +1 -0
  23. package/dist/cli/logs.test.js +272 -0
  24. package/dist/cli-extras.d.ts +3 -3
  25. package/dist/cli-extras.js +17 -12
  26. package/dist/cli-words.d.ts +10 -1
  27. package/dist/cli-words.js +18 -0
  28. package/dist/cli.d.ts +6 -2
  29. package/dist/cli.js +468 -219
  30. package/dist/client.d.ts +13 -63
  31. package/dist/client.js +35 -124
  32. package/dist/cloudflare/errors.d.ts +143 -0
  33. package/dist/cloudflare/errors.js +167 -0
  34. package/dist/cloudflare/index.d.ts +175 -6
  35. package/dist/cloudflare/index.js +416 -26
  36. package/dist/cloudflare/interpreter.d.ts +119 -0
  37. package/dist/cloudflare/interpreter.js +223 -0
  38. package/dist/cloudflare/streams.d.ts +38 -0
  39. package/dist/cloudflare/streams.js +145 -0
  40. package/dist/cloudflare/watch.d.ts +9 -0
  41. package/dist/cloudflare/watch.js +24 -0
  42. package/dist/codesandbox/index.d.ts +216 -28
  43. package/dist/codesandbox/index.js +478 -78
  44. package/dist/compat/core.d.ts +18 -0
  45. package/dist/compat/core.js +29 -0
  46. package/dist/compat/files.d.ts +1 -0
  47. package/dist/compat/files.js +25 -5
  48. package/dist/daytona/computer-use.d.ts +13 -4
  49. package/dist/daytona/computer-use.js +66 -8
  50. package/dist/daytona/daytona.d.ts +7 -2
  51. package/dist/daytona/daytona.js +31 -5
  52. package/dist/daytona/errors.d.ts +11 -0
  53. package/dist/daytona/errors.js +18 -0
  54. package/dist/daytona/filesystem.js +2 -1
  55. package/dist/daytona/image.d.ts +13 -9
  56. package/dist/daytona/image.js +18 -12
  57. package/dist/daytona/index.d.ts +1 -1
  58. package/dist/daytona/sandbox.d.ts +9 -3
  59. package/dist/daytona/sandbox.js +17 -3
  60. package/dist/domains/index.d.ts +9 -0
  61. package/dist/domains/index.js +10 -0
  62. package/dist/e2b/client.d.ts +15 -2
  63. package/dist/e2b/client.js +61 -7
  64. package/dist/e2b/commands.d.ts +4 -1
  65. package/dist/e2b/commands.js +17 -7
  66. package/dist/e2b/errors.d.ts +43 -2
  67. package/dist/e2b/errors.js +78 -6
  68. package/dist/e2b/filesystem.d.ts +1 -1
  69. package/dist/e2b/filesystem.js +82 -16
  70. package/dist/e2b/index.d.ts +3 -3
  71. package/dist/e2b/index.js +2 -2
  72. package/dist/e2b/sandbox.d.ts +50 -35
  73. package/dist/e2b/sandbox.js +228 -185
  74. package/dist/e2b/unsupported.d.ts +19 -0
  75. package/dist/e2b/unsupported.js +22 -1
  76. package/dist/e2b/users.d.ts +9 -0
  77. package/dist/e2b/users.js +22 -0
  78. package/dist/errors.d.ts +14 -5
  79. package/dist/errors.js +28 -14
  80. package/dist/freestyle/index.d.ts +285 -37
  81. package/dist/freestyle/index.js +656 -144
  82. package/dist/identity.d.ts +3 -0
  83. package/dist/identity.js +27 -0
  84. package/dist/images/index.d.ts +9 -0
  85. package/dist/images/index.js +10 -0
  86. package/dist/index.d.ts +1 -0
  87. package/dist/ips/index.d.ts +9 -0
  88. package/dist/ips/index.js +10 -0
  89. package/dist/jobs/index.d.ts +9 -0
  90. package/dist/jobs/index.js +10 -0
  91. package/dist/keys.js +2 -0
  92. package/dist/login.js +2 -0
  93. package/dist/machine.d.ts +189 -0
  94. package/dist/machine.js +521 -0
  95. package/dist/mcp.js +1 -1
  96. package/dist/modal/index.d.ts +339 -61
  97. package/dist/modal/index.js +781 -121
  98. package/dist/network-cli.d.ts +3 -1
  99. package/dist/network-cli.js +78 -36
  100. package/dist/networks/index.d.ts +11 -0
  101. package/dist/networks/index.js +10 -0
  102. package/dist/openai-agents/index.d.ts +1 -1
  103. package/dist/openai-agents/index.js +1 -1
  104. package/dist/ports/index.d.ts +9 -0
  105. package/dist/ports/index.js +10 -0
  106. package/dist/product-names.d.ts +52 -0
  107. package/dist/product-names.js +164 -0
  108. package/dist/product-renames.d.ts +103 -0
  109. package/dist/product-renames.js +189 -0
  110. package/dist/products/apps.d.ts +176 -0
  111. package/dist/products/apps.js +165 -0
  112. package/dist/products/billing.d.ts +41 -0
  113. package/dist/products/billing.js +13 -0
  114. package/dist/products/browser.d.ts +10 -0
  115. package/dist/products/browsers.d.ts +71 -0
  116. package/dist/products/browsers.js +102 -0
  117. package/dist/products/builds.d.ts +126 -0
  118. package/dist/products/builds.js +221 -0
  119. package/dist/products/index.d.ts +3 -16
  120. package/dist/products/index.js +3 -14
  121. package/dist/products/jobs.d.ts +19 -3
  122. package/dist/products/jobs.js +28 -12
  123. package/dist/products/limits.d.ts +4 -10
  124. package/dist/products/logs.d.ts +245 -0
  125. package/dist/products/logs.js +274 -0
  126. package/dist/products/logs.test.d.ts +1 -0
  127. package/dist/products/logs.test.js +343 -0
  128. package/dist/products/network-products.d.ts +38 -7
  129. package/dist/products/network-products.js +31 -18
  130. package/dist/products/network.d.ts +1 -1
  131. package/dist/products/observability.d.ts +3 -2
  132. package/dist/products/private-network.js +1 -1
  133. package/dist/products/queues.d.ts +149 -0
  134. package/dist/products/queues.js +213 -0
  135. package/dist/products/referrals.d.ts +21 -7
  136. package/dist/products/referrals.js +3 -3
  137. package/dist/products/secrets.d.ts +8 -4
  138. package/dist/products/secrets.js +39 -18
  139. package/dist/products/signed-requests.d.ts +21 -0
  140. package/dist/products/signed-requests.js +11 -0
  141. package/dist/products/streams.d.ts +73 -0
  142. package/dist/products/streams.js +17 -0
  143. package/dist/products/switching.d.ts +37 -10
  144. package/dist/products/switching.js +4 -3
  145. package/dist/products/volumes.d.ts +3 -0
  146. package/dist/record-cli.d.ts +23 -0
  147. package/dist/record-cli.js +308 -0
  148. package/dist/renamed-products.d.ts +38 -0
  149. package/dist/renamed-products.js +34 -0
  150. package/dist/runloop/index.d.ts +9 -2
  151. package/dist/runloop/index.js +29 -8
  152. package/dist/runloop/sdk.d.ts +131 -0
  153. package/dist/runloop/sdk.js +167 -0
  154. package/dist/sandbox.d.ts +10 -94
  155. package/dist/sandbox.js +22 -371
  156. package/dist/sandboxes/index.d.ts +11 -0
  157. package/dist/sandboxes/index.js +12 -0
  158. package/dist/snapshots/index.d.ts +9 -0
  159. package/dist/snapshots/index.js +11 -0
  160. package/dist/snapshots.d.ts +7 -4
  161. package/dist/sprites/index.d.ts +343 -31
  162. package/dist/sprites/index.js +866 -84
  163. package/dist/ssh.d.ts +1 -1
  164. package/dist/ssh.js +11 -11
  165. package/dist/terminal.d.ts +24 -0
  166. package/dist/terminal.js +32 -0
  167. package/dist/transport.d.ts +18 -2
  168. package/dist/transport.js +61 -6
  169. package/dist/tunnel.js +1 -1
  170. package/dist/types.d.ts +116 -8
  171. package/dist/vercel/sandbox.d.ts +1 -2
  172. package/dist/vercel/sandbox.js +6 -5
  173. package/dist/vercel/snapshot.js +10 -2
  174. package/dist/volumes/index.d.ts +9 -0
  175. package/dist/volumes/index.js +10 -0
  176. package/package.json +48 -2
@@ -0,0 +1,9 @@
1
+ /** `withruntime/domains`: Domains alone. Your own hostname for a sandbox's port, with HTTPS.
2
+ *
3
+ * import { domains } from "withruntime/domains";
4
+ * const client = domains(); // RUNTIME_API_KEY, or this machine's saved connection
5
+ */
6
+ import { Runtime, type RuntimeOptions } from "../client.js";
7
+ export type { Domain, CheckedDomain, DnsRecord } from "../products/network-products.js";
8
+ /** `runtime.domains` of a client made with `options`. */
9
+ export declare function domains(options?: RuntimeOptions): Runtime["domains"];
@@ -0,0 +1,10 @@
1
+ /** `withruntime/domains`: Domains alone. Your own hostname for a sandbox's port, with HTTPS.
2
+ *
3
+ * import { domains } from "withruntime/domains";
4
+ * const client = domains(); // RUNTIME_API_KEY, or this machine's saved connection
5
+ */
6
+ import { Runtime } from "../client.js";
7
+ /** `runtime.domains` of a client made with `options`. */
8
+ export function domains(options) {
9
+ return new Runtime(options).domains;
10
+ }
@@ -43,15 +43,28 @@ export interface RuntimeOpts {
43
43
  /** Refuses the E2B-only connection options: they point at E2B, and quietly
44
44
  * dropping one would send the call somewhere its author did not mean. */
45
45
  export declare function refuseE2BConnection(opts?: ConnectionOpts): void;
46
+ /** Connections the adapter's own client may hold at once: E2B's SDK has no
47
+ * cap of its own, so an orchestrator that drives a thousand sandboxes from one
48
+ * process runs every command at once. 4,096 is the widest room Runtime's edge
49
+ * gives an address that has used a valid key (api.md, "Limits"); the edge and
50
+ * the API answer anything past an account's room with a retry, which the
51
+ * client waits out. Runtime's own SDK keeps 48. */
52
+ export declare const E2B_MAX_CONNECTIONS = 4096;
53
+ /** Said once per process, the first time a call has to wait for a
54
+ * connection: E2B never holds a call back, so a caller should hear it. */
55
+ export declare function warnQueued(maxConnections: number): void;
46
56
  /** The Runtime client for these options: an explicit client, else one per key,
47
57
  * made once and reused so its connections stay open. */
48
58
  export declare function clientFor(opts?: ConnectionOpts & {
49
59
  runtime?: RuntimeOpts;
50
60
  }): Runtime;
51
61
  /** Explicit key, then RUNTIME_API_KEY, then E2B_API_KEY when it holds a
52
- * Runtime key; undefined lets the Runtime SDK find the saved login. */
62
+ * Runtime key; undefined, when none is set, lets the Runtime SDK find the
63
+ * saved login. An E2B key is refused, never sent. */
53
64
  export declare function pickKey(explicit?: string): string | undefined;
54
- /** Test hook: forget the cached clients. */
65
+ /** Where the key in use came from, as an error names it. */
66
+ export declare function keySource(explicit?: string): string;
67
+ /** Test hook: forget the cached clients and the warnings. */
55
68
  export declare function resetClients(): void;
56
69
  /** Request options for a Runtime call from E2B's. */
57
70
  export declare function request(opts?: {
@@ -26,6 +26,23 @@ export function refuseE2BConnection(opts = {}) {
26
26
  throw new NotSupportedError("E2B's debug mode (a local envd)", "Remove debug: true; there is no local Runtime guest to connect to.");
27
27
  }
28
28
  const clients = new Map();
29
+ /** Connections the adapter's own client may hold at once: E2B's SDK has no
30
+ * cap of its own, so an orchestrator that drives a thousand sandboxes from one
31
+ * process runs every command at once. 4,096 is the widest room Runtime's edge
32
+ * gives an address that has used a valid key (api.md, "Limits"); the edge and
33
+ * the API answer anything past an account's room with a retry, which the
34
+ * client waits out. Runtime's own SDK keeps 48. */
35
+ export const E2B_MAX_CONNECTIONS = 4096;
36
+ let warnedQueued = false;
37
+ /** Said once per process, the first time a call has to wait for a
38
+ * connection: E2B never holds a call back, so a caller should hear it. */
39
+ export function warnQueued(maxConnections) {
40
+ if (warnedQueued || typeof process === "undefined" || !process.emitWarning)
41
+ return;
42
+ warnedQueued = true;
43
+ process.emitWarning(`More than ${maxConnections} calls and command streams are open at once from this process, so the rest wait their turn. ` +
44
+ "Pass runtime: { client: new Runtime({ maxConnections }) } for more.", { code: "RUNTIME_E2B_QUEUED" });
45
+ }
29
46
  /** The Runtime client for these options: an explicit client, else one per key,
30
47
  * made once and reused so its connections stay open. */
31
48
  export function clientFor(opts = {}) {
@@ -36,30 +53,67 @@ export function clientFor(opts = {}) {
36
53
  const cacheKey = key ?? "";
37
54
  let client = clients.get(cacheKey);
38
55
  if (!client) {
39
- client = new Runtime(key ? { apiKey: key } : {});
56
+ client = new Runtime({
57
+ ...(key ? { apiKey: key } : {}),
58
+ maxConnections: E2B_MAX_CONNECTIONS,
59
+ onQueued: warnQueued,
60
+ });
40
61
  clients.set(cacheKey, client);
41
62
  }
42
63
  return client;
43
64
  }
44
65
  /** Explicit key, then RUNTIME_API_KEY, then E2B_API_KEY when it holds a
45
- * Runtime key; undefined lets the Runtime SDK find the saved login. */
66
+ * Runtime key; undefined, when none is set, lets the Runtime SDK find the
67
+ * saved login. An E2B key is refused, never sent. */
46
68
  export function pickKey(explicit) {
47
69
  const runtimeKey = env("RUNTIME_API_KEY");
48
70
  if (explicit !== undefined && !isE2BKey(explicit))
49
71
  return explicit;
50
- if (runtimeKey)
72
+ if (runtimeKey) {
73
+ warnTwoKeys(runtimeKey, env("E2B_API_KEY"));
51
74
  return runtimeKey;
75
+ }
52
76
  if (explicit !== undefined)
53
77
  throw new AuthenticationError("The apiKey passed is an E2B API key (e2b_...), and it was not sent. Set RUNTIME_API_KEY to a Runtime key " +
54
78
  "(https://withruntime.com/account/keys, or run `npx withruntime login`), or pass the Runtime key as apiKey.");
55
- // An E2B key left in E2B_API_KEY is skipped, never sent: the Runtime SDK
56
- // then uses the key `npx withruntime login` saved, or says none was found.
79
+ // An E2B key left in E2B_API_KEY is never sent, and never quietly passed
80
+ // over for the key `npx withruntime login` saved, which may be another
81
+ // account's.
57
82
  const e2bVariable = env("E2B_API_KEY");
58
- return e2bVariable && !isE2BKey(e2bVariable) ? e2bVariable : undefined;
83
+ if (e2bVariable && isE2BKey(e2bVariable))
84
+ throw new AuthenticationError("E2B_API_KEY holds an E2B API key (e2b_...), and it was not sent. Set RUNTIME_API_KEY to a Runtime key " +
85
+ "(https://withruntime.com/account/keys, or run `npx withruntime login`), or put the Runtime key in E2B_API_KEY.");
86
+ return e2bVariable || undefined;
87
+ }
88
+ /** Where the key in use came from, as an error names it. */
89
+ export function keySource(explicit) {
90
+ if (explicit !== undefined && !isE2BKey(explicit))
91
+ return "apiKey";
92
+ if (env("RUNTIME_API_KEY"))
93
+ return "RUNTIME_API_KEY";
94
+ if (env("E2B_API_KEY"))
95
+ return "E2B_API_KEY";
96
+ return "the saved login";
97
+ }
98
+ let warnedTwoKeys = false;
99
+ /** Said once per process: RUNTIME_API_KEY wins, as in Runtime's own SDK, but
100
+ * code written for E2B means E2B_API_KEY, so two different Runtime keys are
101
+ * likely two accounts, and the wrong one fails later as a missing image
102
+ * (5 October 2026). */
103
+ function warnTwoKeys(runtimeKey, e2bVariable) {
104
+ if (!e2bVariable || isE2BKey(e2bVariable) || e2bVariable === runtimeKey)
105
+ return;
106
+ if (warnedTwoKeys || typeof process === "undefined" || !process.emitWarning)
107
+ return;
108
+ warnedTwoKeys = true;
109
+ process.emitWarning("RUNTIME_API_KEY and E2B_API_KEY hold different Runtime keys, which may be different accounts; this package uses " +
110
+ "RUNTIME_API_KEY. Unset one, or set both to the same key.", { code: "RUNTIME_E2B_TWO_KEYS" });
59
111
  }
60
- /** Test hook: forget the cached clients. */
112
+ /** Test hook: forget the cached clients and the warnings. */
61
113
  export function resetClients() {
62
114
  clients.clear();
115
+ warnedQueued = false;
116
+ warnedTwoKeys = false;
63
117
  }
64
118
  /** Request options for a Runtime call from E2B's. */
65
119
  export function request(opts = {}) {
@@ -1,6 +1,6 @@
1
1
  import type { Process } from "../sandbox.js";
2
2
  import type { OutputEvent, ProcessInfo as RuntimeProcessInfo } from "../types.js";
3
- import type { RequestOptions } from "../transport.js";
3
+ import { type RequestOptions } from "../transport.js";
4
4
  import type { RuntimeSandbox } from "./client.js";
5
5
  import { TimeoutError, type CommandResult } from "./errors.js";
6
6
  export type { CommandResult };
@@ -43,6 +43,9 @@ export interface SandboxContext {
43
43
  /** Makes /home/user (E2B's home) lead to /workspace (Runtime's), once, when
44
44
  * something names it. */
45
45
  ensureHome(text: string | undefined, options?: RequestOptions): Promise<void>;
46
+ /** Whether /home/user leads to /workspace, linking it first if nothing is
47
+ * there; false when that cannot be told. Never fails. */
48
+ homeIsWorkspace(): Promise<boolean>;
46
49
  }
47
50
  /** E2B's pids are numbers; Runtime's process ids are strings. The number is a
48
51
  * 31-bit FNV-1a hash of the id, so every client derives the same one. */
@@ -1,5 +1,6 @@
1
- import { CommandExitError, guard, InvalidArgumentError, SandboxError, TimeoutError, translate, } from "./errors.js";
2
- import { commandAs, runAs } from "./users.js";
1
+ import { timeoutSignal } from "../transport.js";
2
+ import { CommandExitError, guard, InvalidArgumentError, SandboxError, SandboxNotFoundError, SandboxNotRunningError, TimeoutError, translate, } from "./errors.js";
3
+ import { commandAs, listedAs, runAs } from "./users.js";
3
4
  const DEFAULT_TIMEOUT_MS = 60_000;
4
5
  /** How long a command may run on Runtime: a day, E2B's longest sandbox. E2B's
5
6
  * own `timeoutMs` bounds only the connection, so the process gets this. */
@@ -25,7 +26,7 @@ export function timeoutFor(timeoutMs) {
25
26
  export function commandRequest(opts, defaultTimeoutMs = 60_000) {
26
27
  opts.signal?.throwIfAborted();
27
28
  const timeoutMs = timeoutFor(opts.requestTimeoutMs ?? defaultTimeoutMs);
28
- const deadline = timeoutMs > 0 ? AbortSignal.timeout(timeoutMs) : undefined;
29
+ const deadline = timeoutMs > 0 ? timeoutSignal(timeoutMs) : undefined;
29
30
  const signal = opts.signal && deadline ? AbortSignal.any([opts.signal, deadline]) : (opts.signal ?? deadline);
30
31
  return { timeoutMs: 0, ...(signal ? { signal } : {}) };
31
32
  }
@@ -85,7 +86,7 @@ function toResult(result) {
85
86
  export function connectionRequest(opts, defaultTimeoutMs) {
86
87
  const timeoutMs = timeoutFor(opts.timeoutMs);
87
88
  const deadline = timeoutMs > 0 ? performance.now() + timeoutMs : undefined;
88
- const connectionTimeout = timeoutMs > 0 ? AbortSignal.timeout(timeoutMs) : undefined;
89
+ const connectionTimeout = timeoutMs > 0 ? timeoutSignal(timeoutMs) : undefined;
89
90
  const signal = connectionTimeout && opts.signal
90
91
  ? AbortSignal.any([connectionTimeout, opts.signal])
91
92
  : (connectionTimeout ?? opts.signal);
@@ -238,12 +239,17 @@ export class Commands {
238
239
  function describe(info) {
239
240
  return {
240
241
  pid: pidOf(info.id),
241
- cmd: "/bin/bash",
242
- args: ["-c", info.command],
242
+ ...listedAs(info.command),
243
243
  envs: {},
244
244
  cwd: info.cwd,
245
245
  };
246
246
  }
247
+ /** E2B 2.53's confirmed sandbox loss remains catchable as TimeoutError. */
248
+ function sandboxEnded(cause) {
249
+ const error = new SandboxNotRunningError("The command ended before the stream completed: the sandbox was killed or reached its end of life.");
250
+ error.cause = cause;
251
+ return error;
252
+ }
247
253
  /** Reader cancellation also releases a callback whose promise never settles. */
248
254
  async function deliver(signal, callback) {
249
255
  signal.throwIfAborted();
@@ -349,7 +355,11 @@ export class CommandHandle {
349
355
  if (!this.#disconnected && !this.#failure) {
350
356
  const translated = translate(error, "sandbox");
351
357
  this.#failure =
352
- translated instanceof Error ? translated : new SandboxError(String(translated));
358
+ translated instanceof SandboxNotFoundError
359
+ ? sandboxEnded(translated)
360
+ : translated instanceof Error
361
+ ? translated
362
+ : new SandboxError(String(translated));
353
363
  }
354
364
  }
355
365
  finally {
@@ -8,6 +8,14 @@ export declare class SandboxError extends Error {
8
8
  }
9
9
  export declare class TimeoutError extends SandboxError {
10
10
  }
11
+ /** The sandbox is confirmed stopped or paused; create or resume one. */
12
+ export declare class SandboxNotRunningError extends TimeoutError {
13
+ constructor(message: string, options?: ErrorOptions);
14
+ }
15
+ /** The sandbox cannot be reached, without confirmation that it stopped. */
16
+ export declare class SandboxUnreachableError extends TimeoutError {
17
+ constructor(message: string, options?: ErrorOptions);
18
+ }
11
19
  export declare class InvalidArgumentError extends SandboxError {
12
20
  }
13
21
  export declare class NotEnoughSpaceError extends SandboxError {
@@ -37,6 +45,40 @@ export declare class ServiceBusyError extends Error {
37
45
  requestId?: string;
38
46
  constructor(message: string);
39
47
  }
48
+ /** E2B's other error classes, by the same names and parents, so a module
49
+ * that imports or catches them loads and works: nothing in this package
50
+ * throws them, since the features behind them (template builds, git,
51
+ * volumes, secrets) answer NotSupportedError here. */
52
+ export declare class GitAuthError extends AuthenticationError {
53
+ }
54
+ export declare class GitUpstreamError extends SandboxError {
55
+ }
56
+ export declare class BuildError extends Error {
57
+ constructor(message?: string);
58
+ }
59
+ export declare class FileUploadError extends BuildError {
60
+ }
61
+ export declare class VolumeError extends Error {
62
+ constructor(message?: string);
63
+ }
64
+ export declare class VolumeNotFoundError extends VolumeError {
65
+ }
66
+ export declare class VolumePathNotFoundError extends VolumeError {
67
+ }
68
+ export declare class SecretError extends Error {
69
+ constructor(message?: string);
70
+ }
71
+ export declare class SecretNotFoundError extends SecretError {
72
+ }
73
+ /** E2B 2.55's Dockerfile parse error, from `@e2b/dockerfile-utils`: a plain
74
+ * Error, not a SandboxError, with the line it names. Runtime parses no
75
+ * Dockerfile here (templates are Runtime images, built by
76
+ * `npx withruntime images build`), so nothing throws it; it exists so that
77
+ * code importing it or catching it still loads. */
78
+ export declare class DockerfileSyntaxError extends Error {
79
+ readonly line: number | undefined;
80
+ constructor(message: string, line?: number);
81
+ }
40
82
  /** A call that E2B supports and Runtime does not, or not in the same way.
41
83
  * Thrown before anything is done, never after doing something different.
42
84
  * `feature` names what was asked for; `alternative` says what to use on
@@ -46,8 +88,7 @@ export declare class NotSupportedError extends SandboxError {
46
88
  readonly alternative: string;
47
89
  constructor(feature: string, alternative: string, message?: string);
48
90
  }
49
- /** Runtime's addition, for `getHost` and `getPublicHost`: a free-trial
50
- * sandbox shares a port only privately, so a request needs the port's token,
91
+ /** Runtime's addition, for `getHost` and `getPublicHost`: a sandbox without credit shares a port only privately, so a request needs the port's token,
51
92
  * and a host name alone cannot carry one. The message says what works. A
52
93
  * NotSupportedError, so code that catches those catches this too. */
53
94
  export declare class PublicPreviewNotAllowedError extends NotSupportedError {
@@ -23,6 +23,24 @@ export class SandboxError extends Error {
23
23
  }
24
24
  export class TimeoutError extends SandboxError {
25
25
  }
26
+ /** The sandbox is confirmed stopped or paused; create or resume one. */
27
+ export class SandboxNotRunningError extends TimeoutError {
28
+ constructor(message, options) {
29
+ super(message);
30
+ this.name = "SandboxNotRunningError";
31
+ if (options?.cause !== undefined)
32
+ this.cause = options.cause;
33
+ }
34
+ }
35
+ /** The sandbox cannot be reached, without confirmation that it stopped. */
36
+ export class SandboxUnreachableError extends TimeoutError {
37
+ constructor(message, options) {
38
+ super(message);
39
+ this.name = "SandboxUnreachableError";
40
+ if (options?.cause !== undefined)
41
+ this.cause = options.cause;
42
+ }
43
+ }
26
44
  export class InvalidArgumentError extends SandboxError {
27
45
  }
28
46
  export class NotEnoughSpaceError extends SandboxError {
@@ -58,6 +76,55 @@ export class ServiceBusyError extends Error {
58
76
  this.name = new.target.name;
59
77
  }
60
78
  }
79
+ /** E2B's other error classes, by the same names and parents, so a module
80
+ * that imports or catches them loads and works: nothing in this package
81
+ * throws them, since the features behind them (template builds, git,
82
+ * volumes, secrets) answer NotSupportedError here. */
83
+ export class GitAuthError extends AuthenticationError {
84
+ }
85
+ export class GitUpstreamError extends SandboxError {
86
+ }
87
+ export class BuildError extends Error {
88
+ constructor(message) {
89
+ super(message);
90
+ this.name = new.target.name;
91
+ }
92
+ }
93
+ export class FileUploadError extends BuildError {
94
+ }
95
+ export class VolumeError extends Error {
96
+ constructor(message) {
97
+ super(message);
98
+ this.name = new.target.name;
99
+ }
100
+ }
101
+ export class VolumeNotFoundError extends VolumeError {
102
+ }
103
+ export class VolumePathNotFoundError extends VolumeError {
104
+ }
105
+ export class SecretError extends Error {
106
+ constructor(message) {
107
+ super(message);
108
+ this.name = new.target.name;
109
+ }
110
+ }
111
+ export class SecretNotFoundError extends SecretError {
112
+ }
113
+ /** E2B 2.55's Dockerfile parse error, from `@e2b/dockerfile-utils`: a plain
114
+ * Error, not a SandboxError, with the line it names. Runtime parses no
115
+ * Dockerfile here (templates are Runtime images, built by
116
+ * `npx withruntime images build`), so nothing throws it; it exists so that
117
+ * code importing it or catching it still loads. */
118
+ export class DockerfileSyntaxError extends Error {
119
+ line;
120
+ constructor(message, line) {
121
+ super(line === undefined
122
+ ? `Dockerfile parse error: ${message}`
123
+ : `Dockerfile parse error on line ${line}: ${message}`);
124
+ this.line = line;
125
+ this.name = "DockerfileSyntaxError";
126
+ }
127
+ }
61
128
  /** A call that E2B supports and Runtime does not, or not in the same way.
62
129
  * Thrown before anything is done, never after doing something different.
63
130
  * `feature` names what was asked for; `alternative` says what to use on
@@ -72,19 +139,18 @@ export class NotSupportedError extends SandboxError {
72
139
  this.code = "not_supported";
73
140
  }
74
141
  }
75
- /** Runtime's addition, for `getHost` and `getPublicHost`: a free-trial
76
- * sandbox shares a port only privately, so a request needs the port's token,
142
+ /** Runtime's addition, for `getHost` and `getPublicHost`: a sandbox without credit shares a port only privately, so a request needs the port's token,
77
143
  * and a host name alone cannot carry one. The message says what works. A
78
144
  * NotSupportedError, so code that catches those catches this too. */
79
145
  export class PublicPreviewNotAllowedError extends NotSupportedError {
80
146
  constructor(sandboxId, port, message) {
81
147
  const where = port === undefined ? "<port>" : String(port);
82
- const alternative = `For an address that works on the trial, use (await sandbox.runtime.previews.create(${where})).urlWithToken: ` +
148
+ const alternative = `For an address that works without credit, use (await sandbox.runtime.previews.create(${where})).urlWithToken: ` +
83
149
  "it carries the token, in a browser, fetch or curl. For other paths on it, send the token as the " +
84
150
  "x-runtime-preview-token header or the runtime_preview_token query parameter. A public host needs a paid " +
85
151
  "sandbox, which is the account owner's decision.";
86
- super("A public address on a free-trial sandbox", alternative, message ??
87
- `Sandbox ${sandboxId} runs on the free trial, where a shared port is private: every request needs the ` +
152
+ super("A public address on a sandbox without credit", alternative, message ??
153
+ `Sandbox ${sandboxId} runs without credit, where a shared port is private: every request needs the ` +
88
154
  `port's token, and a host name alone cannot carry one. ${alternative}`);
89
155
  this.code = "public_preview_not_allowed";
90
156
  }
@@ -126,7 +192,13 @@ export function translate(error, subject = "other") {
126
192
  .filter(Boolean)
127
193
  .join("\n");
128
194
  let out;
129
- if (error.status === 503 && error.code.endsWith("_unavailable"))
195
+ // Runtime reports the sandbox state explicitly. A generic connection failure
196
+ // does not prove it stopped; never replay a command or write to classify it.
197
+ if (["sandbox_stopped", "sandbox_paused", "sandbox_not_running"].includes(error.code))
198
+ out = new SandboxNotRunningError(message);
199
+ else if (error.code === "sandbox_not_ready" || error.code === "host_unavailable")
200
+ out = new SandboxUnreachableError(message);
201
+ else if (error.status === 503 && error.code.endsWith("_unavailable"))
130
202
  // A product switched off on purpose (forks, previews): Runtime's own words
131
203
  // say what is off and what to use meanwhile.
132
204
  out = new NotSupportedError(error.code.replace(/_unavailable$/, ""), error.hint ?? "", message);
@@ -72,7 +72,7 @@ export interface FilesystemListOpts extends FilesystemRequestOpts {
72
72
  }
73
73
  /** `sandbox.files`: E2B's filesystem module over Runtime's files API. Relative
74
74
  * paths resolve against the home directory, as in E2B (Runtime's is
75
- * /workspace). */
75
+ * /workspace), and come back under /home/user, as E2B gives them. */
76
76
  export declare class Filesystem {
77
77
  #private;
78
78
  constructor(ctx: SandboxContext);
@@ -20,17 +20,51 @@ const HOME = "/workspace";
20
20
  function absolute(path) {
21
21
  return path.startsWith("/") ? path : `${HOME}/${path.replace(/^\.\//, "")}`;
22
22
  }
23
+ /** E2B's home, which E2B code expects in the paths it gets back. */
24
+ const E2B_HOME = "/home/user";
25
+ /** A path as E2B gives it back: one the caller asked for relative to the home
26
+ * is shown under /home/user, as E2B shows it, when /home/user leads to
27
+ * /workspace (`linked`), so it names the same file. A path the caller gave in
28
+ * full comes back as given. */
29
+ function shown(path, asked, linked) {
30
+ if (asked.startsWith("/") || !linked)
31
+ return path;
32
+ if (path === HOME)
33
+ return E2B_HOME;
34
+ return path.startsWith(`${HOME}/`) ? E2B_HOME + path.slice(HOME.length) : path;
35
+ }
23
36
  function refuse(opts) {
24
37
  if (opts.metadata && Object.keys(opts.metadata).length)
25
38
  throw new NotSupportedError("File metadata (user.e2b.* extended attributes)", "Keep the metadata beside the file, for example in a JSON file.");
26
39
  }
27
40
  const PERMISSIONS = ["---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"];
28
- function entryInfo(entry) {
41
+ /** The first letter of envd's permissions, Go's FileMode string. */
42
+ const KIND_LETTER = { directory: "d", symlink: "L" };
43
+ /** The sandbox user as E2B names it: Runtime's is `runtime`, E2B's `user`. */
44
+ function e2bUser(name) {
45
+ return name === "runtime" ? "user" : (name ?? "");
46
+ }
47
+ /** A link's target as envd gives it: absolute, a relative one taken from the
48
+ * link's own directory. */
49
+ function linkTarget(path, target) {
50
+ if (target.startsWith("/"))
51
+ return target;
52
+ const parts = path.split("/").slice(0, -1);
53
+ for (const part of target.split("/")) {
54
+ if (part === "..")
55
+ parts.pop();
56
+ else if (part && part !== ".")
57
+ parts.push(part);
58
+ }
59
+ return `/${parts.filter(Boolean).join("/")}`;
60
+ }
61
+ function entryInfo(entry, asked, linked) {
29
62
  const mode = parseInt(entry.mode, 8) || 0;
30
63
  const bits = mode & 0o777;
64
+ const path = shown(entry.path, asked, linked);
31
65
  return {
32
66
  name: entry.name,
33
- path: entry.path,
67
+ path,
34
68
  ...(entry.type === "file"
35
69
  ? { type: FileType.FILE }
36
70
  : entry.type === "directory"
@@ -40,10 +74,15 @@ function entryInfo(entry) {
40
74
  : {}),
41
75
  size: entry.size,
42
76
  mode,
43
- permissions: PERMISSIONS[(bits >> 6) & 7] + PERMISSIONS[(bits >> 3) & 7] + PERMISSIONS[bits & 7],
44
- owner: entry.owner ?? "",
45
- group: entry.group ?? "",
46
- ...(entry.symlinkTarget ? { symlinkTarget: entry.symlinkTarget } : {}),
77
+ permissions: (KIND_LETTER[entry.type] ?? "-") +
78
+ PERMISSIONS[(bits >> 6) & 7] +
79
+ PERMISSIONS[(bits >> 3) & 7] +
80
+ PERMISSIONS[bits & 7],
81
+ owner: e2bUser(entry.owner),
82
+ group: e2bUser(entry.group),
83
+ ...(entry.symlinkTarget
84
+ ? { symlinkTarget: shown(linkTarget(entry.path, entry.symlinkTarget), asked, linked) }
85
+ : {}),
47
86
  modifiedTime: new Date(entry.modifiedAt),
48
87
  };
49
88
  }
@@ -63,7 +102,7 @@ async function bytesOf(data) {
63
102
  const basename = (path) => path.split("/").filter(Boolean).pop() ?? path;
64
103
  /** `sandbox.files`: E2B's filesystem module over Runtime's files API. Relative
65
104
  * paths resolve against the home directory, as in E2B (Runtime's is
66
- * /workspace). */
105
+ * /workspace), and come back under /home/user, as E2B gives them. */
67
106
  export class Filesystem {
68
107
  #ctx;
69
108
  constructor(ctx) {
@@ -79,6 +118,11 @@ export class Filesystem {
79
118
  opts.signal?.throwIfAborted();
80
119
  return absolute(path);
81
120
  }
121
+ /** Whether a path asked relative to the home may be shown under /home/user.
122
+ * Asked at the start of a call, it is learned beside the call itself. */
123
+ #linked(path) {
124
+ return path.startsWith("/") ? Promise.resolve(false) : this.#ctx.homeIsWorkspace();
125
+ }
82
126
  /** The calls as another Linux user, or undefined for the sandbox's own. */
83
127
  async #as(opts) {
84
128
  if (opts.user === undefined || opts.user === "user")
@@ -121,22 +165,27 @@ export class Filesystem {
121
165
  }
122
166
  async #writeOne(path, data, opts) {
123
167
  const file = await this.#path(path, opts);
168
+ const linked = this.#linked(path);
124
169
  const as = await this.#as(opts);
125
170
  const bytes = await bytesOf(data);
126
171
  if (as)
127
172
  await as.write(file, bytes);
128
173
  else
129
174
  await guard("file", () => this.#files.write(file, bytes, request(opts)));
130
- return { name: basename(file), type: FileType.FILE, path: file };
175
+ return { name: basename(file), type: FileType.FILE, path: shown(file, path, await linked) };
131
176
  }
132
177
  /** A directory's entries, hidden ones included; `depth` goes deeper. */
133
178
  async list(path, opts = {}) {
179
+ if (opts.depth !== undefined && opts.depth < 1)
180
+ throw new InvalidArgumentError("depth should be at least one");
134
181
  const dir = await this.#path(path, opts);
182
+ const linked = this.#linked(path);
135
183
  const as = await this.#as(opts);
136
- if (as)
137
- return (await as.list(dir, opts.depth ?? 1)).map(entryInfo);
138
- const entries = await guard("file", () => this.#files.list(dir, { depth: opts.depth ?? 1, hidden: true, ...request(opts) }));
139
- return entries.map(entryInfo);
184
+ const entries = as
185
+ ? await as.list(dir, opts.depth ?? 1)
186
+ : await guard("file", () => this.#files.list(dir, { depth: opts.depth ?? 1, hidden: true, ...request(opts) }));
187
+ const home = await linked;
188
+ return entries.map((entry) => entryInfo(entry, path, home));
140
189
  }
141
190
  /** Makes a directory and its parents. False when it already existed. */
142
191
  async makeDir(path, opts = {}) {
@@ -158,7 +207,7 @@ export class Filesystem {
158
207
  await as.rename(from, to);
159
208
  else
160
209
  await guard("file", () => this.#files.rename(from, to, { overwrite: true, ...request(opts) }));
161
- return this.getInfo(to, opts);
210
+ return this.getInfo(newPath, opts);
162
211
  }
163
212
  /** Removes a file, or a directory with everything in it. */
164
213
  async remove(path, opts = {}) {
@@ -177,13 +226,14 @@ export class Filesystem {
177
226
  }
178
227
  async getInfo(path, opts = {}) {
179
228
  const target = await this.#path(path, opts);
229
+ const linked = this.#linked(path);
180
230
  const as = await this.#as(opts);
181
231
  if (as)
182
- return entryInfo(await as.stat(target));
232
+ return entryInfo(await as.stat(target), path, await linked);
183
233
  const stat = await guard("file", () => this.#files.stat(target, request(opts)));
184
234
  if (!stat.exists)
185
235
  throw new FileNotFoundError(`${target} does not exist.`);
186
- return entryInfo(stat);
236
+ return entryInfo(stat, path, await linked);
187
237
  }
188
238
  /** E2B's watchDir on Runtime's file watch. A local deadline preserves
189
239
  * subsecond subscriptions; 0 runs until stopped or the sandbox ends. */
@@ -197,6 +247,10 @@ export class Filesystem {
197
247
  if (!Number.isFinite(lifetime) || lifetime < 0)
198
248
  throw new InvalidArgumentError("timeoutMs must be a nonnegative finite number.");
199
249
  const target = await this.#path(path, opts);
250
+ // envd watches only a directory; Runtime's watch also takes a file.
251
+ const stat = await guard("file", () => this.#files.stat(target, request(opts)));
252
+ if (stat.exists && stat.type !== "directory")
253
+ throw new InvalidArgumentError(`${target} is not a directory; watchDir watches a directory.`);
200
254
  const prefix = target.endsWith("/") ? target : `${target}/`;
201
255
  let exited = false;
202
256
  let native;
@@ -277,12 +331,24 @@ export class Filesystem {
277
331
  throw error;
278
332
  }
279
333
  }
334
+ const name = event.path.startsWith(prefix)
335
+ ? event.path.slice(prefix.length)
336
+ : event.path;
280
337
  if (!exited)
281
338
  await onEvent({
282
- name: event.path.startsWith(prefix) ? event.path.slice(prefix.length) : event.path,
339
+ name,
283
340
  type: event.type,
284
341
  ...(entry ? { entry } : {}),
285
342
  });
343
+ // Runtime writes a file whole, by a rename into place, which the
344
+ // watch sees as a create; envd writes in place, which is a write.
345
+ // Code written for E2B waits for the write (5 October 2026).
346
+ if (!exited && event.type === "create" && !event.isDir)
347
+ await onEvent({
348
+ name,
349
+ type: FilesystemEventType.WRITE,
350
+ ...(entry ? { entry } : {}),
351
+ });
286
352
  }, {
287
353
  recursive: opts.recursive === true,
288
354
  timeoutMs: 0,
@@ -7,13 +7,13 @@
7
7
  import type { Runtime } from "../client.js";
8
8
  import { type ConnectionOpts } from "./client.js";
9
9
  import { Sandbox } from "./sandbox.js";
10
- export { Sandbox, SandboxPaginator, DEFAULT_SHAPE, DEFAULT_TIMEOUT_MS, type SandboxOpts, type SandboxConnectOpts, type SandboxApiOpts, type SandboxInfo, type SandboxListOpts, type SandboxState, type SandboxLifecycle, type SandboxOnTimeout, type SnapshotInfo, } from "./sandbox.js";
10
+ export { Sandbox, SandboxPaginator, DEFAULT_SHAPE, DEFAULT_TIMEOUT_MS, type SandboxOpts, type SandboxConnectOpts, type SandboxApiOpts, type SandboxInfo, type SandboxListOpts, type SandboxState, type SandboxLifecycle, type SandboxOnTimeout, type SandboxPauseOpts, type CreateSnapshotOpts, type SnapshotMode, type SnapshotInfo, } from "./sandbox.js";
11
11
  export { Commands, CommandHandle, pidOf, type CommandStartOpts, type CommandConnectOpts, type CommandRequestOpts, type ProcessInfo, type Username, } from "./commands.js";
12
12
  export { Filesystem, FileType, type EntryInfo, type WriteInfo, type WriteEntry, type FilesystemRequestOpts, type FilesystemReadOpts, type FilesystemWriteOpts, type FilesystemListOpts, FilesystemEventType, type FilesystemEvent, type WatchOpts, type E2BWatchHandle as WatchHandle, } from "./filesystem.js";
13
- export { SandboxError, TimeoutError, InvalidArgumentError, NotEnoughSpaceError, NotFoundError, FileNotFoundError, SandboxNotFoundError, TemplateError, RateLimitError, AuthenticationError, ServiceBusyError, CommandExitError, NotSupportedError, PublicPreviewNotAllowedError, type CommandResult, } from "./errors.js";
13
+ export { SandboxError, TimeoutError, InvalidArgumentError, NotEnoughSpaceError, NotFoundError, FileNotFoundError, SandboxNotFoundError, SandboxNotRunningError, SandboxUnreachableError, TemplateError, RateLimitError, AuthenticationError, ServiceBusyError, CommandExitError, NotSupportedError, PublicPreviewNotAllowedError, GitAuthError, GitUpstreamError, BuildError, FileUploadError, VolumeError, VolumeNotFoundError, VolumePathNotFoundError, SecretError, SecretNotFoundError, DockerfileSyntaxError, type CommandResult, } from "./errors.js";
14
14
  export type { ConnectionOpts, HttpVersion, RuntimeOpts } from "./client.js";
15
15
  export { Pty, type PtyCreateOpts, type PtyConnectOpts } from "./pty.js";
16
- export { Template, TemplateBase, Volume, Secret, waitForPort, waitForURL, waitForProcess, waitForFile, waitForTimeout, } from "./unsupported.js";
16
+ export { Template, TemplateBase, Volume, Secret, waitForPort, waitForURL, waitForProcess, waitForFile, waitForTimeout, SecretPaginator, Git, ReadyCmd, LogEntry, LogEntryStart, LogEntryEnd, defaultBuildLogger, ConnectionConfig, ApiClient, getSignature, ALL_TRAFFIC, VolumeFileType, } from "./unsupported.js";
17
17
  export default Sandbox;
18
18
  /** E2B's client with bound options: `new E2B({ apiKey }).Sandbox.create()`. */
19
19
  export declare class E2B {
package/dist/e2b/index.js CHANGED
@@ -3,9 +3,9 @@ import { Sandbox } from "./sandbox.js";
3
3
  export { Sandbox, SandboxPaginator, DEFAULT_SHAPE, DEFAULT_TIMEOUT_MS, } from "./sandbox.js";
4
4
  export { Commands, CommandHandle, pidOf, } from "./commands.js";
5
5
  export { Filesystem, FileType, FilesystemEventType, } from "./filesystem.js";
6
- export { SandboxError, TimeoutError, InvalidArgumentError, NotEnoughSpaceError, NotFoundError, FileNotFoundError, SandboxNotFoundError, TemplateError, RateLimitError, AuthenticationError, ServiceBusyError, CommandExitError, NotSupportedError, PublicPreviewNotAllowedError, } from "./errors.js";
6
+ export { SandboxError, TimeoutError, InvalidArgumentError, NotEnoughSpaceError, NotFoundError, FileNotFoundError, SandboxNotFoundError, SandboxNotRunningError, SandboxUnreachableError, TemplateError, RateLimitError, AuthenticationError, ServiceBusyError, CommandExitError, NotSupportedError, PublicPreviewNotAllowedError, GitAuthError, GitUpstreamError, BuildError, FileUploadError, VolumeError, VolumeNotFoundError, VolumePathNotFoundError, SecretError, SecretNotFoundError, DockerfileSyntaxError, } from "./errors.js";
7
7
  export { Pty } from "./pty.js";
8
- export { Template, TemplateBase, Volume, Secret, waitForPort, waitForURL, waitForProcess, waitForFile, waitForTimeout, } from "./unsupported.js";
8
+ export { Template, TemplateBase, Volume, Secret, waitForPort, waitForURL, waitForProcess, waitForFile, waitForTimeout, SecretPaginator, Git, ReadyCmd, LogEntry, LogEntryStart, LogEntryEnd, defaultBuildLogger, ConnectionConfig, ApiClient, getSignature, ALL_TRAFFIC, VolumeFileType, } from "./unsupported.js";
9
9
  export default Sandbox;
10
10
  /** E2B's client with bound options: `new E2B({ apiKey }).Sandbox.create()`. */
11
11
  export class E2B {