withruntime 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DAYTONA.md +129 -0
- package/E2B.md +146 -0
- package/README.md +84 -12
- package/VERCEL.md +127 -0
- package/dist/ai/index.d.ts +33 -0
- package/dist/ai/index.js +38 -0
- package/dist/claude-agent-sdk/index.d.ts +7 -0
- package/dist/claude-agent-sdk/index.js +65 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +949 -105
- package/dist/client.d.ts +25 -2
- package/dist/client.js +14 -1
- package/dist/credentials.d.ts +10 -0
- package/dist/credentials.js +56 -1
- package/dist/daytona/client.d.ts +44 -0
- package/dist/daytona/client.js +40 -0
- package/dist/daytona/context.d.ts +26 -0
- package/dist/daytona/context.js +22 -0
- package/dist/daytona/daytona.d.ts +185 -0
- package/dist/daytona/daytona.js +416 -0
- package/dist/daytona/errors.d.ts +106 -0
- package/dist/daytona/errors.js +182 -0
- package/dist/daytona/filesystem.d.ts +84 -0
- package/dist/daytona/filesystem.js +206 -0
- package/dist/daytona/git.d.ts +33 -0
- package/dist/daytona/git.js +128 -0
- package/dist/daytona/image.d.ts +45 -0
- package/dist/daytona/image.js +140 -0
- package/dist/daytona/index.d.ts +50 -0
- package/dist/daytona/index.js +50 -0
- package/dist/daytona/process.d.ts +86 -0
- package/dist/daytona/process.js +339 -0
- package/dist/daytona/sandbox.d.ts +196 -0
- package/dist/daytona/sandbox.js +448 -0
- package/dist/e2b/client.d.ts +58 -0
- package/dist/e2b/client.js +66 -0
- package/dist/e2b/code-interpreter.d.ts +144 -0
- package/dist/e2b/code-interpreter.js +244 -0
- package/dist/e2b/commands.d.ts +100 -0
- package/dist/e2b/commands.js +268 -0
- package/dist/e2b/errors.d.ts +72 -0
- package/dist/e2b/errors.js +151 -0
- package/dist/e2b/filesystem.d.ts +82 -0
- package/dist/e2b/filesystem.js +143 -0
- package/dist/e2b/index.d.ts +28 -0
- package/dist/e2b/index.js +61 -0
- package/dist/e2b/sandbox.d.ts +203 -0
- package/dist/e2b/sandbox.js +490 -0
- package/dist/e2b/unsupported.d.ts +19 -0
- package/dist/e2b/unsupported.js +27 -0
- package/dist/errors.d.ts +10 -4
- package/dist/errors.js +26 -6
- package/dist/image-context.d.ts +39 -0
- package/dist/image-context.js +176 -0
- package/dist/index.d.ts +19 -2
- package/dist/index.js +17 -2
- package/dist/keys.d.ts +43 -0
- package/dist/keys.js +196 -0
- package/dist/login.js +27 -6
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +4 -3
- package/dist/openai-agents/index.d.ts +132 -0
- package/dist/openai-agents/index.js +791 -0
- package/dist/products/audit.d.ts +48 -0
- package/dist/products/audit.js +10 -0
- package/dist/products/images.d.ts +113 -7
- package/dist/products/images.js +186 -27
- package/dist/products/index.d.ts +11 -0
- package/dist/products/index.js +17 -1
- package/dist/products/observability.d.ts +186 -0
- package/dist/products/observability.js +117 -0
- package/dist/products/secrets.d.ts +40 -0
- package/dist/products/secrets.js +24 -0
- package/dist/products/switching.d.ts +89 -0
- package/dist/products/switching.js +25 -0
- package/dist/proxy.d.ts +30 -0
- package/dist/proxy.js +178 -0
- package/dist/sandbox.d.ts +30 -2
- package/dist/sandbox.js +101 -1
- package/dist/ssh.d.ts +21 -0
- package/dist/ssh.js +312 -0
- package/dist/tar.d.ts +1 -0
- package/dist/tar.js +4 -4
- package/dist/tools/index.d.ts +62 -0
- package/dist/tools/index.js +180 -0
- package/dist/transport.d.ts +12 -2
- package/dist/transport.js +76 -16
- package/dist/tunnel.d.ts +67 -0
- package/dist/tunnel.js +311 -0
- package/dist/types.d.ts +57 -1
- package/dist/vercel/client.d.ts +40 -0
- package/dist/vercel/client.js +39 -0
- package/dist/vercel/command.d.ts +70 -0
- package/dist/vercel/command.js +148 -0
- package/dist/vercel/errors.d.ts +53 -0
- package/dist/vercel/errors.js +113 -0
- package/dist/vercel/filesystem.d.ts +150 -0
- package/dist/vercel/filesystem.js +248 -0
- package/dist/vercel/index.d.ts +30 -0
- package/dist/vercel/index.js +38 -0
- package/dist/vercel/sandbox.d.ts +317 -0
- package/dist/vercel/sandbox.js +726 -0
- package/dist/vercel/snapshot.d.ts +93 -0
- package/dist/vercel/snapshot.js +123 -0
- package/package.json +69 -5
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { request } from "./client.js";
|
|
2
|
+
import { CommandExitError, guard, InvalidArgumentError, NotSupportedError, SandboxError, TimeoutError, translate, } from "./errors.js";
|
|
3
|
+
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
4
|
+
const LONGEST_MS = 86_400_000;
|
|
5
|
+
/** E2B's pids are numbers; Runtime's process ids are strings. The number is a
|
|
6
|
+
* 31-bit FNV-1a hash of the id, so every client derives the same one. */
|
|
7
|
+
export function pidOf(processId) {
|
|
8
|
+
let hash = 0x811c9dc5;
|
|
9
|
+
for (let i = 0; i < processId.length; i++) {
|
|
10
|
+
hash ^= processId.charCodeAt(i);
|
|
11
|
+
hash = Math.imul(hash, 0x01000193);
|
|
12
|
+
}
|
|
13
|
+
return hash >>> 1 || 1;
|
|
14
|
+
}
|
|
15
|
+
function refuseUser(user) {
|
|
16
|
+
if (user !== undefined && user !== "user")
|
|
17
|
+
throw new NotSupportedError(`Running as the user "${user}"`, 'Runtime runs commands as its sandbox owner, with passwordless sudo: prefix the command with "sudo" to run it as root.');
|
|
18
|
+
}
|
|
19
|
+
function timeoutFor(timeoutMs) {
|
|
20
|
+
if (timeoutMs === undefined)
|
|
21
|
+
return DEFAULT_TIMEOUT_MS;
|
|
22
|
+
if (timeoutMs === 0)
|
|
23
|
+
return LONGEST_MS;
|
|
24
|
+
return Math.min(timeoutMs, LONGEST_MS);
|
|
25
|
+
}
|
|
26
|
+
function timedOut(timeoutMs) {
|
|
27
|
+
return new TimeoutError(`Command timed out after ${timeoutMs} ms: this error is likely due to exceeding 'timeoutMs'. ` +
|
|
28
|
+
"Pass a larger 'timeoutMs', or 0 for no limit.");
|
|
29
|
+
}
|
|
30
|
+
/** A Runtime result as E2B's: a command killed by a signal has no exit code on
|
|
31
|
+
* Runtime; E2B reports -1. */
|
|
32
|
+
function toResult(result) {
|
|
33
|
+
const exitCode = result.exitCode ?? -1;
|
|
34
|
+
return {
|
|
35
|
+
exitCode,
|
|
36
|
+
stdout: result.stdout,
|
|
37
|
+
stderr: result.stderr,
|
|
38
|
+
...(exitCode === 0
|
|
39
|
+
? {}
|
|
40
|
+
: {
|
|
41
|
+
error: result.exitCode === null ? "terminated by a signal" : `exit status ${exitCode}`,
|
|
42
|
+
}),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function settle(result, timeoutMs) {
|
|
46
|
+
if (result.timedOut)
|
|
47
|
+
throw timedOut(timeoutMs);
|
|
48
|
+
const out = toResult(result);
|
|
49
|
+
if (out.exitCode !== 0)
|
|
50
|
+
throw new CommandExitError(out);
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
/** `sandbox.commands`: E2B's command module over Runtime's exec and processes. */
|
|
54
|
+
export class Commands {
|
|
55
|
+
#ctx;
|
|
56
|
+
constructor(ctx) {
|
|
57
|
+
this.#ctx = ctx;
|
|
58
|
+
}
|
|
59
|
+
#env(envs) {
|
|
60
|
+
const merged = { ...this.#ctx.envs, ...envs };
|
|
61
|
+
return Object.keys(merged).length ? { env: merged } : {};
|
|
62
|
+
}
|
|
63
|
+
async run(cmd, opts = {}) {
|
|
64
|
+
refuseUser(opts.user);
|
|
65
|
+
await this.#ctx.ensureHome(`${cmd}\n${opts.cwd ?? ""}`);
|
|
66
|
+
const timeoutMs = timeoutFor(opts.timeoutMs);
|
|
67
|
+
if (opts.background || opts.stdin) {
|
|
68
|
+
const handle = await this.#start(cmd, opts, timeoutMs);
|
|
69
|
+
return opts.background ? handle : handle.wait();
|
|
70
|
+
}
|
|
71
|
+
const result = await guard("sandbox", () => this.#ctx.runtime.exec(cmd, {
|
|
72
|
+
...(opts.cwd === undefined ? {} : { cwd: opts.cwd }),
|
|
73
|
+
...this.#env(opts.envs),
|
|
74
|
+
timeoutMs,
|
|
75
|
+
...(opts.onStdout ? { onStdout: (text) => void opts.onStdout(text) } : {}),
|
|
76
|
+
...(opts.onStderr ? { onStderr: (text) => void opts.onStderr(text) } : {}),
|
|
77
|
+
...(opts.signal ? { signal: opts.signal } : {}),
|
|
78
|
+
}));
|
|
79
|
+
return settle(result, timeoutMs);
|
|
80
|
+
}
|
|
81
|
+
async #start(cmd, opts, timeoutMs) {
|
|
82
|
+
const process = await guard("sandbox", () => this.#ctx.runtime.spawn(cmd, {
|
|
83
|
+
...(opts.cwd === undefined ? {} : { cwd: opts.cwd }),
|
|
84
|
+
...this.#env(opts.envs),
|
|
85
|
+
timeoutMs,
|
|
86
|
+
...(opts.stdin ? { stdin: "pipe" } : {}),
|
|
87
|
+
...request(opts),
|
|
88
|
+
}));
|
|
89
|
+
return new CommandHandle(process, {
|
|
90
|
+
stdin: opts.stdin === true,
|
|
91
|
+
timeoutMs,
|
|
92
|
+
...(opts.onStdout ? { onStdout: opts.onStdout } : {}),
|
|
93
|
+
...(opts.onStderr ? { onStderr: opts.onStderr } : {}),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/** Running commands. */
|
|
97
|
+
async list(_opts = {}) {
|
|
98
|
+
const processes = await guard("sandbox", () => this.#ctx.runtime.processes.list());
|
|
99
|
+
return processes.filter((info) => info.state === "running").map(describe);
|
|
100
|
+
}
|
|
101
|
+
async #find(pid) {
|
|
102
|
+
const processes = await guard("sandbox", () => this.#ctx.runtime.processes.list());
|
|
103
|
+
return processes.find((info) => pidOf(info.id) === pid && info.state === "running");
|
|
104
|
+
}
|
|
105
|
+
async #process(pid) {
|
|
106
|
+
const info = await this.#find(pid);
|
|
107
|
+
if (!info)
|
|
108
|
+
throw new SandboxError(`No running command with pid ${pid}.`);
|
|
109
|
+
return guard("sandbox", () => this.#ctx.runtime.processes.get(info.id));
|
|
110
|
+
}
|
|
111
|
+
/** Kills a command with SIGKILL, as E2B does. False when there is none. */
|
|
112
|
+
async kill(pid, _opts = {}) {
|
|
113
|
+
const info = await this.#find(pid);
|
|
114
|
+
if (!info)
|
|
115
|
+
return false;
|
|
116
|
+
const process = await guard("sandbox", () => this.#ctx.runtime.processes.get(info.id));
|
|
117
|
+
await guard("sandbox", () => process.kill("SIGKILL"));
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
/** Sends input to a command started with `stdin: true`. */
|
|
121
|
+
async sendStdin(pid, data, _opts = {}) {
|
|
122
|
+
const process = await this.#process(pid);
|
|
123
|
+
if (!process.info.stdinOpen)
|
|
124
|
+
throw new InvalidArgumentError(`The command with pid ${pid} was not started with stdin: true, so its input is closed.`);
|
|
125
|
+
await guard("sandbox", () => process.write(data));
|
|
126
|
+
}
|
|
127
|
+
async closeStdin(pid, _opts = {}) {
|
|
128
|
+
const process = await this.#process(pid);
|
|
129
|
+
await guard("sandbox", () => process.write("", { eof: true }));
|
|
130
|
+
}
|
|
131
|
+
/** Attaches to a running command; output from now on reaches the handle. */
|
|
132
|
+
async connect(pid, opts = {}) {
|
|
133
|
+
const process = await this.#process(pid);
|
|
134
|
+
return new CommandHandle(process, {
|
|
135
|
+
stdin: process.info.stdinOpen,
|
|
136
|
+
timeoutMs: timeoutFor(opts.timeoutMs),
|
|
137
|
+
cursor: process.info.outputBytes,
|
|
138
|
+
...(opts.onStdout ? { onStdout: opts.onStdout } : {}),
|
|
139
|
+
...(opts.onStderr ? { onStderr: opts.onStderr } : {}),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function describe(info) {
|
|
144
|
+
return {
|
|
145
|
+
pid: pidOf(info.id),
|
|
146
|
+
cmd: "/bin/bash",
|
|
147
|
+
args: ["-c", info.command],
|
|
148
|
+
envs: {},
|
|
149
|
+
cwd: info.cwd,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/** A command started in the background: E2B's CommandHandle over a Runtime
|
|
153
|
+
* process. Output streams into `stdout`/`stderr` and the callbacks as it
|
|
154
|
+
* arrives. */
|
|
155
|
+
export class CommandHandle {
|
|
156
|
+
pid;
|
|
157
|
+
#process;
|
|
158
|
+
#stdin;
|
|
159
|
+
#timeoutMs;
|
|
160
|
+
#abort = new AbortController();
|
|
161
|
+
#done;
|
|
162
|
+
#stdout = "";
|
|
163
|
+
#stderr = "";
|
|
164
|
+
#exit;
|
|
165
|
+
#failure;
|
|
166
|
+
#disconnected = false;
|
|
167
|
+
constructor(process, options) {
|
|
168
|
+
this.#process = process;
|
|
169
|
+
this.pid = pidOf(process.id);
|
|
170
|
+
this.#stdin = options.stdin;
|
|
171
|
+
this.#timeoutMs = options.timeoutMs;
|
|
172
|
+
this.#done = this.#follow(options);
|
|
173
|
+
}
|
|
174
|
+
async #follow(options) {
|
|
175
|
+
try {
|
|
176
|
+
for await (const event of this.#process.output({
|
|
177
|
+
...(options.cursor ? { cursor: options.cursor } : {}),
|
|
178
|
+
signal: this.#abort.signal,
|
|
179
|
+
})) {
|
|
180
|
+
if (event.type === "stdout") {
|
|
181
|
+
this.#stdout += event.data;
|
|
182
|
+
await options.onStdout?.(event.data);
|
|
183
|
+
}
|
|
184
|
+
else if (event.type === "stderr") {
|
|
185
|
+
this.#stderr += event.data;
|
|
186
|
+
await options.onStderr?.(event.data);
|
|
187
|
+
}
|
|
188
|
+
else if (event.type === "exit") {
|
|
189
|
+
this.#exit = { exitCode: event.exitCode, timedOut: event.timedOut };
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
if (!this.#disconnected) {
|
|
195
|
+
const translated = translate(error, "sandbox");
|
|
196
|
+
this.#failure =
|
|
197
|
+
translated instanceof Error ? translated : new SandboxError(String(translated));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/** The exit code, or undefined while the command runs. */
|
|
202
|
+
get exitCode() {
|
|
203
|
+
return this.#exit ? (this.#exit.exitCode ?? -1) : undefined;
|
|
204
|
+
}
|
|
205
|
+
get error() {
|
|
206
|
+
return this.#exit ? this.#result().error : undefined;
|
|
207
|
+
}
|
|
208
|
+
get stdout() {
|
|
209
|
+
return this.#stdout;
|
|
210
|
+
}
|
|
211
|
+
get stderr() {
|
|
212
|
+
return this.#stderr;
|
|
213
|
+
}
|
|
214
|
+
#result() {
|
|
215
|
+
return toResult({
|
|
216
|
+
exitCode: this.#exit?.exitCode ?? null,
|
|
217
|
+
stdout: this.#stdout,
|
|
218
|
+
stderr: this.#stderr,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
/** Waits for the command to end. Throws CommandExitError on a non-zero exit,
|
|
222
|
+
* as E2B does. */
|
|
223
|
+
async wait() {
|
|
224
|
+
await this.#done;
|
|
225
|
+
if (this.#failure)
|
|
226
|
+
throw this.#failure;
|
|
227
|
+
if (!this.#exit)
|
|
228
|
+
throw new SandboxError(this.#disconnected
|
|
229
|
+
? "Disconnected from the command before it ended; reconnect with commands.connect(pid)."
|
|
230
|
+
: "The command's output ended without an exit.");
|
|
231
|
+
return settle({
|
|
232
|
+
exitCode: this.#exit.exitCode,
|
|
233
|
+
stdout: this.#stdout,
|
|
234
|
+
stderr: this.#stderr,
|
|
235
|
+
timedOut: this.#exit.timedOut,
|
|
236
|
+
}, this.#timeoutMs);
|
|
237
|
+
}
|
|
238
|
+
/** Stops receiving output; the command keeps running. */
|
|
239
|
+
async disconnect() {
|
|
240
|
+
this.#disconnected = true;
|
|
241
|
+
this.#abort.abort();
|
|
242
|
+
await Promise.resolve();
|
|
243
|
+
}
|
|
244
|
+
/** Kills the command with SIGKILL. False when it had already ended. */
|
|
245
|
+
async kill() {
|
|
246
|
+
if (this.#exit)
|
|
247
|
+
return false;
|
|
248
|
+
try {
|
|
249
|
+
await guard("sandbox", () => this.#process.kill("SIGKILL"));
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
if (error instanceof SandboxError && error.statusCode === 404)
|
|
254
|
+
return false;
|
|
255
|
+
throw error;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
async sendStdin(data, _opts = {}) {
|
|
259
|
+
if (!this.#stdin)
|
|
260
|
+
throw new InvalidArgumentError("The command was not started with stdin: true, so its input is closed.");
|
|
261
|
+
await guard("sandbox", () => this.#process.write(data));
|
|
262
|
+
}
|
|
263
|
+
async closeStdin(_opts = {}) {
|
|
264
|
+
if (!this.#stdin)
|
|
265
|
+
throw new InvalidArgumentError("The command was not started with stdin: true.");
|
|
266
|
+
await guard("sandbox", () => this.#process.write("", { eof: true }));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/** Base class for E2B's sandbox errors. */
|
|
2
|
+
export declare class SandboxError extends Error {
|
|
3
|
+
statusCode?: number;
|
|
4
|
+
code?: string;
|
|
5
|
+
hint?: string;
|
|
6
|
+
requestId?: string;
|
|
7
|
+
constructor(message?: string);
|
|
8
|
+
}
|
|
9
|
+
export declare class TimeoutError extends SandboxError {
|
|
10
|
+
}
|
|
11
|
+
export declare class InvalidArgumentError extends SandboxError {
|
|
12
|
+
}
|
|
13
|
+
export declare class NotEnoughSpaceError extends SandboxError {
|
|
14
|
+
}
|
|
15
|
+
export declare class NotFoundError extends SandboxError {
|
|
16
|
+
}
|
|
17
|
+
export declare class FileNotFoundError extends NotFoundError {
|
|
18
|
+
}
|
|
19
|
+
export declare class SandboxNotFoundError extends NotFoundError {
|
|
20
|
+
}
|
|
21
|
+
export declare class TemplateError extends SandboxError {
|
|
22
|
+
}
|
|
23
|
+
export declare class RateLimitError extends SandboxError {
|
|
24
|
+
}
|
|
25
|
+
/** E2B's AuthenticationError extends Error, not SandboxError. */
|
|
26
|
+
export declare class AuthenticationError extends Error {
|
|
27
|
+
code?: string;
|
|
28
|
+
hint?: string;
|
|
29
|
+
requestId?: string;
|
|
30
|
+
constructor(message: string);
|
|
31
|
+
}
|
|
32
|
+
/** E2B's ServiceBusyError extends Error, not SandboxError. */
|
|
33
|
+
export declare class ServiceBusyError extends Error {
|
|
34
|
+
readonly statusCode = 503;
|
|
35
|
+
code?: string;
|
|
36
|
+
hint?: string;
|
|
37
|
+
requestId?: string;
|
|
38
|
+
constructor(message: string);
|
|
39
|
+
}
|
|
40
|
+
/** A call that E2B supports and Runtime does not, or not in the same way.
|
|
41
|
+
* Thrown before anything is done, never after doing something different.
|
|
42
|
+
* `feature` names what was asked for; `alternative` says what to use on
|
|
43
|
+
* Runtime instead. */
|
|
44
|
+
export declare class NotSupportedError extends SandboxError {
|
|
45
|
+
readonly feature: string;
|
|
46
|
+
readonly alternative: string;
|
|
47
|
+
constructor(feature: string, alternative: string, message?: string);
|
|
48
|
+
}
|
|
49
|
+
/** What `commands.run` resolves with, as in E2B. */
|
|
50
|
+
export interface CommandResult {
|
|
51
|
+
exitCode: number;
|
|
52
|
+
error?: string;
|
|
53
|
+
stdout: string;
|
|
54
|
+
stderr: string;
|
|
55
|
+
}
|
|
56
|
+
/** Thrown by `commands.run` and `CommandHandle.wait` when the command exits
|
|
57
|
+
* with a code other than 0. It carries the result, as in E2B. */
|
|
58
|
+
export declare class CommandExitError extends SandboxError implements CommandResult {
|
|
59
|
+
#private;
|
|
60
|
+
constructor(result: CommandResult);
|
|
61
|
+
get exitCode(): number;
|
|
62
|
+
get error(): string | undefined;
|
|
63
|
+
get stdout(): string;
|
|
64
|
+
get stderr(): string;
|
|
65
|
+
}
|
|
66
|
+
/** Which object a call was about, to name a 404 the way E2B would. */
|
|
67
|
+
export type Subject = "sandbox" | "file" | "other";
|
|
68
|
+
/** Turns a Runtime SDK error into the E2B error a caller written for E2B
|
|
69
|
+
* expects. Anything that is not a Runtime error passes through unchanged. */
|
|
70
|
+
export declare function translate(error: unknown, subject?: Subject): unknown;
|
|
71
|
+
/** Runs `work`, turning any Runtime error into its E2B counterpart. */
|
|
72
|
+
export declare function guard<T>(subject: Subject, work: () => Promise<T>): Promise<T>;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { RuntimeError } from "../errors.js";
|
|
2
|
+
function attach(error, detail) {
|
|
3
|
+
const target = error;
|
|
4
|
+
if (detail.code !== undefined)
|
|
5
|
+
target.code = detail.code;
|
|
6
|
+
if (detail.hint !== undefined)
|
|
7
|
+
target.hint = detail.hint;
|
|
8
|
+
if (detail.requestId !== undefined)
|
|
9
|
+
target.requestId = detail.requestId;
|
|
10
|
+
if (detail.cause !== undefined)
|
|
11
|
+
target.cause = detail.cause;
|
|
12
|
+
}
|
|
13
|
+
/** Base class for E2B's sandbox errors. */
|
|
14
|
+
export class SandboxError extends Error {
|
|
15
|
+
statusCode;
|
|
16
|
+
code;
|
|
17
|
+
hint;
|
|
18
|
+
requestId;
|
|
19
|
+
constructor(message) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = new.target.name;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export class TimeoutError extends SandboxError {
|
|
25
|
+
}
|
|
26
|
+
export class InvalidArgumentError extends SandboxError {
|
|
27
|
+
}
|
|
28
|
+
export class NotEnoughSpaceError extends SandboxError {
|
|
29
|
+
}
|
|
30
|
+
export class NotFoundError extends SandboxError {
|
|
31
|
+
}
|
|
32
|
+
export class FileNotFoundError extends NotFoundError {
|
|
33
|
+
}
|
|
34
|
+
export class SandboxNotFoundError extends NotFoundError {
|
|
35
|
+
}
|
|
36
|
+
export class TemplateError extends SandboxError {
|
|
37
|
+
}
|
|
38
|
+
export class RateLimitError extends SandboxError {
|
|
39
|
+
}
|
|
40
|
+
/** E2B's AuthenticationError extends Error, not SandboxError. */
|
|
41
|
+
export class AuthenticationError extends Error {
|
|
42
|
+
code;
|
|
43
|
+
hint;
|
|
44
|
+
requestId;
|
|
45
|
+
constructor(message) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.name = new.target.name;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** E2B's ServiceBusyError extends Error, not SandboxError. */
|
|
51
|
+
export class ServiceBusyError extends Error {
|
|
52
|
+
statusCode = 503;
|
|
53
|
+
code;
|
|
54
|
+
hint;
|
|
55
|
+
requestId;
|
|
56
|
+
constructor(message) {
|
|
57
|
+
super(message);
|
|
58
|
+
this.name = new.target.name;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/** A call that E2B supports and Runtime does not, or not in the same way.
|
|
62
|
+
* Thrown before anything is done, never after doing something different.
|
|
63
|
+
* `feature` names what was asked for; `alternative` says what to use on
|
|
64
|
+
* Runtime instead. */
|
|
65
|
+
export class NotSupportedError extends SandboxError {
|
|
66
|
+
feature;
|
|
67
|
+
alternative;
|
|
68
|
+
constructor(feature, alternative, message) {
|
|
69
|
+
super(message ?? `${feature} is not supported on Runtime. ${alternative}`);
|
|
70
|
+
this.feature = feature;
|
|
71
|
+
this.alternative = alternative;
|
|
72
|
+
this.code = "not_supported";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/** Thrown by `commands.run` and `CommandHandle.wait` when the command exits
|
|
76
|
+
* with a code other than 0. It carries the result, as in E2B. */
|
|
77
|
+
export class CommandExitError extends SandboxError {
|
|
78
|
+
#result;
|
|
79
|
+
constructor(result) {
|
|
80
|
+
super(`Command exited with code ${result.exitCode} and error:\n${result.stderr}`);
|
|
81
|
+
this.#result = result;
|
|
82
|
+
}
|
|
83
|
+
get exitCode() {
|
|
84
|
+
return this.#result.exitCode;
|
|
85
|
+
}
|
|
86
|
+
get error() {
|
|
87
|
+
return this.#result.error;
|
|
88
|
+
}
|
|
89
|
+
get stdout() {
|
|
90
|
+
return this.#result.stdout;
|
|
91
|
+
}
|
|
92
|
+
get stderr() {
|
|
93
|
+
return this.#result.stderr;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** Turns a Runtime SDK error into the E2B error a caller written for E2B
|
|
97
|
+
* expects. Anything that is not a Runtime error passes through unchanged. */
|
|
98
|
+
export function translate(error, subject = "other") {
|
|
99
|
+
if (!(error instanceof RuntimeError))
|
|
100
|
+
return error;
|
|
101
|
+
const message = [
|
|
102
|
+
error.message,
|
|
103
|
+
error.hint ? `Hint: ${error.hint}` : "",
|
|
104
|
+
error.requestId ? `Request: ${error.requestId}` : "",
|
|
105
|
+
]
|
|
106
|
+
.filter(Boolean)
|
|
107
|
+
.join("\n");
|
|
108
|
+
let out;
|
|
109
|
+
if (error.status === 503 && error.code.endsWith("_unavailable"))
|
|
110
|
+
// A product switched off on purpose (forks, previews): Runtime's own words
|
|
111
|
+
// say what is off and what to use meanwhile.
|
|
112
|
+
out = new NotSupportedError(error.code.replace(/_unavailable$/, ""), error.hint ?? "", message);
|
|
113
|
+
else if (error.status === 401 || error.status === 403)
|
|
114
|
+
out = new AuthenticationError(message);
|
|
115
|
+
else if (error.code === "file_not_found" || (error.status === 404 && subject === "file"))
|
|
116
|
+
out = new FileNotFoundError(message);
|
|
117
|
+
else if (error.status === 404 && subject === "sandbox")
|
|
118
|
+
out = new SandboxNotFoundError(message);
|
|
119
|
+
else if (error.status === 404)
|
|
120
|
+
out = new NotFoundError(message);
|
|
121
|
+
else if (error.status === 429)
|
|
122
|
+
out = new RateLimitError(message);
|
|
123
|
+
else if (/space|disk_full|no_room/.test(error.code))
|
|
124
|
+
out = new NotEnoughSpaceError(message);
|
|
125
|
+
else if (error.status === 400 || error.status === 413 || error.status === 422)
|
|
126
|
+
out = new InvalidArgumentError(message);
|
|
127
|
+
else if (error.status === 503)
|
|
128
|
+
out = new ServiceBusyError(message);
|
|
129
|
+
else if (error.code === "command_timeout")
|
|
130
|
+
out = new TimeoutError(message);
|
|
131
|
+
else
|
|
132
|
+
out = new SandboxError(message);
|
|
133
|
+
if (out instanceof SandboxError && error.status)
|
|
134
|
+
out.statusCode = error.status;
|
|
135
|
+
attach(out, {
|
|
136
|
+
code: error.code,
|
|
137
|
+
...(error.hint ? { hint: error.hint } : {}),
|
|
138
|
+
...(error.requestId ? { requestId: error.requestId } : {}),
|
|
139
|
+
cause: error,
|
|
140
|
+
});
|
|
141
|
+
return out;
|
|
142
|
+
}
|
|
143
|
+
/** Runs `work`, turning any Runtime error into its E2B counterpart. */
|
|
144
|
+
export async function guard(subject, work) {
|
|
145
|
+
try {
|
|
146
|
+
return await work();
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
throw translate(error, subject);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { SandboxContext, Username } from "./commands.js";
|
|
2
|
+
export declare enum FileType {
|
|
3
|
+
FILE = "file",
|
|
4
|
+
DIR = "dir",
|
|
5
|
+
SYMLINK = "symlink"
|
|
6
|
+
}
|
|
7
|
+
export interface WriteInfo {
|
|
8
|
+
name: string;
|
|
9
|
+
type?: FileType;
|
|
10
|
+
path: string;
|
|
11
|
+
metadata?: Record<string, string>;
|
|
12
|
+
}
|
|
13
|
+
export interface EntryInfo extends WriteInfo {
|
|
14
|
+
size: number;
|
|
15
|
+
mode: number;
|
|
16
|
+
permissions: string;
|
|
17
|
+
/** Runtime's listing does not say who owns a file: always "". */
|
|
18
|
+
owner: string;
|
|
19
|
+
/** Runtime's listing does not say a file's group: always "". */
|
|
20
|
+
group: string;
|
|
21
|
+
modifiedTime?: Date;
|
|
22
|
+
symlinkTarget?: string;
|
|
23
|
+
}
|
|
24
|
+
export type WriteEntry = {
|
|
25
|
+
path: string;
|
|
26
|
+
data: string | ArrayBuffer | Blob | ReadableStream;
|
|
27
|
+
};
|
|
28
|
+
export interface FilesystemRequestOpts {
|
|
29
|
+
requestTimeoutMs?: number;
|
|
30
|
+
signal?: AbortSignal;
|
|
31
|
+
/** Only the default user; see CommandStartOpts.user. */
|
|
32
|
+
user?: Username;
|
|
33
|
+
}
|
|
34
|
+
export interface FilesystemReadOpts extends FilesystemRequestOpts {
|
|
35
|
+
/** Accepted; Runtime's transfer is already compressed where it helps. */
|
|
36
|
+
gzip?: boolean;
|
|
37
|
+
streamIdleTimeoutMs?: number;
|
|
38
|
+
}
|
|
39
|
+
export interface FilesystemWriteOpts extends FilesystemRequestOpts {
|
|
40
|
+
/** Accepted; Runtime chooses the transfer. */
|
|
41
|
+
gzip?: boolean;
|
|
42
|
+
useOctetStream?: boolean;
|
|
43
|
+
/** Refused: Runtime does not keep metadata on files. */
|
|
44
|
+
metadata?: Record<string, string>;
|
|
45
|
+
}
|
|
46
|
+
export interface FilesystemListOpts extends FilesystemRequestOpts {
|
|
47
|
+
depth?: number;
|
|
48
|
+
}
|
|
49
|
+
/** `sandbox.files`: E2B's filesystem module over Runtime's files API. Relative
|
|
50
|
+
* paths resolve against the home directory, as in E2B (Runtime's is
|
|
51
|
+
* /workspace). */
|
|
52
|
+
export declare class Filesystem {
|
|
53
|
+
#private;
|
|
54
|
+
constructor(ctx: SandboxContext);
|
|
55
|
+
read(path: string, opts?: FilesystemReadOpts & {
|
|
56
|
+
format?: "text";
|
|
57
|
+
}): Promise<string>;
|
|
58
|
+
read(path: string, opts: FilesystemReadOpts & {
|
|
59
|
+
format: "bytes";
|
|
60
|
+
}): Promise<Uint8Array>;
|
|
61
|
+
read(path: string, opts: FilesystemReadOpts & {
|
|
62
|
+
format: "blob";
|
|
63
|
+
}): Promise<Blob>;
|
|
64
|
+
read(path: string, opts: FilesystemReadOpts & {
|
|
65
|
+
format: "stream";
|
|
66
|
+
}): Promise<ReadableStream<Uint8Array>>;
|
|
67
|
+
/** Writes a file, making its directories, and overwrites one that exists. */
|
|
68
|
+
write(path: string, data: string | ArrayBuffer | Blob | ReadableStream, opts?: FilesystemWriteOpts): Promise<WriteInfo>;
|
|
69
|
+
write(files: WriteEntry[], opts?: FilesystemWriteOpts): Promise<WriteInfo[]>;
|
|
70
|
+
writeFiles(files: WriteEntry[], opts?: FilesystemWriteOpts): Promise<WriteInfo[]>;
|
|
71
|
+
/** A directory's entries, hidden ones included; `depth` goes deeper. */
|
|
72
|
+
list(path: string, opts?: FilesystemListOpts): Promise<EntryInfo[]>;
|
|
73
|
+
/** Makes a directory and its parents. False when it already existed. */
|
|
74
|
+
makeDir(path: string, opts?: FilesystemRequestOpts): Promise<boolean>;
|
|
75
|
+
/** Moves a file or directory, replacing a file at the new path, as E2B does. */
|
|
76
|
+
rename(oldPath: string, newPath: string, opts?: FilesystemRequestOpts): Promise<EntryInfo>;
|
|
77
|
+
/** Removes a file, or a directory with everything in it. */
|
|
78
|
+
remove(path: string, opts?: FilesystemRequestOpts): Promise<void>;
|
|
79
|
+
exists(path: string, opts?: FilesystemRequestOpts): Promise<boolean>;
|
|
80
|
+
getInfo(path: string, opts?: FilesystemRequestOpts): Promise<EntryInfo>;
|
|
81
|
+
watchDir(): Promise<never>;
|
|
82
|
+
}
|