withruntime 0.4.0 → 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 +2 -2
- package/README.md +47 -9
- 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 +939 -102
- 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/sandbox.d.ts +22 -2
- package/dist/e2b/sandbox.js +39 -7
- 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 +14 -2
- 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/sandbox.d.ts +26 -1
- 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 +31 -4
- 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 +56 -4
package/dist/types.d.ts
CHANGED
|
@@ -16,6 +16,16 @@ export type SandboxInfo = {
|
|
|
16
16
|
cpuFloorMillis: number;
|
|
17
17
|
pausable: boolean;
|
|
18
18
|
timeoutSeconds: number;
|
|
19
|
+
/** Pauses after this many idle seconds; 0 is never. */
|
|
20
|
+
idlePauseSeconds?: number;
|
|
21
|
+
/** True for the default idle pause, which pauses only a sandbox nothing has used yet. */
|
|
22
|
+
idlePauseUnusedOnly?: boolean;
|
|
23
|
+
/** A request (exec, files, terminal, a visit to a shared port) wakes it when paused. */
|
|
24
|
+
autoWake?: boolean;
|
|
25
|
+
/** Its lease renews itself while credit lasts, and its disk is kept after a stop. */
|
|
26
|
+
persistent?: boolean;
|
|
27
|
+
/** The last exec, file, terminal, desktop or preview request, to within a minute. */
|
|
28
|
+
lastActiveAt?: string | null;
|
|
19
29
|
onLeaseEnd: "pause" | "stop";
|
|
20
30
|
createdAt: string;
|
|
21
31
|
readyAt: string | null;
|
|
@@ -28,6 +38,16 @@ export type SandboxInfo = {
|
|
|
28
38
|
heldMicros: number;
|
|
29
39
|
simulated?: boolean;
|
|
30
40
|
replayed?: boolean;
|
|
41
|
+
/** On a create from an image with a start command: whether it started,
|
|
42
|
+
* became ready (readyMs), timed out, or exited first. */
|
|
43
|
+
start?: {
|
|
44
|
+
state: "started" | "ready" | "timeout" | "exited" | "not_running";
|
|
45
|
+
processId?: string;
|
|
46
|
+
exitCode?: number | null;
|
|
47
|
+
readyMs?: number;
|
|
48
|
+
};
|
|
49
|
+
/** Present and true when getOrCreate answered a sandbox that already held the name. */
|
|
50
|
+
reused?: boolean;
|
|
31
51
|
[key: string]: unknown;
|
|
32
52
|
};
|
|
33
53
|
export type CreateSandbox = {
|
|
@@ -46,6 +66,18 @@ export type CreateSandbox = {
|
|
|
46
66
|
pausable?: boolean;
|
|
47
67
|
/** What happens when timeoutSeconds runs out: "pause" (default) or "stop". */
|
|
48
68
|
onLeaseEnd?: "pause" | "stop";
|
|
69
|
+
/** Pause after this many seconds with no exec, file, terminal, desktop or
|
|
70
|
+
* preview request (60 to 86400; 0 never). A request wakes it again. */
|
|
71
|
+
idlePauseSeconds?: number;
|
|
72
|
+
/** A request to a paused sandbox wakes it. Default true. */
|
|
73
|
+
autoWake?: boolean;
|
|
74
|
+
/** Keep it running while credit lasts (its lease renews itself) and keep its
|
|
75
|
+
* disk after a stop, for restart(). Paid only. */
|
|
76
|
+
persistent?: boolean;
|
|
77
|
+
/** The most it may cost over its whole life, in microdollars. */
|
|
78
|
+
maxTotalCostMicros?: number;
|
|
79
|
+
/** With name: return the sandbox that already has the name, woken if paused. */
|
|
80
|
+
getOrCreate?: boolean;
|
|
49
81
|
maxCostMicros?: number;
|
|
50
82
|
/** Network rules from the first start; the same shape as sandbox.network.set.
|
|
51
83
|
* Omit for the public web on ports 80 and 443. */
|
|
@@ -55,7 +87,9 @@ export type CreateSandbox = {
|
|
|
55
87
|
deny?: string[];
|
|
56
88
|
connect?: string[];
|
|
57
89
|
};
|
|
58
|
-
/** A ready image
|
|
90
|
+
/** A ready image (runtime.images.build): its id, name (its latest tag),
|
|
91
|
+
* name:tag or name@version. When the image has a start command, create
|
|
92
|
+
* answers once its ready check passes and says how in `start`. */
|
|
59
93
|
image?: string;
|
|
60
94
|
/** A ready snapshot's id: the sandbox starts as a copy of it, with its shape. Not with image. */
|
|
61
95
|
snapshot?: string;
|
|
@@ -173,3 +207,25 @@ export type Usage = {
|
|
|
173
207
|
[key: string]: unknown;
|
|
174
208
|
};
|
|
175
209
|
export type FeedbackKind = "bug" | "missing_feature" | "competitor_gap" | "migration_blocker" | "docs" | "pricing" | "praise" | "other";
|
|
210
|
+
/** What `sandbox.update()` changes; fields left out stay as they are. */
|
|
211
|
+
export type SandboxSettings = {
|
|
212
|
+
name?: string;
|
|
213
|
+
labels?: Record<string, string>;
|
|
214
|
+
/** A request to a paused sandbox wakes it. */
|
|
215
|
+
autoWake?: boolean;
|
|
216
|
+
/** Pause after this many seconds with no activity, counted from now; 0 never. */
|
|
217
|
+
idlePauseSeconds?: number;
|
|
218
|
+
/** Keep it running while credit lasts and keep its disk after a stop. Paid only. */
|
|
219
|
+
persistent?: boolean;
|
|
220
|
+
/** Lifetime cap in microdollars; null removes it. */
|
|
221
|
+
maxTotalCostMicros?: number | null;
|
|
222
|
+
};
|
|
223
|
+
/** How `sandbox.keepAlive()` extends the lease. */
|
|
224
|
+
export type KeepAliveOptions = {
|
|
225
|
+
/** How often it checks, in seconds. Default 60. */
|
|
226
|
+
everySeconds?: number;
|
|
227
|
+
/** How much lease it keeps ahead of now, in seconds (60 to 3600). Default 600. */
|
|
228
|
+
marginSeconds?: number;
|
|
229
|
+
/** Called with an error an extension met; the loop carries on. */
|
|
230
|
+
onError?: (error: unknown) => void;
|
|
231
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Runtime } from "../client.js";
|
|
2
|
+
/** The Runtime sandbox the adapter drives. */
|
|
3
|
+
export type RuntimeSandbox = Awaited<ReturnType<Runtime["sandboxes"]["create"]>>;
|
|
4
|
+
/** What `Sandbox.create` sends to Runtime. */
|
|
5
|
+
export type RuntimeCreate = NonNullable<Parameters<Runtime["sandboxes"]["create"]>[0]>;
|
|
6
|
+
/** Vercel's credentials. A Vercel token is never sent anywhere: only a
|
|
7
|
+
* Runtime key (`rtcloud_...`) given as `token` is used. */
|
|
8
|
+
export interface Credentials {
|
|
9
|
+
token?: string;
|
|
10
|
+
teamId?: string;
|
|
11
|
+
projectId?: string;
|
|
12
|
+
}
|
|
13
|
+
/** Runtime-only options, for code that has been through the switch. The key
|
|
14
|
+
* is `withruntime` because Vercel's own `runtime` field names a Node.js or
|
|
15
|
+
* Python version. */
|
|
16
|
+
export interface WithRuntime {
|
|
17
|
+
/** A Runtime client to use instead of one made from the key. */
|
|
18
|
+
client?: Runtime;
|
|
19
|
+
/** Fields sent as they are with the create, over the adapter's own: for
|
|
20
|
+
* example `{ funding: "trial" }` or `{ memoryMiB: 8192 }`. */
|
|
21
|
+
create?: Partial<RuntimeCreate>;
|
|
22
|
+
}
|
|
23
|
+
/** The key to use: a Runtime key given as `token`, then RUNTIME_API_KEY, then
|
|
24
|
+
* the key `npx withruntime login` saved (undefined lets the Runtime SDK find
|
|
25
|
+
* it). A Vercel token passed as `token` is refused, never sent. */
|
|
26
|
+
export declare function pickKey(token?: string): string | undefined;
|
|
27
|
+
/** The Runtime client for these options: an explicit client, else one per key,
|
|
28
|
+
* made once and reused so its connections stay open. */
|
|
29
|
+
export declare function clientFor(opts?: Credentials & {
|
|
30
|
+
withruntime?: WithRuntime;
|
|
31
|
+
}): Runtime;
|
|
32
|
+
/** Test hook: forget the cached clients. */
|
|
33
|
+
export declare function resetClients(): void;
|
|
34
|
+
export declare function signalOf(opts?: {
|
|
35
|
+
signal?: AbortSignal;
|
|
36
|
+
}): {
|
|
37
|
+
signal: AbortSignal;
|
|
38
|
+
} | {
|
|
39
|
+
signal?: undefined;
|
|
40
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Runtime } from "../client.js";
|
|
2
|
+
import { AuthenticationError } from "./errors.js";
|
|
3
|
+
const env = (name) => typeof process === "undefined" ? undefined : process.env[name];
|
|
4
|
+
const isRuntimeKey = (key) => key.startsWith("rtcloud_");
|
|
5
|
+
const clients = new Map();
|
|
6
|
+
/** The key to use: a Runtime key given as `token`, then RUNTIME_API_KEY, then
|
|
7
|
+
* the key `npx withruntime login` saved (undefined lets the Runtime SDK find
|
|
8
|
+
* it). A Vercel token passed as `token` is refused, never sent. */
|
|
9
|
+
export function pickKey(token) {
|
|
10
|
+
if (token !== undefined && isRuntimeKey(token))
|
|
11
|
+
return token;
|
|
12
|
+
const runtimeKey = env("RUNTIME_API_KEY");
|
|
13
|
+
if (runtimeKey)
|
|
14
|
+
return runtimeKey;
|
|
15
|
+
if (token !== undefined && token !== "")
|
|
16
|
+
throw new AuthenticationError("The token passed is a Vercel token, and it was not sent. Set RUNTIME_API_KEY to a Runtime key " +
|
|
17
|
+
"(https://withruntime.com/account/keys, or run `npx withruntime login`), or pass the Runtime key as token.");
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
/** The Runtime client for these options: an explicit client, else one per key,
|
|
21
|
+
* made once and reused so its connections stay open. */
|
|
22
|
+
export function clientFor(opts = {}) {
|
|
23
|
+
if (opts.withruntime?.client)
|
|
24
|
+
return opts.withruntime.client;
|
|
25
|
+
const key = pickKey(opts.token);
|
|
26
|
+
let client = clients.get(key ?? "");
|
|
27
|
+
if (!client) {
|
|
28
|
+
client = new Runtime(key ? { apiKey: key } : {});
|
|
29
|
+
clients.set(key ?? "", client);
|
|
30
|
+
}
|
|
31
|
+
return client;
|
|
32
|
+
}
|
|
33
|
+
/** Test hook: forget the cached clients. */
|
|
34
|
+
export function resetClients() {
|
|
35
|
+
clients.clear();
|
|
36
|
+
}
|
|
37
|
+
export function signalOf(opts) {
|
|
38
|
+
return opts?.signal ? { signal: opts.signal } : {};
|
|
39
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Process } from "../sandbox.js";
|
|
2
|
+
/** One piece of a command's output, as Vercel's `logs()` yields it. */
|
|
3
|
+
export type LogOutputLine = {
|
|
4
|
+
stream: "stdout" | "stderr";
|
|
5
|
+
data: string;
|
|
6
|
+
};
|
|
7
|
+
/** Cached output from a command, as in Vercel. */
|
|
8
|
+
export interface CommandOutput {
|
|
9
|
+
stdout: string;
|
|
10
|
+
stderr: string;
|
|
11
|
+
}
|
|
12
|
+
export type Signal = "SIGTERM" | "SIGKILL" | "SIGINT" | "SIGHUP" | "SIGQUIT" | "SIGUSR1" | "SIGUSR2" | number;
|
|
13
|
+
/** A command killed by a signal has no exit code on Runtime. One killed at
|
|
14
|
+
* its `timeoutMs` (SIGKILL) reports 137, as a shell does; any other signal
|
|
15
|
+
* reports -1. */
|
|
16
|
+
export declare function exitCodeOf(exitCode: number | null, timedOut: boolean): number;
|
|
17
|
+
type Init = {
|
|
18
|
+
id: string;
|
|
19
|
+
cwd: string;
|
|
20
|
+
startedAt: number;
|
|
21
|
+
sandboxName: string;
|
|
22
|
+
process?: Process;
|
|
23
|
+
lines?: LogOutputLine[];
|
|
24
|
+
};
|
|
25
|
+
/** A command in a sandbox, as Vercel's `Command`. A detached one runs on:
|
|
26
|
+
* `wait()` for its end, `logs()` for its output as it happens. `cmdId` is
|
|
27
|
+
* Runtime's process id. */
|
|
28
|
+
export declare class Command {
|
|
29
|
+
#private;
|
|
30
|
+
exitCode: number | null;
|
|
31
|
+
durationMs?: number;
|
|
32
|
+
protected readonly init: Init;
|
|
33
|
+
constructor(init: Init);
|
|
34
|
+
get cmdId(): string;
|
|
35
|
+
get cwd(): string;
|
|
36
|
+
get startedAt(): number;
|
|
37
|
+
/** The command's output from its start, as it happens:
|
|
38
|
+
* `for await (const log of cmd.logs()) process.stdout.write(log.data)`. */
|
|
39
|
+
logs(opts?: {
|
|
40
|
+
signal?: AbortSignal;
|
|
41
|
+
}): AsyncGenerator<LogOutputLine, void, void> & Disposable & {
|
|
42
|
+
close(): void;
|
|
43
|
+
};
|
|
44
|
+
/** Waits for the command to end. */
|
|
45
|
+
wait(params?: {
|
|
46
|
+
signal?: AbortSignal;
|
|
47
|
+
}): Promise<CommandFinished>;
|
|
48
|
+
/** stdout, stderr, or both in the order they were written. For a detached
|
|
49
|
+
* command this waits for it to end. */
|
|
50
|
+
output(stream?: "stdout" | "stderr" | "both", opts?: {
|
|
51
|
+
signal?: AbortSignal;
|
|
52
|
+
}): Promise<string>;
|
|
53
|
+
stdout(opts?: {
|
|
54
|
+
signal?: AbortSignal;
|
|
55
|
+
}): Promise<string>;
|
|
56
|
+
stderr(opts?: {
|
|
57
|
+
signal?: AbortSignal;
|
|
58
|
+
}): Promise<string>;
|
|
59
|
+
/** Sends a signal, SIGTERM by default. */
|
|
60
|
+
kill(signal?: Signal, _opts?: {
|
|
61
|
+
abortSignal?: AbortSignal;
|
|
62
|
+
}): Promise<void>;
|
|
63
|
+
}
|
|
64
|
+
/** A command that has ended; `exitCode` is set. */
|
|
65
|
+
export declare class CommandFinished extends Command {
|
|
66
|
+
exitCode: number;
|
|
67
|
+
constructor(init: Init, exitCode: number, durationMs?: number);
|
|
68
|
+
wait(): Promise<CommandFinished>;
|
|
69
|
+
}
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { guard, StreamError, translate } from "./errors.js";
|
|
2
|
+
const SIGNAL_NUMBERS = {
|
|
3
|
+
1: "SIGHUP",
|
|
4
|
+
2: "SIGINT",
|
|
5
|
+
3: "SIGQUIT",
|
|
6
|
+
9: "SIGKILL",
|
|
7
|
+
10: "SIGUSR1",
|
|
8
|
+
12: "SIGUSR2",
|
|
9
|
+
15: "SIGTERM",
|
|
10
|
+
};
|
|
11
|
+
/** A command killed by a signal has no exit code on Runtime. One killed at
|
|
12
|
+
* its `timeoutMs` (SIGKILL) reports 137, as a shell does; any other signal
|
|
13
|
+
* reports -1. */
|
|
14
|
+
export function exitCodeOf(exitCode, timedOut) {
|
|
15
|
+
if (exitCode !== null)
|
|
16
|
+
return exitCode;
|
|
17
|
+
return timedOut ? 137 : -1;
|
|
18
|
+
}
|
|
19
|
+
function join(lines, stream) {
|
|
20
|
+
return lines
|
|
21
|
+
.filter((line) => stream === "both" || line.stream === stream)
|
|
22
|
+
.map((line) => line.data)
|
|
23
|
+
.join("");
|
|
24
|
+
}
|
|
25
|
+
/** A command in a sandbox, as Vercel's `Command`. A detached one runs on:
|
|
26
|
+
* `wait()` for its end, `logs()` for its output as it happens. `cmdId` is
|
|
27
|
+
* Runtime's process id. */
|
|
28
|
+
export class Command {
|
|
29
|
+
exitCode = null;
|
|
30
|
+
durationMs;
|
|
31
|
+
init;
|
|
32
|
+
#finished;
|
|
33
|
+
constructor(init) {
|
|
34
|
+
this.init = init;
|
|
35
|
+
}
|
|
36
|
+
get cmdId() {
|
|
37
|
+
return this.init.id;
|
|
38
|
+
}
|
|
39
|
+
get cwd() {
|
|
40
|
+
return this.init.cwd;
|
|
41
|
+
}
|
|
42
|
+
get startedAt() {
|
|
43
|
+
return this.init.startedAt;
|
|
44
|
+
}
|
|
45
|
+
/** The command's output from its start, as it happens:
|
|
46
|
+
* `for await (const log of cmd.logs()) process.stdout.write(log.data)`. */
|
|
47
|
+
logs(opts = {}) {
|
|
48
|
+
const abort = new AbortController();
|
|
49
|
+
const forward = () => abort.abort();
|
|
50
|
+
opts.signal?.addEventListener("abort", forward, { once: true });
|
|
51
|
+
const lines = this.init.lines;
|
|
52
|
+
const process = this.init.process;
|
|
53
|
+
const sandboxName = this.init.sandboxName;
|
|
54
|
+
async function* read() {
|
|
55
|
+
try {
|
|
56
|
+
if (lines) {
|
|
57
|
+
yield* lines;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
for await (const event of process.output({ signal: abort.signal })) {
|
|
62
|
+
if (event.type === "stdout" || event.type === "stderr")
|
|
63
|
+
yield { stream: event.type, data: event.data };
|
|
64
|
+
if (event.type === "exit")
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (abort.signal.aborted)
|
|
70
|
+
return;
|
|
71
|
+
const translated = translate(error, sandboxName);
|
|
72
|
+
throw translated instanceof Error && translated !== error
|
|
73
|
+
? translated
|
|
74
|
+
: new StreamError("stream_failed", String(error.message ?? error), process.id);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
opts.signal?.removeEventListener("abort", forward);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const generator = read();
|
|
82
|
+
return Object.assign(generator, {
|
|
83
|
+
close: () => abort.abort(),
|
|
84
|
+
[Symbol.dispose]: () => abort.abort(),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/** Waits for the command to end. */
|
|
88
|
+
wait(params = {}) {
|
|
89
|
+
this.#finished ??= this.#follow(params);
|
|
90
|
+
return this.#finished;
|
|
91
|
+
}
|
|
92
|
+
async #follow(params) {
|
|
93
|
+
const lines = [];
|
|
94
|
+
let exit;
|
|
95
|
+
await guard(async () => {
|
|
96
|
+
for await (const event of this.init.process.output(params.signal ? { signal: params.signal } : {})) {
|
|
97
|
+
if (event.type === "stdout" || event.type === "stderr")
|
|
98
|
+
lines.push({ stream: event.type, data: event.data });
|
|
99
|
+
else if (event.type === "exit")
|
|
100
|
+
exit = {
|
|
101
|
+
exitCode: event.exitCode,
|
|
102
|
+
timedOut: event.timedOut,
|
|
103
|
+
...(event.durationMs === undefined ? {} : { durationMs: event.durationMs }),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}, this.init.sandboxName);
|
|
107
|
+
if (!exit)
|
|
108
|
+
throw new StreamError("stream_ended", "The command's output ended before it exited.", this.init.process.id);
|
|
109
|
+
this.exitCode = exitCodeOf(exit.exitCode, exit.timedOut);
|
|
110
|
+
if (exit.durationMs !== undefined)
|
|
111
|
+
this.durationMs = exit.durationMs;
|
|
112
|
+
return new CommandFinished({ ...this.init, lines }, this.exitCode, exit.durationMs ?? undefined);
|
|
113
|
+
}
|
|
114
|
+
/** stdout, stderr, or both in the order they were written. For a detached
|
|
115
|
+
* command this waits for it to end. */
|
|
116
|
+
async output(stream = "both", opts = {}) {
|
|
117
|
+
if (this.init.lines)
|
|
118
|
+
return join(this.init.lines, stream);
|
|
119
|
+
return (await this.wait(opts)).output(stream);
|
|
120
|
+
}
|
|
121
|
+
stdout(opts = {}) {
|
|
122
|
+
return this.output("stdout", opts);
|
|
123
|
+
}
|
|
124
|
+
stderr(opts = {}) {
|
|
125
|
+
return this.output("stderr", opts);
|
|
126
|
+
}
|
|
127
|
+
/** Sends a signal, SIGTERM by default. */
|
|
128
|
+
async kill(signal = "SIGTERM", _opts = {}) {
|
|
129
|
+
if (!this.init.process)
|
|
130
|
+
return;
|
|
131
|
+
const name = typeof signal === "number" ? SIGNAL_NUMBERS[signal] : signal;
|
|
132
|
+
if (!name)
|
|
133
|
+
throw new RangeError(`Signal ${signal} has no name Runtime knows; use SIGTERM, SIGKILL, SIGINT, SIGHUP, SIGQUIT, SIGUSR1 or SIGUSR2.`);
|
|
134
|
+
await guard(() => this.init.process.kill(name), this.init.sandboxName);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** A command that has ended; `exitCode` is set. */
|
|
138
|
+
export class CommandFinished extends Command {
|
|
139
|
+
constructor(init, exitCode, durationMs) {
|
|
140
|
+
super(init);
|
|
141
|
+
this.exitCode = exitCode;
|
|
142
|
+
if (durationMs !== undefined)
|
|
143
|
+
this.durationMs = durationMs;
|
|
144
|
+
}
|
|
145
|
+
wait() {
|
|
146
|
+
return Promise.resolve(this);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
interface Options<ErrorData> {
|
|
2
|
+
message?: string;
|
|
3
|
+
json?: ErrorData;
|
|
4
|
+
text?: string;
|
|
5
|
+
sandboxName?: string;
|
|
6
|
+
sessionId?: string;
|
|
7
|
+
}
|
|
8
|
+
/** An error answer from the API. `response.status` is the HTTP status, and
|
|
9
|
+
* `json` is `{ error: { code, message } }` as Vercel's is. */
|
|
10
|
+
export declare class APIError<ErrorData = unknown> extends Error {
|
|
11
|
+
response: Response;
|
|
12
|
+
json?: ErrorData;
|
|
13
|
+
text?: string;
|
|
14
|
+
sandboxName?: string;
|
|
15
|
+
sessionId?: string;
|
|
16
|
+
code?: string;
|
|
17
|
+
hint?: string;
|
|
18
|
+
requestId?: string;
|
|
19
|
+
constructor(response: Response, options?: Options<ErrorData>);
|
|
20
|
+
}
|
|
21
|
+
/** A command's output stream failed, as Vercel's StreamError. */
|
|
22
|
+
export declare class StreamError extends Error {
|
|
23
|
+
code: string;
|
|
24
|
+
sessionId: string;
|
|
25
|
+
constructor(code: string, message: string, sessionId: string);
|
|
26
|
+
}
|
|
27
|
+
/** A Vercel token was given where a Runtime key is needed. */
|
|
28
|
+
export declare class AuthenticationError extends Error {
|
|
29
|
+
constructor(message: string);
|
|
30
|
+
}
|
|
31
|
+
/** A call that Vercel Sandbox supports and Runtime does not, or not in the
|
|
32
|
+
* same way. Thrown before anything is done, never after doing something
|
|
33
|
+
* different. `feature` names what was asked for; `alternative` says what to
|
|
34
|
+
* use on Runtime instead. */
|
|
35
|
+
export declare class NotSupportedError extends Error {
|
|
36
|
+
readonly code = "not_supported";
|
|
37
|
+
readonly feature: string;
|
|
38
|
+
readonly alternative: string;
|
|
39
|
+
constructor(feature: string, alternative: string, message?: string);
|
|
40
|
+
}
|
|
41
|
+
/** A Runtime SDK error as Vercel's APIError, or NotSupportedError for a
|
|
42
|
+
* product Runtime has switched off. Anything else passes through unchanged. */
|
|
43
|
+
export declare function translate(error: unknown, sandboxName?: string): unknown;
|
|
44
|
+
/** Runs `work`, turning any Runtime error into Vercel's. */
|
|
45
|
+
export declare function guard<T>(work: () => Promise<T>, sandboxName?: string): Promise<T>;
|
|
46
|
+
/** A node:fs-style error, as Vercel's `sandbox.fs` throws. */
|
|
47
|
+
export declare function fsError(code: "ENOENT" | "EEXIST" | "EISDIR" | "ENOTDIR", syscall: string, path: string): Error & {
|
|
48
|
+
code: "ENOENT" | "EEXIST" | "EISDIR" | "ENOTDIR";
|
|
49
|
+
errno: number;
|
|
50
|
+
syscall: string;
|
|
51
|
+
path: string;
|
|
52
|
+
};
|
|
53
|
+
export {};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { RuntimeError } from "../errors.js";
|
|
2
|
+
/** An error answer from the API. `response.status` is the HTTP status, and
|
|
3
|
+
* `json` is `{ error: { code, message } }` as Vercel's is. */
|
|
4
|
+
export class APIError extends Error {
|
|
5
|
+
response;
|
|
6
|
+
json;
|
|
7
|
+
text;
|
|
8
|
+
sandboxName;
|
|
9
|
+
sessionId;
|
|
10
|
+
code;
|
|
11
|
+
hint;
|
|
12
|
+
requestId;
|
|
13
|
+
constructor(response, options = {}) {
|
|
14
|
+
super(options.message ?? `Status code ${response.status} is not ok`);
|
|
15
|
+
this.name = new.target.name;
|
|
16
|
+
this.response = response;
|
|
17
|
+
if (options.json !== undefined)
|
|
18
|
+
this.json = options.json;
|
|
19
|
+
if (options.text !== undefined)
|
|
20
|
+
this.text = options.text;
|
|
21
|
+
if (options.sandboxName !== undefined)
|
|
22
|
+
this.sandboxName = options.sandboxName;
|
|
23
|
+
if (options.sessionId !== undefined)
|
|
24
|
+
this.sessionId = options.sessionId;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** A command's output stream failed, as Vercel's StreamError. */
|
|
28
|
+
export class StreamError extends Error {
|
|
29
|
+
code;
|
|
30
|
+
sessionId;
|
|
31
|
+
constructor(code, message, sessionId) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = new.target.name;
|
|
34
|
+
this.code = code;
|
|
35
|
+
this.sessionId = sessionId;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** A Vercel token was given where a Runtime key is needed. */
|
|
39
|
+
export class AuthenticationError extends Error {
|
|
40
|
+
constructor(message) {
|
|
41
|
+
super(message);
|
|
42
|
+
this.name = new.target.name;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** A call that Vercel Sandbox supports and Runtime does not, or not in the
|
|
46
|
+
* same way. Thrown before anything is done, never after doing something
|
|
47
|
+
* different. `feature` names what was asked for; `alternative` says what to
|
|
48
|
+
* use on Runtime instead. */
|
|
49
|
+
export class NotSupportedError extends Error {
|
|
50
|
+
code = "not_supported";
|
|
51
|
+
feature;
|
|
52
|
+
alternative;
|
|
53
|
+
constructor(feature, alternative, message) {
|
|
54
|
+
super(message ?? `${feature} is not supported on Runtime. ${alternative}`);
|
|
55
|
+
this.name = new.target.name;
|
|
56
|
+
this.feature = feature;
|
|
57
|
+
this.alternative = alternative;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** A Runtime SDK error as Vercel's APIError, or NotSupportedError for a
|
|
61
|
+
* product Runtime has switched off. Anything else passes through unchanged. */
|
|
62
|
+
export function translate(error, sandboxName) {
|
|
63
|
+
if (!(error instanceof RuntimeError))
|
|
64
|
+
return error;
|
|
65
|
+
const message = [
|
|
66
|
+
error.message,
|
|
67
|
+
error.hint ? `Hint: ${error.hint}` : "",
|
|
68
|
+
error.requestId ? `Request: ${error.requestId}` : "",
|
|
69
|
+
]
|
|
70
|
+
.filter(Boolean)
|
|
71
|
+
.join("\n");
|
|
72
|
+
if (error.status === 503 && error.code.endsWith("_unavailable"))
|
|
73
|
+
return new NotSupportedError(error.code.replace(/_unavailable$/, ""), error.hint ?? "", message);
|
|
74
|
+
if (error.status < 200 || error.status > 599)
|
|
75
|
+
return error;
|
|
76
|
+
const json = { error: { code: error.code, message: error.message } };
|
|
77
|
+
const out = new APIError(new Response(JSON.stringify(json), {
|
|
78
|
+
status: error.status,
|
|
79
|
+
headers: { "content-type": "application/json" },
|
|
80
|
+
}), { message, json, text: JSON.stringify(json), ...(sandboxName ? { sandboxName } : {}) });
|
|
81
|
+
out.code = error.code;
|
|
82
|
+
if (error.hint)
|
|
83
|
+
out.hint = error.hint;
|
|
84
|
+
if (error.requestId)
|
|
85
|
+
out.requestId = error.requestId;
|
|
86
|
+
out.cause = error;
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
/** Runs `work`, turning any Runtime error into Vercel's. */
|
|
90
|
+
export async function guard(work, sandboxName) {
|
|
91
|
+
try {
|
|
92
|
+
return await work();
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
throw translate(error, sandboxName);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** A node:fs-style error, as Vercel's `sandbox.fs` throws. */
|
|
99
|
+
export function fsError(code, syscall, path) {
|
|
100
|
+
const text = {
|
|
101
|
+
ENOENT: "no such file or directory",
|
|
102
|
+
EEXIST: "file already exists",
|
|
103
|
+
EISDIR: "illegal operation on a directory",
|
|
104
|
+
ENOTDIR: "not a directory",
|
|
105
|
+
}[code];
|
|
106
|
+
const errno = { ENOENT: -2, EEXIST: -17, EISDIR: -21, ENOTDIR: -20 }[code];
|
|
107
|
+
return Object.assign(new Error(`${code}: ${text}, ${syscall} '${path}'`), {
|
|
108
|
+
code,
|
|
109
|
+
errno,
|
|
110
|
+
syscall,
|
|
111
|
+
path,
|
|
112
|
+
});
|
|
113
|
+
}
|