withruntime 0.8.0 → 0.8.1

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 CHANGED
@@ -24,15 +24,26 @@ anywhere: in `BL_API_KEY` it is ignored, and passed to `initialize` while
24
24
  nothing on Runtime. With no Runtime key at all, the first call throws
25
25
  `CredentialsError` saying what to set.
26
26
 
27
+ **A key sees only the sandboxes it created.** A sandbox made with another of
28
+ the account's keys answers 404, though its name stays taken
29
+ ([teams](https://withruntime.com/docs/teams)). Services that share sandboxes by
30
+ name, or through `getByExternalId`, use the same Runtime key.
31
+
27
32
  ## What a sandbox gets
28
33
 
29
34
  - **Machine:** Blaxel's default of 4096 MB. vCPUs follow Blaxel's rule of one
30
- per 2048 MB (at least one, at most 16): 2 for the default, 4 for 8192 MB.
35
+ per 2048 MB (at least one, at most 16): 2 for the default, 4 for 8192 MB. A
36
+ trial sandbox has at most 4096 MB (2 vCPUs); `memory: 8192` or more needs
37
+ prepaid credit, and on the trial is refused with a 400 (`invalid_trial`)
38
+ saying so.
31
39
  - **Image:** Blaxel's `base-image`, `py-app`, `ts-app`, `node`,
32
40
  `docker-in-sandbox` and `jupyter-server` are Runtime's stock image (Ubuntu
33
41
  24.04 with Node.js 24, Python 3.12, Bun, git and a compiler; Docker after
34
- `sudo enable-docker`). Any other name must be a ready Runtime image with that
35
- name.
42
+ `sudo enable-docker`). Any other image `ns/name:tag` is the ready Runtime
43
+ image named `ns-name` (every `/` becomes `-`, as Runtime names hold none) at
44
+ tag `tag` (`latest` when none is given). Build it once with
45
+ `npx withruntime image build --dockerfile Dockerfile --name ns-name -t ns-name:tag`,
46
+ and the code keeps `image: "ns/name:tag"` as it is.
36
47
  - **Standby and lifetime:** a Blaxel sandbox goes to standby when unused and
37
48
  resumes on the next call. On Runtime it pauses after 60 seconds without a
38
49
  call (Runtime's shortest idle pause; Blaxel's is about 15 seconds), keeping
@@ -107,21 +118,21 @@ nothing on Runtime. With no Runtime key at all, the first call throws
107
118
  Each of these throws `NotSupportedError` before anything happens. Its `feature`
108
119
  names the gap and its `alternative` says what to use.
109
120
 
110
- | Blaxel | Use instead |
111
- | ------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
112
- | An `image` with no Runtime image of that name (Blaxel's other templates too) | Build it: `npx withruntime image build --dockerfile Dockerfile --name <image>`. |
113
- | A region outside the US | Runtime runs in one US region. |
114
- | `extraArgs` other than `iptables`; network `egress`, `firewall`, `subnet`, and a `proxy` with more than its domain lists | Remove them; `allowedDomains` and `forbiddenDomains` (on the network or its proxy); Runtime secrets. |
115
- | Read-only and ephemeral volumes; a volume with no Runtime volume of that name | Mount read-write; write scratch files under `/workspace`; `runtime.volumes.create`. |
116
- | `snapshots.restore`, `restore` | Fork from the snapshot into a new sandbox. |
117
- | Forking into an application | Fork into a sandbox and share its port. |
118
- | A preview's `prefixUrl`, `customDomain`, `requestHeaders`, `responseHeaders`, `ttl` | Runtime's fixed preview address; set headers (CORS) in your server; delete the preview. |
119
- | `preview.tokens.list()`; a token for over a week | Keep the tokens you create; create a new one when it runs out. |
120
- | `runCode` with `envs` for one run; a language Runtime's interpreter lacks | Sandbox envs at create, or set them in the code; `process.exec`. |
121
- | Listing by `cursor`, `q`, `anchor`, a status other than `DEPLOYED`, or newest first | List and filter the result yourself; `nextPage()` walks the pages. |
122
- | `sandbox.sessions`, `schedules`, `codegen`, `system`, `drives`; `fromSession` | Previews and tokens; your own scheduler; `fs.write`; nothing to upgrade; Runtime volumes. |
123
- | Agents, models, tools, MCP transports, jobs, applications, images, volumes, drives | Your own agent with Runtime sandboxes, `withruntime/tools`, `npx withruntime mcp`, `runtime.images`, `runtime.volumes`. |
124
- | Blaxel's generated API functions (`createSandbox`, `postProcess`, ...) | `SandboxInstance` here, or `import { Runtime } from "withruntime"`. |
121
+ | Blaxel | Use instead |
122
+ | ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
123
+ | An `image` with no ready Runtime image of the name it maps to (Blaxel's other templates too) | Build it: `npx withruntime image build --dockerfile Dockerfile --name ns-name -t ns-name:tag`; the error gives the exact command. |
124
+ | A region outside the US | Runtime runs in one US region. |
125
+ | `extraArgs` other than `iptables`; network `egress`, `firewall`, `subnet`, and a `proxy` with more than its domain lists | Remove them; `allowedDomains` and `forbiddenDomains` (on the network or its proxy); Runtime secrets. |
126
+ | Read-only and ephemeral volumes; a volume with no Runtime volume of that name | Mount read-write; write scratch files under `/workspace`; `runtime.volumes.create`. |
127
+ | `snapshots.restore`, `restore` | Fork from the snapshot into a new sandbox. |
128
+ | Forking into an application | Fork into a sandbox and share its port. |
129
+ | A preview's `prefixUrl`, `customDomain`, `requestHeaders`, `responseHeaders`, `ttl` | Runtime's fixed preview address; set headers (CORS) in your server; delete the preview. |
130
+ | `preview.tokens.list()`; a token for over a week | Keep the tokens you create; create a new one when it runs out. |
131
+ | `runCode` with `envs` for one run; a language Runtime's interpreter lacks | Sandbox envs at create, or set them in the code; `process.exec`. |
132
+ | Listing by `cursor`, `q`, `anchor`, a status other than `DEPLOYED`, or newest first | List and filter the result yourself; `nextPage()` walks the pages. |
133
+ | `sandbox.sessions`, `schedules`, `codegen`, `system`, `drives`; `fromSession` | Previews and tokens; your own scheduler; `fs.write`; nothing to upgrade; Runtime volumes. |
134
+ | Agents, models, tools, MCP transports, jobs, applications, images, volumes, drives | Your own agent with Runtime sandboxes, `withruntime/tools`, `npx withruntime mcp`, `runtime.images`, `runtime.volumes`. |
135
+ | Blaxel's generated API functions (`createSandbox`, `postProcess`, ...) | `SandboxInstance` here, or `import { Runtime } from "withruntime"`. |
125
136
 
126
137
  Some differences are not refusals:
127
138
 
@@ -82,6 +82,27 @@ export function responseError(status, message) {
82
82
  headers: { "content-type": "application/json" },
83
83
  }), data, data);
84
84
  }
85
+ const LS = "List the directory with sandbox.fs.ls(path).";
86
+ /** Runtime's hints that name Runtime's calls, in the Blaxel calls a Blaxel
87
+ * program makes (the Python adapter uses the same words). */
88
+ const AGAIN = "Try again in a moment.";
89
+ const BLAXEL_HINTS = {
90
+ name_taken: "SandboxInstance.createIfNotExists({ name }) answers the sandbox that has the name.",
91
+ file_not_found: LS,
92
+ path_not_found: LS,
93
+ is_a_directory: "That path is a directory: list it with sandbox.fs.ls(path), or name a file in it.",
94
+ cwd_not_found: "Make the directory with sandbox.fs.mkdir(path), or pass an existing workingDir to sandbox.process.exec.",
95
+ sandbox_paused: "Call sandbox.unarchive(), then try again.",
96
+ not_running: "The sandbox is not running: call sandbox.unarchive() if it was archived, or make a new one with SandboxInstance.create if it was deleted.",
97
+ trial_busy: "The trial's sandboxes are all in use: delete one you no longer need (sandbox.delete()) or archive it (sandbox.archive()), then try again. Moving to paid credit is the account owner's decision.",
98
+ public_preview_not_allowed: "On the trial, share the port privately: sandbox.previews.create({ metadata: { name }, spec: { port, public: false } }) and a token from preview.tokens.create(expiresAt). A public preview needs a paid sandbox, which is the account owner's decision.",
99
+ busy: AGAIN,
100
+ guest_busy: AGAIN,
101
+ rate_limited: AGAIN,
102
+ unauthorized: "Set RUNTIME_API_KEY to a Runtime key (https://withruntime.com/account/keys), or run `npx withruntime login` once. A Blaxel key (BL_API_KEY) is never sent.",
103
+ };
104
+ /** A trial create over the trial's size, in Blaxel's field. */
105
+ const TRIAL_CAP = "A trial sandbox has at most 4096 MB of memory (2 vCPUs); pass memory 4096 or add credit.";
85
106
  /** A Runtime SDK error as Blaxel's: ResponseError (SandboxGatewayError for a
86
107
  * 502, 503 or 504), NotSupportedError for a product Runtime has switched off,
87
108
  * CredentialsError when there is no Runtime key. Anything else passes through
@@ -97,9 +118,11 @@ export function translate(error) {
97
118
  return new NotSupportedError(error.code.replace(/_unavailable$/, ""), error.hint ?? "", error.message);
98
119
  if (error.status < 200 || error.status > 599)
99
120
  return error;
121
+ const trialCap = error.code === "invalid_trial";
122
+ const hint = trialCap ? undefined : (BLAXEL_HINTS[error.code] ?? error.hint);
100
123
  const message = [
101
- error.message,
102
- error.hint ? `Hint: ${error.hint}` : "",
124
+ trialCap ? TRIAL_CAP : error.message,
125
+ hint ? `Hint: ${hint}` : "",
103
126
  error.requestId ? `Request: ${error.requestId}` : "",
104
127
  ]
105
128
  .filter(Boolean)
@@ -107,7 +130,7 @@ export function translate(error) {
107
130
  const data = {
108
131
  error: message,
109
132
  code: error.code,
110
- ...(error.hint ? { hint: error.hint } : {}),
133
+ ...(hint ? { hint } : {}),
111
134
  ...(error.requestId ? { requestId: error.requestId } : {}),
112
135
  };
113
136
  const Kind = GATEWAY.has(error.status) ? SandboxGatewayError : ResponseError;
@@ -116,8 +139,8 @@ export function translate(error) {
116
139
  headers: { "content-type": "application/json" },
117
140
  }), data, data);
118
141
  out.runtimeCode = error.code;
119
- if (error.hint)
120
- out.hint = error.hint;
142
+ if (hint)
143
+ out.hint = hint;
121
144
  if (error.requestId)
122
145
  out.requestId = error.requestId;
123
146
  out.cause = error;
@@ -1,5 +1,5 @@
1
1
  import { type Config } from "./client.js";
2
- export { SandboxInstance, DEFAULT_IMAGE, DEFAULT_MEMORY, MEMORY_PER_VCPU, durationMs, lifetimeOf, networkRules, type SandboxListQuery, type SandboxForkOptions, type SandboxArchiveOptions, } from "./sandbox.js";
2
+ export { SandboxInstance, DEFAULT_IMAGE, DEFAULT_MEMORY, MEMORY_PER_VCPU, durationMs, imageRef, lifetimeOf, networkRules, type SandboxListQuery, type SandboxForkOptions, type SandboxArchiveOptions, } from "./sandbox.js";
3
3
  export { CodeInterpreter } from "./interpreter.js";
4
4
  export { SandboxFileSystem, retryOnTransient, type File, type FileWithContent, type Subdirectory, type Directory, type SuccessResponse, type FindMatch, type FindResponse, type FuzzySearchMatch, type FuzzySearchResponse, type ContentSearchMatch, type ContentSearchResponse, type CopyResponse, type WatchEvent, type SandboxFilesystemFile, type FilesystemSearchOptions, type FilesystemFindOptions, type FilesystemGrepOptions, } from "./filesystem.js";
5
5
  export { SandboxProcess, type ProcessRequest, type ProcessResponse, type ProcessRequestWithLog, type ProcessResponseWithLog, type PostProcessResponse, type GetProcessResponse, type GetProcessByIdentifierResponse, type DeleteProcessByIdentifierResponse, type DeleteProcessByIdentifierKillResponse, type ProcessLogs, } from "./process.js";
@@ -11,7 +11,7 @@ import { RuntimeError } from "../errors.js";
11
11
  import { initialize } from "./client.js";
12
12
  import { NotSupportedError } from "./errors.js";
13
13
  import { CODEGEN, DRIVES, SCHEDULES, SESSIONS, SYSTEM, unsupportedExport } from "./unsupported.js";
14
- export { SandboxInstance, DEFAULT_IMAGE, DEFAULT_MEMORY, MEMORY_PER_VCPU, durationMs, lifetimeOf, networkRules, } from "./sandbox.js";
14
+ export { SandboxInstance, DEFAULT_IMAGE, DEFAULT_MEMORY, MEMORY_PER_VCPU, durationMs, imageRef, lifetimeOf, networkRules, } from "./sandbox.js";
15
15
  export { CodeInterpreter } from "./interpreter.js";
16
16
  export { SandboxFileSystem, retryOnTransient, } from "./filesystem.js";
17
17
  export { SandboxProcess, } from "./process.js";
@@ -72,6 +72,13 @@ export declare function lifetimeOf(asked: {
72
72
  * domains, from the network itself or from its proxy (Blaxel's current
73
73
  * place for them). Anything else of the proxy is refused. */
74
74
  export declare function networkRules(network: SandboxNetwork): NonNullable<RuntimeCreate["network"]>;
75
+ /** A Blaxel image `ns/name:tag` as the Runtime image name and tag it maps to
76
+ * (Runtime names allow no "/"): every "/" becomes "-" (`ns-name`), and the
77
+ * tag is `latest` when none is given. The Python adapter maps the same way. */
78
+ export declare function imageRef(image: string): {
79
+ name: string;
80
+ tag: string;
81
+ };
75
82
  /** A sandbox with Blaxel's methods. `sandbox.withruntime` is the Runtime
76
83
  * sandbox underneath, for anything Blaxel has no name for. */
77
84
  export declare class SandboxInstance {
@@ -1,3 +1,4 @@
1
+ import { RuntimeError } from "../errors.js";
1
2
  import { clientFor, defaultRegion, } from "./client.js";
2
3
  import { checkEnvNames, envFileCommand, envFileLines, parseProcessLine, toRuntimePath, } from "./context.js";
3
4
  import { codeOf, guard, NotSupportedError, responseError, translate } from "./errors.js";
@@ -178,26 +179,38 @@ function checkRegion(region) {
178
179
  if (region !== undefined && region !== "" && !US_REGIONS.has(region))
179
180
  throw new NotSupportedError(`The region ${region}`, "Runtime runs in one US region: remove region (and BL_REGION), or use us-was-1.");
180
181
  }
181
- /** Where an image sends the create: Runtime's stock image, or a ready Runtime
182
- * image by name or id. */
182
+ /** A Blaxel image `ns/name:tag` as the Runtime image name and tag it maps to
183
+ * (Runtime names allow no "/"): every "/" becomes "-" (`ns-name`), and the
184
+ * tag is `latest` when none is given. The Python adapter maps the same way. */
185
+ export function imageRef(image) {
186
+ const slash = image.lastIndexOf("/");
187
+ const colon = image.lastIndexOf(":");
188
+ const [name, tag] = colon > slash ? [image.slice(0, colon), image.slice(colon + 1)] : [image, ""];
189
+ return { name: name.replaceAll("/", "-"), tag: tag || "latest" };
190
+ }
191
+ /** Where an image sends the create: Runtime's stock image, or the ready
192
+ * Runtime image `imageRef` maps it to. */
183
193
  async function resolveImage(client, image) {
184
194
  if (image === undefined || STOCK_IMAGE.test(image))
185
195
  return {};
186
196
  if (UUID.test(image))
187
197
  return { image };
188
- const name = image.replace(/:latest$/, "");
198
+ const { name, tag } = imageRef(image);
189
199
  let found;
190
200
  try {
191
- found = (await client.images.list({ name, state: "ready", limit: 1 })).data[0];
201
+ found = await client.images.resolve(`${name}:${tag}`);
192
202
  }
193
203
  catch (error) {
194
- // A name Runtime could not hold is simply not one of its images.
195
- if (codeOf(error) !== "invalid_request")
204
+ // Not an image, or a name Runtime could not hold: the same answer.
205
+ const status = error instanceof RuntimeError ? error.status : undefined;
206
+ if (status !== 400 && status !== 404)
196
207
  throw translate(error);
197
208
  }
198
- if (found)
209
+ if (found && found.state === "ready")
199
210
  return { image: found.id };
200
- throw new NotSupportedError(`The image ${image}, which is not a Runtime image`, `Build it as a Runtime image with that name and this call starts from it: \`npx withruntime image build --dockerfile Dockerfile --name ${name}\`, or runtime.images.build({ name: "${name}", image: "<registry reference>" }).`);
211
+ const feature = `The image ${image}, which is not a Runtime image`;
212
+ const alternative = `Build it as a Runtime image named ${name}: \`npx withruntime image build --dockerfile Dockerfile --name ${name} -t ${name}:${tag}\`. The code can keep "${image}": the adapter starts from ${name}:${tag}.`;
213
+ throw new NotSupportedError(feature, alternative, `${feature}, is not supported on Runtime. ${alternative}`);
201
214
  }
202
215
  /** Blaxel volumes (by name) as Runtime volumes. */
203
216
  async function resolveVolumes(client, volumes) {
package/dist/cli.js CHANGED
@@ -2150,8 +2150,8 @@ async function snapshot(argv, env, out) {
2150
2150
  }
2151
2151
  const IMAGE_HELP = `runtime image <command>
2152
2152
 
2153
- build [<folder>] [-f <Dockerfile>] | --from <image> | --pip <pkg>... --apt <pkg>... --npm <pkg>...
2154
- [-t name[:tag]]... [--target <stage>] [--build-arg K=V]... [--no-cache]
2153
+ build [<folder>] [--dockerfile|-f <Dockerfile>] | --from <image> | --pip <pkg>... --apt <pkg>... --npm <pkg>...
2154
+ [--name <name>] [--tag|-t name[:tag]]... [--target <stage>] [--build-arg K=V]... [--no-cache]
2155
2155
  [--start <command>] [--ready-port <port> | --ready-command <command>]
2156
2156
  [--max-image-mib 8192] [--disk-mib <1024-32768>] [--timeout <seconds>]
2157
2157
  Build and stream the log; prints the image id when ready.
@@ -1,5 +1,5 @@
1
1
  import { RuntimeError } from "./errors.js";
2
- export declare const VERSION = "0.8.0";
2
+ export declare const VERSION = "0.8.1";
3
3
  export declare const DEFAULT_BASE_URL = "https://api.withruntime.com";
4
4
  export type RequestOptions = {
5
5
  /** Sent as Idempotency-Key. Omit it: the SDK makes one per call and keeps
package/dist/transport.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ConnectionError, errorFor, RuntimeError, WAITS_FOR_ROOM } from "./errors.js";
2
2
  import { describeRoute, envFetch, openWebSocket } from "./proxy.js";
3
- export const VERSION = "0.8.0";
3
+ export const VERSION = "0.8.1";
4
4
  export const DEFAULT_BASE_URL = "https://api.withruntime.com";
5
5
  export function apiOrigin(value) {
6
6
  const url = new URL(value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "withruntime",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "license": "Apache-2.0",
5
5
  "mcpName": "com.withruntime/runtime",
6
6
  "type": "module",