withruntime 0.11.1 → 0.11.2

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 (53) hide show
  1. package/BLAXEL.md +1 -1
  2. package/DAYTONA.md +1 -1
  3. package/E2B.md +57 -29
  4. package/README.md +4 -3
  5. package/README.src.md +4 -3
  6. package/compatibility-lock.json +14 -14
  7. package/dist/blaxel/errors.js +4 -4
  8. package/dist/blaxel/sandbox.d.ts +1 -2
  9. package/dist/blaxel/sandbox.js +1 -2
  10. package/dist/cli.d.ts +1 -1
  11. package/dist/cli.js +63 -15
  12. package/dist/client.d.ts +11 -59
  13. package/dist/client.js +8 -113
  14. package/dist/daytona/daytona.d.ts +1 -2
  15. package/dist/daytona/daytona.js +1 -2
  16. package/dist/daytona/sandbox.d.ts +1 -1
  17. package/dist/daytona/sandbox.js +1 -1
  18. package/dist/e2b/client.d.ts +15 -2
  19. package/dist/e2b/client.js +61 -7
  20. package/dist/e2b/commands.d.ts +3 -0
  21. package/dist/e2b/commands.js +16 -5
  22. package/dist/e2b/errors.d.ts +26 -2
  23. package/dist/e2b/errors.js +38 -5
  24. package/dist/e2b/filesystem.d.ts +1 -1
  25. package/dist/e2b/filesystem.js +82 -16
  26. package/dist/e2b/index.d.ts +2 -2
  27. package/dist/e2b/index.js +2 -2
  28. package/dist/e2b/sandbox.d.ts +23 -14
  29. package/dist/e2b/sandbox.js +195 -178
  30. package/dist/e2b/unsupported.d.ts +19 -0
  31. package/dist/e2b/unsupported.js +21 -0
  32. package/dist/e2b/users.d.ts +9 -0
  33. package/dist/e2b/users.js +22 -0
  34. package/dist/errors.d.ts +4 -4
  35. package/dist/errors.js +12 -10
  36. package/dist/machine.d.ts +180 -0
  37. package/dist/machine.js +504 -0
  38. package/dist/openai-agents/index.d.ts +1 -1
  39. package/dist/openai-agents/index.js +1 -1
  40. package/dist/products/jobs.d.ts +2 -3
  41. package/dist/products/jobs.js +1 -2
  42. package/dist/products/limits.d.ts +4 -10
  43. package/dist/products/network.d.ts +1 -1
  44. package/dist/products/secrets.d.ts +1 -1
  45. package/dist/products/switching.d.ts +2 -3
  46. package/dist/sandbox.d.ts +7 -92
  47. package/dist/sandbox.js +8 -362
  48. package/dist/transport.d.ts +4 -2
  49. package/dist/transport.js +6 -4
  50. package/dist/types.d.ts +53 -8
  51. package/dist/vercel/sandbox.d.ts +1 -2
  52. package/dist/vercel/sandbox.js +1 -2
  53. package/package.json +1 -1
@@ -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,
@@ -10,10 +10,10 @@ import { Sandbox } from "./sandbox.js";
10
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";
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, TemplateError, RateLimitError, AuthenticationError, ServiceBusyError, CommandExitError, NotSupportedError, PublicPreviewNotAllowedError, GitAuthError, GitUpstreamError, BuildError, FileUploadError, VolumeError, VolumeNotFoundError, VolumePathNotFoundError, SecretError, SecretNotFoundError, 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, TemplateError, RateLimitError, AuthenticationError, ServiceBusyError, CommandExitError, NotSupportedError, PublicPreviewNotAllowedError, GitAuthError, GitUpstreamError, BuildError, FileUploadError, VolumeError, VolumeNotFoundError, VolumePathNotFoundError, SecretError, SecretNotFoundError, } 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 {
@@ -9,10 +9,14 @@ export declare const DEFAULT_SHAPE: {
9
9
  readonly vcpu: 2;
10
10
  readonly memoryMiB: 512;
11
11
  };
12
- /** E2B's default sandbox timeout, 300 s. Not sent: a sandbox created with no
13
- * timeoutMs has no time limit on Runtime, running while it works and pausing
14
- * when idle (0300). Kept for code that imports it. */
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 declare const DEFAULT_TIMEOUT_MS = 300000;
17
+ /** The label that keeps the template a sandbox was made from, so getInfo
18
+ * and list name it whichever client asks; it is not shown in `metadata`. */
19
+ export declare const TEMPLATE_LABEL = "e2b-template";
16
20
  export type SandboxState = "running" | "paused";
17
21
  export type SandboxOnTimeout = "pause" | "kill" | {
18
22
  action: "pause" | "kill";
@@ -92,10 +96,10 @@ export type SandboxListOpts = SandboxApiOpts & {
92
96
  limit?: number;
93
97
  nextToken?: string;
94
98
  };
95
- /** Test hook: renew every kept lease now, as the five-minute timer would. */
96
- export declare function renewLeases(): Promise<void>;
97
- /** Test hook: the end each kept sandbox is kept until. */
98
- export declare function keptLeases(): Map<string, number>;
99
+ /** The Runtime image name an E2B template name stands for: E2B's
100
+ * `team/name:tag` without the team, since a Runtime account's images are its
101
+ * own. `name`, `name:tag` and `name@version` are Runtime image names too. */
102
+ export declare function imageNameFor(template: string): string;
99
103
  export declare class Sandbox {
100
104
  #private;
101
105
  protected readonly requestTimeoutMs: number;
@@ -112,8 +116,7 @@ export declare class Sandbox {
112
116
  protected get client(): Runtime;
113
117
  /** Creates a sandbox from `template` (default "base", Runtime's stock image)
114
118
  * with E2B's default machine, 2 vCPU and 512 MiB, and waits until it runs.
115
- * Funding is left to Runtime: the free trial while the account has trial
116
- * time, then prepaid credit, exactly as withruntime's own create. */
119
+ * Funding is left to Runtime: the included usage first, then prepaid credit, exactly as withruntime's own create. */
117
120
  static create<S extends typeof Sandbox>(this: S, opts?: SandboxOpts): Promise<InstanceType<S>>;
118
121
  static create<S extends typeof Sandbox>(this: S, template: string, opts?: SandboxOpts): Promise<InstanceType<S>>;
119
122
  /** Connects to a sandbox by id, waking it if it is paused, as E2B does. A
@@ -123,17 +126,23 @@ export declare class Sandbox {
123
126
  connect(opts?: SandboxConnectOpts): Promise<this>;
124
127
  /** Running and paused sandboxes, a page at a time, as E2B's paginator. */
125
128
  static list(opts?: SandboxListOpts): SandboxPaginator;
126
- /** Stops the sandbox. False when it was not found or had already ended. */
129
+ /** Deletes the sandbox for good, as E2B's kill destroys it: its disk,
130
+ * memory and shared ports go. False when it was not found or was already
131
+ * deleted. */
127
132
  static kill(sandboxId: string, opts?: SandboxApiOpts): Promise<boolean>;
128
133
  kill(opts?: Pick<ConnectionOpts, "requestTimeoutMs" | "signal">): Promise<boolean>;
129
- /** Sets the sandbox to end `timeoutMs` from now, up to 24 hours; past an
130
- * hour the lease is moved on while this process runs. Runtime cannot end a
131
- * lease early, so an end sooner than the lease's is refused. A sandbox
132
- * created with no timeout has no end to move, and nothing changes. */
134
+ /** Sets the sandbox to end `timeoutMs` from now, up to 24 hours, as E2B
135
+ * does; the server keeps the deadline. Runtime cannot bring an end
136
+ * sooner, so that is refused. A sandbox with no time limit (one on a pilot
137
+ * account, kept running by the server) has no end to move, and nothing
138
+ * changes. */
133
139
  static setTimeout(sandboxId: string, timeoutMs: number, opts?: SandboxApiOpts): Promise<void>;
134
140
  setTimeout(timeoutMs: number, opts?: Pick<ConnectionOpts, "requestTimeoutMs" | "signal">): Promise<void>;
135
141
  static getInfo(sandboxId: string, opts?: SandboxApiOpts): Promise<SandboxInfo>;
136
142
  getInfo(opts?: Pick<ConnectionOpts, "requestTimeoutMs" | "signal">): Promise<SandboxInfo>;
143
+ /** Whether the sandbox answers calls, as E2B's health check says: true
144
+ * while it runs, and while Runtime has paused it for being idle, since the
145
+ * next call wakes it. False once paused on request, ended or deleted. */
137
146
  isRunning(opts?: Pick<ConnectionOpts, "requestTimeoutMs" | "signal">): Promise<boolean>;
138
147
  /** Pauses the sandbox, keeping memory and files. False when it was paused. */
139
148
  static pause(sandboxId: string, opts?: SandboxApiOpts & {