withruntime 0.8.4 → 0.10.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/BLAXEL.md +4 -4
- package/COMPATIBILITY.md +145 -0
- package/DAYTONA.md +13 -7
- package/E2B.md +34 -20
- package/README.md +22 -11
- package/README.src.md +22 -11
- package/VERCEL.md +13 -3
- package/compatibility-lock.json +357 -0
- package/dist/ai-harness/index.js +2 -2
- package/dist/api-defaults.d.ts +17 -0
- package/dist/api-defaults.js +22 -0
- package/dist/blaxel/errors.js +1 -1
- package/dist/blaxel/lookup.d.ts +3 -2
- package/dist/blaxel/lookup.js +3 -2
- package/dist/blaxel/pagination.d.ts +2 -2
- package/dist/blaxel/pagination.js +45 -17
- package/dist/blaxel/preview.d.ts +3 -2
- package/dist/blaxel/preview.js +16 -14
- package/dist/blaxel/process.d.ts +1 -1
- package/dist/blaxel/process.js +66 -11
- package/dist/blaxel/sandbox.d.ts +2 -0
- package/dist/blaxel/sandbox.js +2 -0
- package/dist/blaxel/snapshot.js +3 -5
- package/dist/cli-words.d.ts +27 -0
- package/dist/cli-words.js +135 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +351 -85
- package/dist/client.d.ts +33 -4
- package/dist/client.js +47 -12
- package/dist/cloudflare/identity.d.ts +7 -0
- package/dist/cloudflare/identity.js +13 -0
- package/dist/cloudflare/index.d.ts +303 -0
- package/dist/cloudflare/index.js +592 -0
- package/dist/cloudflare/worker.d.ts +419 -0
- package/dist/cloudflare/worker.js +447 -0
- package/dist/codesandbox/guest-service.d.ts +31 -0
- package/dist/codesandbox/guest-service.js +490 -0
- package/dist/codesandbox/index.d.ts +386 -0
- package/dist/codesandbox/index.js +1347 -0
- package/dist/compat/core.d.ts +40 -0
- package/dist/compat/core.js +238 -0
- package/dist/compat/files.d.ts +26 -0
- package/dist/compat/files.js +133 -0
- package/dist/compat/shell.d.ts +4 -0
- package/dist/compat/shell.js +165 -0
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +107 -38
- package/dist/daytona/context.js +2 -1
- package/dist/daytona/daytona.js +11 -4
- package/dist/daytona/filesystem.d.ts +17 -4
- package/dist/daytona/filesystem.js +121 -38
- package/dist/daytona/index.d.ts +1 -1
- package/dist/daytona/process.d.ts +1 -1
- package/dist/daytona/process.js +27 -4
- package/dist/daytona/sandbox.d.ts +1 -1
- package/dist/daytona/sandbox.js +16 -7
- package/dist/e2b/client.js +1 -1
- package/dist/e2b/code-interpreter.d.ts +4 -5
- package/dist/e2b/code-interpreter.js +54 -56
- package/dist/e2b/commands.d.ts +35 -12
- package/dist/e2b/commands.js +169 -73
- package/dist/e2b/errors.js +1 -1
- package/dist/e2b/filesystem.d.ts +8 -6
- package/dist/e2b/filesystem.js +127 -28
- package/dist/e2b/index.d.ts +1 -0
- package/dist/e2b/index.js +1 -0
- package/dist/e2b/pty.d.ts +24 -0
- package/dist/e2b/pty.js +80 -0
- package/dist/e2b/sandbox.d.ts +6 -4
- package/dist/e2b/sandbox.js +59 -25
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +15 -2
- package/dist/file-source.d.ts +16 -0
- package/dist/file-source.js +162 -0
- package/dist/freestyle/index.d.ts +309 -0
- package/dist/freestyle/index.js +343 -0
- package/dist/index.d.ts +3 -1
- package/dist/login.js +16 -10
- package/dist/mcp.js +2 -2
- package/dist/modal/index.d.ts +205 -0
- package/dist/modal/index.js +486 -0
- package/dist/network-cli.js +112 -44
- package/dist/openai-agents/index.js +18 -71
- package/dist/page.d.ts +1 -1
- package/dist/page.js +2 -2
- package/dist/products/audit.d.ts +3 -0
- package/dist/products/index.d.ts +14 -0
- package/dist/products/index.js +12 -0
- package/dist/products/interpreter.d.ts +11 -4
- package/dist/products/interpreter.js +33 -9
- package/dist/products/jobs.js +3 -2
- package/dist/products/limits.d.ts +10 -0
- package/dist/products/observability.d.ts +44 -0
- package/dist/products/observability.js +6 -0
- package/dist/products/previews.d.ts +18 -4
- package/dist/products/previews.js +14 -5
- package/dist/products/tailscale.d.ts +57 -0
- package/dist/products/tailscale.js +17 -0
- package/dist/products/volumes.d.ts +42 -0
- package/dist/products/volumes.js +36 -0
- package/dist/products/watch.d.ts +25 -4
- package/dist/products/watch.js +132 -18
- package/dist/proxy.js +3 -1
- package/dist/runloop/index.d.ts +326 -0
- package/dist/runloop/index.js +535 -0
- package/dist/runloop/pagination.d.ts +46 -0
- package/dist/runloop/pagination.js +82 -0
- package/dist/runloop/snapshots.d.ts +52 -0
- package/dist/runloop/snapshots.js +244 -0
- package/dist/sandbox.d.ts +76 -16
- package/dist/sandbox.js +696 -135
- package/dist/snapshots.d.ts +20 -5
- package/dist/snapshots.js +11 -1
- package/dist/sprites/index.d.ts +251 -0
- package/dist/sprites/index.js +588 -0
- package/dist/tar.d.ts +10 -1
- package/dist/tar.js +311 -35
- package/dist/tools/index.js +2 -1
- package/dist/transport.d.ts +23 -8
- package/dist/transport.js +247 -127
- package/dist/types.d.ts +52 -3
- package/dist/vercel/command.d.ts +1 -1
- package/dist/vercel/command.js +15 -3
- package/dist/vercel/errors.d.ts +2 -2
- package/dist/vercel/errors.js +19 -2
- package/dist/vercel/filesystem.d.ts +21 -18
- package/dist/vercel/filesystem.js +95 -48
- package/dist/vercel/sandbox.d.ts +8 -8
- package/dist/vercel/sandbox.js +159 -64
- package/dist/vercel/snapshot.d.ts +3 -2
- package/dist/vercel/snapshot.js +9 -5
- package/package.json +46 -3
package/dist/vercel/command.js
CHANGED
|
@@ -47,18 +47,25 @@ export class Command {
|
|
|
47
47
|
logs(opts = {}) {
|
|
48
48
|
const abort = new AbortController();
|
|
49
49
|
const forward = () => abort.abort();
|
|
50
|
-
opts.signal?.
|
|
50
|
+
if (opts.signal?.aborted)
|
|
51
|
+
abort.abort();
|
|
52
|
+
else
|
|
53
|
+
opts.signal?.addEventListener("abort", forward, { once: true });
|
|
51
54
|
const lines = this.init.lines;
|
|
52
55
|
const process = this.init.process;
|
|
53
56
|
const sandboxName = this.init.sandboxName;
|
|
54
57
|
async function* read() {
|
|
55
58
|
try {
|
|
59
|
+
if (abort.signal.aborted)
|
|
60
|
+
return;
|
|
56
61
|
if (lines) {
|
|
57
62
|
yield* lines;
|
|
58
63
|
return;
|
|
59
64
|
}
|
|
60
65
|
try {
|
|
61
66
|
for await (const event of process.output({ signal: abort.signal })) {
|
|
67
|
+
if (event.type === "truncated")
|
|
68
|
+
throw new StreamError("output_truncated", "Some command output is no longer available.", process.id);
|
|
62
69
|
if (event.type === "stdout" || event.type === "stderr")
|
|
63
70
|
yield { stream: event.type, data: event.data };
|
|
64
71
|
if (event.type === "exit")
|
|
@@ -68,6 +75,8 @@ export class Command {
|
|
|
68
75
|
catch (error) {
|
|
69
76
|
if (abort.signal.aborted)
|
|
70
77
|
return;
|
|
78
|
+
if (error instanceof StreamError)
|
|
79
|
+
throw error;
|
|
71
80
|
const translated = translate(error, sandboxName);
|
|
72
81
|
throw translated instanceof Error && translated !== error
|
|
73
82
|
? translated
|
|
@@ -94,6 +103,8 @@ export class Command {
|
|
|
94
103
|
let exit;
|
|
95
104
|
await guard(async () => {
|
|
96
105
|
for await (const event of this.init.process.output(params.signal ? { signal: params.signal } : {})) {
|
|
106
|
+
if (event.type === "truncated")
|
|
107
|
+
throw new StreamError("output_truncated", "Some command output is no longer available.", this.init.process.id);
|
|
97
108
|
if (event.type === "stdout" || event.type === "stderr")
|
|
98
109
|
lines.push({ stream: event.type, data: event.data });
|
|
99
110
|
else if (event.type === "exit")
|
|
@@ -125,13 +136,14 @@ export class Command {
|
|
|
125
136
|
return this.output("stderr", opts);
|
|
126
137
|
}
|
|
127
138
|
/** Sends a signal, SIGTERM by default. */
|
|
128
|
-
async kill(signal = "SIGTERM",
|
|
139
|
+
async kill(signal = "SIGTERM", opts = {}) {
|
|
140
|
+
opts.abortSignal?.throwIfAborted();
|
|
129
141
|
if (!this.init.process)
|
|
130
142
|
return;
|
|
131
143
|
const name = typeof signal === "number" ? SIGNAL_NUMBERS[signal] : signal;
|
|
132
144
|
if (!name)
|
|
133
145
|
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);
|
|
146
|
+
await guard(() => this.init.process.kill(name, opts.abortSignal ? { signal: opts.abortSignal } : {}), this.init.sandboxName);
|
|
135
147
|
}
|
|
136
148
|
}
|
|
137
149
|
/** A command that has ended; `exitCode` is set. */
|
package/dist/vercel/errors.d.ts
CHANGED
|
@@ -44,8 +44,8 @@ export declare function translate(error: unknown, sandboxName?: string): unknown
|
|
|
44
44
|
/** Runs `work`, turning any Runtime error into Vercel's. */
|
|
45
45
|
export declare function guard<T>(work: () => Promise<T>, sandboxName?: string): Promise<T>;
|
|
46
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" | "
|
|
47
|
+
export declare function fsError(code: "ENOENT" | "EEXIST" | "EISDIR" | "ENOTDIR" | "EACCES" | "ENOTEMPTY" | "EINVAL", syscall: string, path: string): Error & {
|
|
48
|
+
code: "ENOENT" | "ENOTDIR" | "EEXIST" | "EISDIR" | "EACCES" | "ENOTEMPTY" | "EINVAL";
|
|
49
49
|
errno: number;
|
|
50
50
|
syscall: string;
|
|
51
51
|
path: string;
|
package/dist/vercel/errors.js
CHANGED
|
@@ -73,7 +73,13 @@ export function translate(error, sandboxName) {
|
|
|
73
73
|
return new NotSupportedError(error.code.replace(/_unavailable$/, ""), error.hint ?? "", message);
|
|
74
74
|
if (error.status < 200 || error.status > 599)
|
|
75
75
|
return error;
|
|
76
|
-
const json = {
|
|
76
|
+
const json = {
|
|
77
|
+
error: {
|
|
78
|
+
code: error.code,
|
|
79
|
+
message: error.message,
|
|
80
|
+
...(error.details ? { details: error.details } : {}),
|
|
81
|
+
},
|
|
82
|
+
};
|
|
77
83
|
const out = new APIError(new Response(JSON.stringify(json), {
|
|
78
84
|
status: error.status,
|
|
79
85
|
headers: { "content-type": "application/json" },
|
|
@@ -102,8 +108,19 @@ export function fsError(code, syscall, path) {
|
|
|
102
108
|
EEXIST: "file already exists",
|
|
103
109
|
EISDIR: "illegal operation on a directory",
|
|
104
110
|
ENOTDIR: "not a directory",
|
|
111
|
+
EACCES: "permission denied",
|
|
112
|
+
ENOTEMPTY: "directory not empty",
|
|
113
|
+
EINVAL: "invalid argument",
|
|
114
|
+
}[code];
|
|
115
|
+
const errno = {
|
|
116
|
+
ENOENT: -2,
|
|
117
|
+
EEXIST: -17,
|
|
118
|
+
EISDIR: -21,
|
|
119
|
+
ENOTDIR: -20,
|
|
120
|
+
EACCES: -13,
|
|
121
|
+
ENOTEMPTY: -39,
|
|
122
|
+
EINVAL: -22,
|
|
105
123
|
}[code];
|
|
106
|
-
const errno = { ENOENT: -2, EEXIST: -17, EISDIR: -21, ENOTDIR: -20 }[code];
|
|
107
124
|
return Object.assign(new Error(`${code}: ${text}, ${syscall} '${path}'`), {
|
|
108
125
|
code,
|
|
109
126
|
errno,
|
|
@@ -5,7 +5,10 @@ export interface FsContext {
|
|
|
5
5
|
readonly name: string;
|
|
6
6
|
files(): Promise<Files>;
|
|
7
7
|
/** Runs argv without a shell; resolves with the exit code and output. */
|
|
8
|
-
run(argv: string[]
|
|
8
|
+
run(argv: string[], options?: {
|
|
9
|
+
stdin?: Uint8Array;
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
}): Promise<{
|
|
9
12
|
exitCode: number | null;
|
|
10
13
|
stdout: string;
|
|
11
14
|
stderr: string;
|
|
@@ -28,8 +31,8 @@ export declare class Stats {
|
|
|
28
31
|
readonly ctimeMs: number;
|
|
29
32
|
readonly birthtime: Date;
|
|
30
33
|
readonly birthtimeMs: number;
|
|
31
|
-
readonly uid
|
|
32
|
-
readonly gid
|
|
34
|
+
readonly uid: number;
|
|
35
|
+
readonly gid: number;
|
|
33
36
|
readonly dev = 0;
|
|
34
37
|
readonly ino = 0;
|
|
35
38
|
readonly nlink = 1;
|
|
@@ -86,7 +89,7 @@ export declare class FileSystem {
|
|
|
86
89
|
mkdir(path: string, options?: {
|
|
87
90
|
recursive?: boolean;
|
|
88
91
|
signal?: AbortSignal;
|
|
89
|
-
} | number): Promise<
|
|
92
|
+
} | number): Promise<void>;
|
|
90
93
|
readdir(path: string, options?: {
|
|
91
94
|
signal?: AbortSignal;
|
|
92
95
|
withFileTypes?: false;
|
|
@@ -95,14 +98,14 @@ export declare class FileSystem {
|
|
|
95
98
|
signal?: AbortSignal;
|
|
96
99
|
withFileTypes: true;
|
|
97
100
|
}): Promise<Dirent[]>;
|
|
98
|
-
lstat(path: string,
|
|
101
|
+
lstat(path: string, options?: {
|
|
99
102
|
signal?: AbortSignal;
|
|
100
103
|
}): Promise<Stats>;
|
|
101
104
|
/** Follows symbolic links. */
|
|
102
105
|
stat(path: string, options?: {
|
|
103
106
|
signal?: AbortSignal;
|
|
104
107
|
}): Promise<Stats>;
|
|
105
|
-
unlink(path: string,
|
|
108
|
+
unlink(path: string, options?: {
|
|
106
109
|
signal?: AbortSignal;
|
|
107
110
|
}): Promise<void>;
|
|
108
111
|
rm(path: string, options?: {
|
|
@@ -110,40 +113,40 @@ export declare class FileSystem {
|
|
|
110
113
|
force?: boolean;
|
|
111
114
|
signal?: AbortSignal;
|
|
112
115
|
}): Promise<void>;
|
|
113
|
-
rmdir(path: string,
|
|
116
|
+
rmdir(path: string, options?: {
|
|
114
117
|
signal?: AbortSignal;
|
|
115
118
|
}): Promise<void>;
|
|
116
|
-
rename(oldPath: string, newPath: string,
|
|
119
|
+
rename(oldPath: string, newPath: string, options?: {
|
|
117
120
|
signal?: AbortSignal;
|
|
118
121
|
}): Promise<void>;
|
|
119
|
-
copyFile(src: string, dest: string,
|
|
122
|
+
copyFile(src: string, dest: string, options?: {
|
|
120
123
|
signal?: AbortSignal;
|
|
121
124
|
}): Promise<void>;
|
|
122
|
-
access(path: string,
|
|
125
|
+
access(path: string, options?: {
|
|
123
126
|
signal?: AbortSignal;
|
|
124
127
|
}): Promise<void>;
|
|
125
|
-
exists(path: string,
|
|
128
|
+
exists(path: string, options?: {
|
|
126
129
|
signal?: AbortSignal;
|
|
127
130
|
}): Promise<boolean>;
|
|
128
|
-
chmod(path: string, mode: number | string,
|
|
131
|
+
chmod(path: string, mode: number | string, options?: {
|
|
129
132
|
signal?: AbortSignal;
|
|
130
133
|
}): Promise<void>;
|
|
131
|
-
chown(path: string, uid: number, gid: number,
|
|
134
|
+
chown(path: string, uid: number, gid: number, options?: {
|
|
132
135
|
signal?: AbortSignal;
|
|
133
136
|
}): Promise<void>;
|
|
134
|
-
symlink(target: string, path: string,
|
|
137
|
+
symlink(target: string, path: string, options?: {
|
|
135
138
|
signal?: AbortSignal;
|
|
136
139
|
}): Promise<void>;
|
|
137
|
-
readlink(path: string,
|
|
140
|
+
readlink(path: string, options?: {
|
|
138
141
|
signal?: AbortSignal;
|
|
139
142
|
}): Promise<string>;
|
|
140
|
-
realpath(path: string,
|
|
143
|
+
realpath(path: string, options?: {
|
|
141
144
|
signal?: AbortSignal;
|
|
142
145
|
}): Promise<string>;
|
|
143
|
-
truncate(path: string, len?: number,
|
|
146
|
+
truncate(path: string, len?: number, options?: {
|
|
144
147
|
signal?: AbortSignal;
|
|
145
148
|
}): Promise<void>;
|
|
146
|
-
mkdtemp(prefix: string,
|
|
149
|
+
mkdtemp(prefix: string, options?: {
|
|
147
150
|
signal?: AbortSignal;
|
|
148
151
|
}): Promise<string>;
|
|
149
152
|
}
|
|
@@ -13,8 +13,8 @@ export class Stats {
|
|
|
13
13
|
ctimeMs;
|
|
14
14
|
birthtime;
|
|
15
15
|
birthtimeMs;
|
|
16
|
-
uid
|
|
17
|
-
gid
|
|
16
|
+
uid;
|
|
17
|
+
gid;
|
|
18
18
|
dev = 0;
|
|
19
19
|
ino = 0;
|
|
20
20
|
nlink = 1;
|
|
@@ -24,6 +24,8 @@ export class Stats {
|
|
|
24
24
|
#type;
|
|
25
25
|
constructor(entry) {
|
|
26
26
|
this.#type = entry.type;
|
|
27
|
+
this.uid = entry.uid ?? 0;
|
|
28
|
+
this.gid = entry.gid ?? 0;
|
|
27
29
|
this.size = entry.size;
|
|
28
30
|
const typeBits = entry.type === "directory" ? 0o040000 : entry.type === "symlink" ? 0o120000 : 0o100000;
|
|
29
31
|
this.mode = typeBits | ((parseInt(entry.mode, 8) || 0) & 0o7777);
|
|
@@ -61,7 +63,8 @@ export class Dirent {
|
|
|
61
63
|
#type;
|
|
62
64
|
constructor(entry, parent) {
|
|
63
65
|
this.name = entry.name;
|
|
64
|
-
this.parentPath =
|
|
66
|
+
this.parentPath = parent;
|
|
67
|
+
this.path = entry.path;
|
|
65
68
|
this.#type = entry.type;
|
|
66
69
|
}
|
|
67
70
|
isFile() {
|
|
@@ -111,18 +114,27 @@ export class FileSystem {
|
|
|
111
114
|
}
|
|
112
115
|
}
|
|
113
116
|
/** Runs a coreutils command; a failure is a Node-style error. */
|
|
114
|
-
async #run(syscall, path, argv) {
|
|
115
|
-
|
|
117
|
+
async #run(syscall, path, argv, options) {
|
|
118
|
+
options?.signal?.throwIfAborted();
|
|
119
|
+
const result = await this.#call(syscall, path, () => this.#ctx.run(argv, options));
|
|
116
120
|
if (result.exitCode === 0)
|
|
117
121
|
return result.stdout;
|
|
118
122
|
if (/No such file or directory/.test(result.stderr))
|
|
119
123
|
throw fsError("ENOENT", syscall, path);
|
|
124
|
+
if (syscall === "unlink" || syscall === "rm")
|
|
125
|
+
throw fsError("EACCES", syscall, path);
|
|
126
|
+
if (syscall === "rmdir" && /not empty/i.test(result.stderr))
|
|
127
|
+
throw fsError("ENOTEMPTY", syscall, path);
|
|
128
|
+
if (syscall === "readlink")
|
|
129
|
+
throw fsError("EINVAL", syscall, path);
|
|
120
130
|
if (/File exists/.test(result.stderr))
|
|
121
131
|
throw fsError("EEXIST", syscall, path);
|
|
122
132
|
if (/Is a directory/.test(result.stderr))
|
|
123
133
|
throw fsError("EISDIR", syscall, path);
|
|
124
134
|
if (/Not a directory/.test(result.stderr))
|
|
125
135
|
throw fsError("ENOTDIR", syscall, path);
|
|
136
|
+
if (/Permission denied/.test(result.stderr))
|
|
137
|
+
throw fsError("EACCES", syscall, path);
|
|
126
138
|
throw Object.assign(new Error(`${syscall} '${path}' failed: ${result.stderr.trim()}`), {
|
|
127
139
|
code: "EIO",
|
|
128
140
|
syscall,
|
|
@@ -131,6 +143,8 @@ export class FileSystem {
|
|
|
131
143
|
}
|
|
132
144
|
async readFile(path, options) {
|
|
133
145
|
const target = this.#ctx.resolve(path);
|
|
146
|
+
if (options && typeof options === "object")
|
|
147
|
+
options.signal?.throwIfAborted();
|
|
134
148
|
const files = await this.#ctx.files();
|
|
135
149
|
const bytes = await this.#call("open", target, () => files.read(target, typeof options === "object" && options?.signal ? { signal: options.signal } : {}));
|
|
136
150
|
const encoding = encodingOf(options);
|
|
@@ -139,37 +153,64 @@ export class FileSystem {
|
|
|
139
153
|
}
|
|
140
154
|
async writeFile(path, data, options) {
|
|
141
155
|
const target = this.#ctx.resolve(path);
|
|
156
|
+
if (options && typeof options === "object")
|
|
157
|
+
options.signal?.throwIfAborted();
|
|
142
158
|
const files = await this.#ctx.files();
|
|
143
|
-
await this.#call("open", target, () => files.write(target, bytesOf(data, encodingOf(options))));
|
|
159
|
+
await this.#call("open", target, () => files.write(target, bytesOf(data, encodingOf(options)), typeof options === "object" && options.signal ? { signal: options.signal } : {}));
|
|
144
160
|
}
|
|
145
161
|
async appendFile(path, data, options) {
|
|
146
162
|
const target = this.#ctx.resolve(path);
|
|
163
|
+
// Never read-modify-write: another caller may be appending too. The
|
|
164
|
+
// shell opens with O_APPEND and cat streams bytes without Python. Exec
|
|
165
|
+
// stdin is limited to 1 MiB; larger input uses the chunked files API.
|
|
166
|
+
const bytes = bytesOf(data, encodingOf(options));
|
|
167
|
+
const signal = typeof options === "object" ? options.signal : undefined;
|
|
168
|
+
signal?.throwIfAborted();
|
|
169
|
+
if (bytes.byteLength <= 1_048_576) {
|
|
170
|
+
await this.#run("open", target, ["sh", "-c", 'cat >> "$1"', "runtime-append", target], {
|
|
171
|
+
stdin: bytes,
|
|
172
|
+
...(signal ? { signal } : {}),
|
|
173
|
+
});
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (options && typeof options === "object")
|
|
177
|
+
options.signal?.throwIfAborted();
|
|
147
178
|
const files = await this.#ctx.files();
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
|
|
179
|
+
const staging = `/workspace/.runtime/append/${crypto.randomUUID()}`;
|
|
180
|
+
try {
|
|
181
|
+
await this.#call("open", target, () => files.write(staging, bytes, { mode: 0o600, ...(signal ? { signal } : {}) }));
|
|
182
|
+
await this.#run("open", target, ["sh", "-c", 'cat "$1" >> "$2"', "runtime-append", staging, target], signal ? { signal } : undefined);
|
|
183
|
+
}
|
|
184
|
+
finally {
|
|
185
|
+
// A cleanup failure must not invite retrying a successful append.
|
|
186
|
+
await files.remove(staging).catch(() => undefined);
|
|
187
|
+
}
|
|
154
188
|
}
|
|
155
189
|
async mkdir(path, options) {
|
|
156
190
|
const target = this.#ctx.resolve(path);
|
|
157
191
|
const recursive = typeof options === "object" && options.recursive === true;
|
|
158
|
-
await this.#run("mkdir", target, recursive ? ["mkdir", "-p", target] : ["mkdir", target]);
|
|
159
|
-
return recursive ? target : undefined;
|
|
192
|
+
await this.#run("mkdir", target, recursive ? ["mkdir", "-p", "--", target] : ["mkdir", "--", target], typeof options === "object" ? options : {});
|
|
160
193
|
}
|
|
161
194
|
async readdir(path, options = {}) {
|
|
162
195
|
const target = this.#ctx.resolve(path);
|
|
196
|
+
if (options && typeof options === "object")
|
|
197
|
+
options.signal?.throwIfAborted();
|
|
163
198
|
const files = await this.#ctx.files();
|
|
164
|
-
const entries = await this.#call("scandir", target, () => files.list(target, {
|
|
199
|
+
const entries = await this.#call("scandir", target, () => files.list(target, {
|
|
200
|
+
depth: 1,
|
|
201
|
+
hidden: options.withFileTypes === true,
|
|
202
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
203
|
+
}));
|
|
165
204
|
return options.withFileTypes
|
|
166
205
|
? entries.map((entry) => new Dirent(entry, target))
|
|
167
|
-
: entries.map((entry) => entry.name);
|
|
206
|
+
: entries.filter((entry) => !entry.name.startsWith(".")).map((entry) => entry.name);
|
|
168
207
|
}
|
|
169
|
-
async lstat(path,
|
|
208
|
+
async lstat(path, options = {}) {
|
|
170
209
|
const target = this.#ctx.resolve(path);
|
|
210
|
+
if (options && typeof options === "object")
|
|
211
|
+
options.signal?.throwIfAborted();
|
|
171
212
|
const files = await this.#ctx.files();
|
|
172
|
-
const found = await this.#call("lstat", target, () => files.stat(target));
|
|
213
|
+
const found = await this.#call("lstat", target, () => files.stat(target, options));
|
|
173
214
|
if (!found.exists)
|
|
174
215
|
throw fsError("ENOENT", "lstat", target);
|
|
175
216
|
return new Stats(found);
|
|
@@ -180,69 +221,75 @@ export class FileSystem {
|
|
|
180
221
|
const first = await this.lstat(target, options);
|
|
181
222
|
if (!first.isSymbolicLink())
|
|
182
223
|
return first;
|
|
183
|
-
return this.lstat(await this.realpath(target), options);
|
|
224
|
+
return this.lstat(await this.realpath(target, options), options);
|
|
184
225
|
}
|
|
185
|
-
async unlink(path,
|
|
226
|
+
async unlink(path, options = {}) {
|
|
186
227
|
const target = this.#ctx.resolve(path);
|
|
187
|
-
await this.#run("unlink", target, ["rm", "--", target]);
|
|
228
|
+
await this.#run("unlink", target, ["rm", "--", target], options);
|
|
188
229
|
}
|
|
189
230
|
async rm(path, options = {}) {
|
|
190
231
|
const target = this.#ctx.resolve(path);
|
|
191
232
|
const flags = `${options.recursive ? "r" : ""}${options.force ? "f" : ""}`;
|
|
192
|
-
await this.#run("rm", target, flags ? ["rm", `-${flags}`, "--", target] : ["rm", "--", target]);
|
|
233
|
+
await this.#run("rm", target, flags ? ["rm", `-${flags}`, "--", target] : ["rm", "--", target], options);
|
|
193
234
|
}
|
|
194
|
-
async rmdir(path,
|
|
235
|
+
async rmdir(path, options = {}) {
|
|
195
236
|
const target = this.#ctx.resolve(path);
|
|
196
|
-
await this.#run("rmdir", target, ["rmdir", "--", target]);
|
|
237
|
+
await this.#run("rmdir", target, ["rmdir", "--", target], options);
|
|
197
238
|
}
|
|
198
|
-
async rename(oldPath, newPath,
|
|
239
|
+
async rename(oldPath, newPath, options = {}) {
|
|
199
240
|
const from = this.#ctx.resolve(oldPath);
|
|
200
241
|
const to = this.#ctx.resolve(newPath);
|
|
242
|
+
if (options && typeof options === "object")
|
|
243
|
+
options.signal?.throwIfAborted();
|
|
201
244
|
const files = await this.#ctx.files();
|
|
202
|
-
await this.#call("rename", from, () => files.rename(from, to, { overwrite: true }));
|
|
245
|
+
await this.#call("rename", from, () => files.rename(from, to, { overwrite: true, ...options }));
|
|
203
246
|
}
|
|
204
|
-
async copyFile(src, dest,
|
|
247
|
+
async copyFile(src, dest, options = {}) {
|
|
205
248
|
const from = this.#ctx.resolve(src);
|
|
206
249
|
const to = this.#ctx.resolve(dest);
|
|
207
|
-
await this.#run("copyfile", from, ["cp", "--", from, to]);
|
|
250
|
+
await this.#run("copyfile", from, ["cp", "--", from, to], options);
|
|
208
251
|
}
|
|
209
|
-
async access(path,
|
|
252
|
+
async access(path, options = {}) {
|
|
210
253
|
const target = this.#ctx.resolve(path);
|
|
211
|
-
if (!(await this.exists(target)))
|
|
254
|
+
if (!(await this.exists(target, options)))
|
|
212
255
|
throw fsError("ENOENT", "access", target);
|
|
213
256
|
}
|
|
214
|
-
async exists(path,
|
|
257
|
+
async exists(path, options = {}) {
|
|
215
258
|
const target = this.#ctx.resolve(path);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
259
|
+
if (options && typeof options === "object")
|
|
260
|
+
options.signal?.throwIfAborted();
|
|
261
|
+
// The native stat is lstat: a dangling link exists there. Vercel's public
|
|
262
|
+
// exists/access follow the target, matching test -e in its published SDK.
|
|
263
|
+
const result = await this.#call("access", target, () => this.#ctx.run(["test", "-e", target], options));
|
|
264
|
+
return result.exitCode === 0;
|
|
265
|
+
}
|
|
266
|
+
async chmod(path, mode, options = {}) {
|
|
220
267
|
const target = this.#ctx.resolve(path);
|
|
221
268
|
const octal = typeof mode === "number" ? mode.toString(8) : mode;
|
|
222
|
-
await this.#run("chmod", target, ["chmod", octal, "--", target]);
|
|
269
|
+
await this.#run("chmod", target, ["chmod", octal, "--", target], options);
|
|
223
270
|
}
|
|
224
|
-
async chown(path, uid, gid,
|
|
271
|
+
async chown(path, uid, gid, options = {}) {
|
|
225
272
|
const target = this.#ctx.resolve(path);
|
|
226
|
-
await this.#run("chown", target, ["sudo", "chown", `${uid}:${gid}`, "--", target]);
|
|
273
|
+
await this.#run("chown", target, ["sudo", "chown", `${uid}:${gid}`, "--", target], options);
|
|
227
274
|
}
|
|
228
|
-
async symlink(target, path,
|
|
275
|
+
async symlink(target, path, options = {}) {
|
|
229
276
|
const link = this.#ctx.resolve(path);
|
|
230
|
-
await this.#run("symlink", link, ["ln", "-s", "--", target, link]);
|
|
277
|
+
await this.#run("symlink", link, ["ln", "-s", "--", target, link], options);
|
|
231
278
|
}
|
|
232
|
-
async readlink(path,
|
|
279
|
+
async readlink(path, options = {}) {
|
|
233
280
|
const target = this.#ctx.resolve(path);
|
|
234
|
-
return (await this.#run("readlink", target, ["readlink", "--", target])).replace(/\n$/, "");
|
|
281
|
+
return (await this.#run("readlink", target, ["readlink", "--", target], options)).replace(/\n$/, "");
|
|
235
282
|
}
|
|
236
|
-
async realpath(path,
|
|
283
|
+
async realpath(path, options = {}) {
|
|
237
284
|
const target = this.#ctx.resolve(path);
|
|
238
|
-
return (await this.#run("realpath", target, ["realpath", "-e", "--", target])).replace(/\n$/, "");
|
|
285
|
+
return (await this.#run("realpath", target, ["realpath", "-e", "--", target], options)).replace(/\n$/, "");
|
|
239
286
|
}
|
|
240
|
-
async truncate(path, len = 0,
|
|
287
|
+
async truncate(path, len = 0, options = {}) {
|
|
241
288
|
const target = this.#ctx.resolve(path);
|
|
242
|
-
await this.#run("open", target, ["truncate", "-s", String(len), "--", target]);
|
|
289
|
+
await this.#run("open", target, ["truncate", "-s", String(len), "--", target], options);
|
|
243
290
|
}
|
|
244
|
-
async mkdtemp(prefix,
|
|
291
|
+
async mkdtemp(prefix, options = {}) {
|
|
245
292
|
const base = this.#ctx.resolve(prefix);
|
|
246
|
-
return (await this.#run("mkdtemp", base, ["mktemp", "-d", `${base}XXXXXX`])).replace(/\n$/, "");
|
|
293
|
+
return (await this.#run("mkdtemp", base, ["mktemp", "-d", `${base}XXXXXX`], options)).replace(/\n$/, "");
|
|
247
294
|
}
|
|
248
295
|
}
|
package/dist/vercel/sandbox.d.ts
CHANGED
|
@@ -60,7 +60,7 @@ export interface CreateSandboxParams extends Credentials {
|
|
|
60
60
|
/** Milliseconds a stopped persistent sandbox is kept, as Runtime's
|
|
61
61
|
* retention (whole days, 1 to 365; 0 is 365). */
|
|
62
62
|
snapshotExpiration?: number;
|
|
63
|
-
/**
|
|
63
|
+
/** Refused: Runtime does not enforce snapshot count or eviction policies. */
|
|
64
64
|
keepLastSnapshots?: {
|
|
65
65
|
count: number;
|
|
66
66
|
expiration?: number;
|
|
@@ -198,10 +198,10 @@ export declare class Sandbox {
|
|
|
198
198
|
}): Promise<Command>;
|
|
199
199
|
runCommand(params: RunCommandParams): Promise<CommandFinished>;
|
|
200
200
|
/** A command started earlier, by its cmdId. */
|
|
201
|
-
getCommand(cmdId: string,
|
|
201
|
+
getCommand(cmdId: string, opts?: {
|
|
202
202
|
signal?: AbortSignal;
|
|
203
203
|
}): Promise<Command>;
|
|
204
|
-
mkDir(path: string,
|
|
204
|
+
mkDir(path: string, opts?: {
|
|
205
205
|
signal?: AbortSignal;
|
|
206
206
|
}): Promise<void>;
|
|
207
207
|
/** The file's bytes as a Node stream, or null when there is no file. */
|
|
@@ -235,7 +235,7 @@ export declare class Sandbox {
|
|
|
235
235
|
path: string;
|
|
236
236
|
content: string | Uint8Array;
|
|
237
237
|
mode?: number;
|
|
238
|
-
}[],
|
|
238
|
+
}[], opts?: {
|
|
239
239
|
signal?: AbortSignal;
|
|
240
240
|
}): Promise<void>;
|
|
241
241
|
/** The public address of a port listed in `ports`, such as
|
|
@@ -262,7 +262,7 @@ export declare class Sandbox {
|
|
|
262
262
|
signal?: AbortSignal;
|
|
263
263
|
}): Promise<void>;
|
|
264
264
|
/** Replaces the network rules; returns the policy given. */
|
|
265
|
-
updateNetworkPolicy(networkPolicy: NetworkPolicy,
|
|
265
|
+
updateNetworkPolicy(networkPolicy: NetworkPolicy, opts?: {
|
|
266
266
|
signal?: AbortSignal;
|
|
267
267
|
}): Promise<NetworkPolicy>;
|
|
268
268
|
/** Changes what Runtime can change on a sandbox: `timeout`, `ports`,
|
|
@@ -273,11 +273,11 @@ export declare class Sandbox {
|
|
|
273
273
|
ports?: number[];
|
|
274
274
|
networkPolicy?: NetworkPolicy;
|
|
275
275
|
snapshotExpiration?: number;
|
|
276
|
-
} & Record<string, unknown>,
|
|
276
|
+
} & Record<string, unknown>, opts?: {
|
|
277
277
|
signal?: AbortSignal;
|
|
278
278
|
}): Promise<void>;
|
|
279
|
-
/** Keeps the sandbox's
|
|
280
|
-
*
|
|
279
|
+
/** Keeps the sandbox's filesystem as a disk-only snapshot, then stops
|
|
280
|
+
* the source. Restoring it starts fresh processes, as Vercel does. Start
|
|
281
281
|
* from it with `Sandbox.create({ source: { type: "snapshot", snapshotId } })`. */
|
|
282
282
|
snapshot(opts?: {
|
|
283
283
|
expiration?: number;
|