Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@vercel/sandbox

Package Overview
Dependencies
Maintainers
3
Versions
81
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@vercel/sandbox - npm Package Compare versions

Comparing version
2.0.0-beta.14
to
2.0.0-beta.18
+499
dist/filesystem.cjs
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
let node_constants = require("node:constants");
node_constants = require_rolldown_runtime.__toESM(node_constants);
//#region src/filesystem.ts
const { S_IFMT, S_IFREG, S_IFDIR, S_IFLNK, S_IFBLK, S_IFCHR, S_IFIFO, S_IFSOCK } = node_constants;
const UV_DIRENT_FILE = 1;
const UV_DIRENT_DIR = 2;
const UV_DIRENT_LINK = 3;
const UV_DIRENT_FIFO = 4;
const UV_DIRENT_SOCKET = 5;
const UV_DIRENT_CHAR = 6;
const UV_DIRENT_BLOCK = 7;
function fsError(code, message, syscall, path) {
const err = /* @__PURE__ */ new Error(`${code}: ${message}, ${syscall} '${path}'`);
err.code = code;
err.syscall = syscall;
err.path = path;
return err;
}
function parseEncoding(options) {
if (options === null || options === void 0) return { encoding: null };
if (typeof options === "string") return { encoding: options };
return {
encoding: options.encoding ?? null,
signal: options.signal
};
}
var SandboxStats = class {
constructor(dev, _mode, nlink, uid, gid, rdev, blksize, ino, size, blocks, atimeMs, mtimeMs, ctimeMs, birthtimeMs) {
this.dev = dev;
this._mode = _mode;
this.nlink = nlink;
this.uid = uid;
this.gid = gid;
this.rdev = rdev;
this.blksize = blksize;
this.ino = ino;
this.size = size;
this.blocks = blocks;
this.atimeMs = atimeMs;
this.mtimeMs = mtimeMs;
this.ctimeMs = ctimeMs;
this.birthtimeMs = birthtimeMs;
this.atime = new Date(atimeMs);
this.mtime = new Date(mtimeMs);
this.ctime = new Date(ctimeMs);
this.birthtime = new Date(birthtimeMs);
}
get mode() {
return this._mode;
}
isFile() {
return (this.mode & S_IFMT) === S_IFREG;
}
isDirectory() {
return (this.mode & S_IFMT) === S_IFDIR;
}
isBlockDevice() {
return (this.mode & S_IFMT) === S_IFBLK;
}
isCharacterDevice() {
return (this.mode & S_IFMT) === S_IFCHR;
}
isSymbolicLink() {
return (this.mode & S_IFMT) === S_IFLNK;
}
isFIFO() {
return (this.mode & S_IFMT) === S_IFIFO;
}
isSocket() {
return (this.mode & S_IFMT) === S_IFSOCK;
}
};
var SandboxDirent = class {
constructor(name, type, parentPath) {
this.name = name;
this.type = type;
this.parentPath = parentPath;
this.path = `${this.parentPath}/${this.name}`;
}
isFile() {
return this.type === UV_DIRENT_FILE;
}
isDirectory() {
return this.type === UV_DIRENT_DIR;
}
isBlockDevice() {
return this.type === UV_DIRENT_BLOCK;
}
isCharacterDevice() {
return this.type === UV_DIRENT_CHAR;
}
isSymbolicLink() {
return this.type === UV_DIRENT_LINK;
}
isFIFO() {
return this.type === UV_DIRENT_FIFO;
}
isSocket() {
return this.type === UV_DIRENT_SOCKET;
}
};
function parseStat(stdout) {
const parts = stdout.trim().split("|");
return new SandboxStats(parseInt(parts[10], 10), parseInt(parts[1], 16), parseInt(parts[8], 10), parseInt(parts[2], 10), parseInt(parts[3], 10), 0, parseInt(parts[11], 10), parseInt(parts[9], 10), parseInt(parts[0], 10), parseInt(parts[12], 10), parseFloat(parts[4]) * 1e3, parseFloat(parts[5]) * 1e3, parseFloat(parts[6]) * 1e3, parseFloat(parts[7]) * 1e3);
}
function parseDirent(stdout, path) {
const parts = stdout.trim().split("|");
const name = parts[0];
const type = parts[1];
if (!name) throw fsError("ENOENT", "no such file or directory", "readdir", path);
if (!type) throw new Error(`Invalid dirent type: ${type}`);
return new SandboxDirent(name, FIND_TYPE_TO_DIRENT[type] ?? UV_DIRENT_FILE, path);
}
const STAT_FORMAT = "%s|%f|%u|%g|%X|%Y|%Z|%W|%h|%i|%d|%B|%b";
const FIND_TYPE_TO_DIRENT = {
f: UV_DIRENT_FILE,
d: UV_DIRENT_DIR,
l: UV_DIRENT_LINK,
b: UV_DIRENT_BLOCK,
c: UV_DIRENT_CHAR,
p: UV_DIRENT_FIFO,
s: UV_DIRENT_SOCKET
};
var FileSystem = class {
/** @internal */
constructor(sandbox) {
this.sandbox = sandbox;
}
async readFile(path, options) {
"use step";
const { encoding, signal } = parseEncoding(options);
const buffer = await this.sandbox.readFileToBuffer({ path }, { signal });
if (buffer === null) throw fsError("ENOENT", "no such file or directory", "open", path);
return encoding ? buffer.toString(encoding) : buffer;
}
/**
* Write data to a file, replacing the file if it already exists.
*
* @param path - Path to the file
* @param data - The data to write
* @param options - Write options
*/
async writeFile(path, data, options) {
"use step";
const { encoding, signal } = typeof options === "string" ? {
encoding: options,
signal: void 0
} : {
encoding: options?.encoding,
signal: options?.signal
};
let content;
if (typeof data === "string") content = Buffer.from(data, encoding ?? "utf8");
else if (Buffer.isBuffer(data)) content = data;
else content = Buffer.from(data);
await this.sandbox.writeFiles([{
path,
content
}], { signal });
}
/**
* Append data to a file, creating the file if it does not yet exist.
*
* @param path - Path to the file
* @param data - The data to append
* @param options - Write options
*/
async appendFile(path, data, options) {
"use step";
const { encoding, signal } = typeof options === "string" ? {
encoding: options,
signal: void 0
} : {
encoding: options?.encoding,
signal: options?.signal
};
let appendContent;
if (typeof data === "string") appendContent = Buffer.from(data, encoding ?? "utf8");
else if (Buffer.isBuffer(data)) appendContent = data;
else appendContent = Buffer.from(data);
const existing = await this.sandbox.readFileToBuffer({ path }, { signal });
const content = existing !== null ? Buffer.concat([existing, appendContent]) : appendContent;
await this.sandbox.writeFiles([{
path,
content
}], { signal });
}
/**
* Create a directory.
*
* @param path - Path of the directory to create
* @param options - Options for directory creation
*/
async mkdir(path, options) {
"use step";
const opts = typeof options === "number" ? { recursive: false } : options ?? {};
if (opts.recursive) {
const result = await this.sandbox.runCommand("mkdir", ["-p", path], { signal: opts.signal });
if (result.exitCode !== 0) throw fsError("EACCES", (await result.stderr()).trim() || "permission denied", "mkdir", path);
return;
}
await this.sandbox.mkDir(path, { signal: opts.signal });
}
async readdir(path, options) {
"use step";
if (options?.withFileTypes) {
const result$1 = await this.sandbox.runCommand("find", [
path,
"-maxdepth",
"1",
"-mindepth",
"1",
"-printf",
"%f|%y\\n"
], { signal: options?.signal });
if (result$1.exitCode !== 0) {
const stderr = await result$1.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "scandir", path);
throw fsError("EACCES", stderr.trim(), "scandir", path);
}
return (await result$1.stdout()).trim().split("\n").filter(Boolean).map((line) => parseDirent(line, path));
}
const result = await this.sandbox.runCommand("ls", ["-1", path], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "scandir", path);
throw fsError("EACCES", stderr.trim(), "scandir", path);
}
return (await result.stdout()).trim().split("\n").filter(Boolean);
}
/**
* Get file status. Follows symbolic links.
*
* @param path - Path to the file
* @param options - Options
*/
async stat(path, options) {
"use step";
const result = await this.sandbox.runCommand("stat", [
"-L",
"-c",
STAT_FORMAT,
path
], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "stat", path);
throw fsError("EACCES", stderr.trim(), "stat", path);
}
return parseStat(await result.stdout());
}
/**
* Get file status. Does not follow symbolic links.
*
* @param path - Path to the file
* @param options - Options
*/
async lstat(path, options) {
"use step";
const result = await this.sandbox.runCommand("stat", [
"-c",
STAT_FORMAT,
path
], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "lstat", path);
throw fsError("EACCES", stderr.trim(), "lstat", path);
}
return parseStat(await result.stdout());
}
/**
* Remove a file or symbolic link.
*
* @param path - Path to the file
* @param options - Options
*/
async unlink(path, options) {
"use step";
const result = await this.sandbox.runCommand("rm", [path], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "unlink", path);
throw fsError("EACCES", stderr.trim(), "unlink", path);
}
}
/**
* Remove files and directories.
*
* @param path - Path to remove
* @param options - Options
*/
async rm(path, options) {
"use step";
const args = [];
if (options?.recursive) args.push("-r");
if (options?.force) args.push("-f");
args.push(path);
const result = await this.sandbox.runCommand("rm", args, { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "rm", path);
throw fsError("EACCES", stderr.trim(), "rm", path);
}
}
/**
* Remove a directory.
*
* @param path - Path to the directory
* @param options - Options
*/
async rmdir(path, options) {
"use step";
const result = await this.sandbox.runCommand("rmdir", [path], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "rmdir", path);
if (stderr.includes("not empty")) throw fsError("ENOTEMPTY", "directory not empty", "rmdir", path);
throw fsError("EACCES", stderr.trim(), "rmdir", path);
}
}
/**
* Rename a file or directory.
*
* @param oldPath - Current path
* @param newPath - New path
* @param options - Options
*/
async rename(oldPath, newPath, options) {
"use step";
const result = await this.sandbox.runCommand("mv", [oldPath, newPath], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "rename", oldPath);
throw fsError("EACCES", stderr.trim(), "rename", oldPath);
}
}
/**
* Copy a file.
*
* @param src - Source path
* @param dest - Destination path
* @param options - Options
*/
async copyFile(src, dest, options) {
"use step";
const result = await this.sandbox.runCommand("cp", [src, dest], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "copyfile", src);
throw fsError("EACCES", stderr.trim(), "copyfile", src);
}
}
/**
* Test whether a file exists and the user has the specified permissions.
*
* @param path - Path to the file
* @param options - Options
*/
async access(path, options) {
"use step";
if ((await this.sandbox.runCommand("test", ["-e", path], { signal: options?.signal })).exitCode !== 0) throw fsError("ENOENT", "no such file or directory", "access", path);
}
/**
* Check if a path exists.
*
* This is a convenience method not in `node:fs/promises` but commonly needed.
*
* @param path - Path to check
* @param options - Options
*/
async exists(path, options) {
return (await this.sandbox.runCommand("test", ["-e", path], { signal: options?.signal })).exitCode === 0;
}
/**
* Change file mode (permissions).
*
* @param path - Path to the file
* @param mode - File mode (e.g., 0o755 or "755")
* @param options - Options
*/
async chmod(path, mode, options) {
"use step";
const modeStr = typeof mode === "number" ? mode.toString(8) : mode;
const result = await this.sandbox.runCommand("chmod", [modeStr, path], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "chmod", path);
throw fsError("EACCES", stderr.trim(), "chmod", path);
}
}
/**
* Change file owner and group.
*
* @param path - Path to the file
* @param uid - User ID
* @param gid - Group ID
* @param options - Options
*/
async chown(path, uid, gid, options) {
"use step";
const result = await this.sandbox.runCommand("chown", [`${uid}:${gid}`, path], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "chown", path);
throw fsError("EACCES", stderr.trim(), "chown", path);
}
}
/**
* Create a symbolic link.
*
* @param target - The target of the symbolic link
* @param path - The path of the symbolic link to create
* @param options - Options
*/
async symlink(target, path, options) {
"use step";
const result = await this.sandbox.runCommand("ln", [
"-s",
target,
path
], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("File exists")) throw fsError("EEXIST", "file already exists", "symlink", path);
throw fsError("EACCES", stderr.trim(), "symlink", path);
}
}
/**
* Read the value of a symbolic link.
*
* @param path - Path to the symbolic link
* @param options - Options
*/
async readlink(path, options) {
"use step";
const result = await this.sandbox.runCommand("readlink", [path], { signal: options?.signal });
if (result.exitCode !== 0) {
if ((await result.stderr()).includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "readlink", path);
throw fsError("EINVAL", "invalid argument", "readlink", path);
}
return (await result.stdout()).trim();
}
/**
* Resolve the real path of a file (resolving symlinks).
*
* @param path - Path to resolve
* @param options - Options
*/
async realpath(path, options) {
"use step";
const result = await this.sandbox.runCommand("realpath", [path], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "realpath", path);
throw fsError("EACCES", stderr.trim(), "realpath", path);
}
return (await result.stdout()).trim();
}
/**
* Truncate a file to a specified length.
*
* @param path - Path to the file
* @param len - Length to truncate to (default: 0)
* @param options - Options
*/
async truncate(path, len, options) {
"use step";
const result = await this.sandbox.runCommand("truncate", [
"-s",
String(len ?? 0),
path
], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "truncate", path);
throw fsError("EACCES", stderr.trim(), "truncate", path);
}
}
/**
* Create a unique temporary directory.
*
* @param prefix - The prefix for the temporary directory name
* @param options - Options
* @returns The path of the created temporary directory
*/
async mkdtemp(prefix, options) {
"use step";
const result = await this.sandbox.runCommand("mktemp", ["-d", `${prefix}XXXXXX`], { signal: options?.signal });
if (result.exitCode !== 0) throw fsError("EACCES", (await result.stderr()).trim(), "mkdtemp", prefix);
return (await result.stdout()).trim();
}
};
//#endregion
exports.FileSystem = FileSystem;
//# sourceMappingURL=filesystem.cjs.map
{"version":3,"file":"filesystem.cjs","names":["constants","dev: number","_mode: number","nlink: number","uid: number","gid: number","rdev: number","blksize: number","ino: number","size: number","blocks: number","atimeMs: number","mtimeMs: number","ctimeMs: number","birthtimeMs: number","name: string","type: number","parentPath: string","FIND_TYPE_TO_DIRENT: Record<string, number>","content: Buffer","appendContent: Buffer","result","args: string[]"],"sources":["../src/filesystem.ts"],"sourcesContent":["import type { Stats, Dirent } from \"fs\";\nimport * as constants from \"node:constants\";\n\nconst {\n S_IFMT,\n S_IFREG,\n S_IFDIR,\n S_IFLNK,\n S_IFBLK,\n S_IFCHR,\n S_IFIFO,\n S_IFSOCK,\n} = constants;\n\n// https://github.com/nodejs/node/blob/main/deps/uv/include/uv.h#L1267-L1276\n// exposed through node:constants but not in @types/node\nconst UV_DIRENT_FILE = 1;\nconst UV_DIRENT_DIR = 2;\nconst UV_DIRENT_LINK = 3;\nconst UV_DIRENT_FIFO = 4;\nconst UV_DIRENT_SOCKET = 5;\nconst UV_DIRENT_CHAR = 6;\nconst UV_DIRENT_BLOCK = 7;\n\ntype EncodingOption =\n | { encoding?: BufferEncoding | null; signal?: AbortSignal }\n | BufferEncoding\n | null;\n\ntype WriteFileData = string | Buffer | Uint8Array;\n\ninterface MkdirOptions {\n recursive?: boolean;\n signal?: AbortSignal;\n}\n\ninterface RmOptions {\n recursive?: boolean;\n force?: boolean;\n signal?: AbortSignal;\n}\n\nfunction fsError(\n code: string,\n message: string,\n syscall: string,\n path: string,\n): Error & { code: string; syscall: string; path: string } {\n const err = new Error(\n `${code}: ${message}, ${syscall} '${path}'`,\n ) as Error & {\n code: string;\n syscall: string;\n path: string;\n };\n err.code = code;\n err.syscall = syscall;\n err.path = path;\n return err;\n}\n\ninterface SandboxHandle {\n readFileToBuffer(\n file: { path: string },\n opts?: { signal?: AbortSignal },\n ): Promise<Buffer | null>;\n writeFiles(\n files: { path: string; content: Buffer }[],\n opts?: { signal?: AbortSignal },\n ): Promise<void>;\n mkDir(path: string, opts?: { signal?: AbortSignal }): Promise<void>;\n runCommand(\n cmd: string,\n args?: string[],\n opts?: { signal?: AbortSignal },\n ): Promise<{\n exitCode: number;\n stdout(opts?: { signal?: AbortSignal }): Promise<string>;\n stderr(opts?: { signal?: AbortSignal }): Promise<string>;\n }>;\n}\n\nfunction parseEncoding(options?: EncodingOption): {\n encoding: BufferEncoding | null;\n signal?: AbortSignal;\n} {\n if (options === null || options === undefined) {\n return { encoding: null };\n }\n if (typeof options === \"string\") {\n return { encoding: options };\n }\n return { encoding: options.encoding ?? null, signal: options.signal };\n}\n\nclass SandboxStats {\n readonly atime: Date;\n readonly mtime: Date;\n readonly ctime: Date;\n readonly birthtime: Date;\n\n constructor(\n readonly dev: number,\n private readonly _mode: number,\n readonly nlink: number,\n readonly uid: number,\n readonly gid: number,\n readonly rdev: number,\n readonly blksize: number,\n readonly ino: number,\n readonly size: number,\n readonly blocks: number,\n readonly atimeMs: number,\n readonly mtimeMs: number,\n readonly ctimeMs: number,\n readonly birthtimeMs: number,\n ) {\n this.atime = new Date(atimeMs);\n this.mtime = new Date(mtimeMs);\n this.ctime = new Date(ctimeMs);\n this.birthtime = new Date(birthtimeMs);\n }\n\n get mode(): number {\n return this._mode;\n }\n\n isFile(): boolean {\n return (this.mode & S_IFMT) === S_IFREG;\n }\n isDirectory(): boolean {\n return (this.mode & S_IFMT) === S_IFDIR;\n }\n isBlockDevice(): boolean {\n return (this.mode & S_IFMT) === S_IFBLK;\n }\n isCharacterDevice(): boolean {\n return (this.mode & S_IFMT) === S_IFCHR;\n }\n isSymbolicLink(): boolean {\n return (this.mode & S_IFMT) === S_IFLNK;\n }\n isFIFO(): boolean {\n return (this.mode & S_IFMT) === S_IFIFO;\n }\n isSocket(): boolean {\n return (this.mode & S_IFMT) === S_IFSOCK;\n }\n}\n\nclass SandboxDirent {\n readonly path: string;\n\n constructor(\n public name: string,\n private type: number,\n public parentPath: string,\n ) {\n this.path = `${this.parentPath}/${this.name}`;\n }\n\n isFile(): boolean {\n return this.type === UV_DIRENT_FILE;\n }\n isDirectory(): boolean {\n return this.type === UV_DIRENT_DIR;\n }\n isBlockDevice(): boolean {\n return this.type === UV_DIRENT_BLOCK;\n }\n isCharacterDevice(): boolean {\n return this.type === UV_DIRENT_CHAR;\n }\n isSymbolicLink(): boolean {\n return this.type === UV_DIRENT_LINK;\n }\n isFIFO(): boolean {\n return this.type === UV_DIRENT_FIFO;\n }\n isSocket(): boolean {\n return this.type === UV_DIRENT_SOCKET;\n }\n}\n\nfunction parseStat(stdout: string) {\n // Format: size|rawModeHex|uid|gid|atimeMs|mtimeMs|ctimeMs|birthtimeMs|nlink|ino|dev|blksize|blocks\n const parts = stdout.trim().split(\"|\");\n\n return new SandboxStats(\n parseInt(parts[10]!, 10), // dev\n parseInt(parts[1]!, 16), // mode (raw mode from %f, hex)\n parseInt(parts[8]!, 10), // nlink\n parseInt(parts[2]!, 10), // uid\n parseInt(parts[3]!, 10), // gid\n 0, // rdev\n parseInt(parts[11]!, 10), // blksize\n parseInt(parts[9]!, 10), // ino\n parseInt(parts[0]!, 10), // size\n parseInt(parts[12]!, 10), // blocks\n parseFloat(parts[4]!) * 1000, // atimeMs\n parseFloat(parts[5]!) * 1000, // mtimeMs\n parseFloat(parts[6]!) * 1000, // ctimeMs\n parseFloat(parts[7]!) * 1000, // birthtimeMs\n ) satisfies Stats;\n}\n\nfunction parseDirent(stdout: string, path: string) {\n const parts = stdout.trim().split(\"|\");\n const name = parts[0];\n const type = parts[1];\n\n if (!name) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"readdir\", path);\n }\n\n if (!type) {\n throw new Error(`Invalid dirent type: ${type}`);\n }\n\n const direntType = FIND_TYPE_TO_DIRENT[type] ?? UV_DIRENT_FILE;\n return new SandboxDirent(name, direntType, path) satisfies Dirent;\n}\n\n// %f = raw mode in hex, includes file type bits so Stats.isFile()/isDirectory()/etc. work\nconst STAT_FORMAT = \"%s|%f|%u|%g|%X|%Y|%Z|%W|%h|%i|%d|%B|%b\";\n\nconst FIND_TYPE_TO_DIRENT: Record<string, number> = {\n f: UV_DIRENT_FILE,\n d: UV_DIRENT_DIR,\n l: UV_DIRENT_LINK,\n b: UV_DIRENT_BLOCK,\n c: UV_DIRENT_CHAR,\n p: UV_DIRENT_FIFO,\n s: UV_DIRENT_SOCKET,\n};\n\nexport class FileSystem {\n /** @internal */\n private sandbox: SandboxHandle;\n\n /** @internal */\n constructor(sandbox: SandboxHandle) {\n this.sandbox = sandbox;\n }\n\n /**\n * Read the entire contents of a file.\n *\n * @param path - Path to the file\n * @param options - Encoding or options object. If encoding is specified, returns a string; otherwise returns a Buffer.\n */\n async readFile(\n path: string,\n options?: { encoding?: null; signal?: AbortSignal } | null,\n ): Promise<Buffer>;\n async readFile(\n path: string,\n options:\n | { encoding: BufferEncoding; signal?: AbortSignal }\n | BufferEncoding,\n ): Promise<string>;\n async readFile(\n path: string,\n options?: EncodingOption,\n ): Promise<Buffer | string> {\n \"use step\";\n const { encoding, signal } = parseEncoding(options);\n const buffer = await this.sandbox.readFileToBuffer({ path }, { signal });\n if (buffer === null) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"open\", path);\n }\n return encoding ? buffer.toString(encoding) : buffer;\n }\n\n /**\n * Write data to a file, replacing the file if it already exists.\n *\n * @param path - Path to the file\n * @param data - The data to write\n * @param options - Write options\n */\n async writeFile(\n path: string,\n data: WriteFileData,\n options?:\n | { encoding?: BufferEncoding; signal?: AbortSignal }\n | BufferEncoding,\n ): Promise<void> {\n \"use step\";\n const { encoding, signal } =\n typeof options === \"string\"\n ? { encoding: options, signal: undefined }\n : { encoding: options?.encoding, signal: options?.signal };\n let content: Buffer;\n if (typeof data === \"string\") {\n content = Buffer.from(data, encoding ?? \"utf8\");\n } else if (Buffer.isBuffer(data)) {\n content = data;\n } else {\n content = Buffer.from(data);\n }\n await this.sandbox.writeFiles([{ path, content }], { signal });\n }\n\n /**\n * Append data to a file, creating the file if it does not yet exist.\n *\n * @param path - Path to the file\n * @param data - The data to append\n * @param options - Write options\n */\n async appendFile(\n path: string,\n data: WriteFileData,\n options?:\n | { encoding?: BufferEncoding; signal?: AbortSignal }\n | BufferEncoding,\n ): Promise<void> {\n \"use step\";\n const { encoding, signal } =\n typeof options === \"string\"\n ? { encoding: options, signal: undefined }\n : { encoding: options?.encoding, signal: options?.signal };\n let appendContent: Buffer;\n if (typeof data === \"string\") {\n appendContent = Buffer.from(data, encoding ?? \"utf8\");\n } else if (Buffer.isBuffer(data)) {\n appendContent = data;\n } else {\n appendContent = Buffer.from(data);\n }\n\n const existing = await this.sandbox.readFileToBuffer({ path }, { signal });\n const content =\n existing !== null\n ? Buffer.concat([existing, appendContent])\n : appendContent;\n await this.sandbox.writeFiles([{ path, content }], { signal });\n }\n\n /**\n * Create a directory.\n *\n * @param path - Path of the directory to create\n * @param options - Options for directory creation\n */\n async mkdir(\n path: string,\n options?: MkdirOptions | number,\n ): Promise<string | undefined> {\n \"use step\";\n const opts =\n typeof options === \"number\" ? { recursive: false } : (options ?? {});\n if (opts.recursive) {\n const result = await this.sandbox.runCommand(\"mkdir\", [\"-p\", path], {\n signal: opts.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n throw fsError(\n \"EACCES\",\n stderr.trim() || \"permission denied\",\n \"mkdir\",\n path,\n );\n }\n return undefined;\n }\n await this.sandbox.mkDir(path, { signal: opts.signal });\n return undefined;\n }\n\n /**\n * Read the contents of a directory.\n *\n * @param path - Path to the directory\n * @param options - Options. When `withFileTypes` is true, returns `Dirent` objects.\n */\n async readdir(\n path: string,\n options?: { signal?: AbortSignal; withFileTypes?: false },\n ): Promise<string[]>;\n async readdir(\n path: string,\n options: { signal?: AbortSignal; withFileTypes: true },\n ): Promise<Dirent[]>;\n async readdir(\n path: string,\n options?: { signal?: AbortSignal; withFileTypes?: boolean },\n ): Promise<string[] | Dirent[]> {\n \"use step\";\n if (options?.withFileTypes) {\n // Use find to get name and type in one pass\n const result = await this.sandbox.runCommand(\n \"find\",\n [path, \"-maxdepth\", \"1\", \"-mindepth\", \"1\", \"-printf\", \"%f|%y\\\\n\"],\n { signal: options?.signal },\n );\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"scandir\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"scandir\", path);\n }\n const stdout = await result.stdout();\n const lines = stdout.trim().split(\"\\n\").filter(Boolean);\n\n return lines.map((line) => parseDirent(line, path));\n }\n\n const result = await this.sandbox.runCommand(\"ls\", [\"-1\", path], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"scandir\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"scandir\", path);\n }\n const stdout = await result.stdout();\n return stdout.trim().split(\"\\n\").filter(Boolean);\n }\n\n /**\n * Get file status. Follows symbolic links.\n *\n * @param path - Path to the file\n * @param options - Options\n */\n async stat(path: string, options?: { signal?: AbortSignal }): Promise<Stats> {\n \"use step\";\n const result = await this.sandbox.runCommand(\n \"stat\",\n [\"-L\", \"-c\", STAT_FORMAT, path],\n { signal: options?.signal },\n );\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"stat\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"stat\", path);\n }\n return parseStat(await result.stdout());\n }\n\n /**\n * Get file status. Does not follow symbolic links.\n *\n * @param path - Path to the file\n * @param options - Options\n */\n async lstat(\n path: string,\n options?: { signal?: AbortSignal },\n ): Promise<Stats> {\n \"use step\";\n const result = await this.sandbox.runCommand(\n \"stat\",\n [\"-c\", STAT_FORMAT, path],\n { signal: options?.signal },\n );\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"lstat\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"lstat\", path);\n }\n return parseStat(await result.stdout());\n }\n\n /**\n * Remove a file or symbolic link.\n *\n * @param path - Path to the file\n * @param options - Options\n */\n async unlink(\n path: string,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const result = await this.sandbox.runCommand(\"rm\", [path], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"unlink\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"unlink\", path);\n }\n }\n\n /**\n * Remove files and directories.\n *\n * @param path - Path to remove\n * @param options - Options\n */\n async rm(path: string, options?: RmOptions): Promise<void> {\n \"use step\";\n const args: string[] = [];\n if (options?.recursive) args.push(\"-r\");\n if (options?.force) args.push(\"-f\");\n args.push(path);\n\n const result = await this.sandbox.runCommand(\"rm\", args, {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"rm\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"rm\", path);\n }\n }\n\n /**\n * Remove a directory.\n *\n * @param path - Path to the directory\n * @param options - Options\n */\n async rmdir(path: string, options?: { signal?: AbortSignal }): Promise<void> {\n \"use step\";\n const result = await this.sandbox.runCommand(\"rmdir\", [path], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"rmdir\", path);\n }\n if (stderr.includes(\"not empty\")) {\n throw fsError(\"ENOTEMPTY\", \"directory not empty\", \"rmdir\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"rmdir\", path);\n }\n }\n\n /**\n * Rename a file or directory.\n *\n * @param oldPath - Current path\n * @param newPath - New path\n * @param options - Options\n */\n async rename(\n oldPath: string,\n newPath: string,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const result = await this.sandbox.runCommand(\"mv\", [oldPath, newPath], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"rename\", oldPath);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"rename\", oldPath);\n }\n }\n\n /**\n * Copy a file.\n *\n * @param src - Source path\n * @param dest - Destination path\n * @param options - Options\n */\n async copyFile(\n src: string,\n dest: string,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const result = await this.sandbox.runCommand(\"cp\", [src, dest], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"copyfile\", src);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"copyfile\", src);\n }\n }\n\n /**\n * Test whether a file exists and the user has the specified permissions.\n *\n * @param path - Path to the file\n * @param options - Options\n */\n async access(\n path: string,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const result = await this.sandbox.runCommand(\"test\", [\"-e\", path], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"access\", path);\n }\n }\n\n /**\n * Check if a path exists.\n *\n * This is a convenience method not in `node:fs/promises` but commonly needed.\n *\n * @param path - Path to check\n * @param options - Options\n */\n async exists(\n path: string,\n options?: { signal?: AbortSignal },\n ): Promise<boolean> {\n const result = await this.sandbox.runCommand(\"test\", [\"-e\", path], {\n signal: options?.signal,\n });\n return result.exitCode === 0;\n }\n\n /**\n * Change file mode (permissions).\n *\n * @param path - Path to the file\n * @param mode - File mode (e.g., 0o755 or \"755\")\n * @param options - Options\n */\n async chmod(\n path: string,\n mode: number | string,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const modeStr = typeof mode === \"number\" ? mode.toString(8) : mode;\n const result = await this.sandbox.runCommand(\"chmod\", [modeStr, path], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"chmod\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"chmod\", path);\n }\n }\n\n /**\n * Change file owner and group.\n *\n * @param path - Path to the file\n * @param uid - User ID\n * @param gid - Group ID\n * @param options - Options\n */\n async chown(\n path: string,\n uid: number,\n gid: number,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const result = await this.sandbox.runCommand(\n \"chown\",\n [`${uid}:${gid}`, path],\n { signal: options?.signal },\n );\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"chown\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"chown\", path);\n }\n }\n\n /**\n * Create a symbolic link.\n *\n * @param target - The target of the symbolic link\n * @param path - The path of the symbolic link to create\n * @param options - Options\n */\n async symlink(\n target: string,\n path: string,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const result = await this.sandbox.runCommand(\"ln\", [\"-s\", target, path], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"File exists\")) {\n throw fsError(\"EEXIST\", \"file already exists\", \"symlink\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"symlink\", path);\n }\n }\n\n /**\n * Read the value of a symbolic link.\n *\n * @param path - Path to the symbolic link\n * @param options - Options\n */\n async readlink(\n path: string,\n options?: { signal?: AbortSignal },\n ): Promise<string> {\n \"use step\";\n const result = await this.sandbox.runCommand(\"readlink\", [path], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"readlink\", path);\n }\n throw fsError(\"EINVAL\", \"invalid argument\", \"readlink\", path);\n }\n return (await result.stdout()).trim();\n }\n\n /**\n * Resolve the real path of a file (resolving symlinks).\n *\n * @param path - Path to resolve\n * @param options - Options\n */\n async realpath(\n path: string,\n options?: { signal?: AbortSignal },\n ): Promise<string> {\n \"use step\";\n const result = await this.sandbox.runCommand(\"realpath\", [path], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"realpath\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"realpath\", path);\n }\n return (await result.stdout()).trim();\n }\n\n /**\n * Truncate a file to a specified length.\n *\n * @param path - Path to the file\n * @param len - Length to truncate to (default: 0)\n * @param options - Options\n */\n async truncate(\n path: string,\n len?: number,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const result = await this.sandbox.runCommand(\n \"truncate\",\n [\"-s\", String(len ?? 0), path],\n { signal: options?.signal },\n );\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"truncate\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"truncate\", path);\n }\n }\n\n /**\n * Create a unique temporary directory.\n *\n * @param prefix - The prefix for the temporary directory name\n * @param options - Options\n * @returns The path of the created temporary directory\n */\n async mkdtemp(\n prefix: string,\n options?: { signal?: AbortSignal },\n ): Promise<string> {\n \"use step\";\n const result = await this.sandbox.runCommand(\n \"mktemp\",\n [\"-d\", `${prefix}XXXXXX`],\n { signal: options?.signal },\n );\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n throw fsError(\"EACCES\", stderr.trim(), \"mkdtemp\", prefix);\n }\n return (await result.stdout()).trim();\n }\n}\n"],"mappings":"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"}
import { Dirent, Stats } from "fs";
//#region src/filesystem.d.ts
type WriteFileData = string | Buffer | Uint8Array;
interface MkdirOptions {
recursive?: boolean;
signal?: AbortSignal;
}
interface RmOptions {
recursive?: boolean;
force?: boolean;
signal?: AbortSignal;
}
interface SandboxHandle {
readFileToBuffer(file: {
path: string;
}, opts?: {
signal?: AbortSignal;
}): Promise<Buffer | null>;
writeFiles(files: {
path: string;
content: Buffer;
}[], opts?: {
signal?: AbortSignal;
}): Promise<void>;
mkDir(path: string, opts?: {
signal?: AbortSignal;
}): Promise<void>;
runCommand(cmd: string, args?: string[], opts?: {
signal?: AbortSignal;
}): Promise<{
exitCode: number;
stdout(opts?: {
signal?: AbortSignal;
}): Promise<string>;
stderr(opts?: {
signal?: AbortSignal;
}): Promise<string>;
}>;
}
declare class FileSystem {
/** @internal */
private sandbox;
/** @internal */
constructor(sandbox: SandboxHandle);
/**
* Read the entire contents of a file.
*
* @param path - Path to the file
* @param options - Encoding or options object. If encoding is specified, returns a string; otherwise returns a Buffer.
*/
readFile(path: string, options?: {
encoding?: null;
signal?: AbortSignal;
} | null): Promise<Buffer>;
readFile(path: string, options: {
encoding: BufferEncoding;
signal?: AbortSignal;
} | BufferEncoding): Promise<string>;
/**
* Write data to a file, replacing the file if it already exists.
*
* @param path - Path to the file
* @param data - The data to write
* @param options - Write options
*/
writeFile(path: string, data: WriteFileData, options?: {
encoding?: BufferEncoding;
signal?: AbortSignal;
} | BufferEncoding): Promise<void>;
/**
* Append data to a file, creating the file if it does not yet exist.
*
* @param path - Path to the file
* @param data - The data to append
* @param options - Write options
*/
appendFile(path: string, data: WriteFileData, options?: {
encoding?: BufferEncoding;
signal?: AbortSignal;
} | BufferEncoding): Promise<void>;
/**
* Create a directory.
*
* @param path - Path of the directory to create
* @param options - Options for directory creation
*/
mkdir(path: string, options?: MkdirOptions | number): Promise<string | undefined>;
/**
* Read the contents of a directory.
*
* @param path - Path to the directory
* @param options - Options. When `withFileTypes` is true, returns `Dirent` objects.
*/
readdir(path: string, options?: {
signal?: AbortSignal;
withFileTypes?: false;
}): Promise<string[]>;
readdir(path: string, options: {
signal?: AbortSignal;
withFileTypes: true;
}): Promise<Dirent[]>;
/**
* Get file status. Follows symbolic links.
*
* @param path - Path to the file
* @param options - Options
*/
stat(path: string, options?: {
signal?: AbortSignal;
}): Promise<Stats>;
/**
* Get file status. Does not follow symbolic links.
*
* @param path - Path to the file
* @param options - Options
*/
lstat(path: string, options?: {
signal?: AbortSignal;
}): Promise<Stats>;
/**
* Remove a file or symbolic link.
*
* @param path - Path to the file
* @param options - Options
*/
unlink(path: string, options?: {
signal?: AbortSignal;
}): Promise<void>;
/**
* Remove files and directories.
*
* @param path - Path to remove
* @param options - Options
*/
rm(path: string, options?: RmOptions): Promise<void>;
/**
* Remove a directory.
*
* @param path - Path to the directory
* @param options - Options
*/
rmdir(path: string, options?: {
signal?: AbortSignal;
}): Promise<void>;
/**
* Rename a file or directory.
*
* @param oldPath - Current path
* @param newPath - New path
* @param options - Options
*/
rename(oldPath: string, newPath: string, options?: {
signal?: AbortSignal;
}): Promise<void>;
/**
* Copy a file.
*
* @param src - Source path
* @param dest - Destination path
* @param options - Options
*/
copyFile(src: string, dest: string, options?: {
signal?: AbortSignal;
}): Promise<void>;
/**
* Test whether a file exists and the user has the specified permissions.
*
* @param path - Path to the file
* @param options - Options
*/
access(path: string, options?: {
signal?: AbortSignal;
}): Promise<void>;
/**
* Check if a path exists.
*
* This is a convenience method not in `node:fs/promises` but commonly needed.
*
* @param path - Path to check
* @param options - Options
*/
exists(path: string, options?: {
signal?: AbortSignal;
}): Promise<boolean>;
/**
* Change file mode (permissions).
*
* @param path - Path to the file
* @param mode - File mode (e.g., 0o755 or "755")
* @param options - Options
*/
chmod(path: string, mode: number | string, options?: {
signal?: AbortSignal;
}): Promise<void>;
/**
* Change file owner and group.
*
* @param path - Path to the file
* @param uid - User ID
* @param gid - Group ID
* @param options - Options
*/
chown(path: string, uid: number, gid: number, options?: {
signal?: AbortSignal;
}): Promise<void>;
/**
* Create a symbolic link.
*
* @param target - The target of the symbolic link
* @param path - The path of the symbolic link to create
* @param options - Options
*/
symlink(target: string, path: string, options?: {
signal?: AbortSignal;
}): Promise<void>;
/**
* Read the value of a symbolic link.
*
* @param path - Path to the symbolic link
* @param options - Options
*/
readlink(path: string, options?: {
signal?: AbortSignal;
}): Promise<string>;
/**
* Resolve the real path of a file (resolving symlinks).
*
* @param path - Path to resolve
* @param options - Options
*/
realpath(path: string, options?: {
signal?: AbortSignal;
}): Promise<string>;
/**
* Truncate a file to a specified length.
*
* @param path - Path to the file
* @param len - Length to truncate to (default: 0)
* @param options - Options
*/
truncate(path: string, len?: number, options?: {
signal?: AbortSignal;
}): Promise<void>;
/**
* Create a unique temporary directory.
*
* @param prefix - The prefix for the temporary directory name
* @param options - Options
* @returns The path of the created temporary directory
*/
mkdtemp(prefix: string, options?: {
signal?: AbortSignal;
}): Promise<string>;
}
//#endregion
export { FileSystem };
//# sourceMappingURL=filesystem.d.cts.map
import { Dirent, Stats } from "fs";
//#region src/filesystem.d.ts
type WriteFileData = string | Buffer | Uint8Array;
interface MkdirOptions {
recursive?: boolean;
signal?: AbortSignal;
}
interface RmOptions {
recursive?: boolean;
force?: boolean;
signal?: AbortSignal;
}
interface SandboxHandle {
readFileToBuffer(file: {
path: string;
}, opts?: {
signal?: AbortSignal;
}): Promise<Buffer | null>;
writeFiles(files: {
path: string;
content: Buffer;
}[], opts?: {
signal?: AbortSignal;
}): Promise<void>;
mkDir(path: string, opts?: {
signal?: AbortSignal;
}): Promise<void>;
runCommand(cmd: string, args?: string[], opts?: {
signal?: AbortSignal;
}): Promise<{
exitCode: number;
stdout(opts?: {
signal?: AbortSignal;
}): Promise<string>;
stderr(opts?: {
signal?: AbortSignal;
}): Promise<string>;
}>;
}
declare class FileSystem {
/** @internal */
private sandbox;
/** @internal */
constructor(sandbox: SandboxHandle);
/**
* Read the entire contents of a file.
*
* @param path - Path to the file
* @param options - Encoding or options object. If encoding is specified, returns a string; otherwise returns a Buffer.
*/
readFile(path: string, options?: {
encoding?: null;
signal?: AbortSignal;
} | null): Promise<Buffer>;
readFile(path: string, options: {
encoding: BufferEncoding;
signal?: AbortSignal;
} | BufferEncoding): Promise<string>;
/**
* Write data to a file, replacing the file if it already exists.
*
* @param path - Path to the file
* @param data - The data to write
* @param options - Write options
*/
writeFile(path: string, data: WriteFileData, options?: {
encoding?: BufferEncoding;
signal?: AbortSignal;
} | BufferEncoding): Promise<void>;
/**
* Append data to a file, creating the file if it does not yet exist.
*
* @param path - Path to the file
* @param data - The data to append
* @param options - Write options
*/
appendFile(path: string, data: WriteFileData, options?: {
encoding?: BufferEncoding;
signal?: AbortSignal;
} | BufferEncoding): Promise<void>;
/**
* Create a directory.
*
* @param path - Path of the directory to create
* @param options - Options for directory creation
*/
mkdir(path: string, options?: MkdirOptions | number): Promise<string | undefined>;
/**
* Read the contents of a directory.
*
* @param path - Path to the directory
* @param options - Options. When `withFileTypes` is true, returns `Dirent` objects.
*/
readdir(path: string, options?: {
signal?: AbortSignal;
withFileTypes?: false;
}): Promise<string[]>;
readdir(path: string, options: {
signal?: AbortSignal;
withFileTypes: true;
}): Promise<Dirent[]>;
/**
* Get file status. Follows symbolic links.
*
* @param path - Path to the file
* @param options - Options
*/
stat(path: string, options?: {
signal?: AbortSignal;
}): Promise<Stats>;
/**
* Get file status. Does not follow symbolic links.
*
* @param path - Path to the file
* @param options - Options
*/
lstat(path: string, options?: {
signal?: AbortSignal;
}): Promise<Stats>;
/**
* Remove a file or symbolic link.
*
* @param path - Path to the file
* @param options - Options
*/
unlink(path: string, options?: {
signal?: AbortSignal;
}): Promise<void>;
/**
* Remove files and directories.
*
* @param path - Path to remove
* @param options - Options
*/
rm(path: string, options?: RmOptions): Promise<void>;
/**
* Remove a directory.
*
* @param path - Path to the directory
* @param options - Options
*/
rmdir(path: string, options?: {
signal?: AbortSignal;
}): Promise<void>;
/**
* Rename a file or directory.
*
* @param oldPath - Current path
* @param newPath - New path
* @param options - Options
*/
rename(oldPath: string, newPath: string, options?: {
signal?: AbortSignal;
}): Promise<void>;
/**
* Copy a file.
*
* @param src - Source path
* @param dest - Destination path
* @param options - Options
*/
copyFile(src: string, dest: string, options?: {
signal?: AbortSignal;
}): Promise<void>;
/**
* Test whether a file exists and the user has the specified permissions.
*
* @param path - Path to the file
* @param options - Options
*/
access(path: string, options?: {
signal?: AbortSignal;
}): Promise<void>;
/**
* Check if a path exists.
*
* This is a convenience method not in `node:fs/promises` but commonly needed.
*
* @param path - Path to check
* @param options - Options
*/
exists(path: string, options?: {
signal?: AbortSignal;
}): Promise<boolean>;
/**
* Change file mode (permissions).
*
* @param path - Path to the file
* @param mode - File mode (e.g., 0o755 or "755")
* @param options - Options
*/
chmod(path: string, mode: number | string, options?: {
signal?: AbortSignal;
}): Promise<void>;
/**
* Change file owner and group.
*
* @param path - Path to the file
* @param uid - User ID
* @param gid - Group ID
* @param options - Options
*/
chown(path: string, uid: number, gid: number, options?: {
signal?: AbortSignal;
}): Promise<void>;
/**
* Create a symbolic link.
*
* @param target - The target of the symbolic link
* @param path - The path of the symbolic link to create
* @param options - Options
*/
symlink(target: string, path: string, options?: {
signal?: AbortSignal;
}): Promise<void>;
/**
* Read the value of a symbolic link.
*
* @param path - Path to the symbolic link
* @param options - Options
*/
readlink(path: string, options?: {
signal?: AbortSignal;
}): Promise<string>;
/**
* Resolve the real path of a file (resolving symlinks).
*
* @param path - Path to resolve
* @param options - Options
*/
realpath(path: string, options?: {
signal?: AbortSignal;
}): Promise<string>;
/**
* Truncate a file to a specified length.
*
* @param path - Path to the file
* @param len - Length to truncate to (default: 0)
* @param options - Options
*/
truncate(path: string, len?: number, options?: {
signal?: AbortSignal;
}): Promise<void>;
/**
* Create a unique temporary directory.
*
* @param prefix - The prefix for the temporary directory name
* @param options - Options
* @returns The path of the created temporary directory
*/
mkdtemp(prefix: string, options?: {
signal?: AbortSignal;
}): Promise<string>;
}
//#endregion
export { FileSystem };
//# sourceMappingURL=filesystem.d.ts.map
import * as constants from "node:constants";
//#region src/filesystem.ts
const { S_IFMT, S_IFREG, S_IFDIR, S_IFLNK, S_IFBLK, S_IFCHR, S_IFIFO, S_IFSOCK } = constants;
const UV_DIRENT_FILE = 1;
const UV_DIRENT_DIR = 2;
const UV_DIRENT_LINK = 3;
const UV_DIRENT_FIFO = 4;
const UV_DIRENT_SOCKET = 5;
const UV_DIRENT_CHAR = 6;
const UV_DIRENT_BLOCK = 7;
function fsError(code, message, syscall, path) {
const err = /* @__PURE__ */ new Error(`${code}: ${message}, ${syscall} '${path}'`);
err.code = code;
err.syscall = syscall;
err.path = path;
return err;
}
function parseEncoding(options) {
if (options === null || options === void 0) return { encoding: null };
if (typeof options === "string") return { encoding: options };
return {
encoding: options.encoding ?? null,
signal: options.signal
};
}
var SandboxStats = class {
constructor(dev, _mode, nlink, uid, gid, rdev, blksize, ino, size, blocks, atimeMs, mtimeMs, ctimeMs, birthtimeMs) {
this.dev = dev;
this._mode = _mode;
this.nlink = nlink;
this.uid = uid;
this.gid = gid;
this.rdev = rdev;
this.blksize = blksize;
this.ino = ino;
this.size = size;
this.blocks = blocks;
this.atimeMs = atimeMs;
this.mtimeMs = mtimeMs;
this.ctimeMs = ctimeMs;
this.birthtimeMs = birthtimeMs;
this.atime = new Date(atimeMs);
this.mtime = new Date(mtimeMs);
this.ctime = new Date(ctimeMs);
this.birthtime = new Date(birthtimeMs);
}
get mode() {
return this._mode;
}
isFile() {
return (this.mode & S_IFMT) === S_IFREG;
}
isDirectory() {
return (this.mode & S_IFMT) === S_IFDIR;
}
isBlockDevice() {
return (this.mode & S_IFMT) === S_IFBLK;
}
isCharacterDevice() {
return (this.mode & S_IFMT) === S_IFCHR;
}
isSymbolicLink() {
return (this.mode & S_IFMT) === S_IFLNK;
}
isFIFO() {
return (this.mode & S_IFMT) === S_IFIFO;
}
isSocket() {
return (this.mode & S_IFMT) === S_IFSOCK;
}
};
var SandboxDirent = class {
constructor(name, type, parentPath) {
this.name = name;
this.type = type;
this.parentPath = parentPath;
this.path = `${this.parentPath}/${this.name}`;
}
isFile() {
return this.type === UV_DIRENT_FILE;
}
isDirectory() {
return this.type === UV_DIRENT_DIR;
}
isBlockDevice() {
return this.type === UV_DIRENT_BLOCK;
}
isCharacterDevice() {
return this.type === UV_DIRENT_CHAR;
}
isSymbolicLink() {
return this.type === UV_DIRENT_LINK;
}
isFIFO() {
return this.type === UV_DIRENT_FIFO;
}
isSocket() {
return this.type === UV_DIRENT_SOCKET;
}
};
function parseStat(stdout) {
const parts = stdout.trim().split("|");
return new SandboxStats(parseInt(parts[10], 10), parseInt(parts[1], 16), parseInt(parts[8], 10), parseInt(parts[2], 10), parseInt(parts[3], 10), 0, parseInt(parts[11], 10), parseInt(parts[9], 10), parseInt(parts[0], 10), parseInt(parts[12], 10), parseFloat(parts[4]) * 1e3, parseFloat(parts[5]) * 1e3, parseFloat(parts[6]) * 1e3, parseFloat(parts[7]) * 1e3);
}
function parseDirent(stdout, path) {
const parts = stdout.trim().split("|");
const name = parts[0];
const type = parts[1];
if (!name) throw fsError("ENOENT", "no such file or directory", "readdir", path);
if (!type) throw new Error(`Invalid dirent type: ${type}`);
return new SandboxDirent(name, FIND_TYPE_TO_DIRENT[type] ?? UV_DIRENT_FILE, path);
}
const STAT_FORMAT = "%s|%f|%u|%g|%X|%Y|%Z|%W|%h|%i|%d|%B|%b";
const FIND_TYPE_TO_DIRENT = {
f: UV_DIRENT_FILE,
d: UV_DIRENT_DIR,
l: UV_DIRENT_LINK,
b: UV_DIRENT_BLOCK,
c: UV_DIRENT_CHAR,
p: UV_DIRENT_FIFO,
s: UV_DIRENT_SOCKET
};
var FileSystem = class {
/** @internal */
constructor(sandbox) {
this.sandbox = sandbox;
}
async readFile(path, options) {
"use step";
const { encoding, signal } = parseEncoding(options);
const buffer = await this.sandbox.readFileToBuffer({ path }, { signal });
if (buffer === null) throw fsError("ENOENT", "no such file or directory", "open", path);
return encoding ? buffer.toString(encoding) : buffer;
}
/**
* Write data to a file, replacing the file if it already exists.
*
* @param path - Path to the file
* @param data - The data to write
* @param options - Write options
*/
async writeFile(path, data, options) {
"use step";
const { encoding, signal } = typeof options === "string" ? {
encoding: options,
signal: void 0
} : {
encoding: options?.encoding,
signal: options?.signal
};
let content;
if (typeof data === "string") content = Buffer.from(data, encoding ?? "utf8");
else if (Buffer.isBuffer(data)) content = data;
else content = Buffer.from(data);
await this.sandbox.writeFiles([{
path,
content
}], { signal });
}
/**
* Append data to a file, creating the file if it does not yet exist.
*
* @param path - Path to the file
* @param data - The data to append
* @param options - Write options
*/
async appendFile(path, data, options) {
"use step";
const { encoding, signal } = typeof options === "string" ? {
encoding: options,
signal: void 0
} : {
encoding: options?.encoding,
signal: options?.signal
};
let appendContent;
if (typeof data === "string") appendContent = Buffer.from(data, encoding ?? "utf8");
else if (Buffer.isBuffer(data)) appendContent = data;
else appendContent = Buffer.from(data);
const existing = await this.sandbox.readFileToBuffer({ path }, { signal });
const content = existing !== null ? Buffer.concat([existing, appendContent]) : appendContent;
await this.sandbox.writeFiles([{
path,
content
}], { signal });
}
/**
* Create a directory.
*
* @param path - Path of the directory to create
* @param options - Options for directory creation
*/
async mkdir(path, options) {
"use step";
const opts = typeof options === "number" ? { recursive: false } : options ?? {};
if (opts.recursive) {
const result = await this.sandbox.runCommand("mkdir", ["-p", path], { signal: opts.signal });
if (result.exitCode !== 0) throw fsError("EACCES", (await result.stderr()).trim() || "permission denied", "mkdir", path);
return;
}
await this.sandbox.mkDir(path, { signal: opts.signal });
}
async readdir(path, options) {
"use step";
if (options?.withFileTypes) {
const result$1 = await this.sandbox.runCommand("find", [
path,
"-maxdepth",
"1",
"-mindepth",
"1",
"-printf",
"%f|%y\\n"
], { signal: options?.signal });
if (result$1.exitCode !== 0) {
const stderr = await result$1.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "scandir", path);
throw fsError("EACCES", stderr.trim(), "scandir", path);
}
return (await result$1.stdout()).trim().split("\n").filter(Boolean).map((line) => parseDirent(line, path));
}
const result = await this.sandbox.runCommand("ls", ["-1", path], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "scandir", path);
throw fsError("EACCES", stderr.trim(), "scandir", path);
}
return (await result.stdout()).trim().split("\n").filter(Boolean);
}
/**
* Get file status. Follows symbolic links.
*
* @param path - Path to the file
* @param options - Options
*/
async stat(path, options) {
"use step";
const result = await this.sandbox.runCommand("stat", [
"-L",
"-c",
STAT_FORMAT,
path
], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "stat", path);
throw fsError("EACCES", stderr.trim(), "stat", path);
}
return parseStat(await result.stdout());
}
/**
* Get file status. Does not follow symbolic links.
*
* @param path - Path to the file
* @param options - Options
*/
async lstat(path, options) {
"use step";
const result = await this.sandbox.runCommand("stat", [
"-c",
STAT_FORMAT,
path
], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "lstat", path);
throw fsError("EACCES", stderr.trim(), "lstat", path);
}
return parseStat(await result.stdout());
}
/**
* Remove a file or symbolic link.
*
* @param path - Path to the file
* @param options - Options
*/
async unlink(path, options) {
"use step";
const result = await this.sandbox.runCommand("rm", [path], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "unlink", path);
throw fsError("EACCES", stderr.trim(), "unlink", path);
}
}
/**
* Remove files and directories.
*
* @param path - Path to remove
* @param options - Options
*/
async rm(path, options) {
"use step";
const args = [];
if (options?.recursive) args.push("-r");
if (options?.force) args.push("-f");
args.push(path);
const result = await this.sandbox.runCommand("rm", args, { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "rm", path);
throw fsError("EACCES", stderr.trim(), "rm", path);
}
}
/**
* Remove a directory.
*
* @param path - Path to the directory
* @param options - Options
*/
async rmdir(path, options) {
"use step";
const result = await this.sandbox.runCommand("rmdir", [path], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "rmdir", path);
if (stderr.includes("not empty")) throw fsError("ENOTEMPTY", "directory not empty", "rmdir", path);
throw fsError("EACCES", stderr.trim(), "rmdir", path);
}
}
/**
* Rename a file or directory.
*
* @param oldPath - Current path
* @param newPath - New path
* @param options - Options
*/
async rename(oldPath, newPath, options) {
"use step";
const result = await this.sandbox.runCommand("mv", [oldPath, newPath], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "rename", oldPath);
throw fsError("EACCES", stderr.trim(), "rename", oldPath);
}
}
/**
* Copy a file.
*
* @param src - Source path
* @param dest - Destination path
* @param options - Options
*/
async copyFile(src, dest, options) {
"use step";
const result = await this.sandbox.runCommand("cp", [src, dest], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "copyfile", src);
throw fsError("EACCES", stderr.trim(), "copyfile", src);
}
}
/**
* Test whether a file exists and the user has the specified permissions.
*
* @param path - Path to the file
* @param options - Options
*/
async access(path, options) {
"use step";
if ((await this.sandbox.runCommand("test", ["-e", path], { signal: options?.signal })).exitCode !== 0) throw fsError("ENOENT", "no such file or directory", "access", path);
}
/**
* Check if a path exists.
*
* This is a convenience method not in `node:fs/promises` but commonly needed.
*
* @param path - Path to check
* @param options - Options
*/
async exists(path, options) {
return (await this.sandbox.runCommand("test", ["-e", path], { signal: options?.signal })).exitCode === 0;
}
/**
* Change file mode (permissions).
*
* @param path - Path to the file
* @param mode - File mode (e.g., 0o755 or "755")
* @param options - Options
*/
async chmod(path, mode, options) {
"use step";
const modeStr = typeof mode === "number" ? mode.toString(8) : mode;
const result = await this.sandbox.runCommand("chmod", [modeStr, path], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "chmod", path);
throw fsError("EACCES", stderr.trim(), "chmod", path);
}
}
/**
* Change file owner and group.
*
* @param path - Path to the file
* @param uid - User ID
* @param gid - Group ID
* @param options - Options
*/
async chown(path, uid, gid, options) {
"use step";
const result = await this.sandbox.runCommand("chown", [`${uid}:${gid}`, path], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "chown", path);
throw fsError("EACCES", stderr.trim(), "chown", path);
}
}
/**
* Create a symbolic link.
*
* @param target - The target of the symbolic link
* @param path - The path of the symbolic link to create
* @param options - Options
*/
async symlink(target, path, options) {
"use step";
const result = await this.sandbox.runCommand("ln", [
"-s",
target,
path
], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("File exists")) throw fsError("EEXIST", "file already exists", "symlink", path);
throw fsError("EACCES", stderr.trim(), "symlink", path);
}
}
/**
* Read the value of a symbolic link.
*
* @param path - Path to the symbolic link
* @param options - Options
*/
async readlink(path, options) {
"use step";
const result = await this.sandbox.runCommand("readlink", [path], { signal: options?.signal });
if (result.exitCode !== 0) {
if ((await result.stderr()).includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "readlink", path);
throw fsError("EINVAL", "invalid argument", "readlink", path);
}
return (await result.stdout()).trim();
}
/**
* Resolve the real path of a file (resolving symlinks).
*
* @param path - Path to resolve
* @param options - Options
*/
async realpath(path, options) {
"use step";
const result = await this.sandbox.runCommand("realpath", [path], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "realpath", path);
throw fsError("EACCES", stderr.trim(), "realpath", path);
}
return (await result.stdout()).trim();
}
/**
* Truncate a file to a specified length.
*
* @param path - Path to the file
* @param len - Length to truncate to (default: 0)
* @param options - Options
*/
async truncate(path, len, options) {
"use step";
const result = await this.sandbox.runCommand("truncate", [
"-s",
String(len ?? 0),
path
], { signal: options?.signal });
if (result.exitCode !== 0) {
const stderr = await result.stderr();
if (stderr.includes("No such file or directory")) throw fsError("ENOENT", "no such file or directory", "truncate", path);
throw fsError("EACCES", stderr.trim(), "truncate", path);
}
}
/**
* Create a unique temporary directory.
*
* @param prefix - The prefix for the temporary directory name
* @param options - Options
* @returns The path of the created temporary directory
*/
async mkdtemp(prefix, options) {
"use step";
const result = await this.sandbox.runCommand("mktemp", ["-d", `${prefix}XXXXXX`], { signal: options?.signal });
if (result.exitCode !== 0) throw fsError("EACCES", (await result.stderr()).trim(), "mkdtemp", prefix);
return (await result.stdout()).trim();
}
};
//#endregion
export { FileSystem };
//# sourceMappingURL=filesystem.js.map
{"version":3,"file":"filesystem.js","names":["dev: number","_mode: number","nlink: number","uid: number","gid: number","rdev: number","blksize: number","ino: number","size: number","blocks: number","atimeMs: number","mtimeMs: number","ctimeMs: number","birthtimeMs: number","name: string","type: number","parentPath: string","FIND_TYPE_TO_DIRENT: Record<string, number>","content: Buffer","appendContent: Buffer","result","args: string[]"],"sources":["../src/filesystem.ts"],"sourcesContent":["import type { Stats, Dirent } from \"fs\";\nimport * as constants from \"node:constants\";\n\nconst {\n S_IFMT,\n S_IFREG,\n S_IFDIR,\n S_IFLNK,\n S_IFBLK,\n S_IFCHR,\n S_IFIFO,\n S_IFSOCK,\n} = constants;\n\n// https://github.com/nodejs/node/blob/main/deps/uv/include/uv.h#L1267-L1276\n// exposed through node:constants but not in @types/node\nconst UV_DIRENT_FILE = 1;\nconst UV_DIRENT_DIR = 2;\nconst UV_DIRENT_LINK = 3;\nconst UV_DIRENT_FIFO = 4;\nconst UV_DIRENT_SOCKET = 5;\nconst UV_DIRENT_CHAR = 6;\nconst UV_DIRENT_BLOCK = 7;\n\ntype EncodingOption =\n | { encoding?: BufferEncoding | null; signal?: AbortSignal }\n | BufferEncoding\n | null;\n\ntype WriteFileData = string | Buffer | Uint8Array;\n\ninterface MkdirOptions {\n recursive?: boolean;\n signal?: AbortSignal;\n}\n\ninterface RmOptions {\n recursive?: boolean;\n force?: boolean;\n signal?: AbortSignal;\n}\n\nfunction fsError(\n code: string,\n message: string,\n syscall: string,\n path: string,\n): Error & { code: string; syscall: string; path: string } {\n const err = new Error(\n `${code}: ${message}, ${syscall} '${path}'`,\n ) as Error & {\n code: string;\n syscall: string;\n path: string;\n };\n err.code = code;\n err.syscall = syscall;\n err.path = path;\n return err;\n}\n\ninterface SandboxHandle {\n readFileToBuffer(\n file: { path: string },\n opts?: { signal?: AbortSignal },\n ): Promise<Buffer | null>;\n writeFiles(\n files: { path: string; content: Buffer }[],\n opts?: { signal?: AbortSignal },\n ): Promise<void>;\n mkDir(path: string, opts?: { signal?: AbortSignal }): Promise<void>;\n runCommand(\n cmd: string,\n args?: string[],\n opts?: { signal?: AbortSignal },\n ): Promise<{\n exitCode: number;\n stdout(opts?: { signal?: AbortSignal }): Promise<string>;\n stderr(opts?: { signal?: AbortSignal }): Promise<string>;\n }>;\n}\n\nfunction parseEncoding(options?: EncodingOption): {\n encoding: BufferEncoding | null;\n signal?: AbortSignal;\n} {\n if (options === null || options === undefined) {\n return { encoding: null };\n }\n if (typeof options === \"string\") {\n return { encoding: options };\n }\n return { encoding: options.encoding ?? null, signal: options.signal };\n}\n\nclass SandboxStats {\n readonly atime: Date;\n readonly mtime: Date;\n readonly ctime: Date;\n readonly birthtime: Date;\n\n constructor(\n readonly dev: number,\n private readonly _mode: number,\n readonly nlink: number,\n readonly uid: number,\n readonly gid: number,\n readonly rdev: number,\n readonly blksize: number,\n readonly ino: number,\n readonly size: number,\n readonly blocks: number,\n readonly atimeMs: number,\n readonly mtimeMs: number,\n readonly ctimeMs: number,\n readonly birthtimeMs: number,\n ) {\n this.atime = new Date(atimeMs);\n this.mtime = new Date(mtimeMs);\n this.ctime = new Date(ctimeMs);\n this.birthtime = new Date(birthtimeMs);\n }\n\n get mode(): number {\n return this._mode;\n }\n\n isFile(): boolean {\n return (this.mode & S_IFMT) === S_IFREG;\n }\n isDirectory(): boolean {\n return (this.mode & S_IFMT) === S_IFDIR;\n }\n isBlockDevice(): boolean {\n return (this.mode & S_IFMT) === S_IFBLK;\n }\n isCharacterDevice(): boolean {\n return (this.mode & S_IFMT) === S_IFCHR;\n }\n isSymbolicLink(): boolean {\n return (this.mode & S_IFMT) === S_IFLNK;\n }\n isFIFO(): boolean {\n return (this.mode & S_IFMT) === S_IFIFO;\n }\n isSocket(): boolean {\n return (this.mode & S_IFMT) === S_IFSOCK;\n }\n}\n\nclass SandboxDirent {\n readonly path: string;\n\n constructor(\n public name: string,\n private type: number,\n public parentPath: string,\n ) {\n this.path = `${this.parentPath}/${this.name}`;\n }\n\n isFile(): boolean {\n return this.type === UV_DIRENT_FILE;\n }\n isDirectory(): boolean {\n return this.type === UV_DIRENT_DIR;\n }\n isBlockDevice(): boolean {\n return this.type === UV_DIRENT_BLOCK;\n }\n isCharacterDevice(): boolean {\n return this.type === UV_DIRENT_CHAR;\n }\n isSymbolicLink(): boolean {\n return this.type === UV_DIRENT_LINK;\n }\n isFIFO(): boolean {\n return this.type === UV_DIRENT_FIFO;\n }\n isSocket(): boolean {\n return this.type === UV_DIRENT_SOCKET;\n }\n}\n\nfunction parseStat(stdout: string) {\n // Format: size|rawModeHex|uid|gid|atimeMs|mtimeMs|ctimeMs|birthtimeMs|nlink|ino|dev|blksize|blocks\n const parts = stdout.trim().split(\"|\");\n\n return new SandboxStats(\n parseInt(parts[10]!, 10), // dev\n parseInt(parts[1]!, 16), // mode (raw mode from %f, hex)\n parseInt(parts[8]!, 10), // nlink\n parseInt(parts[2]!, 10), // uid\n parseInt(parts[3]!, 10), // gid\n 0, // rdev\n parseInt(parts[11]!, 10), // blksize\n parseInt(parts[9]!, 10), // ino\n parseInt(parts[0]!, 10), // size\n parseInt(parts[12]!, 10), // blocks\n parseFloat(parts[4]!) * 1000, // atimeMs\n parseFloat(parts[5]!) * 1000, // mtimeMs\n parseFloat(parts[6]!) * 1000, // ctimeMs\n parseFloat(parts[7]!) * 1000, // birthtimeMs\n ) satisfies Stats;\n}\n\nfunction parseDirent(stdout: string, path: string) {\n const parts = stdout.trim().split(\"|\");\n const name = parts[0];\n const type = parts[1];\n\n if (!name) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"readdir\", path);\n }\n\n if (!type) {\n throw new Error(`Invalid dirent type: ${type}`);\n }\n\n const direntType = FIND_TYPE_TO_DIRENT[type] ?? UV_DIRENT_FILE;\n return new SandboxDirent(name, direntType, path) satisfies Dirent;\n}\n\n// %f = raw mode in hex, includes file type bits so Stats.isFile()/isDirectory()/etc. work\nconst STAT_FORMAT = \"%s|%f|%u|%g|%X|%Y|%Z|%W|%h|%i|%d|%B|%b\";\n\nconst FIND_TYPE_TO_DIRENT: Record<string, number> = {\n f: UV_DIRENT_FILE,\n d: UV_DIRENT_DIR,\n l: UV_DIRENT_LINK,\n b: UV_DIRENT_BLOCK,\n c: UV_DIRENT_CHAR,\n p: UV_DIRENT_FIFO,\n s: UV_DIRENT_SOCKET,\n};\n\nexport class FileSystem {\n /** @internal */\n private sandbox: SandboxHandle;\n\n /** @internal */\n constructor(sandbox: SandboxHandle) {\n this.sandbox = sandbox;\n }\n\n /**\n * Read the entire contents of a file.\n *\n * @param path - Path to the file\n * @param options - Encoding or options object. If encoding is specified, returns a string; otherwise returns a Buffer.\n */\n async readFile(\n path: string,\n options?: { encoding?: null; signal?: AbortSignal } | null,\n ): Promise<Buffer>;\n async readFile(\n path: string,\n options:\n | { encoding: BufferEncoding; signal?: AbortSignal }\n | BufferEncoding,\n ): Promise<string>;\n async readFile(\n path: string,\n options?: EncodingOption,\n ): Promise<Buffer | string> {\n \"use step\";\n const { encoding, signal } = parseEncoding(options);\n const buffer = await this.sandbox.readFileToBuffer({ path }, { signal });\n if (buffer === null) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"open\", path);\n }\n return encoding ? buffer.toString(encoding) : buffer;\n }\n\n /**\n * Write data to a file, replacing the file if it already exists.\n *\n * @param path - Path to the file\n * @param data - The data to write\n * @param options - Write options\n */\n async writeFile(\n path: string,\n data: WriteFileData,\n options?:\n | { encoding?: BufferEncoding; signal?: AbortSignal }\n | BufferEncoding,\n ): Promise<void> {\n \"use step\";\n const { encoding, signal } =\n typeof options === \"string\"\n ? { encoding: options, signal: undefined }\n : { encoding: options?.encoding, signal: options?.signal };\n let content: Buffer;\n if (typeof data === \"string\") {\n content = Buffer.from(data, encoding ?? \"utf8\");\n } else if (Buffer.isBuffer(data)) {\n content = data;\n } else {\n content = Buffer.from(data);\n }\n await this.sandbox.writeFiles([{ path, content }], { signal });\n }\n\n /**\n * Append data to a file, creating the file if it does not yet exist.\n *\n * @param path - Path to the file\n * @param data - The data to append\n * @param options - Write options\n */\n async appendFile(\n path: string,\n data: WriteFileData,\n options?:\n | { encoding?: BufferEncoding; signal?: AbortSignal }\n | BufferEncoding,\n ): Promise<void> {\n \"use step\";\n const { encoding, signal } =\n typeof options === \"string\"\n ? { encoding: options, signal: undefined }\n : { encoding: options?.encoding, signal: options?.signal };\n let appendContent: Buffer;\n if (typeof data === \"string\") {\n appendContent = Buffer.from(data, encoding ?? \"utf8\");\n } else if (Buffer.isBuffer(data)) {\n appendContent = data;\n } else {\n appendContent = Buffer.from(data);\n }\n\n const existing = await this.sandbox.readFileToBuffer({ path }, { signal });\n const content =\n existing !== null\n ? Buffer.concat([existing, appendContent])\n : appendContent;\n await this.sandbox.writeFiles([{ path, content }], { signal });\n }\n\n /**\n * Create a directory.\n *\n * @param path - Path of the directory to create\n * @param options - Options for directory creation\n */\n async mkdir(\n path: string,\n options?: MkdirOptions | number,\n ): Promise<string | undefined> {\n \"use step\";\n const opts =\n typeof options === \"number\" ? { recursive: false } : (options ?? {});\n if (opts.recursive) {\n const result = await this.sandbox.runCommand(\"mkdir\", [\"-p\", path], {\n signal: opts.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n throw fsError(\n \"EACCES\",\n stderr.trim() || \"permission denied\",\n \"mkdir\",\n path,\n );\n }\n return undefined;\n }\n await this.sandbox.mkDir(path, { signal: opts.signal });\n return undefined;\n }\n\n /**\n * Read the contents of a directory.\n *\n * @param path - Path to the directory\n * @param options - Options. When `withFileTypes` is true, returns `Dirent` objects.\n */\n async readdir(\n path: string,\n options?: { signal?: AbortSignal; withFileTypes?: false },\n ): Promise<string[]>;\n async readdir(\n path: string,\n options: { signal?: AbortSignal; withFileTypes: true },\n ): Promise<Dirent[]>;\n async readdir(\n path: string,\n options?: { signal?: AbortSignal; withFileTypes?: boolean },\n ): Promise<string[] | Dirent[]> {\n \"use step\";\n if (options?.withFileTypes) {\n // Use find to get name and type in one pass\n const result = await this.sandbox.runCommand(\n \"find\",\n [path, \"-maxdepth\", \"1\", \"-mindepth\", \"1\", \"-printf\", \"%f|%y\\\\n\"],\n { signal: options?.signal },\n );\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"scandir\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"scandir\", path);\n }\n const stdout = await result.stdout();\n const lines = stdout.trim().split(\"\\n\").filter(Boolean);\n\n return lines.map((line) => parseDirent(line, path));\n }\n\n const result = await this.sandbox.runCommand(\"ls\", [\"-1\", path], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"scandir\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"scandir\", path);\n }\n const stdout = await result.stdout();\n return stdout.trim().split(\"\\n\").filter(Boolean);\n }\n\n /**\n * Get file status. Follows symbolic links.\n *\n * @param path - Path to the file\n * @param options - Options\n */\n async stat(path: string, options?: { signal?: AbortSignal }): Promise<Stats> {\n \"use step\";\n const result = await this.sandbox.runCommand(\n \"stat\",\n [\"-L\", \"-c\", STAT_FORMAT, path],\n { signal: options?.signal },\n );\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"stat\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"stat\", path);\n }\n return parseStat(await result.stdout());\n }\n\n /**\n * Get file status. Does not follow symbolic links.\n *\n * @param path - Path to the file\n * @param options - Options\n */\n async lstat(\n path: string,\n options?: { signal?: AbortSignal },\n ): Promise<Stats> {\n \"use step\";\n const result = await this.sandbox.runCommand(\n \"stat\",\n [\"-c\", STAT_FORMAT, path],\n { signal: options?.signal },\n );\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"lstat\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"lstat\", path);\n }\n return parseStat(await result.stdout());\n }\n\n /**\n * Remove a file or symbolic link.\n *\n * @param path - Path to the file\n * @param options - Options\n */\n async unlink(\n path: string,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const result = await this.sandbox.runCommand(\"rm\", [path], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"unlink\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"unlink\", path);\n }\n }\n\n /**\n * Remove files and directories.\n *\n * @param path - Path to remove\n * @param options - Options\n */\n async rm(path: string, options?: RmOptions): Promise<void> {\n \"use step\";\n const args: string[] = [];\n if (options?.recursive) args.push(\"-r\");\n if (options?.force) args.push(\"-f\");\n args.push(path);\n\n const result = await this.sandbox.runCommand(\"rm\", args, {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"rm\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"rm\", path);\n }\n }\n\n /**\n * Remove a directory.\n *\n * @param path - Path to the directory\n * @param options - Options\n */\n async rmdir(path: string, options?: { signal?: AbortSignal }): Promise<void> {\n \"use step\";\n const result = await this.sandbox.runCommand(\"rmdir\", [path], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"rmdir\", path);\n }\n if (stderr.includes(\"not empty\")) {\n throw fsError(\"ENOTEMPTY\", \"directory not empty\", \"rmdir\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"rmdir\", path);\n }\n }\n\n /**\n * Rename a file or directory.\n *\n * @param oldPath - Current path\n * @param newPath - New path\n * @param options - Options\n */\n async rename(\n oldPath: string,\n newPath: string,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const result = await this.sandbox.runCommand(\"mv\", [oldPath, newPath], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"rename\", oldPath);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"rename\", oldPath);\n }\n }\n\n /**\n * Copy a file.\n *\n * @param src - Source path\n * @param dest - Destination path\n * @param options - Options\n */\n async copyFile(\n src: string,\n dest: string,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const result = await this.sandbox.runCommand(\"cp\", [src, dest], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"copyfile\", src);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"copyfile\", src);\n }\n }\n\n /**\n * Test whether a file exists and the user has the specified permissions.\n *\n * @param path - Path to the file\n * @param options - Options\n */\n async access(\n path: string,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const result = await this.sandbox.runCommand(\"test\", [\"-e\", path], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"access\", path);\n }\n }\n\n /**\n * Check if a path exists.\n *\n * This is a convenience method not in `node:fs/promises` but commonly needed.\n *\n * @param path - Path to check\n * @param options - Options\n */\n async exists(\n path: string,\n options?: { signal?: AbortSignal },\n ): Promise<boolean> {\n const result = await this.sandbox.runCommand(\"test\", [\"-e\", path], {\n signal: options?.signal,\n });\n return result.exitCode === 0;\n }\n\n /**\n * Change file mode (permissions).\n *\n * @param path - Path to the file\n * @param mode - File mode (e.g., 0o755 or \"755\")\n * @param options - Options\n */\n async chmod(\n path: string,\n mode: number | string,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const modeStr = typeof mode === \"number\" ? mode.toString(8) : mode;\n const result = await this.sandbox.runCommand(\"chmod\", [modeStr, path], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"chmod\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"chmod\", path);\n }\n }\n\n /**\n * Change file owner and group.\n *\n * @param path - Path to the file\n * @param uid - User ID\n * @param gid - Group ID\n * @param options - Options\n */\n async chown(\n path: string,\n uid: number,\n gid: number,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const result = await this.sandbox.runCommand(\n \"chown\",\n [`${uid}:${gid}`, path],\n { signal: options?.signal },\n );\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"chown\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"chown\", path);\n }\n }\n\n /**\n * Create a symbolic link.\n *\n * @param target - The target of the symbolic link\n * @param path - The path of the symbolic link to create\n * @param options - Options\n */\n async symlink(\n target: string,\n path: string,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const result = await this.sandbox.runCommand(\"ln\", [\"-s\", target, path], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"File exists\")) {\n throw fsError(\"EEXIST\", \"file already exists\", \"symlink\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"symlink\", path);\n }\n }\n\n /**\n * Read the value of a symbolic link.\n *\n * @param path - Path to the symbolic link\n * @param options - Options\n */\n async readlink(\n path: string,\n options?: { signal?: AbortSignal },\n ): Promise<string> {\n \"use step\";\n const result = await this.sandbox.runCommand(\"readlink\", [path], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"readlink\", path);\n }\n throw fsError(\"EINVAL\", \"invalid argument\", \"readlink\", path);\n }\n return (await result.stdout()).trim();\n }\n\n /**\n * Resolve the real path of a file (resolving symlinks).\n *\n * @param path - Path to resolve\n * @param options - Options\n */\n async realpath(\n path: string,\n options?: { signal?: AbortSignal },\n ): Promise<string> {\n \"use step\";\n const result = await this.sandbox.runCommand(\"realpath\", [path], {\n signal: options?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"realpath\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"realpath\", path);\n }\n return (await result.stdout()).trim();\n }\n\n /**\n * Truncate a file to a specified length.\n *\n * @param path - Path to the file\n * @param len - Length to truncate to (default: 0)\n * @param options - Options\n */\n async truncate(\n path: string,\n len?: number,\n options?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const result = await this.sandbox.runCommand(\n \"truncate\",\n [\"-s\", String(len ?? 0), path],\n { signal: options?.signal },\n );\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (stderr.includes(\"No such file or directory\")) {\n throw fsError(\"ENOENT\", \"no such file or directory\", \"truncate\", path);\n }\n throw fsError(\"EACCES\", stderr.trim(), \"truncate\", path);\n }\n }\n\n /**\n * Create a unique temporary directory.\n *\n * @param prefix - The prefix for the temporary directory name\n * @param options - Options\n * @returns The path of the created temporary directory\n */\n async mkdtemp(\n prefix: string,\n options?: { signal?: AbortSignal },\n ): Promise<string> {\n \"use step\";\n const result = await this.sandbox.runCommand(\n \"mktemp\",\n [\"-d\", `${prefix}XXXXXX`],\n { signal: options?.signal },\n );\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n throw fsError(\"EACCES\", stderr.trim(), \"mkdtemp\", prefix);\n }\n return (await result.stdout()).trim();\n }\n}\n"],"mappings":"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"}
//#region src/utils/paginator.ts
function attachPaginator(firstPage, options) {
const { itemsKey, fetchNext, signal } = options;
async function* iteratePages() {
throwIfAborted(signal);
let page = firstPage;
yield page;
while (page.pagination.next !== null) {
throwIfAborted(signal);
page = await fetchNext(page.pagination.next);
yield page;
}
}
async function* iterateItems() {
for await (const page of iteratePages()) {
const items = page[itemsKey];
for (const item of items) {
throwIfAborted(signal);
yield item;
}
}
}
return {
...firstPage,
[Symbol.asyncIterator]: iterateItems,
pages: iteratePages,
toArray: async () => {
const all = [];
for await (const item of iterateItems()) all.push(item);
return all;
}
};
}
function throwIfAborted(signal) {
if (signal?.aborted) throw signal.reason ?? new DOMException("Aborted", "AbortError");
}
//#endregion
exports.attachPaginator = attachPaginator;
//# sourceMappingURL=paginator.cjs.map
{"version":3,"file":"paginator.cjs","names":["all: ItemOf<Page, Key>[]"],"sources":["../../src/utils/paginator.ts"],"sourcesContent":["type CursorPaginationMeta = {\n count: number;\n next: string | null;\n};\n\ntype HasPagination = { pagination: CursorPaginationMeta };\n\ntype ItemOf<Page, Key extends keyof Page> = Page[Key] extends Array<infer Item>\n ? Item\n : never;\n\nexport type Paginator<Page extends HasPagination, Key extends keyof Page> =\n Page &\n AsyncIterable<ItemOf<Page, Key>> & {\n pages(): AsyncIterable<Page>;\n toArray(): Promise<ItemOf<Page, Key>[]>;\n };\n\ntype AttachPaginatorOptions<Page extends HasPagination, Key extends keyof Page> = {\n itemsKey: Key;\n fetchNext: (cursor: string) => Promise<Page>;\n signal?: AbortSignal;\n};\n\nexport function attachPaginator<\n Page extends HasPagination,\n Key extends keyof Page,\n>(\n firstPage: Page,\n options: AttachPaginatorOptions<Page, Key>,\n): Paginator<Page, Key> {\n const { itemsKey, fetchNext, signal } = options;\n\n async function* iteratePages(): AsyncGenerator<Page> {\n throwIfAborted(signal);\n let page = firstPage;\n yield page;\n while (page.pagination.next !== null) {\n throwIfAborted(signal);\n page = await fetchNext(page.pagination.next);\n yield page;\n }\n }\n\n async function* iterateItems(): AsyncGenerator<ItemOf<Page, Key>> {\n for await (const page of iteratePages()) {\n const items = page[itemsKey] as unknown as ItemOf<Page, Key>[];\n for (const item of items) {\n throwIfAborted(signal);\n yield item;\n }\n }\n }\n\n return {\n ...firstPage,\n [Symbol.asyncIterator]: iterateItems,\n pages: iteratePages,\n toArray: async () => {\n const all: ItemOf<Page, Key>[] = [];\n for await (const item of iterateItems()) {\n all.push(item);\n }\n return all;\n },\n };\n}\n\nfunction throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException(\"Aborted\", \"AbortError\");\n }\n}\n"],"mappings":";;AAwBA,SAAgB,gBAId,WACA,SACsB;CACtB,MAAM,EAAE,UAAU,WAAW,WAAW;CAExC,gBAAgB,eAAqC;AACnD,iBAAe,OAAO;EACtB,IAAI,OAAO;AACX,QAAM;AACN,SAAO,KAAK,WAAW,SAAS,MAAM;AACpC,kBAAe,OAAO;AACtB,UAAO,MAAM,UAAU,KAAK,WAAW,KAAK;AAC5C,SAAM;;;CAIV,gBAAgB,eAAkD;AAChE,aAAW,MAAM,QAAQ,cAAc,EAAE;GACvC,MAAM,QAAQ,KAAK;AACnB,QAAK,MAAM,QAAQ,OAAO;AACxB,mBAAe,OAAO;AACtB,UAAM;;;;AAKZ,QAAO;EACL,GAAG;GACF,OAAO,gBAAgB;EACxB,OAAO;EACP,SAAS,YAAY;GACnB,MAAMA,MAA2B,EAAE;AACnC,cAAW,MAAM,QAAQ,cAAc,CACrC,KAAI,KAAK,KAAK;AAEhB,UAAO;;EAEV;;AAGH,SAAS,eAAe,QAA4B;AAClD,KAAI,QAAQ,QACV,OAAM,OAAO,UAAU,IAAI,aAAa,WAAW,aAAa"}
//#region src/utils/paginator.d.ts
type CursorPaginationMeta = {
count: number;
next: string | null;
};
type HasPagination = {
pagination: CursorPaginationMeta;
};
type ItemOf<Page, Key extends keyof Page> = Page[Key] extends Array<infer Item> ? Item : never;
type Paginator<Page extends HasPagination, Key extends keyof Page> = Page & AsyncIterable<ItemOf<Page, Key>> & {
pages(): AsyncIterable<Page>;
toArray(): Promise<ItemOf<Page, Key>[]>;
};
//#endregion
export { Paginator };
//# sourceMappingURL=paginator.d.cts.map
//#region src/utils/paginator.d.ts
type CursorPaginationMeta = {
count: number;
next: string | null;
};
type HasPagination = {
pagination: CursorPaginationMeta;
};
type ItemOf<Page, Key extends keyof Page> = Page[Key] extends Array<infer Item> ? Item : never;
type Paginator<Page extends HasPagination, Key extends keyof Page> = Page & AsyncIterable<ItemOf<Page, Key>> & {
pages(): AsyncIterable<Page>;
toArray(): Promise<ItemOf<Page, Key>[]>;
};
//#endregion
export { Paginator };
//# sourceMappingURL=paginator.d.ts.map
//#region src/utils/paginator.ts
function attachPaginator(firstPage, options) {
const { itemsKey, fetchNext, signal } = options;
async function* iteratePages() {
throwIfAborted(signal);
let page = firstPage;
yield page;
while (page.pagination.next !== null) {
throwIfAborted(signal);
page = await fetchNext(page.pagination.next);
yield page;
}
}
async function* iterateItems() {
for await (const page of iteratePages()) {
const items = page[itemsKey];
for (const item of items) {
throwIfAborted(signal);
yield item;
}
}
}
return {
...firstPage,
[Symbol.asyncIterator]: iterateItems,
pages: iteratePages,
toArray: async () => {
const all = [];
for await (const item of iterateItems()) all.push(item);
return all;
}
};
}
function throwIfAborted(signal) {
if (signal?.aborted) throw signal.reason ?? new DOMException("Aborted", "AbortError");
}
//#endregion
export { attachPaginator };
//# sourceMappingURL=paginator.js.map
{"version":3,"file":"paginator.js","names":["all: ItemOf<Page, Key>[]"],"sources":["../../src/utils/paginator.ts"],"sourcesContent":["type CursorPaginationMeta = {\n count: number;\n next: string | null;\n};\n\ntype HasPagination = { pagination: CursorPaginationMeta };\n\ntype ItemOf<Page, Key extends keyof Page> = Page[Key] extends Array<infer Item>\n ? Item\n : never;\n\nexport type Paginator<Page extends HasPagination, Key extends keyof Page> =\n Page &\n AsyncIterable<ItemOf<Page, Key>> & {\n pages(): AsyncIterable<Page>;\n toArray(): Promise<ItemOf<Page, Key>[]>;\n };\n\ntype AttachPaginatorOptions<Page extends HasPagination, Key extends keyof Page> = {\n itemsKey: Key;\n fetchNext: (cursor: string) => Promise<Page>;\n signal?: AbortSignal;\n};\n\nexport function attachPaginator<\n Page extends HasPagination,\n Key extends keyof Page,\n>(\n firstPage: Page,\n options: AttachPaginatorOptions<Page, Key>,\n): Paginator<Page, Key> {\n const { itemsKey, fetchNext, signal } = options;\n\n async function* iteratePages(): AsyncGenerator<Page> {\n throwIfAborted(signal);\n let page = firstPage;\n yield page;\n while (page.pagination.next !== null) {\n throwIfAborted(signal);\n page = await fetchNext(page.pagination.next);\n yield page;\n }\n }\n\n async function* iterateItems(): AsyncGenerator<ItemOf<Page, Key>> {\n for await (const page of iteratePages()) {\n const items = page[itemsKey] as unknown as ItemOf<Page, Key>[];\n for (const item of items) {\n throwIfAborted(signal);\n yield item;\n }\n }\n }\n\n return {\n ...firstPage,\n [Symbol.asyncIterator]: iterateItems,\n pages: iteratePages,\n toArray: async () => {\n const all: ItemOf<Page, Key>[] = [];\n for await (const item of iterateItems()) {\n all.push(item);\n }\n return all;\n },\n };\n}\n\nfunction throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException(\"Aborted\", \"AbortError\");\n }\n}\n"],"mappings":";AAwBA,SAAgB,gBAId,WACA,SACsB;CACtB,MAAM,EAAE,UAAU,WAAW,WAAW;CAExC,gBAAgB,eAAqC;AACnD,iBAAe,OAAO;EACtB,IAAI,OAAO;AACX,QAAM;AACN,SAAO,KAAK,WAAW,SAAS,MAAM;AACpC,kBAAe,OAAO;AACtB,UAAO,MAAM,UAAU,KAAK,WAAW,KAAK;AAC5C,SAAM;;;CAIV,gBAAgB,eAAkD;AAChE,aAAW,MAAM,QAAQ,cAAc,EAAE;GACvC,MAAM,QAAQ,KAAK;AACnB,QAAK,MAAM,QAAQ,OAAO;AACxB,mBAAe,OAAO;AACtB,UAAM;;;;AAKZ,QAAO;EACL,GAAG;GACF,OAAO,gBAAgB;EACxB,OAAO;EACP,SAAS,YAAY;GACnB,MAAMA,MAA2B,EAAE;AACnC,cAAW,MAAM,QAAQ,cAAc,CACrC,KAAI,KAAK,KAAK;AAEhB,UAAO;;EAEV;;AAGH,SAAS,eAAe,QAA4B;AAClD,KAAI,QAAQ,QACV,OAAM,OAAO,UAAU,IAAI,aAAa,WAAW,aAAa"}
+1
-14

@@ -11,3 +11,2 @@ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');

const require_types = require('../utils/types.cjs');
let node_timers_promises = require("node:timers/promises");
let zod = require("zod");

@@ -299,18 +298,6 @@ let stream = require("stream");

const url = `/v2/sandboxes/sessions/${params.sessionId}/stop`;
const response = await require_base_client.parseOrThrow(require_validators.SessionResponse, await this.request(url, {
return require_base_client.parseOrThrow(require_validators.StopSessionResponse, await this.request(url, {
method: "POST",
signal: params.signal
}));
if (params.blocking) {
let session = response.json.session;
while (session.status !== "stopped" && session.status !== "failed" && session.status !== "aborted") {
await (0, node_timers_promises.setTimeout)(500, void 0, { signal: params.signal });
session = (await this.getSession({
sessionId: params.sessionId,
signal: params.signal
})).json.session;
response.json.session = session;
}
}
return response;
}

@@ -317,0 +304,0 @@ async updateNetworkPolicy(params) {

@@ -1,1 +0,1 @@

{"version":3,"file":"api-client.cjs","names":["BaseClient","VERSION","getPrivateParams","parseOrThrow","SessionAndRoutesResponse","SandboxAndSessionResponse","toAPINetworkPolicy","z","APIError","StreamError","CommandResponse","CommandFinishedResponse","command","EmptyResponse","FileWriter","consumeReadable","SessionsResponse","SnapshotsResponse","normalizePath","Readable","LogLine","SessionResponse","CreateSnapshotResponse","SnapshotResponse","query: Record<string, string | undefined>","SandboxesPaginationResponse","UpdateSandboxResponse"],"sources":["../../src/api-client/api-client.ts"],"sourcesContent":["import {\n BaseClient,\n parseOrThrow,\n type Parsed,\n type RequestParams,\n} from \"./base-client.js\";\nimport {\n type CommandFinishedData,\n SessionAndRoutesResponse,\n SessionResponse,\n SessionsResponse,\n CommandResponse,\n CommandFinishedResponse,\n EmptyResponse,\n LogLine,\n type LogLineStdout,\n type LogLineStderr,\n SnapshotsResponse,\n SnapshotResponse,\n CreateSnapshotResponse,\n SandboxAndSessionResponse,\n SandboxesPaginationResponse,\n UpdateSandboxResponse,\n type CommandData,\n} from \"./validators.js\";\nimport { APIError, StreamError } from \"./api-error.js\";\nimport { FileWriter } from \"./file-writer.js\";\nimport { VERSION } from \"../version.js\";\nimport { consumeReadable } from \"../utils/consume-readable.js\";\nimport { z } from \"zod\";\nimport jsonlines from \"jsonlines\";\nimport os from \"os\";\nimport { Readable } from \"stream\";\nimport { normalizePath } from \"../utils/normalizePath.js\";\nimport { getVercelOidcToken } from \"@vercel/oidc\";\nimport { NetworkPolicy } from \"../network-policy.js\";\nimport {\n toAPINetworkPolicy,\n fromAPINetworkPolicy,\n} from \"../utils/network-policy.js\";\nimport { getPrivateParams, WithPrivate } from \"../utils/types.js\";\nimport { RUNTIMES } from \"../constants.js\";\nimport { setTimeout } from \"node:timers/promises\";\n\ninterface Claims {\n owner_id: string;\n project_id?: string;\n}\n\nfunction decodeUnverifiedToken(token: string): Claims | null {\n if (token.split(\".\").length !== 3) {\n return null;\n }\n try {\n const payload = JSON.parse(\n Buffer.from(token.split(\".\")[1], \"base64url\").toString(\"utf8\"),\n );\n if (payload.owner_id) {\n return { owner_id: payload.owner_id, project_id: payload.project_id };\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport interface WithFetchOptions {\n fetch?: typeof globalThis.fetch;\n}\n\nexport class APIClient extends BaseClient {\n private teamId: string;\n private projectId: string | undefined;\n private isJwtToken: boolean;\n\n constructor(params: {\n baseUrl?: string;\n teamId: string;\n token: string;\n fetch?: typeof globalThis.fetch;\n }) {\n super({\n baseUrl: params.baseUrl ?? \"https://vercel.com/api\",\n token: params.token,\n debug: false,\n fetch: params.fetch,\n });\n\n this.teamId = params.teamId;\n this.isJwtToken = false;\n\n const claims = decodeUnverifiedToken(params.token);\n if (claims) {\n this.isJwtToken = true;\n this.projectId = claims.project_id;\n this.teamId = claims.owner_id;\n }\n }\n\n private async ensureValidToken(): Promise<void> {\n if (!this.isJwtToken) {\n return;\n }\n\n try {\n // Use getVercelOidcToken to refresh the token with team/project scope\n const freshToken = await getVercelOidcToken({\n expirationBufferMs: 5 * 60 * 1000, // 5 minutes\n team: this.teamId,\n project: this.projectId,\n });\n\n // Update token if it changed\n if (freshToken !== this.token) {\n this.token = freshToken;\n\n const claims = decodeUnverifiedToken(freshToken);\n if (claims) {\n this.teamId = claims.owner_id;\n }\n }\n } catch {\n // Ignore refresh errors and continue with current token\n }\n }\n\n protected async request(path: string, params?: RequestParams) {\n await this.ensureValidToken();\n\n return super.request(path, {\n ...params,\n query: { teamId: this.teamId, ...params?.query },\n headers: {\n \"content-type\": \"application/json\",\n \"user-agent\": `vercel/sandbox/${VERSION} (Node.js/${process.version}; ${os.platform()}/${os.arch()})`,\n ...params?.headers,\n },\n });\n }\n\n async getSession(\n params: WithPrivate<{ sessionId: string; signal?: AbortSignal }>,\n ) {\n const privateParams = getPrivateParams(params);\n let querystring = new URLSearchParams(privateParams).toString();\n querystring = querystring ? `?${querystring}` : \"\";\n return parseOrThrow(\n SessionAndRoutesResponse,\n await this.request(`/v2/sandboxes/sessions/${params.sessionId}${querystring}`, {\n signal: params.signal,\n }),\n );\n }\n\n async createSandbox(\n params: WithPrivate<{\n name?: string;\n ports?: number[];\n projectId: string;\n source?:\n | {\n type: \"git\";\n url: string;\n depth?: number;\n revision?: string;\n username?: string;\n password?: string;\n }\n | { type: \"tarball\"; url: string }\n | { type: \"snapshot\"; snapshotId: string };\n timeout?: number;\n resources?: { vcpus: number };\n persistent?: boolean;\n runtime?: RUNTIMES | (string & {});\n networkPolicy?: NetworkPolicy;\n env?: Record<string, string>;\n tags?: Record<string, string>;\n snapshotExpiration?: number;\n signal?: AbortSignal;\n }>,\n ) {\n const privateParams = getPrivateParams(params);\n return parseOrThrow(\n SandboxAndSessionResponse,\n await this.request(\"/v2/sandboxes\", {\n method: \"POST\",\n body: JSON.stringify({\n projectId: params.projectId,\n ports: params.ports,\n source: params.source,\n timeout: params.timeout,\n resources: params.resources,\n runtime: params.runtime,\n name: params.name,\n persistent: params.persistent,\n networkPolicy: params.networkPolicy\n ? toAPINetworkPolicy(params.networkPolicy)\n : undefined,\n env: params.env,\n tags: params.tags,\n snapshotExpiration: params.snapshotExpiration,\n ...privateParams,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async runCommand(params: {\n sessionId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait: true;\n signal?: AbortSignal;\n }): Promise<{ command: CommandData; finished: Promise<CommandFinishedData> }>;\n async runCommand(params: {\n sessionId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait?: false;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandResponse>>>;\n async runCommand(params: {\n sessionId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait?: boolean;\n signal?: AbortSignal;\n }) {\n if (params.wait) {\n const response = await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/cmd`,\n {\n method: \"POST\",\n body: JSON.stringify({\n command: params.command,\n args: params.args,\n cwd: params.cwd,\n env: params.env,\n sudo: params.sudo,\n wait: true,\n }),\n signal: params.signal,\n },\n );\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.headers.get(\"content-type\") !== \"application/x-ndjson\") {\n throw new APIError(response, {\n message: \"Expected a stream of command data\",\n sessionId: params.sessionId,\n });\n }\n\n if (response.body === null) {\n throw new APIError(response, {\n message: \"No response body\",\n sessionId: params.sessionId,\n });\n }\n\n const jsonlinesStream = jsonlines.parse();\n pipe(response.body, jsonlinesStream, { signal: params.signal }).catch(\n (err) => {\n console.error(\"Error piping command stream:\", err);\n },\n );\n\n const iterator = jsonlinesStream[Symbol.asyncIterator]();\n const commandChunk = await iterator.next();\n if (commandChunk.done) {\n throw new StreamError(\n \"stream_ended_early\",\n \"Stream ended before command data was received\",\n params.sessionId,\n );\n }\n const { command } = CommandResponse.parse(commandChunk.value);\n\n const finished = (async () => {\n const finishedChunk = await iterator.next();\n if (finishedChunk.done) {\n throw new StreamError(\n \"stream_ended_early\",\n \"Stream ended before command finished\",\n params.sessionId,\n );\n }\n const { command } = CommandFinishedResponse.parse(finishedChunk.value);\n return command;\n })();\n\n return { command, finished };\n }\n\n return parseOrThrow(\n CommandResponse,\n await this.request(`/v2/sandboxes/sessions/${params.sessionId}/cmd`, {\n method: \"POST\",\n body: JSON.stringify({\n command: params.command,\n args: params.args,\n cwd: params.cwd,\n env: params.env,\n sudo: params.sudo,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async getCommand(params: {\n sessionId: string;\n cmdId: string;\n wait: true;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandFinishedResponse>>>;\n async getCommand(params: {\n sessionId: string;\n cmdId: string;\n wait?: boolean;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandResponse>>>;\n async getCommand(params: {\n sessionId: string;\n cmdId: string;\n wait?: boolean;\n signal?: AbortSignal;\n }) {\n return params.wait\n ? parseOrThrow(\n CommandFinishedResponse,\n await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/cmd/${params.cmdId}`,\n { signal: params.signal, query: { wait: \"true\" } },\n ),\n )\n : parseOrThrow(\n CommandResponse,\n await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/cmd/${params.cmdId}`,\n { signal: params.signal },\n ),\n );\n }\n\n async mkDir(params: {\n sessionId: string;\n path: string;\n cwd?: string;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n EmptyResponse,\n await this.request(`/v2/sandboxes/sessions/${params.sessionId}/fs/mkdir`, {\n method: \"POST\",\n body: JSON.stringify({ path: params.path, cwd: params.cwd }),\n signal: params.signal,\n }),\n );\n }\n\n getFileWriter(params: {\n sessionId: string;\n extractDir: string;\n signal?: AbortSignal;\n }) {\n const writer = new FileWriter();\n return {\n response: (async () => {\n return this.request(`/v2/sandboxes/sessions/${params.sessionId}/fs/write`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/gzip\",\n \"x-cwd\": params.extractDir,\n },\n body: await consumeReadable(writer.readable),\n signal: params.signal,\n });\n })(),\n writer,\n };\n }\n\n async listSessions(params: {\n /**\n * The ID or name of the project to which the sessions belong.\n * @example \"my-project\"\n */\n projectId: string;\n /**\n * Filter sessions by sandbox name.\n */\n name?: string;\n /**\n * Maximum number of sessions to list from a request.\n * @example 10\n */\n limit?: number;\n /**\n * Cursor for pagination.\n */\n cursor?: string;\n /**\n * Sort order for results.\n */\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SessionsResponse,\n await this.request(`/v2/sandboxes/sessions`, {\n query: {\n project: params.projectId,\n name: params.name,\n limit: params.limit,\n cursor: params.cursor,\n sortOrder: params.sortOrder,\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async listSnapshots(params: {\n /**\n * The ID or name of the project to which the snapshots belong.\n * @example \"my-project\"\n */\n projectId: string;\n /**\n * Filter snapshots by sandbox name.\n */\n name?: string;\n /**\n * Maximum number of snapshots to list from a request.\n * @example 10\n */\n limit?: number;\n /**\n * Cursor for pagination.\n */\n cursor?: string;\n /**\n * Sort order for results.\n */\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SnapshotsResponse,\n await this.request(`/v2/sandboxes/snapshots`, {\n query: {\n project: params.projectId,\n name: params.name,\n limit: params.limit,\n cursor: params.cursor,\n sortOrder: params.sortOrder,\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async writeFiles(params: {\n sessionId: string;\n cwd: string;\n files: {\n path: string;\n content: string | Uint8Array;\n mode?: number;\n }[];\n extractDir: string;\n signal?: AbortSignal;\n }) {\n const { writer, response } = this.getFileWriter({\n sessionId: params.sessionId,\n extractDir: params.extractDir,\n signal: params.signal,\n });\n\n for (const file of params.files) {\n await writer.addFile({\n name: normalizePath({\n filePath: file.path,\n extractDir: params.extractDir,\n cwd: params.cwd,\n }),\n content: file.content,\n mode: file.mode,\n });\n }\n\n writer.end();\n await parseOrThrow(EmptyResponse, await response);\n }\n\n async readFile(params: {\n sessionId: string;\n path: string;\n cwd?: string;\n signal?: AbortSignal;\n }): Promise<Readable | null> {\n const response = await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/fs/read`,\n {\n method: \"POST\",\n body: JSON.stringify({ path: params.path, cwd: params.cwd }),\n signal: params.signal,\n },\n );\n\n if (response.status === 404) {\n return null;\n }\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.body === null) {\n return null;\n }\n\n return Readable.fromWeb(response.body);\n }\n\n async killCommand(params: {\n sessionId: string;\n commandId: string;\n signal: number;\n abortSignal?: AbortSignal;\n }) {\n return parseOrThrow(\n CommandResponse,\n await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/cmd/${params.commandId}/kill`,\n {\n method: \"POST\",\n body: JSON.stringify({ signal: params.signal }),\n signal: params.abortSignal,\n },\n ),\n );\n }\n\n getLogs(params: {\n sessionId: string;\n cmdId: string;\n signal?: AbortSignal;\n }): AsyncGenerator<\n z.infer<typeof LogLineStdout> | z.infer<typeof LogLineStderr>,\n void,\n void\n > &\n Disposable & { close(): void } {\n const self = this;\n const disposer = new AbortController();\n const signal = !params.signal\n ? disposer.signal\n : mergeSignals(params.signal, disposer.signal);\n\n const generator = (async function* () {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/cmd/${params.cmdId}/logs`;\n const response = await self.request(url, {\n method: \"GET\",\n signal,\n });\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.headers.get(\"content-type\") !== \"application/x-ndjson\") {\n throw new APIError(response, {\n message: \"Expected a stream of logs\",\n sessionId: params.sessionId,\n });\n }\n\n if (response.body === null) {\n throw new APIError(response, {\n message: \"No response body\",\n sessionId: params.sessionId,\n });\n }\n\n const jsonlinesStream = jsonlines.parse();\n pipe(response.body, jsonlinesStream, { signal }).catch((err) => {\n console.error(\"Error piping logs:\", err);\n });\n\n for await (const chunk of jsonlinesStream) {\n const parsed = LogLine.parse(chunk);\n if (parsed.stream === \"error\") {\n throw new StreamError(\n parsed.data.code,\n parsed.data.message,\n params.sessionId,\n );\n }\n yield parsed;\n }\n })();\n\n return Object.assign(generator, {\n [Symbol.dispose]() {\n disposer.abort(\"Disposed\");\n },\n close: () => disposer.abort(\"Disposed\"),\n });\n }\n\n async stopSession(params: {\n sessionId: string;\n signal?: AbortSignal;\n blocking?: boolean;\n }): Promise<Parsed<z.infer<typeof SessionResponse>>> {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/stop`;\n const response = await parseOrThrow(\n SessionResponse,\n await this.request(url, { method: \"POST\", signal: params.signal }),\n );\n\n if (params.blocking) {\n let session = response.json.session;\n while (\n session.status !== \"stopped\" &&\n session.status !== \"failed\" &&\n session.status !== \"aborted\"\n ) {\n await setTimeout(500, undefined, { signal: params.signal });\n const poll = await this.getSession({\n sessionId: params.sessionId,\n signal: params.signal,\n });\n session = poll.json.session;\n response.json.session = session;\n }\n }\n\n return response;\n }\n\n async updateNetworkPolicy(params: {\n sessionId: string;\n networkPolicy: NetworkPolicy;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SessionResponse>>> {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/network-policy`;\n return parseOrThrow(\n SessionResponse,\n await this.request(url, {\n method: \"POST\",\n body: JSON.stringify(toAPINetworkPolicy(params.networkPolicy)),\n signal: params.signal,\n }),\n );\n }\n\n async extendTimeout(params: {\n sessionId: string;\n duration: number;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SessionResponse>>> {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/extend-timeout`;\n return parseOrThrow(\n SessionResponse,\n await this.request(url, {\n method: \"POST\",\n body: JSON.stringify({ duration: params.duration }),\n signal: params.signal,\n }),\n );\n }\n\n async createSnapshot(params: {\n sessionId: string;\n expiration?: number;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CreateSnapshotResponse>>> {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/snapshot`;\n const body =\n params.expiration === undefined\n ? undefined\n : JSON.stringify({ expiration: params.expiration });\n return parseOrThrow(\n CreateSnapshotResponse,\n await this.request(url, {\n method: \"POST\",\n body,\n signal: params.signal,\n }),\n );\n }\n\n async deleteSnapshot(params: {\n snapshotId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SnapshotResponse>>> {\n const url = `/v2/sandboxes/snapshots/${params.snapshotId}`;\n return parseOrThrow(\n SnapshotResponse,\n await this.request(url, { method: \"DELETE\", signal: params.signal }),\n );\n }\n\n async getSnapshot(params: {\n snapshotId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SnapshotResponse>>> {\n const url = `/v2/sandboxes/snapshots/${params.snapshotId}`;\n return parseOrThrow(\n SnapshotResponse,\n await this.request(url, { signal: params.signal }),\n );\n }\n\n async getSandbox(params: WithPrivate<{\n name: string;\n projectId: string;\n resume?: boolean;\n signal?: AbortSignal;\n }>) {\n const privateParams = getPrivateParams(params);\n const query: Record<string, string | undefined> = {\n projectId: params.projectId,\n ...privateParams,\n };\n if (params.resume !== undefined) {\n query.resume = String(params.resume);\n }\n return parseOrThrow(\n SandboxAndSessionResponse,\n await this.request(`/v2/sandboxes/${encodeURIComponent(params.name)}`, {\n query,\n signal: params.signal,\n }),\n );\n }\n\n async listSandboxes(params: {\n projectId: string;\n limit?: number;\n sortBy?: \"createdAt\" | \"name\" | \"statusUpdatedAt\";\n sortOrder?: \"asc\" | \"desc\";\n namePrefix?: string;\n cursor?: string;\n tags?: Record<string, string>;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SandboxesPaginationResponse,\n await this.request(`/v2/sandboxes`, {\n query: {\n project: params.projectId,\n limit: params.limit,\n sortBy: params.sortBy,\n sortOrder: params.sortOrder,\n namePrefix: params.namePrefix,\n cursor: params.cursor,\n tags: toTagsFilter(params.tags),\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async updateSandbox(params: {\n name: string;\n projectId: string;\n persistent?: boolean;\n resources?: { vcpus?: number; memory?: number };\n runtime?: RUNTIMES | (string & {});\n timeout?: number;\n networkPolicy?: NetworkPolicy;\n tags?: Record<string, string>;\n snapshotExpiration?: number;\n currentSnapshotId?: string;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n UpdateSandboxResponse,\n await this.request(`/v2/sandboxes/${encodeURIComponent(params.name)}`, {\n method: \"PATCH\",\n query: {\n projectId: params.projectId,\n },\n body: JSON.stringify({\n persistent: params.persistent,\n resources: params.resources,\n runtime: params.runtime,\n timeout: params.timeout,\n networkPolicy: params.networkPolicy\n ? toAPINetworkPolicy(params.networkPolicy)\n : undefined,\n tags: params.tags,\n snapshotExpiration: params.snapshotExpiration,\n currentSnapshotId: params.currentSnapshotId,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async deleteSandbox(params: {\n name: string;\n projectId: string;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n UpdateSandboxResponse,\n await this.request(`/v2/sandboxes/${encodeURIComponent(params.name)}`, {\n method: \"DELETE\",\n query: {\n projectId: params.projectId,\n },\n signal: params.signal,\n }),\n );\n }\n}\n\nasync function pipe(\n readable: ReadableStream<Uint8Array>,\n output: NodeJS.WritableStream,\n options?: { signal?: AbortSignal },\n) {\n const reader = readable.getReader();\n let aborted = false;\n\n const signal = options?.signal;\n const onAbort = () => {\n aborted = true;\n const reason =\n signal?.reason ??\n new DOMException(\"The operation was aborted.\", \"AbortError\");\n void reader.cancel(reason).catch(() => {\n // ignore cancel errors when aborting\n });\n\n if (\"destroy\" in output && typeof output.destroy === \"function\") {\n output.destroy(reason as Error);\n return;\n }\n\n output.emit(\"error\", reason);\n output.end();\n };\n\n if (signal) {\n if (signal.aborted) {\n onAbort();\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n try {\n while (true) {\n const read = await reader.read();\n if (read.value) {\n output.write(Buffer.from(read.value));\n }\n if (read.done) {\n break;\n }\n }\n } catch (err) {\n if (!aborted) {\n output.emit(\"error\", err);\n }\n } finally {\n signal?.removeEventListener(\"abort\", onAbort);\n if (!aborted) {\n output.end();\n }\n }\n}\n\nfunction mergeSignals(...signals: [AbortSignal, ...AbortSignal[]]) {\n const controller = new AbortController();\n const onAbort = () => {\n controller.abort();\n for (const signal of signals) {\n signal.removeEventListener(\"abort\", onAbort);\n }\n };\n for (const signal of signals) {\n if (signal.aborted) {\n controller.abort();\n break;\n }\n signal.addEventListener(\"abort\", onAbort);\n }\n return controller.signal;\n}\n\nfunction toTagsFilter(\n tags: Record<string, string> | undefined,\n): string[] | undefined {\n if (tags === undefined) return undefined;\n const entries = Object.entries(tags);\n if (entries.length === 0) return undefined;\n return entries.map(([key, value]) => `${key}:${value}`);\n}\n"],"mappings":"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"}
{"version":3,"file":"api-client.cjs","names":["BaseClient","VERSION","getPrivateParams","parseOrThrow","SessionAndRoutesResponse","SandboxAndSessionResponse","toAPINetworkPolicy","z","APIError","StreamError","CommandResponse","CommandFinishedResponse","command","EmptyResponse","FileWriter","consumeReadable","SessionsResponse","SnapshotsResponse","normalizePath","Readable","LogLine","StopSessionResponse","SessionResponse","CreateSnapshotResponse","SnapshotResponse","query: Record<string, string | undefined>","SandboxesPaginationResponse","UpdateSandboxResponse"],"sources":["../../src/api-client/api-client.ts"],"sourcesContent":["import {\n BaseClient,\n parseOrThrow,\n type Parsed,\n type RequestParams,\n} from \"./base-client.js\";\nimport {\n type CommandFinishedData,\n SessionAndRoutesResponse,\n SessionResponse,\n StopSessionResponse,\n SessionsResponse,\n CommandResponse,\n CommandFinishedResponse,\n EmptyResponse,\n LogLine,\n type LogLineStdout,\n type LogLineStderr,\n SnapshotsResponse,\n SnapshotResponse,\n CreateSnapshotResponse,\n SandboxAndSessionResponse,\n SandboxesPaginationResponse,\n UpdateSandboxResponse,\n type CommandData,\n} from \"./validators.js\";\nimport { APIError, StreamError } from \"./api-error.js\";\nimport { FileWriter } from \"./file-writer.js\";\nimport { VERSION } from \"../version.js\";\nimport { consumeReadable } from \"../utils/consume-readable.js\";\nimport { z } from \"zod\";\nimport jsonlines from \"jsonlines\";\nimport os from \"os\";\nimport { Readable } from \"stream\";\nimport { normalizePath } from \"../utils/normalizePath.js\";\nimport { getVercelOidcToken } from \"@vercel/oidc\";\nimport { NetworkPolicy } from \"../network-policy.js\";\nimport {\n toAPINetworkPolicy,\n fromAPINetworkPolicy,\n} from \"../utils/network-policy.js\";\nimport { getPrivateParams, WithPrivate } from \"../utils/types.js\";\nimport { RUNTIMES } from \"../constants.js\";\n\ninterface Claims {\n owner_id: string;\n project_id?: string;\n}\n\nfunction decodeUnverifiedToken(token: string): Claims | null {\n if (token.split(\".\").length !== 3) {\n return null;\n }\n try {\n const payload = JSON.parse(\n Buffer.from(token.split(\".\")[1], \"base64url\").toString(\"utf8\"),\n );\n if (payload.owner_id) {\n return { owner_id: payload.owner_id, project_id: payload.project_id };\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport interface WithFetchOptions {\n fetch?: typeof globalThis.fetch;\n}\n\nexport class APIClient extends BaseClient {\n private teamId: string;\n private projectId: string | undefined;\n private isJwtToken: boolean;\n\n constructor(params: {\n baseUrl?: string;\n teamId: string;\n token: string;\n fetch?: typeof globalThis.fetch;\n }) {\n super({\n baseUrl: params.baseUrl ?? \"https://vercel.com/api\",\n token: params.token,\n debug: false,\n fetch: params.fetch,\n });\n\n this.teamId = params.teamId;\n this.isJwtToken = false;\n\n const claims = decodeUnverifiedToken(params.token);\n if (claims) {\n this.isJwtToken = true;\n this.projectId = claims.project_id;\n this.teamId = claims.owner_id;\n }\n }\n\n private async ensureValidToken(): Promise<void> {\n if (!this.isJwtToken) {\n return;\n }\n\n try {\n // Use getVercelOidcToken to refresh the token with team/project scope\n const freshToken = await getVercelOidcToken({\n expirationBufferMs: 5 * 60 * 1000, // 5 minutes\n team: this.teamId,\n project: this.projectId,\n });\n\n // Update token if it changed\n if (freshToken !== this.token) {\n this.token = freshToken;\n\n const claims = decodeUnverifiedToken(freshToken);\n if (claims) {\n this.teamId = claims.owner_id;\n }\n }\n } catch {\n // Ignore refresh errors and continue with current token\n }\n }\n\n protected async request(path: string, params?: RequestParams) {\n await this.ensureValidToken();\n\n return super.request(path, {\n ...params,\n query: { teamId: this.teamId, ...params?.query },\n headers: {\n \"content-type\": \"application/json\",\n \"user-agent\": `vercel/sandbox/${VERSION} (Node.js/${process.version}; ${os.platform()}/${os.arch()})`,\n ...params?.headers,\n },\n });\n }\n\n async getSession(\n params: WithPrivate<{ sessionId: string; signal?: AbortSignal }>,\n ) {\n const privateParams = getPrivateParams(params);\n let querystring = new URLSearchParams(privateParams).toString();\n querystring = querystring ? `?${querystring}` : \"\";\n return parseOrThrow(\n SessionAndRoutesResponse,\n await this.request(`/v2/sandboxes/sessions/${params.sessionId}${querystring}`, {\n signal: params.signal,\n }),\n );\n }\n\n async createSandbox(\n params: WithPrivate<{\n name?: string;\n ports?: number[];\n projectId: string;\n source?:\n | {\n type: \"git\";\n url: string;\n depth?: number;\n revision?: string;\n username?: string;\n password?: string;\n }\n | { type: \"tarball\"; url: string }\n | { type: \"snapshot\"; snapshotId: string };\n timeout?: number;\n resources?: { vcpus: number };\n persistent?: boolean;\n runtime?: RUNTIMES | (string & {});\n networkPolicy?: NetworkPolicy;\n env?: Record<string, string>;\n tags?: Record<string, string>;\n snapshotExpiration?: number;\n signal?: AbortSignal;\n }>,\n ) {\n const privateParams = getPrivateParams(params);\n return parseOrThrow(\n SandboxAndSessionResponse,\n await this.request(\"/v2/sandboxes\", {\n method: \"POST\",\n body: JSON.stringify({\n projectId: params.projectId,\n ports: params.ports,\n source: params.source,\n timeout: params.timeout,\n resources: params.resources,\n runtime: params.runtime,\n name: params.name,\n persistent: params.persistent,\n networkPolicy: params.networkPolicy\n ? toAPINetworkPolicy(params.networkPolicy)\n : undefined,\n env: params.env,\n tags: params.tags,\n snapshotExpiration: params.snapshotExpiration,\n ...privateParams,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async runCommand(params: {\n sessionId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait: true;\n signal?: AbortSignal;\n }): Promise<{ command: CommandData; finished: Promise<CommandFinishedData> }>;\n async runCommand(params: {\n sessionId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait?: false;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandResponse>>>;\n async runCommand(params: {\n sessionId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait?: boolean;\n signal?: AbortSignal;\n }) {\n if (params.wait) {\n const response = await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/cmd`,\n {\n method: \"POST\",\n body: JSON.stringify({\n command: params.command,\n args: params.args,\n cwd: params.cwd,\n env: params.env,\n sudo: params.sudo,\n wait: true,\n }),\n signal: params.signal,\n },\n );\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.headers.get(\"content-type\") !== \"application/x-ndjson\") {\n throw new APIError(response, {\n message: \"Expected a stream of command data\",\n sessionId: params.sessionId,\n });\n }\n\n if (response.body === null) {\n throw new APIError(response, {\n message: \"No response body\",\n sessionId: params.sessionId,\n });\n }\n\n const jsonlinesStream = jsonlines.parse();\n pipe(response.body, jsonlinesStream, { signal: params.signal }).catch(\n (err) => {\n console.error(\"Error piping command stream:\", err);\n },\n );\n\n const iterator = jsonlinesStream[Symbol.asyncIterator]();\n const commandChunk = await iterator.next();\n if (commandChunk.done) {\n throw new StreamError(\n \"stream_ended_early\",\n \"Stream ended before command data was received\",\n params.sessionId,\n );\n }\n const { command } = CommandResponse.parse(commandChunk.value);\n\n const finished = (async () => {\n const finishedChunk = await iterator.next();\n if (finishedChunk.done) {\n throw new StreamError(\n \"stream_ended_early\",\n \"Stream ended before command finished\",\n params.sessionId,\n );\n }\n const { command } = CommandFinishedResponse.parse(finishedChunk.value);\n return command;\n })();\n\n return { command, finished };\n }\n\n return parseOrThrow(\n CommandResponse,\n await this.request(`/v2/sandboxes/sessions/${params.sessionId}/cmd`, {\n method: \"POST\",\n body: JSON.stringify({\n command: params.command,\n args: params.args,\n cwd: params.cwd,\n env: params.env,\n sudo: params.sudo,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async getCommand(params: {\n sessionId: string;\n cmdId: string;\n wait: true;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandFinishedResponse>>>;\n async getCommand(params: {\n sessionId: string;\n cmdId: string;\n wait?: boolean;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandResponse>>>;\n async getCommand(params: {\n sessionId: string;\n cmdId: string;\n wait?: boolean;\n signal?: AbortSignal;\n }) {\n return params.wait\n ? parseOrThrow(\n CommandFinishedResponse,\n await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/cmd/${params.cmdId}`,\n { signal: params.signal, query: { wait: \"true\" } },\n ),\n )\n : parseOrThrow(\n CommandResponse,\n await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/cmd/${params.cmdId}`,\n { signal: params.signal },\n ),\n );\n }\n\n async mkDir(params: {\n sessionId: string;\n path: string;\n cwd?: string;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n EmptyResponse,\n await this.request(`/v2/sandboxes/sessions/${params.sessionId}/fs/mkdir`, {\n method: \"POST\",\n body: JSON.stringify({ path: params.path, cwd: params.cwd }),\n signal: params.signal,\n }),\n );\n }\n\n getFileWriter(params: {\n sessionId: string;\n extractDir: string;\n signal?: AbortSignal;\n }) {\n const writer = new FileWriter();\n return {\n response: (async () => {\n return this.request(`/v2/sandboxes/sessions/${params.sessionId}/fs/write`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/gzip\",\n \"x-cwd\": params.extractDir,\n },\n body: await consumeReadable(writer.readable),\n signal: params.signal,\n });\n })(),\n writer,\n };\n }\n\n async listSessions(params: {\n /**\n * The ID or name of the project to which the sessions belong.\n * @example \"my-project\"\n */\n projectId: string;\n /**\n * Filter sessions by sandbox name.\n */\n name?: string;\n /**\n * Maximum number of sessions to list from a request.\n * @example 10\n */\n limit?: number;\n /**\n * Cursor for pagination.\n */\n cursor?: string;\n /**\n * Sort order for results.\n */\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SessionsResponse,\n await this.request(`/v2/sandboxes/sessions`, {\n query: {\n project: params.projectId,\n name: params.name,\n limit: params.limit,\n cursor: params.cursor,\n sortOrder: params.sortOrder,\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async listSnapshots(params: {\n /**\n * The ID or name of the project to which the snapshots belong.\n * @example \"my-project\"\n */\n projectId: string;\n /**\n * Filter snapshots by sandbox name.\n */\n name?: string;\n /**\n * Maximum number of snapshots to list from a request.\n * @example 10\n */\n limit?: number;\n /**\n * Cursor for pagination.\n */\n cursor?: string;\n /**\n * Sort order for results.\n */\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SnapshotsResponse,\n await this.request(`/v2/sandboxes/snapshots`, {\n query: {\n project: params.projectId,\n name: params.name,\n limit: params.limit,\n cursor: params.cursor,\n sortOrder: params.sortOrder,\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async writeFiles(params: {\n sessionId: string;\n cwd: string;\n files: {\n path: string;\n content: string | Uint8Array;\n mode?: number;\n }[];\n extractDir: string;\n signal?: AbortSignal;\n }) {\n const { writer, response } = this.getFileWriter({\n sessionId: params.sessionId,\n extractDir: params.extractDir,\n signal: params.signal,\n });\n\n for (const file of params.files) {\n await writer.addFile({\n name: normalizePath({\n filePath: file.path,\n extractDir: params.extractDir,\n cwd: params.cwd,\n }),\n content: file.content,\n mode: file.mode,\n });\n }\n\n writer.end();\n await parseOrThrow(EmptyResponse, await response);\n }\n\n async readFile(params: {\n sessionId: string;\n path: string;\n cwd?: string;\n signal?: AbortSignal;\n }): Promise<Readable | null> {\n const response = await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/fs/read`,\n {\n method: \"POST\",\n body: JSON.stringify({ path: params.path, cwd: params.cwd }),\n signal: params.signal,\n },\n );\n\n if (response.status === 404) {\n return null;\n }\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.body === null) {\n return null;\n }\n\n return Readable.fromWeb(response.body);\n }\n\n async killCommand(params: {\n sessionId: string;\n commandId: string;\n signal: number;\n abortSignal?: AbortSignal;\n }) {\n return parseOrThrow(\n CommandResponse,\n await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/cmd/${params.commandId}/kill`,\n {\n method: \"POST\",\n body: JSON.stringify({ signal: params.signal }),\n signal: params.abortSignal,\n },\n ),\n );\n }\n\n getLogs(params: {\n sessionId: string;\n cmdId: string;\n signal?: AbortSignal;\n }): AsyncGenerator<\n z.infer<typeof LogLineStdout> | z.infer<typeof LogLineStderr>,\n void,\n void\n > &\n Disposable & { close(): void } {\n const self = this;\n const disposer = new AbortController();\n const signal = !params.signal\n ? disposer.signal\n : mergeSignals(params.signal, disposer.signal);\n\n const generator = (async function* () {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/cmd/${params.cmdId}/logs`;\n const response = await self.request(url, {\n method: \"GET\",\n signal,\n });\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.headers.get(\"content-type\") !== \"application/x-ndjson\") {\n throw new APIError(response, {\n message: \"Expected a stream of logs\",\n sessionId: params.sessionId,\n });\n }\n\n if (response.body === null) {\n throw new APIError(response, {\n message: \"No response body\",\n sessionId: params.sessionId,\n });\n }\n\n const jsonlinesStream = jsonlines.parse();\n pipe(response.body, jsonlinesStream, { signal }).catch((err) => {\n console.error(\"Error piping logs:\", err);\n });\n\n for await (const chunk of jsonlinesStream) {\n const parsed = LogLine.parse(chunk);\n if (parsed.stream === \"error\") {\n throw new StreamError(\n parsed.data.code,\n parsed.data.message,\n params.sessionId,\n );\n }\n yield parsed;\n }\n })();\n\n return Object.assign(generator, {\n [Symbol.dispose]() {\n disposer.abort(\"Disposed\");\n },\n close: () => disposer.abort(\"Disposed\"),\n });\n }\n\n async stopSession(params: {\n sessionId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof StopSessionResponse>>> {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/stop`;\n return parseOrThrow(\n StopSessionResponse,\n await this.request(url, { method: \"POST\", signal: params.signal }),\n );\n }\n\n async updateNetworkPolicy(params: {\n sessionId: string;\n networkPolicy: NetworkPolicy;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SessionResponse>>> {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/network-policy`;\n return parseOrThrow(\n SessionResponse,\n await this.request(url, {\n method: \"POST\",\n body: JSON.stringify(toAPINetworkPolicy(params.networkPolicy)),\n signal: params.signal,\n }),\n );\n }\n\n async extendTimeout(params: {\n sessionId: string;\n duration: number;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SessionResponse>>> {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/extend-timeout`;\n return parseOrThrow(\n SessionResponse,\n await this.request(url, {\n method: \"POST\",\n body: JSON.stringify({ duration: params.duration }),\n signal: params.signal,\n }),\n );\n }\n\n async createSnapshot(params: {\n sessionId: string;\n expiration?: number;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CreateSnapshotResponse>>> {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/snapshot`;\n const body =\n params.expiration === undefined\n ? undefined\n : JSON.stringify({ expiration: params.expiration });\n return parseOrThrow(\n CreateSnapshotResponse,\n await this.request(url, {\n method: \"POST\",\n body,\n signal: params.signal,\n }),\n );\n }\n\n async deleteSnapshot(params: {\n snapshotId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SnapshotResponse>>> {\n const url = `/v2/sandboxes/snapshots/${params.snapshotId}`;\n return parseOrThrow(\n SnapshotResponse,\n await this.request(url, { method: \"DELETE\", signal: params.signal }),\n );\n }\n\n async getSnapshot(params: {\n snapshotId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SnapshotResponse>>> {\n const url = `/v2/sandboxes/snapshots/${params.snapshotId}`;\n return parseOrThrow(\n SnapshotResponse,\n await this.request(url, { signal: params.signal }),\n );\n }\n\n async getSandbox(params: WithPrivate<{\n name: string;\n projectId: string;\n resume?: boolean;\n signal?: AbortSignal;\n }>) {\n const privateParams = getPrivateParams(params);\n const query: Record<string, string | undefined> = {\n projectId: params.projectId,\n ...privateParams,\n };\n if (params.resume !== undefined) {\n query.resume = String(params.resume);\n }\n return parseOrThrow(\n SandboxAndSessionResponse,\n await this.request(`/v2/sandboxes/${encodeURIComponent(params.name)}`, {\n query,\n signal: params.signal,\n }),\n );\n }\n\n async listSandboxes(params: {\n projectId: string;\n limit?: number;\n sortBy?: \"createdAt\" | \"name\" | \"statusUpdatedAt\";\n sortOrder?: \"asc\" | \"desc\";\n namePrefix?: string;\n cursor?: string;\n tags?: Record<string, string>;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SandboxesPaginationResponse,\n await this.request(`/v2/sandboxes`, {\n query: {\n project: params.projectId,\n limit: params.limit,\n sortBy: params.sortBy,\n sortOrder: params.sortOrder,\n namePrefix: params.namePrefix,\n cursor: params.cursor,\n tags: toTagsFilter(params.tags),\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async updateSandbox(params: {\n name: string;\n projectId: string;\n persistent?: boolean;\n resources?: { vcpus?: number; memory?: number };\n runtime?: RUNTIMES | (string & {});\n timeout?: number;\n networkPolicy?: NetworkPolicy;\n tags?: Record<string, string>;\n snapshotExpiration?: number;\n currentSnapshotId?: string;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n UpdateSandboxResponse,\n await this.request(`/v2/sandboxes/${encodeURIComponent(params.name)}`, {\n method: \"PATCH\",\n query: {\n projectId: params.projectId,\n },\n body: JSON.stringify({\n persistent: params.persistent,\n resources: params.resources,\n runtime: params.runtime,\n timeout: params.timeout,\n networkPolicy: params.networkPolicy\n ? toAPINetworkPolicy(params.networkPolicy)\n : undefined,\n tags: params.tags,\n snapshotExpiration: params.snapshotExpiration,\n currentSnapshotId: params.currentSnapshotId,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async deleteSandbox(params: {\n name: string;\n projectId: string;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n UpdateSandboxResponse,\n await this.request(`/v2/sandboxes/${encodeURIComponent(params.name)}`, {\n method: \"DELETE\",\n query: {\n projectId: params.projectId,\n },\n signal: params.signal,\n }),\n );\n }\n}\n\nasync function pipe(\n readable: ReadableStream<Uint8Array>,\n output: NodeJS.WritableStream,\n options?: { signal?: AbortSignal },\n) {\n const reader = readable.getReader();\n let aborted = false;\n\n const signal = options?.signal;\n const onAbort = () => {\n aborted = true;\n const reason =\n signal?.reason ??\n new DOMException(\"The operation was aborted.\", \"AbortError\");\n void reader.cancel(reason).catch(() => {\n // ignore cancel errors when aborting\n });\n\n if (\"destroy\" in output && typeof output.destroy === \"function\") {\n output.destroy(reason as Error);\n return;\n }\n\n output.emit(\"error\", reason);\n output.end();\n };\n\n if (signal) {\n if (signal.aborted) {\n onAbort();\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n try {\n while (true) {\n const read = await reader.read();\n if (read.value) {\n output.write(Buffer.from(read.value));\n }\n if (read.done) {\n break;\n }\n }\n } catch (err) {\n if (!aborted) {\n output.emit(\"error\", err);\n }\n } finally {\n signal?.removeEventListener(\"abort\", onAbort);\n if (!aborted) {\n output.end();\n }\n }\n}\n\nfunction mergeSignals(...signals: [AbortSignal, ...AbortSignal[]]) {\n const controller = new AbortController();\n const onAbort = () => {\n controller.abort();\n for (const signal of signals) {\n signal.removeEventListener(\"abort\", onAbort);\n }\n };\n for (const signal of signals) {\n if (signal.aborted) {\n controller.abort();\n break;\n }\n signal.addEventListener(\"abort\", onAbort);\n }\n return controller.signal;\n}\n\nfunction toTagsFilter(\n tags: Record<string, string> | undefined,\n): string[] | undefined {\n if (tags === undefined) return undefined;\n const entries = Object.entries(tags);\n if (entries.length === 0) return undefined;\n return entries.map(([key, value]) => `${key}:${value}`);\n}\n"],"mappings":"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"}
import { APIError, StreamError } from "./api-error.js";
import { BaseClient, parseOrThrow } from "./base-client.js";
import { CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, EmptyResponse, LogLine, SandboxAndSessionResponse, SandboxesPaginationResponse, SessionAndRoutesResponse, SessionResponse, SessionsResponse, SnapshotResponse, SnapshotsResponse, UpdateSandboxResponse } from "./validators.js";
import { CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, EmptyResponse, LogLine, SandboxAndSessionResponse, SandboxesPaginationResponse, SessionAndRoutesResponse, SessionResponse, SessionsResponse, SnapshotResponse, SnapshotsResponse, StopSessionResponse, UpdateSandboxResponse } from "./validators.js";
import { FileWriter } from "./file-writer.js";

@@ -10,3 +10,2 @@ import { VERSION } from "../version.js";

import { getPrivateParams } from "../utils/types.js";
import { setTimeout } from "node:timers/promises";
import { z } from "zod";

@@ -296,18 +295,6 @@ import { Readable } from "stream";

const url = `/v2/sandboxes/sessions/${params.sessionId}/stop`;
const response = await parseOrThrow(SessionResponse, await this.request(url, {
return parseOrThrow(StopSessionResponse, await this.request(url, {
method: "POST",
signal: params.signal
}));
if (params.blocking) {
let session = response.json.session;
while (session.status !== "stopped" && session.status !== "failed" && session.status !== "aborted") {
await setTimeout(500, void 0, { signal: params.signal });
session = (await this.getSession({
sessionId: params.sessionId,
signal: params.signal
})).json.session;
response.json.session = session;
}
}
return response;
}

@@ -314,0 +301,0 @@ async updateNetworkPolicy(params) {

@@ -1,1 +0,1 @@

{"version":3,"file":"api-client.js","names":["command","query: Record<string, string | undefined>"],"sources":["../../src/api-client/api-client.ts"],"sourcesContent":["import {\n BaseClient,\n parseOrThrow,\n type Parsed,\n type RequestParams,\n} from \"./base-client.js\";\nimport {\n type CommandFinishedData,\n SessionAndRoutesResponse,\n SessionResponse,\n SessionsResponse,\n CommandResponse,\n CommandFinishedResponse,\n EmptyResponse,\n LogLine,\n type LogLineStdout,\n type LogLineStderr,\n SnapshotsResponse,\n SnapshotResponse,\n CreateSnapshotResponse,\n SandboxAndSessionResponse,\n SandboxesPaginationResponse,\n UpdateSandboxResponse,\n type CommandData,\n} from \"./validators.js\";\nimport { APIError, StreamError } from \"./api-error.js\";\nimport { FileWriter } from \"./file-writer.js\";\nimport { VERSION } from \"../version.js\";\nimport { consumeReadable } from \"../utils/consume-readable.js\";\nimport { z } from \"zod\";\nimport jsonlines from \"jsonlines\";\nimport os from \"os\";\nimport { Readable } from \"stream\";\nimport { normalizePath } from \"../utils/normalizePath.js\";\nimport { getVercelOidcToken } from \"@vercel/oidc\";\nimport { NetworkPolicy } from \"../network-policy.js\";\nimport {\n toAPINetworkPolicy,\n fromAPINetworkPolicy,\n} from \"../utils/network-policy.js\";\nimport { getPrivateParams, WithPrivate } from \"../utils/types.js\";\nimport { RUNTIMES } from \"../constants.js\";\nimport { setTimeout } from \"node:timers/promises\";\n\ninterface Claims {\n owner_id: string;\n project_id?: string;\n}\n\nfunction decodeUnverifiedToken(token: string): Claims | null {\n if (token.split(\".\").length !== 3) {\n return null;\n }\n try {\n const payload = JSON.parse(\n Buffer.from(token.split(\".\")[1], \"base64url\").toString(\"utf8\"),\n );\n if (payload.owner_id) {\n return { owner_id: payload.owner_id, project_id: payload.project_id };\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport interface WithFetchOptions {\n fetch?: typeof globalThis.fetch;\n}\n\nexport class APIClient extends BaseClient {\n private teamId: string;\n private projectId: string | undefined;\n private isJwtToken: boolean;\n\n constructor(params: {\n baseUrl?: string;\n teamId: string;\n token: string;\n fetch?: typeof globalThis.fetch;\n }) {\n super({\n baseUrl: params.baseUrl ?? \"https://vercel.com/api\",\n token: params.token,\n debug: false,\n fetch: params.fetch,\n });\n\n this.teamId = params.teamId;\n this.isJwtToken = false;\n\n const claims = decodeUnverifiedToken(params.token);\n if (claims) {\n this.isJwtToken = true;\n this.projectId = claims.project_id;\n this.teamId = claims.owner_id;\n }\n }\n\n private async ensureValidToken(): Promise<void> {\n if (!this.isJwtToken) {\n return;\n }\n\n try {\n // Use getVercelOidcToken to refresh the token with team/project scope\n const freshToken = await getVercelOidcToken({\n expirationBufferMs: 5 * 60 * 1000, // 5 minutes\n team: this.teamId,\n project: this.projectId,\n });\n\n // Update token if it changed\n if (freshToken !== this.token) {\n this.token = freshToken;\n\n const claims = decodeUnverifiedToken(freshToken);\n if (claims) {\n this.teamId = claims.owner_id;\n }\n }\n } catch {\n // Ignore refresh errors and continue with current token\n }\n }\n\n protected async request(path: string, params?: RequestParams) {\n await this.ensureValidToken();\n\n return super.request(path, {\n ...params,\n query: { teamId: this.teamId, ...params?.query },\n headers: {\n \"content-type\": \"application/json\",\n \"user-agent\": `vercel/sandbox/${VERSION} (Node.js/${process.version}; ${os.platform()}/${os.arch()})`,\n ...params?.headers,\n },\n });\n }\n\n async getSession(\n params: WithPrivate<{ sessionId: string; signal?: AbortSignal }>,\n ) {\n const privateParams = getPrivateParams(params);\n let querystring = new URLSearchParams(privateParams).toString();\n querystring = querystring ? `?${querystring}` : \"\";\n return parseOrThrow(\n SessionAndRoutesResponse,\n await this.request(`/v2/sandboxes/sessions/${params.sessionId}${querystring}`, {\n signal: params.signal,\n }),\n );\n }\n\n async createSandbox(\n params: WithPrivate<{\n name?: string;\n ports?: number[];\n projectId: string;\n source?:\n | {\n type: \"git\";\n url: string;\n depth?: number;\n revision?: string;\n username?: string;\n password?: string;\n }\n | { type: \"tarball\"; url: string }\n | { type: \"snapshot\"; snapshotId: string };\n timeout?: number;\n resources?: { vcpus: number };\n persistent?: boolean;\n runtime?: RUNTIMES | (string & {});\n networkPolicy?: NetworkPolicy;\n env?: Record<string, string>;\n tags?: Record<string, string>;\n snapshotExpiration?: number;\n signal?: AbortSignal;\n }>,\n ) {\n const privateParams = getPrivateParams(params);\n return parseOrThrow(\n SandboxAndSessionResponse,\n await this.request(\"/v2/sandboxes\", {\n method: \"POST\",\n body: JSON.stringify({\n projectId: params.projectId,\n ports: params.ports,\n source: params.source,\n timeout: params.timeout,\n resources: params.resources,\n runtime: params.runtime,\n name: params.name,\n persistent: params.persistent,\n networkPolicy: params.networkPolicy\n ? toAPINetworkPolicy(params.networkPolicy)\n : undefined,\n env: params.env,\n tags: params.tags,\n snapshotExpiration: params.snapshotExpiration,\n ...privateParams,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async runCommand(params: {\n sessionId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait: true;\n signal?: AbortSignal;\n }): Promise<{ command: CommandData; finished: Promise<CommandFinishedData> }>;\n async runCommand(params: {\n sessionId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait?: false;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandResponse>>>;\n async runCommand(params: {\n sessionId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait?: boolean;\n signal?: AbortSignal;\n }) {\n if (params.wait) {\n const response = await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/cmd`,\n {\n method: \"POST\",\n body: JSON.stringify({\n command: params.command,\n args: params.args,\n cwd: params.cwd,\n env: params.env,\n sudo: params.sudo,\n wait: true,\n }),\n signal: params.signal,\n },\n );\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.headers.get(\"content-type\") !== \"application/x-ndjson\") {\n throw new APIError(response, {\n message: \"Expected a stream of command data\",\n sessionId: params.sessionId,\n });\n }\n\n if (response.body === null) {\n throw new APIError(response, {\n message: \"No response body\",\n sessionId: params.sessionId,\n });\n }\n\n const jsonlinesStream = jsonlines.parse();\n pipe(response.body, jsonlinesStream, { signal: params.signal }).catch(\n (err) => {\n console.error(\"Error piping command stream:\", err);\n },\n );\n\n const iterator = jsonlinesStream[Symbol.asyncIterator]();\n const commandChunk = await iterator.next();\n if (commandChunk.done) {\n throw new StreamError(\n \"stream_ended_early\",\n \"Stream ended before command data was received\",\n params.sessionId,\n );\n }\n const { command } = CommandResponse.parse(commandChunk.value);\n\n const finished = (async () => {\n const finishedChunk = await iterator.next();\n if (finishedChunk.done) {\n throw new StreamError(\n \"stream_ended_early\",\n \"Stream ended before command finished\",\n params.sessionId,\n );\n }\n const { command } = CommandFinishedResponse.parse(finishedChunk.value);\n return command;\n })();\n\n return { command, finished };\n }\n\n return parseOrThrow(\n CommandResponse,\n await this.request(`/v2/sandboxes/sessions/${params.sessionId}/cmd`, {\n method: \"POST\",\n body: JSON.stringify({\n command: params.command,\n args: params.args,\n cwd: params.cwd,\n env: params.env,\n sudo: params.sudo,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async getCommand(params: {\n sessionId: string;\n cmdId: string;\n wait: true;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandFinishedResponse>>>;\n async getCommand(params: {\n sessionId: string;\n cmdId: string;\n wait?: boolean;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandResponse>>>;\n async getCommand(params: {\n sessionId: string;\n cmdId: string;\n wait?: boolean;\n signal?: AbortSignal;\n }) {\n return params.wait\n ? parseOrThrow(\n CommandFinishedResponse,\n await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/cmd/${params.cmdId}`,\n { signal: params.signal, query: { wait: \"true\" } },\n ),\n )\n : parseOrThrow(\n CommandResponse,\n await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/cmd/${params.cmdId}`,\n { signal: params.signal },\n ),\n );\n }\n\n async mkDir(params: {\n sessionId: string;\n path: string;\n cwd?: string;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n EmptyResponse,\n await this.request(`/v2/sandboxes/sessions/${params.sessionId}/fs/mkdir`, {\n method: \"POST\",\n body: JSON.stringify({ path: params.path, cwd: params.cwd }),\n signal: params.signal,\n }),\n );\n }\n\n getFileWriter(params: {\n sessionId: string;\n extractDir: string;\n signal?: AbortSignal;\n }) {\n const writer = new FileWriter();\n return {\n response: (async () => {\n return this.request(`/v2/sandboxes/sessions/${params.sessionId}/fs/write`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/gzip\",\n \"x-cwd\": params.extractDir,\n },\n body: await consumeReadable(writer.readable),\n signal: params.signal,\n });\n })(),\n writer,\n };\n }\n\n async listSessions(params: {\n /**\n * The ID or name of the project to which the sessions belong.\n * @example \"my-project\"\n */\n projectId: string;\n /**\n * Filter sessions by sandbox name.\n */\n name?: string;\n /**\n * Maximum number of sessions to list from a request.\n * @example 10\n */\n limit?: number;\n /**\n * Cursor for pagination.\n */\n cursor?: string;\n /**\n * Sort order for results.\n */\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SessionsResponse,\n await this.request(`/v2/sandboxes/sessions`, {\n query: {\n project: params.projectId,\n name: params.name,\n limit: params.limit,\n cursor: params.cursor,\n sortOrder: params.sortOrder,\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async listSnapshots(params: {\n /**\n * The ID or name of the project to which the snapshots belong.\n * @example \"my-project\"\n */\n projectId: string;\n /**\n * Filter snapshots by sandbox name.\n */\n name?: string;\n /**\n * Maximum number of snapshots to list from a request.\n * @example 10\n */\n limit?: number;\n /**\n * Cursor for pagination.\n */\n cursor?: string;\n /**\n * Sort order for results.\n */\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SnapshotsResponse,\n await this.request(`/v2/sandboxes/snapshots`, {\n query: {\n project: params.projectId,\n name: params.name,\n limit: params.limit,\n cursor: params.cursor,\n sortOrder: params.sortOrder,\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async writeFiles(params: {\n sessionId: string;\n cwd: string;\n files: {\n path: string;\n content: string | Uint8Array;\n mode?: number;\n }[];\n extractDir: string;\n signal?: AbortSignal;\n }) {\n const { writer, response } = this.getFileWriter({\n sessionId: params.sessionId,\n extractDir: params.extractDir,\n signal: params.signal,\n });\n\n for (const file of params.files) {\n await writer.addFile({\n name: normalizePath({\n filePath: file.path,\n extractDir: params.extractDir,\n cwd: params.cwd,\n }),\n content: file.content,\n mode: file.mode,\n });\n }\n\n writer.end();\n await parseOrThrow(EmptyResponse, await response);\n }\n\n async readFile(params: {\n sessionId: string;\n path: string;\n cwd?: string;\n signal?: AbortSignal;\n }): Promise<Readable | null> {\n const response = await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/fs/read`,\n {\n method: \"POST\",\n body: JSON.stringify({ path: params.path, cwd: params.cwd }),\n signal: params.signal,\n },\n );\n\n if (response.status === 404) {\n return null;\n }\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.body === null) {\n return null;\n }\n\n return Readable.fromWeb(response.body);\n }\n\n async killCommand(params: {\n sessionId: string;\n commandId: string;\n signal: number;\n abortSignal?: AbortSignal;\n }) {\n return parseOrThrow(\n CommandResponse,\n await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/cmd/${params.commandId}/kill`,\n {\n method: \"POST\",\n body: JSON.stringify({ signal: params.signal }),\n signal: params.abortSignal,\n },\n ),\n );\n }\n\n getLogs(params: {\n sessionId: string;\n cmdId: string;\n signal?: AbortSignal;\n }): AsyncGenerator<\n z.infer<typeof LogLineStdout> | z.infer<typeof LogLineStderr>,\n void,\n void\n > &\n Disposable & { close(): void } {\n const self = this;\n const disposer = new AbortController();\n const signal = !params.signal\n ? disposer.signal\n : mergeSignals(params.signal, disposer.signal);\n\n const generator = (async function* () {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/cmd/${params.cmdId}/logs`;\n const response = await self.request(url, {\n method: \"GET\",\n signal,\n });\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.headers.get(\"content-type\") !== \"application/x-ndjson\") {\n throw new APIError(response, {\n message: \"Expected a stream of logs\",\n sessionId: params.sessionId,\n });\n }\n\n if (response.body === null) {\n throw new APIError(response, {\n message: \"No response body\",\n sessionId: params.sessionId,\n });\n }\n\n const jsonlinesStream = jsonlines.parse();\n pipe(response.body, jsonlinesStream, { signal }).catch((err) => {\n console.error(\"Error piping logs:\", err);\n });\n\n for await (const chunk of jsonlinesStream) {\n const parsed = LogLine.parse(chunk);\n if (parsed.stream === \"error\") {\n throw new StreamError(\n parsed.data.code,\n parsed.data.message,\n params.sessionId,\n );\n }\n yield parsed;\n }\n })();\n\n return Object.assign(generator, {\n [Symbol.dispose]() {\n disposer.abort(\"Disposed\");\n },\n close: () => disposer.abort(\"Disposed\"),\n });\n }\n\n async stopSession(params: {\n sessionId: string;\n signal?: AbortSignal;\n blocking?: boolean;\n }): Promise<Parsed<z.infer<typeof SessionResponse>>> {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/stop`;\n const response = await parseOrThrow(\n SessionResponse,\n await this.request(url, { method: \"POST\", signal: params.signal }),\n );\n\n if (params.blocking) {\n let session = response.json.session;\n while (\n session.status !== \"stopped\" &&\n session.status !== \"failed\" &&\n session.status !== \"aborted\"\n ) {\n await setTimeout(500, undefined, { signal: params.signal });\n const poll = await this.getSession({\n sessionId: params.sessionId,\n signal: params.signal,\n });\n session = poll.json.session;\n response.json.session = session;\n }\n }\n\n return response;\n }\n\n async updateNetworkPolicy(params: {\n sessionId: string;\n networkPolicy: NetworkPolicy;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SessionResponse>>> {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/network-policy`;\n return parseOrThrow(\n SessionResponse,\n await this.request(url, {\n method: \"POST\",\n body: JSON.stringify(toAPINetworkPolicy(params.networkPolicy)),\n signal: params.signal,\n }),\n );\n }\n\n async extendTimeout(params: {\n sessionId: string;\n duration: number;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SessionResponse>>> {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/extend-timeout`;\n return parseOrThrow(\n SessionResponse,\n await this.request(url, {\n method: \"POST\",\n body: JSON.stringify({ duration: params.duration }),\n signal: params.signal,\n }),\n );\n }\n\n async createSnapshot(params: {\n sessionId: string;\n expiration?: number;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CreateSnapshotResponse>>> {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/snapshot`;\n const body =\n params.expiration === undefined\n ? undefined\n : JSON.stringify({ expiration: params.expiration });\n return parseOrThrow(\n CreateSnapshotResponse,\n await this.request(url, {\n method: \"POST\",\n body,\n signal: params.signal,\n }),\n );\n }\n\n async deleteSnapshot(params: {\n snapshotId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SnapshotResponse>>> {\n const url = `/v2/sandboxes/snapshots/${params.snapshotId}`;\n return parseOrThrow(\n SnapshotResponse,\n await this.request(url, { method: \"DELETE\", signal: params.signal }),\n );\n }\n\n async getSnapshot(params: {\n snapshotId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SnapshotResponse>>> {\n const url = `/v2/sandboxes/snapshots/${params.snapshotId}`;\n return parseOrThrow(\n SnapshotResponse,\n await this.request(url, { signal: params.signal }),\n );\n }\n\n async getSandbox(params: WithPrivate<{\n name: string;\n projectId: string;\n resume?: boolean;\n signal?: AbortSignal;\n }>) {\n const privateParams = getPrivateParams(params);\n const query: Record<string, string | undefined> = {\n projectId: params.projectId,\n ...privateParams,\n };\n if (params.resume !== undefined) {\n query.resume = String(params.resume);\n }\n return parseOrThrow(\n SandboxAndSessionResponse,\n await this.request(`/v2/sandboxes/${encodeURIComponent(params.name)}`, {\n query,\n signal: params.signal,\n }),\n );\n }\n\n async listSandboxes(params: {\n projectId: string;\n limit?: number;\n sortBy?: \"createdAt\" | \"name\" | \"statusUpdatedAt\";\n sortOrder?: \"asc\" | \"desc\";\n namePrefix?: string;\n cursor?: string;\n tags?: Record<string, string>;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SandboxesPaginationResponse,\n await this.request(`/v2/sandboxes`, {\n query: {\n project: params.projectId,\n limit: params.limit,\n sortBy: params.sortBy,\n sortOrder: params.sortOrder,\n namePrefix: params.namePrefix,\n cursor: params.cursor,\n tags: toTagsFilter(params.tags),\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async updateSandbox(params: {\n name: string;\n projectId: string;\n persistent?: boolean;\n resources?: { vcpus?: number; memory?: number };\n runtime?: RUNTIMES | (string & {});\n timeout?: number;\n networkPolicy?: NetworkPolicy;\n tags?: Record<string, string>;\n snapshotExpiration?: number;\n currentSnapshotId?: string;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n UpdateSandboxResponse,\n await this.request(`/v2/sandboxes/${encodeURIComponent(params.name)}`, {\n method: \"PATCH\",\n query: {\n projectId: params.projectId,\n },\n body: JSON.stringify({\n persistent: params.persistent,\n resources: params.resources,\n runtime: params.runtime,\n timeout: params.timeout,\n networkPolicy: params.networkPolicy\n ? toAPINetworkPolicy(params.networkPolicy)\n : undefined,\n tags: params.tags,\n snapshotExpiration: params.snapshotExpiration,\n currentSnapshotId: params.currentSnapshotId,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async deleteSandbox(params: {\n name: string;\n projectId: string;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n UpdateSandboxResponse,\n await this.request(`/v2/sandboxes/${encodeURIComponent(params.name)}`, {\n method: \"DELETE\",\n query: {\n projectId: params.projectId,\n },\n signal: params.signal,\n }),\n );\n }\n}\n\nasync function pipe(\n readable: ReadableStream<Uint8Array>,\n output: NodeJS.WritableStream,\n options?: { signal?: AbortSignal },\n) {\n const reader = readable.getReader();\n let aborted = false;\n\n const signal = options?.signal;\n const onAbort = () => {\n aborted = true;\n const reason =\n signal?.reason ??\n new DOMException(\"The operation was aborted.\", \"AbortError\");\n void reader.cancel(reason).catch(() => {\n // ignore cancel errors when aborting\n });\n\n if (\"destroy\" in output && typeof output.destroy === \"function\") {\n output.destroy(reason as Error);\n return;\n }\n\n output.emit(\"error\", reason);\n output.end();\n };\n\n if (signal) {\n if (signal.aborted) {\n onAbort();\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n try {\n while (true) {\n const read = await reader.read();\n if (read.value) {\n output.write(Buffer.from(read.value));\n }\n if (read.done) {\n break;\n }\n }\n } catch (err) {\n if (!aborted) {\n output.emit(\"error\", err);\n }\n } finally {\n signal?.removeEventListener(\"abort\", onAbort);\n if (!aborted) {\n output.end();\n }\n }\n}\n\nfunction mergeSignals(...signals: [AbortSignal, ...AbortSignal[]]) {\n const controller = new AbortController();\n const onAbort = () => {\n controller.abort();\n for (const signal of signals) {\n signal.removeEventListener(\"abort\", onAbort);\n }\n };\n for (const signal of signals) {\n if (signal.aborted) {\n controller.abort();\n break;\n }\n signal.addEventListener(\"abort\", onAbort);\n }\n return controller.signal;\n}\n\nfunction toTagsFilter(\n tags: Record<string, string> | undefined,\n): string[] | undefined {\n if (tags === undefined) return undefined;\n const entries = Object.entries(tags);\n if (entries.length === 0) return undefined;\n return entries.map(([key, value]) => `${key}:${value}`);\n}\n"],"mappings":"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"}
{"version":3,"file":"api-client.js","names":["command","query: Record<string, string | undefined>"],"sources":["../../src/api-client/api-client.ts"],"sourcesContent":["import {\n BaseClient,\n parseOrThrow,\n type Parsed,\n type RequestParams,\n} from \"./base-client.js\";\nimport {\n type CommandFinishedData,\n SessionAndRoutesResponse,\n SessionResponse,\n StopSessionResponse,\n SessionsResponse,\n CommandResponse,\n CommandFinishedResponse,\n EmptyResponse,\n LogLine,\n type LogLineStdout,\n type LogLineStderr,\n SnapshotsResponse,\n SnapshotResponse,\n CreateSnapshotResponse,\n SandboxAndSessionResponse,\n SandboxesPaginationResponse,\n UpdateSandboxResponse,\n type CommandData,\n} from \"./validators.js\";\nimport { APIError, StreamError } from \"./api-error.js\";\nimport { FileWriter } from \"./file-writer.js\";\nimport { VERSION } from \"../version.js\";\nimport { consumeReadable } from \"../utils/consume-readable.js\";\nimport { z } from \"zod\";\nimport jsonlines from \"jsonlines\";\nimport os from \"os\";\nimport { Readable } from \"stream\";\nimport { normalizePath } from \"../utils/normalizePath.js\";\nimport { getVercelOidcToken } from \"@vercel/oidc\";\nimport { NetworkPolicy } from \"../network-policy.js\";\nimport {\n toAPINetworkPolicy,\n fromAPINetworkPolicy,\n} from \"../utils/network-policy.js\";\nimport { getPrivateParams, WithPrivate } from \"../utils/types.js\";\nimport { RUNTIMES } from \"../constants.js\";\n\ninterface Claims {\n owner_id: string;\n project_id?: string;\n}\n\nfunction decodeUnverifiedToken(token: string): Claims | null {\n if (token.split(\".\").length !== 3) {\n return null;\n }\n try {\n const payload = JSON.parse(\n Buffer.from(token.split(\".\")[1], \"base64url\").toString(\"utf8\"),\n );\n if (payload.owner_id) {\n return { owner_id: payload.owner_id, project_id: payload.project_id };\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport interface WithFetchOptions {\n fetch?: typeof globalThis.fetch;\n}\n\nexport class APIClient extends BaseClient {\n private teamId: string;\n private projectId: string | undefined;\n private isJwtToken: boolean;\n\n constructor(params: {\n baseUrl?: string;\n teamId: string;\n token: string;\n fetch?: typeof globalThis.fetch;\n }) {\n super({\n baseUrl: params.baseUrl ?? \"https://vercel.com/api\",\n token: params.token,\n debug: false,\n fetch: params.fetch,\n });\n\n this.teamId = params.teamId;\n this.isJwtToken = false;\n\n const claims = decodeUnverifiedToken(params.token);\n if (claims) {\n this.isJwtToken = true;\n this.projectId = claims.project_id;\n this.teamId = claims.owner_id;\n }\n }\n\n private async ensureValidToken(): Promise<void> {\n if (!this.isJwtToken) {\n return;\n }\n\n try {\n // Use getVercelOidcToken to refresh the token with team/project scope\n const freshToken = await getVercelOidcToken({\n expirationBufferMs: 5 * 60 * 1000, // 5 minutes\n team: this.teamId,\n project: this.projectId,\n });\n\n // Update token if it changed\n if (freshToken !== this.token) {\n this.token = freshToken;\n\n const claims = decodeUnverifiedToken(freshToken);\n if (claims) {\n this.teamId = claims.owner_id;\n }\n }\n } catch {\n // Ignore refresh errors and continue with current token\n }\n }\n\n protected async request(path: string, params?: RequestParams) {\n await this.ensureValidToken();\n\n return super.request(path, {\n ...params,\n query: { teamId: this.teamId, ...params?.query },\n headers: {\n \"content-type\": \"application/json\",\n \"user-agent\": `vercel/sandbox/${VERSION} (Node.js/${process.version}; ${os.platform()}/${os.arch()})`,\n ...params?.headers,\n },\n });\n }\n\n async getSession(\n params: WithPrivate<{ sessionId: string; signal?: AbortSignal }>,\n ) {\n const privateParams = getPrivateParams(params);\n let querystring = new URLSearchParams(privateParams).toString();\n querystring = querystring ? `?${querystring}` : \"\";\n return parseOrThrow(\n SessionAndRoutesResponse,\n await this.request(`/v2/sandboxes/sessions/${params.sessionId}${querystring}`, {\n signal: params.signal,\n }),\n );\n }\n\n async createSandbox(\n params: WithPrivate<{\n name?: string;\n ports?: number[];\n projectId: string;\n source?:\n | {\n type: \"git\";\n url: string;\n depth?: number;\n revision?: string;\n username?: string;\n password?: string;\n }\n | { type: \"tarball\"; url: string }\n | { type: \"snapshot\"; snapshotId: string };\n timeout?: number;\n resources?: { vcpus: number };\n persistent?: boolean;\n runtime?: RUNTIMES | (string & {});\n networkPolicy?: NetworkPolicy;\n env?: Record<string, string>;\n tags?: Record<string, string>;\n snapshotExpiration?: number;\n signal?: AbortSignal;\n }>,\n ) {\n const privateParams = getPrivateParams(params);\n return parseOrThrow(\n SandboxAndSessionResponse,\n await this.request(\"/v2/sandboxes\", {\n method: \"POST\",\n body: JSON.stringify({\n projectId: params.projectId,\n ports: params.ports,\n source: params.source,\n timeout: params.timeout,\n resources: params.resources,\n runtime: params.runtime,\n name: params.name,\n persistent: params.persistent,\n networkPolicy: params.networkPolicy\n ? toAPINetworkPolicy(params.networkPolicy)\n : undefined,\n env: params.env,\n tags: params.tags,\n snapshotExpiration: params.snapshotExpiration,\n ...privateParams,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async runCommand(params: {\n sessionId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait: true;\n signal?: AbortSignal;\n }): Promise<{ command: CommandData; finished: Promise<CommandFinishedData> }>;\n async runCommand(params: {\n sessionId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait?: false;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandResponse>>>;\n async runCommand(params: {\n sessionId: string;\n cwd?: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n sudo: boolean;\n wait?: boolean;\n signal?: AbortSignal;\n }) {\n if (params.wait) {\n const response = await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/cmd`,\n {\n method: \"POST\",\n body: JSON.stringify({\n command: params.command,\n args: params.args,\n cwd: params.cwd,\n env: params.env,\n sudo: params.sudo,\n wait: true,\n }),\n signal: params.signal,\n },\n );\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.headers.get(\"content-type\") !== \"application/x-ndjson\") {\n throw new APIError(response, {\n message: \"Expected a stream of command data\",\n sessionId: params.sessionId,\n });\n }\n\n if (response.body === null) {\n throw new APIError(response, {\n message: \"No response body\",\n sessionId: params.sessionId,\n });\n }\n\n const jsonlinesStream = jsonlines.parse();\n pipe(response.body, jsonlinesStream, { signal: params.signal }).catch(\n (err) => {\n console.error(\"Error piping command stream:\", err);\n },\n );\n\n const iterator = jsonlinesStream[Symbol.asyncIterator]();\n const commandChunk = await iterator.next();\n if (commandChunk.done) {\n throw new StreamError(\n \"stream_ended_early\",\n \"Stream ended before command data was received\",\n params.sessionId,\n );\n }\n const { command } = CommandResponse.parse(commandChunk.value);\n\n const finished = (async () => {\n const finishedChunk = await iterator.next();\n if (finishedChunk.done) {\n throw new StreamError(\n \"stream_ended_early\",\n \"Stream ended before command finished\",\n params.sessionId,\n );\n }\n const { command } = CommandFinishedResponse.parse(finishedChunk.value);\n return command;\n })();\n\n return { command, finished };\n }\n\n return parseOrThrow(\n CommandResponse,\n await this.request(`/v2/sandboxes/sessions/${params.sessionId}/cmd`, {\n method: \"POST\",\n body: JSON.stringify({\n command: params.command,\n args: params.args,\n cwd: params.cwd,\n env: params.env,\n sudo: params.sudo,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async getCommand(params: {\n sessionId: string;\n cmdId: string;\n wait: true;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandFinishedResponse>>>;\n async getCommand(params: {\n sessionId: string;\n cmdId: string;\n wait?: boolean;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CommandResponse>>>;\n async getCommand(params: {\n sessionId: string;\n cmdId: string;\n wait?: boolean;\n signal?: AbortSignal;\n }) {\n return params.wait\n ? parseOrThrow(\n CommandFinishedResponse,\n await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/cmd/${params.cmdId}`,\n { signal: params.signal, query: { wait: \"true\" } },\n ),\n )\n : parseOrThrow(\n CommandResponse,\n await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/cmd/${params.cmdId}`,\n { signal: params.signal },\n ),\n );\n }\n\n async mkDir(params: {\n sessionId: string;\n path: string;\n cwd?: string;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n EmptyResponse,\n await this.request(`/v2/sandboxes/sessions/${params.sessionId}/fs/mkdir`, {\n method: \"POST\",\n body: JSON.stringify({ path: params.path, cwd: params.cwd }),\n signal: params.signal,\n }),\n );\n }\n\n getFileWriter(params: {\n sessionId: string;\n extractDir: string;\n signal?: AbortSignal;\n }) {\n const writer = new FileWriter();\n return {\n response: (async () => {\n return this.request(`/v2/sandboxes/sessions/${params.sessionId}/fs/write`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/gzip\",\n \"x-cwd\": params.extractDir,\n },\n body: await consumeReadable(writer.readable),\n signal: params.signal,\n });\n })(),\n writer,\n };\n }\n\n async listSessions(params: {\n /**\n * The ID or name of the project to which the sessions belong.\n * @example \"my-project\"\n */\n projectId: string;\n /**\n * Filter sessions by sandbox name.\n */\n name?: string;\n /**\n * Maximum number of sessions to list from a request.\n * @example 10\n */\n limit?: number;\n /**\n * Cursor for pagination.\n */\n cursor?: string;\n /**\n * Sort order for results.\n */\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SessionsResponse,\n await this.request(`/v2/sandboxes/sessions`, {\n query: {\n project: params.projectId,\n name: params.name,\n limit: params.limit,\n cursor: params.cursor,\n sortOrder: params.sortOrder,\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async listSnapshots(params: {\n /**\n * The ID or name of the project to which the snapshots belong.\n * @example \"my-project\"\n */\n projectId: string;\n /**\n * Filter snapshots by sandbox name.\n */\n name?: string;\n /**\n * Maximum number of snapshots to list from a request.\n * @example 10\n */\n limit?: number;\n /**\n * Cursor for pagination.\n */\n cursor?: string;\n /**\n * Sort order for results.\n */\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SnapshotsResponse,\n await this.request(`/v2/sandboxes/snapshots`, {\n query: {\n project: params.projectId,\n name: params.name,\n limit: params.limit,\n cursor: params.cursor,\n sortOrder: params.sortOrder,\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async writeFiles(params: {\n sessionId: string;\n cwd: string;\n files: {\n path: string;\n content: string | Uint8Array;\n mode?: number;\n }[];\n extractDir: string;\n signal?: AbortSignal;\n }) {\n const { writer, response } = this.getFileWriter({\n sessionId: params.sessionId,\n extractDir: params.extractDir,\n signal: params.signal,\n });\n\n for (const file of params.files) {\n await writer.addFile({\n name: normalizePath({\n filePath: file.path,\n extractDir: params.extractDir,\n cwd: params.cwd,\n }),\n content: file.content,\n mode: file.mode,\n });\n }\n\n writer.end();\n await parseOrThrow(EmptyResponse, await response);\n }\n\n async readFile(params: {\n sessionId: string;\n path: string;\n cwd?: string;\n signal?: AbortSignal;\n }): Promise<Readable | null> {\n const response = await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/fs/read`,\n {\n method: \"POST\",\n body: JSON.stringify({ path: params.path, cwd: params.cwd }),\n signal: params.signal,\n },\n );\n\n if (response.status === 404) {\n return null;\n }\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.body === null) {\n return null;\n }\n\n return Readable.fromWeb(response.body);\n }\n\n async killCommand(params: {\n sessionId: string;\n commandId: string;\n signal: number;\n abortSignal?: AbortSignal;\n }) {\n return parseOrThrow(\n CommandResponse,\n await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/cmd/${params.commandId}/kill`,\n {\n method: \"POST\",\n body: JSON.stringify({ signal: params.signal }),\n signal: params.abortSignal,\n },\n ),\n );\n }\n\n getLogs(params: {\n sessionId: string;\n cmdId: string;\n signal?: AbortSignal;\n }): AsyncGenerator<\n z.infer<typeof LogLineStdout> | z.infer<typeof LogLineStderr>,\n void,\n void\n > &\n Disposable & { close(): void } {\n const self = this;\n const disposer = new AbortController();\n const signal = !params.signal\n ? disposer.signal\n : mergeSignals(params.signal, disposer.signal);\n\n const generator = (async function* () {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/cmd/${params.cmdId}/logs`;\n const response = await self.request(url, {\n method: \"GET\",\n signal,\n });\n\n if (!response.ok) {\n await parseOrThrow(z.any(), response);\n }\n\n if (response.headers.get(\"content-type\") !== \"application/x-ndjson\") {\n throw new APIError(response, {\n message: \"Expected a stream of logs\",\n sessionId: params.sessionId,\n });\n }\n\n if (response.body === null) {\n throw new APIError(response, {\n message: \"No response body\",\n sessionId: params.sessionId,\n });\n }\n\n const jsonlinesStream = jsonlines.parse();\n pipe(response.body, jsonlinesStream, { signal }).catch((err) => {\n console.error(\"Error piping logs:\", err);\n });\n\n for await (const chunk of jsonlinesStream) {\n const parsed = LogLine.parse(chunk);\n if (parsed.stream === \"error\") {\n throw new StreamError(\n parsed.data.code,\n parsed.data.message,\n params.sessionId,\n );\n }\n yield parsed;\n }\n })();\n\n return Object.assign(generator, {\n [Symbol.dispose]() {\n disposer.abort(\"Disposed\");\n },\n close: () => disposer.abort(\"Disposed\"),\n });\n }\n\n async stopSession(params: {\n sessionId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof StopSessionResponse>>> {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/stop`;\n return parseOrThrow(\n StopSessionResponse,\n await this.request(url, { method: \"POST\", signal: params.signal }),\n );\n }\n\n async updateNetworkPolicy(params: {\n sessionId: string;\n networkPolicy: NetworkPolicy;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SessionResponse>>> {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/network-policy`;\n return parseOrThrow(\n SessionResponse,\n await this.request(url, {\n method: \"POST\",\n body: JSON.stringify(toAPINetworkPolicy(params.networkPolicy)),\n signal: params.signal,\n }),\n );\n }\n\n async extendTimeout(params: {\n sessionId: string;\n duration: number;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SessionResponse>>> {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/extend-timeout`;\n return parseOrThrow(\n SessionResponse,\n await this.request(url, {\n method: \"POST\",\n body: JSON.stringify({ duration: params.duration }),\n signal: params.signal,\n }),\n );\n }\n\n async createSnapshot(params: {\n sessionId: string;\n expiration?: number;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof CreateSnapshotResponse>>> {\n const url = `/v2/sandboxes/sessions/${params.sessionId}/snapshot`;\n const body =\n params.expiration === undefined\n ? undefined\n : JSON.stringify({ expiration: params.expiration });\n return parseOrThrow(\n CreateSnapshotResponse,\n await this.request(url, {\n method: \"POST\",\n body,\n signal: params.signal,\n }),\n );\n }\n\n async deleteSnapshot(params: {\n snapshotId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SnapshotResponse>>> {\n const url = `/v2/sandboxes/snapshots/${params.snapshotId}`;\n return parseOrThrow(\n SnapshotResponse,\n await this.request(url, { method: \"DELETE\", signal: params.signal }),\n );\n }\n\n async getSnapshot(params: {\n snapshotId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof SnapshotResponse>>> {\n const url = `/v2/sandboxes/snapshots/${params.snapshotId}`;\n return parseOrThrow(\n SnapshotResponse,\n await this.request(url, { signal: params.signal }),\n );\n }\n\n async getSandbox(params: WithPrivate<{\n name: string;\n projectId: string;\n resume?: boolean;\n signal?: AbortSignal;\n }>) {\n const privateParams = getPrivateParams(params);\n const query: Record<string, string | undefined> = {\n projectId: params.projectId,\n ...privateParams,\n };\n if (params.resume !== undefined) {\n query.resume = String(params.resume);\n }\n return parseOrThrow(\n SandboxAndSessionResponse,\n await this.request(`/v2/sandboxes/${encodeURIComponent(params.name)}`, {\n query,\n signal: params.signal,\n }),\n );\n }\n\n async listSandboxes(params: {\n projectId: string;\n limit?: number;\n sortBy?: \"createdAt\" | \"name\" | \"statusUpdatedAt\";\n sortOrder?: \"asc\" | \"desc\";\n namePrefix?: string;\n cursor?: string;\n tags?: Record<string, string>;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SandboxesPaginationResponse,\n await this.request(`/v2/sandboxes`, {\n query: {\n project: params.projectId,\n limit: params.limit,\n sortBy: params.sortBy,\n sortOrder: params.sortOrder,\n namePrefix: params.namePrefix,\n cursor: params.cursor,\n tags: toTagsFilter(params.tags),\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async updateSandbox(params: {\n name: string;\n projectId: string;\n persistent?: boolean;\n resources?: { vcpus?: number; memory?: number };\n runtime?: RUNTIMES | (string & {});\n timeout?: number;\n networkPolicy?: NetworkPolicy;\n tags?: Record<string, string>;\n snapshotExpiration?: number;\n currentSnapshotId?: string;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n UpdateSandboxResponse,\n await this.request(`/v2/sandboxes/${encodeURIComponent(params.name)}`, {\n method: \"PATCH\",\n query: {\n projectId: params.projectId,\n },\n body: JSON.stringify({\n persistent: params.persistent,\n resources: params.resources,\n runtime: params.runtime,\n timeout: params.timeout,\n networkPolicy: params.networkPolicy\n ? toAPINetworkPolicy(params.networkPolicy)\n : undefined,\n tags: params.tags,\n snapshotExpiration: params.snapshotExpiration,\n currentSnapshotId: params.currentSnapshotId,\n }),\n signal: params.signal,\n }),\n );\n }\n\n async deleteSandbox(params: {\n name: string;\n projectId: string;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n UpdateSandboxResponse,\n await this.request(`/v2/sandboxes/${encodeURIComponent(params.name)}`, {\n method: \"DELETE\",\n query: {\n projectId: params.projectId,\n },\n signal: params.signal,\n }),\n );\n }\n}\n\nasync function pipe(\n readable: ReadableStream<Uint8Array>,\n output: NodeJS.WritableStream,\n options?: { signal?: AbortSignal },\n) {\n const reader = readable.getReader();\n let aborted = false;\n\n const signal = options?.signal;\n const onAbort = () => {\n aborted = true;\n const reason =\n signal?.reason ??\n new DOMException(\"The operation was aborted.\", \"AbortError\");\n void reader.cancel(reason).catch(() => {\n // ignore cancel errors when aborting\n });\n\n if (\"destroy\" in output && typeof output.destroy === \"function\") {\n output.destroy(reason as Error);\n return;\n }\n\n output.emit(\"error\", reason);\n output.end();\n };\n\n if (signal) {\n if (signal.aborted) {\n onAbort();\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n try {\n while (true) {\n const read = await reader.read();\n if (read.value) {\n output.write(Buffer.from(read.value));\n }\n if (read.done) {\n break;\n }\n }\n } catch (err) {\n if (!aborted) {\n output.emit(\"error\", err);\n }\n } finally {\n signal?.removeEventListener(\"abort\", onAbort);\n if (!aborted) {\n output.end();\n }\n }\n}\n\nfunction mergeSignals(...signals: [AbortSignal, ...AbortSignal[]]) {\n const controller = new AbortController();\n const onAbort = () => {\n controller.abort();\n for (const signal of signals) {\n signal.removeEventListener(\"abort\", onAbort);\n }\n };\n for (const signal of signals) {\n if (signal.aborted) {\n controller.abort();\n break;\n }\n signal.addEventListener(\"abort\", onAbort);\n }\n return controller.signal;\n}\n\nfunction toTagsFilter(\n tags: Record<string, string> | undefined,\n): string[] | undefined {\n if (tags === undefined) return undefined;\n const entries = Object.entries(tags);\n if (entries.length === 0) return undefined;\n return entries.map(([key, value]) => `${key}:${value}`);\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAiDA,SAAS,sBAAsB,OAA8B;AAC3D,KAAI,MAAM,MAAM,IAAI,CAAC,WAAW,EAC9B,QAAO;AAET,KAAI;EACF,MAAM,UAAU,KAAK,MACnB,OAAO,KAAK,MAAM,MAAM,IAAI,CAAC,IAAI,YAAY,CAAC,SAAS,OAAO,CAC/D;AACD,MAAI,QAAQ,SACV,QAAO;GAAE,UAAU,QAAQ;GAAU,YAAY,QAAQ;GAAY;AAEvE,SAAO;SACD;AACN,SAAO;;;AAQX,IAAa,YAAb,cAA+B,WAAW;CAKxC,YAAY,QAKT;AACD,QAAM;GACJ,SAAS,OAAO,WAAW;GAC3B,OAAO,OAAO;GACd,OAAO;GACP,OAAO,OAAO;GACf,CAAC;AAEF,OAAK,SAAS,OAAO;AACrB,OAAK,aAAa;EAElB,MAAM,SAAS,sBAAsB,OAAO,MAAM;AAClD,MAAI,QAAQ;AACV,QAAK,aAAa;AAClB,QAAK,YAAY,OAAO;AACxB,QAAK,SAAS,OAAO;;;CAIzB,MAAc,mBAAkC;AAC9C,MAAI,CAAC,KAAK,WACR;AAGF,MAAI;GAEF,MAAM,aAAa,MAAM,mBAAmB;IAC1C,oBAAoB,MAAS;IAC7B,MAAM,KAAK;IACX,SAAS,KAAK;IACf,CAAC;AAGF,OAAI,eAAe,KAAK,OAAO;AAC7B,SAAK,QAAQ;IAEb,MAAM,SAAS,sBAAsB,WAAW;AAChD,QAAI,OACF,MAAK,SAAS,OAAO;;UAGnB;;CAKV,MAAgB,QAAQ,MAAc,QAAwB;AAC5D,QAAM,KAAK,kBAAkB;AAE7B,SAAO,MAAM,QAAQ,MAAM;GACzB,GAAG;GACH,OAAO;IAAE,QAAQ,KAAK;IAAQ,GAAG,QAAQ;IAAO;GAChD,SAAS;IACP,gBAAgB;IAChB,cAAc,kBAAkB,QAAQ,YAAY,QAAQ,QAAQ,IAAI,GAAG,UAAU,CAAC,GAAG,GAAG,MAAM,CAAC;IACnG,GAAG,QAAQ;IACZ;GACF,CAAC;;CAGJ,MAAM,WACJ,QACA;EACA,MAAM,gBAAgB,iBAAiB,OAAO;EAC9C,IAAI,cAAc,IAAI,gBAAgB,cAAc,CAAC,UAAU;AAC/D,gBAAc,cAAc,IAAI,gBAAgB;AAChD,SAAO,aACL,0BACA,MAAM,KAAK,QAAQ,0BAA0B,OAAO,YAAY,eAAe,EAC7E,QAAQ,OAAO,QAChB,CAAC,CACH;;CAGH,MAAM,cACJ,QAyBA;EACA,MAAM,gBAAgB,iBAAiB,OAAO;AAC9C,SAAO,aACL,2BACA,MAAM,KAAK,QAAQ,iBAAiB;GAClC,QAAQ;GACR,MAAM,KAAK,UAAU;IACnB,WAAW,OAAO;IAClB,OAAO,OAAO;IACd,QAAQ,OAAO;IACf,SAAS,OAAO;IAChB,WAAW,OAAO;IAClB,SAAS,OAAO;IAChB,MAAM,OAAO;IACb,YAAY,OAAO;IACnB,eAAe,OAAO,gBAClB,mBAAmB,OAAO,cAAc,GACxC;IACJ,KAAK,OAAO;IACZ,MAAM,OAAO;IACb,oBAAoB,OAAO;IAC3B,GAAG;IACJ,CAAC;GACF,QAAQ,OAAO;GAChB,CAAC,CACH;;CAuBH,MAAM,WAAW,QASd;AACD,MAAI,OAAO,MAAM;GACf,MAAM,WAAW,MAAM,KAAK,QAC1B,0BAA0B,OAAO,UAAU,OAC3C;IACE,QAAQ;IACR,MAAM,KAAK,UAAU;KACnB,SAAS,OAAO;KAChB,MAAM,OAAO;KACb,KAAK,OAAO;KACZ,KAAK,OAAO;KACZ,MAAM,OAAO;KACb,MAAM;KACP,CAAC;IACF,QAAQ,OAAO;IAChB,CACF;AAED,OAAI,CAAC,SAAS,GACZ,OAAM,aAAa,EAAE,KAAK,EAAE,SAAS;AAGvC,OAAI,SAAS,QAAQ,IAAI,eAAe,KAAK,uBAC3C,OAAM,IAAI,SAAS,UAAU;IAC3B,SAAS;IACT,WAAW,OAAO;IACnB,CAAC;AAGJ,OAAI,SAAS,SAAS,KACpB,OAAM,IAAI,SAAS,UAAU;IAC3B,SAAS;IACT,WAAW,OAAO;IACnB,CAAC;GAGJ,MAAM,kBAAkB,UAAU,OAAO;AACzC,QAAK,SAAS,MAAM,iBAAiB,EAAE,QAAQ,OAAO,QAAQ,CAAC,CAAC,OAC7D,QAAQ;AACP,YAAQ,MAAM,gCAAgC,IAAI;KAErD;GAED,MAAM,WAAW,gBAAgB,OAAO,gBAAgB;GACxD,MAAM,eAAe,MAAM,SAAS,MAAM;AAC1C,OAAI,aAAa,KACf,OAAM,IAAI,YACR,sBACA,iDACA,OAAO,UACR;GAEH,MAAM,EAAE,YAAY,gBAAgB,MAAM,aAAa,MAAM;AAe7D,UAAO;IAAE;IAAS,WAbA,YAAY;KAC5B,MAAM,gBAAgB,MAAM,SAAS,MAAM;AAC3C,SAAI,cAAc,KAChB,OAAM,IAAI,YACR,sBACA,wCACA,OAAO,UACR;KAEH,MAAM,EAAE,uBAAY,wBAAwB,MAAM,cAAc,MAAM;AACtE,YAAOA;QACL;IAEwB;;AAG9B,SAAO,aACL,iBACA,MAAM,KAAK,QAAQ,0BAA0B,OAAO,UAAU,OAAO;GACnE,QAAQ;GACR,MAAM,KAAK,UAAU;IACnB,SAAS,OAAO;IAChB,MAAM,OAAO;IACb,KAAK,OAAO;IACZ,KAAK,OAAO;IACZ,MAAM,OAAO;IACd,CAAC;GACF,QAAQ,OAAO;GAChB,CAAC,CACH;;CAeH,MAAM,WAAW,QAKd;AACD,SAAO,OAAO,OACV,aACE,yBACA,MAAM,KAAK,QACT,0BAA0B,OAAO,UAAU,OAAO,OAAO,SACzD;GAAE,QAAQ,OAAO;GAAQ,OAAO,EAAE,MAAM,QAAQ;GAAE,CACnD,CACF,GACD,aACE,iBACA,MAAM,KAAK,QACT,0BAA0B,OAAO,UAAU,OAAO,OAAO,SACzD,EAAE,QAAQ,OAAO,QAAQ,CAC1B,CACF;;CAGP,MAAM,MAAM,QAKT;AACD,SAAO,aACL,eACA,MAAM,KAAK,QAAQ,0BAA0B,OAAO,UAAU,YAAY;GACxE,QAAQ;GACR,MAAM,KAAK,UAAU;IAAE,MAAM,OAAO;IAAM,KAAK,OAAO;IAAK,CAAC;GAC5D,QAAQ,OAAO;GAChB,CAAC,CACH;;CAGH,cAAc,QAIX;EACD,MAAM,SAAS,IAAI,YAAY;AAC/B,SAAO;GACL,WAAW,YAAY;AACrB,WAAO,KAAK,QAAQ,0BAA0B,OAAO,UAAU,YAAY;KACzE,QAAQ;KACR,SAAS;MACP,gBAAgB;MAChB,SAAS,OAAO;MACjB;KACD,MAAM,MAAM,gBAAgB,OAAO,SAAS;KAC5C,QAAQ,OAAO;KAChB,CAAC;OACA;GACJ;GACD;;CAGH,MAAM,aAAa,QAwBhB;AACD,SAAO,aACL,kBACA,MAAM,KAAK,QAAQ,0BAA0B;GAC3C,OAAO;IACL,SAAS,OAAO;IAChB,MAAM,OAAO;IACb,OAAO,OAAO;IACd,QAAQ,OAAO;IACf,WAAW,OAAO;IACnB;GACD,QAAQ;GACR,QAAQ,OAAO;GAChB,CAAC,CACH;;CAGH,MAAM,cAAc,QAwBjB;AACD,SAAO,aACL,mBACA,MAAM,KAAK,QAAQ,2BAA2B;GAC5C,OAAO;IACL,SAAS,OAAO;IAChB,MAAM,OAAO;IACb,OAAO,OAAO;IACd,QAAQ,OAAO;IACf,WAAW,OAAO;IACnB;GACD,QAAQ;GACR,QAAQ,OAAO;GAChB,CAAC,CACH;;CAGH,MAAM,WAAW,QAUd;EACD,MAAM,EAAE,QAAQ,aAAa,KAAK,cAAc;GAC9C,WAAW,OAAO;GAClB,YAAY,OAAO;GACnB,QAAQ,OAAO;GAChB,CAAC;AAEF,OAAK,MAAM,QAAQ,OAAO,MACxB,OAAM,OAAO,QAAQ;GACnB,MAAM,cAAc;IAClB,UAAU,KAAK;IACf,YAAY,OAAO;IACnB,KAAK,OAAO;IACb,CAAC;GACF,SAAS,KAAK;GACd,MAAM,KAAK;GACZ,CAAC;AAGJ,SAAO,KAAK;AACZ,QAAM,aAAa,eAAe,MAAM,SAAS;;CAGnD,MAAM,SAAS,QAKc;EAC3B,MAAM,WAAW,MAAM,KAAK,QAC1B,0BAA0B,OAAO,UAAU,WAC3C;GACE,QAAQ;GACR,MAAM,KAAK,UAAU;IAAE,MAAM,OAAO;IAAM,KAAK,OAAO;IAAK,CAAC;GAC5D,QAAQ,OAAO;GAChB,CACF;AAED,MAAI,SAAS,WAAW,IACtB,QAAO;AAGT,MAAI,CAAC,SAAS,GACZ,OAAM,aAAa,EAAE,KAAK,EAAE,SAAS;AAGvC,MAAI,SAAS,SAAS,KACpB,QAAO;AAGT,SAAO,SAAS,QAAQ,SAAS,KAAK;;CAGxC,MAAM,YAAY,QAKf;AACD,SAAO,aACL,iBACA,MAAM,KAAK,QACT,0BAA0B,OAAO,UAAU,OAAO,OAAO,UAAU,QACnE;GACE,QAAQ;GACR,MAAM,KAAK,UAAU,EAAE,QAAQ,OAAO,QAAQ,CAAC;GAC/C,QAAQ,OAAO;GAChB,CACF,CACF;;CAGH,QAAQ,QASyB;EAC/B,MAAM,OAAO;EACb,MAAM,WAAW,IAAI,iBAAiB;EACtC,MAAM,SAAS,CAAC,OAAO,SACnB,SAAS,SACT,aAAa,OAAO,QAAQ,SAAS,OAAO;EAEhD,MAAM,aAAa,mBAAmB;GACpC,MAAM,MAAM,0BAA0B,OAAO,UAAU,OAAO,OAAO,MAAM;GAC3E,MAAM,WAAW,MAAM,KAAK,QAAQ,KAAK;IACvC,QAAQ;IACR;IACD,CAAC;AAEF,OAAI,CAAC,SAAS,GACZ,OAAM,aAAa,EAAE,KAAK,EAAE,SAAS;AAGvC,OAAI,SAAS,QAAQ,IAAI,eAAe,KAAK,uBAC3C,OAAM,IAAI,SAAS,UAAU;IAC3B,SAAS;IACT,WAAW,OAAO;IACnB,CAAC;AAGJ,OAAI,SAAS,SAAS,KACpB,OAAM,IAAI,SAAS,UAAU;IAC3B,SAAS;IACT,WAAW,OAAO;IACnB,CAAC;GAGJ,MAAM,kBAAkB,UAAU,OAAO;AACzC,QAAK,SAAS,MAAM,iBAAiB,EAAE,QAAQ,CAAC,CAAC,OAAO,QAAQ;AAC9D,YAAQ,MAAM,sBAAsB,IAAI;KACxC;AAEF,cAAW,MAAM,SAAS,iBAAiB;IACzC,MAAM,SAAS,QAAQ,MAAM,MAAM;AACnC,QAAI,OAAO,WAAW,QACpB,OAAM,IAAI,YACR,OAAO,KAAK,MACZ,OAAO,KAAK,SACZ,OAAO,UACR;AAEH,UAAM;;MAEN;AAEJ,SAAO,OAAO,OAAO,WAAW;GAC9B,CAAC,OAAO,WAAW;AACjB,aAAS,MAAM,WAAW;;GAE5B,aAAa,SAAS,MAAM,WAAW;GACxC,CAAC;;CAGJ,MAAM,YAAY,QAGuC;EACvD,MAAM,MAAM,0BAA0B,OAAO,UAAU;AACvD,SAAO,aACL,qBACA,MAAM,KAAK,QAAQ,KAAK;GAAE,QAAQ;GAAQ,QAAQ,OAAO;GAAQ,CAAC,CACnE;;CAGH,MAAM,oBAAoB,QAI2B;EACnD,MAAM,MAAM,0BAA0B,OAAO,UAAU;AACvD,SAAO,aACL,iBACA,MAAM,KAAK,QAAQ,KAAK;GACtB,QAAQ;GACR,MAAM,KAAK,UAAU,mBAAmB,OAAO,cAAc,CAAC;GAC9D,QAAQ,OAAO;GAChB,CAAC,CACH;;CAGH,MAAM,cAAc,QAIiC;EACnD,MAAM,MAAM,0BAA0B,OAAO,UAAU;AACvD,SAAO,aACL,iBACA,MAAM,KAAK,QAAQ,KAAK;GACtB,QAAQ;GACR,MAAM,KAAK,UAAU,EAAE,UAAU,OAAO,UAAU,CAAC;GACnD,QAAQ,OAAO;GAChB,CAAC,CACH;;CAGH,MAAM,eAAe,QAIuC;EAC1D,MAAM,MAAM,0BAA0B,OAAO,UAAU;EACvD,MAAM,OACJ,OAAO,eAAe,SAClB,SACA,KAAK,UAAU,EAAE,YAAY,OAAO,YAAY,CAAC;AACvD,SAAO,aACL,wBACA,MAAM,KAAK,QAAQ,KAAK;GACtB,QAAQ;GACR;GACA,QAAQ,OAAO;GAChB,CAAC,CACH;;CAGH,MAAM,eAAe,QAGiC;EACpD,MAAM,MAAM,2BAA2B,OAAO;AAC9C,SAAO,aACL,kBACA,MAAM,KAAK,QAAQ,KAAK;GAAE,QAAQ;GAAU,QAAQ,OAAO;GAAQ,CAAC,CACrE;;CAGH,MAAM,YAAY,QAGoC;EACpD,MAAM,MAAM,2BAA2B,OAAO;AAC9C,SAAO,aACL,kBACA,MAAM,KAAK,QAAQ,KAAK,EAAE,QAAQ,OAAO,QAAQ,CAAC,CACnD;;CAGH,MAAM,WAAW,QAKb;EACF,MAAM,gBAAgB,iBAAiB,OAAO;EAC9C,MAAMC,QAA4C;GAChD,WAAW,OAAO;GAClB,GAAG;GACJ;AACD,MAAI,OAAO,WAAW,OACpB,OAAM,SAAS,OAAO,OAAO,OAAO;AAEtC,SAAO,aACL,2BACA,MAAM,KAAK,QAAQ,iBAAiB,mBAAmB,OAAO,KAAK,IAAI;GACrE;GACA,QAAQ,OAAO;GAChB,CAAC,CACH;;CAGH,MAAM,cAAc,QASjB;AACD,SAAO,aACL,6BACA,MAAM,KAAK,QAAQ,iBAAiB;GAClC,OAAO;IACL,SAAS,OAAO;IAChB,OAAO,OAAO;IACd,QAAQ,OAAO;IACf,WAAW,OAAO;IAClB,YAAY,OAAO;IACnB,QAAQ,OAAO;IACf,MAAM,aAAa,OAAO,KAAK;IAChC;GACD,QAAQ;GACR,QAAQ,OAAO;GAChB,CAAC,CACH;;CAGH,MAAM,cAAc,QAYjB;AACD,SAAO,aACL,uBACA,MAAM,KAAK,QAAQ,iBAAiB,mBAAmB,OAAO,KAAK,IAAI;GACrE,QAAQ;GACR,OAAO,EACL,WAAW,OAAO,WACnB;GACD,MAAM,KAAK,UAAU;IACnB,YAAY,OAAO;IACnB,WAAW,OAAO;IAClB,SAAS,OAAO;IAChB,SAAS,OAAO;IAChB,eAAe,OAAO,gBAClB,mBAAmB,OAAO,cAAc,GACxC;IACJ,MAAM,OAAO;IACb,oBAAoB,OAAO;IAC3B,mBAAmB,OAAO;IAC3B,CAAC;GACF,QAAQ,OAAO;GAChB,CAAC,CACH;;CAGH,MAAM,cAAc,QAIjB;AACD,SAAO,aACL,uBACA,MAAM,KAAK,QAAQ,iBAAiB,mBAAmB,OAAO,KAAK,IAAI;GACrE,QAAQ;GACR,OAAO,EACL,WAAW,OAAO,WACnB;GACD,QAAQ,OAAO;GAChB,CAAC,CACH;;;AAIL,eAAe,KACb,UACA,QACA,SACA;CACA,MAAM,SAAS,SAAS,WAAW;CACnC,IAAI,UAAU;CAEd,MAAM,SAAS,SAAS;CACxB,MAAM,gBAAgB;AACpB,YAAU;EACV,MAAM,SACJ,QAAQ,UACR,IAAI,aAAa,8BAA8B,aAAa;AAC9D,EAAK,OAAO,OAAO,OAAO,CAAC,YAAY,GAErC;AAEF,MAAI,aAAa,UAAU,OAAO,OAAO,YAAY,YAAY;AAC/D,UAAO,QAAQ,OAAgB;AAC/B;;AAGF,SAAO,KAAK,SAAS,OAAO;AAC5B,SAAO,KAAK;;AAGd,KAAI,OACF,KAAI,OAAO,QACT,UAAS;KAET,QAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM,CAAC;AAI7D,KAAI;AACF,SAAO,MAAM;GACX,MAAM,OAAO,MAAM,OAAO,MAAM;AAChC,OAAI,KAAK,MACP,QAAO,MAAM,OAAO,KAAK,KAAK,MAAM,CAAC;AAEvC,OAAI,KAAK,KACP;;UAGG,KAAK;AACZ,MAAI,CAAC,QACH,QAAO,KAAK,SAAS,IAAI;WAEnB;AACR,UAAQ,oBAAoB,SAAS,QAAQ;AAC7C,MAAI,CAAC,QACH,QAAO,KAAK;;;AAKlB,SAAS,aAAa,GAAG,SAA0C;CACjE,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,gBAAgB;AACpB,aAAW,OAAO;AAClB,OAAK,MAAM,UAAU,QACnB,QAAO,oBAAoB,SAAS,QAAQ;;AAGhD,MAAK,MAAM,UAAU,SAAS;AAC5B,MAAI,OAAO,SAAS;AAClB,cAAW,OAAO;AAClB;;AAEF,SAAO,iBAAiB,SAAS,QAAQ;;AAE3C,QAAO,WAAW;;AAGpB,SAAS,aACP,MACsB;AACtB,KAAI,SAAS,OAAW,QAAO;CAC/B,MAAM,UAAU,OAAO,QAAQ,KAAK;AACpC,KAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,QAAO,QAAQ,KAAK,CAAC,KAAK,WAAW,GAAG,IAAI,GAAG,QAAQ"}

@@ -8,2 +8,3 @@ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');

//#region src/api-client/base-client.ts
const DEFAULT_AGENT = new undici.Agent({ bodyTimeout: 0 });
/**

@@ -20,3 +21,3 @@ * A base API client that provides a convenience wrapper for fetching where

this.token = params.token;
this.agent = new undici.Agent({ bodyTimeout: 0 });
this.agent = DEFAULT_AGENT;
}

@@ -23,0 +24,0 @@ async request(path, opts) {

@@ -1,1 +0,1 @@

{"version":3,"file":"base-client.cjs","names":["withRetry","Agent","value","sandboxName: string | undefined","APIError","json: Data | ErrorData"],"sources":["../../src/api-client/base-client.ts"],"sourcesContent":["import type { Options as RetryOptions } from \"async-retry\";\nimport { APIError } from \"./api-error.js\";\nimport { ZodType } from \"zod\";\nimport { array } from \"../utils/array.js\";\nimport { withRetry, type RequestOptions } from \"./with-retry.js\";\nimport { Agent } from \"undici\";\n\nexport interface RequestParams extends RequestInit {\n headers?: Record<string, string>;\n method?: string;\n onRetry?(error: any, options: RequestOptions): void;\n query?: Record<string, number | string | null | undefined | string[]>;\n retry?: Partial<RetryOptions>;\n}\n\n/**\n * A base API client that provides a convenience wrapper for fetching where\n * we can pass query parameters as an object, support retries, debugging\n * and automatic authorization.\n */\nexport class BaseClient {\n protected token?: string;\n private fetch: ReturnType<typeof withRetry<RequestInit>>;\n private debug: boolean;\n private baseUrl: string;\n private agent: Agent;\n\n constructor(params: {\n debug?: boolean;\n baseUrl: string;\n token?: string;\n fetch?: typeof globalThis.fetch;\n }) {\n this.fetch = withRetry(params.fetch ?? globalThis.fetch);\n this.baseUrl = params.baseUrl;\n this.debug = params.debug ?? process.env.DEBUG_FETCH === \"true\";\n this.token = params.token;\n this.agent = new Agent({\n bodyTimeout: 0, // disable body timeout to allow long logs streaming\n });\n }\n\n protected async request(path: string, opts?: RequestParams) {\n const url = new URL(`${this.baseUrl}${path}`);\n if (opts?.query) {\n for (const [key, value] of Object.entries(opts.query)) {\n array(value).forEach((value) => {\n url.searchParams.append(key, value.toString());\n });\n }\n }\n\n const start = Date.now();\n const response = await this.fetch(url.toString(), {\n ...opts,\n body: opts?.body,\n method: opts?.method || \"GET\",\n headers: this.token\n ? { Authorization: `Bearer ${this.token}`, ...opts?.headers }\n : opts?.headers,\n // @ts-expect-error Node.js' and undici's Agent have different types\n dispatcher: this.agent,\n signal: opts?.signal,\n });\n\n if (this.debug) {\n const duration = Date.now() - start;\n console.log(`[API] ${url} (${response.status}) ${duration}ms`);\n if (response.status === 429) {\n const retry = parseInt(response.headers.get(\"Retry-After\") ?? \"\", 10);\n const hours = Math.floor(retry / 60 / 60);\n const minutes = Math.floor(retry / 60) % 60;\n const seconds = retry % 60;\n console.warn(\n `[API] ${url} Rate Limited, Retry After ${hours}h ${minutes}m ${seconds}s`,\n );\n }\n }\n\n return response;\n }\n}\n\nexport interface Parsed<Data> {\n response: Response;\n text: string;\n json: Data;\n}\n\n/**\n * Extract sessionId from a sandbox API URL.\n */\nfunction extractSessionId(url: string): string | undefined {\n const match = url.match(/\\/v2\\/sandboxes\\/sessions\\/([^/?]+)/);\n return match?.[1];\n}\n\n/**\n * Extract sandbox name from a sandbox API url.\n * Excludes known sub-paths like /sessions/ and /snapshots/.\n */\nfunction extractSandboxName(url: string): string | undefined {\n const match = url.match(/\\/v2\\/sandboxes\\/(?!sessions(?:\\/|$|\\?))(?!snapshots(?:\\/|$|\\?))([^/?]+)/);\n return match?.[1];\n}\n\n/**\n * Allows to read the response text and parse it as JSON casting to the given\n * type. If the response is not ok or cannot be parsed it will return error.\n *\n * @param response Response to parse.\n * @returns Parsed response or error.\n */\nexport async function parse<Data, ErrorData>(\n validator: ZodType<Data>,\n response: Response,\n): Promise<Parsed<Data> | APIError<ErrorData>> {\n const sessionId = extractSessionId(response.url);\n let sandboxName: string | undefined;\n if (!sessionId) {\n sandboxName = extractSandboxName(response.url);\n }\n\n const text = await response.text().catch((err) => {\n return new APIError<ErrorData>(response, {\n message: `Can't read response text: ${String(err)}`,\n sessionId,\n sandboxName\n });\n });\n\n if (typeof text !== \"string\") {\n return text;\n }\n\n let json: Data | ErrorData;\n\n try {\n json = JSON.parse(text || \"{}\");\n } catch (error) {\n return new APIError<ErrorData>(response, {\n message: `Can't parse JSON: ${String(error)}`,\n text,\n sessionId,\n sandboxName\n });\n }\n\n if (!response.ok) {\n return new APIError<ErrorData>(response, {\n message: `Status code ${response.status} is not ok`,\n json: json as ErrorData,\n text,\n sessionId,\n sandboxName\n });\n }\n\n const validated = validator.safeParse(json);\n if (!validated.success) {\n return new APIError<ErrorData>(response, {\n message: `Response JSON is not valid: ${validated.error}`,\n json: json as ErrorData,\n text,\n sessionId,\n sandboxName\n });\n }\n\n return {\n json: validated.data,\n response,\n text,\n };\n}\n\nexport async function parseOrThrow<Data, ErrorData>(\n validator: ZodType<Data>,\n response: Response,\n): Promise<Parsed<Data>> {\n const result = await parse<Data, ErrorData>(validator, response);\n if (result instanceof APIError) {\n throw result;\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;;AAoBA,IAAa,aAAb,MAAwB;CAOtB,YAAY,QAKT;AACD,OAAK,QAAQA,6BAAU,OAAO,SAAS,WAAW,MAAM;AACxD,OAAK,UAAU,OAAO;AACtB,OAAK,QAAQ,OAAO,SAAS,QAAQ,IAAI,gBAAgB;AACzD,OAAK,QAAQ,OAAO;AACpB,OAAK,QAAQ,IAAIC,aAAM,EACrB,aAAa,GACd,CAAC;;CAGJ,MAAgB,QAAQ,MAAc,MAAsB;EAC1D,MAAM,MAAM,IAAI,IAAI,GAAG,KAAK,UAAU,OAAO;AAC7C,MAAI,MAAM,MACR,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,MAAM,CACnD,qBAAM,MAAM,CAAC,SAAS,YAAU;AAC9B,OAAI,aAAa,OAAO,KAAKC,QAAM,UAAU,CAAC;IAC9C;EAIN,MAAM,QAAQ,KAAK,KAAK;EACxB,MAAM,WAAW,MAAM,KAAK,MAAM,IAAI,UAAU,EAAE;GAChD,GAAG;GACH,MAAM,MAAM;GACZ,QAAQ,MAAM,UAAU;GACxB,SAAS,KAAK,QACV;IAAE,eAAe,UAAU,KAAK;IAAS,GAAG,MAAM;IAAS,GAC3D,MAAM;GAEV,YAAY,KAAK;GACjB,QAAQ,MAAM;GACf,CAAC;AAEF,MAAI,KAAK,OAAO;GACd,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,WAAQ,IAAI,SAAS,IAAI,IAAI,SAAS,OAAO,IAAI,SAAS,IAAI;AAC9D,OAAI,SAAS,WAAW,KAAK;IAC3B,MAAM,QAAQ,SAAS,SAAS,QAAQ,IAAI,cAAc,IAAI,IAAI,GAAG;IACrE,MAAM,QAAQ,KAAK,MAAM,QAAQ,KAAK,GAAG;IACzC,MAAM,UAAU,KAAK,MAAM,QAAQ,GAAG,GAAG;IACzC,MAAM,UAAU,QAAQ;AACxB,YAAQ,KACN,SAAS,IAAI,6BAA6B,MAAM,IAAI,QAAQ,IAAI,QAAQ,GACzE;;;AAIL,SAAO;;;;;;AAaX,SAAS,iBAAiB,KAAiC;AAEzD,QADc,IAAI,MAAM,sCAAsC,GAC/C;;;;;;AAOjB,SAAS,mBAAmB,KAAiC;AAE3D,QADc,IAAI,MAAM,2EAA2E,GACpF;;;;;;;;;AAUjB,eAAsB,MACpB,WACA,UAC6C;CAC7C,MAAM,YAAY,iBAAiB,SAAS,IAAI;CAChD,IAAIC;AACJ,KAAI,CAAC,UACH,eAAc,mBAAmB,SAAS,IAAI;CAGhD,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,OAAO,QAAQ;AAChD,SAAO,IAAIC,2BAAoB,UAAU;GACvC,SAAS,6BAA6B,OAAO,IAAI;GACjD;GACA;GACD,CAAC;GACF;AAEF,KAAI,OAAO,SAAS,SAClB,QAAO;CAGT,IAAIC;AAEJ,KAAI;AACF,SAAO,KAAK,MAAM,QAAQ,KAAK;UACxB,OAAO;AACd,SAAO,IAAID,2BAAoB,UAAU;GACvC,SAAS,qBAAqB,OAAO,MAAM;GAC3C;GACA;GACA;GACD,CAAC;;AAGJ,KAAI,CAAC,SAAS,GACZ,QAAO,IAAIA,2BAAoB,UAAU;EACvC,SAAS,eAAe,SAAS,OAAO;EAClC;EACN;EACA;EACA;EACD,CAAC;CAGJ,MAAM,YAAY,UAAU,UAAU,KAAK;AAC3C,KAAI,CAAC,UAAU,QACb,QAAO,IAAIA,2BAAoB,UAAU;EACvC,SAAS,+BAA+B,UAAU;EAC5C;EACN;EACA;EACA;EACD,CAAC;AAGJ,QAAO;EACL,MAAM,UAAU;EAChB;EACA;EACD;;AAGH,eAAsB,aACpB,WACA,UACuB;CACvB,MAAM,SAAS,MAAM,MAAuB,WAAW,SAAS;AAChE,KAAI,kBAAkBA,2BACpB,OAAM;AAGR,QAAO"}
{"version":3,"file":"base-client.cjs","names":["Agent","withRetry","value","sandboxName: string | undefined","APIError","json: Data | ErrorData"],"sources":["../../src/api-client/base-client.ts"],"sourcesContent":["import type { Options as RetryOptions } from \"async-retry\";\nimport { APIError } from \"./api-error.js\";\nimport { ZodType } from \"zod\";\nimport { array } from \"../utils/array.js\";\nimport { withRetry, type RequestOptions } from \"./with-retry.js\";\nimport { Agent } from \"undici\";\n\nexport interface RequestParams extends RequestInit {\n headers?: Record<string, string>;\n method?: string;\n onRetry?(error: any, options: RequestOptions): void;\n query?: Record<string, number | string | null | undefined | string[]>;\n retry?: Partial<RetryOptions>;\n}\n\nconst DEFAULT_AGENT = new Agent({\n bodyTimeout: 0, // disable body timeout to allow long logs streaming\n});\n\n/**\n * A base API client that provides a convenience wrapper for fetching where\n * we can pass query parameters as an object, support retries, debugging\n * and automatic authorization.\n */\nexport class BaseClient {\n protected token?: string;\n private fetch: ReturnType<typeof withRetry<RequestInit>>;\n private debug: boolean;\n private baseUrl: string;\n private agent: Agent;\n\n constructor(params: {\n debug?: boolean;\n baseUrl: string;\n token?: string;\n fetch?: typeof globalThis.fetch;\n }) {\n this.fetch = withRetry(params.fetch ?? globalThis.fetch);\n this.baseUrl = params.baseUrl;\n this.debug = params.debug ?? process.env.DEBUG_FETCH === \"true\";\n this.token = params.token;\n this.agent = DEFAULT_AGENT;\n }\n\n protected async request(path: string, opts?: RequestParams) {\n const url = new URL(`${this.baseUrl}${path}`);\n if (opts?.query) {\n for (const [key, value] of Object.entries(opts.query)) {\n array(value).forEach((value) => {\n url.searchParams.append(key, value.toString());\n });\n }\n }\n\n const start = Date.now();\n const response = await this.fetch(url.toString(), {\n ...opts,\n body: opts?.body,\n method: opts?.method || \"GET\",\n headers: this.token\n ? { Authorization: `Bearer ${this.token}`, ...opts?.headers }\n : opts?.headers,\n // @ts-expect-error Node.js' and undici's Agent have different types\n dispatcher: this.agent,\n signal: opts?.signal,\n });\n\n if (this.debug) {\n const duration = Date.now() - start;\n console.log(`[API] ${url} (${response.status}) ${duration}ms`);\n if (response.status === 429) {\n const retry = parseInt(response.headers.get(\"Retry-After\") ?? \"\", 10);\n const hours = Math.floor(retry / 60 / 60);\n const minutes = Math.floor(retry / 60) % 60;\n const seconds = retry % 60;\n console.warn(\n `[API] ${url} Rate Limited, Retry After ${hours}h ${minutes}m ${seconds}s`,\n );\n }\n }\n\n return response;\n }\n}\n\nexport interface Parsed<Data> {\n response: Response;\n text: string;\n json: Data;\n}\n\n/**\n * Extract sessionId from a sandbox API URL.\n */\nfunction extractSessionId(url: string): string | undefined {\n const match = url.match(/\\/v2\\/sandboxes\\/sessions\\/([^/?]+)/);\n return match?.[1];\n}\n\n/**\n * Extract sandbox name from a sandbox API url.\n * Excludes known sub-paths like /sessions/ and /snapshots/.\n */\nfunction extractSandboxName(url: string): string | undefined {\n const match = url.match(/\\/v2\\/sandboxes\\/(?!sessions(?:\\/|$|\\?))(?!snapshots(?:\\/|$|\\?))([^/?]+)/);\n return match?.[1];\n}\n\n/**\n * Allows to read the response text and parse it as JSON casting to the given\n * type. If the response is not ok or cannot be parsed it will return error.\n *\n * @param response Response to parse.\n * @returns Parsed response or error.\n */\nexport async function parse<Data, ErrorData>(\n validator: ZodType<Data>,\n response: Response,\n): Promise<Parsed<Data> | APIError<ErrorData>> {\n const sessionId = extractSessionId(response.url);\n let sandboxName: string | undefined;\n if (!sessionId) {\n sandboxName = extractSandboxName(response.url);\n }\n\n const text = await response.text().catch((err) => {\n return new APIError<ErrorData>(response, {\n message: `Can't read response text: ${String(err)}`,\n sessionId,\n sandboxName\n });\n });\n\n if (typeof text !== \"string\") {\n return text;\n }\n\n let json: Data | ErrorData;\n\n try {\n json = JSON.parse(text || \"{}\");\n } catch (error) {\n return new APIError<ErrorData>(response, {\n message: `Can't parse JSON: ${String(error)}`,\n text,\n sessionId,\n sandboxName\n });\n }\n\n if (!response.ok) {\n return new APIError<ErrorData>(response, {\n message: `Status code ${response.status} is not ok`,\n json: json as ErrorData,\n text,\n sessionId,\n sandboxName\n });\n }\n\n const validated = validator.safeParse(json);\n if (!validated.success) {\n return new APIError<ErrorData>(response, {\n message: `Response JSON is not valid: ${validated.error}`,\n json: json as ErrorData,\n text,\n sessionId,\n sandboxName\n });\n }\n\n return {\n json: validated.data,\n response,\n text,\n };\n}\n\nexport async function parseOrThrow<Data, ErrorData>(\n validator: ZodType<Data>,\n response: Response,\n): Promise<Parsed<Data>> {\n const result = await parse<Data, ErrorData>(validator, response);\n if (result instanceof APIError) {\n throw result;\n }\n\n return result;\n}\n"],"mappings":";;;;;;;AAeA,MAAM,gBAAgB,IAAIA,aAAM,EAC9B,aAAa,GACd,CAAC;;;;;;AAOF,IAAa,aAAb,MAAwB;CAOtB,YAAY,QAKT;AACD,OAAK,QAAQC,6BAAU,OAAO,SAAS,WAAW,MAAM;AACxD,OAAK,UAAU,OAAO;AACtB,OAAK,QAAQ,OAAO,SAAS,QAAQ,IAAI,gBAAgB;AACzD,OAAK,QAAQ,OAAO;AACpB,OAAK,QAAQ;;CAGf,MAAgB,QAAQ,MAAc,MAAsB;EAC1D,MAAM,MAAM,IAAI,IAAI,GAAG,KAAK,UAAU,OAAO;AAC7C,MAAI,MAAM,MACR,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,MAAM,CACnD,qBAAM,MAAM,CAAC,SAAS,YAAU;AAC9B,OAAI,aAAa,OAAO,KAAKC,QAAM,UAAU,CAAC;IAC9C;EAIN,MAAM,QAAQ,KAAK,KAAK;EACxB,MAAM,WAAW,MAAM,KAAK,MAAM,IAAI,UAAU,EAAE;GAChD,GAAG;GACH,MAAM,MAAM;GACZ,QAAQ,MAAM,UAAU;GACxB,SAAS,KAAK,QACV;IAAE,eAAe,UAAU,KAAK;IAAS,GAAG,MAAM;IAAS,GAC3D,MAAM;GAEV,YAAY,KAAK;GACjB,QAAQ,MAAM;GACf,CAAC;AAEF,MAAI,KAAK,OAAO;GACd,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,WAAQ,IAAI,SAAS,IAAI,IAAI,SAAS,OAAO,IAAI,SAAS,IAAI;AAC9D,OAAI,SAAS,WAAW,KAAK;IAC3B,MAAM,QAAQ,SAAS,SAAS,QAAQ,IAAI,cAAc,IAAI,IAAI,GAAG;IACrE,MAAM,QAAQ,KAAK,MAAM,QAAQ,KAAK,GAAG;IACzC,MAAM,UAAU,KAAK,MAAM,QAAQ,GAAG,GAAG;IACzC,MAAM,UAAU,QAAQ;AACxB,YAAQ,KACN,SAAS,IAAI,6BAA6B,MAAM,IAAI,QAAQ,IAAI,QAAQ,GACzE;;;AAIL,SAAO;;;;;;AAaX,SAAS,iBAAiB,KAAiC;AAEzD,QADc,IAAI,MAAM,sCAAsC,GAC/C;;;;;;AAOjB,SAAS,mBAAmB,KAAiC;AAE3D,QADc,IAAI,MAAM,2EAA2E,GACpF;;;;;;;;;AAUjB,eAAsB,MACpB,WACA,UAC6C;CAC7C,MAAM,YAAY,iBAAiB,SAAS,IAAI;CAChD,IAAIC;AACJ,KAAI,CAAC,UACH,eAAc,mBAAmB,SAAS,IAAI;CAGhD,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,OAAO,QAAQ;AAChD,SAAO,IAAIC,2BAAoB,UAAU;GACvC,SAAS,6BAA6B,OAAO,IAAI;GACjD;GACA;GACD,CAAC;GACF;AAEF,KAAI,OAAO,SAAS,SAClB,QAAO;CAGT,IAAIC;AAEJ,KAAI;AACF,SAAO,KAAK,MAAM,QAAQ,KAAK;UACxB,OAAO;AACd,SAAO,IAAID,2BAAoB,UAAU;GACvC,SAAS,qBAAqB,OAAO,MAAM;GAC3C;GACA;GACA;GACD,CAAC;;AAGJ,KAAI,CAAC,SAAS,GACZ,QAAO,IAAIA,2BAAoB,UAAU;EACvC,SAAS,eAAe,SAAS,OAAO;EAClC;EACN;EACA;EACA;EACD,CAAC;CAGJ,MAAM,YAAY,UAAU,UAAU,KAAK;AAC3C,KAAI,CAAC,UAAU,QACb,QAAO,IAAIA,2BAAoB,UAAU;EACvC,SAAS,+BAA+B,UAAU;EAC5C;EACN;EACA;EACA;EACD,CAAC;AAGJ,QAAO;EACL,MAAM,UAAU;EAChB;EACA;EACD;;AAGH,eAAsB,aACpB,WACA,UACuB;CACvB,MAAM,SAAS,MAAM,MAAuB,WAAW,SAAS;AAChE,KAAI,kBAAkBA,2BACpB,OAAM;AAGR,QAAO"}

@@ -7,2 +7,3 @@ import { APIError } from "./api-error.js";

//#region src/api-client/base-client.ts
const DEFAULT_AGENT = new Agent({ bodyTimeout: 0 });
/**

@@ -19,3 +20,3 @@ * A base API client that provides a convenience wrapper for fetching where

this.token = params.token;
this.agent = new Agent({ bodyTimeout: 0 });
this.agent = DEFAULT_AGENT;
}

@@ -22,0 +23,0 @@ async request(path, opts) {

@@ -1,1 +0,1 @@

{"version":3,"file":"base-client.js","names":["value","sandboxName: string | undefined","json: Data | ErrorData"],"sources":["../../src/api-client/base-client.ts"],"sourcesContent":["import type { Options as RetryOptions } from \"async-retry\";\nimport { APIError } from \"./api-error.js\";\nimport { ZodType } from \"zod\";\nimport { array } from \"../utils/array.js\";\nimport { withRetry, type RequestOptions } from \"./with-retry.js\";\nimport { Agent } from \"undici\";\n\nexport interface RequestParams extends RequestInit {\n headers?: Record<string, string>;\n method?: string;\n onRetry?(error: any, options: RequestOptions): void;\n query?: Record<string, number | string | null | undefined | string[]>;\n retry?: Partial<RetryOptions>;\n}\n\n/**\n * A base API client that provides a convenience wrapper for fetching where\n * we can pass query parameters as an object, support retries, debugging\n * and automatic authorization.\n */\nexport class BaseClient {\n protected token?: string;\n private fetch: ReturnType<typeof withRetry<RequestInit>>;\n private debug: boolean;\n private baseUrl: string;\n private agent: Agent;\n\n constructor(params: {\n debug?: boolean;\n baseUrl: string;\n token?: string;\n fetch?: typeof globalThis.fetch;\n }) {\n this.fetch = withRetry(params.fetch ?? globalThis.fetch);\n this.baseUrl = params.baseUrl;\n this.debug = params.debug ?? process.env.DEBUG_FETCH === \"true\";\n this.token = params.token;\n this.agent = new Agent({\n bodyTimeout: 0, // disable body timeout to allow long logs streaming\n });\n }\n\n protected async request(path: string, opts?: RequestParams) {\n const url = new URL(`${this.baseUrl}${path}`);\n if (opts?.query) {\n for (const [key, value] of Object.entries(opts.query)) {\n array(value).forEach((value) => {\n url.searchParams.append(key, value.toString());\n });\n }\n }\n\n const start = Date.now();\n const response = await this.fetch(url.toString(), {\n ...opts,\n body: opts?.body,\n method: opts?.method || \"GET\",\n headers: this.token\n ? { Authorization: `Bearer ${this.token}`, ...opts?.headers }\n : opts?.headers,\n // @ts-expect-error Node.js' and undici's Agent have different types\n dispatcher: this.agent,\n signal: opts?.signal,\n });\n\n if (this.debug) {\n const duration = Date.now() - start;\n console.log(`[API] ${url} (${response.status}) ${duration}ms`);\n if (response.status === 429) {\n const retry = parseInt(response.headers.get(\"Retry-After\") ?? \"\", 10);\n const hours = Math.floor(retry / 60 / 60);\n const minutes = Math.floor(retry / 60) % 60;\n const seconds = retry % 60;\n console.warn(\n `[API] ${url} Rate Limited, Retry After ${hours}h ${minutes}m ${seconds}s`,\n );\n }\n }\n\n return response;\n }\n}\n\nexport interface Parsed<Data> {\n response: Response;\n text: string;\n json: Data;\n}\n\n/**\n * Extract sessionId from a sandbox API URL.\n */\nfunction extractSessionId(url: string): string | undefined {\n const match = url.match(/\\/v2\\/sandboxes\\/sessions\\/([^/?]+)/);\n return match?.[1];\n}\n\n/**\n * Extract sandbox name from a sandbox API url.\n * Excludes known sub-paths like /sessions/ and /snapshots/.\n */\nfunction extractSandboxName(url: string): string | undefined {\n const match = url.match(/\\/v2\\/sandboxes\\/(?!sessions(?:\\/|$|\\?))(?!snapshots(?:\\/|$|\\?))([^/?]+)/);\n return match?.[1];\n}\n\n/**\n * Allows to read the response text and parse it as JSON casting to the given\n * type. If the response is not ok or cannot be parsed it will return error.\n *\n * @param response Response to parse.\n * @returns Parsed response or error.\n */\nexport async function parse<Data, ErrorData>(\n validator: ZodType<Data>,\n response: Response,\n): Promise<Parsed<Data> | APIError<ErrorData>> {\n const sessionId = extractSessionId(response.url);\n let sandboxName: string | undefined;\n if (!sessionId) {\n sandboxName = extractSandboxName(response.url);\n }\n\n const text = await response.text().catch((err) => {\n return new APIError<ErrorData>(response, {\n message: `Can't read response text: ${String(err)}`,\n sessionId,\n sandboxName\n });\n });\n\n if (typeof text !== \"string\") {\n return text;\n }\n\n let json: Data | ErrorData;\n\n try {\n json = JSON.parse(text || \"{}\");\n } catch (error) {\n return new APIError<ErrorData>(response, {\n message: `Can't parse JSON: ${String(error)}`,\n text,\n sessionId,\n sandboxName\n });\n }\n\n if (!response.ok) {\n return new APIError<ErrorData>(response, {\n message: `Status code ${response.status} is not ok`,\n json: json as ErrorData,\n text,\n sessionId,\n sandboxName\n });\n }\n\n const validated = validator.safeParse(json);\n if (!validated.success) {\n return new APIError<ErrorData>(response, {\n message: `Response JSON is not valid: ${validated.error}`,\n json: json as ErrorData,\n text,\n sessionId,\n sandboxName\n });\n }\n\n return {\n json: validated.data,\n response,\n text,\n };\n}\n\nexport async function parseOrThrow<Data, ErrorData>(\n validator: ZodType<Data>,\n response: Response,\n): Promise<Parsed<Data>> {\n const result = await parse<Data, ErrorData>(validator, response);\n if (result instanceof APIError) {\n throw result;\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;AAoBA,IAAa,aAAb,MAAwB;CAOtB,YAAY,QAKT;AACD,OAAK,QAAQ,UAAU,OAAO,SAAS,WAAW,MAAM;AACxD,OAAK,UAAU,OAAO;AACtB,OAAK,QAAQ,OAAO,SAAS,QAAQ,IAAI,gBAAgB;AACzD,OAAK,QAAQ,OAAO;AACpB,OAAK,QAAQ,IAAI,MAAM,EACrB,aAAa,GACd,CAAC;;CAGJ,MAAgB,QAAQ,MAAc,MAAsB;EAC1D,MAAM,MAAM,IAAI,IAAI,GAAG,KAAK,UAAU,OAAO;AAC7C,MAAI,MAAM,MACR,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,MAAM,CACnD,OAAM,MAAM,CAAC,SAAS,YAAU;AAC9B,OAAI,aAAa,OAAO,KAAKA,QAAM,UAAU,CAAC;IAC9C;EAIN,MAAM,QAAQ,KAAK,KAAK;EACxB,MAAM,WAAW,MAAM,KAAK,MAAM,IAAI,UAAU,EAAE;GAChD,GAAG;GACH,MAAM,MAAM;GACZ,QAAQ,MAAM,UAAU;GACxB,SAAS,KAAK,QACV;IAAE,eAAe,UAAU,KAAK;IAAS,GAAG,MAAM;IAAS,GAC3D,MAAM;GAEV,YAAY,KAAK;GACjB,QAAQ,MAAM;GACf,CAAC;AAEF,MAAI,KAAK,OAAO;GACd,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,WAAQ,IAAI,SAAS,IAAI,IAAI,SAAS,OAAO,IAAI,SAAS,IAAI;AAC9D,OAAI,SAAS,WAAW,KAAK;IAC3B,MAAM,QAAQ,SAAS,SAAS,QAAQ,IAAI,cAAc,IAAI,IAAI,GAAG;IACrE,MAAM,QAAQ,KAAK,MAAM,QAAQ,KAAK,GAAG;IACzC,MAAM,UAAU,KAAK,MAAM,QAAQ,GAAG,GAAG;IACzC,MAAM,UAAU,QAAQ;AACxB,YAAQ,KACN,SAAS,IAAI,6BAA6B,MAAM,IAAI,QAAQ,IAAI,QAAQ,GACzE;;;AAIL,SAAO;;;;;;AAaX,SAAS,iBAAiB,KAAiC;AAEzD,QADc,IAAI,MAAM,sCAAsC,GAC/C;;;;;;AAOjB,SAAS,mBAAmB,KAAiC;AAE3D,QADc,IAAI,MAAM,2EAA2E,GACpF;;;;;;;;;AAUjB,eAAsB,MACpB,WACA,UAC6C;CAC7C,MAAM,YAAY,iBAAiB,SAAS,IAAI;CAChD,IAAIC;AACJ,KAAI,CAAC,UACH,eAAc,mBAAmB,SAAS,IAAI;CAGhD,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,OAAO,QAAQ;AAChD,SAAO,IAAI,SAAoB,UAAU;GACvC,SAAS,6BAA6B,OAAO,IAAI;GACjD;GACA;GACD,CAAC;GACF;AAEF,KAAI,OAAO,SAAS,SAClB,QAAO;CAGT,IAAIC;AAEJ,KAAI;AACF,SAAO,KAAK,MAAM,QAAQ,KAAK;UACxB,OAAO;AACd,SAAO,IAAI,SAAoB,UAAU;GACvC,SAAS,qBAAqB,OAAO,MAAM;GAC3C;GACA;GACA;GACD,CAAC;;AAGJ,KAAI,CAAC,SAAS,GACZ,QAAO,IAAI,SAAoB,UAAU;EACvC,SAAS,eAAe,SAAS,OAAO;EAClC;EACN;EACA;EACA;EACD,CAAC;CAGJ,MAAM,YAAY,UAAU,UAAU,KAAK;AAC3C,KAAI,CAAC,UAAU,QACb,QAAO,IAAI,SAAoB,UAAU;EACvC,SAAS,+BAA+B,UAAU;EAC5C;EACN;EACA;EACA;EACD,CAAC;AAGJ,QAAO;EACL,MAAM,UAAU;EAChB;EACA;EACD;;AAGH,eAAsB,aACpB,WACA,UACuB;CACvB,MAAM,SAAS,MAAM,MAAuB,WAAW,SAAS;AAChE,KAAI,kBAAkB,SACpB,OAAM;AAGR,QAAO"}
{"version":3,"file":"base-client.js","names":["value","sandboxName: string | undefined","json: Data | ErrorData"],"sources":["../../src/api-client/base-client.ts"],"sourcesContent":["import type { Options as RetryOptions } from \"async-retry\";\nimport { APIError } from \"./api-error.js\";\nimport { ZodType } from \"zod\";\nimport { array } from \"../utils/array.js\";\nimport { withRetry, type RequestOptions } from \"./with-retry.js\";\nimport { Agent } from \"undici\";\n\nexport interface RequestParams extends RequestInit {\n headers?: Record<string, string>;\n method?: string;\n onRetry?(error: any, options: RequestOptions): void;\n query?: Record<string, number | string | null | undefined | string[]>;\n retry?: Partial<RetryOptions>;\n}\n\nconst DEFAULT_AGENT = new Agent({\n bodyTimeout: 0, // disable body timeout to allow long logs streaming\n});\n\n/**\n * A base API client that provides a convenience wrapper for fetching where\n * we can pass query parameters as an object, support retries, debugging\n * and automatic authorization.\n */\nexport class BaseClient {\n protected token?: string;\n private fetch: ReturnType<typeof withRetry<RequestInit>>;\n private debug: boolean;\n private baseUrl: string;\n private agent: Agent;\n\n constructor(params: {\n debug?: boolean;\n baseUrl: string;\n token?: string;\n fetch?: typeof globalThis.fetch;\n }) {\n this.fetch = withRetry(params.fetch ?? globalThis.fetch);\n this.baseUrl = params.baseUrl;\n this.debug = params.debug ?? process.env.DEBUG_FETCH === \"true\";\n this.token = params.token;\n this.agent = DEFAULT_AGENT;\n }\n\n protected async request(path: string, opts?: RequestParams) {\n const url = new URL(`${this.baseUrl}${path}`);\n if (opts?.query) {\n for (const [key, value] of Object.entries(opts.query)) {\n array(value).forEach((value) => {\n url.searchParams.append(key, value.toString());\n });\n }\n }\n\n const start = Date.now();\n const response = await this.fetch(url.toString(), {\n ...opts,\n body: opts?.body,\n method: opts?.method || \"GET\",\n headers: this.token\n ? { Authorization: `Bearer ${this.token}`, ...opts?.headers }\n : opts?.headers,\n // @ts-expect-error Node.js' and undici's Agent have different types\n dispatcher: this.agent,\n signal: opts?.signal,\n });\n\n if (this.debug) {\n const duration = Date.now() - start;\n console.log(`[API] ${url} (${response.status}) ${duration}ms`);\n if (response.status === 429) {\n const retry = parseInt(response.headers.get(\"Retry-After\") ?? \"\", 10);\n const hours = Math.floor(retry / 60 / 60);\n const minutes = Math.floor(retry / 60) % 60;\n const seconds = retry % 60;\n console.warn(\n `[API] ${url} Rate Limited, Retry After ${hours}h ${minutes}m ${seconds}s`,\n );\n }\n }\n\n return response;\n }\n}\n\nexport interface Parsed<Data> {\n response: Response;\n text: string;\n json: Data;\n}\n\n/**\n * Extract sessionId from a sandbox API URL.\n */\nfunction extractSessionId(url: string): string | undefined {\n const match = url.match(/\\/v2\\/sandboxes\\/sessions\\/([^/?]+)/);\n return match?.[1];\n}\n\n/**\n * Extract sandbox name from a sandbox API url.\n * Excludes known sub-paths like /sessions/ and /snapshots/.\n */\nfunction extractSandboxName(url: string): string | undefined {\n const match = url.match(/\\/v2\\/sandboxes\\/(?!sessions(?:\\/|$|\\?))(?!snapshots(?:\\/|$|\\?))([^/?]+)/);\n return match?.[1];\n}\n\n/**\n * Allows to read the response text and parse it as JSON casting to the given\n * type. If the response is not ok or cannot be parsed it will return error.\n *\n * @param response Response to parse.\n * @returns Parsed response or error.\n */\nexport async function parse<Data, ErrorData>(\n validator: ZodType<Data>,\n response: Response,\n): Promise<Parsed<Data> | APIError<ErrorData>> {\n const sessionId = extractSessionId(response.url);\n let sandboxName: string | undefined;\n if (!sessionId) {\n sandboxName = extractSandboxName(response.url);\n }\n\n const text = await response.text().catch((err) => {\n return new APIError<ErrorData>(response, {\n message: `Can't read response text: ${String(err)}`,\n sessionId,\n sandboxName\n });\n });\n\n if (typeof text !== \"string\") {\n return text;\n }\n\n let json: Data | ErrorData;\n\n try {\n json = JSON.parse(text || \"{}\");\n } catch (error) {\n return new APIError<ErrorData>(response, {\n message: `Can't parse JSON: ${String(error)}`,\n text,\n sessionId,\n sandboxName\n });\n }\n\n if (!response.ok) {\n return new APIError<ErrorData>(response, {\n message: `Status code ${response.status} is not ok`,\n json: json as ErrorData,\n text,\n sessionId,\n sandboxName\n });\n }\n\n const validated = validator.safeParse(json);\n if (!validated.success) {\n return new APIError<ErrorData>(response, {\n message: `Response JSON is not valid: ${validated.error}`,\n json: json as ErrorData,\n text,\n sessionId,\n sandboxName\n });\n }\n\n return {\n json: validated.data,\n response,\n text,\n };\n}\n\nexport async function parseOrThrow<Data, ErrorData>(\n validator: ZodType<Data>,\n response: Response,\n): Promise<Parsed<Data>> {\n const result = await parse<Data, ErrorData>(validator, response);\n if (result instanceof APIError) {\n throw result;\n }\n\n return result;\n}\n"],"mappings":";;;;;;AAeA,MAAM,gBAAgB,IAAI,MAAM,EAC9B,aAAa,GACd,CAAC;;;;;;AAOF,IAAa,aAAb,MAAwB;CAOtB,YAAY,QAKT;AACD,OAAK,QAAQ,UAAU,OAAO,SAAS,WAAW,MAAM;AACxD,OAAK,UAAU,OAAO;AACtB,OAAK,QAAQ,OAAO,SAAS,QAAQ,IAAI,gBAAgB;AACzD,OAAK,QAAQ,OAAO;AACpB,OAAK,QAAQ;;CAGf,MAAgB,QAAQ,MAAc,MAAsB;EAC1D,MAAM,MAAM,IAAI,IAAI,GAAG,KAAK,UAAU,OAAO;AAC7C,MAAI,MAAM,MACR,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,MAAM,CACnD,OAAM,MAAM,CAAC,SAAS,YAAU;AAC9B,OAAI,aAAa,OAAO,KAAKA,QAAM,UAAU,CAAC;IAC9C;EAIN,MAAM,QAAQ,KAAK,KAAK;EACxB,MAAM,WAAW,MAAM,KAAK,MAAM,IAAI,UAAU,EAAE;GAChD,GAAG;GACH,MAAM,MAAM;GACZ,QAAQ,MAAM,UAAU;GACxB,SAAS,KAAK,QACV;IAAE,eAAe,UAAU,KAAK;IAAS,GAAG,MAAM;IAAS,GAC3D,MAAM;GAEV,YAAY,KAAK;GACjB,QAAQ,MAAM;GACf,CAAC;AAEF,MAAI,KAAK,OAAO;GACd,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,WAAQ,IAAI,SAAS,IAAI,IAAI,SAAS,OAAO,IAAI,SAAS,IAAI;AAC9D,OAAI,SAAS,WAAW,KAAK;IAC3B,MAAM,QAAQ,SAAS,SAAS,QAAQ,IAAI,cAAc,IAAI,IAAI,GAAG;IACrE,MAAM,QAAQ,KAAK,MAAM,QAAQ,KAAK,GAAG;IACzC,MAAM,UAAU,KAAK,MAAM,QAAQ,GAAG,GAAG;IACzC,MAAM,UAAU,QAAQ;AACxB,YAAQ,KACN,SAAS,IAAI,6BAA6B,MAAM,IAAI,QAAQ,IAAI,QAAQ,GACzE;;;AAIL,SAAO;;;;;;AAaX,SAAS,iBAAiB,KAAiC;AAEzD,QADc,IAAI,MAAM,sCAAsC,GAC/C;;;;;;AAOjB,SAAS,mBAAmB,KAAiC;AAE3D,QADc,IAAI,MAAM,2EAA2E,GACpF;;;;;;;;;AAUjB,eAAsB,MACpB,WACA,UAC6C;CAC7C,MAAM,YAAY,iBAAiB,SAAS,IAAI;CAChD,IAAIC;AACJ,KAAI,CAAC,UACH,eAAc,mBAAmB,SAAS,IAAI;CAGhD,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,OAAO,QAAQ;AAChD,SAAO,IAAI,SAAoB,UAAU;GACvC,SAAS,6BAA6B,OAAO,IAAI;GACjD;GACA;GACD,CAAC;GACF;AAEF,KAAI,OAAO,SAAS,SAClB,QAAO;CAGT,IAAIC;AAEJ,KAAI;AACF,SAAO,KAAK,MAAM,QAAQ,KAAK;UACxB,OAAO;AACd,SAAO,IAAI,SAAoB,UAAU;GACvC,SAAS,qBAAqB,OAAO,MAAM;GAC3C;GACA;GACA;GACD,CAAC;;AAGJ,KAAI,CAAC,SAAS,GACZ,QAAO,IAAI,SAAoB,UAAU;EACvC,SAAS,eAAe,SAAS,OAAO;EAClC;EACN;EACA;EACA;EACD,CAAC;CAGJ,MAAM,YAAY,UAAU,UAAU,KAAK;AAC3C,KAAI,CAAC,UAAU,QACb,QAAO,IAAI,SAAoB,UAAU;EACvC,SAAS,+BAA+B,UAAU;EAC5C;EACN;EACA;EACA;EACD,CAAC;AAGJ,QAAO;EACL,MAAM,UAAU;EAChB;EACA;EACD;;AAGH,eAAsB,aACpB,WACA,UACuB;CACvB,MAAM,SAAS,MAAM,MAAuB,WAAW,SAAS;AAChE,KAAI,kBAAkB,SACpB,OAAM;AAGR,QAAO"}

@@ -1,2 +0,2 @@

import { Command, CommandData, CommandFinishedData, CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, LogLineStderr, LogLineStdout, Sandbox, SandboxMetaData, SandboxRoute, SandboxRouteData, Session, SessionMetaData, SessionResponse, Snapshot, SnapshotMetadata, SnapshotResponse } from "./validators.js";
import { Command, CommandData, CommandFinishedData, CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, LogLineStderr, LogLineStdout, Sandbox, SandboxMetaData, SandboxRoute, SandboxRouteData, Session, SessionMetaData, SessionResponse, Snapshot, SnapshotMetadata, SnapshotResponse, StopSessionResponse } from "./validators.js";
import { APIClient } from "./api-client.js";

@@ -1,4 +0,4 @@

import { Command, CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, CursorPagination, EmptyResponse, InjectionRuleValidator, LogError, LogLine, LogLineStderr, LogLineStdout, NetworkPolicyValidator, Sandbox, SandboxAndSessionResponse, SandboxRoute, SandboxesPaginationResponse, Session, SessionAndRoutesResponse, SessionResponse, SessionsResponse, Snapshot, SnapshotResponse, SnapshotsResponse, UpdateSandboxResponse } from "./validators.js";
import { Command, CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, CursorPagination, EmptyResponse, ForwardRuleValidator, InjectionRuleValidator, LogError, LogLine, LogLineStderr, LogLineStdout, NetworkPolicyResponseValidator, NetworkPolicyRuleValidator, NetworkPolicyTransformValidator, Sandbox, SandboxAndSessionResponse, SandboxRoute, SandboxesPaginationResponse, Session, SessionAndRoutesResponse, SessionResponse, SessionsResponse, Snapshot, SnapshotResponse, SnapshotsResponse, StopSessionResponse, UpdateSandboxResponse, V2NetworkPolicyObjectValidator } from "./validators.js";
import { APIClient } from "./api-client.js";
export { };

@@ -5,18 +5,52 @@ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');

//#region src/api-client/validators.ts
const RuleMatcherValidator = zod.z.object({
exact: zod.z.string().optional(),
startsWith: zod.z.string().optional(),
regex: zod.z.string().optional()
});
const KeyValueMatcherValidator = zod.z.object({
key: RuleMatcherValidator.optional(),
value: RuleMatcherValidator.optional()
});
const RuleMatchValidator = zod.z.object({
path: RuleMatcherValidator.optional(),
method: zod.z.array(zod.z.string()).optional(),
queryString: zod.z.array(KeyValueMatcherValidator).optional(),
headers: zod.z.array(KeyValueMatcherValidator).optional()
});
const InjectionRuleValidator = zod.z.object({
domain: zod.z.string(),
headers: zod.z.record(zod.z.string()).optional(),
headerNames: zod.z.array(zod.z.string()).optional()
headerNames: zod.z.array(zod.z.string()).optional(),
match: RuleMatchValidator.optional()
});
const NetworkPolicyValidator = zod.z.union([
zod.z.object({ mode: zod.z.literal("allow-all") }).passthrough(),
zod.z.object({ mode: zod.z.literal("deny-all") }).passthrough(),
zod.z.object({
mode: zod.z.literal("custom"),
allowedDomains: zod.z.array(zod.z.string()).optional(),
allowedCIDRs: zod.z.array(zod.z.string()).optional(),
deniedCIDRs: zod.z.array(zod.z.string()).optional(),
injectionRules: zod.z.array(InjectionRuleValidator).optional()
}).passthrough()
]);
const ForwardRuleValidator = zod.z.object({
domain: zod.z.string(),
forwardURL: zod.z.string(),
match: RuleMatchValidator.optional()
});
const NetworkPolicyTransformValidator = zod.z.object({ headers: zod.z.record(zod.z.string()).optional() });
const NetworkPolicyRuleValidator = zod.z.object({
match: RuleMatchValidator.optional(),
transform: zod.z.array(NetworkPolicyTransformValidator).optional(),
forwardURL: zod.z.string().optional()
});
const V2NetworkPolicyObjectValidator = zod.z.object({
allow: zod.z.union([zod.z.array(zod.z.string()), zod.z.record(zod.z.array(NetworkPolicyRuleValidator))]).optional(),
subnets: zod.z.object({
allow: zod.z.array(zod.z.string()).optional(),
deny: zod.z.array(zod.z.string()).optional()
}).optional()
});
const NetworkPolicyModeValidator = zod.z.union([zod.z.object({ mode: zod.z.literal("allow-all") }).passthrough(), zod.z.object({ mode: zod.z.literal("deny-all") }).passthrough()]);
const LegacyCustomNetworkPolicyValidator = zod.z.object({
mode: zod.z.literal("custom"),
allowedDomains: zod.z.array(zod.z.string()).optional(),
allowedCIDRs: zod.z.array(zod.z.string()).optional(),
deniedCIDRs: zod.z.array(zod.z.string()).optional(),
injectionRules: zod.z.array(InjectionRuleValidator).optional(),
forwardRules: zod.z.array(ForwardRuleValidator).optional()
}).passthrough();
const NetworkPolicyRequestValidator = zod.z.union([NetworkPolicyModeValidator, V2NetworkPolicyObjectValidator.passthrough()]);
const NetworkPolicyResponseValidator = zod.z.union([NetworkPolicyModeValidator, LegacyCustomNetworkPolicyValidator]);
const Session = zod.z.object({

@@ -50,3 +84,3 @@ id: zod.z.string(),

interactivePort: zod.z.number().optional(),
networkPolicy: NetworkPolicyValidator.optional(),
networkPolicy: NetworkPolicyResponseValidator.optional(),
activeCpuDurationMs: zod.z.number().optional(),

@@ -132,3 +166,3 @@ networkTransfer: zod.z.object({

timeout: zod.z.number().optional(),
networkPolicy: NetworkPolicyValidator.optional(),
networkPolicy: NetworkPolicyResponseValidator.optional(),
totalEgressBytes: zod.z.number().optional(),

@@ -148,2 +182,7 @@ totalIngressBytes: zod.z.number().optional(),

});
const StopSessionResponse = zod.z.object({
session: Session.passthrough(),
sandbox: Sandbox.optional(),
snapshot: Snapshot.optional()
});
const SandboxAndSessionResponse = zod.z.object({

@@ -168,2 +207,3 @@ sandbox: Sandbox,

exports.EmptyResponse = EmptyResponse;
exports.ForwardRuleValidator = ForwardRuleValidator;
exports.InjectionRuleValidator = InjectionRuleValidator;

@@ -174,3 +214,5 @@ exports.LogError = LogError;

exports.LogLineStdout = LogLineStdout;
exports.NetworkPolicyValidator = NetworkPolicyValidator;
exports.NetworkPolicyResponseValidator = NetworkPolicyResponseValidator;
exports.NetworkPolicyRuleValidator = NetworkPolicyRuleValidator;
exports.NetworkPolicyTransformValidator = NetworkPolicyTransformValidator;
exports.Sandbox = Sandbox;

@@ -187,3 +229,5 @@ exports.SandboxAndSessionResponse = SandboxAndSessionResponse;

exports.SnapshotsResponse = SnapshotsResponse;
exports.StopSessionResponse = StopSessionResponse;
exports.UpdateSandboxResponse = UpdateSandboxResponse;
exports.V2NetworkPolicyObjectValidator = V2NetworkPolicyObjectValidator;
//# sourceMappingURL=validators.cjs.map

@@ -1,1 +0,1 @@

{"version":3,"file":"validators.cjs","names":["z"],"sources":["../../src/api-client/validators.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport type SessionMetaData = z.infer<typeof Session>;\n\nexport const InjectionRuleValidator = z.object({\n domain: z.string(),\n // headers are only sent in requests\n headers: z.record(z.string()).optional(),\n // headerNames are returned in responses\n headerNames: z.array(z.string()).optional(),\n});\n\nexport const NetworkPolicyValidator = z.union([\n z.object({ mode: z.literal(\"allow-all\") }).passthrough(),\n z.object({ mode: z.literal(\"deny-all\") }).passthrough(),\n z\n .object({\n mode: z.literal(\"custom\"),\n allowedDomains: z.array(z.string()).optional(),\n allowedCIDRs: z.array(z.string()).optional(),\n deniedCIDRs: z.array(z.string()).optional(),\n injectionRules: z.array(InjectionRuleValidator).optional(),\n })\n .passthrough(),\n]);\n\nexport const Session = z.object({\n id: z.string(),\n memory: z.number(),\n vcpus: z.number(),\n region: z.string(),\n runtime: z.string(),\n timeout: z.number(),\n status: z.enum([\n \"pending\",\n \"running\",\n \"stopping\",\n \"stopped\",\n \"failed\",\n \"aborted\",\n \"snapshotting\",\n ]),\n requestedAt: z.number(),\n startedAt: z.number().optional(),\n requestedStopAt: z.number().optional(),\n stoppedAt: z.number().optional(),\n abortedAt: z.number().optional(),\n duration: z.number().optional(),\n sourceSnapshotId: z.string().optional(),\n snapshottedAt: z.number().optional(),\n createdAt: z.number(),\n cwd: z.string(),\n updatedAt: z.number(),\n interactivePort: z.number().optional(),\n networkPolicy: NetworkPolicyValidator.optional(),\n activeCpuDurationMs: z.number().optional(),\n networkTransfer: z.object({\n ingress: z.number(),\n egress: z.number(),\n }).optional(),\n});\n\nexport type SandboxRouteData = z.infer<typeof SandboxRoute>;\n\nexport const SandboxRoute = z.object({\n url: z.string(),\n subdomain: z.string(),\n port: z.number(),\n});\n\nexport type SnapshotMetadata = z.infer<typeof Snapshot>;\n\nexport const Snapshot = z.object({\n id: z.string(),\n sourceSessionId: z.string(),\n region: z.string(),\n status: z.enum([\"created\", \"deleted\", \"failed\"]),\n sizeBytes: z.number(),\n expiresAt: z.number().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n});\n\nexport const CursorPagination = z.object({\n count: z.number(),\n next: z.string().nullable(),\n});\n\nexport type CommandData = z.infer<typeof Command>;\n\nexport const Command = z.object({\n id: z.string(),\n name: z.string(),\n args: z.array(z.string()),\n cwd: z.string(),\n sessionId: z.string(),\n exitCode: z.number().nullable(),\n startedAt: z.number(),\n});\n\nconst CommandFinished = Command.extend({\n exitCode: z.number(),\n});\n\nexport const SessionResponse = z.object({\n session: Session.passthrough(),\n});\n\nexport const SessionAndRoutesResponse = SessionResponse.extend({\n routes: z.array(SandboxRoute),\n});\n\nexport const SessionsResponse = z.object({\n sessions: z.array(Session.passthrough()),\n pagination: CursorPagination,\n});\n\nexport const CommandResponse = z.object({\n command: Command,\n});\n\nexport type CommandFinishedData = z.infer<typeof CommandFinishedResponse>[\"command\"];\n\nexport const CommandFinishedResponse = z.object({\n command: CommandFinished,\n});\n\nexport const EmptyResponse = z.object({});\n\nconst LogLineBase = z.object({ data: z.string() });\nexport const LogLineStdout = LogLineBase.extend({\n stream: z.literal(\"stdout\"),\n});\nexport const LogLineStderr = LogLineBase.extend({\n stream: z.literal(\"stderr\"),\n});\n\nexport const LogError = z.object({\n stream: z.literal(\"error\"),\n data: z.object({\n code: z.string(),\n message: z.string(),\n }),\n});\n\nexport const LogLine = z.discriminatedUnion(\"stream\", [\n LogLineStdout,\n LogLineStderr,\n LogError,\n]);\n\nexport const SnapshotsResponse = z.object({\n snapshots: z.array(Snapshot),\n pagination: CursorPagination,\n});\n\nexport const CreateSnapshotResponse = z.object({\n snapshot: Snapshot,\n session: Session.passthrough(),\n});\n\nexport const SnapshotResponse = z.object({\n snapshot: Snapshot,\n});\n\nexport const Sandbox = z.object({\n name: z.string(),\n persistent: z.boolean(),\n region: z.string().optional(),\n vcpus: z.number().optional(),\n memory: z.number().optional(),\n runtime: z.string().optional(),\n timeout: z.number().optional(),\n networkPolicy: NetworkPolicyValidator.optional(),\n totalEgressBytes: z.number().optional(),\n totalIngressBytes: z.number().optional(),\n totalActiveCpuDurationMs: z.number().optional(),\n totalDurationMs: z.number().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n currentSessionId: z.string(),\n currentSnapshotId: z.string().optional(),\n status: Session.shape.status,\n statusUpdatedAt: z.number().optional(),\n cwd: z.string().optional(),\n tags: z.record(z.string()).optional(),\n snapshotExpiration: z.number().optional(),\n});\n\nexport type SandboxMetaData = z.infer<typeof Sandbox>;\n\nexport const SandboxAndSessionResponse = z.object({\n sandbox: Sandbox,\n session: Session.passthrough(),\n routes: z.array(SandboxRoute),\n resumed: z.boolean().optional(),\n});\n\nexport const SandboxesPaginationResponse = z.object({\n sandboxes: z.array(Sandbox),\n pagination: CursorPagination,\n});\n\nexport const UpdateSandboxResponse = z.object({\n sandbox: Sandbox,\n});\n"],"mappings":"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"}
{"version":3,"file":"validators.cjs","names":["z"],"sources":["../../src/api-client/validators.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport type SessionMetaData = z.infer<typeof Session>;\n\nconst RuleMatcherValidator = z.object({\n exact: z.string().optional(),\n startsWith: z.string().optional(),\n regex: z.string().optional(),\n});\n\nconst KeyValueMatcherValidator = z.object({\n key: RuleMatcherValidator.optional(),\n value: RuleMatcherValidator.optional(),\n});\n\nconst RuleMatchValidator = z.object({\n path: RuleMatcherValidator.optional(),\n method: z.array(z.string()).optional(),\n queryString: z.array(KeyValueMatcherValidator).optional(),\n headers: z.array(KeyValueMatcherValidator).optional(),\n});\n\nexport const InjectionRuleValidator = z.object({\n domain: z.string(),\n // headers are only sent in requests\n headers: z.record(z.string()).optional(),\n // headerNames are returned in responses\n headerNames: z.array(z.string()).optional(),\n match: RuleMatchValidator.optional(),\n});\n\nexport const ForwardRuleValidator = z.object({\n domain: z.string(),\n forwardURL: z.string(),\n match: RuleMatchValidator.optional(),\n});\n\nexport const NetworkPolicyTransformValidator = z.object({\n headers: z.record(z.string()).optional(),\n});\n\nexport const NetworkPolicyRuleValidator = z.object({\n match: RuleMatchValidator.optional(),\n transform: z.array(NetworkPolicyTransformValidator).optional(),\n forwardURL: z.string().optional(),\n});\n\nexport const V2NetworkPolicyObjectValidator = z.object({\n allow: z\n .union([\n z.array(z.string()),\n z.record(z.array(NetworkPolicyRuleValidator)),\n ])\n .optional(),\n subnets: z\n .object({\n allow: z.array(z.string()).optional(),\n deny: z.array(z.string()).optional(),\n })\n .optional(),\n});\n\nconst NetworkPolicyModeValidator = z.union([\n z.object({ mode: z.literal(\"allow-all\") }).passthrough(),\n z.object({ mode: z.literal(\"deny-all\") }).passthrough(),\n]);\n\nconst LegacyCustomNetworkPolicyValidator = z\n .object({\n mode: z.literal(\"custom\"),\n allowedDomains: z.array(z.string()).optional(),\n allowedCIDRs: z.array(z.string()).optional(),\n deniedCIDRs: z.array(z.string()).optional(),\n injectionRules: z.array(InjectionRuleValidator).optional(),\n forwardRules: z.array(ForwardRuleValidator).optional(),\n })\n .passthrough();\n\nexport const NetworkPolicyRequestValidator = z.union([\n NetworkPolicyModeValidator,\n V2NetworkPolicyObjectValidator.passthrough(),\n]);\n\nexport const NetworkPolicyResponseValidator = z.union([\n NetworkPolicyModeValidator,\n LegacyCustomNetworkPolicyValidator,\n]);\n\nexport const Session = z.object({\n id: z.string(),\n memory: z.number(),\n vcpus: z.number(),\n region: z.string(),\n runtime: z.string(),\n timeout: z.number(),\n status: z.enum([\n \"pending\",\n \"running\",\n \"stopping\",\n \"stopped\",\n \"failed\",\n \"aborted\",\n \"snapshotting\",\n ]),\n requestedAt: z.number(),\n startedAt: z.number().optional(),\n requestedStopAt: z.number().optional(),\n stoppedAt: z.number().optional(),\n abortedAt: z.number().optional(),\n duration: z.number().optional(),\n sourceSnapshotId: z.string().optional(),\n snapshottedAt: z.number().optional(),\n createdAt: z.number(),\n cwd: z.string(),\n updatedAt: z.number(),\n interactivePort: z.number().optional(),\n networkPolicy: NetworkPolicyResponseValidator.optional(),\n activeCpuDurationMs: z.number().optional(),\n networkTransfer: z.object({\n ingress: z.number(),\n egress: z.number(),\n }).optional(),\n});\n\nexport type SandboxRouteData = z.infer<typeof SandboxRoute>;\n\nexport const SandboxRoute = z.object({\n url: z.string(),\n subdomain: z.string(),\n port: z.number(),\n});\n\nexport type SnapshotMetadata = z.infer<typeof Snapshot>;\n\nexport const Snapshot = z.object({\n id: z.string(),\n sourceSessionId: z.string(),\n region: z.string(),\n status: z.enum([\"created\", \"deleted\", \"failed\"]),\n sizeBytes: z.number(),\n expiresAt: z.number().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n});\n\nexport const CursorPagination = z.object({\n count: z.number(),\n next: z.string().nullable(),\n});\n\nexport type CommandData = z.infer<typeof Command>;\n\nexport const Command = z.object({\n id: z.string(),\n name: z.string(),\n args: z.array(z.string()),\n cwd: z.string(),\n sessionId: z.string(),\n exitCode: z.number().nullable(),\n startedAt: z.number(),\n});\n\nconst CommandFinished = Command.extend({\n exitCode: z.number(),\n});\n\nexport const SessionResponse = z.object({\n session: Session.passthrough(),\n});\n\nexport const SessionAndRoutesResponse = SessionResponse.extend({\n routes: z.array(SandboxRoute),\n});\n\nexport const SessionsResponse = z.object({\n sessions: z.array(Session.passthrough()),\n pagination: CursorPagination,\n});\n\nexport const CommandResponse = z.object({\n command: Command,\n});\n\nexport type CommandFinishedData = z.infer<typeof CommandFinishedResponse>[\"command\"];\n\nexport const CommandFinishedResponse = z.object({\n command: CommandFinished,\n});\n\nexport const EmptyResponse = z.object({});\n\nconst LogLineBase = z.object({ data: z.string() });\nexport const LogLineStdout = LogLineBase.extend({\n stream: z.literal(\"stdout\"),\n});\nexport const LogLineStderr = LogLineBase.extend({\n stream: z.literal(\"stderr\"),\n});\n\nexport const LogError = z.object({\n stream: z.literal(\"error\"),\n data: z.object({\n code: z.string(),\n message: z.string(),\n }),\n});\n\nexport const LogLine = z.discriminatedUnion(\"stream\", [\n LogLineStdout,\n LogLineStderr,\n LogError,\n]);\n\nexport const SnapshotsResponse = z.object({\n snapshots: z.array(Snapshot),\n pagination: CursorPagination,\n});\n\nexport const CreateSnapshotResponse = z.object({\n snapshot: Snapshot,\n session: Session.passthrough(),\n});\n\nexport const SnapshotResponse = z.object({\n snapshot: Snapshot,\n});\n\nexport const Sandbox = z.object({\n name: z.string(),\n persistent: z.boolean(),\n region: z.string().optional(),\n vcpus: z.number().optional(),\n memory: z.number().optional(),\n runtime: z.string().optional(),\n timeout: z.number().optional(),\n networkPolicy: NetworkPolicyResponseValidator.optional(),\n totalEgressBytes: z.number().optional(),\n totalIngressBytes: z.number().optional(),\n totalActiveCpuDurationMs: z.number().optional(),\n totalDurationMs: z.number().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n currentSessionId: z.string(),\n currentSnapshotId: z.string().optional(),\n status: Session.shape.status,\n statusUpdatedAt: z.number().optional(),\n cwd: z.string().optional(),\n tags: z.record(z.string()).optional(),\n snapshotExpiration: z.number().optional(),\n});\n\nexport type SandboxMetaData = z.infer<typeof Sandbox>;\n\nexport const StopSessionResponse = z.object({\n session: Session.passthrough(),\n sandbox: Sandbox.optional(),\n snapshot: Snapshot.optional(),\n});\n\nexport const SandboxAndSessionResponse = z.object({\n sandbox: Sandbox,\n session: Session.passthrough(),\n routes: z.array(SandboxRoute),\n resumed: z.boolean().optional(),\n});\n\nexport const SandboxesPaginationResponse = z.object({\n sandboxes: z.array(Sandbox),\n pagination: CursorPagination,\n});\n\nexport const UpdateSandboxResponse = z.object({\n sandbox: Sandbox,\n});\n"],"mappings":"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"}
import { z } from "zod";
//#region src/api-client/validators.ts
const RuleMatcherValidator = z.object({
exact: z.string().optional(),
startsWith: z.string().optional(),
regex: z.string().optional()
});
const KeyValueMatcherValidator = z.object({
key: RuleMatcherValidator.optional(),
value: RuleMatcherValidator.optional()
});
const RuleMatchValidator = z.object({
path: RuleMatcherValidator.optional(),
method: z.array(z.string()).optional(),
queryString: z.array(KeyValueMatcherValidator).optional(),
headers: z.array(KeyValueMatcherValidator).optional()
});
const InjectionRuleValidator = z.object({
domain: z.string(),
headers: z.record(z.string()).optional(),
headerNames: z.array(z.string()).optional()
headerNames: z.array(z.string()).optional(),
match: RuleMatchValidator.optional()
});
const NetworkPolicyValidator = z.union([
z.object({ mode: z.literal("allow-all") }).passthrough(),
z.object({ mode: z.literal("deny-all") }).passthrough(),
z.object({
mode: z.literal("custom"),
allowedDomains: z.array(z.string()).optional(),
allowedCIDRs: z.array(z.string()).optional(),
deniedCIDRs: z.array(z.string()).optional(),
injectionRules: z.array(InjectionRuleValidator).optional()
}).passthrough()
]);
const ForwardRuleValidator = z.object({
domain: z.string(),
forwardURL: z.string(),
match: RuleMatchValidator.optional()
});
const NetworkPolicyTransformValidator = z.object({ headers: z.record(z.string()).optional() });
const NetworkPolicyRuleValidator = z.object({
match: RuleMatchValidator.optional(),
transform: z.array(NetworkPolicyTransformValidator).optional(),
forwardURL: z.string().optional()
});
const V2NetworkPolicyObjectValidator = z.object({
allow: z.union([z.array(z.string()), z.record(z.array(NetworkPolicyRuleValidator))]).optional(),
subnets: z.object({
allow: z.array(z.string()).optional(),
deny: z.array(z.string()).optional()
}).optional()
});
const NetworkPolicyModeValidator = z.union([z.object({ mode: z.literal("allow-all") }).passthrough(), z.object({ mode: z.literal("deny-all") }).passthrough()]);
const LegacyCustomNetworkPolicyValidator = z.object({
mode: z.literal("custom"),
allowedDomains: z.array(z.string()).optional(),
allowedCIDRs: z.array(z.string()).optional(),
deniedCIDRs: z.array(z.string()).optional(),
injectionRules: z.array(InjectionRuleValidator).optional(),
forwardRules: z.array(ForwardRuleValidator).optional()
}).passthrough();
const NetworkPolicyRequestValidator = z.union([NetworkPolicyModeValidator, V2NetworkPolicyObjectValidator.passthrough()]);
const NetworkPolicyResponseValidator = z.union([NetworkPolicyModeValidator, LegacyCustomNetworkPolicyValidator]);
const Session = z.object({

@@ -48,3 +82,3 @@ id: z.string(),

interactivePort: z.number().optional(),
networkPolicy: NetworkPolicyValidator.optional(),
networkPolicy: NetworkPolicyResponseValidator.optional(),
activeCpuDurationMs: z.number().optional(),

@@ -130,3 +164,3 @@ networkTransfer: z.object({

timeout: z.number().optional(),
networkPolicy: NetworkPolicyValidator.optional(),
networkPolicy: NetworkPolicyResponseValidator.optional(),
totalEgressBytes: z.number().optional(),

@@ -146,2 +180,7 @@ totalIngressBytes: z.number().optional(),

});
const StopSessionResponse = z.object({
session: Session.passthrough(),
sandbox: Sandbox.optional(),
snapshot: Snapshot.optional()
});
const SandboxAndSessionResponse = z.object({

@@ -160,3 +199,3 @@ sandbox: Sandbox,

//#endregion
export { Command, CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, CursorPagination, EmptyResponse, InjectionRuleValidator, LogError, LogLine, LogLineStderr, LogLineStdout, NetworkPolicyValidator, Sandbox, SandboxAndSessionResponse, SandboxRoute, SandboxesPaginationResponse, Session, SessionAndRoutesResponse, SessionResponse, SessionsResponse, Snapshot, SnapshotResponse, SnapshotsResponse, UpdateSandboxResponse };
export { Command, CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, CursorPagination, EmptyResponse, ForwardRuleValidator, InjectionRuleValidator, LogError, LogLine, LogLineStderr, LogLineStdout, NetworkPolicyResponseValidator, NetworkPolicyRuleValidator, NetworkPolicyTransformValidator, Sandbox, SandboxAndSessionResponse, SandboxRoute, SandboxesPaginationResponse, Session, SessionAndRoutesResponse, SessionResponse, SessionsResponse, Snapshot, SnapshotResponse, SnapshotsResponse, StopSessionResponse, UpdateSandboxResponse, V2NetworkPolicyObjectValidator };
//# sourceMappingURL=validators.js.map

@@ -1,1 +0,1 @@

{"version":3,"file":"validators.js","names":[],"sources":["../../src/api-client/validators.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport type SessionMetaData = z.infer<typeof Session>;\n\nexport const InjectionRuleValidator = z.object({\n domain: z.string(),\n // headers are only sent in requests\n headers: z.record(z.string()).optional(),\n // headerNames are returned in responses\n headerNames: z.array(z.string()).optional(),\n});\n\nexport const NetworkPolicyValidator = z.union([\n z.object({ mode: z.literal(\"allow-all\") }).passthrough(),\n z.object({ mode: z.literal(\"deny-all\") }).passthrough(),\n z\n .object({\n mode: z.literal(\"custom\"),\n allowedDomains: z.array(z.string()).optional(),\n allowedCIDRs: z.array(z.string()).optional(),\n deniedCIDRs: z.array(z.string()).optional(),\n injectionRules: z.array(InjectionRuleValidator).optional(),\n })\n .passthrough(),\n]);\n\nexport const Session = z.object({\n id: z.string(),\n memory: z.number(),\n vcpus: z.number(),\n region: z.string(),\n runtime: z.string(),\n timeout: z.number(),\n status: z.enum([\n \"pending\",\n \"running\",\n \"stopping\",\n \"stopped\",\n \"failed\",\n \"aborted\",\n \"snapshotting\",\n ]),\n requestedAt: z.number(),\n startedAt: z.number().optional(),\n requestedStopAt: z.number().optional(),\n stoppedAt: z.number().optional(),\n abortedAt: z.number().optional(),\n duration: z.number().optional(),\n sourceSnapshotId: z.string().optional(),\n snapshottedAt: z.number().optional(),\n createdAt: z.number(),\n cwd: z.string(),\n updatedAt: z.number(),\n interactivePort: z.number().optional(),\n networkPolicy: NetworkPolicyValidator.optional(),\n activeCpuDurationMs: z.number().optional(),\n networkTransfer: z.object({\n ingress: z.number(),\n egress: z.number(),\n }).optional(),\n});\n\nexport type SandboxRouteData = z.infer<typeof SandboxRoute>;\n\nexport const SandboxRoute = z.object({\n url: z.string(),\n subdomain: z.string(),\n port: z.number(),\n});\n\nexport type SnapshotMetadata = z.infer<typeof Snapshot>;\n\nexport const Snapshot = z.object({\n id: z.string(),\n sourceSessionId: z.string(),\n region: z.string(),\n status: z.enum([\"created\", \"deleted\", \"failed\"]),\n sizeBytes: z.number(),\n expiresAt: z.number().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n});\n\nexport const CursorPagination = z.object({\n count: z.number(),\n next: z.string().nullable(),\n});\n\nexport type CommandData = z.infer<typeof Command>;\n\nexport const Command = z.object({\n id: z.string(),\n name: z.string(),\n args: z.array(z.string()),\n cwd: z.string(),\n sessionId: z.string(),\n exitCode: z.number().nullable(),\n startedAt: z.number(),\n});\n\nconst CommandFinished = Command.extend({\n exitCode: z.number(),\n});\n\nexport const SessionResponse = z.object({\n session: Session.passthrough(),\n});\n\nexport const SessionAndRoutesResponse = SessionResponse.extend({\n routes: z.array(SandboxRoute),\n});\n\nexport const SessionsResponse = z.object({\n sessions: z.array(Session.passthrough()),\n pagination: CursorPagination,\n});\n\nexport const CommandResponse = z.object({\n command: Command,\n});\n\nexport type CommandFinishedData = z.infer<typeof CommandFinishedResponse>[\"command\"];\n\nexport const CommandFinishedResponse = z.object({\n command: CommandFinished,\n});\n\nexport const EmptyResponse = z.object({});\n\nconst LogLineBase = z.object({ data: z.string() });\nexport const LogLineStdout = LogLineBase.extend({\n stream: z.literal(\"stdout\"),\n});\nexport const LogLineStderr = LogLineBase.extend({\n stream: z.literal(\"stderr\"),\n});\n\nexport const LogError = z.object({\n stream: z.literal(\"error\"),\n data: z.object({\n code: z.string(),\n message: z.string(),\n }),\n});\n\nexport const LogLine = z.discriminatedUnion(\"stream\", [\n LogLineStdout,\n LogLineStderr,\n LogError,\n]);\n\nexport const SnapshotsResponse = z.object({\n snapshots: z.array(Snapshot),\n pagination: CursorPagination,\n});\n\nexport const CreateSnapshotResponse = z.object({\n snapshot: Snapshot,\n session: Session.passthrough(),\n});\n\nexport const SnapshotResponse = z.object({\n snapshot: Snapshot,\n});\n\nexport const Sandbox = z.object({\n name: z.string(),\n persistent: z.boolean(),\n region: z.string().optional(),\n vcpus: z.number().optional(),\n memory: z.number().optional(),\n runtime: z.string().optional(),\n timeout: z.number().optional(),\n networkPolicy: NetworkPolicyValidator.optional(),\n totalEgressBytes: z.number().optional(),\n totalIngressBytes: z.number().optional(),\n totalActiveCpuDurationMs: z.number().optional(),\n totalDurationMs: z.number().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n currentSessionId: z.string(),\n currentSnapshotId: z.string().optional(),\n status: Session.shape.status,\n statusUpdatedAt: z.number().optional(),\n cwd: z.string().optional(),\n tags: z.record(z.string()).optional(),\n snapshotExpiration: z.number().optional(),\n});\n\nexport type SandboxMetaData = z.infer<typeof Sandbox>;\n\nexport const SandboxAndSessionResponse = z.object({\n sandbox: Sandbox,\n session: Session.passthrough(),\n routes: z.array(SandboxRoute),\n resumed: z.boolean().optional(),\n});\n\nexport const SandboxesPaginationResponse = z.object({\n sandboxes: z.array(Sandbox),\n pagination: CursorPagination,\n});\n\nexport const UpdateSandboxResponse = z.object({\n sandbox: Sandbox,\n});\n"],"mappings":"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"}
{"version":3,"file":"validators.js","names":[],"sources":["../../src/api-client/validators.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport type SessionMetaData = z.infer<typeof Session>;\n\nconst RuleMatcherValidator = z.object({\n exact: z.string().optional(),\n startsWith: z.string().optional(),\n regex: z.string().optional(),\n});\n\nconst KeyValueMatcherValidator = z.object({\n key: RuleMatcherValidator.optional(),\n value: RuleMatcherValidator.optional(),\n});\n\nconst RuleMatchValidator = z.object({\n path: RuleMatcherValidator.optional(),\n method: z.array(z.string()).optional(),\n queryString: z.array(KeyValueMatcherValidator).optional(),\n headers: z.array(KeyValueMatcherValidator).optional(),\n});\n\nexport const InjectionRuleValidator = z.object({\n domain: z.string(),\n // headers are only sent in requests\n headers: z.record(z.string()).optional(),\n // headerNames are returned in responses\n headerNames: z.array(z.string()).optional(),\n match: RuleMatchValidator.optional(),\n});\n\nexport const ForwardRuleValidator = z.object({\n domain: z.string(),\n forwardURL: z.string(),\n match: RuleMatchValidator.optional(),\n});\n\nexport const NetworkPolicyTransformValidator = z.object({\n headers: z.record(z.string()).optional(),\n});\n\nexport const NetworkPolicyRuleValidator = z.object({\n match: RuleMatchValidator.optional(),\n transform: z.array(NetworkPolicyTransformValidator).optional(),\n forwardURL: z.string().optional(),\n});\n\nexport const V2NetworkPolicyObjectValidator = z.object({\n allow: z\n .union([\n z.array(z.string()),\n z.record(z.array(NetworkPolicyRuleValidator)),\n ])\n .optional(),\n subnets: z\n .object({\n allow: z.array(z.string()).optional(),\n deny: z.array(z.string()).optional(),\n })\n .optional(),\n});\n\nconst NetworkPolicyModeValidator = z.union([\n z.object({ mode: z.literal(\"allow-all\") }).passthrough(),\n z.object({ mode: z.literal(\"deny-all\") }).passthrough(),\n]);\n\nconst LegacyCustomNetworkPolicyValidator = z\n .object({\n mode: z.literal(\"custom\"),\n allowedDomains: z.array(z.string()).optional(),\n allowedCIDRs: z.array(z.string()).optional(),\n deniedCIDRs: z.array(z.string()).optional(),\n injectionRules: z.array(InjectionRuleValidator).optional(),\n forwardRules: z.array(ForwardRuleValidator).optional(),\n })\n .passthrough();\n\nexport const NetworkPolicyRequestValidator = z.union([\n NetworkPolicyModeValidator,\n V2NetworkPolicyObjectValidator.passthrough(),\n]);\n\nexport const NetworkPolicyResponseValidator = z.union([\n NetworkPolicyModeValidator,\n LegacyCustomNetworkPolicyValidator,\n]);\n\nexport const Session = z.object({\n id: z.string(),\n memory: z.number(),\n vcpus: z.number(),\n region: z.string(),\n runtime: z.string(),\n timeout: z.number(),\n status: z.enum([\n \"pending\",\n \"running\",\n \"stopping\",\n \"stopped\",\n \"failed\",\n \"aborted\",\n \"snapshotting\",\n ]),\n requestedAt: z.number(),\n startedAt: z.number().optional(),\n requestedStopAt: z.number().optional(),\n stoppedAt: z.number().optional(),\n abortedAt: z.number().optional(),\n duration: z.number().optional(),\n sourceSnapshotId: z.string().optional(),\n snapshottedAt: z.number().optional(),\n createdAt: z.number(),\n cwd: z.string(),\n updatedAt: z.number(),\n interactivePort: z.number().optional(),\n networkPolicy: NetworkPolicyResponseValidator.optional(),\n activeCpuDurationMs: z.number().optional(),\n networkTransfer: z.object({\n ingress: z.number(),\n egress: z.number(),\n }).optional(),\n});\n\nexport type SandboxRouteData = z.infer<typeof SandboxRoute>;\n\nexport const SandboxRoute = z.object({\n url: z.string(),\n subdomain: z.string(),\n port: z.number(),\n});\n\nexport type SnapshotMetadata = z.infer<typeof Snapshot>;\n\nexport const Snapshot = z.object({\n id: z.string(),\n sourceSessionId: z.string(),\n region: z.string(),\n status: z.enum([\"created\", \"deleted\", \"failed\"]),\n sizeBytes: z.number(),\n expiresAt: z.number().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n});\n\nexport const CursorPagination = z.object({\n count: z.number(),\n next: z.string().nullable(),\n});\n\nexport type CommandData = z.infer<typeof Command>;\n\nexport const Command = z.object({\n id: z.string(),\n name: z.string(),\n args: z.array(z.string()),\n cwd: z.string(),\n sessionId: z.string(),\n exitCode: z.number().nullable(),\n startedAt: z.number(),\n});\n\nconst CommandFinished = Command.extend({\n exitCode: z.number(),\n});\n\nexport const SessionResponse = z.object({\n session: Session.passthrough(),\n});\n\nexport const SessionAndRoutesResponse = SessionResponse.extend({\n routes: z.array(SandboxRoute),\n});\n\nexport const SessionsResponse = z.object({\n sessions: z.array(Session.passthrough()),\n pagination: CursorPagination,\n});\n\nexport const CommandResponse = z.object({\n command: Command,\n});\n\nexport type CommandFinishedData = z.infer<typeof CommandFinishedResponse>[\"command\"];\n\nexport const CommandFinishedResponse = z.object({\n command: CommandFinished,\n});\n\nexport const EmptyResponse = z.object({});\n\nconst LogLineBase = z.object({ data: z.string() });\nexport const LogLineStdout = LogLineBase.extend({\n stream: z.literal(\"stdout\"),\n});\nexport const LogLineStderr = LogLineBase.extend({\n stream: z.literal(\"stderr\"),\n});\n\nexport const LogError = z.object({\n stream: z.literal(\"error\"),\n data: z.object({\n code: z.string(),\n message: z.string(),\n }),\n});\n\nexport const LogLine = z.discriminatedUnion(\"stream\", [\n LogLineStdout,\n LogLineStderr,\n LogError,\n]);\n\nexport const SnapshotsResponse = z.object({\n snapshots: z.array(Snapshot),\n pagination: CursorPagination,\n});\n\nexport const CreateSnapshotResponse = z.object({\n snapshot: Snapshot,\n session: Session.passthrough(),\n});\n\nexport const SnapshotResponse = z.object({\n snapshot: Snapshot,\n});\n\nexport const Sandbox = z.object({\n name: z.string(),\n persistent: z.boolean(),\n region: z.string().optional(),\n vcpus: z.number().optional(),\n memory: z.number().optional(),\n runtime: z.string().optional(),\n timeout: z.number().optional(),\n networkPolicy: NetworkPolicyResponseValidator.optional(),\n totalEgressBytes: z.number().optional(),\n totalIngressBytes: z.number().optional(),\n totalActiveCpuDurationMs: z.number().optional(),\n totalDurationMs: z.number().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n currentSessionId: z.string(),\n currentSnapshotId: z.string().optional(),\n status: Session.shape.status,\n statusUpdatedAt: z.number().optional(),\n cwd: z.string().optional(),\n tags: z.record(z.string()).optional(),\n snapshotExpiration: z.number().optional(),\n});\n\nexport type SandboxMetaData = z.infer<typeof Sandbox>;\n\nexport const StopSessionResponse = z.object({\n session: Session.passthrough(),\n sandbox: Sandbox.optional(),\n snapshot: Snapshot.optional(),\n});\n\nexport const SandboxAndSessionResponse = z.object({\n sandbox: Sandbox,\n session: Session.passthrough(),\n routes: z.array(SandboxRoute),\n resumed: z.boolean().optional(),\n});\n\nexport const SandboxesPaginationResponse = z.object({\n sandboxes: z.array(Sandbox),\n pagination: CursorPagination,\n});\n\nexport const UpdateSandboxResponse = z.object({\n sandbox: Sandbox,\n});\n"],"mappings":"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"}

@@ -5,3 +5,3 @@ const require_error = require('./error.cjs');

async function fetchApi(opts) {
const x = await fetch(`https://api.vercel.com${opts.endpoint}`, {
const x = await fetch(`https://vercel.com/api${opts.endpoint}`, {
method: opts.method,

@@ -8,0 +8,0 @@ body: opts.body,

@@ -1,1 +0,1 @@

{"version":3,"file":"api.cjs","names":["NotOk"],"sources":["../../src/auth/api.ts"],"sourcesContent":["import { NotOk } from \"./error.js\";\n\nexport async function fetchApi(opts: {\n token: string;\n endpoint: string;\n method?: string;\n body?: string;\n}): Promise<unknown> {\n const x = await fetch(`https://api.vercel.com${opts.endpoint}`, {\n method: opts.method,\n body: opts.body,\n headers: {\n Authorization: `Bearer ${opts.token}`,\n \"Content-Type\": \"application/json\",\n },\n });\n if (!x.ok) {\n let message = await x.text();\n\n try {\n const { error } = JSON.parse(message);\n message = `${error.code.toUpperCase()}: ${error.message}`;\n } catch {}\n\n throw new NotOk({\n responseText: message,\n statusCode: x.status,\n });\n }\n return (await x.json()) as unknown;\n}\n"],"mappings":";;;AAEA,eAAsB,SAAS,MAKV;CACnB,MAAM,IAAI,MAAM,MAAM,yBAAyB,KAAK,YAAY;EAC9D,QAAQ,KAAK;EACb,MAAM,KAAK;EACX,SAAS;GACP,eAAe,UAAU,KAAK;GAC9B,gBAAgB;GACjB;EACF,CAAC;AACF,KAAI,CAAC,EAAE,IAAI;EACT,IAAI,UAAU,MAAM,EAAE,MAAM;AAE5B,MAAI;GACF,MAAM,EAAE,UAAU,KAAK,MAAM,QAAQ;AACrC,aAAU,GAAG,MAAM,KAAK,aAAa,CAAC,IAAI,MAAM;UAC1C;AAER,QAAM,IAAIA,oBAAM;GACd,cAAc;GACd,YAAY,EAAE;GACf,CAAC;;AAEJ,QAAQ,MAAM,EAAE,MAAM"}
{"version":3,"file":"api.cjs","names":["NotOk"],"sources":["../../src/auth/api.ts"],"sourcesContent":["import { NotOk } from \"./error.js\";\n\nexport async function fetchApi(opts: {\n token: string;\n endpoint: string;\n method?: string;\n body?: string;\n}): Promise<unknown> {\n const x = await fetch(`https://vercel.com/api${opts.endpoint}`, {\n method: opts.method,\n body: opts.body,\n headers: {\n Authorization: `Bearer ${opts.token}`,\n \"Content-Type\": \"application/json\",\n },\n });\n if (!x.ok) {\n let message = await x.text();\n\n try {\n const { error } = JSON.parse(message);\n message = `${error.code.toUpperCase()}: ${error.message}`;\n } catch {}\n\n throw new NotOk({\n responseText: message,\n statusCode: x.status,\n });\n }\n return (await x.json()) as unknown;\n}\n"],"mappings":";;;AAEA,eAAsB,SAAS,MAKV;CACnB,MAAM,IAAI,MAAM,MAAM,yBAAyB,KAAK,YAAY;EAC9D,QAAQ,KAAK;EACb,MAAM,KAAK;EACX,SAAS;GACP,eAAe,UAAU,KAAK;GAC9B,gBAAgB;GACjB;EACF,CAAC;AACF,KAAI,CAAC,EAAE,IAAI;EACT,IAAI,UAAU,MAAM,EAAE,MAAM;AAE5B,MAAI;GACF,MAAM,EAAE,UAAU,KAAK,MAAM,QAAQ;AACrC,aAAU,GAAG,MAAM,KAAK,aAAa,CAAC,IAAI,MAAM;UAC1C;AAER,QAAM,IAAIA,oBAAM;GACd,cAAc;GACd,YAAY,EAAE;GACf,CAAC;;AAEJ,QAAQ,MAAM,EAAE,MAAM"}

@@ -5,3 +5,3 @@ import { NotOk } from "./error.js";

async function fetchApi(opts) {
const x = await fetch(`https://api.vercel.com${opts.endpoint}`, {
const x = await fetch(`https://vercel.com/api${opts.endpoint}`, {
method: opts.method,

@@ -8,0 +8,0 @@ body: opts.body,

@@ -1,1 +0,1 @@

{"version":3,"file":"api.js","names":[],"sources":["../../src/auth/api.ts"],"sourcesContent":["import { NotOk } from \"./error.js\";\n\nexport async function fetchApi(opts: {\n token: string;\n endpoint: string;\n method?: string;\n body?: string;\n}): Promise<unknown> {\n const x = await fetch(`https://api.vercel.com${opts.endpoint}`, {\n method: opts.method,\n body: opts.body,\n headers: {\n Authorization: `Bearer ${opts.token}`,\n \"Content-Type\": \"application/json\",\n },\n });\n if (!x.ok) {\n let message = await x.text();\n\n try {\n const { error } = JSON.parse(message);\n message = `${error.code.toUpperCase()}: ${error.message}`;\n } catch {}\n\n throw new NotOk({\n responseText: message,\n statusCode: x.status,\n });\n }\n return (await x.json()) as unknown;\n}\n"],"mappings":";;;AAEA,eAAsB,SAAS,MAKV;CACnB,MAAM,IAAI,MAAM,MAAM,yBAAyB,KAAK,YAAY;EAC9D,QAAQ,KAAK;EACb,MAAM,KAAK;EACX,SAAS;GACP,eAAe,UAAU,KAAK;GAC9B,gBAAgB;GACjB;EACF,CAAC;AACF,KAAI,CAAC,EAAE,IAAI;EACT,IAAI,UAAU,MAAM,EAAE,MAAM;AAE5B,MAAI;GACF,MAAM,EAAE,UAAU,KAAK,MAAM,QAAQ;AACrC,aAAU,GAAG,MAAM,KAAK,aAAa,CAAC,IAAI,MAAM;UAC1C;AAER,QAAM,IAAI,MAAM;GACd,cAAc;GACd,YAAY,EAAE;GACf,CAAC;;AAEJ,QAAQ,MAAM,EAAE,MAAM"}
{"version":3,"file":"api.js","names":[],"sources":["../../src/auth/api.ts"],"sourcesContent":["import { NotOk } from \"./error.js\";\n\nexport async function fetchApi(opts: {\n token: string;\n endpoint: string;\n method?: string;\n body?: string;\n}): Promise<unknown> {\n const x = await fetch(`https://vercel.com/api${opts.endpoint}`, {\n method: opts.method,\n body: opts.body,\n headers: {\n Authorization: `Bearer ${opts.token}`,\n \"Content-Type\": \"application/json\",\n },\n });\n if (!x.ok) {\n let message = await x.text();\n\n try {\n const { error } = JSON.parse(message);\n message = `${error.code.toUpperCase()}: ${error.message}`;\n } catch {}\n\n throw new NotOk({\n responseText: message,\n statusCode: x.status,\n });\n }\n return (await x.json()) as unknown;\n}\n"],"mappings":";;;AAEA,eAAsB,SAAS,MAKV;CACnB,MAAM,IAAI,MAAM,MAAM,yBAAyB,KAAK,YAAY;EAC9D,QAAQ,KAAK;EACb,MAAM,KAAK;EACX,SAAS;GACP,eAAe,UAAU,KAAK;GAC9B,gBAAgB;GACjB;EACF,CAAC;AACF,KAAI,CAAC,EAAE,IAAI;EACT,IAAI,UAAU,MAAM,EAAE,MAAM;AAE5B,MAAI;GACF,MAAM,EAAE,UAAU,KAAK,MAAM,QAAQ;AACrC,aAAU,GAAG,MAAM,KAAK,aAAa,CAAC,IAAI,MAAM;UAC1C;AAER,QAAM,IAAI,MAAM;GACd,cAAc;GACd,YAAY,EAAE;GACf,CAAC;;AAEJ,QAAQ,MAAM,EAAE,MAAM"}

@@ -11,3 +11,2 @@ const require_file = require('./file.cjs');

exports.pollForToken = require_poll_for_token.pollForToken;
exports.selectTeam = require_project.selectTeam;
exports.updateAuthConfig = require_file.updateAuthConfig;
import { getAuth, updateAuthConfig } from "./file.cjs";
import { DeviceAuthorizationRequest, OAuth, isOAuthError } from "./oauth.cjs";
import { pollForToken } from "./poll-for-token.cjs";
import { inferScope, selectTeam } from "./project.cjs";
export { DeviceAuthorizationRequest, OAuth, getAuth, inferScope, isOAuthError, pollForToken, selectTeam, updateAuthConfig };
import { inferScope } from "./project.cjs";
export { DeviceAuthorizationRequest, OAuth, getAuth, inferScope, isOAuthError, pollForToken, updateAuthConfig };
import { getAuth, updateAuthConfig } from "./file.js";
import { DeviceAuthorizationRequest, OAuth, isOAuthError } from "./oauth.js";
import { pollForToken } from "./poll-for-token.js";
import { inferScope, selectTeam } from "./project.js";
export { DeviceAuthorizationRequest, OAuth, getAuth, inferScope, isOAuthError, pollForToken, selectTeam, updateAuthConfig };
import { inferScope } from "./project.js";
export { DeviceAuthorizationRequest, OAuth, getAuth, inferScope, isOAuthError, pollForToken, updateAuthConfig };
import { getAuth, updateAuthConfig } from "./file.js";
import { OAuth, isOAuthError } from "./oauth.js";
import { pollForToken } from "./poll-for-token.js";
import { inferScope, selectTeam } from "./project.js";
import { inferScope } from "./project.js";
export { OAuth, getAuth, inferScope, isOAuthError, pollForToken, selectTeam, updateAuthConfig };
export { OAuth, getAuth, inferScope, isOAuthError, pollForToken, updateAuthConfig };

@@ -8,4 +8,25 @@ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');

//#region src/auth/project.ts
const TeamsSchema = zod.z.object({ teams: zod.z.array(zod.z.object({ slug: zod.z.string() })).min(1, `No teams found. Please create a team first.`) });
const UserSchema = zod.z.object({ user: zod.z.object({
defaultTeamId: zod.z.string().nullable(),
username: zod.z.string()
}) });
const TeamSchema = zod.z.object({
id: zod.z.string(),
slug: zod.z.string(),
updatedAt: zod.z.number(),
membership: zod.z.object({ role: zod.z.string() }),
billing: zod.z.object({ plan: zod.z.string() })
});
const TeamsSchema = zod.z.object({
teams: zod.z.array(TeamSchema),
pagination: zod.z.object({
count: zod.z.number(),
next: zod.z.number().nullable()
})
});
const DEFAULT_PROJECT_NAME = "vercel-sandbox-default-project";
/** Status codes that mean "this team can't be used, try the next one". */
function isSkippableTeamError(e) {
return e instanceof require_error.NotOk && (e.response.statusCode === 402 || e.response.statusCode === 403);
}
/**

@@ -17,7 +38,10 @@ * Resolves the team and project scope for sandbox operations.

*
* Otherwise, if `teamId` is not provided, selects the first available team for the account.
* Ensures a default project exists within the team, creating it if necessary.
* Otherwise, if `teamId` is not provided, builds an ordered list of candidate
* teams to try: the user's `defaultTeamId` first (if set), then hobby-plan
* teams where the user has an OWNER role (preferring the personal team matching
* the username, then the most recently updated). Tries each candidate until one
* succeeds.
*
* @param opts.token - Vercel API authentication token.
* @param opts.teamId - Optional team slug. If omitted, the first team is selected.
* @param opts.teamId - Optional team slug. If omitted, candidate teams are resolved automatically.
* @param opts.cwd - Optional directory to search for `.vercel/project.json`. Defaults to `process.cwd()`.

@@ -27,3 +51,2 @@ * @returns The resolved scope with `projectId`, `teamId`, and whether the project was `created`.

* @throws {NotOk} If the API returns an error other than 404 when checking the project.
* @throws {ZodError} If no teams exist for the account.
*

@@ -38,12 +61,79 @@ * @example

const linkedProject = await require_linked_project.readLinkedProject(opts.cwd ?? process.cwd());
if (linkedProject) return {
...linkedProject,
created: false
};
const teamId = opts.teamId ?? await selectTeam(opts.token);
if (linkedProject) {
const slugs = await resolveLinkedProjectSlugs(opts.token, linkedProject.teamId, linkedProject.projectId);
return {
...linkedProject,
created: false,
...slugs
};
}
if (opts.teamId) return tryTeam(opts.token, opts.teamId);
const { defaultTeamId, username } = (await require_api.fetchApi({
token: opts.token,
endpoint: "/v2/user"
}).then(UserSchema.parse)).user;
if (defaultTeamId) try {
const result = await tryTeam(opts.token, defaultTeamId);
try {
const team = await require_api.fetchApi({
token: opts.token,
endpoint: `/v2/teams/${encodeURIComponent(defaultTeamId)}`
}).then(zod.z.object({ slug: zod.z.string() }).parse);
return {
...result,
teamSlug: team.slug
};
} catch {
return result;
}
} catch (e) {
if (!isSkippableTeamError(e)) throw e;
}
let next = null;
do {
const endpoint = next === null ? "/v2/teams?limit=20" : `/v2/teams?limit=20&until=${next}`;
const page = await require_api.fetchApi({
token: opts.token,
endpoint
}).then(TeamsSchema.parse);
next = page.pagination.next;
const hobbyOwnerTeams = page.teams.filter((t) => t.membership.role === "OWNER" && t.billing.plan === "hobby");
if (hobbyOwnerTeams.length === 0) continue;
const bestHobbyTeam = hobbyOwnerTeams.find((t) => t.slug === username) ?? hobbyOwnerTeams.sort((a, b) => b.updatedAt - a.updatedAt)[0];
if (bestHobbyTeam && bestHobbyTeam.id !== defaultTeamId) try {
return {
...await tryTeam(opts.token, bestHobbyTeam.id),
teamSlug: bestHobbyTeam.slug
};
} catch (e) {
if (!isSkippableTeamError(e)) throw e;
}
} while (next !== null);
try {
return {
...await tryTeam(opts.token, username),
teamSlug: username
};
} catch (e) {
if (!isSkippableTeamError(e)) throw e;
}
throw new require_error.NotOk({
statusCode: 403,
responseText: `Authenticated as "${username}" but none of the available teams allow sandbox creation. Specify a team explicitly with --scope <team-id-or-slug>.`
});
}
/**
* Attempts to use a specific team for sandbox operations by checking for
* (or creating) the default project within that team.
*
* @returns The resolved scope if the team is usable.
* @throws {NotOk} On authorization or other API errors.
*/
async function tryTeam(token, teamId) {
const teamParam = teamId.startsWith("team_") ? `teamId=${encodeURIComponent(teamId)}` : `slug=${encodeURIComponent(teamId)}`;
let created = false;
try {
await require_api.fetchApi({
token: opts.token,
endpoint: `/v2/projects/${encodeURIComponent(DEFAULT_PROJECT_NAME)}?slug=${encodeURIComponent(teamId)}`
token,
endpoint: `/v2/projects/${encodeURIComponent(DEFAULT_PROJECT_NAME)}?${teamParam}`
});

@@ -53,4 +143,4 @@ } catch (e) {

await require_api.fetchApi({
token: opts.token,
endpoint: `/v11/projects?slug=${encodeURIComponent(teamId)}`,
token,
endpoint: `/v11/projects?${teamParam}`,
method: "POST",

@@ -68,14 +158,23 @@ body: JSON.stringify({ name: DEFAULT_PROJECT_NAME })

/**
* Selects a team for the current token by querying the Teams API and
* returning the slug of the first team in the result set.
*
* @param token - Authentication token used to call the Vercel API.
* @returns A promise that resolves to the first team's slug.
* Best-effort resolution of team slug and project name for a linked project.
* Both IDs may be opaque (e.g. `team_xxx`, `prj_xxx`), so we fetch the
* human-readable names from the API in parallel.
*/
async function selectTeam(token) {
const { teams: [team] } = await require_api.fetchApi({
token,
endpoint: "/v2/teams?limit=1"
}).then(TeamsSchema.parse);
return team.slug;
async function resolveLinkedProjectSlugs(token, teamId, projectId) {
try {
const teamParam = teamId.startsWith("team_") ? `teamId=${encodeURIComponent(teamId)}` : `slug=${encodeURIComponent(teamId)}`;
const [teamData, projectData] = await Promise.all([require_api.fetchApi({
token,
endpoint: `/v2/teams/${encodeURIComponent(teamId)}`
}).then(zod.z.object({ slug: zod.z.string() }).parse), require_api.fetchApi({
token,
endpoint: `/v2/projects/${encodeURIComponent(projectId)}?${teamParam}`
}).then(zod.z.object({ name: zod.z.string() }).parse)]);
return {
teamSlug: teamData.slug,
projectSlug: projectData.name
};
} catch {
return {};
}
}

@@ -85,3 +184,2 @@

exports.inferScope = inferScope;
exports.selectTeam = selectTeam;
//# sourceMappingURL=project.cjs.map

@@ -1,1 +0,1 @@

{"version":3,"file":"project.cjs","names":["z","readLinkedProject","fetchApi","NotOk"],"sources":["../../src/auth/project.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { fetchApi } from \"./api.js\";\nimport { NotOk } from \"./error.js\";\nimport { readLinkedProject } from \"./linked-project.js\";\n\nconst TeamsSchema = z.object({\n teams: z\n .array(\n z.object({\n slug: z.string(),\n }),\n )\n .min(1, `No teams found. Please create a team first.`),\n});\n\nconst DEFAULT_PROJECT_NAME = \"vercel-sandbox-default-project\";\n\n/**\n * Resolves the team and project scope for sandbox operations.\n *\n * First checks for a locally linked project in `.vercel/project.json`.\n * If found, uses the `projectId` and `orgId` from there.\n *\n * Otherwise, if `teamId` is not provided, selects the first available team for the account.\n * Ensures a default project exists within the team, creating it if necessary.\n *\n * @param opts.token - Vercel API authentication token.\n * @param opts.teamId - Optional team slug. If omitted, the first team is selected.\n * @param opts.cwd - Optional directory to search for `.vercel/project.json`. Defaults to `process.cwd()`.\n * @returns The resolved scope with `projectId`, `teamId`, and whether the project was `created`.\n *\n * @throws {NotOk} If the API returns an error other than 404 when checking the project.\n * @throws {ZodError} If no teams exist for the account.\n *\n * @example\n * ```ts\n * const scope = await inferScope({ token: \"vercel_...\" });\n * // => { projectId: \"vercel-sandbox-default-project\", teamId: \"my-team\", created: false }\n * ```\n */\nexport async function inferScope(opts: {\n token: string;\n teamId?: string;\n cwd?: string;\n}): Promise<{ projectId: string; teamId: string; created: boolean }> {\n const linkedProject = await readLinkedProject(opts.cwd ?? process.cwd());\n if (linkedProject) {\n return { ...linkedProject, created: false };\n }\n\n const teamId = opts.teamId ?? (await selectTeam(opts.token));\n\n let created = false;\n try {\n await fetchApi({\n token: opts.token,\n endpoint: `/v2/projects/${encodeURIComponent(DEFAULT_PROJECT_NAME)}?slug=${encodeURIComponent(teamId)}`,\n });\n } catch (e) {\n if (!(e instanceof NotOk) || e.response.statusCode !== 404) {\n throw e;\n }\n\n await fetchApi({\n token: opts.token,\n endpoint: `/v11/projects?slug=${encodeURIComponent(teamId)}`,\n method: \"POST\",\n body: JSON.stringify({\n name: DEFAULT_PROJECT_NAME,\n }),\n });\n created = true;\n }\n\n return { projectId: DEFAULT_PROJECT_NAME, teamId, created };\n}\n\n/**\n * Selects a team for the current token by querying the Teams API and\n * returning the slug of the first team in the result set.\n *\n * @param token - Authentication token used to call the Vercel API.\n * @returns A promise that resolves to the first team's slug.\n */\nexport async function selectTeam(token: string) {\n const {\n teams: [team],\n } = await fetchApi({ token, endpoint: \"/v2/teams?limit=1\" }).then(\n TeamsSchema.parse,\n );\n return team.slug;\n}\n"],"mappings":";;;;;;;AAKA,MAAM,cAAcA,MAAE,OAAO,EAC3B,OAAOA,MACJ,MACCA,MAAE,OAAO,EACP,MAAMA,MAAE,QAAQ,EACjB,CAAC,CACH,CACA,IAAI,GAAG,8CAA8C,EACzD,CAAC;AAEF,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;AAyB7B,eAAsB,WAAW,MAIoC;CACnE,MAAM,gBAAgB,MAAMC,yCAAkB,KAAK,OAAO,QAAQ,KAAK,CAAC;AACxE,KAAI,cACF,QAAO;EAAE,GAAG;EAAe,SAAS;EAAO;CAG7C,MAAM,SAAS,KAAK,UAAW,MAAM,WAAW,KAAK,MAAM;CAE3D,IAAI,UAAU;AACd,KAAI;AACF,QAAMC,qBAAS;GACb,OAAO,KAAK;GACZ,UAAU,gBAAgB,mBAAmB,qBAAqB,CAAC,QAAQ,mBAAmB,OAAO;GACtG,CAAC;UACK,GAAG;AACV,MAAI,EAAE,aAAaC,wBAAU,EAAE,SAAS,eAAe,IACrD,OAAM;AAGR,QAAMD,qBAAS;GACb,OAAO,KAAK;GACZ,UAAU,sBAAsB,mBAAmB,OAAO;GAC1D,QAAQ;GACR,MAAM,KAAK,UAAU,EACnB,MAAM,sBACP,CAAC;GACH,CAAC;AACF,YAAU;;AAGZ,QAAO;EAAE,WAAW;EAAsB;EAAQ;EAAS;;;;;;;;;AAU7D,eAAsB,WAAW,OAAe;CAC9C,MAAM,EACJ,OAAO,CAAC,UACN,MAAMA,qBAAS;EAAE;EAAO,UAAU;EAAqB,CAAC,CAAC,KAC3D,YAAY,MACb;AACD,QAAO,KAAK"}
{"version":3,"file":"project.cjs","names":["z","NotOk","readLinkedProject","fetchApi","next: number | null","endpoint: string"],"sources":["../../src/auth/project.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { fetchApi } from \"./api.js\";\nimport { NotOk } from \"./error.js\";\nimport { readLinkedProject } from \"./linked-project.js\";\n\nconst UserSchema = z.object({\n user: z.object({\n defaultTeamId: z.string().nullable(),\n username: z.string(),\n }),\n});\n\nconst TeamSchema = z.object({\n id: z.string(),\n slug: z.string(),\n updatedAt: z.number(),\n membership: z.object({\n role: z.string(),\n }),\n billing: z.object({\n plan: z.string(),\n }),\n});\n\nconst TeamsSchema = z.object({\n teams: z.array(TeamSchema),\n pagination: z.object({\n count: z.number(),\n next: z.number().nullable(),\n }),\n});\n\nconst DEFAULT_PROJECT_NAME = \"vercel-sandbox-default-project\";\n\n/** Status codes that mean \"this team can't be used, try the next one\". */\nfunction isSkippableTeamError(e: unknown): boolean {\n return e instanceof NotOk && (e.response.statusCode === 402 || e.response.statusCode === 403);\n}\n\n/**\n * Resolves the team and project scope for sandbox operations.\n *\n * First checks for a locally linked project in `.vercel/project.json`.\n * If found, uses the `projectId` and `orgId` from there.\n *\n * Otherwise, if `teamId` is not provided, builds an ordered list of candidate\n * teams to try: the user's `defaultTeamId` first (if set), then hobby-plan\n * teams where the user has an OWNER role (preferring the personal team matching\n * the username, then the most recently updated). Tries each candidate until one\n * succeeds.\n *\n * @param opts.token - Vercel API authentication token.\n * @param opts.teamId - Optional team slug. If omitted, candidate teams are resolved automatically.\n * @param opts.cwd - Optional directory to search for `.vercel/project.json`. Defaults to `process.cwd()`.\n * @returns The resolved scope with `projectId`, `teamId`, and whether the project was `created`.\n *\n * @throws {NotOk} If the API returns an error other than 404 when checking the project.\n *\n * @example\n * ```ts\n * const scope = await inferScope({ token: \"vercel_...\" });\n * // => { projectId: \"vercel-sandbox-default-project\", teamId: \"my-team\", created: false }\n * ```\n */\nexport async function inferScope(opts: {\n token: string;\n teamId?: string;\n cwd?: string;\n}): Promise<{\n projectId: string;\n teamId: string;\n created: boolean;\n teamSlug?: string;\n projectSlug?: string;\n}> {\n const linkedProject = await readLinkedProject(opts.cwd ?? process.cwd());\n if (linkedProject) {\n const slugs = await resolveLinkedProjectSlugs(\n opts.token,\n linkedProject.teamId,\n linkedProject.projectId,\n );\n return { ...linkedProject, created: false, ...slugs };\n }\n\n if (opts.teamId) {\n return tryTeam(opts.token, opts.teamId);\n }\n\n const userData = await fetchApi({\n token: opts.token,\n endpoint: \"/v2/user\",\n }).then(UserSchema.parse);\n const { defaultTeamId, username } = userData.user;\n\n // 1. Try defaultTeamId first\n if (defaultTeamId) {\n try {\n const result = await tryTeam(opts.token, defaultTeamId);\n // Resolve team slug (best-effort)\n try {\n const team = await fetchApi({\n token: opts.token,\n endpoint: `/v2/teams/${encodeURIComponent(defaultTeamId)}`,\n }).then(z.object({ slug: z.string() }).parse);\n return { ...result, teamSlug: team.slug };\n } catch {\n return result;\n }\n } catch (e) {\n if (!isSkippableTeamError(e)) throw e;\n }\n }\n\n // 2. Paginate teams in pages of 20, try best hobby team per page\n let next: number | null = null;\n do {\n const endpoint: string =\n next === null\n ? \"/v2/teams?limit=20\"\n : `/v2/teams?limit=20&until=${next}`;\n const page = await fetchApi({ token: opts.token, endpoint }).then(\n TeamsSchema.parse,\n );\n\n next = page.pagination.next;\n\n const hobbyOwnerTeams = page.teams.filter(\n (t) => t.membership.role === \"OWNER\" && t.billing.plan === \"hobby\",\n );\n if (hobbyOwnerTeams.length === 0) {\n continue;\n }\n\n const bestHobbyTeam =\n hobbyOwnerTeams.find((t) => t.slug === username) ??\n hobbyOwnerTeams.sort((a, b) => b.updatedAt - a.updatedAt)[0];\n\n if (bestHobbyTeam && bestHobbyTeam.id !== defaultTeamId) {\n try {\n const result = await tryTeam(opts.token, bestHobbyTeam.id);\n return { ...result, teamSlug: bestHobbyTeam.slug };\n } catch (e) {\n if (!isSkippableTeamError(e)) throw e;\n }\n }\n } while (next !== null);\n\n // 3. Fall back to username as personal team\n try {\n const result = await tryTeam(opts.token, username);\n return { ...result, teamSlug: username };\n } catch (e) {\n if (!isSkippableTeamError(e)) throw e;\n }\n\n throw new NotOk({\n statusCode: 403,\n responseText: `Authenticated as \"${username}\" but none of the available teams allow sandbox creation. Specify a team explicitly with --scope <team-id-or-slug>.`,\n });\n}\n\n/**\n * Attempts to use a specific team for sandbox operations by checking for\n * (or creating) the default project within that team.\n *\n * @returns The resolved scope if the team is usable.\n * @throws {NotOk} On authorization or other API errors.\n */\nasync function tryTeam(\n token: string,\n teamId: string,\n): Promise<{ projectId: string; teamId: string; created: boolean }> {\n const teamParam = teamId.startsWith(\"team_\")\n ? `teamId=${encodeURIComponent(teamId)}`\n : `slug=${encodeURIComponent(teamId)}`;\n\n let created = false;\n try {\n await fetchApi({\n token,\n endpoint: `/v2/projects/${encodeURIComponent(DEFAULT_PROJECT_NAME)}?${teamParam}`,\n });\n } catch (e) {\n if (!(e instanceof NotOk) || e.response.statusCode !== 404) {\n throw e;\n }\n\n await fetchApi({\n token,\n endpoint: `/v11/projects?${teamParam}`,\n method: \"POST\",\n body: JSON.stringify({\n name: DEFAULT_PROJECT_NAME,\n }),\n });\n created = true;\n }\n\n return { projectId: DEFAULT_PROJECT_NAME, teamId, created };\n}\n\n/**\n * Best-effort resolution of team slug and project name for a linked project.\n * Both IDs may be opaque (e.g. `team_xxx`, `prj_xxx`), so we fetch the\n * human-readable names from the API in parallel.\n */\nasync function resolveLinkedProjectSlugs(\n token: string,\n teamId: string,\n projectId: string,\n): Promise<{ teamSlug?: string; projectSlug?: string }> {\n try {\n const teamParam = teamId.startsWith(\"team_\")\n ? `teamId=${encodeURIComponent(teamId)}`\n : `slug=${encodeURIComponent(teamId)}`;\n const [teamData, projectData] = await Promise.all([\n fetchApi({\n token,\n endpoint: `/v2/teams/${encodeURIComponent(teamId)}`,\n }).then(z.object({ slug: z.string() }).parse),\n fetchApi({\n token,\n endpoint: `/v2/projects/${encodeURIComponent(projectId)}?${teamParam}`,\n }).then(z.object({ name: z.string() }).parse),\n ]);\n return { teamSlug: teamData.slug, projectSlug: projectData.name };\n } catch {\n return {};\n }\n}\n"],"mappings":"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"}

@@ -8,7 +8,10 @@ //#region src/auth/project.d.ts

*
* Otherwise, if `teamId` is not provided, selects the first available team for the account.
* Ensures a default project exists within the team, creating it if necessary.
* Otherwise, if `teamId` is not provided, builds an ordered list of candidate
* teams to try: the user's `defaultTeamId` first (if set), then hobby-plan
* teams where the user has an OWNER role (preferring the personal team matching
* the username, then the most recently updated). Tries each candidate until one
* succeeds.
*
* @param opts.token - Vercel API authentication token.
* @param opts.teamId - Optional team slug. If omitted, the first team is selected.
* @param opts.teamId - Optional team slug. If omitted, candidate teams are resolved automatically.
* @param opts.cwd - Optional directory to search for `.vercel/project.json`. Defaults to `process.cwd()`.

@@ -18,3 +21,2 @@ * @returns The resolved scope with `projectId`, `teamId`, and whether the project was `created`.

* @throws {NotOk} If the API returns an error other than 404 when checking the project.
* @throws {ZodError} If no teams exist for the account.
*

@@ -35,13 +37,7 @@ * @example

created: boolean;
teamSlug?: string;
projectSlug?: string;
}>;
/**
* Selects a team for the current token by querying the Teams API and
* returning the slug of the first team in the result set.
*
* @param token - Authentication token used to call the Vercel API.
* @returns A promise that resolves to the first team's slug.
*/
declare function selectTeam(token: string): Promise<string>;
//#endregion
export { inferScope, selectTeam };
export { inferScope };
//# sourceMappingURL=project.d.cts.map

@@ -8,7 +8,10 @@ //#region src/auth/project.d.ts

*
* Otherwise, if `teamId` is not provided, selects the first available team for the account.
* Ensures a default project exists within the team, creating it if necessary.
* Otherwise, if `teamId` is not provided, builds an ordered list of candidate
* teams to try: the user's `defaultTeamId` first (if set), then hobby-plan
* teams where the user has an OWNER role (preferring the personal team matching
* the username, then the most recently updated). Tries each candidate until one
* succeeds.
*
* @param opts.token - Vercel API authentication token.
* @param opts.teamId - Optional team slug. If omitted, the first team is selected.
* @param opts.teamId - Optional team slug. If omitted, candidate teams are resolved automatically.
* @param opts.cwd - Optional directory to search for `.vercel/project.json`. Defaults to `process.cwd()`.

@@ -18,3 +21,2 @@ * @returns The resolved scope with `projectId`, `teamId`, and whether the project was `created`.

* @throws {NotOk} If the API returns an error other than 404 when checking the project.
* @throws {ZodError} If no teams exist for the account.
*

@@ -35,13 +37,7 @@ * @example

created: boolean;
teamSlug?: string;
projectSlug?: string;
}>;
/**
* Selects a team for the current token by querying the Teams API and
* returning the slug of the first team in the result set.
*
* @param token - Authentication token used to call the Vercel API.
* @returns A promise that resolves to the first team's slug.
*/
declare function selectTeam(token: string): Promise<string>;
//#endregion
export { inferScope, selectTeam };
export { inferScope };
//# sourceMappingURL=project.d.ts.map

@@ -7,4 +7,25 @@ import { NotOk } from "./error.js";

//#region src/auth/project.ts
const TeamsSchema = z.object({ teams: z.array(z.object({ slug: z.string() })).min(1, `No teams found. Please create a team first.`) });
const UserSchema = z.object({ user: z.object({
defaultTeamId: z.string().nullable(),
username: z.string()
}) });
const TeamSchema = z.object({
id: z.string(),
slug: z.string(),
updatedAt: z.number(),
membership: z.object({ role: z.string() }),
billing: z.object({ plan: z.string() })
});
const TeamsSchema = z.object({
teams: z.array(TeamSchema),
pagination: z.object({
count: z.number(),
next: z.number().nullable()
})
});
const DEFAULT_PROJECT_NAME = "vercel-sandbox-default-project";
/** Status codes that mean "this team can't be used, try the next one". */
function isSkippableTeamError(e) {
return e instanceof NotOk && (e.response.statusCode === 402 || e.response.statusCode === 403);
}
/**

@@ -16,7 +37,10 @@ * Resolves the team and project scope for sandbox operations.

*
* Otherwise, if `teamId` is not provided, selects the first available team for the account.
* Ensures a default project exists within the team, creating it if necessary.
* Otherwise, if `teamId` is not provided, builds an ordered list of candidate
* teams to try: the user's `defaultTeamId` first (if set), then hobby-plan
* teams where the user has an OWNER role (preferring the personal team matching
* the username, then the most recently updated). Tries each candidate until one
* succeeds.
*
* @param opts.token - Vercel API authentication token.
* @param opts.teamId - Optional team slug. If omitted, the first team is selected.
* @param opts.teamId - Optional team slug. If omitted, candidate teams are resolved automatically.
* @param opts.cwd - Optional directory to search for `.vercel/project.json`. Defaults to `process.cwd()`.

@@ -26,3 +50,2 @@ * @returns The resolved scope with `projectId`, `teamId`, and whether the project was `created`.

* @throws {NotOk} If the API returns an error other than 404 when checking the project.
* @throws {ZodError} If no teams exist for the account.
*

@@ -37,12 +60,79 @@ * @example

const linkedProject = await readLinkedProject(opts.cwd ?? process.cwd());
if (linkedProject) return {
...linkedProject,
created: false
};
const teamId = opts.teamId ?? await selectTeam(opts.token);
if (linkedProject) {
const slugs = await resolveLinkedProjectSlugs(opts.token, linkedProject.teamId, linkedProject.projectId);
return {
...linkedProject,
created: false,
...slugs
};
}
if (opts.teamId) return tryTeam(opts.token, opts.teamId);
const { defaultTeamId, username } = (await fetchApi({
token: opts.token,
endpoint: "/v2/user"
}).then(UserSchema.parse)).user;
if (defaultTeamId) try {
const result = await tryTeam(opts.token, defaultTeamId);
try {
const team = await fetchApi({
token: opts.token,
endpoint: `/v2/teams/${encodeURIComponent(defaultTeamId)}`
}).then(z.object({ slug: z.string() }).parse);
return {
...result,
teamSlug: team.slug
};
} catch {
return result;
}
} catch (e) {
if (!isSkippableTeamError(e)) throw e;
}
let next = null;
do {
const endpoint = next === null ? "/v2/teams?limit=20" : `/v2/teams?limit=20&until=${next}`;
const page = await fetchApi({
token: opts.token,
endpoint
}).then(TeamsSchema.parse);
next = page.pagination.next;
const hobbyOwnerTeams = page.teams.filter((t) => t.membership.role === "OWNER" && t.billing.plan === "hobby");
if (hobbyOwnerTeams.length === 0) continue;
const bestHobbyTeam = hobbyOwnerTeams.find((t) => t.slug === username) ?? hobbyOwnerTeams.sort((a, b) => b.updatedAt - a.updatedAt)[0];
if (bestHobbyTeam && bestHobbyTeam.id !== defaultTeamId) try {
return {
...await tryTeam(opts.token, bestHobbyTeam.id),
teamSlug: bestHobbyTeam.slug
};
} catch (e) {
if (!isSkippableTeamError(e)) throw e;
}
} while (next !== null);
try {
return {
...await tryTeam(opts.token, username),
teamSlug: username
};
} catch (e) {
if (!isSkippableTeamError(e)) throw e;
}
throw new NotOk({
statusCode: 403,
responseText: `Authenticated as "${username}" but none of the available teams allow sandbox creation. Specify a team explicitly with --scope <team-id-or-slug>.`
});
}
/**
* Attempts to use a specific team for sandbox operations by checking for
* (or creating) the default project within that team.
*
* @returns The resolved scope if the team is usable.
* @throws {NotOk} On authorization or other API errors.
*/
async function tryTeam(token, teamId) {
const teamParam = teamId.startsWith("team_") ? `teamId=${encodeURIComponent(teamId)}` : `slug=${encodeURIComponent(teamId)}`;
let created = false;
try {
await fetchApi({
token: opts.token,
endpoint: `/v2/projects/${encodeURIComponent(DEFAULT_PROJECT_NAME)}?slug=${encodeURIComponent(teamId)}`
token,
endpoint: `/v2/projects/${encodeURIComponent(DEFAULT_PROJECT_NAME)}?${teamParam}`
});

@@ -52,4 +142,4 @@ } catch (e) {

await fetchApi({
token: opts.token,
endpoint: `/v11/projects?slug=${encodeURIComponent(teamId)}`,
token,
endpoint: `/v11/projects?${teamParam}`,
method: "POST",

@@ -67,18 +157,27 @@ body: JSON.stringify({ name: DEFAULT_PROJECT_NAME })

/**
* Selects a team for the current token by querying the Teams API and
* returning the slug of the first team in the result set.
*
* @param token - Authentication token used to call the Vercel API.
* @returns A promise that resolves to the first team's slug.
* Best-effort resolution of team slug and project name for a linked project.
* Both IDs may be opaque (e.g. `team_xxx`, `prj_xxx`), so we fetch the
* human-readable names from the API in parallel.
*/
async function selectTeam(token) {
const { teams: [team] } = await fetchApi({
token,
endpoint: "/v2/teams?limit=1"
}).then(TeamsSchema.parse);
return team.slug;
async function resolveLinkedProjectSlugs(token, teamId, projectId) {
try {
const teamParam = teamId.startsWith("team_") ? `teamId=${encodeURIComponent(teamId)}` : `slug=${encodeURIComponent(teamId)}`;
const [teamData, projectData] = await Promise.all([fetchApi({
token,
endpoint: `/v2/teams/${encodeURIComponent(teamId)}`
}).then(z.object({ slug: z.string() }).parse), fetchApi({
token,
endpoint: `/v2/projects/${encodeURIComponent(projectId)}?${teamParam}`
}).then(z.object({ name: z.string() }).parse)]);
return {
teamSlug: teamData.slug,
projectSlug: projectData.name
};
} catch {
return {};
}
}
//#endregion
export { inferScope, selectTeam };
export { inferScope };
//# sourceMappingURL=project.js.map

@@ -1,1 +0,1 @@

{"version":3,"file":"project.js","names":[],"sources":["../../src/auth/project.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { fetchApi } from \"./api.js\";\nimport { NotOk } from \"./error.js\";\nimport { readLinkedProject } from \"./linked-project.js\";\n\nconst TeamsSchema = z.object({\n teams: z\n .array(\n z.object({\n slug: z.string(),\n }),\n )\n .min(1, `No teams found. Please create a team first.`),\n});\n\nconst DEFAULT_PROJECT_NAME = \"vercel-sandbox-default-project\";\n\n/**\n * Resolves the team and project scope for sandbox operations.\n *\n * First checks for a locally linked project in `.vercel/project.json`.\n * If found, uses the `projectId` and `orgId` from there.\n *\n * Otherwise, if `teamId` is not provided, selects the first available team for the account.\n * Ensures a default project exists within the team, creating it if necessary.\n *\n * @param opts.token - Vercel API authentication token.\n * @param opts.teamId - Optional team slug. If omitted, the first team is selected.\n * @param opts.cwd - Optional directory to search for `.vercel/project.json`. Defaults to `process.cwd()`.\n * @returns The resolved scope with `projectId`, `teamId`, and whether the project was `created`.\n *\n * @throws {NotOk} If the API returns an error other than 404 when checking the project.\n * @throws {ZodError} If no teams exist for the account.\n *\n * @example\n * ```ts\n * const scope = await inferScope({ token: \"vercel_...\" });\n * // => { projectId: \"vercel-sandbox-default-project\", teamId: \"my-team\", created: false }\n * ```\n */\nexport async function inferScope(opts: {\n token: string;\n teamId?: string;\n cwd?: string;\n}): Promise<{ projectId: string; teamId: string; created: boolean }> {\n const linkedProject = await readLinkedProject(opts.cwd ?? process.cwd());\n if (linkedProject) {\n return { ...linkedProject, created: false };\n }\n\n const teamId = opts.teamId ?? (await selectTeam(opts.token));\n\n let created = false;\n try {\n await fetchApi({\n token: opts.token,\n endpoint: `/v2/projects/${encodeURIComponent(DEFAULT_PROJECT_NAME)}?slug=${encodeURIComponent(teamId)}`,\n });\n } catch (e) {\n if (!(e instanceof NotOk) || e.response.statusCode !== 404) {\n throw e;\n }\n\n await fetchApi({\n token: opts.token,\n endpoint: `/v11/projects?slug=${encodeURIComponent(teamId)}`,\n method: \"POST\",\n body: JSON.stringify({\n name: DEFAULT_PROJECT_NAME,\n }),\n });\n created = true;\n }\n\n return { projectId: DEFAULT_PROJECT_NAME, teamId, created };\n}\n\n/**\n * Selects a team for the current token by querying the Teams API and\n * returning the slug of the first team in the result set.\n *\n * @param token - Authentication token used to call the Vercel API.\n * @returns A promise that resolves to the first team's slug.\n */\nexport async function selectTeam(token: string) {\n const {\n teams: [team],\n } = await fetchApi({ token, endpoint: \"/v2/teams?limit=1\" }).then(\n TeamsSchema.parse,\n );\n return team.slug;\n}\n"],"mappings":";;;;;;AAKA,MAAM,cAAc,EAAE,OAAO,EAC3B,OAAO,EACJ,MACC,EAAE,OAAO,EACP,MAAM,EAAE,QAAQ,EACjB,CAAC,CACH,CACA,IAAI,GAAG,8CAA8C,EACzD,CAAC;AAEF,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;AAyB7B,eAAsB,WAAW,MAIoC;CACnE,MAAM,gBAAgB,MAAM,kBAAkB,KAAK,OAAO,QAAQ,KAAK,CAAC;AACxE,KAAI,cACF,QAAO;EAAE,GAAG;EAAe,SAAS;EAAO;CAG7C,MAAM,SAAS,KAAK,UAAW,MAAM,WAAW,KAAK,MAAM;CAE3D,IAAI,UAAU;AACd,KAAI;AACF,QAAM,SAAS;GACb,OAAO,KAAK;GACZ,UAAU,gBAAgB,mBAAmB,qBAAqB,CAAC,QAAQ,mBAAmB,OAAO;GACtG,CAAC;UACK,GAAG;AACV,MAAI,EAAE,aAAa,UAAU,EAAE,SAAS,eAAe,IACrD,OAAM;AAGR,QAAM,SAAS;GACb,OAAO,KAAK;GACZ,UAAU,sBAAsB,mBAAmB,OAAO;GAC1D,QAAQ;GACR,MAAM,KAAK,UAAU,EACnB,MAAM,sBACP,CAAC;GACH,CAAC;AACF,YAAU;;AAGZ,QAAO;EAAE,WAAW;EAAsB;EAAQ;EAAS;;;;;;;;;AAU7D,eAAsB,WAAW,OAAe;CAC9C,MAAM,EACJ,OAAO,CAAC,UACN,MAAM,SAAS;EAAE;EAAO,UAAU;EAAqB,CAAC,CAAC,KAC3D,YAAY,MACb;AACD,QAAO,KAAK"}
{"version":3,"file":"project.js","names":["next: number | null","endpoint: string"],"sources":["../../src/auth/project.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { fetchApi } from \"./api.js\";\nimport { NotOk } from \"./error.js\";\nimport { readLinkedProject } from \"./linked-project.js\";\n\nconst UserSchema = z.object({\n user: z.object({\n defaultTeamId: z.string().nullable(),\n username: z.string(),\n }),\n});\n\nconst TeamSchema = z.object({\n id: z.string(),\n slug: z.string(),\n updatedAt: z.number(),\n membership: z.object({\n role: z.string(),\n }),\n billing: z.object({\n plan: z.string(),\n }),\n});\n\nconst TeamsSchema = z.object({\n teams: z.array(TeamSchema),\n pagination: z.object({\n count: z.number(),\n next: z.number().nullable(),\n }),\n});\n\nconst DEFAULT_PROJECT_NAME = \"vercel-sandbox-default-project\";\n\n/** Status codes that mean \"this team can't be used, try the next one\". */\nfunction isSkippableTeamError(e: unknown): boolean {\n return e instanceof NotOk && (e.response.statusCode === 402 || e.response.statusCode === 403);\n}\n\n/**\n * Resolves the team and project scope for sandbox operations.\n *\n * First checks for a locally linked project in `.vercel/project.json`.\n * If found, uses the `projectId` and `orgId` from there.\n *\n * Otherwise, if `teamId` is not provided, builds an ordered list of candidate\n * teams to try: the user's `defaultTeamId` first (if set), then hobby-plan\n * teams where the user has an OWNER role (preferring the personal team matching\n * the username, then the most recently updated). Tries each candidate until one\n * succeeds.\n *\n * @param opts.token - Vercel API authentication token.\n * @param opts.teamId - Optional team slug. If omitted, candidate teams are resolved automatically.\n * @param opts.cwd - Optional directory to search for `.vercel/project.json`. Defaults to `process.cwd()`.\n * @returns The resolved scope with `projectId`, `teamId`, and whether the project was `created`.\n *\n * @throws {NotOk} If the API returns an error other than 404 when checking the project.\n *\n * @example\n * ```ts\n * const scope = await inferScope({ token: \"vercel_...\" });\n * // => { projectId: \"vercel-sandbox-default-project\", teamId: \"my-team\", created: false }\n * ```\n */\nexport async function inferScope(opts: {\n token: string;\n teamId?: string;\n cwd?: string;\n}): Promise<{\n projectId: string;\n teamId: string;\n created: boolean;\n teamSlug?: string;\n projectSlug?: string;\n}> {\n const linkedProject = await readLinkedProject(opts.cwd ?? process.cwd());\n if (linkedProject) {\n const slugs = await resolveLinkedProjectSlugs(\n opts.token,\n linkedProject.teamId,\n linkedProject.projectId,\n );\n return { ...linkedProject, created: false, ...slugs };\n }\n\n if (opts.teamId) {\n return tryTeam(opts.token, opts.teamId);\n }\n\n const userData = await fetchApi({\n token: opts.token,\n endpoint: \"/v2/user\",\n }).then(UserSchema.parse);\n const { defaultTeamId, username } = userData.user;\n\n // 1. Try defaultTeamId first\n if (defaultTeamId) {\n try {\n const result = await tryTeam(opts.token, defaultTeamId);\n // Resolve team slug (best-effort)\n try {\n const team = await fetchApi({\n token: opts.token,\n endpoint: `/v2/teams/${encodeURIComponent(defaultTeamId)}`,\n }).then(z.object({ slug: z.string() }).parse);\n return { ...result, teamSlug: team.slug };\n } catch {\n return result;\n }\n } catch (e) {\n if (!isSkippableTeamError(e)) throw e;\n }\n }\n\n // 2. Paginate teams in pages of 20, try best hobby team per page\n let next: number | null = null;\n do {\n const endpoint: string =\n next === null\n ? \"/v2/teams?limit=20\"\n : `/v2/teams?limit=20&until=${next}`;\n const page = await fetchApi({ token: opts.token, endpoint }).then(\n TeamsSchema.parse,\n );\n\n next = page.pagination.next;\n\n const hobbyOwnerTeams = page.teams.filter(\n (t) => t.membership.role === \"OWNER\" && t.billing.plan === \"hobby\",\n );\n if (hobbyOwnerTeams.length === 0) {\n continue;\n }\n\n const bestHobbyTeam =\n hobbyOwnerTeams.find((t) => t.slug === username) ??\n hobbyOwnerTeams.sort((a, b) => b.updatedAt - a.updatedAt)[0];\n\n if (bestHobbyTeam && bestHobbyTeam.id !== defaultTeamId) {\n try {\n const result = await tryTeam(opts.token, bestHobbyTeam.id);\n return { ...result, teamSlug: bestHobbyTeam.slug };\n } catch (e) {\n if (!isSkippableTeamError(e)) throw e;\n }\n }\n } while (next !== null);\n\n // 3. Fall back to username as personal team\n try {\n const result = await tryTeam(opts.token, username);\n return { ...result, teamSlug: username };\n } catch (e) {\n if (!isSkippableTeamError(e)) throw e;\n }\n\n throw new NotOk({\n statusCode: 403,\n responseText: `Authenticated as \"${username}\" but none of the available teams allow sandbox creation. Specify a team explicitly with --scope <team-id-or-slug>.`,\n });\n}\n\n/**\n * Attempts to use a specific team for sandbox operations by checking for\n * (or creating) the default project within that team.\n *\n * @returns The resolved scope if the team is usable.\n * @throws {NotOk} On authorization or other API errors.\n */\nasync function tryTeam(\n token: string,\n teamId: string,\n): Promise<{ projectId: string; teamId: string; created: boolean }> {\n const teamParam = teamId.startsWith(\"team_\")\n ? `teamId=${encodeURIComponent(teamId)}`\n : `slug=${encodeURIComponent(teamId)}`;\n\n let created = false;\n try {\n await fetchApi({\n token,\n endpoint: `/v2/projects/${encodeURIComponent(DEFAULT_PROJECT_NAME)}?${teamParam}`,\n });\n } catch (e) {\n if (!(e instanceof NotOk) || e.response.statusCode !== 404) {\n throw e;\n }\n\n await fetchApi({\n token,\n endpoint: `/v11/projects?${teamParam}`,\n method: \"POST\",\n body: JSON.stringify({\n name: DEFAULT_PROJECT_NAME,\n }),\n });\n created = true;\n }\n\n return { projectId: DEFAULT_PROJECT_NAME, teamId, created };\n}\n\n/**\n * Best-effort resolution of team slug and project name for a linked project.\n * Both IDs may be opaque (e.g. `team_xxx`, `prj_xxx`), so we fetch the\n * human-readable names from the API in parallel.\n */\nasync function resolveLinkedProjectSlugs(\n token: string,\n teamId: string,\n projectId: string,\n): Promise<{ teamSlug?: string; projectSlug?: string }> {\n try {\n const teamParam = teamId.startsWith(\"team_\")\n ? `teamId=${encodeURIComponent(teamId)}`\n : `slug=${encodeURIComponent(teamId)}`;\n const [teamData, projectData] = await Promise.all([\n fetchApi({\n token,\n endpoint: `/v2/teams/${encodeURIComponent(teamId)}`,\n }).then(z.object({ slug: z.string() }).parse),\n fetchApi({\n token,\n endpoint: `/v2/projects/${encodeURIComponent(projectId)}?${teamParam}`,\n }).then(z.object({ name: z.string() }).parse),\n ]);\n return { teamSlug: teamData.slug, projectSlug: projectData.name };\n } catch {\n return {};\n }\n}\n"],"mappings":";;;;;;AAKA,MAAM,aAAa,EAAE,OAAO,EAC1B,MAAM,EAAE,OAAO;CACb,eAAe,EAAE,QAAQ,CAAC,UAAU;CACpC,UAAU,EAAE,QAAQ;CACrB,CAAC,EACH,CAAC;AAEF,MAAM,aAAa,EAAE,OAAO;CAC1B,IAAI,EAAE,QAAQ;CACd,MAAM,EAAE,QAAQ;CAChB,WAAW,EAAE,QAAQ;CACrB,YAAY,EAAE,OAAO,EACnB,MAAM,EAAE,QAAQ,EACjB,CAAC;CACF,SAAS,EAAE,OAAO,EAChB,MAAM,EAAE,QAAQ,EACjB,CAAC;CACH,CAAC;AAEF,MAAM,cAAc,EAAE,OAAO;CAC3B,OAAO,EAAE,MAAM,WAAW;CAC1B,YAAY,EAAE,OAAO;EACnB,OAAO,EAAE,QAAQ;EACjB,MAAM,EAAE,QAAQ,CAAC,UAAU;EAC5B,CAAC;CACH,CAAC;AAEF,MAAM,uBAAuB;;AAG7B,SAAS,qBAAqB,GAAqB;AACjD,QAAO,aAAa,UAAU,EAAE,SAAS,eAAe,OAAO,EAAE,SAAS,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4B3F,eAAsB,WAAW,MAU9B;CACD,MAAM,gBAAgB,MAAM,kBAAkB,KAAK,OAAO,QAAQ,KAAK,CAAC;AACxE,KAAI,eAAe;EACjB,MAAM,QAAQ,MAAM,0BAClB,KAAK,OACL,cAAc,QACd,cAAc,UACf;AACD,SAAO;GAAE,GAAG;GAAe,SAAS;GAAO,GAAG;GAAO;;AAGvD,KAAI,KAAK,OACP,QAAO,QAAQ,KAAK,OAAO,KAAK,OAAO;CAOzC,MAAM,EAAE,eAAe,cAJN,MAAM,SAAS;EAC9B,OAAO,KAAK;EACZ,UAAU;EACX,CAAC,CAAC,KAAK,WAAW,MAAM,EACoB;AAG7C,KAAI,cACF,KAAI;EACF,MAAM,SAAS,MAAM,QAAQ,KAAK,OAAO,cAAc;AAEvD,MAAI;GACF,MAAM,OAAO,MAAM,SAAS;IAC1B,OAAO,KAAK;IACZ,UAAU,aAAa,mBAAmB,cAAc;IACzD,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM;AAC7C,UAAO;IAAE,GAAG;IAAQ,UAAU,KAAK;IAAM;UACnC;AACN,UAAO;;UAEF,GAAG;AACV,MAAI,CAAC,qBAAqB,EAAE,CAAE,OAAM;;CAKxC,IAAIA,OAAsB;AAC1B,IAAG;EACD,MAAMC,WACJ,SAAS,OACL,uBACA,4BAA4B;EAClC,MAAM,OAAO,MAAM,SAAS;GAAE,OAAO,KAAK;GAAO;GAAU,CAAC,CAAC,KAC3D,YAAY,MACb;AAED,SAAO,KAAK,WAAW;EAEvB,MAAM,kBAAkB,KAAK,MAAM,QAChC,MAAM,EAAE,WAAW,SAAS,WAAW,EAAE,QAAQ,SAAS,QAC5D;AACD,MAAI,gBAAgB,WAAW,EAC7B;EAGF,MAAM,gBACJ,gBAAgB,MAAM,MAAM,EAAE,SAAS,SAAS,IAChD,gBAAgB,MAAM,GAAG,MAAM,EAAE,YAAY,EAAE,UAAU,CAAC;AAE5D,MAAI,iBAAiB,cAAc,OAAO,cACxC,KAAI;AAEF,UAAO;IAAE,GADM,MAAM,QAAQ,KAAK,OAAO,cAAc,GAAG;IACtC,UAAU,cAAc;IAAM;WAC3C,GAAG;AACV,OAAI,CAAC,qBAAqB,EAAE,CAAE,OAAM;;UAGjC,SAAS;AAGlB,KAAI;AAEF,SAAO;GAAE,GADM,MAAM,QAAQ,KAAK,OAAO,SAAS;GAC9B,UAAU;GAAU;UACjC,GAAG;AACV,MAAI,CAAC,qBAAqB,EAAE,CAAE,OAAM;;AAGtC,OAAM,IAAI,MAAM;EACd,YAAY;EACZ,cAAc,qBAAqB,SAAS;EAC7C,CAAC;;;;;;;;;AAUJ,eAAe,QACb,OACA,QACkE;CAClE,MAAM,YAAY,OAAO,WAAW,QAAQ,GACxC,UAAU,mBAAmB,OAAO,KACpC,QAAQ,mBAAmB,OAAO;CAEtC,IAAI,UAAU;AACd,KAAI;AACF,QAAM,SAAS;GACb;GACA,UAAU,gBAAgB,mBAAmB,qBAAqB,CAAC,GAAG;GACvE,CAAC;UACK,GAAG;AACV,MAAI,EAAE,aAAa,UAAU,EAAE,SAAS,eAAe,IACrD,OAAM;AAGR,QAAM,SAAS;GACb;GACA,UAAU,iBAAiB;GAC3B,QAAQ;GACR,MAAM,KAAK,UAAU,EACnB,MAAM,sBACP,CAAC;GACH,CAAC;AACF,YAAU;;AAGZ,QAAO;EAAE,WAAW;EAAsB;EAAQ;EAAS;;;;;;;AAQ7D,eAAe,0BACb,OACA,QACA,WACsD;AACtD,KAAI;EACF,MAAM,YAAY,OAAO,WAAW,QAAQ,GACxC,UAAU,mBAAmB,OAAO,KACpC,QAAQ,mBAAmB,OAAO;EACtC,MAAM,CAAC,UAAU,eAAe,MAAM,QAAQ,IAAI,CAChD,SAAS;GACP;GACA,UAAU,aAAa,mBAAmB,OAAO;GAClD,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAC7C,SAAS;GACP;GACA,UAAU,gBAAgB,mBAAmB,UAAU,CAAC,GAAG;GAC5D,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,CAC9C,CAAC;AACF,SAAO;GAAE,UAAU,SAAS;GAAM,aAAa,YAAY;GAAM;SAC3D;AACN,SAAO,EAAE"}

@@ -5,2 +5,3 @@ const require_api_error = require('./api-client/api-error.cjs');

const require_session = require('./session.cjs');
const require_filesystem = require('./filesystem.cjs');
const require_sandbox = require('./sandbox.cjs');

@@ -11,2 +12,3 @@

exports.CommandFinished = require_command.CommandFinished;
exports.FileSystem = require_filesystem.FileSystem;
exports.Sandbox = require_sandbox.Sandbox;

@@ -13,0 +15,0 @@ exports.Session = require_session.Session;

import { APIError, StreamError } from "./api-client/api-error.cjs";
import { NetworkPolicy, NetworkPolicyRule, NetworkTransformer } from "./network-policy.cjs";
import { NetworkPolicy, NetworkPolicyKeyValueMatcher, NetworkPolicyMatch, NetworkPolicyMatcher, NetworkPolicyRule, NetworkTransformer } from "./network-policy.cjs";
import { Command, CommandFinished, CommandOutput, SerializedCommand, SerializedCommandFinished } from "./command.cjs";
import { Snapshot } from "./snapshot.cjs";
import { SerializedSnapshot, Snapshot } from "./snapshot.cjs";
import { Session } from "./session.cjs";
import { FileSystem } from "./filesystem.cjs";
import { Sandbox, SerializedSandbox } from "./sandbox.cjs";
export { APIError, Command, CommandFinished, type CommandOutput, type NetworkPolicy, type NetworkPolicyRule, type NetworkTransformer, Sandbox, type SerializedCommand, type SerializedCommandFinished, type SerializedSandbox, Session, Snapshot, StreamError };
export { APIError, Command, CommandFinished, type CommandOutput, FileSystem, type NetworkPolicy, type NetworkPolicyKeyValueMatcher, type NetworkPolicyMatch, type NetworkPolicyMatcher, type NetworkPolicyRule, type NetworkTransformer, Sandbox, type SerializedCommand, type SerializedCommandFinished, type SerializedSandbox, type SerializedSnapshot, Session, Snapshot, StreamError };
import { APIError, StreamError } from "./api-client/api-error.js";
import { NetworkPolicy, NetworkPolicyRule, NetworkTransformer } from "./network-policy.js";
import { NetworkPolicy, NetworkPolicyKeyValueMatcher, NetworkPolicyMatch, NetworkPolicyMatcher, NetworkPolicyRule, NetworkTransformer } from "./network-policy.js";
import { Command, CommandFinished, CommandOutput, SerializedCommand, SerializedCommandFinished } from "./command.js";
import { Snapshot } from "./snapshot.js";
import { SerializedSnapshot, Snapshot } from "./snapshot.js";
import { Session } from "./session.js";
import { FileSystem } from "./filesystem.js";
import { Sandbox, SerializedSandbox } from "./sandbox.js";
export { APIError, Command, CommandFinished, type CommandOutput, type NetworkPolicy, type NetworkPolicyRule, type NetworkTransformer, Sandbox, type SerializedCommand, type SerializedCommandFinished, type SerializedSandbox, Session, Snapshot, StreamError };
export { APIError, Command, CommandFinished, type CommandOutput, FileSystem, type NetworkPolicy, type NetworkPolicyKeyValueMatcher, type NetworkPolicyMatch, type NetworkPolicyMatcher, type NetworkPolicyRule, type NetworkTransformer, Sandbox, type SerializedCommand, type SerializedCommandFinished, type SerializedSandbox, type SerializedSnapshot, Session, Snapshot, StreamError };

@@ -5,4 +5,5 @@ import { APIError, StreamError } from "./api-client/api-error.js";

import { Session } from "./session.js";
import { FileSystem } from "./filesystem.js";
import { Sandbox } from "./sandbox.js";
export { APIError, Command, CommandFinished, Sandbox, Session, Snapshot, StreamError };
export { APIError, Command, CommandFinished, FileSystem, Sandbox, Session, Snapshot, StreamError };

@@ -15,7 +15,56 @@ //#region src/network-policy.d.ts

/**
* Defines how a request value is matched.
*/
type NetworkPolicyMatcher = {
/** Match the value exactly. */
exact?: string;
} | {
/** Match values that start with the provided prefix. */
startsWith?: string;
} | {
/** Match values against an RE2 regular expression. */
regex?: string;
};
/**
* Matcher for key/value request entries such as headers and query parameters.
*/
type NetworkPolicyKeyValueMatcher = {
/** Matcher for the entry key. */
key?: NetworkPolicyMatcher;
/** Matcher for the entry value. */
value?: NetworkPolicyMatcher;
};
/**
* Request matcher for a network policy rule.
*
* All specified dimensions must match. Multiple methods are ORed; multiple
* header and query-string matchers are ANDed.
*/
type NetworkPolicyMatch = {
/** Match on the request path. */
path?: NetworkPolicyMatcher;
/** Match on the HTTP method. */
method?: string[];
/** Match on query-string entries. */
queryString?: NetworkPolicyKeyValueMatcher[];
/** Match on request headers. */
headers?: NetworkPolicyKeyValueMatcher[];
};
/**
* A rule applied to requests matching a domain in the network policy.
*/
type NetworkPolicyRule = {
/** Transforms to apply to matching requests. */
/**
* Optional request matcher. When provided, transforms only apply to requests
* that match every specified dimension.
*/
match?: NetworkPolicyMatch;
/**
* Transforms to apply to matching requests.
*/
transform?: NetworkTransformer[];
/**
* HTTPS proxy URL to forward matching requests to. Must not include query string or fragment.
*/
forwardURL?: string;
};

@@ -63,2 +112,9 @@ /**

* {
* match: {
* method: ["POST"],
* path: { startsWith: "/v1/" },
* headers: [
* { key: { exact: "x-api-key" }, value: { exact: "placeholder" } }
* ]
* },
* transform: [{

@@ -101,3 +157,3 @@ * headers: { authorization: "Bearer ..." }

//#endregion
export { NetworkPolicy, NetworkPolicyRule, NetworkTransformer };
export { NetworkPolicy, NetworkPolicyKeyValueMatcher, NetworkPolicyMatch, NetworkPolicyMatcher, NetworkPolicyRule, NetworkTransformer };
//# sourceMappingURL=network-policy.d.cts.map

@@ -15,7 +15,56 @@ //#region src/network-policy.d.ts

/**
* Defines how a request value is matched.
*/
type NetworkPolicyMatcher = {
/** Match the value exactly. */
exact?: string;
} | {
/** Match values that start with the provided prefix. */
startsWith?: string;
} | {
/** Match values against an RE2 regular expression. */
regex?: string;
};
/**
* Matcher for key/value request entries such as headers and query parameters.
*/
type NetworkPolicyKeyValueMatcher = {
/** Matcher for the entry key. */
key?: NetworkPolicyMatcher;
/** Matcher for the entry value. */
value?: NetworkPolicyMatcher;
};
/**
* Request matcher for a network policy rule.
*
* All specified dimensions must match. Multiple methods are ORed; multiple
* header and query-string matchers are ANDed.
*/
type NetworkPolicyMatch = {
/** Match on the request path. */
path?: NetworkPolicyMatcher;
/** Match on the HTTP method. */
method?: string[];
/** Match on query-string entries. */
queryString?: NetworkPolicyKeyValueMatcher[];
/** Match on request headers. */
headers?: NetworkPolicyKeyValueMatcher[];
};
/**
* A rule applied to requests matching a domain in the network policy.
*/
type NetworkPolicyRule = {
/** Transforms to apply to matching requests. */
/**
* Optional request matcher. When provided, transforms only apply to requests
* that match every specified dimension.
*/
match?: NetworkPolicyMatch;
/**
* Transforms to apply to matching requests.
*/
transform?: NetworkTransformer[];
/**
* HTTPS proxy URL to forward matching requests to. Must not include query string or fragment.
*/
forwardURL?: string;
};

@@ -63,2 +112,9 @@ /**

* {
* match: {
* method: ["POST"],
* path: { startsWith: "/v1/" },
* headers: [
* { key: { exact: "x-api-key" }, value: { exact: "placeholder" } }
* ]
* },
* transform: [{

@@ -101,3 +157,3 @@ * headers: { authorization: "Bearer ..." }

//#endregion
export { NetworkPolicy, NetworkPolicyRule, NetworkTransformer };
export { NetworkPolicy, NetworkPolicyKeyValueMatcher, NetworkPolicyMatch, NetworkPolicyMatcher, NetworkPolicyRule, NetworkTransformer };
//# sourceMappingURL=network-policy.d.ts.map

@@ -8,3 +8,5 @@ const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');

const require_get_credentials = require('./utils/get-credentials.cjs');
const require_paginator = require('./utils/paginator.cjs');
const require_session = require('./session.cjs');
const require_filesystem = require('./filesystem.cjs');
let __workflow_serde = require("@workflow/serde");

@@ -17,2 +19,8 @@ let node_timers_promises = require("node:timers/promises");

}
function isNotFoundError(err) {
return err instanceof require_api_error.APIError && err.response.status === 404;
}
function isSnapshotNotFoundError(err) {
return err instanceof require_api_error.APIError && err.response.status === 400 && err.json?.error?.code === "snapshot_not_found";
}
function isSandboxStoppingError(err) {

@@ -191,2 +199,11 @@ return err instanceof require_api_error.APIError && err.response.status === 422 && err.json?.error?.code === "sandbox_stopping";

* the next page of results.
*
* The returned object is async-iterable to auto-paginate through all pages:
*
* ```ts
* const result = await Sandbox.list({ namePrefix: "ci-" });
* for await (const sandbox of result) { ... }
* // or: await result.toArray();
* // or: for await (const page of result.pages()) { ... }
* ```
*/

@@ -196,10 +213,19 @@ static async list(params) {

const credentials = await require_get_credentials.getCredentials(params);
return (await new require_api_client.APIClient({
const client = new require_api_client.APIClient({
teamId: credentials.teamId,
token: credentials.token,
fetch: params?.fetch
}).listSandboxes({
...credentials,
...params
})).json;
});
const fetchPage = async (cursor) => {
return (await client.listSandboxes({
...credentials,
...params,
...cursor !== void 0 && { cursor }
})).json;
};
return require_paginator.attachPaginator(await fetchPage(params?.cursor), {
itemsKey: "sandboxes",
fetchNext: fetchPage,
signal: params?.signal
});
}

@@ -325,2 +351,73 @@ /**

/**
* Retrieve an existing named sandbox, or create a new one if none exists.
*
* If `name` is omitted, this always creates a new sandbox and fires
* `onCreate`. If `name` is provided, it first tries {@link Sandbox.get};
* on `not_found` it creates a new sandbox with that name; on
* `snapshot_not_found` it deletes the stale named sandbox and creates
* a fresh one with the same name.
*
* @param params - Get/create parameters plus an optional `onCreate` hook.
* @returns A promise resolving to the {@link Sandbox}.
*
* @example
* <caption>Idempotent named sandbox with one-time setup</caption>
* const sandbox = await Sandbox.getOrCreate({
* name: "my-workspace",
* onCreate: async (sbx) => {
* await sbx.writeFiles([
* { path: "README.md", content: Buffer.from("# Hello") },
* ]);
* },
* });
*
* @example
* <caption>Unnamed — always creates</caption>
* const sandbox = await Sandbox.getOrCreate({
* onCreate: async (sbx) => {
* await sbx.runCommand("npm", ["install"]);
* },
* });
*/
static async getOrCreate(params) {
"use step";
if (!params?.name) {
const sandbox = await Sandbox.create(params);
if (params?.onCreate) await params.onCreate(sandbox);
return sandbox;
}
try {
return await Sandbox.get(params);
} catch (err) {
if (isNotFoundError(err)) {
const sandbox = await Sandbox.create(params);
if (params.onCreate) await params.onCreate(sandbox);
return sandbox;
}
if (isSnapshotNotFoundError(err)) {
const credentials = await require_get_credentials.getCredentials(params);
const client = new require_api_client.APIClient({
teamId: credentials.teamId,
token: credentials.token,
fetch: params.fetch
});
const privateParams = require_types.getPrivateParams(params);
try {
await client.deleteSandbox({
name: params.name,
projectId: credentials.projectId,
signal: params.signal,
...privateParams
});
} catch (deleteErr) {
if (!isNotFoundError(deleteErr)) throw deleteErr;
}
const sandbox = await Sandbox.create(params);
if (params.onCreate) await params.onCreate(sandbox);
return sandbox;
}
throw err;
}
}
/**
* Create a new Sandbox instance.

@@ -344,2 +441,3 @@ *

this.onResume = onResume;
this.fs = new require_filesystem.FileSystem(this);
}

@@ -521,4 +619,3 @@ /**

* @param opts.signal - An AbortSignal to cancel the operation.
* @param opts.blocking - If true, poll until the sandbox has fully stopped and return the final state.
* @returns The sandbox at the time the stop was acknowledged, or after fully stopped if `blocking` is true.
* @returns The final session state after stopping, with optional snapshot metadata.
*/

@@ -528,3 +625,5 @@ async stop(opts) {

if (!this.session) throw new Error("No active session to stop.");
return this.session.stop(opts);
const { session, sandbox, snapshot } = await this.session.stop(opts);
if (sandbox) this.sandbox = sandbox;
return Object.assign(session, { snapshot });
}

@@ -659,10 +758,18 @@ /**

"use step";
return (await (await this.ensureClient()).listSessions({
projectId: this.projectId,
name: this.sandbox.name,
limit: params?.limit,
cursor: params?.cursor,
sortOrder: params?.sortOrder,
const client = await this.ensureClient();
const fetchPage = async (cursor) => {
return (await client.listSessions({
projectId: this.projectId,
name: this.sandbox.name,
limit: params?.limit,
cursor,
sortOrder: params?.sortOrder,
signal: params?.signal
})).json;
};
return require_paginator.attachPaginator(await fetchPage(params?.cursor), {
itemsKey: "sessions",
fetchNext: fetchPage,
signal: params?.signal
})).json;
});
}

@@ -677,10 +784,18 @@ /**

"use step";
return (await (await this.ensureClient()).listSnapshots({
projectId: this.projectId,
name: this.sandbox.name,
limit: params?.limit,
cursor: params?.cursor,
sortOrder: params?.sortOrder,
const client = await this.ensureClient();
const fetchPage = async (cursor) => {
return (await client.listSnapshots({
projectId: this.projectId,
name: this.sandbox.name,
limit: params?.limit,
cursor,
sortOrder: params?.sortOrder,
signal: params?.signal
})).json;
};
return require_paginator.attachPaginator(await fetchPage(params?.cursor), {
itemsKey: "snapshots",
fetchNext: fetchPage,
signal: params?.signal
})).json;
});
}

@@ -687,0 +802,0 @@ };

@@ -1,1 +0,1 @@

{"version":3,"file":"sandbox.cjs","names":["APIError","getCredentials","APIClient","fromAPINetworkPolicy","WORKFLOW_SERIALIZE","WORKFLOW_DESERIALIZE","Session","getPrivateParams","resources: { vcpus: number; memory: number } | undefined"],"sources":["../src/sandbox.ts"],"sourcesContent":["import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport type {\n SessionMetaData,\n SandboxRouteData,\n SandboxMetaData,\n} from \"./api-client/index.js\";\nimport { APIClient } from \"./api-client/index.js\";\nimport { APIError } from \"./api-client/api-error.js\";\nimport { type Credentials, getCredentials } from \"./utils/get-credentials.js\";\nimport { getPrivateParams, type WithPrivate } from \"./utils/types.js\";\nimport type { WithFetchOptions } from \"./api-client/api-client.js\";\nimport type { RUNTIMES } from \"./constants.js\";\nimport { Session, type RunCommandParams } from \"./session.js\";\nimport type { Command, CommandFinished } from \"./command.js\";\nimport type { Snapshot } from \"./snapshot.js\";\nimport type { SandboxSnapshot } from \"./utils/sandbox-snapshot.js\";\nimport type { NetworkPolicy } from \"./network-policy.js\";\nimport { fromAPINetworkPolicy } from \"./utils/network-policy.js\";\nimport { setTimeout } from \"node:timers/promises\";\n\nexport type { NetworkPolicy };\n\n/** @inline */\nexport interface BaseCreateSandboxParams {\n /**\n * The name of the sandbox. If omitted, a random name will be generated.\n */\n name?: string;\n /**\n * The source of the sandbox.\n *\n * Omit this parameter start a sandbox without a source.\n *\n * For git sources:\n * - `depth`: Creates shallow clones with limited commit history (minimum: 1)\n * - `revision`: Clones and checks out a specific commit, branch, or tag\n */\n source?:\n | {\n type: \"git\";\n url: string;\n depth?: number;\n revision?: string;\n }\n | {\n type: \"git\";\n url: string;\n username: string;\n password: string;\n depth?: number;\n revision?: string;\n }\n | { type: \"tarball\"; url: string };\n /**\n * Array of port numbers to expose from the sandbox. Sandboxes can\n * expose up to 4 ports.\n */\n ports?: number[];\n /**\n * Timeout in milliseconds before the sandbox auto-terminates.\n */\n timeout?: number;\n /**\n * Resources to allocate to the sandbox.\n *\n * Your sandbox will get the amount of vCPUs you specify here and\n * 2048 MB of memory per vCPU.\n */\n resources?: { vcpus: number };\n /**\n * The runtime of the sandbox, currently only `node24`, `node22` and `python3.13` are supported.\n * If not specified, the default runtime `node24` will be used.\n */\n runtime?: RUNTIMES | (string & {});\n /**\n * Network policy to define network restrictions for the sandbox.\n * Defaults to full internet access if not specified.\n */\n networkPolicy?: NetworkPolicy;\n /**\n * Default environment variables for the sandbox.\n * These are inherited by all commands unless overridden with\n * the `env` option in `runCommand`.\n *\n * @example\n * const sandbox = await Sandbox.create({\n * env: { NODE_ENV: \"production\", API_KEY: \"secret\" },\n * });\n * // All commands will have NODE_ENV and API_KEY set\n * await sandbox.runCommand(\"node\", [\"app.js\"]);\n */\n env?: Record<string, string>;\n /**\n * Key-value tags to associate with the sandbox. Maximum 5 tags.\n * @example { env: \"staging\", team: \"infra\" }\n */\n tags?: Record<string, string>;\n\n /**\n * An AbortSignal to cancel sandbox creation.\n */\n signal?: AbortSignal;\n /**\n * Enable or disable automatic restore of the filesystem between sessions.\n */\n persistent?: boolean;\n /**\n * Default snapshot expiration in milliseconds.\n * When set, snapshots created for this sandbox will expire after this duration.\n * Use `0` for no expiration.\n */\n snapshotExpiration?: number;\n /**\n * Called when the sandbox session is resumed (e.g., after a snapshot restore).\n * Use this to re-warm caches, restore transient state, or run other setup logic.\n */\n onResume?: (sandbox: Sandbox) => Promise<void>;\n}\n\nexport type CreateSandboxParams =\n | BaseCreateSandboxParams\n | (Omit<BaseCreateSandboxParams, \"runtime\" | \"source\"> & {\n source: { type: \"snapshot\"; snapshotId: string };\n });\n\n/** @inline */\ninterface GetSandboxParams {\n /**\n * The name of the sandbox.\n */\n name: string;\n /**\n * Whether to resume an existing session. Defaults to true.\n */\n resume?: boolean;\n /**\n * An AbortSignal to cancel the operation.\n */\n signal?: AbortSignal;\n /**\n * Called when the sandbox session is resumed (e.g., after a snapshot restore).\n * Use this to re-warm caches, restore transient state, or run other setup logic.\n */\n onResume?: (sandbox: Sandbox) => Promise<void>;\n}\n\nfunction isSandboxStoppedError(err: unknown): boolean {\n return err instanceof APIError && err.response.status === 410;\n}\n\nfunction isSandboxStoppingError(err: unknown): boolean {\n return (\n err instanceof APIError &&\n err.response.status === 422 &&\n (err.json as any)?.error?.code === \"sandbox_stopping\"\n );\n}\n\nfunction isSandboxSnapshottingError(err: unknown): boolean {\n return (\n err instanceof APIError &&\n err.response.status === 422 &&\n (err.json as any)?.error?.code === \"sandbox_snapshotting\"\n );\n}\n\n/**\n * Serialized representation of a Sandbox for @workflow/serde.\n * Fields `metadata` and `routes` are the original wire format from main.\n * Fields `sandboxMetadata` and `projectId` are added for named-sandboxes.\n */\nexport interface SerializedSandbox {\n metadata: SandboxSnapshot;\n routes: SandboxRouteData[];\n sandboxMetadata?: SandboxMetaData;\n projectId?: string;\n}\n\n// ============================================================================\n// Sandbox class\n// ============================================================================\n\n/**\n * A Sandbox is a persistent, isolated Linux MicroVMs to run commands in.\n * Use {@link Sandbox.create} or {@link Sandbox.get} to construct.\n * @hideconstructor\n */\nexport class Sandbox {\n private _client: APIClient | null = null;\n private readonly projectId: string;\n\n /**\n * In-flight resume promise, used to deduplicate concurrent resume calls.\n */\n private resumePromise: Promise<void> | null = null;\n\n /**\n * Internal Session instance for the current VM.\n */\n private session: Session | undefined;\n\n /**\n * Internal metadata about the sandbox.\n */\n private sandbox: SandboxMetaData;\n\n /**\n * Hook that will be executed when a new session is created during resume.\n */\n private readonly onResume?: (sandbox: Sandbox) => Promise<void>;\n\n /**\n * Lazily resolve credentials and construct an API client.\n * @internal\n */\n private async ensureClient(): Promise<APIClient> {\n \"use step\";\n if (this._client) return this._client;\n const credentials = await getCredentials();\n this._client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n });\n return this._client;\n }\n\n /**\n * The name of this sandbox.\n */\n public get name(): string {\n return this.sandbox.name;\n }\n\n /**\n * Routes from ports to subdomains.\n * @hidden\n */\n public get routes(): SandboxRouteData[] {\n return this.currentSession().routes;\n }\n\n /**\n * Whether the sandbox persists the state.\n */\n public get persistent(): boolean {\n return this.sandbox.persistent;\n }\n\n /**\n * The region this sandbox runs in.\n */\n public get region(): string | undefined {\n return this.sandbox.region;\n }\n\n /**\n * Number of virtual CPUs allocated.\n */\n public get vcpus(): number | undefined {\n return this.sandbox.vcpus;\n }\n\n /**\n * Memory allocated in MB.\n */\n public get memory(): number | undefined {\n return this.sandbox.memory;\n }\n\n /** Runtime identifier (e.g. \"node24\", \"python3.13\"). */\n public get runtime(): string | undefined {\n return this.sandbox.runtime;\n }\n\n /**\n * Cumulative egress bytes across all sessions.\n */\n public get totalEgressBytes(): number | undefined {\n return this.sandbox.totalEgressBytes;\n }\n\n /**\n * Cumulative ingress bytes across all sessions.\n */\n public get totalIngressBytes(): number | undefined {\n return this.sandbox.totalIngressBytes;\n }\n\n /**\n * Cumulative active CPU duration in milliseconds across all sessions.\n */\n public get totalActiveCpuDurationMs(): number | undefined {\n return this.sandbox.totalActiveCpuDurationMs;\n }\n\n /**\n * Cumulative wall-clock duration in milliseconds across all sessions.\n */\n public get totalDurationMs(): number | undefined {\n return this.sandbox.totalDurationMs;\n }\n\n /**\n * When this sandbox was last updated.\n */\n public get updatedAt(): Date {\n return new Date(this.sandbox.updatedAt);\n }\n\n /**\n * When the sandbox status was last updated.\n */\n public get statusUpdatedAt(): Date | undefined {\n return this.sandbox.statusUpdatedAt\n ? new Date(this.sandbox.statusUpdatedAt)\n : undefined;\n }\n\n /**\n * When this sandbox was created.\n */\n public get createdAt(): Date {\n return new Date(this.sandbox.createdAt);\n }\n\n /**\n * Interactive port.\n */\n public get interactivePort(): number | undefined {\n return this.currentSession().interactivePort;\n }\n\n /**\n * The status of the current session.\n */\n public get status(): SessionMetaData[\"status\"] {\n return this.currentSession().status;\n }\n\n /**\n * The default timeout of this sandbox in milliseconds.\n */\n public get timeout(): number | undefined {\n return this.sandbox.timeout;\n }\n\n /**\n * Key-value tags attached to the sandbox.\n */\n public get tags(): Record<string, string> | undefined {\n return this.sandbox.tags;\n }\n\n /**\n * The default network policy of this sandbox.\n */\n public get networkPolicy(): NetworkPolicy | undefined {\n return this.sandbox.networkPolicy\n ? fromAPINetworkPolicy(this.sandbox.networkPolicy)\n : undefined;\n }\n\n /**\n * If the session was created from a snapshot, the ID of that snapshot.\n */\n public get sourceSnapshotId(): string | undefined {\n return this.currentSession().sourceSnapshotId;\n }\n\n /**\n * The current snapshot ID of this sandbox, if any.\n */\n public get currentSnapshotId(): string | undefined {\n return this.sandbox.currentSnapshotId;\n }\n\n /**\n * The default snapshot expiration in milliseconds, if set.\n */\n public get snapshotExpiration(): number | undefined {\n return this.sandbox.snapshotExpiration;\n }\n\n /**\n * The amount of CPU used by the session. Only reported once the VM is stopped.\n */\n public get activeCpuUsageMs(): number | undefined {\n return this.currentSession().activeCpuUsageMs;\n }\n\n /**\n * The amount of network data used by the session. Only reported once the VM is stopped.\n */\n public get networkTransfer():\n | { ingress: number; egress: number }\n | undefined {\n return this.currentSession().networkTransfer;\n }\n\n /**\n * Allow to get a list of sandboxes for a team narrowed to the given params.\n * It returns both the sandboxes and the pagination metadata to allow getting\n * the next page of results.\n */\n static async list(\n params?: Partial<Parameters<APIClient[\"listSandboxes\"]>[0]> &\n Partial<Credentials> &\n WithFetchOptions,\n ) {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n fetch: params?.fetch,\n });\n const response = await client.listSandboxes({\n ...credentials,\n ...params,\n });\n return response.json;\n }\n\n /**\n * Serialize a Sandbox instance to plain data for @workflow/serde.\n *\n * @param instance - The Sandbox instance to serialize\n * @returns A plain object containing sandbox metadata and routes\n */\n static [WORKFLOW_SERIALIZE](instance: Sandbox): SerializedSandbox {\n return {\n metadata: instance.session?._sessionSnapshot!,\n routes: instance.session?.routes ?? [],\n sandboxMetadata: instance.sandbox,\n projectId: instance.projectId,\n };\n }\n\n /**\n * Deserialize a Sandbox from serialized snapshot data.\n *\n * The deserialized instance uses the serialized metadata synchronously and\n * lazily creates an API client only when methods perform API requests.\n *\n * @param data - The serialized sandbox data\n * @returns The reconstructed Sandbox instance\n */\n static [WORKFLOW_DESERIALIZE](data: SerializedSandbox): Sandbox {\n const sandbox = new Sandbox({\n sandbox: data.sandboxMetadata!,\n routes: data.routes,\n projectId: data.projectId,\n });\n if (data.metadata) {\n sandbox.session = new Session({ routes: data.routes, snapshot: data.metadata });\n }\n return sandbox;\n }\n\n /**\n * Create a new sandbox.\n *\n * @param params - Creation parameters and optional credentials.\n * @returns A promise resolving to the created {@link Sandbox}.\n * @example\n * <caption>Create a sandbox with default options</caption>\n * const sandbox = await Sandbox.create();\n *\n * @example\n * <caption>Create a sandbox and drop it in the end of the block</caption>\n * async function fn() {\n * await using const sandbox = await Sandbox.create();\n * // Sandbox automatically stopped at the end of the lexical scope\n * }\n */\n static async create(\n params?: WithPrivate<\n CreateSandboxParams | (CreateSandboxParams & Credentials)\n > &\n WithFetchOptions,\n ): Promise<Sandbox & AsyncDisposable> {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n fetch: params?.fetch,\n });\n\n const privateParams = getPrivateParams(params);\n const response = await client.createSandbox({\n source: params?.source,\n projectId: credentials.projectId,\n ports: params?.ports ?? [],\n timeout: params?.timeout,\n resources: params?.resources,\n runtime: params && \"runtime\" in params ? params?.runtime : undefined,\n networkPolicy: params?.networkPolicy,\n env: params?.env,\n tags: params?.tags,\n snapshotExpiration: params?.snapshotExpiration,\n signal: params?.signal,\n name: params?.name,\n persistent: params?.persistent,\n ...privateParams,\n });\n\n return new DisposableSandbox({\n client,\n session: response.json.session,\n sandbox: response.json.sandbox,\n routes: response.json.routes,\n projectId: credentials.projectId,\n onResume: params?.onResume,\n });\n }\n\n /**\n * Retrieve an existing sandbox and resume its session.\n *\n * @param params - Get parameters and optional credentials.\n * @returns A promise resolving to the {@link Sandbox}.\n */\n static async get(\n params: WithPrivate<GetSandboxParams | (GetSandboxParams & Credentials)> &\n WithFetchOptions,\n ): Promise<Sandbox> {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n fetch: params.fetch,\n });\n\n const privateParams = getPrivateParams(params);\n const response = await client.getSandbox({\n name: params.name,\n projectId: credentials.projectId,\n resume: params.resume,\n signal: params.signal,\n ...privateParams,\n });\n\n const sandbox = new Sandbox({\n client,\n session: response.json.session,\n sandbox: response.json.sandbox,\n routes: response.json.routes,\n projectId: credentials.projectId,\n onResume: params.onResume,\n });\n\n if (response.json.resumed && params.onResume) {\n await params.onResume(sandbox);\n }\n\n return sandbox;\n }\n\n /**\n * Create a new Sandbox instance.\n *\n * @param params.client - Optional API client. If not provided, will be lazily created using global credentials.\n * @param params.routes - Port-to-subdomain mappings for exposed ports\n * @param params.sandbox - Sandbox snapshot metadata\n */\n constructor({\n client,\n routes,\n session,\n sandbox,\n projectId,\n onResume,\n }: {\n client?: APIClient;\n routes: SandboxRouteData[];\n session?: SessionMetaData;\n sandbox: SandboxMetaData;\n projectId?: string;\n onResume?: (sandbox: Sandbox) => Promise<void>;\n }) {\n this._client = client ?? null;\n if (session) {\n this.session = new Session({ client: client!, routes, session });\n }\n this.sandbox = sandbox;\n this.projectId = projectId ?? \"\";\n this.onResume = onResume;\n }\n\n /**\n * Get the current session (the running VM) for this sandbox.\n *\n * @returns The {@link Session} instance.\n */\n currentSession(): Session {\n if (!this.session) {\n throw new Error(\"No active session. Run a command or call resume first.\");\n }\n return this.session;\n }\n\n /**\n * Resume this sandbox by creating a new session via `getSandbox`.\n */\n private async resume(signal?: AbortSignal): Promise<void> {\n if (!this.resumePromise) {\n this.resumePromise = this.doResume(signal).finally(() => {\n this.resumePromise = null;\n });\n }\n return this.resumePromise;\n }\n\n private async doResume(signal?: AbortSignal): Promise<void> {\n const client = await this.ensureClient();\n const response = await client.getSandbox({\n name: this.sandbox.name,\n projectId: this.projectId,\n resume: true,\n signal,\n });\n this.session = new Session({\n client,\n routes: response.json.routes,\n session: response.json.session,\n });\n if (this.onResume && response.json.resumed) {\n await this.onResume(this);\n }\n }\n\n /**\n * Poll until the current session reaches a terminal state, then resume.\n */\n private async waitForStopAndResume(signal?: AbortSignal): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n const pollingInterval = 500;\n let status = this.session!.status;\n\n while (status === \"stopping\" || status === \"snapshotting\") {\n await setTimeout(pollingInterval, undefined, { signal });\n const poll = await client.getSession({\n sessionId: this.session!.sessionId,\n signal,\n });\n this.session = new Session({\n client,\n routes: poll.json.routes,\n session: poll.json.session,\n });\n status = poll.json.session.status;\n }\n await this.resume(signal);\n }\n\n /**\n * Execute `fn`, and if the session is stopped/stopping/snapshotting, resume and retry.\n */\n private async withResume<T>(\n fn: () => Promise<T>,\n signal?: AbortSignal,\n ): Promise<T> {\n if (!this.session) {\n await this.resume(signal);\n }\n try {\n return await fn();\n } catch (err) {\n if (isSandboxStoppedError(err)) {\n await this.resume(signal);\n return fn();\n }\n if (isSandboxStoppingError(err) || isSandboxSnapshottingError(err)) {\n await this.waitForStopAndResume(signal);\n return fn();\n }\n throw err;\n }\n }\n\n /**\n * Start executing a command in this sandbox.\n *\n * @param command - The command to execute.\n * @param args - Arguments to pass to the command.\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the command execution.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n async runCommand(\n command: string,\n args?: string[],\n opts?: { signal?: AbortSignal },\n ): Promise<CommandFinished>;\n /**\n * Start executing a command in detached mode.\n *\n * @param params - The command parameters.\n * @returns A {@link Command} instance for the running command.\n */\n async runCommand(\n params: RunCommandParams & { detached: true },\n ): Promise<Command>;\n\n /**\n * Start executing a command in this sandbox.\n *\n * @param params - The command parameters.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n\n async runCommand(params: RunCommandParams): Promise<CommandFinished>;\n\n async runCommand(\n commandOrParams: string | RunCommandParams,\n args?: string[],\n opts?: { signal?: AbortSignal },\n ): Promise<Command | CommandFinished> {\n \"use step\";\n const signal =\n typeof commandOrParams === \"string\"\n ? opts?.signal\n : commandOrParams.signal;\n return this.withResume(\n () => this.session!.runCommand(commandOrParams as any, args, opts),\n signal,\n );\n }\n\n /**\n * Internal helper to start a command in the sandbox.\n *\n * @param params - Command execution parameters.\n * @returns A {@link Command} or {@link CommandFinished}, depending on `detached`.\n * @internal\n */\n async getCommand(\n cmdId: string,\n opts?: { signal?: AbortSignal },\n ): Promise<Command> {\n \"use step\";\n return this.withResume(\n () => this.session!.getCommand(cmdId, opts),\n opts?.signal,\n );\n }\n\n /**\n * Create a directory in the filesystem of this sandbox.\n *\n * @param path - Path of the directory to create\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n */\n async mkDir(path: string, opts?: { signal?: AbortSignal }): Promise<void> {\n \"use step\";\n return this.withResume(\n () => this.session!.mkDir(path, opts),\n opts?.signal,\n );\n }\n\n /**\n * Read a file from the filesystem of this sandbox as a stream.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to a ReadableStream containing the file contents, or null if file not found\n */\n async readFile(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<NodeJS.ReadableStream | null> {\n \"use step\";\n return this.withResume(\n () => this.session!.readFile(file, opts),\n opts?.signal,\n );\n }\n\n /**\n * Read a file from the filesystem of this sandbox as a Buffer.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to the file contents as a Buffer, or null if file not found\n */\n async readFileToBuffer(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<Buffer | null> {\n \"use step\";\n return this.withResume(\n () => this.session!.readFileToBuffer(file, opts),\n opts?.signal,\n );\n }\n\n /**\n * Download a file from the sandbox to the local filesystem.\n *\n * @param src - Source file on the sandbox, with path and optional cwd\n * @param dst - Destination file on the local machine, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.mkdirRecursive - If true, create parent directories for the destination if they don't exist.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns The absolute path to the written file, or null if the source file was not found\n */\n async downloadFile(\n src: { path: string; cwd?: string },\n dst: { path: string; cwd?: string },\n opts?: { mkdirRecursive?: boolean; signal?: AbortSignal },\n ): Promise<string | null> {\n \"use step\";\n return this.withResume(\n () => this.session!.downloadFile(src, dst, opts),\n opts?.signal,\n );\n }\n\n /**\n * Write files to the filesystem of this sandbox.\n * Defaults to writing to /vercel/sandbox unless an absolute path is specified.\n * Writes files using the `vercel-sandbox` user.\n *\n * @param files - Array of files with path, content, and optional mode (permissions)\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the files are written\n *\n * @example\n * // Write an executable script\n * await sandbox.writeFiles([\n * { path: \"/usr/local/bin/myscript\", content: \"#!/bin/bash\\necho hello\", mode: 0o755 }\n * ]);\n */\n async writeFiles(\n files: {\n path: string;\n content: string | Uint8Array;\n mode?: number;\n }[],\n opts?: { signal?: AbortSignal },\n ) {\n \"use step\";\n return this.withResume(\n () => this.session!.writeFiles(files, opts),\n opts?.signal,\n );\n }\n\n /**\n * Get the public domain of a port of this sandbox.\n *\n * @param p - Port number to resolve\n * @returns A full domain (e.g. `https://subdomain.vercel.run`)\n * @throws If the port has no associated route\n */\n domain(p: number): string {\n return this.currentSession().domain(p);\n }\n\n /**\n * Stop the sandbox.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @param opts.blocking - If true, poll until the sandbox has fully stopped and return the final state.\n * @returns The sandbox at the time the stop was acknowledged, or after fully stopped if `blocking` is true.\n */\n async stop(opts?: {\n signal?: AbortSignal;\n blocking?: boolean;\n }): Promise<SandboxSnapshot> {\n \"use step\";\n if (!this.session) {\n throw new Error(\"No active session to stop.\");\n }\n return this.session.stop(opts);\n }\n\n /**\n * Update the network policy for this sandbox.\n *\n * @deprecated Use {@link Sandbox.update} instead.\n *\n * @param networkPolicy - The new network policy to apply.\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the network policy is updated.\n *\n * @example\n * // Restrict to specific domains\n * await sandbox.updateNetworkPolicy({\n * allow: [\"*.npmjs.org\", \"github.com\"],\n * });\n *\n * @example\n * // Inject credentials with per-domain transformers\n * await sandbox.updateNetworkPolicy({\n * allow: {\n * \"ai-gateway.vercel.sh\": [{\n * transform: [{\n * headers: { authorization: \"Bearer ...\" }\n * }]\n * }],\n * \"*\": []\n * }\n * });\n *\n * @example\n * // Deny all network access\n * await sandbox.updateNetworkPolicy(\"deny-all\");\n */\n async updateNetworkPolicy(\n networkPolicy: NetworkPolicy,\n opts?: { signal?: AbortSignal },\n ): Promise<NetworkPolicy> {\n \"use step\";\n await this.withResume(\n () => this.session!.update({ networkPolicy: networkPolicy }, opts),\n opts?.signal,\n );\n\n return this.session!.networkPolicy!;\n }\n\n /**\n * Extend the timeout of the sandbox by the specified duration.\n *\n * This allows you to extend the lifetime of a sandbox up until the maximum\n * execution timeout for your plan.\n *\n * @param duration - The duration in milliseconds to extend the timeout by\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the timeout is extended\n *\n * @example\n * const sandbox = await Sandbox.create({ timeout: ms('10m') });\n * // Extends timeout by 5 minutes, to a total of 15 minutes.\n * await sandbox.extendTimeout(ms('5m'));\n */\n async extendTimeout(\n duration: number,\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n return this.withResume(\n () => this.session!.extendTimeout(duration, opts),\n opts?.signal,\n );\n }\n\n /**\n * Create a snapshot from this currently running sandbox. New sandboxes can\n * then be created from this snapshot using {@link Sandbox.createFromSnapshot}.\n *\n * Note: this sandbox will be stopped as part of the snapshot creation process.\n *\n * @param opts - Optional parameters.\n * @param opts.expiration - Optional expiration time in milliseconds. Use 0 for no expiration at all.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to the Snapshot instance\n */\n async snapshot(opts?: {\n expiration?: number;\n signal?: AbortSignal;\n }): Promise<Snapshot> {\n \"use step\";\n return this.withResume(\n () => this.session!.snapshot(opts),\n opts?.signal,\n );\n }\n\n /**\n * Update the sandbox configuration.\n *\n * @param params - Fields to update.\n * @param opts - Optional abort signal.\n */\n async update(\n params: {\n persistent?: boolean;\n resources?: { vcpus?: number };\n timeout?: number;\n networkPolicy?: NetworkPolicy;\n tags?: Record<string, string>;\n snapshotExpiration?: number;\n currentSnapshotId?: string;\n },\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n let resources: { vcpus: number; memory: number } | undefined;\n if (params.resources?.vcpus) {\n resources = {\n vcpus: params.resources.vcpus,\n memory: params.resources.vcpus * 2048,\n };\n }\n\n // Update the sandbox config. This config will be used on the next session.\n const response = await client.updateSandbox({\n name: this.sandbox.name,\n projectId: this.projectId,\n persistent: params.persistent,\n resources,\n timeout: params.timeout,\n networkPolicy: params.networkPolicy,\n tags: params.tags,\n snapshotExpiration: params.snapshotExpiration,\n currentSnapshotId: params.currentSnapshotId,\n signal: opts?.signal,\n });\n this.sandbox = response.json.sandbox;\n\n // Update the current session config. This only applies to network policy.\n if (params.networkPolicy) {\n try {\n return await this.session?.update(\n { networkPolicy: params.networkPolicy },\n opts,\n );\n } catch (err) {\n if (isSandboxStoppedError(err) || isSandboxStoppingError(err)) {\n return;\n }\n throw err;\n }\n }\n }\n\n /**\n * Delete this sandbox.\n *\n * After deletion the instance becomes inert — all further API calls will\n * throw immediately.\n */\n async delete(opts?: { signal?: AbortSignal }): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n await client.deleteSandbox({\n name: this.sandbox.name,\n projectId: this.projectId,\n signal: opts?.signal,\n });\n }\n\n /**\n * List sessions (VMs) that have been created for this sandbox.\n *\n * @param params - Optional pagination parameters.\n * @returns The list of sessions and pagination metadata.\n */\n async listSessions(params?: {\n limit?: number;\n cursor?: string;\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n \"use step\";\n const client = await this.ensureClient();\n const response = await client.listSessions({\n projectId: this.projectId,\n name: this.sandbox.name,\n limit: params?.limit,\n cursor: params?.cursor,\n sortOrder: params?.sortOrder,\n signal: params?.signal,\n });\n return response.json;\n }\n\n /**\n * List snapshots that belong to this sandbox.\n *\n * @param params - Optional pagination parameters.\n * @returns The list of snapshots and pagination metadata.\n */\n async listSnapshots(params?: {\n limit?: number;\n cursor?: string;\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n \"use step\";\n const client = await this.ensureClient();\n const response = await client.listSnapshots({\n projectId: this.projectId,\n name: this.sandbox.name,\n limit: params?.limit,\n cursor: params?.cursor,\n sortOrder: params?.sortOrder,\n signal: params?.signal,\n });\n return response.json;\n }\n}\n\n/**\n * A {@link Sandbox} that can automatically be disposed using a `await using` statement.\n *\n * @example\n * {\n * await using const sandbox = await Sandbox.create();\n * }\n * // Sandbox is automatically stopped here\n */\nclass DisposableSandbox extends Sandbox implements AsyncDisposable {\n async [Symbol.asyncDispose]() {\n await this.stop();\n }\n}\n"],"mappings":"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"}
{"version":3,"file":"sandbox.cjs","names":["APIError","getCredentials","APIClient","fromAPINetworkPolicy","attachPaginator","WORKFLOW_SERIALIZE","WORKFLOW_DESERIALIZE","Session","getPrivateParams","FileSystem","resources: { vcpus: number; memory: number } | undefined"],"sources":["../src/sandbox.ts"],"sourcesContent":["import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport type {\n SessionMetaData,\n SandboxRouteData,\n SandboxMetaData,\n SnapshotMetadata,\n} from \"./api-client/index.js\";\nimport { APIClient } from \"./api-client/index.js\";\nimport { APIError } from \"./api-client/api-error.js\";\nimport { type Credentials, getCredentials } from \"./utils/get-credentials.js\";\nimport { getPrivateParams, type WithPrivate } from \"./utils/types.js\";\nimport type { WithFetchOptions } from \"./api-client/api-client.js\";\nimport type { RUNTIMES } from \"./constants.js\";\nimport { Session, type RunCommandParams } from \"./session.js\";\nimport type { Command, CommandFinished } from \"./command.js\";\nimport type { Snapshot } from \"./snapshot.js\";\nimport type { SandboxSnapshot } from \"./utils/sandbox-snapshot.js\";\nimport type {\n NetworkPolicy,\n NetworkPolicyKeyValueMatcher,\n NetworkPolicyMatch,\n NetworkPolicyMatcher,\n} from \"./network-policy.js\";\nimport { fromAPINetworkPolicy } from \"./utils/network-policy.js\";\nimport { attachPaginator } from \"./utils/paginator.js\";\nimport { setTimeout } from \"node:timers/promises\";\nimport { FileSystem } from \"./filesystem.js\";\n\nexport type {\n NetworkPolicy,\n NetworkPolicyKeyValueMatcher,\n NetworkPolicyMatch,\n NetworkPolicyMatcher,\n};\n\n/** @inline */\nexport interface BaseCreateSandboxParams {\n /**\n * The name of the sandbox. If omitted, a random name will be generated.\n */\n name?: string;\n /**\n * The source of the sandbox.\n *\n * Omit this parameter start a sandbox without a source.\n *\n * For git sources:\n * - `depth`: Creates shallow clones with limited commit history (minimum: 1)\n * - `revision`: Clones and checks out a specific commit, branch, or tag\n */\n source?:\n | {\n type: \"git\";\n url: string;\n depth?: number;\n revision?: string;\n }\n | {\n type: \"git\";\n url: string;\n username: string;\n password: string;\n depth?: number;\n revision?: string;\n }\n | { type: \"tarball\"; url: string };\n /**\n * Array of port numbers to expose from the sandbox. Sandboxes can\n * expose up to 4 ports.\n */\n ports?: number[];\n /**\n * Timeout in milliseconds before the sandbox auto-terminates.\n */\n timeout?: number;\n /**\n * Resources to allocate to the sandbox.\n *\n * Your sandbox will get the amount of vCPUs you specify here and\n * 2048 MB of memory per vCPU.\n */\n resources?: { vcpus: number };\n /**\n * The runtime of the sandbox, currently only `node24`, `node22` and `python3.13` are supported.\n * If not specified, the default runtime `node24` will be used.\n */\n runtime?: RUNTIMES | (string & {});\n /**\n * Network policy to define network restrictions for the sandbox.\n * Defaults to full internet access if not specified.\n */\n networkPolicy?: NetworkPolicy;\n /**\n * Default environment variables for the sandbox.\n * These are inherited by all commands unless overridden with\n * the `env` option in `runCommand`.\n *\n * @example\n * const sandbox = await Sandbox.create({\n * env: { NODE_ENV: \"production\", API_KEY: \"secret\" },\n * });\n * // All commands will have NODE_ENV and API_KEY set\n * await sandbox.runCommand(\"node\", [\"app.js\"]);\n */\n env?: Record<string, string>;\n /**\n * Key-value tags to associate with the sandbox. Maximum 5 tags.\n * @example { env: \"staging\", team: \"infra\" }\n */\n tags?: Record<string, string>;\n\n /**\n * An AbortSignal to cancel sandbox creation.\n */\n signal?: AbortSignal;\n /**\n * Enable or disable automatic restore of the filesystem between sessions.\n */\n persistent?: boolean;\n /**\n * Default snapshot expiration in milliseconds.\n * When set, snapshots created for this sandbox will expire after this duration.\n * Use `0` for no expiration.\n */\n snapshotExpiration?: number;\n /**\n * Called when the sandbox session is resumed (e.g., after a snapshot restore).\n * Use this to re-warm caches, restore transient state, or run other setup logic.\n */\n onResume?: (sandbox: Sandbox) => Promise<void>;\n}\n\nexport type CreateSandboxParams =\n | BaseCreateSandboxParams\n | (Omit<BaseCreateSandboxParams, \"runtime\" | \"source\"> & {\n source: { type: \"snapshot\"; snapshotId: string };\n });\n\n/** @inline */\ninterface GetSandboxParams {\n /**\n * The name of the sandbox.\n */\n name: string;\n /**\n * Whether to resume an existing session. Defaults to true.\n */\n resume?: boolean;\n /**\n * An AbortSignal to cancel the operation.\n */\n signal?: AbortSignal;\n /**\n * Called when the sandbox session is resumed (e.g., after a snapshot restore).\n * Use this to re-warm caches, restore transient state, or run other setup logic.\n */\n onResume?: (sandbox: Sandbox) => Promise<void>;\n}\n\n/**\n * Extends both {@link BaseCreateSandboxParams} and {@link GetSandboxParams}\n * (minus `name`, which is required on get but optional here) so that any\n * new parameter added to either flow is picked up automatically. The\n * structural overlap on `signal` / `onResume` is intentional — both\n * interfaces declare them with identical optional types.\n * @inline\n */\ninterface GetOrCreateSandboxParams\n extends BaseCreateSandboxParams,\n Omit<GetSandboxParams, \"name\"> {\n /**\n * Called once after a sandbox is freshly created (not when an existing\n * sandbox is retrieved). Use this for one-time setup such as seeding\n * files or warming caches. The returned promise is awaited before\n * {@link Sandbox.getOrCreate} resolves.\n */\n onCreate?: (sandbox: Sandbox) => Promise<void>;\n}\n\nfunction isSandboxStoppedError(err: unknown): boolean {\n return err instanceof APIError && err.response.status === 410;\n}\n\nfunction isNotFoundError(err: unknown): boolean {\n return err instanceof APIError && err.response.status === 404;\n}\n\nfunction isSnapshotNotFoundError(err: unknown): boolean {\n return (\n err instanceof APIError &&\n err.response.status === 400 &&\n (err.json as any)?.error?.code === \"snapshot_not_found\"\n );\n}\n\nfunction isSandboxStoppingError(err: unknown): boolean {\n return (\n err instanceof APIError &&\n err.response.status === 422 &&\n (err.json as any)?.error?.code === \"sandbox_stopping\"\n );\n}\n\nfunction isSandboxSnapshottingError(err: unknown): boolean {\n return (\n err instanceof APIError &&\n err.response.status === 422 &&\n (err.json as any)?.error?.code === \"sandbox_snapshotting\"\n );\n}\n\n/**\n * Serialized representation of a Sandbox for @workflow/serde.\n * Fields `metadata` and `routes` are the original wire format from main.\n * Fields `sandboxMetadata` and `projectId` are added for named-sandboxes.\n */\nexport interface SerializedSandbox {\n metadata: SandboxSnapshot;\n routes: SandboxRouteData[];\n sandboxMetadata?: SandboxMetaData;\n projectId?: string;\n}\n\n// ============================================================================\n// Sandbox class\n// ============================================================================\n\n/**\n * A Sandbox is a persistent, isolated Linux MicroVMs to run commands in.\n * Use {@link Sandbox.create} or {@link Sandbox.get} to construct.\n * @hideconstructor\n */\nexport class Sandbox {\n private _client: APIClient | null = null;\n private readonly projectId: string;\n\n /**\n * In-flight resume promise, used to deduplicate concurrent resume calls.\n */\n private resumePromise: Promise<void> | null = null;\n\n /**\n * Internal Session instance for the current VM.\n */\n private session: Session | undefined;\n\n /**\n * Internal metadata about the sandbox.\n */\n private sandbox: SandboxMetaData;\n\n /**\n * Hook that will be executed when a new session is created during resume.\n */\n private readonly onResume?: (sandbox: Sandbox) => Promise<void>;\n\n /**\n * A `node:fs/promises`-compatible API for interacting with the sandbox filesystem.\n *\n * @example\n * const content = await sandbox.fs.readFile('/etc/hostname', 'utf8');\n * await sandbox.fs.writeFile('/tmp/hello.txt', 'Hello, world!');\n * const files = await sandbox.fs.readdir('/tmp');\n * const stats = await sandbox.fs.stat('/tmp/hello.txt');\n */\n public readonly fs: FileSystem;\n\n /**\n * Lazily resolve credentials and construct an API client.\n * @internal\n */\n private async ensureClient(): Promise<APIClient> {\n \"use step\";\n if (this._client) return this._client;\n const credentials = await getCredentials();\n this._client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n });\n return this._client;\n }\n\n /**\n * The name of this sandbox.\n */\n public get name(): string {\n return this.sandbox.name;\n }\n\n /**\n * Routes from ports to subdomains.\n * @hidden\n */\n public get routes(): SandboxRouteData[] {\n return this.currentSession().routes;\n }\n\n /**\n * Whether the sandbox persists the state.\n */\n public get persistent(): boolean {\n return this.sandbox.persistent;\n }\n\n /**\n * The region this sandbox runs in.\n */\n public get region(): string | undefined {\n return this.sandbox.region;\n }\n\n /**\n * Number of virtual CPUs allocated.\n */\n public get vcpus(): number | undefined {\n return this.sandbox.vcpus;\n }\n\n /**\n * Memory allocated in MB.\n */\n public get memory(): number | undefined {\n return this.sandbox.memory;\n }\n\n /** Runtime identifier (e.g. \"node24\", \"python3.13\"). */\n public get runtime(): string | undefined {\n return this.sandbox.runtime;\n }\n\n /**\n * Cumulative egress bytes across all sessions.\n */\n public get totalEgressBytes(): number | undefined {\n return this.sandbox.totalEgressBytes;\n }\n\n /**\n * Cumulative ingress bytes across all sessions.\n */\n public get totalIngressBytes(): number | undefined {\n return this.sandbox.totalIngressBytes;\n }\n\n /**\n * Cumulative active CPU duration in milliseconds across all sessions.\n */\n public get totalActiveCpuDurationMs(): number | undefined {\n return this.sandbox.totalActiveCpuDurationMs;\n }\n\n /**\n * Cumulative wall-clock duration in milliseconds across all sessions.\n */\n public get totalDurationMs(): number | undefined {\n return this.sandbox.totalDurationMs;\n }\n\n /**\n * When this sandbox was last updated.\n */\n public get updatedAt(): Date {\n return new Date(this.sandbox.updatedAt);\n }\n\n /**\n * When the sandbox status was last updated.\n */\n public get statusUpdatedAt(): Date | undefined {\n return this.sandbox.statusUpdatedAt\n ? new Date(this.sandbox.statusUpdatedAt)\n : undefined;\n }\n\n /**\n * When this sandbox was created.\n */\n public get createdAt(): Date {\n return new Date(this.sandbox.createdAt);\n }\n\n /**\n * Interactive port.\n */\n public get interactivePort(): number | undefined {\n return this.currentSession().interactivePort;\n }\n\n /**\n * The status of the current session.\n */\n public get status(): SessionMetaData[\"status\"] {\n return this.currentSession().status;\n }\n\n /**\n * The default timeout of this sandbox in milliseconds.\n */\n public get timeout(): number | undefined {\n return this.sandbox.timeout;\n }\n\n /**\n * Key-value tags attached to the sandbox.\n */\n public get tags(): Record<string, string> | undefined {\n return this.sandbox.tags;\n }\n\n /**\n * The default network policy of this sandbox.\n */\n public get networkPolicy(): NetworkPolicy | undefined {\n return this.sandbox.networkPolicy\n ? fromAPINetworkPolicy(this.sandbox.networkPolicy)\n : undefined;\n }\n\n /**\n * If the session was created from a snapshot, the ID of that snapshot.\n */\n public get sourceSnapshotId(): string | undefined {\n return this.currentSession().sourceSnapshotId;\n }\n\n /**\n * The current snapshot ID of this sandbox, if any.\n */\n public get currentSnapshotId(): string | undefined {\n return this.sandbox.currentSnapshotId;\n }\n\n /**\n * The default snapshot expiration in milliseconds, if set.\n */\n public get snapshotExpiration(): number | undefined {\n return this.sandbox.snapshotExpiration;\n }\n\n /**\n * The amount of CPU used by the session. Only reported once the VM is stopped.\n */\n public get activeCpuUsageMs(): number | undefined {\n return this.currentSession().activeCpuUsageMs;\n }\n\n /**\n * The amount of network data used by the session. Only reported once the VM is stopped.\n */\n public get networkTransfer():\n | { ingress: number; egress: number }\n | undefined {\n return this.currentSession().networkTransfer;\n }\n\n /**\n * Allow to get a list of sandboxes for a team narrowed to the given params.\n * It returns both the sandboxes and the pagination metadata to allow getting\n * the next page of results.\n *\n * The returned object is async-iterable to auto-paginate through all pages:\n *\n * ```ts\n * const result = await Sandbox.list({ namePrefix: \"ci-\" });\n * for await (const sandbox of result) { ... }\n * // or: await result.toArray();\n * // or: for await (const page of result.pages()) { ... }\n * ```\n */\n static async list(\n params?: Partial<Parameters<APIClient[\"listSandboxes\"]>[0]> &\n Partial<Credentials> &\n WithFetchOptions,\n ) {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n fetch: params?.fetch,\n });\n const fetchPage = async (cursor?: string) => {\n const response = await client.listSandboxes({\n ...credentials,\n ...params,\n ...(cursor !== undefined && { cursor }),\n });\n return response.json;\n };\n const firstPage = await fetchPage(params?.cursor);\n return attachPaginator(firstPage, {\n itemsKey: \"sandboxes\",\n fetchNext: fetchPage,\n signal: params?.signal,\n });\n }\n\n /**\n * Serialize a Sandbox instance to plain data for @workflow/serde.\n *\n * @param instance - The Sandbox instance to serialize\n * @returns A plain object containing sandbox metadata and routes\n */\n static [WORKFLOW_SERIALIZE](instance: Sandbox): SerializedSandbox {\n return {\n metadata: instance.session?._sessionSnapshot!,\n routes: instance.session?.routes ?? [],\n sandboxMetadata: instance.sandbox,\n projectId: instance.projectId,\n };\n }\n\n /**\n * Deserialize a Sandbox from serialized snapshot data.\n *\n * The deserialized instance uses the serialized metadata synchronously and\n * lazily creates an API client only when methods perform API requests.\n *\n * @param data - The serialized sandbox data\n * @returns The reconstructed Sandbox instance\n */\n static [WORKFLOW_DESERIALIZE](data: SerializedSandbox): Sandbox {\n const sandbox = new Sandbox({\n sandbox: data.sandboxMetadata!,\n routes: data.routes,\n projectId: data.projectId,\n });\n if (data.metadata) {\n sandbox.session = new Session({ routes: data.routes, snapshot: data.metadata });\n }\n return sandbox;\n }\n\n /**\n * Create a new sandbox.\n *\n * @param params - Creation parameters and optional credentials.\n * @returns A promise resolving to the created {@link Sandbox}.\n * @example\n * <caption>Create a sandbox with default options</caption>\n * const sandbox = await Sandbox.create();\n *\n * @example\n * <caption>Create a sandbox and drop it in the end of the block</caption>\n * async function fn() {\n * await using const sandbox = await Sandbox.create();\n * // Sandbox automatically stopped at the end of the lexical scope\n * }\n */\n static async create(\n params?: WithPrivate<\n CreateSandboxParams | (CreateSandboxParams & Credentials)\n > &\n WithFetchOptions,\n ): Promise<Sandbox & AsyncDisposable> {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n fetch: params?.fetch,\n });\n\n const privateParams = getPrivateParams(params);\n const response = await client.createSandbox({\n source: params?.source,\n projectId: credentials.projectId,\n ports: params?.ports ?? [],\n timeout: params?.timeout,\n resources: params?.resources,\n runtime: params && \"runtime\" in params ? params?.runtime : undefined,\n networkPolicy: params?.networkPolicy,\n env: params?.env,\n tags: params?.tags,\n snapshotExpiration: params?.snapshotExpiration,\n signal: params?.signal,\n name: params?.name,\n persistent: params?.persistent,\n ...privateParams,\n });\n\n return new DisposableSandbox({\n client,\n session: response.json.session,\n sandbox: response.json.sandbox,\n routes: response.json.routes,\n projectId: credentials.projectId,\n onResume: params?.onResume,\n });\n }\n\n /**\n * Retrieve an existing sandbox and resume its session.\n *\n * @param params - Get parameters and optional credentials.\n * @returns A promise resolving to the {@link Sandbox}.\n */\n static async get(\n params: WithPrivate<GetSandboxParams | (GetSandboxParams & Credentials)> &\n WithFetchOptions,\n ): Promise<Sandbox> {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n fetch: params.fetch,\n });\n\n const privateParams = getPrivateParams(params);\n const response = await client.getSandbox({\n name: params.name,\n projectId: credentials.projectId,\n resume: params.resume,\n signal: params.signal,\n ...privateParams,\n });\n\n const sandbox = new Sandbox({\n client,\n session: response.json.session,\n sandbox: response.json.sandbox,\n routes: response.json.routes,\n projectId: credentials.projectId,\n onResume: params.onResume,\n });\n\n if (response.json.resumed && params.onResume) {\n await params.onResume(sandbox);\n }\n\n return sandbox;\n }\n\n /**\n * Retrieve an existing named sandbox, or create a new one if none exists.\n *\n * If `name` is omitted, this always creates a new sandbox and fires\n * `onCreate`. If `name` is provided, it first tries {@link Sandbox.get};\n * on `not_found` it creates a new sandbox with that name; on\n * `snapshot_not_found` it deletes the stale named sandbox and creates\n * a fresh one with the same name.\n *\n * @param params - Get/create parameters plus an optional `onCreate` hook.\n * @returns A promise resolving to the {@link Sandbox}.\n *\n * @example\n * <caption>Idempotent named sandbox with one-time setup</caption>\n * const sandbox = await Sandbox.getOrCreate({\n * name: \"my-workspace\",\n * onCreate: async (sbx) => {\n * await sbx.writeFiles([\n * { path: \"README.md\", content: Buffer.from(\"# Hello\") },\n * ]);\n * },\n * });\n *\n * @example\n * <caption>Unnamed — always creates</caption>\n * const sandbox = await Sandbox.getOrCreate({\n * onCreate: async (sbx) => {\n * await sbx.runCommand(\"npm\", [\"install\"]);\n * },\n * });\n */\n static async getOrCreate(\n params?: WithPrivate<\n GetOrCreateSandboxParams | (GetOrCreateSandboxParams & Credentials)\n > &\n WithFetchOptions,\n ): Promise<Sandbox> {\n \"use step\";\n // No name → always create, fire onCreate.\n if (!params?.name) {\n const sandbox = await Sandbox.create(params);\n if (params?.onCreate) {\n await params.onCreate(sandbox);\n }\n return sandbox;\n }\n\n try {\n return await Sandbox.get(\n params as unknown as Parameters<typeof Sandbox.get>[0],\n );\n } catch (err) {\n if (isNotFoundError(err)) {\n // Sandbox does not exist: re-create it.\n const sandbox = await Sandbox.create(params);\n if (params.onCreate) {\n await params.onCreate(sandbox);\n }\n return sandbox;\n }\n\n if (isSnapshotNotFoundError(err)) {\n // Sandbox exists but the snapshot has expired. Delete it and create\n // a new one.\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n fetch: params.fetch,\n });\n const privateParams = getPrivateParams(params);\n try {\n await client.deleteSandbox({\n name: params.name,\n projectId: credentials.projectId,\n signal: params.signal,\n ...privateParams,\n });\n } catch (deleteErr) {\n // Tolerate 404 — the named sandbox was already cleaned up by a\n // concurrent request. Propagate anything else.\n if (!isNotFoundError(deleteErr)) {\n throw deleteErr;\n }\n }\n\n const sandbox = await Sandbox.create(params);\n if (params.onCreate) {\n await params.onCreate(sandbox);\n }\n return sandbox;\n }\n\n throw err;\n }\n }\n\n /**\n * Create a new Sandbox instance.\n *\n * @param params.client - Optional API client. If not provided, will be lazily created using global credentials.\n * @param params.routes - Port-to-subdomain mappings for exposed ports\n * @param params.sandbox - Sandbox snapshot metadata\n */\n constructor({\n client,\n routes,\n session,\n sandbox,\n projectId,\n onResume,\n }: {\n client?: APIClient;\n routes: SandboxRouteData[];\n session?: SessionMetaData;\n sandbox: SandboxMetaData;\n projectId?: string;\n onResume?: (sandbox: Sandbox) => Promise<void>;\n }) {\n this._client = client ?? null;\n if (session) {\n this.session = new Session({ client: client!, routes, session });\n }\n this.sandbox = sandbox;\n this.projectId = projectId ?? \"\";\n this.onResume = onResume;\n this.fs = new FileSystem(this);\n }\n\n /**\n * Get the current session (the running VM) for this sandbox.\n *\n * @returns The {@link Session} instance.\n */\n currentSession(): Session {\n if (!this.session) {\n throw new Error(\"No active session. Run a command or call resume first.\");\n }\n return this.session;\n }\n\n /**\n * Resume this sandbox by creating a new session via `getSandbox`.\n */\n private async resume(signal?: AbortSignal): Promise<void> {\n if (!this.resumePromise) {\n this.resumePromise = this.doResume(signal).finally(() => {\n this.resumePromise = null;\n });\n }\n return this.resumePromise;\n }\n\n private async doResume(signal?: AbortSignal): Promise<void> {\n const client = await this.ensureClient();\n const response = await client.getSandbox({\n name: this.sandbox.name,\n projectId: this.projectId,\n resume: true,\n signal,\n });\n this.session = new Session({\n client,\n routes: response.json.routes,\n session: response.json.session,\n });\n if (this.onResume && response.json.resumed) {\n await this.onResume(this);\n }\n }\n\n /**\n * Poll until the current session reaches a terminal state, then resume.\n */\n private async waitForStopAndResume(signal?: AbortSignal): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n const pollingInterval = 500;\n let status = this.session!.status;\n\n while (status === \"stopping\" || status === \"snapshotting\") {\n await setTimeout(pollingInterval, undefined, { signal });\n const poll = await client.getSession({\n sessionId: this.session!.sessionId,\n signal,\n });\n this.session = new Session({\n client,\n routes: poll.json.routes,\n session: poll.json.session,\n });\n status = poll.json.session.status;\n }\n await this.resume(signal);\n }\n\n /**\n * Execute `fn`, and if the session is stopped/stopping/snapshotting, resume and retry.\n */\n private async withResume<T>(\n fn: () => Promise<T>,\n signal?: AbortSignal,\n ): Promise<T> {\n if (!this.session) {\n await this.resume(signal);\n }\n try {\n return await fn();\n } catch (err) {\n if (isSandboxStoppedError(err)) {\n await this.resume(signal);\n return fn();\n }\n if (isSandboxStoppingError(err) || isSandboxSnapshottingError(err)) {\n await this.waitForStopAndResume(signal);\n return fn();\n }\n throw err;\n }\n }\n\n /**\n * Start executing a command in this sandbox.\n *\n * @param command - The command to execute.\n * @param args - Arguments to pass to the command.\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the command execution.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n async runCommand(\n command: string,\n args?: string[],\n opts?: { signal?: AbortSignal },\n ): Promise<CommandFinished>;\n /**\n * Start executing a command in detached mode.\n *\n * @param params - The command parameters.\n * @returns A {@link Command} instance for the running command.\n */\n async runCommand(\n params: RunCommandParams & { detached: true },\n ): Promise<Command>;\n\n /**\n * Start executing a command in this sandbox.\n *\n * @param params - The command parameters.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n\n async runCommand(params: RunCommandParams): Promise<CommandFinished>;\n\n async runCommand(\n commandOrParams: string | RunCommandParams,\n args?: string[],\n opts?: { signal?: AbortSignal },\n ): Promise<Command | CommandFinished> {\n \"use step\";\n const signal =\n typeof commandOrParams === \"string\"\n ? opts?.signal\n : commandOrParams.signal;\n return this.withResume(\n () => this.session!.runCommand(commandOrParams as any, args, opts),\n signal,\n );\n }\n\n /**\n * Internal helper to start a command in the sandbox.\n *\n * @param params - Command execution parameters.\n * @returns A {@link Command} or {@link CommandFinished}, depending on `detached`.\n * @internal\n */\n async getCommand(\n cmdId: string,\n opts?: { signal?: AbortSignal },\n ): Promise<Command> {\n \"use step\";\n return this.withResume(\n () => this.session!.getCommand(cmdId, opts),\n opts?.signal,\n );\n }\n\n /**\n * Create a directory in the filesystem of this sandbox.\n *\n * @param path - Path of the directory to create\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n */\n async mkDir(path: string, opts?: { signal?: AbortSignal }): Promise<void> {\n \"use step\";\n return this.withResume(\n () => this.session!.mkDir(path, opts),\n opts?.signal,\n );\n }\n\n /**\n * Read a file from the filesystem of this sandbox as a stream.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to a ReadableStream containing the file contents, or null if file not found\n */\n async readFile(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<NodeJS.ReadableStream | null> {\n \"use step\";\n return this.withResume(\n () => this.session!.readFile(file, opts),\n opts?.signal,\n );\n }\n\n /**\n * Read a file from the filesystem of this sandbox as a Buffer.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to the file contents as a Buffer, or null if file not found\n */\n async readFileToBuffer(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<Buffer | null> {\n \"use step\";\n return this.withResume(\n () => this.session!.readFileToBuffer(file, opts),\n opts?.signal,\n );\n }\n\n /**\n * Download a file from the sandbox to the local filesystem.\n *\n * @param src - Source file on the sandbox, with path and optional cwd\n * @param dst - Destination file on the local machine, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.mkdirRecursive - If true, create parent directories for the destination if they don't exist.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns The absolute path to the written file, or null if the source file was not found\n */\n async downloadFile(\n src: { path: string; cwd?: string },\n dst: { path: string; cwd?: string },\n opts?: { mkdirRecursive?: boolean; signal?: AbortSignal },\n ): Promise<string | null> {\n \"use step\";\n return this.withResume(\n () => this.session!.downloadFile(src, dst, opts),\n opts?.signal,\n );\n }\n\n /**\n * Write files to the filesystem of this sandbox.\n * Defaults to writing to /vercel/sandbox unless an absolute path is specified.\n * Writes files using the `vercel-sandbox` user.\n *\n * @param files - Array of files with path, content, and optional mode (permissions)\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the files are written\n *\n * @example\n * // Write an executable script\n * await sandbox.writeFiles([\n * { path: \"/usr/local/bin/myscript\", content: \"#!/bin/bash\\necho hello\", mode: 0o755 }\n * ]);\n */\n async writeFiles(\n files: {\n path: string;\n content: string | Uint8Array;\n mode?: number;\n }[],\n opts?: { signal?: AbortSignal },\n ) {\n \"use step\";\n return this.withResume(\n () => this.session!.writeFiles(files, opts),\n opts?.signal,\n );\n }\n\n /**\n * Get the public domain of a port of this sandbox.\n *\n * @param p - Port number to resolve\n * @returns A full domain (e.g. `https://subdomain.vercel.run`)\n * @throws If the port has no associated route\n */\n domain(p: number): string {\n return this.currentSession().domain(p);\n }\n\n /**\n * Stop the sandbox.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns The final session state after stopping, with optional snapshot metadata.\n */\n async stop(opts?: {\n signal?: AbortSignal;\n }): Promise<SandboxSnapshot & { snapshot?: SnapshotMetadata }> {\n \"use step\";\n if (!this.session) {\n throw new Error(\"No active session to stop.\");\n }\n const { session, sandbox, snapshot } = await this.session.stop(opts);\n if (sandbox) {\n this.sandbox = sandbox;\n }\n return Object.assign(session, { snapshot });\n }\n\n /**\n * Update the network policy for this sandbox.\n *\n * @deprecated Use {@link Sandbox.update} instead.\n *\n * @param networkPolicy - The new network policy to apply.\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the network policy is updated.\n *\n * @example\n * // Restrict to specific domains\n * await sandbox.updateNetworkPolicy({\n * allow: [\"*.npmjs.org\", \"github.com\"],\n * });\n *\n * @example\n * // Inject credentials with per-domain transformers\n * await sandbox.updateNetworkPolicy({\n * allow: {\n * \"ai-gateway.vercel.sh\": [{\n * transform: [{\n * headers: { authorization: \"Bearer ...\" }\n * }]\n * }],\n * \"*\": []\n * }\n * });\n *\n * @example\n * // Deny all network access\n * await sandbox.updateNetworkPolicy(\"deny-all\");\n */\n async updateNetworkPolicy(\n networkPolicy: NetworkPolicy,\n opts?: { signal?: AbortSignal },\n ): Promise<NetworkPolicy> {\n \"use step\";\n await this.withResume(\n () => this.session!.update({ networkPolicy: networkPolicy }, opts),\n opts?.signal,\n );\n\n return this.session!.networkPolicy!;\n }\n\n /**\n * Extend the timeout of the sandbox by the specified duration.\n *\n * This allows you to extend the lifetime of a sandbox up until the maximum\n * execution timeout for your plan.\n *\n * @param duration - The duration in milliseconds to extend the timeout by\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the timeout is extended\n *\n * @example\n * const sandbox = await Sandbox.create({ timeout: ms('10m') });\n * // Extends timeout by 5 minutes, to a total of 15 minutes.\n * await sandbox.extendTimeout(ms('5m'));\n */\n async extendTimeout(\n duration: number,\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n return this.withResume(\n () => this.session!.extendTimeout(duration, opts),\n opts?.signal,\n );\n }\n\n /**\n * Create a snapshot from this currently running sandbox. New sandboxes can\n * then be created from this snapshot using {@link Sandbox.createFromSnapshot}.\n *\n * Note: this sandbox will be stopped as part of the snapshot creation process.\n *\n * @param opts - Optional parameters.\n * @param opts.expiration - Optional expiration time in milliseconds. Use 0 for no expiration at all.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to the Snapshot instance\n */\n async snapshot(opts?: {\n expiration?: number;\n signal?: AbortSignal;\n }): Promise<Snapshot> {\n \"use step\";\n return this.withResume(\n () => this.session!.snapshot(opts),\n opts?.signal,\n );\n }\n\n /**\n * Update the sandbox configuration.\n *\n * @param params - Fields to update.\n * @param opts - Optional abort signal.\n */\n async update(\n params: {\n persistent?: boolean;\n resources?: { vcpus?: number };\n timeout?: number;\n networkPolicy?: NetworkPolicy;\n tags?: Record<string, string>;\n snapshotExpiration?: number;\n currentSnapshotId?: string;\n },\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n let resources: { vcpus: number; memory: number } | undefined;\n if (params.resources?.vcpus) {\n resources = {\n vcpus: params.resources.vcpus,\n memory: params.resources.vcpus * 2048,\n };\n }\n\n // Update the sandbox config. This config will be used on the next session.\n const response = await client.updateSandbox({\n name: this.sandbox.name,\n projectId: this.projectId,\n persistent: params.persistent,\n resources,\n timeout: params.timeout,\n networkPolicy: params.networkPolicy,\n tags: params.tags,\n snapshotExpiration: params.snapshotExpiration,\n currentSnapshotId: params.currentSnapshotId,\n signal: opts?.signal,\n });\n this.sandbox = response.json.sandbox;\n\n // Update the current session config. This only applies to network policy.\n if (params.networkPolicy) {\n try {\n return await this.session?.update(\n { networkPolicy: params.networkPolicy },\n opts,\n );\n } catch (err) {\n if (isSandboxStoppedError(err) || isSandboxStoppingError(err)) {\n return;\n }\n throw err;\n }\n }\n }\n\n /**\n * Delete this sandbox.\n *\n * After deletion the instance becomes inert — all further API calls will\n * throw immediately.\n */\n async delete(opts?: { signal?: AbortSignal }): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n await client.deleteSandbox({\n name: this.sandbox.name,\n projectId: this.projectId,\n signal: opts?.signal,\n });\n }\n\n /**\n * List sessions (VMs) that have been created for this sandbox.\n *\n * @param params - Optional pagination parameters.\n * @returns The list of sessions and pagination metadata.\n */\n async listSessions(params?: {\n limit?: number;\n cursor?: string;\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n \"use step\";\n const client = await this.ensureClient();\n const fetchPage = async (cursor?: string) => {\n const response = await client.listSessions({\n projectId: this.projectId,\n name: this.sandbox.name,\n limit: params?.limit,\n cursor,\n sortOrder: params?.sortOrder,\n signal: params?.signal,\n });\n return response.json;\n };\n const firstPage = await fetchPage(params?.cursor);\n return attachPaginator(firstPage, {\n itemsKey: \"sessions\",\n fetchNext: fetchPage,\n signal: params?.signal,\n });\n }\n\n /**\n * List snapshots that belong to this sandbox.\n *\n * @param params - Optional pagination parameters.\n * @returns The list of snapshots and pagination metadata.\n */\n async listSnapshots(params?: {\n limit?: number;\n cursor?: string;\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n \"use step\";\n const client = await this.ensureClient();\n const fetchPage = async (cursor?: string) => {\n const response = await client.listSnapshots({\n projectId: this.projectId,\n name: this.sandbox.name,\n limit: params?.limit,\n cursor,\n sortOrder: params?.sortOrder,\n signal: params?.signal,\n });\n return response.json;\n };\n const firstPage = await fetchPage(params?.cursor);\n return attachPaginator(firstPage, {\n itemsKey: \"snapshots\",\n fetchNext: fetchPage,\n signal: params?.signal,\n });\n }\n}\n\n/**\n * A {@link Sandbox} that can automatically be disposed using a `await using` statement.\n *\n * @example\n * {\n * await using const sandbox = await Sandbox.create();\n * }\n * // Sandbox is automatically stopped here\n */\nclass DisposableSandbox extends Sandbox implements AsyncDisposable {\n async [Symbol.asyncDispose]() {\n await this.stop();\n }\n}\n"],"mappings":"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"}

@@ -7,3 +7,5 @@ import { APIError } from "./api-client/api-error.js";

import { getCredentials } from "./utils/get-credentials.js";
import { attachPaginator } from "./utils/paginator.js";
import { Session } from "./session.js";
import { FileSystem } from "./filesystem.js";
import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from "@workflow/serde";

@@ -16,2 +18,8 @@ import { setTimeout } from "node:timers/promises";

}
function isNotFoundError(err) {
return err instanceof APIError && err.response.status === 404;
}
function isSnapshotNotFoundError(err) {
return err instanceof APIError && err.response.status === 400 && err.json?.error?.code === "snapshot_not_found";
}
function isSandboxStoppingError(err) {

@@ -190,2 +198,11 @@ return err instanceof APIError && err.response.status === 422 && err.json?.error?.code === "sandbox_stopping";

* the next page of results.
*
* The returned object is async-iterable to auto-paginate through all pages:
*
* ```ts
* const result = await Sandbox.list({ namePrefix: "ci-" });
* for await (const sandbox of result) { ... }
* // or: await result.toArray();
* // or: for await (const page of result.pages()) { ... }
* ```
*/

@@ -195,10 +212,19 @@ static async list(params) {

const credentials = await getCredentials(params);
return (await new APIClient({
const client = new APIClient({
teamId: credentials.teamId,
token: credentials.token,
fetch: params?.fetch
}).listSandboxes({
...credentials,
...params
})).json;
});
const fetchPage = async (cursor) => {
return (await client.listSandboxes({
...credentials,
...params,
...cursor !== void 0 && { cursor }
})).json;
};
return attachPaginator(await fetchPage(params?.cursor), {
itemsKey: "sandboxes",
fetchNext: fetchPage,
signal: params?.signal
});
}

@@ -324,2 +350,73 @@ /**

/**
* Retrieve an existing named sandbox, or create a new one if none exists.
*
* If `name` is omitted, this always creates a new sandbox and fires
* `onCreate`. If `name` is provided, it first tries {@link Sandbox.get};
* on `not_found` it creates a new sandbox with that name; on
* `snapshot_not_found` it deletes the stale named sandbox and creates
* a fresh one with the same name.
*
* @param params - Get/create parameters plus an optional `onCreate` hook.
* @returns A promise resolving to the {@link Sandbox}.
*
* @example
* <caption>Idempotent named sandbox with one-time setup</caption>
* const sandbox = await Sandbox.getOrCreate({
* name: "my-workspace",
* onCreate: async (sbx) => {
* await sbx.writeFiles([
* { path: "README.md", content: Buffer.from("# Hello") },
* ]);
* },
* });
*
* @example
* <caption>Unnamed — always creates</caption>
* const sandbox = await Sandbox.getOrCreate({
* onCreate: async (sbx) => {
* await sbx.runCommand("npm", ["install"]);
* },
* });
*/
static async getOrCreate(params) {
"use step";
if (!params?.name) {
const sandbox = await Sandbox.create(params);
if (params?.onCreate) await params.onCreate(sandbox);
return sandbox;
}
try {
return await Sandbox.get(params);
} catch (err) {
if (isNotFoundError(err)) {
const sandbox = await Sandbox.create(params);
if (params.onCreate) await params.onCreate(sandbox);
return sandbox;
}
if (isSnapshotNotFoundError(err)) {
const credentials = await getCredentials(params);
const client = new APIClient({
teamId: credentials.teamId,
token: credentials.token,
fetch: params.fetch
});
const privateParams = getPrivateParams(params);
try {
await client.deleteSandbox({
name: params.name,
projectId: credentials.projectId,
signal: params.signal,
...privateParams
});
} catch (deleteErr) {
if (!isNotFoundError(deleteErr)) throw deleteErr;
}
const sandbox = await Sandbox.create(params);
if (params.onCreate) await params.onCreate(sandbox);
return sandbox;
}
throw err;
}
}
/**
* Create a new Sandbox instance.

@@ -343,2 +440,3 @@ *

this.onResume = onResume;
this.fs = new FileSystem(this);
}

@@ -520,4 +618,3 @@ /**

* @param opts.signal - An AbortSignal to cancel the operation.
* @param opts.blocking - If true, poll until the sandbox has fully stopped and return the final state.
* @returns The sandbox at the time the stop was acknowledged, or after fully stopped if `blocking` is true.
* @returns The final session state after stopping, with optional snapshot metadata.
*/

@@ -527,3 +624,5 @@ async stop(opts) {

if (!this.session) throw new Error("No active session to stop.");
return this.session.stop(opts);
const { session, sandbox, snapshot } = await this.session.stop(opts);
if (sandbox) this.sandbox = sandbox;
return Object.assign(session, { snapshot });
}

@@ -658,10 +757,18 @@ /**

"use step";
return (await (await this.ensureClient()).listSessions({
projectId: this.projectId,
name: this.sandbox.name,
limit: params?.limit,
cursor: params?.cursor,
sortOrder: params?.sortOrder,
const client = await this.ensureClient();
const fetchPage = async (cursor) => {
return (await client.listSessions({
projectId: this.projectId,
name: this.sandbox.name,
limit: params?.limit,
cursor,
sortOrder: params?.sortOrder,
signal: params?.signal
})).json;
};
return attachPaginator(await fetchPage(params?.cursor), {
itemsKey: "sessions",
fetchNext: fetchPage,
signal: params?.signal
})).json;
});
}

@@ -676,10 +783,18 @@ /**

"use step";
return (await (await this.ensureClient()).listSnapshots({
projectId: this.projectId,
name: this.sandbox.name,
limit: params?.limit,
cursor: params?.cursor,
sortOrder: params?.sortOrder,
const client = await this.ensureClient();
const fetchPage = async (cursor) => {
return (await client.listSnapshots({
projectId: this.projectId,
name: this.sandbox.name,
limit: params?.limit,
cursor,
sortOrder: params?.sortOrder,
signal: params?.signal
})).json;
};
return attachPaginator(await fetchPage(params?.cursor), {
itemsKey: "snapshots",
fetchNext: fetchPage,
signal: params?.signal
})).json;
});
}

@@ -686,0 +801,0 @@ };

@@ -1,1 +0,1 @@

{"version":3,"file":"sandbox.js","names":["resources: { vcpus: number; memory: number } | undefined"],"sources":["../src/sandbox.ts"],"sourcesContent":["import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport type {\n SessionMetaData,\n SandboxRouteData,\n SandboxMetaData,\n} from \"./api-client/index.js\";\nimport { APIClient } from \"./api-client/index.js\";\nimport { APIError } from \"./api-client/api-error.js\";\nimport { type Credentials, getCredentials } from \"./utils/get-credentials.js\";\nimport { getPrivateParams, type WithPrivate } from \"./utils/types.js\";\nimport type { WithFetchOptions } from \"./api-client/api-client.js\";\nimport type { RUNTIMES } from \"./constants.js\";\nimport { Session, type RunCommandParams } from \"./session.js\";\nimport type { Command, CommandFinished } from \"./command.js\";\nimport type { Snapshot } from \"./snapshot.js\";\nimport type { SandboxSnapshot } from \"./utils/sandbox-snapshot.js\";\nimport type { NetworkPolicy } from \"./network-policy.js\";\nimport { fromAPINetworkPolicy } from \"./utils/network-policy.js\";\nimport { setTimeout } from \"node:timers/promises\";\n\nexport type { NetworkPolicy };\n\n/** @inline */\nexport interface BaseCreateSandboxParams {\n /**\n * The name of the sandbox. If omitted, a random name will be generated.\n */\n name?: string;\n /**\n * The source of the sandbox.\n *\n * Omit this parameter start a sandbox without a source.\n *\n * For git sources:\n * - `depth`: Creates shallow clones with limited commit history (minimum: 1)\n * - `revision`: Clones and checks out a specific commit, branch, or tag\n */\n source?:\n | {\n type: \"git\";\n url: string;\n depth?: number;\n revision?: string;\n }\n | {\n type: \"git\";\n url: string;\n username: string;\n password: string;\n depth?: number;\n revision?: string;\n }\n | { type: \"tarball\"; url: string };\n /**\n * Array of port numbers to expose from the sandbox. Sandboxes can\n * expose up to 4 ports.\n */\n ports?: number[];\n /**\n * Timeout in milliseconds before the sandbox auto-terminates.\n */\n timeout?: number;\n /**\n * Resources to allocate to the sandbox.\n *\n * Your sandbox will get the amount of vCPUs you specify here and\n * 2048 MB of memory per vCPU.\n */\n resources?: { vcpus: number };\n /**\n * The runtime of the sandbox, currently only `node24`, `node22` and `python3.13` are supported.\n * If not specified, the default runtime `node24` will be used.\n */\n runtime?: RUNTIMES | (string & {});\n /**\n * Network policy to define network restrictions for the sandbox.\n * Defaults to full internet access if not specified.\n */\n networkPolicy?: NetworkPolicy;\n /**\n * Default environment variables for the sandbox.\n * These are inherited by all commands unless overridden with\n * the `env` option in `runCommand`.\n *\n * @example\n * const sandbox = await Sandbox.create({\n * env: { NODE_ENV: \"production\", API_KEY: \"secret\" },\n * });\n * // All commands will have NODE_ENV and API_KEY set\n * await sandbox.runCommand(\"node\", [\"app.js\"]);\n */\n env?: Record<string, string>;\n /**\n * Key-value tags to associate with the sandbox. Maximum 5 tags.\n * @example { env: \"staging\", team: \"infra\" }\n */\n tags?: Record<string, string>;\n\n /**\n * An AbortSignal to cancel sandbox creation.\n */\n signal?: AbortSignal;\n /**\n * Enable or disable automatic restore of the filesystem between sessions.\n */\n persistent?: boolean;\n /**\n * Default snapshot expiration in milliseconds.\n * When set, snapshots created for this sandbox will expire after this duration.\n * Use `0` for no expiration.\n */\n snapshotExpiration?: number;\n /**\n * Called when the sandbox session is resumed (e.g., after a snapshot restore).\n * Use this to re-warm caches, restore transient state, or run other setup logic.\n */\n onResume?: (sandbox: Sandbox) => Promise<void>;\n}\n\nexport type CreateSandboxParams =\n | BaseCreateSandboxParams\n | (Omit<BaseCreateSandboxParams, \"runtime\" | \"source\"> & {\n source: { type: \"snapshot\"; snapshotId: string };\n });\n\n/** @inline */\ninterface GetSandboxParams {\n /**\n * The name of the sandbox.\n */\n name: string;\n /**\n * Whether to resume an existing session. Defaults to true.\n */\n resume?: boolean;\n /**\n * An AbortSignal to cancel the operation.\n */\n signal?: AbortSignal;\n /**\n * Called when the sandbox session is resumed (e.g., after a snapshot restore).\n * Use this to re-warm caches, restore transient state, or run other setup logic.\n */\n onResume?: (sandbox: Sandbox) => Promise<void>;\n}\n\nfunction isSandboxStoppedError(err: unknown): boolean {\n return err instanceof APIError && err.response.status === 410;\n}\n\nfunction isSandboxStoppingError(err: unknown): boolean {\n return (\n err instanceof APIError &&\n err.response.status === 422 &&\n (err.json as any)?.error?.code === \"sandbox_stopping\"\n );\n}\n\nfunction isSandboxSnapshottingError(err: unknown): boolean {\n return (\n err instanceof APIError &&\n err.response.status === 422 &&\n (err.json as any)?.error?.code === \"sandbox_snapshotting\"\n );\n}\n\n/**\n * Serialized representation of a Sandbox for @workflow/serde.\n * Fields `metadata` and `routes` are the original wire format from main.\n * Fields `sandboxMetadata` and `projectId` are added for named-sandboxes.\n */\nexport interface SerializedSandbox {\n metadata: SandboxSnapshot;\n routes: SandboxRouteData[];\n sandboxMetadata?: SandboxMetaData;\n projectId?: string;\n}\n\n// ============================================================================\n// Sandbox class\n// ============================================================================\n\n/**\n * A Sandbox is a persistent, isolated Linux MicroVMs to run commands in.\n * Use {@link Sandbox.create} or {@link Sandbox.get} to construct.\n * @hideconstructor\n */\nexport class Sandbox {\n private _client: APIClient | null = null;\n private readonly projectId: string;\n\n /**\n * In-flight resume promise, used to deduplicate concurrent resume calls.\n */\n private resumePromise: Promise<void> | null = null;\n\n /**\n * Internal Session instance for the current VM.\n */\n private session: Session | undefined;\n\n /**\n * Internal metadata about the sandbox.\n */\n private sandbox: SandboxMetaData;\n\n /**\n * Hook that will be executed when a new session is created during resume.\n */\n private readonly onResume?: (sandbox: Sandbox) => Promise<void>;\n\n /**\n * Lazily resolve credentials and construct an API client.\n * @internal\n */\n private async ensureClient(): Promise<APIClient> {\n \"use step\";\n if (this._client) return this._client;\n const credentials = await getCredentials();\n this._client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n });\n return this._client;\n }\n\n /**\n * The name of this sandbox.\n */\n public get name(): string {\n return this.sandbox.name;\n }\n\n /**\n * Routes from ports to subdomains.\n * @hidden\n */\n public get routes(): SandboxRouteData[] {\n return this.currentSession().routes;\n }\n\n /**\n * Whether the sandbox persists the state.\n */\n public get persistent(): boolean {\n return this.sandbox.persistent;\n }\n\n /**\n * The region this sandbox runs in.\n */\n public get region(): string | undefined {\n return this.sandbox.region;\n }\n\n /**\n * Number of virtual CPUs allocated.\n */\n public get vcpus(): number | undefined {\n return this.sandbox.vcpus;\n }\n\n /**\n * Memory allocated in MB.\n */\n public get memory(): number | undefined {\n return this.sandbox.memory;\n }\n\n /** Runtime identifier (e.g. \"node24\", \"python3.13\"). */\n public get runtime(): string | undefined {\n return this.sandbox.runtime;\n }\n\n /**\n * Cumulative egress bytes across all sessions.\n */\n public get totalEgressBytes(): number | undefined {\n return this.sandbox.totalEgressBytes;\n }\n\n /**\n * Cumulative ingress bytes across all sessions.\n */\n public get totalIngressBytes(): number | undefined {\n return this.sandbox.totalIngressBytes;\n }\n\n /**\n * Cumulative active CPU duration in milliseconds across all sessions.\n */\n public get totalActiveCpuDurationMs(): number | undefined {\n return this.sandbox.totalActiveCpuDurationMs;\n }\n\n /**\n * Cumulative wall-clock duration in milliseconds across all sessions.\n */\n public get totalDurationMs(): number | undefined {\n return this.sandbox.totalDurationMs;\n }\n\n /**\n * When this sandbox was last updated.\n */\n public get updatedAt(): Date {\n return new Date(this.sandbox.updatedAt);\n }\n\n /**\n * When the sandbox status was last updated.\n */\n public get statusUpdatedAt(): Date | undefined {\n return this.sandbox.statusUpdatedAt\n ? new Date(this.sandbox.statusUpdatedAt)\n : undefined;\n }\n\n /**\n * When this sandbox was created.\n */\n public get createdAt(): Date {\n return new Date(this.sandbox.createdAt);\n }\n\n /**\n * Interactive port.\n */\n public get interactivePort(): number | undefined {\n return this.currentSession().interactivePort;\n }\n\n /**\n * The status of the current session.\n */\n public get status(): SessionMetaData[\"status\"] {\n return this.currentSession().status;\n }\n\n /**\n * The default timeout of this sandbox in milliseconds.\n */\n public get timeout(): number | undefined {\n return this.sandbox.timeout;\n }\n\n /**\n * Key-value tags attached to the sandbox.\n */\n public get tags(): Record<string, string> | undefined {\n return this.sandbox.tags;\n }\n\n /**\n * The default network policy of this sandbox.\n */\n public get networkPolicy(): NetworkPolicy | undefined {\n return this.sandbox.networkPolicy\n ? fromAPINetworkPolicy(this.sandbox.networkPolicy)\n : undefined;\n }\n\n /**\n * If the session was created from a snapshot, the ID of that snapshot.\n */\n public get sourceSnapshotId(): string | undefined {\n return this.currentSession().sourceSnapshotId;\n }\n\n /**\n * The current snapshot ID of this sandbox, if any.\n */\n public get currentSnapshotId(): string | undefined {\n return this.sandbox.currentSnapshotId;\n }\n\n /**\n * The default snapshot expiration in milliseconds, if set.\n */\n public get snapshotExpiration(): number | undefined {\n return this.sandbox.snapshotExpiration;\n }\n\n /**\n * The amount of CPU used by the session. Only reported once the VM is stopped.\n */\n public get activeCpuUsageMs(): number | undefined {\n return this.currentSession().activeCpuUsageMs;\n }\n\n /**\n * The amount of network data used by the session. Only reported once the VM is stopped.\n */\n public get networkTransfer():\n | { ingress: number; egress: number }\n | undefined {\n return this.currentSession().networkTransfer;\n }\n\n /**\n * Allow to get a list of sandboxes for a team narrowed to the given params.\n * It returns both the sandboxes and the pagination metadata to allow getting\n * the next page of results.\n */\n static async list(\n params?: Partial<Parameters<APIClient[\"listSandboxes\"]>[0]> &\n Partial<Credentials> &\n WithFetchOptions,\n ) {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n fetch: params?.fetch,\n });\n const response = await client.listSandboxes({\n ...credentials,\n ...params,\n });\n return response.json;\n }\n\n /**\n * Serialize a Sandbox instance to plain data for @workflow/serde.\n *\n * @param instance - The Sandbox instance to serialize\n * @returns A plain object containing sandbox metadata and routes\n */\n static [WORKFLOW_SERIALIZE](instance: Sandbox): SerializedSandbox {\n return {\n metadata: instance.session?._sessionSnapshot!,\n routes: instance.session?.routes ?? [],\n sandboxMetadata: instance.sandbox,\n projectId: instance.projectId,\n };\n }\n\n /**\n * Deserialize a Sandbox from serialized snapshot data.\n *\n * The deserialized instance uses the serialized metadata synchronously and\n * lazily creates an API client only when methods perform API requests.\n *\n * @param data - The serialized sandbox data\n * @returns The reconstructed Sandbox instance\n */\n static [WORKFLOW_DESERIALIZE](data: SerializedSandbox): Sandbox {\n const sandbox = new Sandbox({\n sandbox: data.sandboxMetadata!,\n routes: data.routes,\n projectId: data.projectId,\n });\n if (data.metadata) {\n sandbox.session = new Session({ routes: data.routes, snapshot: data.metadata });\n }\n return sandbox;\n }\n\n /**\n * Create a new sandbox.\n *\n * @param params - Creation parameters and optional credentials.\n * @returns A promise resolving to the created {@link Sandbox}.\n * @example\n * <caption>Create a sandbox with default options</caption>\n * const sandbox = await Sandbox.create();\n *\n * @example\n * <caption>Create a sandbox and drop it in the end of the block</caption>\n * async function fn() {\n * await using const sandbox = await Sandbox.create();\n * // Sandbox automatically stopped at the end of the lexical scope\n * }\n */\n static async create(\n params?: WithPrivate<\n CreateSandboxParams | (CreateSandboxParams & Credentials)\n > &\n WithFetchOptions,\n ): Promise<Sandbox & AsyncDisposable> {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n fetch: params?.fetch,\n });\n\n const privateParams = getPrivateParams(params);\n const response = await client.createSandbox({\n source: params?.source,\n projectId: credentials.projectId,\n ports: params?.ports ?? [],\n timeout: params?.timeout,\n resources: params?.resources,\n runtime: params && \"runtime\" in params ? params?.runtime : undefined,\n networkPolicy: params?.networkPolicy,\n env: params?.env,\n tags: params?.tags,\n snapshotExpiration: params?.snapshotExpiration,\n signal: params?.signal,\n name: params?.name,\n persistent: params?.persistent,\n ...privateParams,\n });\n\n return new DisposableSandbox({\n client,\n session: response.json.session,\n sandbox: response.json.sandbox,\n routes: response.json.routes,\n projectId: credentials.projectId,\n onResume: params?.onResume,\n });\n }\n\n /**\n * Retrieve an existing sandbox and resume its session.\n *\n * @param params - Get parameters and optional credentials.\n * @returns A promise resolving to the {@link Sandbox}.\n */\n static async get(\n params: WithPrivate<GetSandboxParams | (GetSandboxParams & Credentials)> &\n WithFetchOptions,\n ): Promise<Sandbox> {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n fetch: params.fetch,\n });\n\n const privateParams = getPrivateParams(params);\n const response = await client.getSandbox({\n name: params.name,\n projectId: credentials.projectId,\n resume: params.resume,\n signal: params.signal,\n ...privateParams,\n });\n\n const sandbox = new Sandbox({\n client,\n session: response.json.session,\n sandbox: response.json.sandbox,\n routes: response.json.routes,\n projectId: credentials.projectId,\n onResume: params.onResume,\n });\n\n if (response.json.resumed && params.onResume) {\n await params.onResume(sandbox);\n }\n\n return sandbox;\n }\n\n /**\n * Create a new Sandbox instance.\n *\n * @param params.client - Optional API client. If not provided, will be lazily created using global credentials.\n * @param params.routes - Port-to-subdomain mappings for exposed ports\n * @param params.sandbox - Sandbox snapshot metadata\n */\n constructor({\n client,\n routes,\n session,\n sandbox,\n projectId,\n onResume,\n }: {\n client?: APIClient;\n routes: SandboxRouteData[];\n session?: SessionMetaData;\n sandbox: SandboxMetaData;\n projectId?: string;\n onResume?: (sandbox: Sandbox) => Promise<void>;\n }) {\n this._client = client ?? null;\n if (session) {\n this.session = new Session({ client: client!, routes, session });\n }\n this.sandbox = sandbox;\n this.projectId = projectId ?? \"\";\n this.onResume = onResume;\n }\n\n /**\n * Get the current session (the running VM) for this sandbox.\n *\n * @returns The {@link Session} instance.\n */\n currentSession(): Session {\n if (!this.session) {\n throw new Error(\"No active session. Run a command or call resume first.\");\n }\n return this.session;\n }\n\n /**\n * Resume this sandbox by creating a new session via `getSandbox`.\n */\n private async resume(signal?: AbortSignal): Promise<void> {\n if (!this.resumePromise) {\n this.resumePromise = this.doResume(signal).finally(() => {\n this.resumePromise = null;\n });\n }\n return this.resumePromise;\n }\n\n private async doResume(signal?: AbortSignal): Promise<void> {\n const client = await this.ensureClient();\n const response = await client.getSandbox({\n name: this.sandbox.name,\n projectId: this.projectId,\n resume: true,\n signal,\n });\n this.session = new Session({\n client,\n routes: response.json.routes,\n session: response.json.session,\n });\n if (this.onResume && response.json.resumed) {\n await this.onResume(this);\n }\n }\n\n /**\n * Poll until the current session reaches a terminal state, then resume.\n */\n private async waitForStopAndResume(signal?: AbortSignal): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n const pollingInterval = 500;\n let status = this.session!.status;\n\n while (status === \"stopping\" || status === \"snapshotting\") {\n await setTimeout(pollingInterval, undefined, { signal });\n const poll = await client.getSession({\n sessionId: this.session!.sessionId,\n signal,\n });\n this.session = new Session({\n client,\n routes: poll.json.routes,\n session: poll.json.session,\n });\n status = poll.json.session.status;\n }\n await this.resume(signal);\n }\n\n /**\n * Execute `fn`, and if the session is stopped/stopping/snapshotting, resume and retry.\n */\n private async withResume<T>(\n fn: () => Promise<T>,\n signal?: AbortSignal,\n ): Promise<T> {\n if (!this.session) {\n await this.resume(signal);\n }\n try {\n return await fn();\n } catch (err) {\n if (isSandboxStoppedError(err)) {\n await this.resume(signal);\n return fn();\n }\n if (isSandboxStoppingError(err) || isSandboxSnapshottingError(err)) {\n await this.waitForStopAndResume(signal);\n return fn();\n }\n throw err;\n }\n }\n\n /**\n * Start executing a command in this sandbox.\n *\n * @param command - The command to execute.\n * @param args - Arguments to pass to the command.\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the command execution.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n async runCommand(\n command: string,\n args?: string[],\n opts?: { signal?: AbortSignal },\n ): Promise<CommandFinished>;\n /**\n * Start executing a command in detached mode.\n *\n * @param params - The command parameters.\n * @returns A {@link Command} instance for the running command.\n */\n async runCommand(\n params: RunCommandParams & { detached: true },\n ): Promise<Command>;\n\n /**\n * Start executing a command in this sandbox.\n *\n * @param params - The command parameters.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n\n async runCommand(params: RunCommandParams): Promise<CommandFinished>;\n\n async runCommand(\n commandOrParams: string | RunCommandParams,\n args?: string[],\n opts?: { signal?: AbortSignal },\n ): Promise<Command | CommandFinished> {\n \"use step\";\n const signal =\n typeof commandOrParams === \"string\"\n ? opts?.signal\n : commandOrParams.signal;\n return this.withResume(\n () => this.session!.runCommand(commandOrParams as any, args, opts),\n signal,\n );\n }\n\n /**\n * Internal helper to start a command in the sandbox.\n *\n * @param params - Command execution parameters.\n * @returns A {@link Command} or {@link CommandFinished}, depending on `detached`.\n * @internal\n */\n async getCommand(\n cmdId: string,\n opts?: { signal?: AbortSignal },\n ): Promise<Command> {\n \"use step\";\n return this.withResume(\n () => this.session!.getCommand(cmdId, opts),\n opts?.signal,\n );\n }\n\n /**\n * Create a directory in the filesystem of this sandbox.\n *\n * @param path - Path of the directory to create\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n */\n async mkDir(path: string, opts?: { signal?: AbortSignal }): Promise<void> {\n \"use step\";\n return this.withResume(\n () => this.session!.mkDir(path, opts),\n opts?.signal,\n );\n }\n\n /**\n * Read a file from the filesystem of this sandbox as a stream.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to a ReadableStream containing the file contents, or null if file not found\n */\n async readFile(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<NodeJS.ReadableStream | null> {\n \"use step\";\n return this.withResume(\n () => this.session!.readFile(file, opts),\n opts?.signal,\n );\n }\n\n /**\n * Read a file from the filesystem of this sandbox as a Buffer.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to the file contents as a Buffer, or null if file not found\n */\n async readFileToBuffer(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<Buffer | null> {\n \"use step\";\n return this.withResume(\n () => this.session!.readFileToBuffer(file, opts),\n opts?.signal,\n );\n }\n\n /**\n * Download a file from the sandbox to the local filesystem.\n *\n * @param src - Source file on the sandbox, with path and optional cwd\n * @param dst - Destination file on the local machine, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.mkdirRecursive - If true, create parent directories for the destination if they don't exist.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns The absolute path to the written file, or null if the source file was not found\n */\n async downloadFile(\n src: { path: string; cwd?: string },\n dst: { path: string; cwd?: string },\n opts?: { mkdirRecursive?: boolean; signal?: AbortSignal },\n ): Promise<string | null> {\n \"use step\";\n return this.withResume(\n () => this.session!.downloadFile(src, dst, opts),\n opts?.signal,\n );\n }\n\n /**\n * Write files to the filesystem of this sandbox.\n * Defaults to writing to /vercel/sandbox unless an absolute path is specified.\n * Writes files using the `vercel-sandbox` user.\n *\n * @param files - Array of files with path, content, and optional mode (permissions)\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the files are written\n *\n * @example\n * // Write an executable script\n * await sandbox.writeFiles([\n * { path: \"/usr/local/bin/myscript\", content: \"#!/bin/bash\\necho hello\", mode: 0o755 }\n * ]);\n */\n async writeFiles(\n files: {\n path: string;\n content: string | Uint8Array;\n mode?: number;\n }[],\n opts?: { signal?: AbortSignal },\n ) {\n \"use step\";\n return this.withResume(\n () => this.session!.writeFiles(files, opts),\n opts?.signal,\n );\n }\n\n /**\n * Get the public domain of a port of this sandbox.\n *\n * @param p - Port number to resolve\n * @returns A full domain (e.g. `https://subdomain.vercel.run`)\n * @throws If the port has no associated route\n */\n domain(p: number): string {\n return this.currentSession().domain(p);\n }\n\n /**\n * Stop the sandbox.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @param opts.blocking - If true, poll until the sandbox has fully stopped and return the final state.\n * @returns The sandbox at the time the stop was acknowledged, or after fully stopped if `blocking` is true.\n */\n async stop(opts?: {\n signal?: AbortSignal;\n blocking?: boolean;\n }): Promise<SandboxSnapshot> {\n \"use step\";\n if (!this.session) {\n throw new Error(\"No active session to stop.\");\n }\n return this.session.stop(opts);\n }\n\n /**\n * Update the network policy for this sandbox.\n *\n * @deprecated Use {@link Sandbox.update} instead.\n *\n * @param networkPolicy - The new network policy to apply.\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the network policy is updated.\n *\n * @example\n * // Restrict to specific domains\n * await sandbox.updateNetworkPolicy({\n * allow: [\"*.npmjs.org\", \"github.com\"],\n * });\n *\n * @example\n * // Inject credentials with per-domain transformers\n * await sandbox.updateNetworkPolicy({\n * allow: {\n * \"ai-gateway.vercel.sh\": [{\n * transform: [{\n * headers: { authorization: \"Bearer ...\" }\n * }]\n * }],\n * \"*\": []\n * }\n * });\n *\n * @example\n * // Deny all network access\n * await sandbox.updateNetworkPolicy(\"deny-all\");\n */\n async updateNetworkPolicy(\n networkPolicy: NetworkPolicy,\n opts?: { signal?: AbortSignal },\n ): Promise<NetworkPolicy> {\n \"use step\";\n await this.withResume(\n () => this.session!.update({ networkPolicy: networkPolicy }, opts),\n opts?.signal,\n );\n\n return this.session!.networkPolicy!;\n }\n\n /**\n * Extend the timeout of the sandbox by the specified duration.\n *\n * This allows you to extend the lifetime of a sandbox up until the maximum\n * execution timeout for your plan.\n *\n * @param duration - The duration in milliseconds to extend the timeout by\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the timeout is extended\n *\n * @example\n * const sandbox = await Sandbox.create({ timeout: ms('10m') });\n * // Extends timeout by 5 minutes, to a total of 15 minutes.\n * await sandbox.extendTimeout(ms('5m'));\n */\n async extendTimeout(\n duration: number,\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n return this.withResume(\n () => this.session!.extendTimeout(duration, opts),\n opts?.signal,\n );\n }\n\n /**\n * Create a snapshot from this currently running sandbox. New sandboxes can\n * then be created from this snapshot using {@link Sandbox.createFromSnapshot}.\n *\n * Note: this sandbox will be stopped as part of the snapshot creation process.\n *\n * @param opts - Optional parameters.\n * @param opts.expiration - Optional expiration time in milliseconds. Use 0 for no expiration at all.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to the Snapshot instance\n */\n async snapshot(opts?: {\n expiration?: number;\n signal?: AbortSignal;\n }): Promise<Snapshot> {\n \"use step\";\n return this.withResume(\n () => this.session!.snapshot(opts),\n opts?.signal,\n );\n }\n\n /**\n * Update the sandbox configuration.\n *\n * @param params - Fields to update.\n * @param opts - Optional abort signal.\n */\n async update(\n params: {\n persistent?: boolean;\n resources?: { vcpus?: number };\n timeout?: number;\n networkPolicy?: NetworkPolicy;\n tags?: Record<string, string>;\n snapshotExpiration?: number;\n currentSnapshotId?: string;\n },\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n let resources: { vcpus: number; memory: number } | undefined;\n if (params.resources?.vcpus) {\n resources = {\n vcpus: params.resources.vcpus,\n memory: params.resources.vcpus * 2048,\n };\n }\n\n // Update the sandbox config. This config will be used on the next session.\n const response = await client.updateSandbox({\n name: this.sandbox.name,\n projectId: this.projectId,\n persistent: params.persistent,\n resources,\n timeout: params.timeout,\n networkPolicy: params.networkPolicy,\n tags: params.tags,\n snapshotExpiration: params.snapshotExpiration,\n currentSnapshotId: params.currentSnapshotId,\n signal: opts?.signal,\n });\n this.sandbox = response.json.sandbox;\n\n // Update the current session config. This only applies to network policy.\n if (params.networkPolicy) {\n try {\n return await this.session?.update(\n { networkPolicy: params.networkPolicy },\n opts,\n );\n } catch (err) {\n if (isSandboxStoppedError(err) || isSandboxStoppingError(err)) {\n return;\n }\n throw err;\n }\n }\n }\n\n /**\n * Delete this sandbox.\n *\n * After deletion the instance becomes inert — all further API calls will\n * throw immediately.\n */\n async delete(opts?: { signal?: AbortSignal }): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n await client.deleteSandbox({\n name: this.sandbox.name,\n projectId: this.projectId,\n signal: opts?.signal,\n });\n }\n\n /**\n * List sessions (VMs) that have been created for this sandbox.\n *\n * @param params - Optional pagination parameters.\n * @returns The list of sessions and pagination metadata.\n */\n async listSessions(params?: {\n limit?: number;\n cursor?: string;\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n \"use step\";\n const client = await this.ensureClient();\n const response = await client.listSessions({\n projectId: this.projectId,\n name: this.sandbox.name,\n limit: params?.limit,\n cursor: params?.cursor,\n sortOrder: params?.sortOrder,\n signal: params?.signal,\n });\n return response.json;\n }\n\n /**\n * List snapshots that belong to this sandbox.\n *\n * @param params - Optional pagination parameters.\n * @returns The list of snapshots and pagination metadata.\n */\n async listSnapshots(params?: {\n limit?: number;\n cursor?: string;\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n \"use step\";\n const client = await this.ensureClient();\n const response = await client.listSnapshots({\n projectId: this.projectId,\n name: this.sandbox.name,\n limit: params?.limit,\n cursor: params?.cursor,\n sortOrder: params?.sortOrder,\n signal: params?.signal,\n });\n return response.json;\n }\n}\n\n/**\n * A {@link Sandbox} that can automatically be disposed using a `await using` statement.\n *\n * @example\n * {\n * await using const sandbox = await Sandbox.create();\n * }\n * // Sandbox is automatically stopped here\n */\nclass DisposableSandbox extends Sandbox implements AsyncDisposable {\n async [Symbol.asyncDispose]() {\n await this.stop();\n }\n}\n"],"mappings":";;;;;;;;;;;AAkJA,SAAS,sBAAsB,KAAuB;AACpD,QAAO,eAAe,YAAY,IAAI,SAAS,WAAW;;AAG5D,SAAS,uBAAuB,KAAuB;AACrD,QACE,eAAe,YACf,IAAI,SAAS,WAAW,OACvB,IAAI,MAAc,OAAO,SAAS;;AAIvC,SAAS,2BAA2B,KAAuB;AACzD,QACE,eAAe,YACf,IAAI,SAAS,WAAW,OACvB,IAAI,MAAc,OAAO,SAAS;;;;;;;AAyBvC,IAAa,UAAb,MAAa,QAAQ;;;;;CA4BnB,MAAc,eAAmC;AAC/C;AACA,MAAI,KAAK,QAAS,QAAO,KAAK;EAC9B,MAAM,cAAc,MAAM,gBAAgB;AAC1C,OAAK,UAAU,IAAI,UAAU;GAC3B,QAAQ,YAAY;GACpB,OAAO,YAAY;GACpB,CAAC;AACF,SAAO,KAAK;;;;;CAMd,IAAW,OAAe;AACxB,SAAO,KAAK,QAAQ;;;;;;CAOtB,IAAW,SAA6B;AACtC,SAAO,KAAK,gBAAgB,CAAC;;;;;CAM/B,IAAW,aAAsB;AAC/B,SAAO,KAAK,QAAQ;;;;;CAMtB,IAAW,SAA6B;AACtC,SAAO,KAAK,QAAQ;;;;;CAMtB,IAAW,QAA4B;AACrC,SAAO,KAAK,QAAQ;;;;;CAMtB,IAAW,SAA6B;AACtC,SAAO,KAAK,QAAQ;;;CAItB,IAAW,UAA8B;AACvC,SAAO,KAAK,QAAQ;;;;;CAMtB,IAAW,mBAAuC;AAChD,SAAO,KAAK,QAAQ;;;;;CAMtB,IAAW,oBAAwC;AACjD,SAAO,KAAK,QAAQ;;;;;CAMtB,IAAW,2BAA+C;AACxD,SAAO,KAAK,QAAQ;;;;;CAMtB,IAAW,kBAAsC;AAC/C,SAAO,KAAK,QAAQ;;;;;CAMtB,IAAW,YAAkB;AAC3B,SAAO,IAAI,KAAK,KAAK,QAAQ,UAAU;;;;;CAMzC,IAAW,kBAAoC;AAC7C,SAAO,KAAK,QAAQ,kBAChB,IAAI,KAAK,KAAK,QAAQ,gBAAgB,GACtC;;;;;CAMN,IAAW,YAAkB;AAC3B,SAAO,IAAI,KAAK,KAAK,QAAQ,UAAU;;;;;CAMzC,IAAW,kBAAsC;AAC/C,SAAO,KAAK,gBAAgB,CAAC;;;;;CAM/B,IAAW,SAAoC;AAC7C,SAAO,KAAK,gBAAgB,CAAC;;;;;CAM/B,IAAW,UAA8B;AACvC,SAAO,KAAK,QAAQ;;;;;CAMtB,IAAW,OAA2C;AACpD,SAAO,KAAK,QAAQ;;;;;CAMtB,IAAW,gBAA2C;AACpD,SAAO,KAAK,QAAQ,gBAChB,qBAAqB,KAAK,QAAQ,cAAc,GAChD;;;;;CAMN,IAAW,mBAAuC;AAChD,SAAO,KAAK,gBAAgB,CAAC;;;;;CAM/B,IAAW,oBAAwC;AACjD,SAAO,KAAK,QAAQ;;;;;CAMtB,IAAW,qBAAyC;AAClD,SAAO,KAAK,QAAQ;;;;;CAMtB,IAAW,mBAAuC;AAChD,SAAO,KAAK,gBAAgB,CAAC;;;;;CAM/B,IAAW,kBAEG;AACZ,SAAO,KAAK,gBAAgB,CAAC;;;;;;;CAQ/B,aAAa,KACX,QAGA;AACA;EACA,MAAM,cAAc,MAAM,eAAe,OAAO;AAUhD,UAJiB,MALF,IAAI,UAAU;GAC3B,QAAQ,YAAY;GACpB,OAAO,YAAY;GACnB,OAAO,QAAQ;GAChB,CAAC,CAC4B,cAAc;GAC1C,GAAG;GACH,GAAG;GACJ,CAAC,EACc;;;;;;;;CASlB,QAAQ,oBAAoB,UAAsC;AAChE,SAAO;GACL,UAAU,SAAS,SAAS;GAC5B,QAAQ,SAAS,SAAS,UAAU,EAAE;GACtC,iBAAiB,SAAS;GAC1B,WAAW,SAAS;GACrB;;;;;;;;;;;CAYH,QAAQ,sBAAsB,MAAkC;EAC9D,MAAM,UAAU,IAAI,QAAQ;GAC1B,SAAS,KAAK;GACd,QAAQ,KAAK;GACb,WAAW,KAAK;GACjB,CAAC;AACF,MAAI,KAAK,SACP,SAAQ,UAAU,IAAI,QAAQ;GAAE,QAAQ,KAAK;GAAQ,UAAU,KAAK;GAAU,CAAC;AAEjF,SAAO;;;;;;;;;;;;;;;;;;CAmBT,aAAa,OACX,QAIoC;AACpC;EACA,MAAM,cAAc,MAAM,eAAe,OAAO;EAChD,MAAM,SAAS,IAAI,UAAU;GAC3B,QAAQ,YAAY;GACpB,OAAO,YAAY;GACnB,OAAO,QAAQ;GAChB,CAAC;EAEF,MAAM,gBAAgB,iBAAiB,OAAO;EAC9C,MAAM,WAAW,MAAM,OAAO,cAAc;GAC1C,QAAQ,QAAQ;GAChB,WAAW,YAAY;GACvB,OAAO,QAAQ,SAAS,EAAE;GAC1B,SAAS,QAAQ;GACjB,WAAW,QAAQ;GACnB,SAAS,UAAU,aAAa,SAAS,QAAQ,UAAU;GAC3D,eAAe,QAAQ;GACvB,KAAK,QAAQ;GACb,MAAM,QAAQ;GACd,oBAAoB,QAAQ;GAC5B,QAAQ,QAAQ;GAChB,MAAM,QAAQ;GACd,YAAY,QAAQ;GACpB,GAAG;GACJ,CAAC;AAEF,SAAO,IAAI,kBAAkB;GAC3B;GACA,SAAS,SAAS,KAAK;GACvB,SAAS,SAAS,KAAK;GACvB,QAAQ,SAAS,KAAK;GACtB,WAAW,YAAY;GACvB,UAAU,QAAQ;GACnB,CAAC;;;;;;;;CASJ,aAAa,IACX,QAEkB;AAClB;EACA,MAAM,cAAc,MAAM,eAAe,OAAO;EAChD,MAAM,SAAS,IAAI,UAAU;GAC3B,QAAQ,YAAY;GACpB,OAAO,YAAY;GACnB,OAAO,OAAO;GACf,CAAC;EAEF,MAAM,gBAAgB,iBAAiB,OAAO;EAC9C,MAAM,WAAW,MAAM,OAAO,WAAW;GACvC,MAAM,OAAO;GACb,WAAW,YAAY;GACvB,QAAQ,OAAO;GACf,QAAQ,OAAO;GACf,GAAG;GACJ,CAAC;EAEF,MAAM,UAAU,IAAI,QAAQ;GAC1B;GACA,SAAS,SAAS,KAAK;GACvB,SAAS,SAAS,KAAK;GACvB,QAAQ,SAAS,KAAK;GACtB,WAAW,YAAY;GACvB,UAAU,OAAO;GAClB,CAAC;AAEF,MAAI,SAAS,KAAK,WAAW,OAAO,SAClC,OAAM,OAAO,SAAS,QAAQ;AAGhC,SAAO;;;;;;;;;CAUT,YAAY,EACV,QACA,QACA,SACA,SACA,WACA,YAQC;OAzYK,UAA4B;OAM5B,gBAAsC;AAoY5C,OAAK,UAAU,UAAU;AACzB,MAAI,QACF,MAAK,UAAU,IAAI,QAAQ;GAAU;GAAS;GAAQ;GAAS,CAAC;AAElE,OAAK,UAAU;AACf,OAAK,YAAY,aAAa;AAC9B,OAAK,WAAW;;;;;;;CAQlB,iBAA0B;AACxB,MAAI,CAAC,KAAK,QACR,OAAM,IAAI,MAAM,yDAAyD;AAE3E,SAAO,KAAK;;;;;CAMd,MAAc,OAAO,QAAqC;AACxD,MAAI,CAAC,KAAK,cACR,MAAK,gBAAgB,KAAK,SAAS,OAAO,CAAC,cAAc;AACvD,QAAK,gBAAgB;IACrB;AAEJ,SAAO,KAAK;;CAGd,MAAc,SAAS,QAAqC;EAC1D,MAAM,SAAS,MAAM,KAAK,cAAc;EACxC,MAAM,WAAW,MAAM,OAAO,WAAW;GACvC,MAAM,KAAK,QAAQ;GACnB,WAAW,KAAK;GAChB,QAAQ;GACR;GACD,CAAC;AACF,OAAK,UAAU,IAAI,QAAQ;GACzB;GACA,QAAQ,SAAS,KAAK;GACtB,SAAS,SAAS,KAAK;GACxB,CAAC;AACF,MAAI,KAAK,YAAY,SAAS,KAAK,QACjC,OAAM,KAAK,SAAS,KAAK;;;;;CAO7B,MAAc,qBAAqB,QAAqC;AACtE;EACA,MAAM,SAAS,MAAM,KAAK,cAAc;EACxC,MAAM,kBAAkB;EACxB,IAAI,SAAS,KAAK,QAAS;AAE3B,SAAO,WAAW,cAAc,WAAW,gBAAgB;AACzD,SAAM,WAAW,iBAAiB,QAAW,EAAE,QAAQ,CAAC;GACxD,MAAM,OAAO,MAAM,OAAO,WAAW;IACnC,WAAW,KAAK,QAAS;IACzB;IACD,CAAC;AACF,QAAK,UAAU,IAAI,QAAQ;IACzB;IACA,QAAQ,KAAK,KAAK;IAClB,SAAS,KAAK,KAAK;IACpB,CAAC;AACF,YAAS,KAAK,KAAK,QAAQ;;AAE7B,QAAM,KAAK,OAAO,OAAO;;;;;CAM3B,MAAc,WACZ,IACA,QACY;AACZ,MAAI,CAAC,KAAK,QACR,OAAM,KAAK,OAAO,OAAO;AAE3B,MAAI;AACF,UAAO,MAAM,IAAI;WACV,KAAK;AACZ,OAAI,sBAAsB,IAAI,EAAE;AAC9B,UAAM,KAAK,OAAO,OAAO;AACzB,WAAO,IAAI;;AAEb,OAAI,uBAAuB,IAAI,IAAI,2BAA2B,IAAI,EAAE;AAClE,UAAM,KAAK,qBAAqB,OAAO;AACvC,WAAO,IAAI;;AAEb,SAAM;;;CAqCV,MAAM,WACJ,iBACA,MACA,MACoC;AACpC;EACA,MAAM,SACJ,OAAO,oBAAoB,WACvB,MAAM,SACN,gBAAgB;AACtB,SAAO,KAAK,iBACJ,KAAK,QAAS,WAAW,iBAAwB,MAAM,KAAK,EAClE,OACD;;;;;;;;;CAUH,MAAM,WACJ,OACA,MACkB;AAClB;AACA,SAAO,KAAK,iBACJ,KAAK,QAAS,WAAW,OAAO,KAAK,EAC3C,MAAM,OACP;;;;;;;;;CAUH,MAAM,MAAM,MAAc,MAAgD;AACxE;AACA,SAAO,KAAK,iBACJ,KAAK,QAAS,MAAM,MAAM,KAAK,EACrC,MAAM,OACP;;;;;;;;;;CAWH,MAAM,SACJ,MACA,MACuC;AACvC;AACA,SAAO,KAAK,iBACJ,KAAK,QAAS,SAAS,MAAM,KAAK,EACxC,MAAM,OACP;;;;;;;;;;CAWH,MAAM,iBACJ,MACA,MACwB;AACxB;AACA,SAAO,KAAK,iBACJ,KAAK,QAAS,iBAAiB,MAAM,KAAK,EAChD,MAAM,OACP;;;;;;;;;;;;CAaH,MAAM,aACJ,KACA,KACA,MACwB;AACxB;AACA,SAAO,KAAK,iBACJ,KAAK,QAAS,aAAa,KAAK,KAAK,KAAK,EAChD,MAAM,OACP;;;;;;;;;;;;;;;;;;CAmBH,MAAM,WACJ,OAKA,MACA;AACA;AACA,SAAO,KAAK,iBACJ,KAAK,QAAS,WAAW,OAAO,KAAK,EAC3C,MAAM,OACP;;;;;;;;;CAUH,OAAO,GAAmB;AACxB,SAAO,KAAK,gBAAgB,CAAC,OAAO,EAAE;;;;;;;;;;CAWxC,MAAM,KAAK,MAGkB;AAC3B;AACA,MAAI,CAAC,KAAK,QACR,OAAM,IAAI,MAAM,6BAA6B;AAE/C,SAAO,KAAK,QAAQ,KAAK,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoChC,MAAM,oBACJ,eACA,MACwB;AACxB;AACA,QAAM,KAAK,iBACH,KAAK,QAAS,OAAO,EAAiB,eAAe,EAAE,KAAK,EAClE,MAAM,OACP;AAED,SAAO,KAAK,QAAS;;;;;;;;;;;;;;;;;;CAmBvB,MAAM,cACJ,UACA,MACe;AACf;AACA,SAAO,KAAK,iBACJ,KAAK,QAAS,cAAc,UAAU,KAAK,EACjD,MAAM,OACP;;;;;;;;;;;;;CAcH,MAAM,SAAS,MAGO;AACpB;AACA,SAAO,KAAK,iBACJ,KAAK,QAAS,SAAS,KAAK,EAClC,MAAM,OACP;;;;;;;;CASH,MAAM,OACJ,QASA,MACe;AACf;EACA,MAAM,SAAS,MAAM,KAAK,cAAc;EACxC,IAAIA;AACJ,MAAI,OAAO,WAAW,MACpB,aAAY;GACV,OAAO,OAAO,UAAU;GACxB,QAAQ,OAAO,UAAU,QAAQ;GAClC;AAgBH,OAAK,WAZY,MAAM,OAAO,cAAc;GAC1C,MAAM,KAAK,QAAQ;GACnB,WAAW,KAAK;GAChB,YAAY,OAAO;GACnB;GACA,SAAS,OAAO;GAChB,eAAe,OAAO;GACtB,MAAM,OAAO;GACb,oBAAoB,OAAO;GAC3B,mBAAmB,OAAO;GAC1B,QAAQ,MAAM;GACf,CAAC,EACsB,KAAK;AAG7B,MAAI,OAAO,cACT,KAAI;AACF,UAAO,MAAM,KAAK,SAAS,OACzB,EAAE,eAAe,OAAO,eAAe,EACvC,KACD;WACM,KAAK;AACZ,OAAI,sBAAsB,IAAI,IAAI,uBAAuB,IAAI,CAC3D;AAEF,SAAM;;;;;;;;;CAWZ,MAAM,OAAO,MAAgD;AAC3D;AAEA,SADe,MAAM,KAAK,cAAc,EAC3B,cAAc;GACzB,MAAM,KAAK,QAAQ;GACnB,WAAW,KAAK;GAChB,QAAQ,MAAM;GACf,CAAC;;;;;;;;CASJ,MAAM,aAAa,QAKhB;AACD;AAUA,UARiB,OADF,MAAM,KAAK,cAAc,EACV,aAAa;GACzC,WAAW,KAAK;GAChB,MAAM,KAAK,QAAQ;GACnB,OAAO,QAAQ;GACf,QAAQ,QAAQ;GAChB,WAAW,QAAQ;GACnB,QAAQ,QAAQ;GACjB,CAAC,EACc;;;;;;;;CASlB,MAAM,cAAc,QAKjB;AACD;AAUA,UARiB,OADF,MAAM,KAAK,cAAc,EACV,cAAc;GAC1C,WAAW,KAAK;GAChB,MAAM,KAAK,QAAQ;GACnB,OAAO,QAAQ;GACf,QAAQ,QAAQ;GAChB,WAAW,QAAQ;GACnB,QAAQ,QAAQ;GACjB,CAAC,EACc;;;;;;;;;;;;AAapB,IAAM,oBAAN,cAAgC,QAAmC;CACjE,OAAO,OAAO,gBAAgB;AAC5B,QAAM,KAAK,MAAM"}
{"version":3,"file":"sandbox.js","names":["resources: { vcpus: number; memory: number } | undefined"],"sources":["../src/sandbox.ts"],"sourcesContent":["import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport type {\n SessionMetaData,\n SandboxRouteData,\n SandboxMetaData,\n SnapshotMetadata,\n} from \"./api-client/index.js\";\nimport { APIClient } from \"./api-client/index.js\";\nimport { APIError } from \"./api-client/api-error.js\";\nimport { type Credentials, getCredentials } from \"./utils/get-credentials.js\";\nimport { getPrivateParams, type WithPrivate } from \"./utils/types.js\";\nimport type { WithFetchOptions } from \"./api-client/api-client.js\";\nimport type { RUNTIMES } from \"./constants.js\";\nimport { Session, type RunCommandParams } from \"./session.js\";\nimport type { Command, CommandFinished } from \"./command.js\";\nimport type { Snapshot } from \"./snapshot.js\";\nimport type { SandboxSnapshot } from \"./utils/sandbox-snapshot.js\";\nimport type {\n NetworkPolicy,\n NetworkPolicyKeyValueMatcher,\n NetworkPolicyMatch,\n NetworkPolicyMatcher,\n} from \"./network-policy.js\";\nimport { fromAPINetworkPolicy } from \"./utils/network-policy.js\";\nimport { attachPaginator } from \"./utils/paginator.js\";\nimport { setTimeout } from \"node:timers/promises\";\nimport { FileSystem } from \"./filesystem.js\";\n\nexport type {\n NetworkPolicy,\n NetworkPolicyKeyValueMatcher,\n NetworkPolicyMatch,\n NetworkPolicyMatcher,\n};\n\n/** @inline */\nexport interface BaseCreateSandboxParams {\n /**\n * The name of the sandbox. If omitted, a random name will be generated.\n */\n name?: string;\n /**\n * The source of the sandbox.\n *\n * Omit this parameter start a sandbox without a source.\n *\n * For git sources:\n * - `depth`: Creates shallow clones with limited commit history (minimum: 1)\n * - `revision`: Clones and checks out a specific commit, branch, or tag\n */\n source?:\n | {\n type: \"git\";\n url: string;\n depth?: number;\n revision?: string;\n }\n | {\n type: \"git\";\n url: string;\n username: string;\n password: string;\n depth?: number;\n revision?: string;\n }\n | { type: \"tarball\"; url: string };\n /**\n * Array of port numbers to expose from the sandbox. Sandboxes can\n * expose up to 4 ports.\n */\n ports?: number[];\n /**\n * Timeout in milliseconds before the sandbox auto-terminates.\n */\n timeout?: number;\n /**\n * Resources to allocate to the sandbox.\n *\n * Your sandbox will get the amount of vCPUs you specify here and\n * 2048 MB of memory per vCPU.\n */\n resources?: { vcpus: number };\n /**\n * The runtime of the sandbox, currently only `node24`, `node22` and `python3.13` are supported.\n * If not specified, the default runtime `node24` will be used.\n */\n runtime?: RUNTIMES | (string & {});\n /**\n * Network policy to define network restrictions for the sandbox.\n * Defaults to full internet access if not specified.\n */\n networkPolicy?: NetworkPolicy;\n /**\n * Default environment variables for the sandbox.\n * These are inherited by all commands unless overridden with\n * the `env` option in `runCommand`.\n *\n * @example\n * const sandbox = await Sandbox.create({\n * env: { NODE_ENV: \"production\", API_KEY: \"secret\" },\n * });\n * // All commands will have NODE_ENV and API_KEY set\n * await sandbox.runCommand(\"node\", [\"app.js\"]);\n */\n env?: Record<string, string>;\n /**\n * Key-value tags to associate with the sandbox. Maximum 5 tags.\n * @example { env: \"staging\", team: \"infra\" }\n */\n tags?: Record<string, string>;\n\n /**\n * An AbortSignal to cancel sandbox creation.\n */\n signal?: AbortSignal;\n /**\n * Enable or disable automatic restore of the filesystem between sessions.\n */\n persistent?: boolean;\n /**\n * Default snapshot expiration in milliseconds.\n * When set, snapshots created for this sandbox will expire after this duration.\n * Use `0` for no expiration.\n */\n snapshotExpiration?: number;\n /**\n * Called when the sandbox session is resumed (e.g., after a snapshot restore).\n * Use this to re-warm caches, restore transient state, or run other setup logic.\n */\n onResume?: (sandbox: Sandbox) => Promise<void>;\n}\n\nexport type CreateSandboxParams =\n | BaseCreateSandboxParams\n | (Omit<BaseCreateSandboxParams, \"runtime\" | \"source\"> & {\n source: { type: \"snapshot\"; snapshotId: string };\n });\n\n/** @inline */\ninterface GetSandboxParams {\n /**\n * The name of the sandbox.\n */\n name: string;\n /**\n * Whether to resume an existing session. Defaults to true.\n */\n resume?: boolean;\n /**\n * An AbortSignal to cancel the operation.\n */\n signal?: AbortSignal;\n /**\n * Called when the sandbox session is resumed (e.g., after a snapshot restore).\n * Use this to re-warm caches, restore transient state, or run other setup logic.\n */\n onResume?: (sandbox: Sandbox) => Promise<void>;\n}\n\n/**\n * Extends both {@link BaseCreateSandboxParams} and {@link GetSandboxParams}\n * (minus `name`, which is required on get but optional here) so that any\n * new parameter added to either flow is picked up automatically. The\n * structural overlap on `signal` / `onResume` is intentional — both\n * interfaces declare them with identical optional types.\n * @inline\n */\ninterface GetOrCreateSandboxParams\n extends BaseCreateSandboxParams,\n Omit<GetSandboxParams, \"name\"> {\n /**\n * Called once after a sandbox is freshly created (not when an existing\n * sandbox is retrieved). Use this for one-time setup such as seeding\n * files or warming caches. The returned promise is awaited before\n * {@link Sandbox.getOrCreate} resolves.\n */\n onCreate?: (sandbox: Sandbox) => Promise<void>;\n}\n\nfunction isSandboxStoppedError(err: unknown): boolean {\n return err instanceof APIError && err.response.status === 410;\n}\n\nfunction isNotFoundError(err: unknown): boolean {\n return err instanceof APIError && err.response.status === 404;\n}\n\nfunction isSnapshotNotFoundError(err: unknown): boolean {\n return (\n err instanceof APIError &&\n err.response.status === 400 &&\n (err.json as any)?.error?.code === \"snapshot_not_found\"\n );\n}\n\nfunction isSandboxStoppingError(err: unknown): boolean {\n return (\n err instanceof APIError &&\n err.response.status === 422 &&\n (err.json as any)?.error?.code === \"sandbox_stopping\"\n );\n}\n\nfunction isSandboxSnapshottingError(err: unknown): boolean {\n return (\n err instanceof APIError &&\n err.response.status === 422 &&\n (err.json as any)?.error?.code === \"sandbox_snapshotting\"\n );\n}\n\n/**\n * Serialized representation of a Sandbox for @workflow/serde.\n * Fields `metadata` and `routes` are the original wire format from main.\n * Fields `sandboxMetadata` and `projectId` are added for named-sandboxes.\n */\nexport interface SerializedSandbox {\n metadata: SandboxSnapshot;\n routes: SandboxRouteData[];\n sandboxMetadata?: SandboxMetaData;\n projectId?: string;\n}\n\n// ============================================================================\n// Sandbox class\n// ============================================================================\n\n/**\n * A Sandbox is a persistent, isolated Linux MicroVMs to run commands in.\n * Use {@link Sandbox.create} or {@link Sandbox.get} to construct.\n * @hideconstructor\n */\nexport class Sandbox {\n private _client: APIClient | null = null;\n private readonly projectId: string;\n\n /**\n * In-flight resume promise, used to deduplicate concurrent resume calls.\n */\n private resumePromise: Promise<void> | null = null;\n\n /**\n * Internal Session instance for the current VM.\n */\n private session: Session | undefined;\n\n /**\n * Internal metadata about the sandbox.\n */\n private sandbox: SandboxMetaData;\n\n /**\n * Hook that will be executed when a new session is created during resume.\n */\n private readonly onResume?: (sandbox: Sandbox) => Promise<void>;\n\n /**\n * A `node:fs/promises`-compatible API for interacting with the sandbox filesystem.\n *\n * @example\n * const content = await sandbox.fs.readFile('/etc/hostname', 'utf8');\n * await sandbox.fs.writeFile('/tmp/hello.txt', 'Hello, world!');\n * const files = await sandbox.fs.readdir('/tmp');\n * const stats = await sandbox.fs.stat('/tmp/hello.txt');\n */\n public readonly fs: FileSystem;\n\n /**\n * Lazily resolve credentials and construct an API client.\n * @internal\n */\n private async ensureClient(): Promise<APIClient> {\n \"use step\";\n if (this._client) return this._client;\n const credentials = await getCredentials();\n this._client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n });\n return this._client;\n }\n\n /**\n * The name of this sandbox.\n */\n public get name(): string {\n return this.sandbox.name;\n }\n\n /**\n * Routes from ports to subdomains.\n * @hidden\n */\n public get routes(): SandboxRouteData[] {\n return this.currentSession().routes;\n }\n\n /**\n * Whether the sandbox persists the state.\n */\n public get persistent(): boolean {\n return this.sandbox.persistent;\n }\n\n /**\n * The region this sandbox runs in.\n */\n public get region(): string | undefined {\n return this.sandbox.region;\n }\n\n /**\n * Number of virtual CPUs allocated.\n */\n public get vcpus(): number | undefined {\n return this.sandbox.vcpus;\n }\n\n /**\n * Memory allocated in MB.\n */\n public get memory(): number | undefined {\n return this.sandbox.memory;\n }\n\n /** Runtime identifier (e.g. \"node24\", \"python3.13\"). */\n public get runtime(): string | undefined {\n return this.sandbox.runtime;\n }\n\n /**\n * Cumulative egress bytes across all sessions.\n */\n public get totalEgressBytes(): number | undefined {\n return this.sandbox.totalEgressBytes;\n }\n\n /**\n * Cumulative ingress bytes across all sessions.\n */\n public get totalIngressBytes(): number | undefined {\n return this.sandbox.totalIngressBytes;\n }\n\n /**\n * Cumulative active CPU duration in milliseconds across all sessions.\n */\n public get totalActiveCpuDurationMs(): number | undefined {\n return this.sandbox.totalActiveCpuDurationMs;\n }\n\n /**\n * Cumulative wall-clock duration in milliseconds across all sessions.\n */\n public get totalDurationMs(): number | undefined {\n return this.sandbox.totalDurationMs;\n }\n\n /**\n * When this sandbox was last updated.\n */\n public get updatedAt(): Date {\n return new Date(this.sandbox.updatedAt);\n }\n\n /**\n * When the sandbox status was last updated.\n */\n public get statusUpdatedAt(): Date | undefined {\n return this.sandbox.statusUpdatedAt\n ? new Date(this.sandbox.statusUpdatedAt)\n : undefined;\n }\n\n /**\n * When this sandbox was created.\n */\n public get createdAt(): Date {\n return new Date(this.sandbox.createdAt);\n }\n\n /**\n * Interactive port.\n */\n public get interactivePort(): number | undefined {\n return this.currentSession().interactivePort;\n }\n\n /**\n * The status of the current session.\n */\n public get status(): SessionMetaData[\"status\"] {\n return this.currentSession().status;\n }\n\n /**\n * The default timeout of this sandbox in milliseconds.\n */\n public get timeout(): number | undefined {\n return this.sandbox.timeout;\n }\n\n /**\n * Key-value tags attached to the sandbox.\n */\n public get tags(): Record<string, string> | undefined {\n return this.sandbox.tags;\n }\n\n /**\n * The default network policy of this sandbox.\n */\n public get networkPolicy(): NetworkPolicy | undefined {\n return this.sandbox.networkPolicy\n ? fromAPINetworkPolicy(this.sandbox.networkPolicy)\n : undefined;\n }\n\n /**\n * If the session was created from a snapshot, the ID of that snapshot.\n */\n public get sourceSnapshotId(): string | undefined {\n return this.currentSession().sourceSnapshotId;\n }\n\n /**\n * The current snapshot ID of this sandbox, if any.\n */\n public get currentSnapshotId(): string | undefined {\n return this.sandbox.currentSnapshotId;\n }\n\n /**\n * The default snapshot expiration in milliseconds, if set.\n */\n public get snapshotExpiration(): number | undefined {\n return this.sandbox.snapshotExpiration;\n }\n\n /**\n * The amount of CPU used by the session. Only reported once the VM is stopped.\n */\n public get activeCpuUsageMs(): number | undefined {\n return this.currentSession().activeCpuUsageMs;\n }\n\n /**\n * The amount of network data used by the session. Only reported once the VM is stopped.\n */\n public get networkTransfer():\n | { ingress: number; egress: number }\n | undefined {\n return this.currentSession().networkTransfer;\n }\n\n /**\n * Allow to get a list of sandboxes for a team narrowed to the given params.\n * It returns both the sandboxes and the pagination metadata to allow getting\n * the next page of results.\n *\n * The returned object is async-iterable to auto-paginate through all pages:\n *\n * ```ts\n * const result = await Sandbox.list({ namePrefix: \"ci-\" });\n * for await (const sandbox of result) { ... }\n * // or: await result.toArray();\n * // or: for await (const page of result.pages()) { ... }\n * ```\n */\n static async list(\n params?: Partial<Parameters<APIClient[\"listSandboxes\"]>[0]> &\n Partial<Credentials> &\n WithFetchOptions,\n ) {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n fetch: params?.fetch,\n });\n const fetchPage = async (cursor?: string) => {\n const response = await client.listSandboxes({\n ...credentials,\n ...params,\n ...(cursor !== undefined && { cursor }),\n });\n return response.json;\n };\n const firstPage = await fetchPage(params?.cursor);\n return attachPaginator(firstPage, {\n itemsKey: \"sandboxes\",\n fetchNext: fetchPage,\n signal: params?.signal,\n });\n }\n\n /**\n * Serialize a Sandbox instance to plain data for @workflow/serde.\n *\n * @param instance - The Sandbox instance to serialize\n * @returns A plain object containing sandbox metadata and routes\n */\n static [WORKFLOW_SERIALIZE](instance: Sandbox): SerializedSandbox {\n return {\n metadata: instance.session?._sessionSnapshot!,\n routes: instance.session?.routes ?? [],\n sandboxMetadata: instance.sandbox,\n projectId: instance.projectId,\n };\n }\n\n /**\n * Deserialize a Sandbox from serialized snapshot data.\n *\n * The deserialized instance uses the serialized metadata synchronously and\n * lazily creates an API client only when methods perform API requests.\n *\n * @param data - The serialized sandbox data\n * @returns The reconstructed Sandbox instance\n */\n static [WORKFLOW_DESERIALIZE](data: SerializedSandbox): Sandbox {\n const sandbox = new Sandbox({\n sandbox: data.sandboxMetadata!,\n routes: data.routes,\n projectId: data.projectId,\n });\n if (data.metadata) {\n sandbox.session = new Session({ routes: data.routes, snapshot: data.metadata });\n }\n return sandbox;\n }\n\n /**\n * Create a new sandbox.\n *\n * @param params - Creation parameters and optional credentials.\n * @returns A promise resolving to the created {@link Sandbox}.\n * @example\n * <caption>Create a sandbox with default options</caption>\n * const sandbox = await Sandbox.create();\n *\n * @example\n * <caption>Create a sandbox and drop it in the end of the block</caption>\n * async function fn() {\n * await using const sandbox = await Sandbox.create();\n * // Sandbox automatically stopped at the end of the lexical scope\n * }\n */\n static async create(\n params?: WithPrivate<\n CreateSandboxParams | (CreateSandboxParams & Credentials)\n > &\n WithFetchOptions,\n ): Promise<Sandbox & AsyncDisposable> {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n fetch: params?.fetch,\n });\n\n const privateParams = getPrivateParams(params);\n const response = await client.createSandbox({\n source: params?.source,\n projectId: credentials.projectId,\n ports: params?.ports ?? [],\n timeout: params?.timeout,\n resources: params?.resources,\n runtime: params && \"runtime\" in params ? params?.runtime : undefined,\n networkPolicy: params?.networkPolicy,\n env: params?.env,\n tags: params?.tags,\n snapshotExpiration: params?.snapshotExpiration,\n signal: params?.signal,\n name: params?.name,\n persistent: params?.persistent,\n ...privateParams,\n });\n\n return new DisposableSandbox({\n client,\n session: response.json.session,\n sandbox: response.json.sandbox,\n routes: response.json.routes,\n projectId: credentials.projectId,\n onResume: params?.onResume,\n });\n }\n\n /**\n * Retrieve an existing sandbox and resume its session.\n *\n * @param params - Get parameters and optional credentials.\n * @returns A promise resolving to the {@link Sandbox}.\n */\n static async get(\n params: WithPrivate<GetSandboxParams | (GetSandboxParams & Credentials)> &\n WithFetchOptions,\n ): Promise<Sandbox> {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n fetch: params.fetch,\n });\n\n const privateParams = getPrivateParams(params);\n const response = await client.getSandbox({\n name: params.name,\n projectId: credentials.projectId,\n resume: params.resume,\n signal: params.signal,\n ...privateParams,\n });\n\n const sandbox = new Sandbox({\n client,\n session: response.json.session,\n sandbox: response.json.sandbox,\n routes: response.json.routes,\n projectId: credentials.projectId,\n onResume: params.onResume,\n });\n\n if (response.json.resumed && params.onResume) {\n await params.onResume(sandbox);\n }\n\n return sandbox;\n }\n\n /**\n * Retrieve an existing named sandbox, or create a new one if none exists.\n *\n * If `name` is omitted, this always creates a new sandbox and fires\n * `onCreate`. If `name` is provided, it first tries {@link Sandbox.get};\n * on `not_found` it creates a new sandbox with that name; on\n * `snapshot_not_found` it deletes the stale named sandbox and creates\n * a fresh one with the same name.\n *\n * @param params - Get/create parameters plus an optional `onCreate` hook.\n * @returns A promise resolving to the {@link Sandbox}.\n *\n * @example\n * <caption>Idempotent named sandbox with one-time setup</caption>\n * const sandbox = await Sandbox.getOrCreate({\n * name: \"my-workspace\",\n * onCreate: async (sbx) => {\n * await sbx.writeFiles([\n * { path: \"README.md\", content: Buffer.from(\"# Hello\") },\n * ]);\n * },\n * });\n *\n * @example\n * <caption>Unnamed — always creates</caption>\n * const sandbox = await Sandbox.getOrCreate({\n * onCreate: async (sbx) => {\n * await sbx.runCommand(\"npm\", [\"install\"]);\n * },\n * });\n */\n static async getOrCreate(\n params?: WithPrivate<\n GetOrCreateSandboxParams | (GetOrCreateSandboxParams & Credentials)\n > &\n WithFetchOptions,\n ): Promise<Sandbox> {\n \"use step\";\n // No name → always create, fire onCreate.\n if (!params?.name) {\n const sandbox = await Sandbox.create(params);\n if (params?.onCreate) {\n await params.onCreate(sandbox);\n }\n return sandbox;\n }\n\n try {\n return await Sandbox.get(\n params as unknown as Parameters<typeof Sandbox.get>[0],\n );\n } catch (err) {\n if (isNotFoundError(err)) {\n // Sandbox does not exist: re-create it.\n const sandbox = await Sandbox.create(params);\n if (params.onCreate) {\n await params.onCreate(sandbox);\n }\n return sandbox;\n }\n\n if (isSnapshotNotFoundError(err)) {\n // Sandbox exists but the snapshot has expired. Delete it and create\n // a new one.\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n fetch: params.fetch,\n });\n const privateParams = getPrivateParams(params);\n try {\n await client.deleteSandbox({\n name: params.name,\n projectId: credentials.projectId,\n signal: params.signal,\n ...privateParams,\n });\n } catch (deleteErr) {\n // Tolerate 404 — the named sandbox was already cleaned up by a\n // concurrent request. Propagate anything else.\n if (!isNotFoundError(deleteErr)) {\n throw deleteErr;\n }\n }\n\n const sandbox = await Sandbox.create(params);\n if (params.onCreate) {\n await params.onCreate(sandbox);\n }\n return sandbox;\n }\n\n throw err;\n }\n }\n\n /**\n * Create a new Sandbox instance.\n *\n * @param params.client - Optional API client. If not provided, will be lazily created using global credentials.\n * @param params.routes - Port-to-subdomain mappings for exposed ports\n * @param params.sandbox - Sandbox snapshot metadata\n */\n constructor({\n client,\n routes,\n session,\n sandbox,\n projectId,\n onResume,\n }: {\n client?: APIClient;\n routes: SandboxRouteData[];\n session?: SessionMetaData;\n sandbox: SandboxMetaData;\n projectId?: string;\n onResume?: (sandbox: Sandbox) => Promise<void>;\n }) {\n this._client = client ?? null;\n if (session) {\n this.session = new Session({ client: client!, routes, session });\n }\n this.sandbox = sandbox;\n this.projectId = projectId ?? \"\";\n this.onResume = onResume;\n this.fs = new FileSystem(this);\n }\n\n /**\n * Get the current session (the running VM) for this sandbox.\n *\n * @returns The {@link Session} instance.\n */\n currentSession(): Session {\n if (!this.session) {\n throw new Error(\"No active session. Run a command or call resume first.\");\n }\n return this.session;\n }\n\n /**\n * Resume this sandbox by creating a new session via `getSandbox`.\n */\n private async resume(signal?: AbortSignal): Promise<void> {\n if (!this.resumePromise) {\n this.resumePromise = this.doResume(signal).finally(() => {\n this.resumePromise = null;\n });\n }\n return this.resumePromise;\n }\n\n private async doResume(signal?: AbortSignal): Promise<void> {\n const client = await this.ensureClient();\n const response = await client.getSandbox({\n name: this.sandbox.name,\n projectId: this.projectId,\n resume: true,\n signal,\n });\n this.session = new Session({\n client,\n routes: response.json.routes,\n session: response.json.session,\n });\n if (this.onResume && response.json.resumed) {\n await this.onResume(this);\n }\n }\n\n /**\n * Poll until the current session reaches a terminal state, then resume.\n */\n private async waitForStopAndResume(signal?: AbortSignal): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n const pollingInterval = 500;\n let status = this.session!.status;\n\n while (status === \"stopping\" || status === \"snapshotting\") {\n await setTimeout(pollingInterval, undefined, { signal });\n const poll = await client.getSession({\n sessionId: this.session!.sessionId,\n signal,\n });\n this.session = new Session({\n client,\n routes: poll.json.routes,\n session: poll.json.session,\n });\n status = poll.json.session.status;\n }\n await this.resume(signal);\n }\n\n /**\n * Execute `fn`, and if the session is stopped/stopping/snapshotting, resume and retry.\n */\n private async withResume<T>(\n fn: () => Promise<T>,\n signal?: AbortSignal,\n ): Promise<T> {\n if (!this.session) {\n await this.resume(signal);\n }\n try {\n return await fn();\n } catch (err) {\n if (isSandboxStoppedError(err)) {\n await this.resume(signal);\n return fn();\n }\n if (isSandboxStoppingError(err) || isSandboxSnapshottingError(err)) {\n await this.waitForStopAndResume(signal);\n return fn();\n }\n throw err;\n }\n }\n\n /**\n * Start executing a command in this sandbox.\n *\n * @param command - The command to execute.\n * @param args - Arguments to pass to the command.\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the command execution.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n async runCommand(\n command: string,\n args?: string[],\n opts?: { signal?: AbortSignal },\n ): Promise<CommandFinished>;\n /**\n * Start executing a command in detached mode.\n *\n * @param params - The command parameters.\n * @returns A {@link Command} instance for the running command.\n */\n async runCommand(\n params: RunCommandParams & { detached: true },\n ): Promise<Command>;\n\n /**\n * Start executing a command in this sandbox.\n *\n * @param params - The command parameters.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n\n async runCommand(params: RunCommandParams): Promise<CommandFinished>;\n\n async runCommand(\n commandOrParams: string | RunCommandParams,\n args?: string[],\n opts?: { signal?: AbortSignal },\n ): Promise<Command | CommandFinished> {\n \"use step\";\n const signal =\n typeof commandOrParams === \"string\"\n ? opts?.signal\n : commandOrParams.signal;\n return this.withResume(\n () => this.session!.runCommand(commandOrParams as any, args, opts),\n signal,\n );\n }\n\n /**\n * Internal helper to start a command in the sandbox.\n *\n * @param params - Command execution parameters.\n * @returns A {@link Command} or {@link CommandFinished}, depending on `detached`.\n * @internal\n */\n async getCommand(\n cmdId: string,\n opts?: { signal?: AbortSignal },\n ): Promise<Command> {\n \"use step\";\n return this.withResume(\n () => this.session!.getCommand(cmdId, opts),\n opts?.signal,\n );\n }\n\n /**\n * Create a directory in the filesystem of this sandbox.\n *\n * @param path - Path of the directory to create\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n */\n async mkDir(path: string, opts?: { signal?: AbortSignal }): Promise<void> {\n \"use step\";\n return this.withResume(\n () => this.session!.mkDir(path, opts),\n opts?.signal,\n );\n }\n\n /**\n * Read a file from the filesystem of this sandbox as a stream.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to a ReadableStream containing the file contents, or null if file not found\n */\n async readFile(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<NodeJS.ReadableStream | null> {\n \"use step\";\n return this.withResume(\n () => this.session!.readFile(file, opts),\n opts?.signal,\n );\n }\n\n /**\n * Read a file from the filesystem of this sandbox as a Buffer.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to the file contents as a Buffer, or null if file not found\n */\n async readFileToBuffer(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<Buffer | null> {\n \"use step\";\n return this.withResume(\n () => this.session!.readFileToBuffer(file, opts),\n opts?.signal,\n );\n }\n\n /**\n * Download a file from the sandbox to the local filesystem.\n *\n * @param src - Source file on the sandbox, with path and optional cwd\n * @param dst - Destination file on the local machine, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.mkdirRecursive - If true, create parent directories for the destination if they don't exist.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns The absolute path to the written file, or null if the source file was not found\n */\n async downloadFile(\n src: { path: string; cwd?: string },\n dst: { path: string; cwd?: string },\n opts?: { mkdirRecursive?: boolean; signal?: AbortSignal },\n ): Promise<string | null> {\n \"use step\";\n return this.withResume(\n () => this.session!.downloadFile(src, dst, opts),\n opts?.signal,\n );\n }\n\n /**\n * Write files to the filesystem of this sandbox.\n * Defaults to writing to /vercel/sandbox unless an absolute path is specified.\n * Writes files using the `vercel-sandbox` user.\n *\n * @param files - Array of files with path, content, and optional mode (permissions)\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the files are written\n *\n * @example\n * // Write an executable script\n * await sandbox.writeFiles([\n * { path: \"/usr/local/bin/myscript\", content: \"#!/bin/bash\\necho hello\", mode: 0o755 }\n * ]);\n */\n async writeFiles(\n files: {\n path: string;\n content: string | Uint8Array;\n mode?: number;\n }[],\n opts?: { signal?: AbortSignal },\n ) {\n \"use step\";\n return this.withResume(\n () => this.session!.writeFiles(files, opts),\n opts?.signal,\n );\n }\n\n /**\n * Get the public domain of a port of this sandbox.\n *\n * @param p - Port number to resolve\n * @returns A full domain (e.g. `https://subdomain.vercel.run`)\n * @throws If the port has no associated route\n */\n domain(p: number): string {\n return this.currentSession().domain(p);\n }\n\n /**\n * Stop the sandbox.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns The final session state after stopping, with optional snapshot metadata.\n */\n async stop(opts?: {\n signal?: AbortSignal;\n }): Promise<SandboxSnapshot & { snapshot?: SnapshotMetadata }> {\n \"use step\";\n if (!this.session) {\n throw new Error(\"No active session to stop.\");\n }\n const { session, sandbox, snapshot } = await this.session.stop(opts);\n if (sandbox) {\n this.sandbox = sandbox;\n }\n return Object.assign(session, { snapshot });\n }\n\n /**\n * Update the network policy for this sandbox.\n *\n * @deprecated Use {@link Sandbox.update} instead.\n *\n * @param networkPolicy - The new network policy to apply.\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the network policy is updated.\n *\n * @example\n * // Restrict to specific domains\n * await sandbox.updateNetworkPolicy({\n * allow: [\"*.npmjs.org\", \"github.com\"],\n * });\n *\n * @example\n * // Inject credentials with per-domain transformers\n * await sandbox.updateNetworkPolicy({\n * allow: {\n * \"ai-gateway.vercel.sh\": [{\n * transform: [{\n * headers: { authorization: \"Bearer ...\" }\n * }]\n * }],\n * \"*\": []\n * }\n * });\n *\n * @example\n * // Deny all network access\n * await sandbox.updateNetworkPolicy(\"deny-all\");\n */\n async updateNetworkPolicy(\n networkPolicy: NetworkPolicy,\n opts?: { signal?: AbortSignal },\n ): Promise<NetworkPolicy> {\n \"use step\";\n await this.withResume(\n () => this.session!.update({ networkPolicy: networkPolicy }, opts),\n opts?.signal,\n );\n\n return this.session!.networkPolicy!;\n }\n\n /**\n * Extend the timeout of the sandbox by the specified duration.\n *\n * This allows you to extend the lifetime of a sandbox up until the maximum\n * execution timeout for your plan.\n *\n * @param duration - The duration in milliseconds to extend the timeout by\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the timeout is extended\n *\n * @example\n * const sandbox = await Sandbox.create({ timeout: ms('10m') });\n * // Extends timeout by 5 minutes, to a total of 15 minutes.\n * await sandbox.extendTimeout(ms('5m'));\n */\n async extendTimeout(\n duration: number,\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n return this.withResume(\n () => this.session!.extendTimeout(duration, opts),\n opts?.signal,\n );\n }\n\n /**\n * Create a snapshot from this currently running sandbox. New sandboxes can\n * then be created from this snapshot using {@link Sandbox.createFromSnapshot}.\n *\n * Note: this sandbox will be stopped as part of the snapshot creation process.\n *\n * @param opts - Optional parameters.\n * @param opts.expiration - Optional expiration time in milliseconds. Use 0 for no expiration at all.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to the Snapshot instance\n */\n async snapshot(opts?: {\n expiration?: number;\n signal?: AbortSignal;\n }): Promise<Snapshot> {\n \"use step\";\n return this.withResume(\n () => this.session!.snapshot(opts),\n opts?.signal,\n );\n }\n\n /**\n * Update the sandbox configuration.\n *\n * @param params - Fields to update.\n * @param opts - Optional abort signal.\n */\n async update(\n params: {\n persistent?: boolean;\n resources?: { vcpus?: number };\n timeout?: number;\n networkPolicy?: NetworkPolicy;\n tags?: Record<string, string>;\n snapshotExpiration?: number;\n currentSnapshotId?: string;\n },\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n let resources: { vcpus: number; memory: number } | undefined;\n if (params.resources?.vcpus) {\n resources = {\n vcpus: params.resources.vcpus,\n memory: params.resources.vcpus * 2048,\n };\n }\n\n // Update the sandbox config. This config will be used on the next session.\n const response = await client.updateSandbox({\n name: this.sandbox.name,\n projectId: this.projectId,\n persistent: params.persistent,\n resources,\n timeout: params.timeout,\n networkPolicy: params.networkPolicy,\n tags: params.tags,\n snapshotExpiration: params.snapshotExpiration,\n currentSnapshotId: params.currentSnapshotId,\n signal: opts?.signal,\n });\n this.sandbox = response.json.sandbox;\n\n // Update the current session config. This only applies to network policy.\n if (params.networkPolicy) {\n try {\n return await this.session?.update(\n { networkPolicy: params.networkPolicy },\n opts,\n );\n } catch (err) {\n if (isSandboxStoppedError(err) || isSandboxStoppingError(err)) {\n return;\n }\n throw err;\n }\n }\n }\n\n /**\n * Delete this sandbox.\n *\n * After deletion the instance becomes inert — all further API calls will\n * throw immediately.\n */\n async delete(opts?: { signal?: AbortSignal }): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n await client.deleteSandbox({\n name: this.sandbox.name,\n projectId: this.projectId,\n signal: opts?.signal,\n });\n }\n\n /**\n * List sessions (VMs) that have been created for this sandbox.\n *\n * @param params - Optional pagination parameters.\n * @returns The list of sessions and pagination metadata.\n */\n async listSessions(params?: {\n limit?: number;\n cursor?: string;\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n \"use step\";\n const client = await this.ensureClient();\n const fetchPage = async (cursor?: string) => {\n const response = await client.listSessions({\n projectId: this.projectId,\n name: this.sandbox.name,\n limit: params?.limit,\n cursor,\n sortOrder: params?.sortOrder,\n signal: params?.signal,\n });\n return response.json;\n };\n const firstPage = await fetchPage(params?.cursor);\n return attachPaginator(firstPage, {\n itemsKey: \"sessions\",\n fetchNext: fetchPage,\n signal: params?.signal,\n });\n }\n\n /**\n * List snapshots that belong to this sandbox.\n *\n * @param params - Optional pagination parameters.\n * @returns The list of snapshots and pagination metadata.\n */\n async listSnapshots(params?: {\n limit?: number;\n cursor?: string;\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n \"use step\";\n const client = await this.ensureClient();\n const fetchPage = async (cursor?: string) => {\n const response = await client.listSnapshots({\n projectId: this.projectId,\n name: this.sandbox.name,\n limit: params?.limit,\n cursor,\n sortOrder: params?.sortOrder,\n signal: params?.signal,\n });\n return response.json;\n };\n const firstPage = await fetchPage(params?.cursor);\n return attachPaginator(firstPage, {\n itemsKey: \"snapshots\",\n fetchNext: fetchPage,\n signal: params?.signal,\n });\n }\n}\n\n/**\n * A {@link Sandbox} that can automatically be disposed using a `await using` statement.\n *\n * @example\n * {\n * await using const sandbox = await Sandbox.create();\n * }\n * // Sandbox is automatically stopped here\n */\nclass DisposableSandbox extends Sandbox implements AsyncDisposable {\n async [Symbol.asyncDispose]() {\n await this.stop();\n }\n}\n"],"mappings":"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"}

@@ -411,13 +411,16 @@ const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');

* @param opts.signal - An AbortSignal to cancel the operation.
* @param opts.blocking - If true, poll until the session has fully stopped and return the final state.
* @returns The session at the time the stop was acknowledged, or after fully stopped if `blocking` is true.
* @returns The final session state and optional sandbox metadata.
*/
async stop(opts) {
"use step";
this.session = require_sandbox_snapshot.toSandboxSnapshot((await (await this.ensureClient()).stopSession({
const response = await (await this.ensureClient()).stopSession({
sessionId: this.session.id,
signal: opts?.signal,
blocking: opts?.blocking
})).json.session);
return this.session;
signal: opts?.signal
});
this.session = require_sandbox_snapshot.toSandboxSnapshot(response.json.session);
return {
session: this.session,
sandbox: response.json.sandbox,
snapshot: response.json.snapshot
};
}

@@ -424,0 +427,0 @@ /**

@@ -1,1 +0,1 @@

{"version":3,"file":"session.cjs","names":["getCredentials","APIClient","WORKFLOW_SERIALIZE","WORKFLOW_DESERIALIZE","toSandboxSnapshot","Command","params: RunCommandParams","command","CommandFinished","path","consumeReadable","Snapshot"],"sources":["../src/session.ts"],"sourcesContent":["import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport { type SessionMetaData, type SandboxRouteData, APIClient } from \"./api-client/index.js\";\nimport type { Writable } from \"stream\";\nimport { pipeline } from \"stream/promises\";\nimport { createWriteStream } from \"fs\";\nimport { mkdir } from \"fs/promises\";\nimport { dirname, resolve } from \"path\";\nimport { Command, CommandFinished } from \"./command.js\";\nimport { Snapshot } from \"./snapshot.js\";\nimport { consumeReadable } from \"./utils/consume-readable.js\";\nimport type {\n NetworkPolicy,\n NetworkPolicyRule,\n NetworkTransformer,\n} from \"./network-policy.js\";\nimport { toSandboxSnapshot, type SandboxSnapshot } from \"./utils/sandbox-snapshot.js\";\nimport { getCredentials } from \"./utils/get-credentials.js\";\n\nexport type { NetworkPolicy, NetworkPolicyRule, NetworkTransformer };\n\n/**\n * Serialized representation of a Session for @workflow/serde.\n */\nexport interface SerializedSession {\n session: SandboxSnapshot;\n routes: SandboxRouteData[];\n}\n\n/** @inline */\nexport interface RunCommandParams {\n /**\n * The command to execute\n */\n cmd: string;\n /**\n * Arguments to pass to the command\n */\n args?: string[];\n /**\n * Working directory to execute the command in\n */\n cwd?: string;\n /**\n * Environment variables to set for this command\n */\n env?: Record<string, string>;\n /**\n * If true, execute this command with root privileges. Defaults to false.\n */\n sudo?: boolean;\n /**\n * If true, the command will return without waiting for `exitCode`\n */\n detached?: boolean;\n /**\n * A `Writable` stream where `stdout` from the command will be piped\n */\n stdout?: Writable;\n /**\n * A `Writable` stream where `stderr` from the command will be piped\n */\n stderr?: Writable;\n /**\n * An AbortSignal to cancel the command execution\n */\n signal?: AbortSignal;\n}\n\n/**\n * A Session represents a running VM instance within a {@link Sandbox}.\n *\n * Obtain a session via {@link Sandbox.currentSession}.\n */\nexport class Session {\n private _client: APIClient | null = null;\n\n /**\n * Lazily resolve credentials and construct an API client.\n * This is used in step contexts where the Sandbox was deserialized\n * without a client (e.g. when crossing workflow/step boundaries).\n * Uses getCredentials() which resolves from OIDC or env vars.\n * @internal\n */\n private async ensureClient(): Promise<APIClient> {\n \"use step\";\n if (this._client) return this._client;\n const credentials = await getCredentials();\n this._client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n });\n return this._client;\n }\n\n /**\n * Routes from ports to subdomains.\n * @hidden\n */\n public readonly routes: SandboxRouteData[];\n\n /**\n * Internal metadata about the current session.\n */\n private session: SandboxSnapshot;\n\n private get client(): APIClient {\n if (!this._client) throw new Error(\"API client not initialized\");\n return this._client;\n }\n\n /** @internal */\n get _sessionSnapshot(): SandboxSnapshot {\n return this.session;\n }\n\n /**\n * Unique ID of this session.\n */\n public get sessionId(): string {\n return this.session.id;\n }\n\n public get interactivePort(): number | undefined {\n return this.session.interactivePort ?? undefined;\n }\n\n /**\n * The status of this session.\n */\n public get status(): SessionMetaData[\"status\"] {\n return this.session.status;\n }\n\n /**\n * The creation date of this session.\n */\n public get createdAt(): Date {\n return new Date(this.session.createdAt);\n }\n\n /**\n * The timeout of this session in milliseconds.\n */\n public get timeout(): number {\n return this.session.timeout;\n }\n\n /**\n * The network policy of this session.\n */\n public get networkPolicy(): NetworkPolicy | undefined {\n return this.session.networkPolicy;\n }\n\n /**\n * If the session was created from a snapshot, the ID of that snapshot.\n */\n public get sourceSnapshotId(): string | undefined {\n return this.session.sourceSnapshotId;\n }\n\n /**\n * Memory allocated to this session in MB.\n */\n public get memory(): number {\n return this.session.memory;\n }\n\n /**\n * Number of vCPUs allocated to this session.\n */\n public get vcpus(): number {\n return this.session.vcpus;\n }\n\n /**\n * The region where this session is hosted.\n */\n public get region(): string {\n return this.session.region;\n }\n\n /**\n * Runtime identifier (e.g. \"node24\", \"python3.13\").\n */\n public get runtime(): string {\n return this.session.runtime;\n }\n\n /**\n * The working directory of this session.\n */\n public get cwd(): string {\n return this.session.cwd;\n }\n\n /**\n * When this session was requested.\n */\n public get requestedAt(): Date {\n return new Date(this.session.requestedAt);\n }\n\n /**\n * When this session started running.\n */\n public get startedAt(): Date | undefined {\n return this.session.startedAt != null\n ? new Date(this.session.startedAt)\n : undefined;\n }\n\n /**\n * When this session was requested to stop.\n */\n public get requestedStopAt(): Date | undefined {\n return this.session.requestedStopAt != null\n ? new Date(this.session.requestedStopAt)\n : undefined;\n }\n\n /**\n * When this session was stopped.\n */\n public get stoppedAt(): Date | undefined {\n return this.session.stoppedAt != null\n ? new Date(this.session.stoppedAt)\n : undefined;\n }\n\n /**\n * When this session was aborted.\n */\n public get abortedAt(): Date | undefined {\n return this.session.abortedAt != null\n ? new Date(this.session.abortedAt)\n : undefined;\n }\n\n /**\n * The wall-clock duration of this session in milliseconds.\n */\n public get duration(): number | undefined {\n return this.session.duration;\n }\n\n /**\n * When a snapshot was requested for this session.\n */\n public get snapshottedAt(): Date | undefined {\n return this.session.snapshottedAt != null\n ? new Date(this.session.snapshottedAt)\n : undefined;\n }\n\n /**\n * When this session was last updated.\n */\n public get updatedAt(): Date {\n return new Date(this.session.updatedAt);\n }\n\n /**\n * The amount of active CPU used by the session. Only reported once the VM is\n * stopped.\n */\n public get activeCpuUsageMs(): number | undefined {\n return this.session.activeCpuDurationMs;\n }\n\n /**\n * The amount of network data used by the session. Only reported once the VM\n * is stopped.\n */\n public get networkTransfer():\n | { ingress: number; egress: number }\n | undefined {\n return this.session.networkTransfer;\n }\n\n /**\n * Serialize a Session instance to plain data for @workflow/serde.\n *\n * Although Sandbox handles top-level serialization, Session needs these\n * methods so the Workflow SWC compiler can resolve the class by name.\n * The `new Session(...)` self-reference in WORKFLOW_DESERIALIZE forces\n * rolldown to preserve the class name in the compiled output.\n */\n static [WORKFLOW_SERIALIZE](instance: Session): SerializedSession {\n return {\n session: instance.session,\n routes: instance.routes,\n };\n }\n\n static [WORKFLOW_DESERIALIZE](data: SerializedSession): Session {\n return new Session({ routes: data.routes, snapshot: data.session });\n }\n\n constructor(params: {\n client: APIClient;\n routes: SandboxRouteData[];\n session: SessionMetaData;\n } | {\n /** @internal – used during deserialization with an already-converted snapshot */\n routes: SandboxRouteData[];\n snapshot: SandboxSnapshot;\n }) {\n this.routes = params.routes;\n if (\"snapshot\" in params) {\n this.session = params.snapshot;\n } else {\n this._client = params.client;\n this.session = toSandboxSnapshot(params.session);\n }\n }\n\n /**\n * Get a previously run command by its ID.\n *\n * @param cmdId - ID of the command to retrieve\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A {@link Command} instance representing the command\n */\n async getCommand(\n cmdId: string,\n opts?: { signal?: AbortSignal },\n ): Promise<Command> {\n \"use step\";\n const client = await this.ensureClient();\n const command = await client.getCommand({\n sessionId: this.session.id,\n cmdId,\n signal: opts?.signal,\n });\n\n return new Command({\n client,\n sessionId: this.session.id,\n cmd: command.json.command,\n });\n }\n\n /**\n * Start executing a command in this session.\n *\n * @param command - The command to execute.\n * @param args - Arguments to pass to the command.\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the command execution.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n async runCommand(\n command: string,\n args?: string[],\n opts?: { signal?: AbortSignal },\n ): Promise<CommandFinished>;\n\n /**\n * Start executing a command in detached mode.\n *\n * @param params - The command parameters.\n * @returns A {@link Command} instance for the running command.\n */\n async runCommand(\n params: RunCommandParams & { detached: true },\n ): Promise<Command>;\n\n /**\n * Start executing a command in this session.\n *\n * @param params - The command parameters.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n async runCommand(params: RunCommandParams): Promise<CommandFinished>;\n\n async runCommand(\n commandOrParams: string | RunCommandParams,\n args?: string[],\n opts?: { signal?: AbortSignal },\n ): Promise<Command | CommandFinished> {\n \"use step\";\n const client = await this.ensureClient();\n const params: RunCommandParams =\n typeof commandOrParams === \"string\"\n ? { cmd: commandOrParams, args, signal: opts?.signal }\n : commandOrParams;\n const wait = params.detached ? false : true;\n const pipeLogs = async (command: Command): Promise<void> => {\n if (!params.stdout && !params.stderr) {\n return;\n }\n\n try {\n for await (const log of command.logs({ signal: params.signal })) {\n if (log.stream === \"stdout\") {\n params.stdout?.write(log.data);\n } else if (log.stream === \"stderr\") {\n params.stderr?.write(log.data);\n }\n }\n } catch (err) {\n if (params.signal?.aborted) {\n return;\n }\n throw err;\n }\n };\n\n if (wait) {\n const commandStream = await client.runCommand({\n sessionId: this.session.id,\n command: params.cmd,\n args: params.args ?? [],\n cwd: params.cwd,\n env: params.env ?? {},\n sudo: params.sudo ?? false,\n wait: true,\n signal: params.signal,\n });\n\n const command = new Command({\n client,\n sessionId: this.session.id,\n cmd: commandStream.command,\n });\n\n const [finished] = await Promise.all([\n commandStream.finished,\n pipeLogs(command),\n ]);\n return new CommandFinished({\n client,\n sessionId: this.session.id,\n cmd: finished,\n exitCode: finished.exitCode ?? 0,\n });\n }\n\n const commandResponse = await client.runCommand({\n sessionId: this.session.id,\n command: params.cmd,\n args: params.args ?? [],\n cwd: params.cwd,\n env: params.env ?? {},\n sudo: params.sudo ?? false,\n signal: params.signal,\n });\n\n const command = new Command({\n client,\n sessionId: this.session.id,\n cmd: commandResponse.json.command,\n });\n\n void pipeLogs(command).catch((err) => {\n if (params.signal?.aborted) {\n return;\n }\n (params.stderr ?? params.stdout)?.emit(\"error\", err);\n });\n\n return command;\n }\n\n /**\n * Create a directory in the filesystem of this session.\n *\n * @param path - Path of the directory to create\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n */\n async mkDir(path: string, opts?: { signal?: AbortSignal }): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n await client.mkDir({\n sessionId: this.session.id,\n path: path,\n signal: opts?.signal,\n });\n }\n\n /**\n * Read a file from the filesystem of this session as a stream.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to a ReadableStream containing the file contents, or null if file not found\n */\n async readFile(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<NodeJS.ReadableStream | null> {\n \"use step\";\n const client = await this.ensureClient();\n return client.readFile({\n sessionId: this.session.id,\n path: file.path,\n cwd: file.cwd,\n signal: opts?.signal,\n });\n }\n\n /**\n * Read a file from the filesystem of this session as a Buffer.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to the file contents as a Buffer, or null if file not found\n */\n async readFileToBuffer(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<Buffer | null> {\n \"use step\";\n const client = await this.ensureClient();\n const stream = await client.readFile({\n sessionId: this.session.id,\n path: file.path,\n cwd: file.cwd,\n signal: opts?.signal,\n });\n\n if (stream === null) {\n return null;\n }\n\n return consumeReadable(stream);\n }\n\n /**\n * Download a file from the session to the local filesystem.\n *\n * @param src - Source file on the session, with path and optional cwd\n * @param dst - Destination file on the local machine, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.mkdirRecursive - If true, create parent directories for the destination if they don't exist.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns The absolute path to the written file, or null if the source file was not found\n */\n async downloadFile(\n src: { path: string; cwd?: string },\n dst: { path: string; cwd?: string },\n opts?: { mkdirRecursive?: boolean; signal?: AbortSignal },\n ): Promise<string | null> {\n \"use step\";\n const client = await this.ensureClient();\n if (!src?.path) {\n throw new Error(\"downloadFile: source path is required\");\n }\n\n if (!dst?.path) {\n throw new Error(\"downloadFile: destination path is required\");\n }\n\n const stream = await client.readFile({\n sessionId: this.session.id,\n path: src.path,\n cwd: src.cwd,\n signal: opts?.signal,\n });\n\n if (stream === null) {\n return null;\n }\n\n try {\n const dstPath = resolve(dst.cwd ?? \"\", dst.path);\n if (opts?.mkdirRecursive) {\n await mkdir(dirname(dstPath), { recursive: true });\n }\n await pipeline(stream, createWriteStream(dstPath), {\n signal: opts?.signal,\n });\n return dstPath;\n } finally {\n stream.destroy();\n }\n }\n\n /**\n * Write files to the filesystem of this session.\n * Defaults to writing to /vercel/sandbox unless an absolute path is specified.\n * Writes files using the `vercel-sandbox` user.\n *\n * @param files - Array of files with path and stream/buffer contents\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the files are written\n */\n async writeFiles(\n files: { path: string; content: string | Uint8Array; mode?: number }[],\n opts?: { signal?: AbortSignal },\n ) {\n \"use step\";\n const client = await this.ensureClient();\n return client.writeFiles({\n sessionId: this.session.id,\n cwd: this.session.cwd,\n extractDir: \"/\",\n files: files,\n signal: opts?.signal,\n });\n }\n\n /**\n * Get the public domain of a port of this session.\n *\n * @param p - Port number to resolve\n * @returns A full domain (e.g. `https://subdomain.vercel.run`)\n * @throws If the port has no associated route\n */\n domain(p: number): string {\n const route = this.routes.find(({ port }) => port == p);\n if (route) {\n return `https://${route.subdomain}.vercel.run`;\n } else {\n throw new Error(`No route for port ${p}`);\n }\n }\n\n /**\n * Stop this session.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @param opts.blocking - If true, poll until the session has fully stopped and return the final state.\n * @returns The session at the time the stop was acknowledged, or after fully stopped if `blocking` is true.\n */\n async stop(opts?: {\n signal?: AbortSignal;\n blocking?: boolean;\n }): Promise<SandboxSnapshot> {\n \"use step\";\n const client = await this.ensureClient();\n const response = await client.stopSession({\n sessionId: this.session.id,\n signal: opts?.signal,\n blocking: opts?.blocking,\n });\n this.session = toSandboxSnapshot(response.json.session);\n return this.session;\n }\n\n /**\n * Update the current session's settings.\n *\n * @param params - Fields to update.\n * @param params.networkPolicy - The new network policy to apply.\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n *\n * @example\n * // Restrict to specific domains\n * await session.update({\n * networkPolicy: {\n * allow: [\"*.npmjs.org\", \"github.com\"],\n * }\n * });\n *\n * @example\n * // Inject credentials with per-domain transformers\n * await session.update({\n * networkPolicy: {\n * allow: {\n * \"ai-gateway.vercel.sh\": [{\n * transform: [{\n * headers: { authorization: \"Bearer ...\" }\n * }]\n * }],\n * \"*\": []\n * }\n * }\n * });\n *\n * @example\n * // Deny all network access\n * await session.update({ networkPolicy: \"deny-all\" });\n */\n async update(\n params: {\n networkPolicy?: NetworkPolicy;\n },\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n if (params.networkPolicy !== undefined) {\n const client = await this.ensureClient();\n const response = await client.updateNetworkPolicy({\n sessionId: this.session.id,\n networkPolicy: params.networkPolicy,\n signal: opts?.signal,\n });\n\n // Update the internal session with the new network policy\n this.session = toSandboxSnapshot(response.json.session);\n }\n }\n\n /**\n * Extend the timeout of the session by the specified duration.\n *\n * This allows you to extend the lifetime of a session up until the maximum\n * execution timeout for your plan.\n *\n * @param duration - The duration in milliseconds to extend the timeout by\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the timeout is extended\n *\n * @example\n * const sandbox = await Sandbox.create({ timeout: ms('10m') });\n * const session = sandbox.currentSession();\n * // Extends timeout by 5 minutes, to a total of 15 minutes.\n * await session.extendTimeout(ms('5m'));\n */\n async extendTimeout(\n duration: number,\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n const response = await client.extendTimeout({\n sessionId: this.session.id,\n duration,\n signal: opts?.signal,\n });\n\n // Update the internal sandbox metadata with the new timeout value\n this.session = toSandboxSnapshot(response.json.session);\n }\n\n /**\n * Create a snapshot from this currently running session. New sandboxes can\n * then be created from this snapshot using {@link Sandbox.create}.\n *\n * Note: this session will be stopped as part of the snapshot creation process.\n *\n * @param opts - Optional parameters.\n * @param opts.expiration - Optional expiration time in milliseconds. Use 0 for no expiration at all.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to the Snapshot instance\n */\n async snapshot(opts?: {\n expiration?: number;\n signal?: AbortSignal;\n }): Promise<Snapshot> {\n \"use step\";\n const client = await this.ensureClient();\n const response = await client.createSnapshot({\n sessionId: this.session.id,\n expiration: opts?.expiration,\n signal: opts?.signal,\n });\n\n this.session = toSandboxSnapshot(response.json.session);\n\n return new Snapshot({\n client,\n snapshot: response.json.snapshot,\n });\n }\n}\n"],"mappings":"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"}
{"version":3,"file":"session.cjs","names":["getCredentials","APIClient","WORKFLOW_SERIALIZE","WORKFLOW_DESERIALIZE","toSandboxSnapshot","Command","params: RunCommandParams","command","CommandFinished","path","consumeReadable","Snapshot"],"sources":["../src/session.ts"],"sourcesContent":["import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport { type SessionMetaData, type SandboxRouteData, type SandboxMetaData, type SnapshotMetadata, APIClient } from \"./api-client/index.js\";\nimport type { Writable } from \"stream\";\nimport { pipeline } from \"stream/promises\";\nimport { createWriteStream } from \"fs\";\nimport { mkdir } from \"fs/promises\";\nimport { dirname, resolve } from \"path\";\nimport { Command, CommandFinished } from \"./command.js\";\nimport { Snapshot } from \"./snapshot.js\";\nimport { consumeReadable } from \"./utils/consume-readable.js\";\nimport type {\n NetworkPolicy,\n NetworkPolicyRule,\n NetworkTransformer,\n} from \"./network-policy.js\";\nimport { toSandboxSnapshot, type SandboxSnapshot } from \"./utils/sandbox-snapshot.js\";\nimport { getCredentials } from \"./utils/get-credentials.js\";\n\nexport type { NetworkPolicy, NetworkPolicyRule, NetworkTransformer };\n\n/**\n * Serialized representation of a Session for @workflow/serde.\n */\nexport interface SerializedSession {\n session: SandboxSnapshot;\n routes: SandboxRouteData[];\n}\n\n/** @inline */\nexport interface RunCommandParams {\n /**\n * The command to execute\n */\n cmd: string;\n /**\n * Arguments to pass to the command\n */\n args?: string[];\n /**\n * Working directory to execute the command in\n */\n cwd?: string;\n /**\n * Environment variables to set for this command\n */\n env?: Record<string, string>;\n /**\n * If true, execute this command with root privileges. Defaults to false.\n */\n sudo?: boolean;\n /**\n * If true, the command will return without waiting for `exitCode`\n */\n detached?: boolean;\n /**\n * A `Writable` stream where `stdout` from the command will be piped\n */\n stdout?: Writable;\n /**\n * A `Writable` stream where `stderr` from the command will be piped\n */\n stderr?: Writable;\n /**\n * An AbortSignal to cancel the command execution\n */\n signal?: AbortSignal;\n}\n\n/**\n * A Session represents a running VM instance within a {@link Sandbox}.\n *\n * Obtain a session via {@link Sandbox.currentSession}.\n */\nexport class Session {\n private _client: APIClient | null = null;\n\n /**\n * Lazily resolve credentials and construct an API client.\n * This is used in step contexts where the Sandbox was deserialized\n * without a client (e.g. when crossing workflow/step boundaries).\n * Uses getCredentials() which resolves from OIDC or env vars.\n * @internal\n */\n private async ensureClient(): Promise<APIClient> {\n \"use step\";\n if (this._client) return this._client;\n const credentials = await getCredentials();\n this._client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n });\n return this._client;\n }\n\n /**\n * Routes from ports to subdomains.\n * @hidden\n */\n public readonly routes: SandboxRouteData[];\n\n /**\n * Internal metadata about the current session.\n */\n private session: SandboxSnapshot;\n\n private get client(): APIClient {\n if (!this._client) throw new Error(\"API client not initialized\");\n return this._client;\n }\n\n /** @internal */\n get _sessionSnapshot(): SandboxSnapshot {\n return this.session;\n }\n\n /**\n * Unique ID of this session.\n */\n public get sessionId(): string {\n return this.session.id;\n }\n\n public get interactivePort(): number | undefined {\n return this.session.interactivePort ?? undefined;\n }\n\n /**\n * The status of this session.\n */\n public get status(): SessionMetaData[\"status\"] {\n return this.session.status;\n }\n\n /**\n * The creation date of this session.\n */\n public get createdAt(): Date {\n return new Date(this.session.createdAt);\n }\n\n /**\n * The timeout of this session in milliseconds.\n */\n public get timeout(): number {\n return this.session.timeout;\n }\n\n /**\n * The network policy of this session.\n */\n public get networkPolicy(): NetworkPolicy | undefined {\n return this.session.networkPolicy;\n }\n\n /**\n * If the session was created from a snapshot, the ID of that snapshot.\n */\n public get sourceSnapshotId(): string | undefined {\n return this.session.sourceSnapshotId;\n }\n\n /**\n * Memory allocated to this session in MB.\n */\n public get memory(): number {\n return this.session.memory;\n }\n\n /**\n * Number of vCPUs allocated to this session.\n */\n public get vcpus(): number {\n return this.session.vcpus;\n }\n\n /**\n * The region where this session is hosted.\n */\n public get region(): string {\n return this.session.region;\n }\n\n /**\n * Runtime identifier (e.g. \"node24\", \"python3.13\").\n */\n public get runtime(): string {\n return this.session.runtime;\n }\n\n /**\n * The working directory of this session.\n */\n public get cwd(): string {\n return this.session.cwd;\n }\n\n /**\n * When this session was requested.\n */\n public get requestedAt(): Date {\n return new Date(this.session.requestedAt);\n }\n\n /**\n * When this session started running.\n */\n public get startedAt(): Date | undefined {\n return this.session.startedAt != null\n ? new Date(this.session.startedAt)\n : undefined;\n }\n\n /**\n * When this session was requested to stop.\n */\n public get requestedStopAt(): Date | undefined {\n return this.session.requestedStopAt != null\n ? new Date(this.session.requestedStopAt)\n : undefined;\n }\n\n /**\n * When this session was stopped.\n */\n public get stoppedAt(): Date | undefined {\n return this.session.stoppedAt != null\n ? new Date(this.session.stoppedAt)\n : undefined;\n }\n\n /**\n * When this session was aborted.\n */\n public get abortedAt(): Date | undefined {\n return this.session.abortedAt != null\n ? new Date(this.session.abortedAt)\n : undefined;\n }\n\n /**\n * The wall-clock duration of this session in milliseconds.\n */\n public get duration(): number | undefined {\n return this.session.duration;\n }\n\n /**\n * When a snapshot was requested for this session.\n */\n public get snapshottedAt(): Date | undefined {\n return this.session.snapshottedAt != null\n ? new Date(this.session.snapshottedAt)\n : undefined;\n }\n\n /**\n * When this session was last updated.\n */\n public get updatedAt(): Date {\n return new Date(this.session.updatedAt);\n }\n\n /**\n * The amount of active CPU used by the session. Only reported once the VM is\n * stopped.\n */\n public get activeCpuUsageMs(): number | undefined {\n return this.session.activeCpuDurationMs;\n }\n\n /**\n * The amount of network data used by the session. Only reported once the VM\n * is stopped.\n */\n public get networkTransfer():\n | { ingress: number; egress: number }\n | undefined {\n return this.session.networkTransfer;\n }\n\n /**\n * Serialize a Session instance to plain data for @workflow/serde.\n *\n * Although Sandbox handles top-level serialization, Session needs these\n * methods so the Workflow SWC compiler can resolve the class by name.\n * The `new Session(...)` self-reference in WORKFLOW_DESERIALIZE forces\n * rolldown to preserve the class name in the compiled output.\n */\n static [WORKFLOW_SERIALIZE](instance: Session): SerializedSession {\n return {\n session: instance.session,\n routes: instance.routes,\n };\n }\n\n static [WORKFLOW_DESERIALIZE](data: SerializedSession): Session {\n return new Session({ routes: data.routes, snapshot: data.session });\n }\n\n constructor(params: {\n client: APIClient;\n routes: SandboxRouteData[];\n session: SessionMetaData;\n } | {\n /** @internal – used during deserialization with an already-converted snapshot */\n routes: SandboxRouteData[];\n snapshot: SandboxSnapshot;\n }) {\n this.routes = params.routes;\n if (\"snapshot\" in params) {\n this.session = params.snapshot;\n } else {\n this._client = params.client;\n this.session = toSandboxSnapshot(params.session);\n }\n }\n\n /**\n * Get a previously run command by its ID.\n *\n * @param cmdId - ID of the command to retrieve\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A {@link Command} instance representing the command\n */\n async getCommand(\n cmdId: string,\n opts?: { signal?: AbortSignal },\n ): Promise<Command> {\n \"use step\";\n const client = await this.ensureClient();\n const command = await client.getCommand({\n sessionId: this.session.id,\n cmdId,\n signal: opts?.signal,\n });\n\n return new Command({\n client,\n sessionId: this.session.id,\n cmd: command.json.command,\n });\n }\n\n /**\n * Start executing a command in this session.\n *\n * @param command - The command to execute.\n * @param args - Arguments to pass to the command.\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the command execution.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n async runCommand(\n command: string,\n args?: string[],\n opts?: { signal?: AbortSignal },\n ): Promise<CommandFinished>;\n\n /**\n * Start executing a command in detached mode.\n *\n * @param params - The command parameters.\n * @returns A {@link Command} instance for the running command.\n */\n async runCommand(\n params: RunCommandParams & { detached: true },\n ): Promise<Command>;\n\n /**\n * Start executing a command in this session.\n *\n * @param params - The command parameters.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n async runCommand(params: RunCommandParams): Promise<CommandFinished>;\n\n async runCommand(\n commandOrParams: string | RunCommandParams,\n args?: string[],\n opts?: { signal?: AbortSignal },\n ): Promise<Command | CommandFinished> {\n \"use step\";\n const client = await this.ensureClient();\n const params: RunCommandParams =\n typeof commandOrParams === \"string\"\n ? { cmd: commandOrParams, args, signal: opts?.signal }\n : commandOrParams;\n const wait = params.detached ? false : true;\n const pipeLogs = async (command: Command): Promise<void> => {\n if (!params.stdout && !params.stderr) {\n return;\n }\n\n try {\n for await (const log of command.logs({ signal: params.signal })) {\n if (log.stream === \"stdout\") {\n params.stdout?.write(log.data);\n } else if (log.stream === \"stderr\") {\n params.stderr?.write(log.data);\n }\n }\n } catch (err) {\n if (params.signal?.aborted) {\n return;\n }\n throw err;\n }\n };\n\n if (wait) {\n const commandStream = await client.runCommand({\n sessionId: this.session.id,\n command: params.cmd,\n args: params.args ?? [],\n cwd: params.cwd,\n env: params.env ?? {},\n sudo: params.sudo ?? false,\n wait: true,\n signal: params.signal,\n });\n\n const command = new Command({\n client,\n sessionId: this.session.id,\n cmd: commandStream.command,\n });\n\n const [finished] = await Promise.all([\n commandStream.finished,\n pipeLogs(command),\n ]);\n return new CommandFinished({\n client,\n sessionId: this.session.id,\n cmd: finished,\n exitCode: finished.exitCode ?? 0,\n });\n }\n\n const commandResponse = await client.runCommand({\n sessionId: this.session.id,\n command: params.cmd,\n args: params.args ?? [],\n cwd: params.cwd,\n env: params.env ?? {},\n sudo: params.sudo ?? false,\n signal: params.signal,\n });\n\n const command = new Command({\n client,\n sessionId: this.session.id,\n cmd: commandResponse.json.command,\n });\n\n void pipeLogs(command).catch((err) => {\n if (params.signal?.aborted) {\n return;\n }\n (params.stderr ?? params.stdout)?.emit(\"error\", err);\n });\n\n return command;\n }\n\n /**\n * Create a directory in the filesystem of this session.\n *\n * @param path - Path of the directory to create\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n */\n async mkDir(path: string, opts?: { signal?: AbortSignal }): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n await client.mkDir({\n sessionId: this.session.id,\n path: path,\n signal: opts?.signal,\n });\n }\n\n /**\n * Read a file from the filesystem of this session as a stream.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to a ReadableStream containing the file contents, or null if file not found\n */\n async readFile(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<NodeJS.ReadableStream | null> {\n \"use step\";\n const client = await this.ensureClient();\n return client.readFile({\n sessionId: this.session.id,\n path: file.path,\n cwd: file.cwd,\n signal: opts?.signal,\n });\n }\n\n /**\n * Read a file from the filesystem of this session as a Buffer.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to the file contents as a Buffer, or null if file not found\n */\n async readFileToBuffer(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<Buffer | null> {\n \"use step\";\n const client = await this.ensureClient();\n const stream = await client.readFile({\n sessionId: this.session.id,\n path: file.path,\n cwd: file.cwd,\n signal: opts?.signal,\n });\n\n if (stream === null) {\n return null;\n }\n\n return consumeReadable(stream);\n }\n\n /**\n * Download a file from the session to the local filesystem.\n *\n * @param src - Source file on the session, with path and optional cwd\n * @param dst - Destination file on the local machine, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.mkdirRecursive - If true, create parent directories for the destination if they don't exist.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns The absolute path to the written file, or null if the source file was not found\n */\n async downloadFile(\n src: { path: string; cwd?: string },\n dst: { path: string; cwd?: string },\n opts?: { mkdirRecursive?: boolean; signal?: AbortSignal },\n ): Promise<string | null> {\n \"use step\";\n const client = await this.ensureClient();\n if (!src?.path) {\n throw new Error(\"downloadFile: source path is required\");\n }\n\n if (!dst?.path) {\n throw new Error(\"downloadFile: destination path is required\");\n }\n\n const stream = await client.readFile({\n sessionId: this.session.id,\n path: src.path,\n cwd: src.cwd,\n signal: opts?.signal,\n });\n\n if (stream === null) {\n return null;\n }\n\n try {\n const dstPath = resolve(dst.cwd ?? \"\", dst.path);\n if (opts?.mkdirRecursive) {\n await mkdir(dirname(dstPath), { recursive: true });\n }\n await pipeline(stream, createWriteStream(dstPath), {\n signal: opts?.signal,\n });\n return dstPath;\n } finally {\n stream.destroy();\n }\n }\n\n /**\n * Write files to the filesystem of this session.\n * Defaults to writing to /vercel/sandbox unless an absolute path is specified.\n * Writes files using the `vercel-sandbox` user.\n *\n * @param files - Array of files with path and stream/buffer contents\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the files are written\n */\n async writeFiles(\n files: { path: string; content: string | Uint8Array; mode?: number }[],\n opts?: { signal?: AbortSignal },\n ) {\n \"use step\";\n const client = await this.ensureClient();\n return client.writeFiles({\n sessionId: this.session.id,\n cwd: this.session.cwd,\n extractDir: \"/\",\n files: files,\n signal: opts?.signal,\n });\n }\n\n /**\n * Get the public domain of a port of this session.\n *\n * @param p - Port number to resolve\n * @returns A full domain (e.g. `https://subdomain.vercel.run`)\n * @throws If the port has no associated route\n */\n domain(p: number): string {\n const route = this.routes.find(({ port }) => port == p);\n if (route) {\n return `https://${route.subdomain}.vercel.run`;\n } else {\n throw new Error(`No route for port ${p}`);\n }\n }\n\n /**\n * Stop this session.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns The final session state and optional sandbox metadata.\n */\n async stop(opts?: {\n signal?: AbortSignal;\n }): Promise<{ session: SandboxSnapshot; sandbox?: SandboxMetaData; snapshot?: SnapshotMetadata }> {\n \"use step\";\n const client = await this.ensureClient();\n const response = await client.stopSession({\n sessionId: this.session.id,\n signal: opts?.signal,\n });\n this.session = toSandboxSnapshot(response.json.session);\n return { session: this.session, sandbox: response.json.sandbox, snapshot: response.json.snapshot };\n }\n\n /**\n * Update the current session's settings.\n *\n * @param params - Fields to update.\n * @param params.networkPolicy - The new network policy to apply.\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n *\n * @example\n * // Restrict to specific domains\n * await session.update({\n * networkPolicy: {\n * allow: [\"*.npmjs.org\", \"github.com\"],\n * }\n * });\n *\n * @example\n * // Inject credentials with per-domain transformers\n * await session.update({\n * networkPolicy: {\n * allow: {\n * \"ai-gateway.vercel.sh\": [{\n * transform: [{\n * headers: { authorization: \"Bearer ...\" }\n * }]\n * }],\n * \"*\": []\n * }\n * }\n * });\n *\n * @example\n * // Deny all network access\n * await session.update({ networkPolicy: \"deny-all\" });\n */\n async update(\n params: {\n networkPolicy?: NetworkPolicy;\n },\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n if (params.networkPolicy !== undefined) {\n const client = await this.ensureClient();\n const response = await client.updateNetworkPolicy({\n sessionId: this.session.id,\n networkPolicy: params.networkPolicy,\n signal: opts?.signal,\n });\n\n // Update the internal session with the new network policy\n this.session = toSandboxSnapshot(response.json.session);\n }\n }\n\n /**\n * Extend the timeout of the session by the specified duration.\n *\n * This allows you to extend the lifetime of a session up until the maximum\n * execution timeout for your plan.\n *\n * @param duration - The duration in milliseconds to extend the timeout by\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the timeout is extended\n *\n * @example\n * const sandbox = await Sandbox.create({ timeout: ms('10m') });\n * const session = sandbox.currentSession();\n * // Extends timeout by 5 minutes, to a total of 15 minutes.\n * await session.extendTimeout(ms('5m'));\n */\n async extendTimeout(\n duration: number,\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n const response = await client.extendTimeout({\n sessionId: this.session.id,\n duration,\n signal: opts?.signal,\n });\n\n // Update the internal sandbox metadata with the new timeout value\n this.session = toSandboxSnapshot(response.json.session);\n }\n\n /**\n * Create a snapshot from this currently running session. New sandboxes can\n * then be created from this snapshot using {@link Sandbox.create}.\n *\n * Note: this session will be stopped as part of the snapshot creation process.\n *\n * @param opts - Optional parameters.\n * @param opts.expiration - Optional expiration time in milliseconds. Use 0 for no expiration at all.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to the Snapshot instance\n */\n async snapshot(opts?: {\n expiration?: number;\n signal?: AbortSignal;\n }): Promise<Snapshot> {\n \"use step\";\n const client = await this.ensureClient();\n const response = await client.createSnapshot({\n sessionId: this.session.id,\n expiration: opts?.expiration,\n signal: opts?.signal,\n });\n\n this.session = toSandboxSnapshot(response.json.session);\n\n return new Snapshot({\n client,\n snapshot: response.json.snapshot,\n });\n }\n}\n"],"mappings":"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"}

@@ -1,2 +0,2 @@

import { SandboxRouteData, SessionMetaData } from "./api-client/validators.cjs";
import { SandboxMetaData, SandboxRouteData, SessionMetaData, SnapshotMetadata } from "./api-client/validators.cjs";
import { NetworkPolicy, NetworkPolicyRule, NetworkTransformer } from "./network-policy.cjs";

@@ -321,9 +321,11 @@ import { APIClient } from "./api-client/api-client.cjs";

* @param opts.signal - An AbortSignal to cancel the operation.
* @param opts.blocking - If true, poll until the session has fully stopped and return the final state.
* @returns The session at the time the stop was acknowledged, or after fully stopped if `blocking` is true.
* @returns The final session state and optional sandbox metadata.
*/
stop(opts?: {
signal?: AbortSignal;
blocking?: boolean;
}): Promise<SandboxSnapshot>;
}): Promise<{
session: SandboxSnapshot;
sandbox?: SandboxMetaData;
snapshot?: SnapshotMetadata;
}>;
/**

@@ -330,0 +332,0 @@ * Update the current session's settings.

@@ -1,2 +0,2 @@

import { SandboxRouteData, SessionMetaData } from "./api-client/validators.js";
import { SandboxMetaData, SandboxRouteData, SessionMetaData, SnapshotMetadata } from "./api-client/validators.js";
import { NetworkPolicy, NetworkPolicyRule, NetworkTransformer } from "./network-policy.js";

@@ -321,9 +321,11 @@ import { APIClient } from "./api-client/api-client.js";

* @param opts.signal - An AbortSignal to cancel the operation.
* @param opts.blocking - If true, poll until the session has fully stopped and return the final state.
* @returns The session at the time the stop was acknowledged, or after fully stopped if `blocking` is true.
* @returns The final session state and optional sandbox metadata.
*/
stop(opts?: {
signal?: AbortSignal;
blocking?: boolean;
}): Promise<SandboxSnapshot>;
}): Promise<{
session: SandboxSnapshot;
sandbox?: SandboxMetaData;
snapshot?: SnapshotMetadata;
}>;
/**

@@ -330,0 +332,0 @@ * Update the current session's settings.

@@ -410,13 +410,16 @@ import { consumeReadable } from "./utils/consume-readable.js";

* @param opts.signal - An AbortSignal to cancel the operation.
* @param opts.blocking - If true, poll until the session has fully stopped and return the final state.
* @returns The session at the time the stop was acknowledged, or after fully stopped if `blocking` is true.
* @returns The final session state and optional sandbox metadata.
*/
async stop(opts) {
"use step";
this.session = toSandboxSnapshot((await (await this.ensureClient()).stopSession({
const response = await (await this.ensureClient()).stopSession({
sessionId: this.session.id,
signal: opts?.signal,
blocking: opts?.blocking
})).json.session);
return this.session;
signal: opts?.signal
});
this.session = toSandboxSnapshot(response.json.session);
return {
session: this.session,
sandbox: response.json.sandbox,
snapshot: response.json.snapshot
};
}

@@ -423,0 +426,0 @@ /**

@@ -1,1 +0,1 @@

{"version":3,"file":"session.js","names":["params: RunCommandParams","command","path"],"sources":["../src/session.ts"],"sourcesContent":["import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport { type SessionMetaData, type SandboxRouteData, APIClient } from \"./api-client/index.js\";\nimport type { Writable } from \"stream\";\nimport { pipeline } from \"stream/promises\";\nimport { createWriteStream } from \"fs\";\nimport { mkdir } from \"fs/promises\";\nimport { dirname, resolve } from \"path\";\nimport { Command, CommandFinished } from \"./command.js\";\nimport { Snapshot } from \"./snapshot.js\";\nimport { consumeReadable } from \"./utils/consume-readable.js\";\nimport type {\n NetworkPolicy,\n NetworkPolicyRule,\n NetworkTransformer,\n} from \"./network-policy.js\";\nimport { toSandboxSnapshot, type SandboxSnapshot } from \"./utils/sandbox-snapshot.js\";\nimport { getCredentials } from \"./utils/get-credentials.js\";\n\nexport type { NetworkPolicy, NetworkPolicyRule, NetworkTransformer };\n\n/**\n * Serialized representation of a Session for @workflow/serde.\n */\nexport interface SerializedSession {\n session: SandboxSnapshot;\n routes: SandboxRouteData[];\n}\n\n/** @inline */\nexport interface RunCommandParams {\n /**\n * The command to execute\n */\n cmd: string;\n /**\n * Arguments to pass to the command\n */\n args?: string[];\n /**\n * Working directory to execute the command in\n */\n cwd?: string;\n /**\n * Environment variables to set for this command\n */\n env?: Record<string, string>;\n /**\n * If true, execute this command with root privileges. Defaults to false.\n */\n sudo?: boolean;\n /**\n * If true, the command will return without waiting for `exitCode`\n */\n detached?: boolean;\n /**\n * A `Writable` stream where `stdout` from the command will be piped\n */\n stdout?: Writable;\n /**\n * A `Writable` stream where `stderr` from the command will be piped\n */\n stderr?: Writable;\n /**\n * An AbortSignal to cancel the command execution\n */\n signal?: AbortSignal;\n}\n\n/**\n * A Session represents a running VM instance within a {@link Sandbox}.\n *\n * Obtain a session via {@link Sandbox.currentSession}.\n */\nexport class Session {\n private _client: APIClient | null = null;\n\n /**\n * Lazily resolve credentials and construct an API client.\n * This is used in step contexts where the Sandbox was deserialized\n * without a client (e.g. when crossing workflow/step boundaries).\n * Uses getCredentials() which resolves from OIDC or env vars.\n * @internal\n */\n private async ensureClient(): Promise<APIClient> {\n \"use step\";\n if (this._client) return this._client;\n const credentials = await getCredentials();\n this._client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n });\n return this._client;\n }\n\n /**\n * Routes from ports to subdomains.\n * @hidden\n */\n public readonly routes: SandboxRouteData[];\n\n /**\n * Internal metadata about the current session.\n */\n private session: SandboxSnapshot;\n\n private get client(): APIClient {\n if (!this._client) throw new Error(\"API client not initialized\");\n return this._client;\n }\n\n /** @internal */\n get _sessionSnapshot(): SandboxSnapshot {\n return this.session;\n }\n\n /**\n * Unique ID of this session.\n */\n public get sessionId(): string {\n return this.session.id;\n }\n\n public get interactivePort(): number | undefined {\n return this.session.interactivePort ?? undefined;\n }\n\n /**\n * The status of this session.\n */\n public get status(): SessionMetaData[\"status\"] {\n return this.session.status;\n }\n\n /**\n * The creation date of this session.\n */\n public get createdAt(): Date {\n return new Date(this.session.createdAt);\n }\n\n /**\n * The timeout of this session in milliseconds.\n */\n public get timeout(): number {\n return this.session.timeout;\n }\n\n /**\n * The network policy of this session.\n */\n public get networkPolicy(): NetworkPolicy | undefined {\n return this.session.networkPolicy;\n }\n\n /**\n * If the session was created from a snapshot, the ID of that snapshot.\n */\n public get sourceSnapshotId(): string | undefined {\n return this.session.sourceSnapshotId;\n }\n\n /**\n * Memory allocated to this session in MB.\n */\n public get memory(): number {\n return this.session.memory;\n }\n\n /**\n * Number of vCPUs allocated to this session.\n */\n public get vcpus(): number {\n return this.session.vcpus;\n }\n\n /**\n * The region where this session is hosted.\n */\n public get region(): string {\n return this.session.region;\n }\n\n /**\n * Runtime identifier (e.g. \"node24\", \"python3.13\").\n */\n public get runtime(): string {\n return this.session.runtime;\n }\n\n /**\n * The working directory of this session.\n */\n public get cwd(): string {\n return this.session.cwd;\n }\n\n /**\n * When this session was requested.\n */\n public get requestedAt(): Date {\n return new Date(this.session.requestedAt);\n }\n\n /**\n * When this session started running.\n */\n public get startedAt(): Date | undefined {\n return this.session.startedAt != null\n ? new Date(this.session.startedAt)\n : undefined;\n }\n\n /**\n * When this session was requested to stop.\n */\n public get requestedStopAt(): Date | undefined {\n return this.session.requestedStopAt != null\n ? new Date(this.session.requestedStopAt)\n : undefined;\n }\n\n /**\n * When this session was stopped.\n */\n public get stoppedAt(): Date | undefined {\n return this.session.stoppedAt != null\n ? new Date(this.session.stoppedAt)\n : undefined;\n }\n\n /**\n * When this session was aborted.\n */\n public get abortedAt(): Date | undefined {\n return this.session.abortedAt != null\n ? new Date(this.session.abortedAt)\n : undefined;\n }\n\n /**\n * The wall-clock duration of this session in milliseconds.\n */\n public get duration(): number | undefined {\n return this.session.duration;\n }\n\n /**\n * When a snapshot was requested for this session.\n */\n public get snapshottedAt(): Date | undefined {\n return this.session.snapshottedAt != null\n ? new Date(this.session.snapshottedAt)\n : undefined;\n }\n\n /**\n * When this session was last updated.\n */\n public get updatedAt(): Date {\n return new Date(this.session.updatedAt);\n }\n\n /**\n * The amount of active CPU used by the session. Only reported once the VM is\n * stopped.\n */\n public get activeCpuUsageMs(): number | undefined {\n return this.session.activeCpuDurationMs;\n }\n\n /**\n * The amount of network data used by the session. Only reported once the VM\n * is stopped.\n */\n public get networkTransfer():\n | { ingress: number; egress: number }\n | undefined {\n return this.session.networkTransfer;\n }\n\n /**\n * Serialize a Session instance to plain data for @workflow/serde.\n *\n * Although Sandbox handles top-level serialization, Session needs these\n * methods so the Workflow SWC compiler can resolve the class by name.\n * The `new Session(...)` self-reference in WORKFLOW_DESERIALIZE forces\n * rolldown to preserve the class name in the compiled output.\n */\n static [WORKFLOW_SERIALIZE](instance: Session): SerializedSession {\n return {\n session: instance.session,\n routes: instance.routes,\n };\n }\n\n static [WORKFLOW_DESERIALIZE](data: SerializedSession): Session {\n return new Session({ routes: data.routes, snapshot: data.session });\n }\n\n constructor(params: {\n client: APIClient;\n routes: SandboxRouteData[];\n session: SessionMetaData;\n } | {\n /** @internal – used during deserialization with an already-converted snapshot */\n routes: SandboxRouteData[];\n snapshot: SandboxSnapshot;\n }) {\n this.routes = params.routes;\n if (\"snapshot\" in params) {\n this.session = params.snapshot;\n } else {\n this._client = params.client;\n this.session = toSandboxSnapshot(params.session);\n }\n }\n\n /**\n * Get a previously run command by its ID.\n *\n * @param cmdId - ID of the command to retrieve\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A {@link Command} instance representing the command\n */\n async getCommand(\n cmdId: string,\n opts?: { signal?: AbortSignal },\n ): Promise<Command> {\n \"use step\";\n const client = await this.ensureClient();\n const command = await client.getCommand({\n sessionId: this.session.id,\n cmdId,\n signal: opts?.signal,\n });\n\n return new Command({\n client,\n sessionId: this.session.id,\n cmd: command.json.command,\n });\n }\n\n /**\n * Start executing a command in this session.\n *\n * @param command - The command to execute.\n * @param args - Arguments to pass to the command.\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the command execution.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n async runCommand(\n command: string,\n args?: string[],\n opts?: { signal?: AbortSignal },\n ): Promise<CommandFinished>;\n\n /**\n * Start executing a command in detached mode.\n *\n * @param params - The command parameters.\n * @returns A {@link Command} instance for the running command.\n */\n async runCommand(\n params: RunCommandParams & { detached: true },\n ): Promise<Command>;\n\n /**\n * Start executing a command in this session.\n *\n * @param params - The command parameters.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n async runCommand(params: RunCommandParams): Promise<CommandFinished>;\n\n async runCommand(\n commandOrParams: string | RunCommandParams,\n args?: string[],\n opts?: { signal?: AbortSignal },\n ): Promise<Command | CommandFinished> {\n \"use step\";\n const client = await this.ensureClient();\n const params: RunCommandParams =\n typeof commandOrParams === \"string\"\n ? { cmd: commandOrParams, args, signal: opts?.signal }\n : commandOrParams;\n const wait = params.detached ? false : true;\n const pipeLogs = async (command: Command): Promise<void> => {\n if (!params.stdout && !params.stderr) {\n return;\n }\n\n try {\n for await (const log of command.logs({ signal: params.signal })) {\n if (log.stream === \"stdout\") {\n params.stdout?.write(log.data);\n } else if (log.stream === \"stderr\") {\n params.stderr?.write(log.data);\n }\n }\n } catch (err) {\n if (params.signal?.aborted) {\n return;\n }\n throw err;\n }\n };\n\n if (wait) {\n const commandStream = await client.runCommand({\n sessionId: this.session.id,\n command: params.cmd,\n args: params.args ?? [],\n cwd: params.cwd,\n env: params.env ?? {},\n sudo: params.sudo ?? false,\n wait: true,\n signal: params.signal,\n });\n\n const command = new Command({\n client,\n sessionId: this.session.id,\n cmd: commandStream.command,\n });\n\n const [finished] = await Promise.all([\n commandStream.finished,\n pipeLogs(command),\n ]);\n return new CommandFinished({\n client,\n sessionId: this.session.id,\n cmd: finished,\n exitCode: finished.exitCode ?? 0,\n });\n }\n\n const commandResponse = await client.runCommand({\n sessionId: this.session.id,\n command: params.cmd,\n args: params.args ?? [],\n cwd: params.cwd,\n env: params.env ?? {},\n sudo: params.sudo ?? false,\n signal: params.signal,\n });\n\n const command = new Command({\n client,\n sessionId: this.session.id,\n cmd: commandResponse.json.command,\n });\n\n void pipeLogs(command).catch((err) => {\n if (params.signal?.aborted) {\n return;\n }\n (params.stderr ?? params.stdout)?.emit(\"error\", err);\n });\n\n return command;\n }\n\n /**\n * Create a directory in the filesystem of this session.\n *\n * @param path - Path of the directory to create\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n */\n async mkDir(path: string, opts?: { signal?: AbortSignal }): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n await client.mkDir({\n sessionId: this.session.id,\n path: path,\n signal: opts?.signal,\n });\n }\n\n /**\n * Read a file from the filesystem of this session as a stream.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to a ReadableStream containing the file contents, or null if file not found\n */\n async readFile(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<NodeJS.ReadableStream | null> {\n \"use step\";\n const client = await this.ensureClient();\n return client.readFile({\n sessionId: this.session.id,\n path: file.path,\n cwd: file.cwd,\n signal: opts?.signal,\n });\n }\n\n /**\n * Read a file from the filesystem of this session as a Buffer.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to the file contents as a Buffer, or null if file not found\n */\n async readFileToBuffer(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<Buffer | null> {\n \"use step\";\n const client = await this.ensureClient();\n const stream = await client.readFile({\n sessionId: this.session.id,\n path: file.path,\n cwd: file.cwd,\n signal: opts?.signal,\n });\n\n if (stream === null) {\n return null;\n }\n\n return consumeReadable(stream);\n }\n\n /**\n * Download a file from the session to the local filesystem.\n *\n * @param src - Source file on the session, with path and optional cwd\n * @param dst - Destination file on the local machine, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.mkdirRecursive - If true, create parent directories for the destination if they don't exist.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns The absolute path to the written file, or null if the source file was not found\n */\n async downloadFile(\n src: { path: string; cwd?: string },\n dst: { path: string; cwd?: string },\n opts?: { mkdirRecursive?: boolean; signal?: AbortSignal },\n ): Promise<string | null> {\n \"use step\";\n const client = await this.ensureClient();\n if (!src?.path) {\n throw new Error(\"downloadFile: source path is required\");\n }\n\n if (!dst?.path) {\n throw new Error(\"downloadFile: destination path is required\");\n }\n\n const stream = await client.readFile({\n sessionId: this.session.id,\n path: src.path,\n cwd: src.cwd,\n signal: opts?.signal,\n });\n\n if (stream === null) {\n return null;\n }\n\n try {\n const dstPath = resolve(dst.cwd ?? \"\", dst.path);\n if (opts?.mkdirRecursive) {\n await mkdir(dirname(dstPath), { recursive: true });\n }\n await pipeline(stream, createWriteStream(dstPath), {\n signal: opts?.signal,\n });\n return dstPath;\n } finally {\n stream.destroy();\n }\n }\n\n /**\n * Write files to the filesystem of this session.\n * Defaults to writing to /vercel/sandbox unless an absolute path is specified.\n * Writes files using the `vercel-sandbox` user.\n *\n * @param files - Array of files with path and stream/buffer contents\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the files are written\n */\n async writeFiles(\n files: { path: string; content: string | Uint8Array; mode?: number }[],\n opts?: { signal?: AbortSignal },\n ) {\n \"use step\";\n const client = await this.ensureClient();\n return client.writeFiles({\n sessionId: this.session.id,\n cwd: this.session.cwd,\n extractDir: \"/\",\n files: files,\n signal: opts?.signal,\n });\n }\n\n /**\n * Get the public domain of a port of this session.\n *\n * @param p - Port number to resolve\n * @returns A full domain (e.g. `https://subdomain.vercel.run`)\n * @throws If the port has no associated route\n */\n domain(p: number): string {\n const route = this.routes.find(({ port }) => port == p);\n if (route) {\n return `https://${route.subdomain}.vercel.run`;\n } else {\n throw new Error(`No route for port ${p}`);\n }\n }\n\n /**\n * Stop this session.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @param opts.blocking - If true, poll until the session has fully stopped and return the final state.\n * @returns The session at the time the stop was acknowledged, or after fully stopped if `blocking` is true.\n */\n async stop(opts?: {\n signal?: AbortSignal;\n blocking?: boolean;\n }): Promise<SandboxSnapshot> {\n \"use step\";\n const client = await this.ensureClient();\n const response = await client.stopSession({\n sessionId: this.session.id,\n signal: opts?.signal,\n blocking: opts?.blocking,\n });\n this.session = toSandboxSnapshot(response.json.session);\n return this.session;\n }\n\n /**\n * Update the current session's settings.\n *\n * @param params - Fields to update.\n * @param params.networkPolicy - The new network policy to apply.\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n *\n * @example\n * // Restrict to specific domains\n * await session.update({\n * networkPolicy: {\n * allow: [\"*.npmjs.org\", \"github.com\"],\n * }\n * });\n *\n * @example\n * // Inject credentials with per-domain transformers\n * await session.update({\n * networkPolicy: {\n * allow: {\n * \"ai-gateway.vercel.sh\": [{\n * transform: [{\n * headers: { authorization: \"Bearer ...\" }\n * }]\n * }],\n * \"*\": []\n * }\n * }\n * });\n *\n * @example\n * // Deny all network access\n * await session.update({ networkPolicy: \"deny-all\" });\n */\n async update(\n params: {\n networkPolicy?: NetworkPolicy;\n },\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n if (params.networkPolicy !== undefined) {\n const client = await this.ensureClient();\n const response = await client.updateNetworkPolicy({\n sessionId: this.session.id,\n networkPolicy: params.networkPolicy,\n signal: opts?.signal,\n });\n\n // Update the internal session with the new network policy\n this.session = toSandboxSnapshot(response.json.session);\n }\n }\n\n /**\n * Extend the timeout of the session by the specified duration.\n *\n * This allows you to extend the lifetime of a session up until the maximum\n * execution timeout for your plan.\n *\n * @param duration - The duration in milliseconds to extend the timeout by\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the timeout is extended\n *\n * @example\n * const sandbox = await Sandbox.create({ timeout: ms('10m') });\n * const session = sandbox.currentSession();\n * // Extends timeout by 5 minutes, to a total of 15 minutes.\n * await session.extendTimeout(ms('5m'));\n */\n async extendTimeout(\n duration: number,\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n const response = await client.extendTimeout({\n sessionId: this.session.id,\n duration,\n signal: opts?.signal,\n });\n\n // Update the internal sandbox metadata with the new timeout value\n this.session = toSandboxSnapshot(response.json.session);\n }\n\n /**\n * Create a snapshot from this currently running session. New sandboxes can\n * then be created from this snapshot using {@link Sandbox.create}.\n *\n * Note: this session will be stopped as part of the snapshot creation process.\n *\n * @param opts - Optional parameters.\n * @param opts.expiration - Optional expiration time in milliseconds. Use 0 for no expiration at all.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to the Snapshot instance\n */\n async snapshot(opts?: {\n expiration?: number;\n signal?: AbortSignal;\n }): Promise<Snapshot> {\n \"use step\";\n const client = await this.ensureClient();\n const response = await client.createSnapshot({\n sessionId: this.session.id,\n expiration: opts?.expiration,\n signal: opts?.signal,\n });\n\n this.session = toSandboxSnapshot(response.json.session);\n\n return new Snapshot({\n client,\n snapshot: response.json.snapshot,\n });\n }\n}\n"],"mappings":"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"}
{"version":3,"file":"session.js","names":["params: RunCommandParams","command","path"],"sources":["../src/session.ts"],"sourcesContent":["import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport { type SessionMetaData, type SandboxRouteData, type SandboxMetaData, type SnapshotMetadata, APIClient } from \"./api-client/index.js\";\nimport type { Writable } from \"stream\";\nimport { pipeline } from \"stream/promises\";\nimport { createWriteStream } from \"fs\";\nimport { mkdir } from \"fs/promises\";\nimport { dirname, resolve } from \"path\";\nimport { Command, CommandFinished } from \"./command.js\";\nimport { Snapshot } from \"./snapshot.js\";\nimport { consumeReadable } from \"./utils/consume-readable.js\";\nimport type {\n NetworkPolicy,\n NetworkPolicyRule,\n NetworkTransformer,\n} from \"./network-policy.js\";\nimport { toSandboxSnapshot, type SandboxSnapshot } from \"./utils/sandbox-snapshot.js\";\nimport { getCredentials } from \"./utils/get-credentials.js\";\n\nexport type { NetworkPolicy, NetworkPolicyRule, NetworkTransformer };\n\n/**\n * Serialized representation of a Session for @workflow/serde.\n */\nexport interface SerializedSession {\n session: SandboxSnapshot;\n routes: SandboxRouteData[];\n}\n\n/** @inline */\nexport interface RunCommandParams {\n /**\n * The command to execute\n */\n cmd: string;\n /**\n * Arguments to pass to the command\n */\n args?: string[];\n /**\n * Working directory to execute the command in\n */\n cwd?: string;\n /**\n * Environment variables to set for this command\n */\n env?: Record<string, string>;\n /**\n * If true, execute this command with root privileges. Defaults to false.\n */\n sudo?: boolean;\n /**\n * If true, the command will return without waiting for `exitCode`\n */\n detached?: boolean;\n /**\n * A `Writable` stream where `stdout` from the command will be piped\n */\n stdout?: Writable;\n /**\n * A `Writable` stream where `stderr` from the command will be piped\n */\n stderr?: Writable;\n /**\n * An AbortSignal to cancel the command execution\n */\n signal?: AbortSignal;\n}\n\n/**\n * A Session represents a running VM instance within a {@link Sandbox}.\n *\n * Obtain a session via {@link Sandbox.currentSession}.\n */\nexport class Session {\n private _client: APIClient | null = null;\n\n /**\n * Lazily resolve credentials and construct an API client.\n * This is used in step contexts where the Sandbox was deserialized\n * without a client (e.g. when crossing workflow/step boundaries).\n * Uses getCredentials() which resolves from OIDC or env vars.\n * @internal\n */\n private async ensureClient(): Promise<APIClient> {\n \"use step\";\n if (this._client) return this._client;\n const credentials = await getCredentials();\n this._client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n });\n return this._client;\n }\n\n /**\n * Routes from ports to subdomains.\n * @hidden\n */\n public readonly routes: SandboxRouteData[];\n\n /**\n * Internal metadata about the current session.\n */\n private session: SandboxSnapshot;\n\n private get client(): APIClient {\n if (!this._client) throw new Error(\"API client not initialized\");\n return this._client;\n }\n\n /** @internal */\n get _sessionSnapshot(): SandboxSnapshot {\n return this.session;\n }\n\n /**\n * Unique ID of this session.\n */\n public get sessionId(): string {\n return this.session.id;\n }\n\n public get interactivePort(): number | undefined {\n return this.session.interactivePort ?? undefined;\n }\n\n /**\n * The status of this session.\n */\n public get status(): SessionMetaData[\"status\"] {\n return this.session.status;\n }\n\n /**\n * The creation date of this session.\n */\n public get createdAt(): Date {\n return new Date(this.session.createdAt);\n }\n\n /**\n * The timeout of this session in milliseconds.\n */\n public get timeout(): number {\n return this.session.timeout;\n }\n\n /**\n * The network policy of this session.\n */\n public get networkPolicy(): NetworkPolicy | undefined {\n return this.session.networkPolicy;\n }\n\n /**\n * If the session was created from a snapshot, the ID of that snapshot.\n */\n public get sourceSnapshotId(): string | undefined {\n return this.session.sourceSnapshotId;\n }\n\n /**\n * Memory allocated to this session in MB.\n */\n public get memory(): number {\n return this.session.memory;\n }\n\n /**\n * Number of vCPUs allocated to this session.\n */\n public get vcpus(): number {\n return this.session.vcpus;\n }\n\n /**\n * The region where this session is hosted.\n */\n public get region(): string {\n return this.session.region;\n }\n\n /**\n * Runtime identifier (e.g. \"node24\", \"python3.13\").\n */\n public get runtime(): string {\n return this.session.runtime;\n }\n\n /**\n * The working directory of this session.\n */\n public get cwd(): string {\n return this.session.cwd;\n }\n\n /**\n * When this session was requested.\n */\n public get requestedAt(): Date {\n return new Date(this.session.requestedAt);\n }\n\n /**\n * When this session started running.\n */\n public get startedAt(): Date | undefined {\n return this.session.startedAt != null\n ? new Date(this.session.startedAt)\n : undefined;\n }\n\n /**\n * When this session was requested to stop.\n */\n public get requestedStopAt(): Date | undefined {\n return this.session.requestedStopAt != null\n ? new Date(this.session.requestedStopAt)\n : undefined;\n }\n\n /**\n * When this session was stopped.\n */\n public get stoppedAt(): Date | undefined {\n return this.session.stoppedAt != null\n ? new Date(this.session.stoppedAt)\n : undefined;\n }\n\n /**\n * When this session was aborted.\n */\n public get abortedAt(): Date | undefined {\n return this.session.abortedAt != null\n ? new Date(this.session.abortedAt)\n : undefined;\n }\n\n /**\n * The wall-clock duration of this session in milliseconds.\n */\n public get duration(): number | undefined {\n return this.session.duration;\n }\n\n /**\n * When a snapshot was requested for this session.\n */\n public get snapshottedAt(): Date | undefined {\n return this.session.snapshottedAt != null\n ? new Date(this.session.snapshottedAt)\n : undefined;\n }\n\n /**\n * When this session was last updated.\n */\n public get updatedAt(): Date {\n return new Date(this.session.updatedAt);\n }\n\n /**\n * The amount of active CPU used by the session. Only reported once the VM is\n * stopped.\n */\n public get activeCpuUsageMs(): number | undefined {\n return this.session.activeCpuDurationMs;\n }\n\n /**\n * The amount of network data used by the session. Only reported once the VM\n * is stopped.\n */\n public get networkTransfer():\n | { ingress: number; egress: number }\n | undefined {\n return this.session.networkTransfer;\n }\n\n /**\n * Serialize a Session instance to plain data for @workflow/serde.\n *\n * Although Sandbox handles top-level serialization, Session needs these\n * methods so the Workflow SWC compiler can resolve the class by name.\n * The `new Session(...)` self-reference in WORKFLOW_DESERIALIZE forces\n * rolldown to preserve the class name in the compiled output.\n */\n static [WORKFLOW_SERIALIZE](instance: Session): SerializedSession {\n return {\n session: instance.session,\n routes: instance.routes,\n };\n }\n\n static [WORKFLOW_DESERIALIZE](data: SerializedSession): Session {\n return new Session({ routes: data.routes, snapshot: data.session });\n }\n\n constructor(params: {\n client: APIClient;\n routes: SandboxRouteData[];\n session: SessionMetaData;\n } | {\n /** @internal – used during deserialization with an already-converted snapshot */\n routes: SandboxRouteData[];\n snapshot: SandboxSnapshot;\n }) {\n this.routes = params.routes;\n if (\"snapshot\" in params) {\n this.session = params.snapshot;\n } else {\n this._client = params.client;\n this.session = toSandboxSnapshot(params.session);\n }\n }\n\n /**\n * Get a previously run command by its ID.\n *\n * @param cmdId - ID of the command to retrieve\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A {@link Command} instance representing the command\n */\n async getCommand(\n cmdId: string,\n opts?: { signal?: AbortSignal },\n ): Promise<Command> {\n \"use step\";\n const client = await this.ensureClient();\n const command = await client.getCommand({\n sessionId: this.session.id,\n cmdId,\n signal: opts?.signal,\n });\n\n return new Command({\n client,\n sessionId: this.session.id,\n cmd: command.json.command,\n });\n }\n\n /**\n * Start executing a command in this session.\n *\n * @param command - The command to execute.\n * @param args - Arguments to pass to the command.\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the command execution.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n async runCommand(\n command: string,\n args?: string[],\n opts?: { signal?: AbortSignal },\n ): Promise<CommandFinished>;\n\n /**\n * Start executing a command in detached mode.\n *\n * @param params - The command parameters.\n * @returns A {@link Command} instance for the running command.\n */\n async runCommand(\n params: RunCommandParams & { detached: true },\n ): Promise<Command>;\n\n /**\n * Start executing a command in this session.\n *\n * @param params - The command parameters.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n async runCommand(params: RunCommandParams): Promise<CommandFinished>;\n\n async runCommand(\n commandOrParams: string | RunCommandParams,\n args?: string[],\n opts?: { signal?: AbortSignal },\n ): Promise<Command | CommandFinished> {\n \"use step\";\n const client = await this.ensureClient();\n const params: RunCommandParams =\n typeof commandOrParams === \"string\"\n ? { cmd: commandOrParams, args, signal: opts?.signal }\n : commandOrParams;\n const wait = params.detached ? false : true;\n const pipeLogs = async (command: Command): Promise<void> => {\n if (!params.stdout && !params.stderr) {\n return;\n }\n\n try {\n for await (const log of command.logs({ signal: params.signal })) {\n if (log.stream === \"stdout\") {\n params.stdout?.write(log.data);\n } else if (log.stream === \"stderr\") {\n params.stderr?.write(log.data);\n }\n }\n } catch (err) {\n if (params.signal?.aborted) {\n return;\n }\n throw err;\n }\n };\n\n if (wait) {\n const commandStream = await client.runCommand({\n sessionId: this.session.id,\n command: params.cmd,\n args: params.args ?? [],\n cwd: params.cwd,\n env: params.env ?? {},\n sudo: params.sudo ?? false,\n wait: true,\n signal: params.signal,\n });\n\n const command = new Command({\n client,\n sessionId: this.session.id,\n cmd: commandStream.command,\n });\n\n const [finished] = await Promise.all([\n commandStream.finished,\n pipeLogs(command),\n ]);\n return new CommandFinished({\n client,\n sessionId: this.session.id,\n cmd: finished,\n exitCode: finished.exitCode ?? 0,\n });\n }\n\n const commandResponse = await client.runCommand({\n sessionId: this.session.id,\n command: params.cmd,\n args: params.args ?? [],\n cwd: params.cwd,\n env: params.env ?? {},\n sudo: params.sudo ?? false,\n signal: params.signal,\n });\n\n const command = new Command({\n client,\n sessionId: this.session.id,\n cmd: commandResponse.json.command,\n });\n\n void pipeLogs(command).catch((err) => {\n if (params.signal?.aborted) {\n return;\n }\n (params.stderr ?? params.stdout)?.emit(\"error\", err);\n });\n\n return command;\n }\n\n /**\n * Create a directory in the filesystem of this session.\n *\n * @param path - Path of the directory to create\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n */\n async mkDir(path: string, opts?: { signal?: AbortSignal }): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n await client.mkDir({\n sessionId: this.session.id,\n path: path,\n signal: opts?.signal,\n });\n }\n\n /**\n * Read a file from the filesystem of this session as a stream.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to a ReadableStream containing the file contents, or null if file not found\n */\n async readFile(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<NodeJS.ReadableStream | null> {\n \"use step\";\n const client = await this.ensureClient();\n return client.readFile({\n sessionId: this.session.id,\n path: file.path,\n cwd: file.cwd,\n signal: opts?.signal,\n });\n }\n\n /**\n * Read a file from the filesystem of this session as a Buffer.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to the file contents as a Buffer, or null if file not found\n */\n async readFileToBuffer(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<Buffer | null> {\n \"use step\";\n const client = await this.ensureClient();\n const stream = await client.readFile({\n sessionId: this.session.id,\n path: file.path,\n cwd: file.cwd,\n signal: opts?.signal,\n });\n\n if (stream === null) {\n return null;\n }\n\n return consumeReadable(stream);\n }\n\n /**\n * Download a file from the session to the local filesystem.\n *\n * @param src - Source file on the session, with path and optional cwd\n * @param dst - Destination file on the local machine, with path and optional cwd\n * @param opts - Optional parameters.\n * @param opts.mkdirRecursive - If true, create parent directories for the destination if they don't exist.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns The absolute path to the written file, or null if the source file was not found\n */\n async downloadFile(\n src: { path: string; cwd?: string },\n dst: { path: string; cwd?: string },\n opts?: { mkdirRecursive?: boolean; signal?: AbortSignal },\n ): Promise<string | null> {\n \"use step\";\n const client = await this.ensureClient();\n if (!src?.path) {\n throw new Error(\"downloadFile: source path is required\");\n }\n\n if (!dst?.path) {\n throw new Error(\"downloadFile: destination path is required\");\n }\n\n const stream = await client.readFile({\n sessionId: this.session.id,\n path: src.path,\n cwd: src.cwd,\n signal: opts?.signal,\n });\n\n if (stream === null) {\n return null;\n }\n\n try {\n const dstPath = resolve(dst.cwd ?? \"\", dst.path);\n if (opts?.mkdirRecursive) {\n await mkdir(dirname(dstPath), { recursive: true });\n }\n await pipeline(stream, createWriteStream(dstPath), {\n signal: opts?.signal,\n });\n return dstPath;\n } finally {\n stream.destroy();\n }\n }\n\n /**\n * Write files to the filesystem of this session.\n * Defaults to writing to /vercel/sandbox unless an absolute path is specified.\n * Writes files using the `vercel-sandbox` user.\n *\n * @param files - Array of files with path and stream/buffer contents\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the files are written\n */\n async writeFiles(\n files: { path: string; content: string | Uint8Array; mode?: number }[],\n opts?: { signal?: AbortSignal },\n ) {\n \"use step\";\n const client = await this.ensureClient();\n return client.writeFiles({\n sessionId: this.session.id,\n cwd: this.session.cwd,\n extractDir: \"/\",\n files: files,\n signal: opts?.signal,\n });\n }\n\n /**\n * Get the public domain of a port of this session.\n *\n * @param p - Port number to resolve\n * @returns A full domain (e.g. `https://subdomain.vercel.run`)\n * @throws If the port has no associated route\n */\n domain(p: number): string {\n const route = this.routes.find(({ port }) => port == p);\n if (route) {\n return `https://${route.subdomain}.vercel.run`;\n } else {\n throw new Error(`No route for port ${p}`);\n }\n }\n\n /**\n * Stop this session.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns The final session state and optional sandbox metadata.\n */\n async stop(opts?: {\n signal?: AbortSignal;\n }): Promise<{ session: SandboxSnapshot; sandbox?: SandboxMetaData; snapshot?: SnapshotMetadata }> {\n \"use step\";\n const client = await this.ensureClient();\n const response = await client.stopSession({\n sessionId: this.session.id,\n signal: opts?.signal,\n });\n this.session = toSandboxSnapshot(response.json.session);\n return { session: this.session, sandbox: response.json.sandbox, snapshot: response.json.snapshot };\n }\n\n /**\n * Update the current session's settings.\n *\n * @param params - Fields to update.\n * @param params.networkPolicy - The new network policy to apply.\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n *\n * @example\n * // Restrict to specific domains\n * await session.update({\n * networkPolicy: {\n * allow: [\"*.npmjs.org\", \"github.com\"],\n * }\n * });\n *\n * @example\n * // Inject credentials with per-domain transformers\n * await session.update({\n * networkPolicy: {\n * allow: {\n * \"ai-gateway.vercel.sh\": [{\n * transform: [{\n * headers: { authorization: \"Bearer ...\" }\n * }]\n * }],\n * \"*\": []\n * }\n * }\n * });\n *\n * @example\n * // Deny all network access\n * await session.update({ networkPolicy: \"deny-all\" });\n */\n async update(\n params: {\n networkPolicy?: NetworkPolicy;\n },\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n if (params.networkPolicy !== undefined) {\n const client = await this.ensureClient();\n const response = await client.updateNetworkPolicy({\n sessionId: this.session.id,\n networkPolicy: params.networkPolicy,\n signal: opts?.signal,\n });\n\n // Update the internal session with the new network policy\n this.session = toSandboxSnapshot(response.json.session);\n }\n }\n\n /**\n * Extend the timeout of the session by the specified duration.\n *\n * This allows you to extend the lifetime of a session up until the maximum\n * execution timeout for your plan.\n *\n * @param duration - The duration in milliseconds to extend the timeout by\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves when the timeout is extended\n *\n * @example\n * const sandbox = await Sandbox.create({ timeout: ms('10m') });\n * const session = sandbox.currentSession();\n * // Extends timeout by 5 minutes, to a total of 15 minutes.\n * await session.extendTimeout(ms('5m'));\n */\n async extendTimeout(\n duration: number,\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n const response = await client.extendTimeout({\n sessionId: this.session.id,\n duration,\n signal: opts?.signal,\n });\n\n // Update the internal sandbox metadata with the new timeout value\n this.session = toSandboxSnapshot(response.json.session);\n }\n\n /**\n * Create a snapshot from this currently running session. New sandboxes can\n * then be created from this snapshot using {@link Sandbox.create}.\n *\n * Note: this session will be stopped as part of the snapshot creation process.\n *\n * @param opts - Optional parameters.\n * @param opts.expiration - Optional expiration time in milliseconds. Use 0 for no expiration at all.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves to the Snapshot instance\n */\n async snapshot(opts?: {\n expiration?: number;\n signal?: AbortSignal;\n }): Promise<Snapshot> {\n \"use step\";\n const client = await this.ensureClient();\n const response = await client.createSnapshot({\n sessionId: this.session.id,\n expiration: opts?.expiration,\n signal: opts?.signal,\n });\n\n this.session = toSandboxSnapshot(response.json.session);\n\n return new Snapshot({\n client,\n snapshot: response.json.snapshot,\n });\n }\n}\n"],"mappings":"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"}

@@ -0,4 +1,7 @@

const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
const require_api_client = require('./api-client/api-client.cjs');
require('./api-client/index.cjs');
const require_get_credentials = require('./utils/get-credentials.cjs');
const require_paginator = require('./utils/paginator.cjs');
let __workflow_serde = require("@workflow/serde");

@@ -14,2 +17,18 @@ //#region src/snapshot.ts

/**
* Lazily resolve credentials and construct an API client.
* This is used in step contexts where the Snapshot was deserialized
* without a client (e.g. when crossing workflow/step boundaries).
* @internal
*/
async ensureClient() {
"use step";
if (this._client) return this._client;
const credentials = await require_get_credentials.getCredentials();
this._client = new require_api_client.APIClient({
teamId: credentials.teamId,
token: credentials.token
});
return this._client;
}
/**
* Unique ID of this snapshot.

@@ -52,9 +71,25 @@ */

/**
* Create a new Snapshot instance.
* Serialize a Snapshot instance to plain data for @workflow/serde.
*
* @param client - API client used to communicate with the backend
* @param snapshot - Snapshot metadata
* @param instance - The Snapshot instance to serialize
* @returns A plain object containing snapshot metadata
*/
static [__workflow_serde.WORKFLOW_SERIALIZE](instance) {
return { snapshot: instance.snapshot };
}
/**
* Deserialize a Snapshot from serialized data.
*
* The deserialized instance uses the serialized metadata synchronously and
* lazily creates an API client only when methods perform API requests.
*
* @param data - The serialized snapshot data
* @returns The reconstructed Snapshot instance
*/
static [__workflow_serde.WORKFLOW_DESERIALIZE](data) {
return new Snapshot({ snapshot: data.snapshot });
}
constructor({ client, snapshot }) {
this.client = client;
this._client = null;
this._client = client ?? null;
this.snapshot = snapshot;

@@ -66,2 +101,11 @@ }

* the next page of results.
*
* The returned object is async-iterable to auto-paginate through all pages:
*
* ```ts
* const result = await Snapshot.list({ name: "my-sandbox" });
* for await (const snapshot of result) { ... }
* // or: await result.toArray();
* // or: for await (const page of result.pages()) { ... }
* ```
*/

@@ -71,10 +115,19 @@ static async list(params) {

const credentials = await require_get_credentials.getCredentials(params);
return (await new require_api_client.APIClient({
const client = new require_api_client.APIClient({
teamId: credentials.teamId,
token: credentials.token,
fetch: params?.fetch
}).listSnapshots({
...credentials,
...params
})).json;
});
const fetchPage = async (cursor) => {
return (await client.listSnapshots({
...credentials,
...params,
...cursor !== void 0 && { cursor }
})).json;
};
return require_paginator.attachPaginator(await fetchPage(params?.cursor), {
itemsKey: "snapshots",
fetchNext: fetchPage,
signal: params?.signal
});
}

@@ -111,3 +164,3 @@ /**

"use step";
this.snapshot = (await this.client.deleteSnapshot({
this.snapshot = (await (await this.ensureClient()).deleteSnapshot({
snapshotId: this.snapshot.id,

@@ -114,0 +167,0 @@ signal: opts?.signal

@@ -1,1 +0,1 @@

{"version":3,"file":"snapshot.cjs","names":["getCredentials","APIClient"],"sources":["../src/snapshot.ts"],"sourcesContent":["import type { WithFetchOptions } from \"./api-client/api-client.js\";\nimport type { SnapshotMetadata } from \"./api-client/index.js\";\nimport { APIClient } from \"./api-client/index.js\";\nimport { type Credentials, getCredentials } from \"./utils/get-credentials.js\";\n\n/** @inline */\ninterface GetSnapshotParams {\n /**\n * Unique identifier of the snapshot.\n */\n snapshotId: string;\n /**\n * An AbortSignal to cancel the operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * A Snapshot is a saved state of a Sandbox that can be used to create new Sandboxes\n *\n * Use {@link Sandbox.snapshot} or {@link Snapshot.get} to construct.\n * @hideconstructor\n */\nexport class Snapshot {\n private readonly client: APIClient;\n\n /**\n * Unique ID of this snapshot.\n */\n public get snapshotId(): string {\n return this.snapshot.id;\n }\n\n /**\n * The ID of the session from which this snapshot was created.\n */\n public get sourceSessionId(): string {\n return this.snapshot.sourceSessionId;\n }\n\n /**\n * The status of the snapshot.\n */\n public get status(): SnapshotMetadata[\"status\"] {\n return this.snapshot.status;\n }\n\n /**\n * The size of the snapshot in bytes, or null if not available.\n */\n public get sizeBytes(): number {\n return this.snapshot.sizeBytes;\n }\n\n /**\n * The creation date of this snapshot.\n */\n public get createdAt(): Date {\n return new Date(this.snapshot.createdAt);\n }\n\n /**\n * The expiration date of this snapshot, or undefined if it does not expire.\n */\n public get expiresAt(): Date | undefined {\n if (this.snapshot.expiresAt === undefined) {\n return undefined;\n }\n\n return new Date(this.snapshot.expiresAt);\n }\n\n /**\n * Internal metadata about this snapshot.\n */\n private snapshot: SnapshotMetadata;\n\n /**\n * Create a new Snapshot instance.\n *\n * @param client - API client used to communicate with the backend\n * @param snapshot - Snapshot metadata\n */\n constructor({\n client,\n snapshot,\n }: {\n client: APIClient;\n snapshot: SnapshotMetadata;\n }) {\n this.client = client;\n this.snapshot = snapshot;\n }\n\n /**\n * Allow to get a list of snapshots for a team narrowed to the given params.\n * It returns both the snapshots and the pagination metadata to allow getting\n * the next page of results.\n */\n static async list(\n params?: Partial<Parameters<APIClient[\"listSnapshots\"]>[0]> &\n Partial<Credentials> &\n WithFetchOptions,\n ) {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n fetch: params?.fetch,\n });\n const response = await client.listSnapshots({\n ...credentials,\n ...params,\n });\n return response.json;\n }\n\n /**\n * Retrieve an existing snapshot.\n *\n * @param params - Get parameters and optional credentials.\n * @returns A promise resolving to the {@link Sandbox}.\n */\n static async get(\n params: GetSnapshotParams | (GetSnapshotParams & Credentials),\n ): Promise<Snapshot> {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n });\n\n const sandbox = await client.getSnapshot({\n snapshotId: params.snapshotId,\n signal: params.signal,\n });\n\n return new Snapshot({\n client,\n snapshot: sandbox.json.snapshot,\n });\n }\n\n /**\n * Delete this snapshot.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves once the snapshot has been deleted.\n */\n async delete(opts?: { signal?: AbortSignal }): Promise<void> {\n \"use step\";\n const response = await this.client!.deleteSnapshot({\n snapshotId: this.snapshot.id,\n signal: opts?.signal,\n });\n\n this.snapshot = response.json.snapshot;\n }\n}\n"],"mappings":";;;;;;;;;;;AAuBA,IAAa,WAAb,MAAa,SAAS;;;;CAMpB,IAAW,aAAqB;AAC9B,SAAO,KAAK,SAAS;;;;;CAMvB,IAAW,kBAA0B;AACnC,SAAO,KAAK,SAAS;;;;;CAMvB,IAAW,SAAqC;AAC9C,SAAO,KAAK,SAAS;;;;;CAMvB,IAAW,YAAoB;AAC7B,SAAO,KAAK,SAAS;;;;;CAMvB,IAAW,YAAkB;AAC3B,SAAO,IAAI,KAAK,KAAK,SAAS,UAAU;;;;;CAM1C,IAAW,YAA8B;AACvC,MAAI,KAAK,SAAS,cAAc,OAC9B;AAGF,SAAO,IAAI,KAAK,KAAK,SAAS,UAAU;;;;;;;;CAc1C,YAAY,EACV,QACA,YAIC;AACD,OAAK,SAAS;AACd,OAAK,WAAW;;;;;;;CAQlB,aAAa,KACX,QAGA;AACA;EACA,MAAM,cAAc,MAAMA,uCAAe,OAAO;AAUhD,UAJiB,MALF,IAAIC,6BAAU;GAC3B,QAAQ,YAAY;GACpB,OAAO,YAAY;GACnB,OAAO,QAAQ;GAChB,CAAC,CAC4B,cAAc;GAC1C,GAAG;GACH,GAAG;GACJ,CAAC,EACc;;;;;;;;CASlB,aAAa,IACX,QACmB;AACnB;EACA,MAAM,cAAc,MAAMD,uCAAe,OAAO;EAChD,MAAM,SAAS,IAAIC,6BAAU;GAC3B,QAAQ,YAAY;GACpB,OAAO,YAAY;GACpB,CAAC;AAOF,SAAO,IAAI,SAAS;GAClB;GACA,WAPc,MAAM,OAAO,YAAY;IACvC,YAAY,OAAO;IACnB,QAAQ,OAAO;IAChB,CAAC,EAIkB,KAAK;GACxB,CAAC;;;;;;;;;CAUJ,MAAM,OAAO,MAAgD;AAC3D;AAMA,OAAK,YALY,MAAM,KAAK,OAAQ,eAAe;GACjD,YAAY,KAAK,SAAS;GAC1B,QAAQ,MAAM;GACf,CAAC,EAEuB,KAAK"}
{"version":3,"file":"snapshot.cjs","names":["getCredentials","APIClient","WORKFLOW_SERIALIZE","WORKFLOW_DESERIALIZE","attachPaginator"],"sources":["../src/snapshot.ts"],"sourcesContent":["import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport type { WithFetchOptions } from \"./api-client/api-client.js\";\nimport type { SnapshotMetadata } from \"./api-client/index.js\";\nimport { APIClient } from \"./api-client/index.js\";\nimport { type Credentials, getCredentials } from \"./utils/get-credentials.js\";\nimport { attachPaginator } from \"./utils/paginator.js\";\n\nexport interface SerializedSnapshot {\n snapshot: SnapshotMetadata;\n}\n\n/** @inline */\ninterface GetSnapshotParams {\n /**\n * Unique identifier of the snapshot.\n */\n snapshotId: string;\n /**\n * An AbortSignal to cancel the operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * A Snapshot is a saved state of a Sandbox that can be used to create new Sandboxes\n *\n * Use {@link Sandbox.snapshot} or {@link Snapshot.get} to construct.\n * @hideconstructor\n */\nexport class Snapshot {\n private _client: APIClient | null = null;\n\n /**\n * Lazily resolve credentials and construct an API client.\n * This is used in step contexts where the Snapshot was deserialized\n * without a client (e.g. when crossing workflow/step boundaries).\n * @internal\n */\n private async ensureClient(): Promise<APIClient> {\n \"use step\";\n if (this._client) return this._client;\n const credentials = await getCredentials();\n this._client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n });\n return this._client;\n }\n\n /**\n * Unique ID of this snapshot.\n */\n public get snapshotId(): string {\n return this.snapshot.id;\n }\n\n /**\n * The ID of the session from which this snapshot was created.\n */\n public get sourceSessionId(): string {\n return this.snapshot.sourceSessionId;\n }\n\n /**\n * The status of the snapshot.\n */\n public get status(): SnapshotMetadata[\"status\"] {\n return this.snapshot.status;\n }\n\n /**\n * The size of the snapshot in bytes, or null if not available.\n */\n public get sizeBytes(): number {\n return this.snapshot.sizeBytes;\n }\n\n /**\n * The creation date of this snapshot.\n */\n public get createdAt(): Date {\n return new Date(this.snapshot.createdAt);\n }\n\n /**\n * The expiration date of this snapshot, or undefined if it does not expire.\n */\n public get expiresAt(): Date | undefined {\n if (this.snapshot.expiresAt === undefined) {\n return undefined;\n }\n\n return new Date(this.snapshot.expiresAt);\n }\n\n /**\n * Internal metadata about this snapshot.\n */\n private snapshot: SnapshotMetadata;\n\n /**\n * Serialize a Snapshot instance to plain data for @workflow/serde.\n *\n * @param instance - The Snapshot instance to serialize\n * @returns A plain object containing snapshot metadata\n */\n static [WORKFLOW_SERIALIZE](instance: Snapshot): SerializedSnapshot {\n return {\n snapshot: instance.snapshot,\n };\n }\n\n /**\n * Deserialize a Snapshot from serialized data.\n *\n * The deserialized instance uses the serialized metadata synchronously and\n * lazily creates an API client only when methods perform API requests.\n *\n * @param data - The serialized snapshot data\n * @returns The reconstructed Snapshot instance\n */\n static [WORKFLOW_DESERIALIZE](data: SerializedSnapshot): Snapshot {\n return new Snapshot({\n snapshot: data.snapshot,\n });\n }\n\n constructor({\n client,\n snapshot,\n }: {\n client?: APIClient;\n snapshot: SnapshotMetadata;\n }) {\n this._client = client ?? null;\n this.snapshot = snapshot;\n }\n\n /**\n * Allow to get a list of snapshots for a team narrowed to the given params.\n * It returns both the snapshots and the pagination metadata to allow getting\n * the next page of results.\n *\n * The returned object is async-iterable to auto-paginate through all pages:\n *\n * ```ts\n * const result = await Snapshot.list({ name: \"my-sandbox\" });\n * for await (const snapshot of result) { ... }\n * // or: await result.toArray();\n * // or: for await (const page of result.pages()) { ... }\n * ```\n */\n static async list(\n params?: Partial<Parameters<APIClient[\"listSnapshots\"]>[0]> &\n Partial<Credentials> &\n WithFetchOptions,\n ) {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n fetch: params?.fetch,\n });\n const fetchPage = async (cursor?: string) => {\n const response = await client.listSnapshots({\n ...credentials,\n ...params,\n ...(cursor !== undefined && { cursor }),\n });\n return response.json;\n };\n const firstPage = await fetchPage(params?.cursor);\n return attachPaginator(firstPage, {\n itemsKey: \"snapshots\",\n fetchNext: fetchPage,\n signal: params?.signal,\n });\n }\n\n /**\n * Retrieve an existing snapshot.\n *\n * @param params - Get parameters and optional credentials.\n * @returns A promise resolving to the {@link Sandbox}.\n */\n static async get(\n params: GetSnapshotParams | (GetSnapshotParams & Credentials),\n ): Promise<Snapshot> {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n });\n\n const sandbox = await client.getSnapshot({\n snapshotId: params.snapshotId,\n signal: params.signal,\n });\n\n return new Snapshot({\n client,\n snapshot: sandbox.json.snapshot,\n });\n }\n\n /**\n * Delete this snapshot.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves once the snapshot has been deleted.\n */\n async delete(opts?: { signal?: AbortSignal }): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n const response = await client.deleteSnapshot({\n snapshotId: this.snapshot.id,\n signal: opts?.signal,\n });\n\n this.snapshot = response.json.snapshot;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AA6BA,IAAa,WAAb,MAAa,SAAS;;;;;;;CASpB,MAAc,eAAmC;AAC/C;AACA,MAAI,KAAK,QAAS,QAAO,KAAK;EAC9B,MAAM,cAAc,MAAMA,wCAAgB;AAC1C,OAAK,UAAU,IAAIC,6BAAU;GAC3B,QAAQ,YAAY;GACpB,OAAO,YAAY;GACpB,CAAC;AACF,SAAO,KAAK;;;;;CAMd,IAAW,aAAqB;AAC9B,SAAO,KAAK,SAAS;;;;;CAMvB,IAAW,kBAA0B;AACnC,SAAO,KAAK,SAAS;;;;;CAMvB,IAAW,SAAqC;AAC9C,SAAO,KAAK,SAAS;;;;;CAMvB,IAAW,YAAoB;AAC7B,SAAO,KAAK,SAAS;;;;;CAMvB,IAAW,YAAkB;AAC3B,SAAO,IAAI,KAAK,KAAK,SAAS,UAAU;;;;;CAM1C,IAAW,YAA8B;AACvC,MAAI,KAAK,SAAS,cAAc,OAC9B;AAGF,SAAO,IAAI,KAAK,KAAK,SAAS,UAAU;;;;;;;;CAc1C,QAAQC,qCAAoB,UAAwC;AAClE,SAAO,EACL,UAAU,SAAS,UACpB;;;;;;;;;;;CAYH,QAAQC,uCAAsB,MAAoC;AAChE,SAAO,IAAI,SAAS,EAClB,UAAU,KAAK,UAChB,CAAC;;CAGJ,YAAY,EACV,QACA,YAIC;OAvGK,UAA4B;AAwGlC,OAAK,UAAU,UAAU;AACzB,OAAK,WAAW;;;;;;;;;;;;;;;;CAiBlB,aAAa,KACX,QAGA;AACA;EACA,MAAM,cAAc,MAAMH,uCAAe,OAAO;EAChD,MAAM,SAAS,IAAIC,6BAAU;GAC3B,QAAQ,YAAY;GACpB,OAAO,YAAY;GACnB,OAAO,QAAQ;GAChB,CAAC;EACF,MAAM,YAAY,OAAO,WAAoB;AAM3C,WALiB,MAAM,OAAO,cAAc;IAC1C,GAAG;IACH,GAAG;IACH,GAAI,WAAW,UAAa,EAAE,QAAQ;IACvC,CAAC,EACc;;AAGlB,SAAOG,kCADW,MAAM,UAAU,QAAQ,OAAO,EACf;GAChC,UAAU;GACV,WAAW;GACX,QAAQ,QAAQ;GACjB,CAAC;;;;;;;;CASJ,aAAa,IACX,QACmB;AACnB;EACA,MAAM,cAAc,MAAMJ,uCAAe,OAAO;EAChD,MAAM,SAAS,IAAIC,6BAAU;GAC3B,QAAQ,YAAY;GACpB,OAAO,YAAY;GACpB,CAAC;AAOF,SAAO,IAAI,SAAS;GAClB;GACA,WAPc,MAAM,OAAO,YAAY;IACvC,YAAY,OAAO;IACnB,QAAQ,OAAO;IAChB,CAAC,EAIkB,KAAK;GACxB,CAAC;;;;;;;;;CAUJ,MAAM,OAAO,MAAgD;AAC3D;AAOA,OAAK,YALY,OADF,MAAM,KAAK,cAAc,EACV,eAAe;GAC3C,YAAY,KAAK,SAAS;GAC1B,QAAQ,MAAM;GACf,CAAC,EAEuB,KAAK"}

@@ -0,6 +1,11 @@

import { Paginator } from "./utils/paginator.cjs";
import { SnapshotMetadata } from "./api-client/validators.cjs";
import { APIClient, WithFetchOptions } from "./api-client/api-client.cjs";
import { Credentials } from "./utils/get-credentials.cjs";
import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from "@workflow/serde";
//#region src/snapshot.d.ts
interface SerializedSnapshot {
snapshot: SnapshotMetadata;
}
/** @inline */

@@ -24,4 +29,11 @@ interface GetSnapshotParams {

declare class Snapshot {
private readonly client;
private _client;
/**
* Lazily resolve credentials and construct an API client.
* This is used in step contexts where the Snapshot was deserialized
* without a client (e.g. when crossing workflow/step boundaries).
* @internal
*/
private ensureClient;
/**
* Unique ID of this snapshot.

@@ -55,7 +67,18 @@ */

/**
* Create a new Snapshot instance.
* Serialize a Snapshot instance to plain data for @workflow/serde.
*
* @param client - API client used to communicate with the backend
* @param snapshot - Snapshot metadata
* @param instance - The Snapshot instance to serialize
* @returns A plain object containing snapshot metadata
*/
static [WORKFLOW_SERIALIZE](instance: Snapshot): SerializedSnapshot;
/**
* Deserialize a Snapshot from serialized data.
*
* The deserialized instance uses the serialized metadata synchronously and
* lazily creates an API client only when methods perform API requests.
*
* @param data - The serialized snapshot data
* @returns The reconstructed Snapshot instance
*/
static [WORKFLOW_DESERIALIZE](data: SerializedSnapshot): Snapshot;
constructor({

@@ -65,3 +88,3 @@ client,

}: {
client: APIClient;
client?: APIClient;
snapshot: SnapshotMetadata;

@@ -73,4 +96,13 @@ });

* the next page of results.
*
* The returned object is async-iterable to auto-paginate through all pages:
*
* ```ts
* const result = await Snapshot.list({ name: "my-sandbox" });
* for await (const snapshot of result) { ... }
* // or: await result.toArray();
* // or: for await (const page of result.pages()) { ... }
* ```
*/
static list(params?: Partial<Parameters<APIClient["listSnapshots"]>[0]> & Partial<Credentials> & WithFetchOptions): Promise<{
static list(params?: Partial<Parameters<APIClient["listSnapshots"]>[0]> & Partial<Credentials> & WithFetchOptions): Promise<Paginator<{
pagination: {

@@ -82,6 +114,6 @@ count: number;

status: "failed" | "created" | "deleted";
id: string;
region: string;
createdAt: number;
updatedAt: number;
id: string;
sourceSessionId: string;

@@ -91,3 +123,3 @@ sizeBytes: number;

}[];
}>;
}, "snapshots">>;
/**

@@ -112,3 +144,3 @@ * Retrieve an existing snapshot.

//#endregion
export { Snapshot };
export { SerializedSnapshot, Snapshot };
//# sourceMappingURL=snapshot.d.cts.map

@@ -0,1 +1,2 @@

import { Paginator } from "./utils/paginator.js";
import { SnapshotMetadata } from "./api-client/validators.js";

@@ -5,4 +6,8 @@ import { APIClient, WithFetchOptions } from "./api-client/api-client.js";

import { Credentials } from "./utils/get-credentials.js";
import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from "@workflow/serde";
//#region src/snapshot.d.ts
interface SerializedSnapshot {
snapshot: SnapshotMetadata;
}
/** @inline */

@@ -26,4 +31,11 @@ interface GetSnapshotParams {

declare class Snapshot {
private readonly client;
private _client;
/**
* Lazily resolve credentials and construct an API client.
* This is used in step contexts where the Snapshot was deserialized
* without a client (e.g. when crossing workflow/step boundaries).
* @internal
*/
private ensureClient;
/**
* Unique ID of this snapshot.

@@ -57,7 +69,18 @@ */

/**
* Create a new Snapshot instance.
* Serialize a Snapshot instance to plain data for @workflow/serde.
*
* @param client - API client used to communicate with the backend
* @param snapshot - Snapshot metadata
* @param instance - The Snapshot instance to serialize
* @returns A plain object containing snapshot metadata
*/
static [WORKFLOW_SERIALIZE](instance: Snapshot): SerializedSnapshot;
/**
* Deserialize a Snapshot from serialized data.
*
* The deserialized instance uses the serialized metadata synchronously and
* lazily creates an API client only when methods perform API requests.
*
* @param data - The serialized snapshot data
* @returns The reconstructed Snapshot instance
*/
static [WORKFLOW_DESERIALIZE](data: SerializedSnapshot): Snapshot;
constructor({

@@ -67,3 +90,3 @@ client,

}: {
client: APIClient;
client?: APIClient;
snapshot: SnapshotMetadata;

@@ -75,4 +98,13 @@ });

* the next page of results.
*
* The returned object is async-iterable to auto-paginate through all pages:
*
* ```ts
* const result = await Snapshot.list({ name: "my-sandbox" });
* for await (const snapshot of result) { ... }
* // or: await result.toArray();
* // or: for await (const page of result.pages()) { ... }
* ```
*/
static list(params?: Partial<Parameters<APIClient["listSnapshots"]>[0]> & Partial<Credentials> & WithFetchOptions): Promise<{
static list(params?: Partial<Parameters<APIClient["listSnapshots"]>[0]> & Partial<Credentials> & WithFetchOptions): Promise<Paginator<{
pagination: {

@@ -84,6 +116,6 @@ count: number;

status: "failed" | "created" | "deleted";
id: string;
region: string;
createdAt: number;
updatedAt: number;
id: string;
sourceSessionId: string;

@@ -93,3 +125,3 @@ sizeBytes: number;

}[];
}>;
}, "snapshots">>;
/**

@@ -114,3 +146,3 @@ * Retrieve an existing snapshot.

//#endregion
export { Snapshot };
export { SerializedSnapshot, Snapshot };
//# sourceMappingURL=snapshot.d.ts.map
import { APIClient } from "./api-client/api-client.js";
import "./api-client/index.js";
import { getCredentials } from "./utils/get-credentials.js";
import { attachPaginator } from "./utils/paginator.js";
import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from "@workflow/serde";

@@ -14,2 +16,18 @@ //#region src/snapshot.ts

/**
* Lazily resolve credentials and construct an API client.
* This is used in step contexts where the Snapshot was deserialized
* without a client (e.g. when crossing workflow/step boundaries).
* @internal
*/
async ensureClient() {
"use step";
if (this._client) return this._client;
const credentials = await getCredentials();
this._client = new APIClient({
teamId: credentials.teamId,
token: credentials.token
});
return this._client;
}
/**
* Unique ID of this snapshot.

@@ -52,9 +70,25 @@ */

/**
* Create a new Snapshot instance.
* Serialize a Snapshot instance to plain data for @workflow/serde.
*
* @param client - API client used to communicate with the backend
* @param snapshot - Snapshot metadata
* @param instance - The Snapshot instance to serialize
* @returns A plain object containing snapshot metadata
*/
static [WORKFLOW_SERIALIZE](instance) {
return { snapshot: instance.snapshot };
}
/**
* Deserialize a Snapshot from serialized data.
*
* The deserialized instance uses the serialized metadata synchronously and
* lazily creates an API client only when methods perform API requests.
*
* @param data - The serialized snapshot data
* @returns The reconstructed Snapshot instance
*/
static [WORKFLOW_DESERIALIZE](data) {
return new Snapshot({ snapshot: data.snapshot });
}
constructor({ client, snapshot }) {
this.client = client;
this._client = null;
this._client = client ?? null;
this.snapshot = snapshot;

@@ -66,2 +100,11 @@ }

* the next page of results.
*
* The returned object is async-iterable to auto-paginate through all pages:
*
* ```ts
* const result = await Snapshot.list({ name: "my-sandbox" });
* for await (const snapshot of result) { ... }
* // or: await result.toArray();
* // or: for await (const page of result.pages()) { ... }
* ```
*/

@@ -71,10 +114,19 @@ static async list(params) {

const credentials = await getCredentials(params);
return (await new APIClient({
const client = new APIClient({
teamId: credentials.teamId,
token: credentials.token,
fetch: params?.fetch
}).listSnapshots({
...credentials,
...params
})).json;
});
const fetchPage = async (cursor) => {
return (await client.listSnapshots({
...credentials,
...params,
...cursor !== void 0 && { cursor }
})).json;
};
return attachPaginator(await fetchPage(params?.cursor), {
itemsKey: "snapshots",
fetchNext: fetchPage,
signal: params?.signal
});
}

@@ -111,3 +163,3 @@ /**

"use step";
this.snapshot = (await this.client.deleteSnapshot({
this.snapshot = (await (await this.ensureClient()).deleteSnapshot({
snapshotId: this.snapshot.id,

@@ -114,0 +166,0 @@ signal: opts?.signal

@@ -1,1 +0,1 @@

{"version":3,"file":"snapshot.js","names":[],"sources":["../src/snapshot.ts"],"sourcesContent":["import type { WithFetchOptions } from \"./api-client/api-client.js\";\nimport type { SnapshotMetadata } from \"./api-client/index.js\";\nimport { APIClient } from \"./api-client/index.js\";\nimport { type Credentials, getCredentials } from \"./utils/get-credentials.js\";\n\n/** @inline */\ninterface GetSnapshotParams {\n /**\n * Unique identifier of the snapshot.\n */\n snapshotId: string;\n /**\n * An AbortSignal to cancel the operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * A Snapshot is a saved state of a Sandbox that can be used to create new Sandboxes\n *\n * Use {@link Sandbox.snapshot} or {@link Snapshot.get} to construct.\n * @hideconstructor\n */\nexport class Snapshot {\n private readonly client: APIClient;\n\n /**\n * Unique ID of this snapshot.\n */\n public get snapshotId(): string {\n return this.snapshot.id;\n }\n\n /**\n * The ID of the session from which this snapshot was created.\n */\n public get sourceSessionId(): string {\n return this.snapshot.sourceSessionId;\n }\n\n /**\n * The status of the snapshot.\n */\n public get status(): SnapshotMetadata[\"status\"] {\n return this.snapshot.status;\n }\n\n /**\n * The size of the snapshot in bytes, or null if not available.\n */\n public get sizeBytes(): number {\n return this.snapshot.sizeBytes;\n }\n\n /**\n * The creation date of this snapshot.\n */\n public get createdAt(): Date {\n return new Date(this.snapshot.createdAt);\n }\n\n /**\n * The expiration date of this snapshot, or undefined if it does not expire.\n */\n public get expiresAt(): Date | undefined {\n if (this.snapshot.expiresAt === undefined) {\n return undefined;\n }\n\n return new Date(this.snapshot.expiresAt);\n }\n\n /**\n * Internal metadata about this snapshot.\n */\n private snapshot: SnapshotMetadata;\n\n /**\n * Create a new Snapshot instance.\n *\n * @param client - API client used to communicate with the backend\n * @param snapshot - Snapshot metadata\n */\n constructor({\n client,\n snapshot,\n }: {\n client: APIClient;\n snapshot: SnapshotMetadata;\n }) {\n this.client = client;\n this.snapshot = snapshot;\n }\n\n /**\n * Allow to get a list of snapshots for a team narrowed to the given params.\n * It returns both the snapshots and the pagination metadata to allow getting\n * the next page of results.\n */\n static async list(\n params?: Partial<Parameters<APIClient[\"listSnapshots\"]>[0]> &\n Partial<Credentials> &\n WithFetchOptions,\n ) {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n fetch: params?.fetch,\n });\n const response = await client.listSnapshots({\n ...credentials,\n ...params,\n });\n return response.json;\n }\n\n /**\n * Retrieve an existing snapshot.\n *\n * @param params - Get parameters and optional credentials.\n * @returns A promise resolving to the {@link Sandbox}.\n */\n static async get(\n params: GetSnapshotParams | (GetSnapshotParams & Credentials),\n ): Promise<Snapshot> {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n });\n\n const sandbox = await client.getSnapshot({\n snapshotId: params.snapshotId,\n signal: params.signal,\n });\n\n return new Snapshot({\n client,\n snapshot: sandbox.json.snapshot,\n });\n }\n\n /**\n * Delete this snapshot.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves once the snapshot has been deleted.\n */\n async delete(opts?: { signal?: AbortSignal }): Promise<void> {\n \"use step\";\n const response = await this.client!.deleteSnapshot({\n snapshotId: this.snapshot.id,\n signal: opts?.signal,\n });\n\n this.snapshot = response.json.snapshot;\n }\n}\n"],"mappings":";;;;;;;;;;;AAuBA,IAAa,WAAb,MAAa,SAAS;;;;CAMpB,IAAW,aAAqB;AAC9B,SAAO,KAAK,SAAS;;;;;CAMvB,IAAW,kBAA0B;AACnC,SAAO,KAAK,SAAS;;;;;CAMvB,IAAW,SAAqC;AAC9C,SAAO,KAAK,SAAS;;;;;CAMvB,IAAW,YAAoB;AAC7B,SAAO,KAAK,SAAS;;;;;CAMvB,IAAW,YAAkB;AAC3B,SAAO,IAAI,KAAK,KAAK,SAAS,UAAU;;;;;CAM1C,IAAW,YAA8B;AACvC,MAAI,KAAK,SAAS,cAAc,OAC9B;AAGF,SAAO,IAAI,KAAK,KAAK,SAAS,UAAU;;;;;;;;CAc1C,YAAY,EACV,QACA,YAIC;AACD,OAAK,SAAS;AACd,OAAK,WAAW;;;;;;;CAQlB,aAAa,KACX,QAGA;AACA;EACA,MAAM,cAAc,MAAM,eAAe,OAAO;AAUhD,UAJiB,MALF,IAAI,UAAU;GAC3B,QAAQ,YAAY;GACpB,OAAO,YAAY;GACnB,OAAO,QAAQ;GAChB,CAAC,CAC4B,cAAc;GAC1C,GAAG;GACH,GAAG;GACJ,CAAC,EACc;;;;;;;;CASlB,aAAa,IACX,QACmB;AACnB;EACA,MAAM,cAAc,MAAM,eAAe,OAAO;EAChD,MAAM,SAAS,IAAI,UAAU;GAC3B,QAAQ,YAAY;GACpB,OAAO,YAAY;GACpB,CAAC;AAOF,SAAO,IAAI,SAAS;GAClB;GACA,WAPc,MAAM,OAAO,YAAY;IACvC,YAAY,OAAO;IACnB,QAAQ,OAAO;IAChB,CAAC,EAIkB,KAAK;GACxB,CAAC;;;;;;;;;CAUJ,MAAM,OAAO,MAAgD;AAC3D;AAMA,OAAK,YALY,MAAM,KAAK,OAAQ,eAAe;GACjD,YAAY,KAAK,SAAS;GAC1B,QAAQ,MAAM;GACf,CAAC,EAEuB,KAAK"}
{"version":3,"file":"snapshot.js","names":[],"sources":["../src/snapshot.ts"],"sourcesContent":["import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport type { WithFetchOptions } from \"./api-client/api-client.js\";\nimport type { SnapshotMetadata } from \"./api-client/index.js\";\nimport { APIClient } from \"./api-client/index.js\";\nimport { type Credentials, getCredentials } from \"./utils/get-credentials.js\";\nimport { attachPaginator } from \"./utils/paginator.js\";\n\nexport interface SerializedSnapshot {\n snapshot: SnapshotMetadata;\n}\n\n/** @inline */\ninterface GetSnapshotParams {\n /**\n * Unique identifier of the snapshot.\n */\n snapshotId: string;\n /**\n * An AbortSignal to cancel the operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * A Snapshot is a saved state of a Sandbox that can be used to create new Sandboxes\n *\n * Use {@link Sandbox.snapshot} or {@link Snapshot.get} to construct.\n * @hideconstructor\n */\nexport class Snapshot {\n private _client: APIClient | null = null;\n\n /**\n * Lazily resolve credentials and construct an API client.\n * This is used in step contexts where the Snapshot was deserialized\n * without a client (e.g. when crossing workflow/step boundaries).\n * @internal\n */\n private async ensureClient(): Promise<APIClient> {\n \"use step\";\n if (this._client) return this._client;\n const credentials = await getCredentials();\n this._client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n });\n return this._client;\n }\n\n /**\n * Unique ID of this snapshot.\n */\n public get snapshotId(): string {\n return this.snapshot.id;\n }\n\n /**\n * The ID of the session from which this snapshot was created.\n */\n public get sourceSessionId(): string {\n return this.snapshot.sourceSessionId;\n }\n\n /**\n * The status of the snapshot.\n */\n public get status(): SnapshotMetadata[\"status\"] {\n return this.snapshot.status;\n }\n\n /**\n * The size of the snapshot in bytes, or null if not available.\n */\n public get sizeBytes(): number {\n return this.snapshot.sizeBytes;\n }\n\n /**\n * The creation date of this snapshot.\n */\n public get createdAt(): Date {\n return new Date(this.snapshot.createdAt);\n }\n\n /**\n * The expiration date of this snapshot, or undefined if it does not expire.\n */\n public get expiresAt(): Date | undefined {\n if (this.snapshot.expiresAt === undefined) {\n return undefined;\n }\n\n return new Date(this.snapshot.expiresAt);\n }\n\n /**\n * Internal metadata about this snapshot.\n */\n private snapshot: SnapshotMetadata;\n\n /**\n * Serialize a Snapshot instance to plain data for @workflow/serde.\n *\n * @param instance - The Snapshot instance to serialize\n * @returns A plain object containing snapshot metadata\n */\n static [WORKFLOW_SERIALIZE](instance: Snapshot): SerializedSnapshot {\n return {\n snapshot: instance.snapshot,\n };\n }\n\n /**\n * Deserialize a Snapshot from serialized data.\n *\n * The deserialized instance uses the serialized metadata synchronously and\n * lazily creates an API client only when methods perform API requests.\n *\n * @param data - The serialized snapshot data\n * @returns The reconstructed Snapshot instance\n */\n static [WORKFLOW_DESERIALIZE](data: SerializedSnapshot): Snapshot {\n return new Snapshot({\n snapshot: data.snapshot,\n });\n }\n\n constructor({\n client,\n snapshot,\n }: {\n client?: APIClient;\n snapshot: SnapshotMetadata;\n }) {\n this._client = client ?? null;\n this.snapshot = snapshot;\n }\n\n /**\n * Allow to get a list of snapshots for a team narrowed to the given params.\n * It returns both the snapshots and the pagination metadata to allow getting\n * the next page of results.\n *\n * The returned object is async-iterable to auto-paginate through all pages:\n *\n * ```ts\n * const result = await Snapshot.list({ name: \"my-sandbox\" });\n * for await (const snapshot of result) { ... }\n * // or: await result.toArray();\n * // or: for await (const page of result.pages()) { ... }\n * ```\n */\n static async list(\n params?: Partial<Parameters<APIClient[\"listSnapshots\"]>[0]> &\n Partial<Credentials> &\n WithFetchOptions,\n ) {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n fetch: params?.fetch,\n });\n const fetchPage = async (cursor?: string) => {\n const response = await client.listSnapshots({\n ...credentials,\n ...params,\n ...(cursor !== undefined && { cursor }),\n });\n return response.json;\n };\n const firstPage = await fetchPage(params?.cursor);\n return attachPaginator(firstPage, {\n itemsKey: \"snapshots\",\n fetchNext: fetchPage,\n signal: params?.signal,\n });\n }\n\n /**\n * Retrieve an existing snapshot.\n *\n * @param params - Get parameters and optional credentials.\n * @returns A promise resolving to the {@link Sandbox}.\n */\n static async get(\n params: GetSnapshotParams | (GetSnapshotParams & Credentials),\n ): Promise<Snapshot> {\n \"use step\";\n const credentials = await getCredentials(params);\n const client = new APIClient({\n teamId: credentials.teamId,\n token: credentials.token,\n });\n\n const sandbox = await client.getSnapshot({\n snapshotId: params.snapshotId,\n signal: params.signal,\n });\n\n return new Snapshot({\n client,\n snapshot: sandbox.json.snapshot,\n });\n }\n\n /**\n * Delete this snapshot.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves once the snapshot has been deleted.\n */\n async delete(opts?: { signal?: AbortSignal }): Promise<void> {\n \"use step\";\n const client = await this.ensureClient();\n const response = await client.deleteSnapshot({\n snapshotId: this.snapshot.id,\n signal: opts?.signal,\n });\n\n this.snapshot = response.json.snapshot;\n }\n}\n"],"mappings":";;;;;;;;;;;;;AA6BA,IAAa,WAAb,MAAa,SAAS;;;;;;;CASpB,MAAc,eAAmC;AAC/C;AACA,MAAI,KAAK,QAAS,QAAO,KAAK;EAC9B,MAAM,cAAc,MAAM,gBAAgB;AAC1C,OAAK,UAAU,IAAI,UAAU;GAC3B,QAAQ,YAAY;GACpB,OAAO,YAAY;GACpB,CAAC;AACF,SAAO,KAAK;;;;;CAMd,IAAW,aAAqB;AAC9B,SAAO,KAAK,SAAS;;;;;CAMvB,IAAW,kBAA0B;AACnC,SAAO,KAAK,SAAS;;;;;CAMvB,IAAW,SAAqC;AAC9C,SAAO,KAAK,SAAS;;;;;CAMvB,IAAW,YAAoB;AAC7B,SAAO,KAAK,SAAS;;;;;CAMvB,IAAW,YAAkB;AAC3B,SAAO,IAAI,KAAK,KAAK,SAAS,UAAU;;;;;CAM1C,IAAW,YAA8B;AACvC,MAAI,KAAK,SAAS,cAAc,OAC9B;AAGF,SAAO,IAAI,KAAK,KAAK,SAAS,UAAU;;;;;;;;CAc1C,QAAQ,oBAAoB,UAAwC;AAClE,SAAO,EACL,UAAU,SAAS,UACpB;;;;;;;;;;;CAYH,QAAQ,sBAAsB,MAAoC;AAChE,SAAO,IAAI,SAAS,EAClB,UAAU,KAAK,UAChB,CAAC;;CAGJ,YAAY,EACV,QACA,YAIC;OAvGK,UAA4B;AAwGlC,OAAK,UAAU,UAAU;AACzB,OAAK,WAAW;;;;;;;;;;;;;;;;CAiBlB,aAAa,KACX,QAGA;AACA;EACA,MAAM,cAAc,MAAM,eAAe,OAAO;EAChD,MAAM,SAAS,IAAI,UAAU;GAC3B,QAAQ,YAAY;GACpB,OAAO,YAAY;GACnB,OAAO,QAAQ;GAChB,CAAC;EACF,MAAM,YAAY,OAAO,WAAoB;AAM3C,WALiB,MAAM,OAAO,cAAc;IAC1C,GAAG;IACH,GAAG;IACH,GAAI,WAAW,UAAa,EAAE,QAAQ;IACvC,CAAC,EACc;;AAGlB,SAAO,gBADW,MAAM,UAAU,QAAQ,OAAO,EACf;GAChC,UAAU;GACV,WAAW;GACX,QAAQ,QAAQ;GACjB,CAAC;;;;;;;;CASJ,aAAa,IACX,QACmB;AACnB;EACA,MAAM,cAAc,MAAM,eAAe,OAAO;EAChD,MAAM,SAAS,IAAI,UAAU;GAC3B,QAAQ,YAAY;GACpB,OAAO,YAAY;GACpB,CAAC;AAOF,SAAO,IAAI,SAAS;GAClB;GACA,WAPc,MAAM,OAAO,YAAY;IACvC,YAAY,OAAO;IACnB,QAAQ,OAAO;IAChB,CAAC,EAIkB,KAAK;GACxB,CAAC;;;;;;;;;CAUJ,MAAM,OAAO,MAAgD;AAC3D;AAOA,OAAK,YALY,OADF,MAAM,KAAK,cAAc,EACV,eAAe;GAC3C,YAAY,KAAK,SAAS;GAC1B,QAAQ,MAAM;GACf,CAAC,EAEuB,KAAK"}
//#region src/utils/network-policy.ts
function toAPINetworkPolicy(policy) {
if (policy === "allow-all") return { mode: "allow-all" };
if (policy === "deny-all") return { mode: "deny-all" };
if (policy.allow && !Array.isArray(policy.allow)) {
const allowedDomains = Object.keys(policy.allow);
const injectionRules = [];
for (const [domain, rules] of Object.entries(policy.allow)) {
const merged = {};
for (const rule of rules) for (const t of rule.transform ?? []) Object.assign(merged, t.headers);
if (Object.keys(merged).length > 0) injectionRules.push({
domain,
headers: merged
});
}
return {
mode: "custom",
...allowedDomains.length > 0 && { allowedDomains },
...injectionRules.length > 0 && { injectionRules },
...policy.subnets?.allow && { allowedCIDRs: policy.subnets.allow },
...policy.subnets?.deny && { deniedCIDRs: policy.subnets.deny }
};
}
return {
mode: "custom",
...policy.allow && { allowedDomains: policy.allow },
...policy.subnets?.allow && { allowedCIDRs: policy.subnets.allow },
...policy.subnets?.deny && { deniedCIDRs: policy.subnets.deny }
};
if (policy === "allow-all" || policy === "deny-all") return { mode: policy };
return policy;
}
function fromAPINetworkPolicy(api) {
if (api.mode === "allow-all") return "allow-all";
if (api.mode === "deny-all") return "deny-all";
if (api.mode === "allow-all" || api.mode === "deny-all") return api.mode;
const subnets = api.allowedCIDRs || api.deniedCIDRs ? { subnets: {

@@ -39,14 +13,24 @@ ...api.allowedCIDRs && { allow: api.allowedCIDRs },

} } : void 0;
if (api.injectionRules && api.injectionRules.length > 0) {
const rulesByDomain = new Map(api.injectionRules.map((r) => [r.domain, r.headerNames ?? []]));
const allow = {};
for (const domain of api.allowedDomains ?? []) {
const headerNames = rulesByDomain.get(domain);
if (headerNames && headerNames.length > 0) allow[domain] = [{ transform: [{ headers: Object.fromEntries(headerNames.map((n) => [n, "<redacted>"])) }] }];
else allow[domain] = [];
}
for (const rule of api.injectionRules) if (!(rule.domain in allow)) {
if (api.injectionRules && api.injectionRules.length > 0 || api.forwardRules && api.forwardRules.length > 0) {
const rulesByDomain = /* @__PURE__ */ new Map();
for (const rule of api.injectionRules ?? []) {
const headers = Object.fromEntries((rule.headerNames ?? []).map((n) => [n, "<redacted>"]));
allow[rule.domain] = [{ transform: [{ headers }] }];
const rules = rulesByDomain.get(rule.domain) ?? [];
rules.push({
...rule.match ? { match: rule.match } : {},
transform: [{ headers }]
});
rulesByDomain.set(rule.domain, rules);
}
for (const rule of api.forwardRules ?? []) {
const rules = rulesByDomain.get(rule.domain) ?? [];
rules.push({
...rule.match ? { match: rule.match } : {},
forwardURL: rule.forwardURL
});
rulesByDomain.set(rule.domain, rules);
}
const allow = {};
for (const domain of api.allowedDomains ?? []) allow[domain] = rulesByDomain.get(domain) ?? [];
for (const rule of [...api.injectionRules ?? [], ...api.forwardRules ?? []]) if (!(rule.domain in allow)) allow[rule.domain] = rulesByDomain.get(rule.domain) ?? [];
return {

@@ -53,0 +37,0 @@ allow,

@@ -1,1 +0,1 @@

{"version":3,"file":"network-policy.cjs","names":["injectionRules: z.infer<typeof InjectionRuleValidator>[]","merged: Record<string, string>","allow: Record<string, NetworkPolicyRule[]>"],"sources":["../../src/utils/network-policy.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { NetworkPolicy, NetworkPolicyRule } from \"../network-policy.js\";\nimport {\n NetworkPolicyValidator,\n InjectionRuleValidator,\n} from \"../api-client/validators.js\";\n\ntype APINetworkPolicy = z.infer<typeof NetworkPolicyValidator>;\n\nexport function toAPINetworkPolicy(policy: NetworkPolicy): APINetworkPolicy {\n if (policy === \"allow-all\") return { mode: \"allow-all\" };\n if (policy === \"deny-all\") return { mode: \"deny-all\" };\n\n if (policy.allow && !Array.isArray(policy.allow)) {\n const allowedDomains = Object.keys(policy.allow);\n const injectionRules: z.infer<typeof InjectionRuleValidator>[] = [];\n\n for (const [domain, rules] of Object.entries(policy.allow)) {\n const merged: Record<string, string> = {};\n for (const rule of rules) {\n for (const t of rule.transform ?? []) {\n Object.assign(merged, t.headers);\n }\n }\n if (Object.keys(merged).length > 0) {\n injectionRules.push({ domain, headers: merged });\n }\n }\n\n return {\n mode: \"custom\",\n ...(allowedDomains.length > 0 && { allowedDomains }),\n ...(injectionRules.length > 0 && { injectionRules }),\n ...(policy.subnets?.allow && { allowedCIDRs: policy.subnets.allow }),\n ...(policy.subnets?.deny && { deniedCIDRs: policy.subnets.deny }),\n };\n }\n\n return {\n mode: \"custom\",\n ...(policy.allow && { allowedDomains: policy.allow }),\n ...(policy.subnets?.allow && { allowedCIDRs: policy.subnets.allow }),\n ...(policy.subnets?.deny && { deniedCIDRs: policy.subnets.deny }),\n };\n}\n\nexport function fromAPINetworkPolicy(api: APINetworkPolicy): NetworkPolicy {\n if (api.mode === \"allow-all\") return \"allow-all\";\n if (api.mode === \"deny-all\") return \"deny-all\";\n\n const subnets =\n api.allowedCIDRs || api.deniedCIDRs\n ? {\n subnets: {\n ...(api.allowedCIDRs && { allow: api.allowedCIDRs }),\n ...(api.deniedCIDRs && { deny: api.deniedCIDRs }),\n },\n }\n : undefined;\n\n // If injectionRules are present, reconstruct the record form.\n // The API returns headerNames (secret values are stripped), so we\n // populate each header value with \"<redacted>\".\n if (api.injectionRules && api.injectionRules.length > 0) {\n const rulesByDomain = new Map(\n api.injectionRules.map((r) => [r.domain, r.headerNames ?? []]),\n );\n\n const allow: Record<string, NetworkPolicyRule[]> = {};\n for (const domain of api.allowedDomains ?? []) {\n const headerNames = rulesByDomain.get(domain);\n if (headerNames && headerNames.length > 0) {\n const headers = Object.fromEntries(\n headerNames.map((n) => [n, \"<redacted>\"]),\n );\n allow[domain] = [{ transform: [{ headers }] }];\n } else {\n allow[domain] = [];\n }\n }\n // Include injection rules for domains not in allowedDomains\n for (const rule of api.injectionRules) {\n if (!(rule.domain in allow)) {\n const headers = Object.fromEntries(\n (rule.headerNames ?? []).map((n) => [n, \"<redacted>\"]),\n );\n allow[rule.domain] = [{ transform: [{ headers }] }];\n }\n }\n\n return { allow, ...subnets };\n }\n\n return {\n ...(api.allowedDomains && { allow: api.allowedDomains }),\n ...subnets,\n };\n}\n"],"mappings":";;AASA,SAAgB,mBAAmB,QAAyC;AAC1E,KAAI,WAAW,YAAa,QAAO,EAAE,MAAM,aAAa;AACxD,KAAI,WAAW,WAAY,QAAO,EAAE,MAAM,YAAY;AAEtD,KAAI,OAAO,SAAS,CAAC,MAAM,QAAQ,OAAO,MAAM,EAAE;EAChD,MAAM,iBAAiB,OAAO,KAAK,OAAO,MAAM;EAChD,MAAMA,iBAA2D,EAAE;AAEnE,OAAK,MAAM,CAAC,QAAQ,UAAU,OAAO,QAAQ,OAAO,MAAM,EAAE;GAC1D,MAAMC,SAAiC,EAAE;AACzC,QAAK,MAAM,QAAQ,MACjB,MAAK,MAAM,KAAK,KAAK,aAAa,EAAE,CAClC,QAAO,OAAO,QAAQ,EAAE,QAAQ;AAGpC,OAAI,OAAO,KAAK,OAAO,CAAC,SAAS,EAC/B,gBAAe,KAAK;IAAE;IAAQ,SAAS;IAAQ,CAAC;;AAIpD,SAAO;GACL,MAAM;GACN,GAAI,eAAe,SAAS,KAAK,EAAE,gBAAgB;GACnD,GAAI,eAAe,SAAS,KAAK,EAAE,gBAAgB;GACnD,GAAI,OAAO,SAAS,SAAS,EAAE,cAAc,OAAO,QAAQ,OAAO;GACnE,GAAI,OAAO,SAAS,QAAQ,EAAE,aAAa,OAAO,QAAQ,MAAM;GACjE;;AAGH,QAAO;EACL,MAAM;EACN,GAAI,OAAO,SAAS,EAAE,gBAAgB,OAAO,OAAO;EACpD,GAAI,OAAO,SAAS,SAAS,EAAE,cAAc,OAAO,QAAQ,OAAO;EACnE,GAAI,OAAO,SAAS,QAAQ,EAAE,aAAa,OAAO,QAAQ,MAAM;EACjE;;AAGH,SAAgB,qBAAqB,KAAsC;AACzE,KAAI,IAAI,SAAS,YAAa,QAAO;AACrC,KAAI,IAAI,SAAS,WAAY,QAAO;CAEpC,MAAM,UACJ,IAAI,gBAAgB,IAAI,cACpB,EACE,SAAS;EACP,GAAI,IAAI,gBAAgB,EAAE,OAAO,IAAI,cAAc;EACnD,GAAI,IAAI,eAAe,EAAE,MAAM,IAAI,aAAa;EACjD,EACF,GACD;AAKN,KAAI,IAAI,kBAAkB,IAAI,eAAe,SAAS,GAAG;EACvD,MAAM,gBAAgB,IAAI,IACxB,IAAI,eAAe,KAAK,MAAM,CAAC,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC,CAAC,CAC/D;EAED,MAAMC,QAA6C,EAAE;AACrD,OAAK,MAAM,UAAU,IAAI,kBAAkB,EAAE,EAAE;GAC7C,MAAM,cAAc,cAAc,IAAI,OAAO;AAC7C,OAAI,eAAe,YAAY,SAAS,EAItC,OAAM,UAAU,CAAC,EAAE,WAAW,CAAC,EAAE,SAHjB,OAAO,YACrB,YAAY,KAAK,MAAM,CAAC,GAAG,aAAa,CAAC,CAC1C,EACyC,CAAC,EAAE,CAAC;OAE9C,OAAM,UAAU,EAAE;;AAItB,OAAK,MAAM,QAAQ,IAAI,eACrB,KAAI,EAAE,KAAK,UAAU,QAAQ;GAC3B,MAAM,UAAU,OAAO,aACpB,KAAK,eAAe,EAAE,EAAE,KAAK,MAAM,CAAC,GAAG,aAAa,CAAC,CACvD;AACD,SAAM,KAAK,UAAU,CAAC,EAAE,WAAW,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC;;AAIvD,SAAO;GAAE;GAAO,GAAG;GAAS;;AAG9B,QAAO;EACL,GAAI,IAAI,kBAAkB,EAAE,OAAO,IAAI,gBAAgB;EACvD,GAAG;EACJ"}
{"version":3,"file":"network-policy.cjs","names":["allow: Record<string, NetworkPolicyRule[]>"],"sources":["../../src/utils/network-policy.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { NetworkPolicy, NetworkPolicyRule } from \"../network-policy.js\";\nimport {\n NetworkPolicyRequestValidator,\n NetworkPolicyResponseValidator,\n} from \"../api-client/validators.js\";\n\ntype APIRequestNetworkPolicy = z.infer<typeof NetworkPolicyRequestValidator>;\ntype APIResponseNetworkPolicy = z.infer<typeof NetworkPolicyResponseValidator>;\n\nexport function toAPINetworkPolicy(\n policy: NetworkPolicy,\n): APIRequestNetworkPolicy {\n if (policy === \"allow-all\" || policy === \"deny-all\") {\n return { mode: policy };\n }\n\n return policy;\n}\n\nexport function fromAPINetworkPolicy(\n api: APIResponseNetworkPolicy,\n): NetworkPolicy {\n if (api.mode === \"allow-all\" || api.mode === \"deny-all\") {\n return api.mode;\n }\n\n const subnets =\n api.allowedCIDRs || api.deniedCIDRs\n ? {\n subnets: {\n ...(api.allowedCIDRs && { allow: api.allowedCIDRs }),\n ...(api.deniedCIDRs && { deny: api.deniedCIDRs }),\n },\n }\n : undefined;\n\n // If L7 rules are present, reconstruct the record form.\n // The API returns headerNames (secret values are stripped), so we\n // populate each header value with \"<redacted>\".\n if (\n (api.injectionRules && api.injectionRules.length > 0) ||\n (api.forwardRules && api.forwardRules.length > 0)\n ) {\n const rulesByDomain = new Map<string, NetworkPolicyRule[]>();\n for (const rule of api.injectionRules ?? []) {\n const headers = Object.fromEntries(\n (rule.headerNames ?? []).map((n) => [n, \"<redacted>\"]),\n );\n const rules = rulesByDomain.get(rule.domain) ?? [];\n rules.push({\n ...(rule.match ? { match: rule.match } : {}),\n transform: [{ headers }],\n });\n rulesByDomain.set(rule.domain, rules);\n }\n for (const rule of api.forwardRules ?? []) {\n const rules = rulesByDomain.get(rule.domain) ?? [];\n rules.push({\n ...(rule.match ? { match: rule.match } : {}),\n forwardURL: rule.forwardURL,\n });\n rulesByDomain.set(rule.domain, rules);\n }\n\n const allow: Record<string, NetworkPolicyRule[]> = {};\n for (const domain of api.allowedDomains ?? []) {\n allow[domain] = rulesByDomain.get(domain) ?? [];\n }\n // Include L7 rules for domains not in allowedDomains\n for (const rule of [...(api.injectionRules ?? []), ...(api.forwardRules ?? [])]) {\n if (!(rule.domain in allow)) {\n allow[rule.domain] = rulesByDomain.get(rule.domain) ?? [];\n }\n }\n\n return { allow, ...subnets };\n }\n\n return {\n ...(api.allowedDomains && { allow: api.allowedDomains }),\n ...subnets,\n };\n}\n"],"mappings":";;AAUA,SAAgB,mBACd,QACyB;AACzB,KAAI,WAAW,eAAe,WAAW,WACvC,QAAO,EAAE,MAAM,QAAQ;AAGzB,QAAO;;AAGT,SAAgB,qBACd,KACe;AACf,KAAI,IAAI,SAAS,eAAe,IAAI,SAAS,WAC3C,QAAO,IAAI;CAGb,MAAM,UACJ,IAAI,gBAAgB,IAAI,cACpB,EACE,SAAS;EACP,GAAI,IAAI,gBAAgB,EAAE,OAAO,IAAI,cAAc;EACnD,GAAI,IAAI,eAAe,EAAE,MAAM,IAAI,aAAa;EACjD,EACF,GACD;AAKN,KACG,IAAI,kBAAkB,IAAI,eAAe,SAAS,KAClD,IAAI,gBAAgB,IAAI,aAAa,SAAS,GAC/C;EACA,MAAM,gCAAgB,IAAI,KAAkC;AAC5D,OAAK,MAAM,QAAQ,IAAI,kBAAkB,EAAE,EAAE;GAC3C,MAAM,UAAU,OAAO,aACpB,KAAK,eAAe,EAAE,EAAE,KAAK,MAAM,CAAC,GAAG,aAAa,CAAC,CACvD;GACD,MAAM,QAAQ,cAAc,IAAI,KAAK,OAAO,IAAI,EAAE;AAClD,SAAM,KAAK;IACT,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,OAAO,GAAG,EAAE;IAC3C,WAAW,CAAC,EAAE,SAAS,CAAC;IACzB,CAAC;AACF,iBAAc,IAAI,KAAK,QAAQ,MAAM;;AAEvC,OAAK,MAAM,QAAQ,IAAI,gBAAgB,EAAE,EAAE;GACzC,MAAM,QAAQ,cAAc,IAAI,KAAK,OAAO,IAAI,EAAE;AAClD,SAAM,KAAK;IACT,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,OAAO,GAAG,EAAE;IAC3C,YAAY,KAAK;IAClB,CAAC;AACF,iBAAc,IAAI,KAAK,QAAQ,MAAM;;EAGvC,MAAMA,QAA6C,EAAE;AACrD,OAAK,MAAM,UAAU,IAAI,kBAAkB,EAAE,CAC3C,OAAM,UAAU,cAAc,IAAI,OAAO,IAAI,EAAE;AAGjD,OAAK,MAAM,QAAQ,CAAC,GAAI,IAAI,kBAAkB,EAAE,EAAG,GAAI,IAAI,gBAAgB,EAAE,CAAE,CAC7E,KAAI,EAAE,KAAK,UAAU,OACnB,OAAM,KAAK,UAAU,cAAc,IAAI,KAAK,OAAO,IAAI,EAAE;AAI7D,SAAO;GAAE;GAAO,GAAG;GAAS;;AAG9B,QAAO;EACL,GAAI,IAAI,kBAAkB,EAAE,OAAO,IAAI,gBAAgB;EACvD,GAAG;EACJ"}
//#region src/utils/network-policy.ts
function toAPINetworkPolicy(policy) {
if (policy === "allow-all") return { mode: "allow-all" };
if (policy === "deny-all") return { mode: "deny-all" };
if (policy.allow && !Array.isArray(policy.allow)) {
const allowedDomains = Object.keys(policy.allow);
const injectionRules = [];
for (const [domain, rules] of Object.entries(policy.allow)) {
const merged = {};
for (const rule of rules) for (const t of rule.transform ?? []) Object.assign(merged, t.headers);
if (Object.keys(merged).length > 0) injectionRules.push({
domain,
headers: merged
});
}
return {
mode: "custom",
...allowedDomains.length > 0 && { allowedDomains },
...injectionRules.length > 0 && { injectionRules },
...policy.subnets?.allow && { allowedCIDRs: policy.subnets.allow },
...policy.subnets?.deny && { deniedCIDRs: policy.subnets.deny }
};
}
return {
mode: "custom",
...policy.allow && { allowedDomains: policy.allow },
...policy.subnets?.allow && { allowedCIDRs: policy.subnets.allow },
...policy.subnets?.deny && { deniedCIDRs: policy.subnets.deny }
};
if (policy === "allow-all" || policy === "deny-all") return { mode: policy };
return policy;
}
function fromAPINetworkPolicy(api) {
if (api.mode === "allow-all") return "allow-all";
if (api.mode === "deny-all") return "deny-all";
if (api.mode === "allow-all" || api.mode === "deny-all") return api.mode;
const subnets = api.allowedCIDRs || api.deniedCIDRs ? { subnets: {

@@ -38,14 +12,24 @@ ...api.allowedCIDRs && { allow: api.allowedCIDRs },

} } : void 0;
if (api.injectionRules && api.injectionRules.length > 0) {
const rulesByDomain = new Map(api.injectionRules.map((r) => [r.domain, r.headerNames ?? []]));
const allow = {};
for (const domain of api.allowedDomains ?? []) {
const headerNames = rulesByDomain.get(domain);
if (headerNames && headerNames.length > 0) allow[domain] = [{ transform: [{ headers: Object.fromEntries(headerNames.map((n) => [n, "<redacted>"])) }] }];
else allow[domain] = [];
}
for (const rule of api.injectionRules) if (!(rule.domain in allow)) {
if (api.injectionRules && api.injectionRules.length > 0 || api.forwardRules && api.forwardRules.length > 0) {
const rulesByDomain = /* @__PURE__ */ new Map();
for (const rule of api.injectionRules ?? []) {
const headers = Object.fromEntries((rule.headerNames ?? []).map((n) => [n, "<redacted>"]));
allow[rule.domain] = [{ transform: [{ headers }] }];
const rules = rulesByDomain.get(rule.domain) ?? [];
rules.push({
...rule.match ? { match: rule.match } : {},
transform: [{ headers }]
});
rulesByDomain.set(rule.domain, rules);
}
for (const rule of api.forwardRules ?? []) {
const rules = rulesByDomain.get(rule.domain) ?? [];
rules.push({
...rule.match ? { match: rule.match } : {},
forwardURL: rule.forwardURL
});
rulesByDomain.set(rule.domain, rules);
}
const allow = {};
for (const domain of api.allowedDomains ?? []) allow[domain] = rulesByDomain.get(domain) ?? [];
for (const rule of [...api.injectionRules ?? [], ...api.forwardRules ?? []]) if (!(rule.domain in allow)) allow[rule.domain] = rulesByDomain.get(rule.domain) ?? [];
return {

@@ -52,0 +36,0 @@ allow,

@@ -1,1 +0,1 @@

{"version":3,"file":"network-policy.js","names":["injectionRules: z.infer<typeof InjectionRuleValidator>[]","merged: Record<string, string>","allow: Record<string, NetworkPolicyRule[]>"],"sources":["../../src/utils/network-policy.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { NetworkPolicy, NetworkPolicyRule } from \"../network-policy.js\";\nimport {\n NetworkPolicyValidator,\n InjectionRuleValidator,\n} from \"../api-client/validators.js\";\n\ntype APINetworkPolicy = z.infer<typeof NetworkPolicyValidator>;\n\nexport function toAPINetworkPolicy(policy: NetworkPolicy): APINetworkPolicy {\n if (policy === \"allow-all\") return { mode: \"allow-all\" };\n if (policy === \"deny-all\") return { mode: \"deny-all\" };\n\n if (policy.allow && !Array.isArray(policy.allow)) {\n const allowedDomains = Object.keys(policy.allow);\n const injectionRules: z.infer<typeof InjectionRuleValidator>[] = [];\n\n for (const [domain, rules] of Object.entries(policy.allow)) {\n const merged: Record<string, string> = {};\n for (const rule of rules) {\n for (const t of rule.transform ?? []) {\n Object.assign(merged, t.headers);\n }\n }\n if (Object.keys(merged).length > 0) {\n injectionRules.push({ domain, headers: merged });\n }\n }\n\n return {\n mode: \"custom\",\n ...(allowedDomains.length > 0 && { allowedDomains }),\n ...(injectionRules.length > 0 && { injectionRules }),\n ...(policy.subnets?.allow && { allowedCIDRs: policy.subnets.allow }),\n ...(policy.subnets?.deny && { deniedCIDRs: policy.subnets.deny }),\n };\n }\n\n return {\n mode: \"custom\",\n ...(policy.allow && { allowedDomains: policy.allow }),\n ...(policy.subnets?.allow && { allowedCIDRs: policy.subnets.allow }),\n ...(policy.subnets?.deny && { deniedCIDRs: policy.subnets.deny }),\n };\n}\n\nexport function fromAPINetworkPolicy(api: APINetworkPolicy): NetworkPolicy {\n if (api.mode === \"allow-all\") return \"allow-all\";\n if (api.mode === \"deny-all\") return \"deny-all\";\n\n const subnets =\n api.allowedCIDRs || api.deniedCIDRs\n ? {\n subnets: {\n ...(api.allowedCIDRs && { allow: api.allowedCIDRs }),\n ...(api.deniedCIDRs && { deny: api.deniedCIDRs }),\n },\n }\n : undefined;\n\n // If injectionRules are present, reconstruct the record form.\n // The API returns headerNames (secret values are stripped), so we\n // populate each header value with \"<redacted>\".\n if (api.injectionRules && api.injectionRules.length > 0) {\n const rulesByDomain = new Map(\n api.injectionRules.map((r) => [r.domain, r.headerNames ?? []]),\n );\n\n const allow: Record<string, NetworkPolicyRule[]> = {};\n for (const domain of api.allowedDomains ?? []) {\n const headerNames = rulesByDomain.get(domain);\n if (headerNames && headerNames.length > 0) {\n const headers = Object.fromEntries(\n headerNames.map((n) => [n, \"<redacted>\"]),\n );\n allow[domain] = [{ transform: [{ headers }] }];\n } else {\n allow[domain] = [];\n }\n }\n // Include injection rules for domains not in allowedDomains\n for (const rule of api.injectionRules) {\n if (!(rule.domain in allow)) {\n const headers = Object.fromEntries(\n (rule.headerNames ?? []).map((n) => [n, \"<redacted>\"]),\n );\n allow[rule.domain] = [{ transform: [{ headers }] }];\n }\n }\n\n return { allow, ...subnets };\n }\n\n return {\n ...(api.allowedDomains && { allow: api.allowedDomains }),\n ...subnets,\n };\n}\n"],"mappings":";AASA,SAAgB,mBAAmB,QAAyC;AAC1E,KAAI,WAAW,YAAa,QAAO,EAAE,MAAM,aAAa;AACxD,KAAI,WAAW,WAAY,QAAO,EAAE,MAAM,YAAY;AAEtD,KAAI,OAAO,SAAS,CAAC,MAAM,QAAQ,OAAO,MAAM,EAAE;EAChD,MAAM,iBAAiB,OAAO,KAAK,OAAO,MAAM;EAChD,MAAMA,iBAA2D,EAAE;AAEnE,OAAK,MAAM,CAAC,QAAQ,UAAU,OAAO,QAAQ,OAAO,MAAM,EAAE;GAC1D,MAAMC,SAAiC,EAAE;AACzC,QAAK,MAAM,QAAQ,MACjB,MAAK,MAAM,KAAK,KAAK,aAAa,EAAE,CAClC,QAAO,OAAO,QAAQ,EAAE,QAAQ;AAGpC,OAAI,OAAO,KAAK,OAAO,CAAC,SAAS,EAC/B,gBAAe,KAAK;IAAE;IAAQ,SAAS;IAAQ,CAAC;;AAIpD,SAAO;GACL,MAAM;GACN,GAAI,eAAe,SAAS,KAAK,EAAE,gBAAgB;GACnD,GAAI,eAAe,SAAS,KAAK,EAAE,gBAAgB;GACnD,GAAI,OAAO,SAAS,SAAS,EAAE,cAAc,OAAO,QAAQ,OAAO;GACnE,GAAI,OAAO,SAAS,QAAQ,EAAE,aAAa,OAAO,QAAQ,MAAM;GACjE;;AAGH,QAAO;EACL,MAAM;EACN,GAAI,OAAO,SAAS,EAAE,gBAAgB,OAAO,OAAO;EACpD,GAAI,OAAO,SAAS,SAAS,EAAE,cAAc,OAAO,QAAQ,OAAO;EACnE,GAAI,OAAO,SAAS,QAAQ,EAAE,aAAa,OAAO,QAAQ,MAAM;EACjE;;AAGH,SAAgB,qBAAqB,KAAsC;AACzE,KAAI,IAAI,SAAS,YAAa,QAAO;AACrC,KAAI,IAAI,SAAS,WAAY,QAAO;CAEpC,MAAM,UACJ,IAAI,gBAAgB,IAAI,cACpB,EACE,SAAS;EACP,GAAI,IAAI,gBAAgB,EAAE,OAAO,IAAI,cAAc;EACnD,GAAI,IAAI,eAAe,EAAE,MAAM,IAAI,aAAa;EACjD,EACF,GACD;AAKN,KAAI,IAAI,kBAAkB,IAAI,eAAe,SAAS,GAAG;EACvD,MAAM,gBAAgB,IAAI,IACxB,IAAI,eAAe,KAAK,MAAM,CAAC,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC,CAAC,CAC/D;EAED,MAAMC,QAA6C,EAAE;AACrD,OAAK,MAAM,UAAU,IAAI,kBAAkB,EAAE,EAAE;GAC7C,MAAM,cAAc,cAAc,IAAI,OAAO;AAC7C,OAAI,eAAe,YAAY,SAAS,EAItC,OAAM,UAAU,CAAC,EAAE,WAAW,CAAC,EAAE,SAHjB,OAAO,YACrB,YAAY,KAAK,MAAM,CAAC,GAAG,aAAa,CAAC,CAC1C,EACyC,CAAC,EAAE,CAAC;OAE9C,OAAM,UAAU,EAAE;;AAItB,OAAK,MAAM,QAAQ,IAAI,eACrB,KAAI,EAAE,KAAK,UAAU,QAAQ;GAC3B,MAAM,UAAU,OAAO,aACpB,KAAK,eAAe,EAAE,EAAE,KAAK,MAAM,CAAC,GAAG,aAAa,CAAC,CACvD;AACD,SAAM,KAAK,UAAU,CAAC,EAAE,WAAW,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC;;AAIvD,SAAO;GAAE;GAAO,GAAG;GAAS;;AAG9B,QAAO;EACL,GAAI,IAAI,kBAAkB,EAAE,OAAO,IAAI,gBAAgB;EACvD,GAAG;EACJ"}
{"version":3,"file":"network-policy.js","names":["allow: Record<string, NetworkPolicyRule[]>"],"sources":["../../src/utils/network-policy.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { NetworkPolicy, NetworkPolicyRule } from \"../network-policy.js\";\nimport {\n NetworkPolicyRequestValidator,\n NetworkPolicyResponseValidator,\n} from \"../api-client/validators.js\";\n\ntype APIRequestNetworkPolicy = z.infer<typeof NetworkPolicyRequestValidator>;\ntype APIResponseNetworkPolicy = z.infer<typeof NetworkPolicyResponseValidator>;\n\nexport function toAPINetworkPolicy(\n policy: NetworkPolicy,\n): APIRequestNetworkPolicy {\n if (policy === \"allow-all\" || policy === \"deny-all\") {\n return { mode: policy };\n }\n\n return policy;\n}\n\nexport function fromAPINetworkPolicy(\n api: APIResponseNetworkPolicy,\n): NetworkPolicy {\n if (api.mode === \"allow-all\" || api.mode === \"deny-all\") {\n return api.mode;\n }\n\n const subnets =\n api.allowedCIDRs || api.deniedCIDRs\n ? {\n subnets: {\n ...(api.allowedCIDRs && { allow: api.allowedCIDRs }),\n ...(api.deniedCIDRs && { deny: api.deniedCIDRs }),\n },\n }\n : undefined;\n\n // If L7 rules are present, reconstruct the record form.\n // The API returns headerNames (secret values are stripped), so we\n // populate each header value with \"<redacted>\".\n if (\n (api.injectionRules && api.injectionRules.length > 0) ||\n (api.forwardRules && api.forwardRules.length > 0)\n ) {\n const rulesByDomain = new Map<string, NetworkPolicyRule[]>();\n for (const rule of api.injectionRules ?? []) {\n const headers = Object.fromEntries(\n (rule.headerNames ?? []).map((n) => [n, \"<redacted>\"]),\n );\n const rules = rulesByDomain.get(rule.domain) ?? [];\n rules.push({\n ...(rule.match ? { match: rule.match } : {}),\n transform: [{ headers }],\n });\n rulesByDomain.set(rule.domain, rules);\n }\n for (const rule of api.forwardRules ?? []) {\n const rules = rulesByDomain.get(rule.domain) ?? [];\n rules.push({\n ...(rule.match ? { match: rule.match } : {}),\n forwardURL: rule.forwardURL,\n });\n rulesByDomain.set(rule.domain, rules);\n }\n\n const allow: Record<string, NetworkPolicyRule[]> = {};\n for (const domain of api.allowedDomains ?? []) {\n allow[domain] = rulesByDomain.get(domain) ?? [];\n }\n // Include L7 rules for domains not in allowedDomains\n for (const rule of [...(api.injectionRules ?? []), ...(api.forwardRules ?? [])]) {\n if (!(rule.domain in allow)) {\n allow[rule.domain] = rulesByDomain.get(rule.domain) ?? [];\n }\n }\n\n return { allow, ...subnets };\n }\n\n return {\n ...(api.allowedDomains && { allow: api.allowedDomains }),\n ...subnets,\n };\n}\n"],"mappings":";AAUA,SAAgB,mBACd,QACyB;AACzB,KAAI,WAAW,eAAe,WAAW,WACvC,QAAO,EAAE,MAAM,QAAQ;AAGzB,QAAO;;AAGT,SAAgB,qBACd,KACe;AACf,KAAI,IAAI,SAAS,eAAe,IAAI,SAAS,WAC3C,QAAO,IAAI;CAGb,MAAM,UACJ,IAAI,gBAAgB,IAAI,cACpB,EACE,SAAS;EACP,GAAI,IAAI,gBAAgB,EAAE,OAAO,IAAI,cAAc;EACnD,GAAI,IAAI,eAAe,EAAE,MAAM,IAAI,aAAa;EACjD,EACF,GACD;AAKN,KACG,IAAI,kBAAkB,IAAI,eAAe,SAAS,KAClD,IAAI,gBAAgB,IAAI,aAAa,SAAS,GAC/C;EACA,MAAM,gCAAgB,IAAI,KAAkC;AAC5D,OAAK,MAAM,QAAQ,IAAI,kBAAkB,EAAE,EAAE;GAC3C,MAAM,UAAU,OAAO,aACpB,KAAK,eAAe,EAAE,EAAE,KAAK,MAAM,CAAC,GAAG,aAAa,CAAC,CACvD;GACD,MAAM,QAAQ,cAAc,IAAI,KAAK,OAAO,IAAI,EAAE;AAClD,SAAM,KAAK;IACT,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,OAAO,GAAG,EAAE;IAC3C,WAAW,CAAC,EAAE,SAAS,CAAC;IACzB,CAAC;AACF,iBAAc,IAAI,KAAK,QAAQ,MAAM;;AAEvC,OAAK,MAAM,QAAQ,IAAI,gBAAgB,EAAE,EAAE;GACzC,MAAM,QAAQ,cAAc,IAAI,KAAK,OAAO,IAAI,EAAE;AAClD,SAAM,KAAK;IACT,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,OAAO,GAAG,EAAE;IAC3C,YAAY,KAAK;IAClB,CAAC;AACF,iBAAc,IAAI,KAAK,QAAQ,MAAM;;EAGvC,MAAMA,QAA6C,EAAE;AACrD,OAAK,MAAM,UAAU,IAAI,kBAAkB,EAAE,CAC3C,OAAM,UAAU,cAAc,IAAI,OAAO,IAAI,EAAE;AAGjD,OAAK,MAAM,QAAQ,CAAC,GAAI,IAAI,kBAAkB,EAAE,EAAG,GAAI,IAAI,gBAAgB,EAAE,CAAE,CAC7E,KAAI,EAAE,KAAK,UAAU,OACnB,OAAM,KAAK,UAAU,cAAc,IAAI,KAAK,OAAO,IAAI,EAAE;AAI7D,SAAO;GAAE;GAAO,GAAG;GAAS;;AAG9B,QAAO;EACL,GAAI,IAAI,kBAAkB,EAAE,OAAO,IAAI,gBAAgB;EACvD,GAAG;EACJ"}
//#region src/version.ts
const VERSION = "2.0.0-beta.14";
const VERSION = "2.0.0-beta.18";

@@ -5,0 +5,0 @@ //#endregion

@@ -1,1 +0,1 @@

{"version":3,"file":"version.cjs","names":[],"sources":["../src/version.ts"],"sourcesContent":["// Autogenerated by inject-version.ts\nexport const VERSION = \"2.0.0-beta.14\";\n"],"mappings":";;AACA,MAAa,UAAU"}
{"version":3,"file":"version.cjs","names":[],"sources":["../src/version.ts"],"sourcesContent":["// Autogenerated by inject-version.ts\nexport const VERSION = \"2.0.0-beta.18\";\n"],"mappings":";;AACA,MAAa,UAAU"}
//#region src/version.ts
const VERSION = "2.0.0-beta.14";
const VERSION = "2.0.0-beta.18";

@@ -4,0 +4,0 @@ //#endregion

@@ -1,1 +0,1 @@

{"version":3,"file":"version.js","names":[],"sources":["../src/version.ts"],"sourcesContent":["// Autogenerated by inject-version.ts\nexport const VERSION = \"2.0.0-beta.14\";\n"],"mappings":";AACA,MAAa,UAAU"}
{"version":3,"file":"version.js","names":[],"sources":["../src/version.ts"],"sourcesContent":["// Autogenerated by inject-version.ts\nexport const VERSION = \"2.0.0-beta.18\";\n"],"mappings":";AACA,MAAa,UAAU"}
{
"name": "@vercel/sandbox",
"version": "2.0.0-beta.14",
"version": "2.0.0-beta.18",
"description": "Software Development Kit for Vercel Sandbox",

@@ -5,0 +5,0 @@ "type": "module",

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display