withruntime 0.21.0 → 0.33.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 (79) hide show
  1. package/COMPATIBILITY.md +64 -3
  2. package/E2B.md +1 -1
  3. package/README.md +36 -2
  4. package/README.src.md +35 -1
  5. package/compatibility-lock.json +3 -3
  6. package/dist/agents/browser.d.ts +12 -0
  7. package/dist/agents/browser.js +12 -0
  8. package/dist/agents/index.d.ts +7 -7
  9. package/dist/agents/index.js +7 -8
  10. package/dist/agents-cli.d.ts +1 -1
  11. package/dist/agents-cli.js +250 -3
  12. package/dist/agents-deploy.d.ts +186 -0
  13. package/dist/agents-deploy.js +752 -0
  14. package/dist/agents-file.d.ts +19 -0
  15. package/dist/agents-file.js +135 -0
  16. package/dist/apps/index.d.ts +10 -0
  17. package/dist/apps/index.js +11 -0
  18. package/dist/apps-cli.d.ts +3 -1
  19. package/dist/apps-cli.js +285 -38
  20. package/dist/blaxel/errors.js +2 -1
  21. package/dist/ci/index.d.ts +10 -0
  22. package/dist/ci/index.js +11 -0
  23. package/dist/ci-cli.d.ts +10 -0
  24. package/dist/ci-cli.js +343 -0
  25. package/dist/cli.d.ts +18 -7
  26. package/dist/cli.js +862 -99
  27. package/dist/daytona/errors.js +2 -1
  28. package/dist/e2b/errors.js +2 -1
  29. package/dist/e2b/filesystem.d.ts +1 -0
  30. package/dist/e2b/filesystem.js +1 -1
  31. package/dist/e2b/sandbox.d.ts +11 -2
  32. package/dist/e2b/sandbox.js +22 -6
  33. package/dist/errors.d.ts +11 -4
  34. package/dist/errors.js +27 -4
  35. package/dist/fly/index.d.ts +102 -0
  36. package/dist/fly/index.js +930 -0
  37. package/dist/index.d.ts +7 -3
  38. package/dist/keys.d.ts +6 -0
  39. package/dist/keys.js +10 -1
  40. package/dist/machine.d.ts +10 -6
  41. package/dist/machine.js +14 -6
  42. package/dist/modal/index.d.ts +34 -0
  43. package/dist/modal/index.js +128 -22
  44. package/dist/product-names.js +7 -0
  45. package/dist/products/agents.d.ts +101 -3
  46. package/dist/products/agents.js +49 -0
  47. package/dist/products/apps.d.ts +206 -10
  48. package/dist/products/apps.js +172 -1
  49. package/dist/products/ci.d.ts +332 -0
  50. package/dist/products/ci.js +101 -0
  51. package/dist/products/index.d.ts +8 -0
  52. package/dist/products/index.js +15 -0
  53. package/dist/products/jobs.d.ts +105 -5
  54. package/dist/products/jobs.js +47 -3
  55. package/dist/products/network.d.ts +13 -0
  56. package/dist/products/observability.d.ts +1 -1
  57. package/dist/products/plans.d.ts +131 -0
  58. package/dist/products/plans.js +24 -0
  59. package/dist/products/reservations.d.ts +108 -0
  60. package/dist/products/reservations.js +41 -0
  61. package/dist/products/resources.d.ts +2 -1
  62. package/dist/products/secrets.d.ts +109 -6
  63. package/dist/products/secrets.js +38 -2
  64. package/dist/proxy.d.ts +3 -3
  65. package/dist/proxy.js +20 -0
  66. package/dist/sandbox.d.ts +29 -1
  67. package/dist/sandbox.js +56 -3
  68. package/dist/snapshots.d.ts +1 -1
  69. package/dist/switched-off.d.ts +5 -0
  70. package/dist/switched-off.js +8 -0
  71. package/dist/tar.d.ts +1 -1
  72. package/dist/tar.js +19 -1
  73. package/dist/transport.d.ts +1 -1
  74. package/dist/transport.js +48 -9
  75. package/dist/types.d.ts +35 -4
  76. package/dist/vercel/errors.d.ts +1 -1
  77. package/dist/vercel/errors.js +2 -1
  78. package/dist/vercel/snapshot.js +4 -2
  79. package/package.json +23 -6
@@ -1,4 +1,5 @@
1
1
  import { RuntimeError } from "../errors.js";
2
+ import { productSwitchedOff } from "../switched-off.js";
2
3
  /* Daytona's error classes by the same names and parents, so a
3
4
  `catch (e) { if (e instanceof DaytonaNotFoundError) ... }` written for
4
5
  Daytona keeps working. One made from a Runtime answer carries Runtime's
@@ -164,7 +165,7 @@ export function translate(error, subject = "other") {
164
165
  .filter(Boolean)
165
166
  .join("\n");
166
167
  let out;
167
- if (error.status === 503 && error.code.endsWith("_unavailable"))
168
+ if (productSwitchedOff(error))
168
169
  out = new NotSupportedError(error.code.replace(/_unavailable$/, ""), error.hint ?? "", message);
169
170
  else if (error.code === "file_not_found" || (error.status === 404 && subject === "file"))
170
171
  out = new DaytonaFileNotFoundError(message);
@@ -1,4 +1,5 @@
1
1
  import { RuntimeError } from "../errors.js";
2
+ import { productSwitchedOff } from "../switched-off.js";
2
3
  function attach(error, detail) {
3
4
  const target = error;
4
5
  if (detail.code !== undefined)
@@ -198,7 +199,7 @@ export function translate(error, subject = "other") {
198
199
  out = new SandboxNotRunningError(message);
199
200
  else if (error.code === "sandbox_not_ready" || error.code === "host_unavailable")
200
201
  out = new SandboxUnreachableError(message);
201
- else if (error.status === 503 && error.code.endsWith("_unavailable"))
202
+ else if (productSwitchedOff(error))
202
203
  // A product switched off on purpose (forks, previews): Runtime's own words
203
204
  // say what is off and what to use meanwhile.
204
205
  out = new NotSupportedError(error.code.replace(/_unavailable$/, ""), error.hint ?? "", message);
@@ -70,6 +70,7 @@ export interface FilesystemWriteOpts extends FilesystemRequestOpts {
70
70
  export interface FilesystemListOpts extends FilesystemRequestOpts {
71
71
  depth?: number;
72
72
  }
73
+ export declare function absolute(path: string): string;
73
74
  /** `sandbox.files`: E2B's filesystem module over Runtime's files API. Relative
74
75
  * paths resolve against the home directory, as in E2B (Runtime's is
75
76
  * /workspace), and come back under /home/user, as E2B gives them. */
@@ -17,7 +17,7 @@ export var FilesystemEventType;
17
17
  })(FilesystemEventType || (FilesystemEventType = {}));
18
18
  /** Runtime's home, where E2B's relative paths land. */
19
19
  const HOME = "/workspace";
20
- function absolute(path) {
20
+ export function absolute(path) {
21
21
  return path.startsWith("/") ? path : `${HOME}/${path.replace(/^\.\//, "")}`;
22
22
  }
23
23
  /** E2B's home, which E2B code expects in the paths it gets back. */
@@ -113,6 +113,10 @@ export type SandboxListOpts = SandboxApiOpts & {
113
113
  limit?: number;
114
114
  nextToken?: string;
115
115
  };
116
+ type FileUrlOpts = {
117
+ user?: string;
118
+ useSignatureExpiration?: number;
119
+ };
116
120
  /** The Runtime image name an E2B template name stands for: E2B's
117
121
  * `team/name:tag` without the team, since a Runtime account's images are its
118
122
  * own. `name`, `name:tag` and `name@version` are Runtime image names too. */
@@ -204,8 +208,13 @@ export declare class Sandbox {
204
208
  getMetrics(opts?: MetricsOpts): Promise<E2BSandboxMetrics[]>;
205
209
  static getMetrics(sandboxId: string, opts?: MetricsOpts & SandboxApiOpts): Promise<E2BSandboxMetrics[]>;
206
210
  updateNetwork(): Promise<never>;
207
- uploadUrl(): Promise<never>;
208
- downloadUrl(): Promise<never>;
211
+ /** E2B's signed upload URL: anyone can PUT a body to it, with no key.
212
+ * Runtime's links name one file, so `path` is required. `useSignatureExpiration`
213
+ * is the lifetime in seconds (one hour by default, at most 7 days); `user`
214
+ * is ignored, a link writes as the sandbox's own user. */
215
+ uploadUrl(path?: string, opts?: FileUrlOpts): Promise<string>;
216
+ /** E2B's signed download URL: anyone can open it, with no key. See `uploadUrl`. */
217
+ downloadUrl(path: string, opts?: FileUrlOpts): Promise<string>;
209
218
  getMcpUrl(): never;
210
219
  getMcpToken(): Promise<never>;
211
220
  }
@@ -1,7 +1,7 @@
1
1
  import { clientFor, keySource, request, } from "./client.js";
2
2
  import { Commands } from "./commands.js";
3
3
  import { guard, InvalidArgumentError, NotSupportedError, PublicPreviewNotAllowedError, SandboxError, SandboxNotFoundError, TemplateError, } from "./errors.js";
4
- import { Filesystem } from "./filesystem.js";
4
+ import { absolute, Filesystem } from "./filesystem.js";
5
5
  import { Pty } from "./pty.js";
6
6
  /* E2B's Sandbox over Runtime's. Every call is one or a few calls of the
7
7
  withruntime SDK; nothing here speaks HTTP. What Runtime cannot do the same
@@ -85,6 +85,13 @@ function snapshotMemory(field, mode, keepMemory) {
85
85
  throw new InvalidArgumentError(`${field} must be one of: ${SNAPSHOT_MODES.join(", ")} (got ${JSON.stringify(mode)}).`);
86
86
  return mode === "full";
87
87
  }
88
+ /** A link's lifetime in whole seconds, one hour unless given, held to Runtime's 60 s to 7 days. */
89
+ function linkLifetime(opts) {
90
+ const asked = opts.useSignatureExpiration;
91
+ if (asked === undefined)
92
+ return 3600;
93
+ return Math.min(604800, Math.max(60, Math.ceil(asked)));
94
+ }
88
95
  const filesOnlyPause = () => new NotSupportedError("A files-only pause (mode: 'filesystem', or keepMemory: false)", "Runtime's pause keeps memory and files; leave mode out or pass mode: 'full'. " +
89
96
  "For a copy of the files alone, use createSnapshot({ mode: 'filesystem' }).");
90
97
  function refuseCreate(opts) {
@@ -575,11 +582,20 @@ export class Sandbox {
575
582
  updateNetwork() {
576
583
  return unsupported("E2B's network rules (updateNetwork)", "Use sandbox.runtime.network.set({ internet, allow, deny }).");
577
584
  }
578
- uploadUrl() {
579
- return unsupported("Signed upload URLs", "Use sandbox.files.write(path, data).");
580
- }
581
- downloadUrl() {
582
- return unsupported("Signed download URLs", "Use sandbox.files.read(path, { format: 'bytes' }).");
585
+ /** E2B's signed upload URL: anyone can PUT a body to it, with no key.
586
+ * Runtime's links name one file, so `path` is required. `useSignatureExpiration`
587
+ * is the lifetime in seconds (one hour by default, at most 7 days); `user`
588
+ * is ignored, a link writes as the sandbox's own user. */
589
+ async uploadUrl(path, opts = {}) {
590
+ if (path === undefined)
591
+ throw new InvalidArgumentError("Runtime's upload URL is made for one file: pass the file's path.");
592
+ const link = await guard("file", () => this.#runtime.files.uploadUrl(absolute(path), { expiresInSeconds: linkLifetime(opts) }));
593
+ return link.url;
594
+ }
595
+ /** E2B's signed download URL: anyone can open it, with no key. See `uploadUrl`. */
596
+ async downloadUrl(path, opts = {}) {
597
+ const link = await guard("file", () => this.#runtime.files.downloadUrl(absolute(path), { expiresInSeconds: linkLifetime(opts) }));
598
+ return link.url;
583
599
  }
584
600
  getMcpUrl() {
585
601
  throw new NotSupportedError("E2B's MCP gateway", "Runtime's own MCP server is `npx withruntime mcp`.");
package/dist/errors.d.ts CHANGED
@@ -2,9 +2,10 @@
2
2
  * `hint` says what to do next, and `requestId` is what to quote in a report
3
3
  * (runtime.feedback.submit, `runtime feedback`, or support). */
4
4
  /** A product switched off on this deployment, or paused on purpose: the API
5
- * answers these 503s in fixed words (`serverFault` "off" in
6
- * packages/cloud/src/api/respond.ts) and retrying cannot change them.
7
- * host_unavailable and busy pass, and are retried. */
5
+ * answers these with 501 (503 from an older server) in fixed words, and
6
+ * retrying cannot change them. host_unavailable and busy pass, and are
7
+ * retried. The API also says so itself (`retryable: false`); this set is for
8
+ * a server too old to. */
8
9
  export declare const DELIBERATE: ReadonlySet<string>;
9
10
  /** Refusals that guarantee nothing ran: a sandbox running as many commands
10
11
  * as it can (`guest_busy`), and the API's own limits, which answer before any
@@ -26,6 +27,10 @@ export declare class RuntimeError extends Error {
26
27
  /** The key the SDK sent. A retry with it never repeats the effect. */
27
28
  readonly idempotencyKey?: string;
28
29
  readonly retryAfterMs?: number;
30
+ /** What the API said of this answer: whether the same request, sent again
31
+ * unchanged, may succeed. Undefined from a server too old to say, or for a
32
+ * failure the SDK made itself; `retryable` then judges by code and status. */
33
+ readonly sentRetryable?: boolean;
29
34
  constructor(init: {
30
35
  message: string;
31
36
  code: string;
@@ -35,13 +40,15 @@ export declare class RuntimeError extends Error {
35
40
  details?: Record<string, unknown>;
36
41
  idempotencyKey?: string;
37
42
  retryAfterMs?: number;
43
+ sentRetryable?: boolean;
38
44
  cause?: unknown;
39
45
  });
40
46
  /** Retrying this exact call (the SDK keeps the key) is safe and may work.
41
47
  * True for no_capacity and no_credit_running_limit as well, which clear by themselves when
42
48
  * a host frees room or a sandbox without credit stops. `sandboxes.create` already
43
49
  * waits for those (see `waitForCapacityMs`), so seeing one from a create
44
- * means the wait ran out or was switched off. */
50
+ * means the wait ran out or was switched off. The API's own word
51
+ * (`sentRetryable`) wins; the rest is for an older server. */
45
52
  get retryable(): boolean;
46
53
  toString(): string;
47
54
  }
package/dist/errors.js CHANGED
@@ -2,20 +2,28 @@
2
2
  * `hint` says what to do next, and `requestId` is what to quote in a report
3
3
  * (runtime.feedback.submit, `runtime feedback`, or support). */
4
4
  /** A product switched off on this deployment, or paused on purpose: the API
5
- * answers these 503s in fixed words (`serverFault` "off" in
6
- * packages/cloud/src/api/respond.ts) and retrying cannot change them.
7
- * host_unavailable and busy pass, and are retried. */
5
+ * answers these with 501 (503 from an older server) in fixed words, and
6
+ * retrying cannot change them. host_unavailable and busy pass, and are
7
+ * retried. The API also says so itself (`retryable: false`); this set is for
8
+ * a server too old to. */
8
9
  export const DELIBERATE = new Set([
9
10
  "unavailable",
10
11
  "unsupported",
11
12
  "fork_unavailable",
12
13
  "previews_unavailable",
14
+ "file_links_unavailable",
13
15
  "network_unavailable",
14
16
  "network_rules_unavailable",
15
17
  "secrets_unavailable",
16
18
  "identity_unavailable",
17
19
  "env_unavailable",
18
20
  "resize_unavailable",
21
+ "feature_unavailable",
22
+ "delete_unavailable",
23
+ "switch_image_unavailable",
24
+ "switching_paused",
25
+ "schema_migration_required",
26
+ "storage_unavailable",
19
27
  ]);
20
28
  /** Refusals that guarantee nothing ran: a sandbox running as many commands
21
29
  * as it can (`guest_busy`), and the API's own limits, which answer before any
@@ -50,6 +58,10 @@ export class RuntimeError extends Error {
50
58
  /** The key the SDK sent. A retry with it never repeats the effect. */
51
59
  idempotencyKey;
52
60
  retryAfterMs;
61
+ /** What the API said of this answer: whether the same request, sent again
62
+ * unchanged, may succeed. Undefined from a server too old to say, or for a
63
+ * failure the SDK made itself; `retryable` then judges by code and status. */
64
+ sentRetryable;
53
65
  constructor(init) {
54
66
  super(init.message, init.cause === undefined ? undefined : { cause: init.cause });
55
67
  this.name = new.target.name;
@@ -65,18 +77,28 @@ export class RuntimeError extends Error {
65
77
  this.idempotencyKey = init.idempotencyKey;
66
78
  if (init.retryAfterMs !== undefined)
67
79
  this.retryAfterMs = init.retryAfterMs;
80
+ if (init.sentRetryable !== undefined)
81
+ this.sentRetryable = init.sentRetryable;
68
82
  }
69
83
  /** Retrying this exact call (the SDK keeps the key) is safe and may work.
70
84
  * True for no_capacity and no_credit_running_limit as well, which clear by themselves when
71
85
  * a host frees room or a sandbox without credit stops. `sandboxes.create` already
72
86
  * waits for those (see `waitForCapacityMs`), so seeing one from a create
73
- * means the wait ran out or was switched off. */
87
+ * means the wait ran out or was switched off. The API's own word
88
+ * (`sentRetryable`) wins; the rest is for an older server. */
74
89
  get retryable() {
90
+ if (this.sentRetryable !== undefined)
91
+ return this.sentRetryable;
75
92
  if (DELIBERATE.has(this.code))
76
93
  return false;
77
94
  // A fork asking for more copies than an account without credit runs at once never fits.
78
95
  if (PASSING.has(this.code))
79
96
  return this.details?.field !== "count";
97
+ // The account at one of its limits (a 429 since 17 October 2026): some
98
+ // free when its own work ends, some only when something is deleted, so
99
+ // the same call again is no answer by itself.
100
+ if (this.code === "quota_exceeded")
101
+ return false;
80
102
  return [0, 429, 502, 503, 504].includes(this.status);
81
103
  }
82
104
  toString() {
@@ -146,6 +168,7 @@ export function errorFor(status, body, idempotencyKey) {
146
168
  ? { details: error.details }
147
169
  : {}),
148
170
  ...(typeof error.retryAfterMs === "number" ? { retryAfterMs: error.retryAfterMs } : {}),
171
+ ...(typeof error.retryable === "boolean" ? { sentRetryable: error.retryable } : {}),
149
172
  ...(idempotencyKey ? { idempotencyKey } : {}),
150
173
  };
151
174
  if (init.code === "account_blocked")
@@ -0,0 +1,102 @@
1
+ import type { Sandbox } from "../sandbox.js";
2
+ import { type ClientOptions } from "../compat/core.js";
3
+ export type MachineState = "created" | "starting" | "started" | "stopping" | "stopped" | "suspending" | "suspended" | "destroying" | "destroyed";
4
+ export type GuestConfig = {
5
+ cpu_kind?: "shared" | "performance";
6
+ cpus?: number;
7
+ memory_mb?: number;
8
+ [key: string]: unknown;
9
+ };
10
+ export type InitConfig = {
11
+ exec?: string[];
12
+ entrypoint?: string[];
13
+ cmd?: string[];
14
+ [key: string]: unknown;
15
+ };
16
+ export type MachineFile = {
17
+ guest_path: string;
18
+ raw_value?: string;
19
+ [key: string]: unknown;
20
+ };
21
+ export type MachineConfig = {
22
+ image: string;
23
+ env?: Record<string, string>;
24
+ guest?: GuestConfig;
25
+ init?: InitConfig;
26
+ metadata?: Record<string, string>;
27
+ files?: MachineFile[];
28
+ auto_destroy?: boolean;
29
+ restart?: {
30
+ policy?: string;
31
+ max_retries?: number;
32
+ [key: string]: unknown;
33
+ };
34
+ [key: string]: unknown;
35
+ };
36
+ export type MachineEvent = {
37
+ id: string;
38
+ type: string;
39
+ status: string;
40
+ source: string;
41
+ timestamp: number;
42
+ };
43
+ export type Machine = {
44
+ id: string;
45
+ name: string;
46
+ state: MachineState;
47
+ region: string;
48
+ instance_id: string;
49
+ private_ip: string;
50
+ config: MachineConfig;
51
+ image_ref: {
52
+ registry: string;
53
+ repository: string;
54
+ tag: string;
55
+ digest: string;
56
+ };
57
+ created_at: string;
58
+ updated_at: string;
59
+ events: MachineEvent[];
60
+ host_status: "ok";
61
+ };
62
+ export type MachinesApiOptions = ClientOptions & {
63
+ /** Where the Fly region asked for is not Runtime's: `iad` (Ashburn,
64
+ * Virginia) is the Fly region next to Runtime's us-east servers. */
65
+ regions?: readonly string[];
66
+ /** Answer only requests that carry their own Runtime key, never with this
67
+ * handler's login: set when anyone but this computer can reach it. */
68
+ requireKey?: boolean;
69
+ };
70
+ /** A Machine's state as Fly words it, from the sandbox's. */
71
+ export declare function machineState(state: Sandbox["state"]): MachineState;
72
+ /** The Dockerfile that makes a Fly image and `init` one Runtime image: Fly's
73
+ * `exec` replaces both entrypoint and cmd, `entrypoint` drops the image's
74
+ * cmd unless `cmd` is given, and `cmd` keeps the entrypoint, as Docker does. */
75
+ export declare function dockerfileFor(image: string, init?: InitConfig, files?: MachineFile[]): string;
76
+ /** The supervised form of a main process. */
77
+ export declare function supervised(argv: readonly string[]): string[];
78
+ /** A restart policy as the supervisor reads it. */
79
+ export declare function restartOf(restart: MachineConfig["restart"]): string;
80
+ export declare class MachinesApi {
81
+ #private;
82
+ constructor({ regions, requireKey, ...options }?: MachinesApiOptions);
83
+ /** Answers one Machines API request. */
84
+ fetch: (request: Request) => Promise<Response>;
85
+ }
86
+ /** A sandbox as Fly's Machine object. */
87
+ export declare function toMachine(sandbox: Sandbox): Machine;
88
+ /** One handler for every Machines API request, for Bun.serve, Deno.serve,
89
+ * Hono or any server that speaks fetch. */
90
+ export declare function machinesApi(options?: MachinesApiOptions): (request: Request) => Promise<Response>;
91
+ /** Serves the Machines API on a port of this computer (default 4280, the
92
+ * port of Fly's own `_api.internal`), for code that reads its address from
93
+ * FLY_API_HOSTNAME (fly-admin's `apiUrl`, fly-go's FLY_FLAPS_BASE_URL). Off
94
+ * this computer's loopback, a request without its own Runtime key is refused,
95
+ * so the login it runs as is never lent to the network. */
96
+ export declare function serveMachinesApi(options?: MachinesApiOptions & {
97
+ port?: number;
98
+ hostname?: string;
99
+ }): Promise<{
100
+ url: string;
101
+ close(): Promise<void>;
102
+ }>;