@vercel/sandbox
Advanced tools
| import { Command, CommandFinished } from "./command.cjs"; | ||
| import { RunCommandParams } from "./session.cjs"; | ||
| //#region src/execution-context.d.ts | ||
| /** | ||
| * The common surface for running commands and performing file operations in | ||
| * a sandbox, regardless of scope. | ||
| * | ||
| * Implemented by {@link Sandbox}, {@link Session}, and {@link SandboxUser}, | ||
| * so code can be written against "somewhere to run commands" without caring | ||
| * whether it targets the whole sandbox or a specific user's context. | ||
| * | ||
| * Implementations may scope the operations: for example, {@link SandboxUser} | ||
| * runs commands as its user and resolves relative paths against the user's | ||
| * home directory. | ||
| */ | ||
| interface ExecutionContext { | ||
| /** | ||
| * Start executing a command in this context. | ||
| * | ||
| * @param command - The command to execute. | ||
| * @param args - Arguments to pass to the command. | ||
| * @param opts - Optional parameters. | ||
| * @returns A {@link CommandFinished} result once execution is done. | ||
| */ | ||
| runCommand(command: string, args?: string[], opts?: { | ||
| signal?: AbortSignal; | ||
| timeoutMs?: number; | ||
| }): Promise<CommandFinished>; | ||
| /** | ||
| * Start executing a command in this context in detached mode. | ||
| * | ||
| * @param params - The command parameters. | ||
| * @returns A {@link Command} instance for the running command. | ||
| */ | ||
| runCommand(params: RunCommandParams & { | ||
| detached: true; | ||
| }): Promise<Command>; | ||
| /** | ||
| * Start executing a command in this context. | ||
| * | ||
| * @param params - The command parameters. | ||
| * @returns A {@link CommandFinished} result once execution is done. | ||
| */ | ||
| runCommand(params: RunCommandParams): Promise<CommandFinished>; | ||
| /** | ||
| * Create a directory in the filesystem of this context. | ||
| * | ||
| * @param path - Path of the directory to create | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| mkDir(path: string, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<void>; | ||
| /** | ||
| * Read a file from this context as a stream. | ||
| * | ||
| * @param file - File to read, with path and optional cwd | ||
| * @param opts - Optional parameters. | ||
| * @returns A ReadableStream of the file contents, or null if not found | ||
| */ | ||
| readFile(file: { | ||
| path: string; | ||
| cwd?: string; | ||
| }, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<NodeJS.ReadableStream | null>; | ||
| /** | ||
| * Read a file from this context as a Buffer. | ||
| * | ||
| * @param file - File to read, with path and optional cwd | ||
| * @param opts - Optional parameters. | ||
| * @returns The file contents as a Buffer, or null if not found | ||
| */ | ||
| readFileToBuffer(file: { | ||
| path: string; | ||
| cwd?: string; | ||
| }, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<Buffer | null>; | ||
| /** | ||
| * Download a file from this context to the local filesystem. | ||
| * | ||
| * @param src - Source file in the sandbox | ||
| * @param dst - Destination on the local machine | ||
| * @param opts - Optional parameters. | ||
| * @returns The absolute path to the written file, or null if not found | ||
| */ | ||
| downloadFile(src: { | ||
| path: string; | ||
| cwd?: string; | ||
| }, dst: { | ||
| path: string; | ||
| cwd?: string; | ||
| }, opts?: { | ||
| mkdirRecursive?: boolean; | ||
| signal?: AbortSignal; | ||
| }): Promise<string | null>; | ||
| /** | ||
| * Write files to the filesystem of this context. | ||
| * | ||
| * @param files - Array of files with path, content, and optional mode | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| writeFiles(files: { | ||
| path: string; | ||
| content: string | Uint8Array; | ||
| mode?: number; | ||
| }[], opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<void>; | ||
| } | ||
| //#endregion | ||
| export { ExecutionContext }; | ||
| //# sourceMappingURL=execution-context.d.cts.map |
| import { Command, CommandFinished } from "./command.js"; | ||
| import { RunCommandParams } from "./session.js"; | ||
| //#region src/execution-context.d.ts | ||
| /** | ||
| * The common surface for running commands and performing file operations in | ||
| * a sandbox, regardless of scope. | ||
| * | ||
| * Implemented by {@link Sandbox}, {@link Session}, and {@link SandboxUser}, | ||
| * so code can be written against "somewhere to run commands" without caring | ||
| * whether it targets the whole sandbox or a specific user's context. | ||
| * | ||
| * Implementations may scope the operations: for example, {@link SandboxUser} | ||
| * runs commands as its user and resolves relative paths against the user's | ||
| * home directory. | ||
| */ | ||
| interface ExecutionContext { | ||
| /** | ||
| * Start executing a command in this context. | ||
| * | ||
| * @param command - The command to execute. | ||
| * @param args - Arguments to pass to the command. | ||
| * @param opts - Optional parameters. | ||
| * @returns A {@link CommandFinished} result once execution is done. | ||
| */ | ||
| runCommand(command: string, args?: string[], opts?: { | ||
| signal?: AbortSignal; | ||
| timeoutMs?: number; | ||
| }): Promise<CommandFinished>; | ||
| /** | ||
| * Start executing a command in this context in detached mode. | ||
| * | ||
| * @param params - The command parameters. | ||
| * @returns A {@link Command} instance for the running command. | ||
| */ | ||
| runCommand(params: RunCommandParams & { | ||
| detached: true; | ||
| }): Promise<Command>; | ||
| /** | ||
| * Start executing a command in this context. | ||
| * | ||
| * @param params - The command parameters. | ||
| * @returns A {@link CommandFinished} result once execution is done. | ||
| */ | ||
| runCommand(params: RunCommandParams): Promise<CommandFinished>; | ||
| /** | ||
| * Create a directory in the filesystem of this context. | ||
| * | ||
| * @param path - Path of the directory to create | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| mkDir(path: string, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<void>; | ||
| /** | ||
| * Read a file from this context as a stream. | ||
| * | ||
| * @param file - File to read, with path and optional cwd | ||
| * @param opts - Optional parameters. | ||
| * @returns A ReadableStream of the file contents, or null if not found | ||
| */ | ||
| readFile(file: { | ||
| path: string; | ||
| cwd?: string; | ||
| }, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<NodeJS.ReadableStream | null>; | ||
| /** | ||
| * Read a file from this context as a Buffer. | ||
| * | ||
| * @param file - File to read, with path and optional cwd | ||
| * @param opts - Optional parameters. | ||
| * @returns The file contents as a Buffer, or null if not found | ||
| */ | ||
| readFileToBuffer(file: { | ||
| path: string; | ||
| cwd?: string; | ||
| }, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<Buffer | null>; | ||
| /** | ||
| * Download a file from this context to the local filesystem. | ||
| * | ||
| * @param src - Source file in the sandbox | ||
| * @param dst - Destination on the local machine | ||
| * @param opts - Optional parameters. | ||
| * @returns The absolute path to the written file, or null if not found | ||
| */ | ||
| downloadFile(src: { | ||
| path: string; | ||
| cwd?: string; | ||
| }, dst: { | ||
| path: string; | ||
| cwd?: string; | ||
| }, opts?: { | ||
| mkdirRecursive?: boolean; | ||
| signal?: AbortSignal; | ||
| }): Promise<string | null>; | ||
| /** | ||
| * Write files to the filesystem of this context. | ||
| * | ||
| * @param files - Array of files with path, content, and optional mode | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| writeFiles(files: { | ||
| path: string; | ||
| content: string | Uint8Array; | ||
| mode?: number; | ||
| }[], opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<void>; | ||
| } | ||
| //#endregion | ||
| export { ExecutionContext }; | ||
| //# sourceMappingURL=execution-context.d.ts.map |
| const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs'); | ||
| const require_validate_name = require('./utils/validate-name.cjs'); | ||
| let stream = require("stream"); | ||
| let path = require("path"); | ||
| let stream_promises = require("stream/promises"); | ||
| let fs = require("fs"); | ||
| let fs_promises = require("fs/promises"); | ||
| //#region src/sandbox-user.ts | ||
| /** | ||
| * A user context within a sandbox. | ||
| * | ||
| * All file and command operations default to running as this user. | ||
| * Created via {@link Sandbox.createUser} or {@link Sandbox.asUser}. | ||
| * | ||
| * @hideconstructor | ||
| */ | ||
| var SandboxUser = class { | ||
| constructor({ sandbox, username }) { | ||
| this.sandbox = sandbox; | ||
| this.username = username; | ||
| this.homeDir = username === "root" ? "/root" : `/home/${username}`; | ||
| } | ||
| /** | ||
| * Build the wrapped command args to run as this user via `sudo -u`. | ||
| * | ||
| * When `env` is provided, injects `env KEY=VAL ...` so that environment | ||
| * variables survive the `sudo -u` transition. | ||
| */ | ||
| buildUserCommand(params) { | ||
| const envEntries = Object.entries(params.env ?? {}); | ||
| const envArgs = envEntries.length > 0 ? ["env", ...envEntries.map(([k, v]) => `${k}=${v}`)] : []; | ||
| const cwd = params.cwd ?? this.homeDir; | ||
| return { | ||
| cmd: "sudo", | ||
| args: [ | ||
| "-u", | ||
| this.username, | ||
| "--", | ||
| "bash", | ||
| "-c", | ||
| "cd \"$1\" || exit 1; shift; exec \"$@\"", | ||
| "bash", | ||
| cwd, | ||
| ...envArgs, | ||
| params.cmd, | ||
| ...params.args ?? [] | ||
| ] | ||
| }; | ||
| } | ||
| /** | ||
| * Resolve a path relative to this user's home directory. | ||
| * Absolute paths are returned as-is. | ||
| */ | ||
| resolvePath(path$1) { | ||
| return path$1.startsWith("/") ? path$1 : `${this.homeDir}/${path$1}`; | ||
| } | ||
| async runCommand(commandOrParams, args, opts) { | ||
| if (typeof commandOrParams === "string") { | ||
| const wrapped$1 = this.buildUserCommand({ | ||
| cmd: commandOrParams, | ||
| args | ||
| }); | ||
| return this.sandbox.runCommand({ | ||
| ...wrapped$1, | ||
| signal: opts?.signal, | ||
| timeoutMs: opts?.timeoutMs | ||
| }); | ||
| } | ||
| const params = commandOrParams; | ||
| if (params.sudo) return this.sandbox.runCommand({ ...params }); | ||
| const wrapped = this.buildUserCommand({ | ||
| cmd: params.cmd, | ||
| args: params.args, | ||
| env: params.env, | ||
| cwd: params.cwd | ||
| }); | ||
| return this.sandbox.runCommand({ | ||
| cmd: wrapped.cmd, | ||
| args: wrapped.args, | ||
| detached: params.detached, | ||
| stdout: params.stdout, | ||
| stderr: params.stderr, | ||
| signal: params.signal, | ||
| timeoutMs: params.timeoutMs | ||
| }); | ||
| } | ||
| /** | ||
| * Write files to this user's home directory (or absolute paths). | ||
| * Files are written via the sandbox HTTP API then chowned to this user. | ||
| * | ||
| * The HTTP API can write to user home dirs because they are group-owned | ||
| * by the sandbox's default user group with `770` permissions. | ||
| * | ||
| * @param files - Array of files with path, content, and optional mode | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| async writeFiles(files, opts) { | ||
| const absoluteFiles = files.map((f) => ({ | ||
| ...f, | ||
| path: this.resolvePath(f.path) | ||
| })); | ||
| await this.sandbox.writeFiles(absoluteFiles, opts); | ||
| const paths = absoluteFiles.map((f) => f.path); | ||
| if (paths.length === 0) return; | ||
| await this.chownOrThrow(paths, `${this.username}:${await this.primaryGroup(opts?.signal)}`, opts?.signal); | ||
| const dirs = this.ancestorDirsUnderHome(paths); | ||
| if (dirs.length > 0) { | ||
| const { group } = await this.sandbox.getDefaultUser(opts); | ||
| await this.chownOrThrow(dirs, `${this.username}:${group}`, opts?.signal); | ||
| await this.chmodOrThrow(dirs, "770", opts?.signal); | ||
| } | ||
| } | ||
| /** | ||
| * Read a file from this user's context as a stream. | ||
| * | ||
| * @param file - File to read, with path and optional cwd | ||
| * @param opts - Optional parameters. | ||
| * @returns A ReadableStream of the file contents, or null if not found | ||
| */ | ||
| async readFile(file, opts) { | ||
| "use step"; | ||
| const buffer = await this.catAsUser(file, opts); | ||
| return buffer === null ? null : stream.Readable.from([buffer]); | ||
| } | ||
| /** | ||
| * Read a file from this user's context as a Buffer. | ||
| * | ||
| * @param file - File to read, with path and optional cwd | ||
| * @param opts - Optional parameters. | ||
| * @returns The file contents as a Buffer, or null if not found | ||
| */ | ||
| async readFileToBuffer(file, opts) { | ||
| return this.catAsUser(file, opts); | ||
| } | ||
| /** | ||
| * Download a file from this user's context to the local filesystem. | ||
| * | ||
| * @param src - Source file in the sandbox | ||
| * @param dst - Destination on the local machine | ||
| * @param opts - Optional parameters. | ||
| * @returns The absolute path to the written file, or null if not found | ||
| */ | ||
| async downloadFile(src, dst, opts) { | ||
| "use step"; | ||
| const stream$1 = await this.readFile(src, opts); | ||
| if (stream$1 === null) return null; | ||
| const dstPath = (0, path.resolve)(dst.cwd ?? "", dst.path); | ||
| if (opts?.mkdirRecursive) await (0, fs_promises.mkdir)((0, path.dirname)(dstPath), { recursive: true }); | ||
| await (0, stream_promises.pipeline)(stream$1, (0, fs.createWriteStream)(dstPath), { signal: opts?.signal }); | ||
| return dstPath; | ||
| } | ||
| /** | ||
| * Read a file as this user and return its bytes, or null if it does not | ||
| * exist. | ||
| * | ||
| * Reads via `sudo -u <user> base64` rather than the HTTP file API: on stock | ||
| * runtimes the API runs as `vercel-sandbox` and cannot read files this user | ||
| * has kept private (e.g. mode `600`), whereas reading as the user always | ||
| * honours the user's own permissions. The payload is base64-encoded because | ||
| * the command output channel is UTF-8 only and would otherwise corrupt | ||
| * binary files. | ||
| */ | ||
| async catAsUser(file, opts) { | ||
| const path$1 = file.path.startsWith("/") ? file.path : `${file.cwd ?? this.homeDir}/${file.path}`; | ||
| const result = await this.runCommand({ | ||
| cmd: "base64", | ||
| args: [path$1], | ||
| signal: opts?.signal | ||
| }); | ||
| if (result.exitCode !== 0) { | ||
| const stderr = await result.stderr(); | ||
| if (/No such file or directory/i.test(stderr)) return null; | ||
| throw new Error(`Failed to read ${path$1}: ${stderr}`); | ||
| } | ||
| return Buffer.from(await result.stdout(), "base64"); | ||
| } | ||
| /** | ||
| * Create a directory owned by this user. | ||
| * | ||
| * @param path - Path of the directory to create | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| async mkDir(path$1, opts) { | ||
| const absPath = this.resolvePath(path$1); | ||
| await this.sandbox.mkDir(absPath, opts); | ||
| const dirs = [absPath, ...this.ancestorDirsUnderHome([absPath])]; | ||
| const { group } = await this.sandbox.getDefaultUser(opts); | ||
| await this.chownOrThrow(dirs, `${this.username}:${group}`, opts?.signal); | ||
| await this.chmodOrThrow(dirs, "770", opts?.signal); | ||
| } | ||
| /** | ||
| * This user's primary group. Users created via {@link Sandbox.createUser} | ||
| * get a group named after them, but {@link Sandbox.asUser} accepts | ||
| * pre-existing users whose primary group can differ (e.g. system users), so | ||
| * we resolve it from the sandbox rather than assuming `<username>`. | ||
| * | ||
| * The result is memoized for the lifetime of this instance. | ||
| */ | ||
| primaryGroup(signal) { | ||
| if (!this.primaryGroupPromise) this.primaryGroupPromise = this.resolvePrimaryGroup(signal).catch((err) => { | ||
| this.primaryGroupPromise = void 0; | ||
| throw err; | ||
| }); | ||
| return this.primaryGroupPromise; | ||
| } | ||
| async resolvePrimaryGroup(signal) { | ||
| const result = await this.sandbox.runCommand({ | ||
| cmd: "id", | ||
| args: ["-gn", this.username], | ||
| signal | ||
| }); | ||
| if (result.exitCode !== 0) { | ||
| const stderr = await result.stderr(); | ||
| throw new Error(`Failed to resolve the primary group of "${this.username}": ${stderr}`); | ||
| } | ||
| const group = (await result.stdout()).trim(); | ||
| if (!group) throw new Error(`Failed to resolve the primary group of "${this.username}"`); | ||
| return group; | ||
| } | ||
| /** | ||
| * Run `chown <ownership> <paths...>` as root, throwing on failure. | ||
| */ | ||
| async chownOrThrow(paths, ownership, signal) { | ||
| const chown = await this.sandbox.runCommand({ | ||
| cmd: "chown", | ||
| args: [ownership, ...paths], | ||
| sudo: true, | ||
| signal | ||
| }); | ||
| if (chown.exitCode !== 0) { | ||
| const stderr = await chown.stderr(); | ||
| throw new Error(`Failed to set ownership on ${paths.join(", ")}: ${stderr}`); | ||
| } | ||
| } | ||
| /** | ||
| * Run `chmod <mode> <paths...>` as root, throwing on failure. | ||
| */ | ||
| async chmodOrThrow(paths, mode, signal) { | ||
| const chmod = await this.sandbox.runCommand({ | ||
| cmd: "chmod", | ||
| args: [mode, ...paths], | ||
| sudo: true, | ||
| signal | ||
| }); | ||
| if (chmod.exitCode !== 0) { | ||
| const stderr = await chmod.stderr(); | ||
| throw new Error(`Failed to set permissions on ${paths.join(", ")}: ${stderr}`); | ||
| } | ||
| } | ||
| /** | ||
| * Given absolute leaf paths, return the directories strictly between this | ||
| * user's home directory and each leaf. The home dir itself is excluded, as | ||
| * are any paths that fall outside the home dir. | ||
| */ | ||
| ancestorDirsUnderHome(paths) { | ||
| const dirs = /* @__PURE__ */ new Set(); | ||
| const homePrefix = `${this.homeDir}/`; | ||
| for (const p of paths) { | ||
| let dir = p.slice(0, p.lastIndexOf("/")); | ||
| while (dir.length > this.homeDir.length && dir.startsWith(homePrefix)) { | ||
| dirs.add(dir); | ||
| dir = dir.slice(0, dir.lastIndexOf("/")); | ||
| } | ||
| } | ||
| return [...dirs]; | ||
| } | ||
| /** | ||
| * Add this user to a group. | ||
| * | ||
| * @param groupname - Name of the group to join | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| async addToGroup(groupname, opts) { | ||
| require_validate_name.validateName(groupname, "group name"); | ||
| await this.sandbox.addUserToGroup(this.username, groupname, opts); | ||
| } | ||
| /** | ||
| * Remove this user from a group. | ||
| * | ||
| * @param groupname - Name of the group to leave | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| async removeFromGroup(groupname, opts) { | ||
| require_validate_name.validateName(groupname, "group name"); | ||
| await this.sandbox.removeUserFromGroup(this.username, groupname, opts); | ||
| } | ||
| }; | ||
| //#endregion | ||
| exports.SandboxUser = SandboxUser; | ||
| //# sourceMappingURL=sandbox-user.cjs.map |
| {"version":3,"file":"sandbox-user.cjs","names":["path","wrapped","Readable","stream"],"sources":["../src/sandbox-user.ts"],"sourcesContent":["import { Readable } from \"stream\";\nimport { pipeline } from \"stream/promises\";\nimport { createWriteStream } from \"fs\";\nimport { mkdir } from \"fs/promises\";\nimport { dirname, resolve } from \"path\";\nimport type { Sandbox } from \"./sandbox.js\";\nimport type { RunCommandParams } from \"./session.js\";\nimport type { Command, CommandFinished } from \"./command.js\";\nimport type { ExecutionContext } from \"./execution-context.js\";\nimport { validateName } from \"./utils/validate-name.js\";\n\n/**\n * A user context within a sandbox.\n *\n * All file and command operations default to running as this user.\n * Created via {@link Sandbox.createUser} or {@link Sandbox.asUser}.\n *\n * @hideconstructor\n */\nexport class SandboxUser implements ExecutionContext {\n /**\n * The Linux username.\n */\n readonly username: string;\n\n /**\n * The user's home directory (e.g., `/home/alice`).\n */\n readonly homeDir: string;\n\n private readonly sandbox: Sandbox;\n\n /**\n * Memoized lookup of this user's primary group.\n * See {@link SandboxUser.primaryGroup}.\n */\n private primaryGroupPromise?: Promise<string>;\n\n constructor({\n sandbox,\n username,\n }: {\n sandbox: Sandbox;\n username: string;\n }) {\n this.sandbox = sandbox;\n this.username = username;\n // `root`'s home is `/root`, not `/home/root`; every other user's home\n // follows the `/home/<username>` convention.\n this.homeDir = username === \"root\" ? \"/root\" : `/home/${username}`;\n }\n\n /**\n * Build the wrapped command args to run as this user via `sudo -u`.\n *\n * When `env` is provided, injects `env KEY=VAL ...` so that environment\n * variables survive the `sudo -u` transition.\n */\n private buildUserCommand(params: {\n cmd: string;\n args?: string[];\n env?: Record<string, string>;\n cwd?: string;\n }): { cmd: string; args: string[] } {\n const envEntries = Object.entries(params.env ?? {});\n const envArgs =\n envEntries.length > 0\n ? [\"env\", ...envEntries.map(([k, v]) => `${k}=${v}`)]\n : [];\n\n const cwd = params.cwd ?? this.homeDir;\n\n // Run as the target user via `sudo -u`, changing into `cwd` first.\n //\n // We can't set the directory via the sandbox API's `cwd` (the backend cd's\n // there before exec, and SUID binaries like sudo cannot start from a `770`\n // home dir), nor via `sudo --chdir` (the sandbox's sudoers policy forbids\n // the `-D` option). Instead we cd inside a `bash -c` wrapper.\n //\n // `cwd`, the command, its args, and any `env KEY=VAL` are passed as\n // separate positional parameters to `bash -c` (`$1` is `cwd`; `$@` after\n // the shift is the command). Because they are argv elements rather than\n // text spliced into the script, they are never re-parsed by the shell —\n // injection-safe by construction.\n return {\n cmd: \"sudo\",\n args: [\n \"-u\",\n this.username,\n \"--\",\n \"bash\",\n \"-c\",\n 'cd \"$1\" || exit 1; shift; exec \"$@\"',\n \"bash\", // $0 placeholder for `bash -c`\n cwd,\n ...envArgs,\n params.cmd,\n ...(params.args ?? []),\n ],\n };\n }\n\n /**\n * Resolve a path relative to this user's home directory.\n * Absolute paths are returned as-is.\n */\n private resolvePath(path: string): string {\n return path.startsWith(\"/\") ? path : `${this.homeDir}/${path}`;\n }\n\n /**\n * Start executing a command as this user.\n *\n * @param command - The command to execute.\n * @param args - Arguments to pass to the command.\n * @param opts - Optional parameters.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n async runCommand(\n command: string,\n args?: string[],\n opts?: { signal?: AbortSignal; timeoutMs?: number },\n ): Promise<CommandFinished>;\n\n /**\n * Start executing a command as this user 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 as this user.\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; timeoutMs?: number },\n ): Promise<Command | CommandFinished> {\n if (typeof commandOrParams === \"string\") {\n const wrapped = this.buildUserCommand({\n cmd: commandOrParams,\n args,\n });\n // Don't pass cwd to the sandbox API — the bash -c wrapper cd's for us.\n // Don't pass sudo: true — the default user already has sudo privileges.\n return this.sandbox.runCommand({\n ...wrapped,\n signal: opts?.signal,\n timeoutMs: opts?.timeoutMs,\n });\n }\n\n const params = commandOrParams;\n\n // When sudo: true is passed, delegate directly to root (skip user wrapping).\n // Don't default cwd to homeDir — the backend can't exec SUID binaries\n // from directories with restricted permissions.\n if (params.sudo) {\n return this.sandbox.runCommand({\n ...params,\n } as RunCommandParams & { detached: true });\n }\n\n const wrapped = this.buildUserCommand({\n cmd: params.cmd,\n args: params.args,\n env: params.env,\n cwd: params.cwd,\n });\n\n return this.sandbox.runCommand({\n cmd: wrapped.cmd,\n args: wrapped.args,\n // Don't pass cwd — the bash -c wrapper cd's for us (see buildUserCommand)\n // Don't pass sudo: true — the default user already has sudo privileges\n // env is already baked into the wrapped command via `env KEY=VAL`\n detached: params.detached,\n stdout: params.stdout,\n stderr: params.stderr,\n signal: params.signal,\n timeoutMs: params.timeoutMs,\n } as RunCommandParams & { detached: true });\n }\n\n /**\n * Write files to this user's home directory (or absolute paths).\n * Files are written via the sandbox HTTP API then chowned to this user.\n *\n * The HTTP API can write to user home dirs because they are group-owned\n * by the sandbox's default user group with `770` permissions.\n *\n * @param files - Array of files with path, content, and optional mode\n * @param opts - Optional parameters.\n */\n async writeFiles(\n files: { path: string; content: string | Uint8Array; mode?: number }[],\n opts?: { signal?: AbortSignal },\n ) {\n // Resolve relative paths to user's home directory\n const absoluteFiles = files.map((f) => ({\n ...f,\n path: this.resolvePath(f.path),\n }));\n\n // Write via the HTTP API (works because home dirs are group-owned\n // by the default user's group)\n await this.sandbox.writeFiles(absoluteFiles, opts);\n\n const paths = absoluteFiles.map((f) => f.path);\n if (paths.length === 0) return;\n\n // The files themselves belong to the user outright.\n await this.chownOrThrow(\n paths,\n `${this.username}:${await this.primaryGroup(opts?.signal)}`,\n opts?.signal,\n );\n\n // Any directories the write implicitly created (e.g. `data/` in\n // `data/config.json`) are owned by the default user. Hand them to the user\n // but keep them group-owned by the default user's group with mode 770 —\n // matching the home dir — so the HTTP API can still traverse and write into\n // them later.\n const dirs = this.ancestorDirsUnderHome(paths);\n if (dirs.length > 0) {\n const { group } = await this.sandbox.getDefaultUser(opts);\n await this.chownOrThrow(\n dirs,\n `${this.username}:${group}`,\n opts?.signal,\n );\n await this.chmodOrThrow(dirs, \"770\", opts?.signal);\n }\n }\n\n /**\n * Read a file from this user's context as a stream.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @returns A ReadableStream of the file contents, or null if not found\n */\n async readFile(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<NodeJS.ReadableStream | null> {\n // `\"use step\"`: touches a Node built-in (`stream`), which the @workflow\n // compiler only permits inside step functions (matches Session.readFile).\n \"use step\";\n const buffer = await this.catAsUser(file, opts);\n return buffer === null ? null : Readable.from([buffer]);\n }\n\n /**\n * Read a file from this user's context as a Buffer.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @returns The file contents as a Buffer, or null if not found\n */\n async readFileToBuffer(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<Buffer | null> {\n return this.catAsUser(file, opts);\n }\n\n /**\n * Download a file from this user's context to the local filesystem.\n *\n * @param src - Source file in the sandbox\n * @param dst - Destination on the local machine\n * @param opts - Optional parameters.\n * @returns The absolute path to the written file, or null if 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\"`: touches Node built-ins (`fs`, `path`), which the @workflow\n // compiler only permits inside step functions (matches Session.downloadFile).\n \"use step\";\n const stream = await this.readFile(src, opts);\n if (stream === null) return null;\n\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 }\n\n /**\n * Read a file as this user and return its bytes, or null if it does not\n * exist.\n *\n * Reads via `sudo -u <user> base64` rather than the HTTP file API: on stock\n * runtimes the API runs as `vercel-sandbox` and cannot read files this user\n * has kept private (e.g. mode `600`), whereas reading as the user always\n * honours the user's own permissions. The payload is base64-encoded because\n * the command output channel is UTF-8 only and would otherwise corrupt\n * binary files.\n */\n private async catAsUser(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<Buffer | null> {\n const path = file.path.startsWith(\"/\")\n ? file.path\n : `${file.cwd ?? this.homeDir}/${file.path}`;\n const result = await this.runCommand({\n cmd: \"base64\",\n args: [path],\n signal: opts?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (/No such file or directory/i.test(stderr)) return null;\n throw new Error(`Failed to read ${path}: ${stderr}`);\n }\n return Buffer.from(await result.stdout(), \"base64\");\n }\n\n /**\n * Create a directory owned by this user.\n *\n * @param path - Path of the directory to create\n * @param opts - Optional parameters.\n */\n async mkDir(path: string, opts?: { signal?: AbortSignal }): Promise<void> {\n const absPath = this.resolvePath(path);\n await this.sandbox.mkDir(absPath, opts);\n // Own the created directory plus any parents the mkdir created under the\n // home dir. Group-owned by the default user's group with mode 770 so the\n // HTTP file API can write into it (matching the home dir).\n const dirs = [absPath, ...this.ancestorDirsUnderHome([absPath])];\n const { group } = await this.sandbox.getDefaultUser(opts);\n await this.chownOrThrow(\n dirs,\n `${this.username}:${group}`,\n opts?.signal,\n );\n await this.chmodOrThrow(dirs, \"770\", opts?.signal);\n }\n\n /**\n * This user's primary group. Users created via {@link Sandbox.createUser}\n * get a group named after them, but {@link Sandbox.asUser} accepts\n * pre-existing users whose primary group can differ (e.g. system users), so\n * we resolve it from the sandbox rather than assuming `<username>`.\n *\n * The result is memoized for the lifetime of this instance.\n */\n private primaryGroup(signal?: AbortSignal): Promise<string> {\n if (!this.primaryGroupPromise) {\n this.primaryGroupPromise = this.resolvePrimaryGroup(signal).catch(\n (err) => {\n // Don't cache failures — allow a later call to retry.\n this.primaryGroupPromise = undefined;\n throw err;\n },\n );\n }\n return this.primaryGroupPromise;\n }\n\n private async resolvePrimaryGroup(signal?: AbortSignal): Promise<string> {\n const result = await this.sandbox.runCommand({\n cmd: \"id\",\n args: [\"-gn\", this.username],\n signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n throw new Error(\n `Failed to resolve the primary group of \"${this.username}\": ${stderr}`,\n );\n }\n const group = (await result.stdout()).trim();\n if (!group) {\n throw new Error(\n `Failed to resolve the primary group of \"${this.username}\"`,\n );\n }\n return group;\n }\n\n /**\n * Run `chown <ownership> <paths...>` as root, throwing on failure.\n */\n private async chownOrThrow(\n paths: string[],\n ownership: string,\n signal?: AbortSignal,\n ): Promise<void> {\n const chown = await this.sandbox.runCommand({\n cmd: \"chown\",\n args: [ownership, ...paths],\n sudo: true,\n signal,\n });\n if (chown.exitCode !== 0) {\n const stderr = await chown.stderr();\n throw new Error(\n `Failed to set ownership on ${paths.join(\", \")}: ${stderr}`,\n );\n }\n }\n\n /**\n * Run `chmod <mode> <paths...>` as root, throwing on failure.\n */\n private async chmodOrThrow(\n paths: string[],\n mode: string,\n signal?: AbortSignal,\n ): Promise<void> {\n const chmod = await this.sandbox.runCommand({\n cmd: \"chmod\",\n args: [mode, ...paths],\n sudo: true,\n signal,\n });\n if (chmod.exitCode !== 0) {\n const stderr = await chmod.stderr();\n throw new Error(\n `Failed to set permissions on ${paths.join(\", \")}: ${stderr}`,\n );\n }\n }\n\n /**\n * Given absolute leaf paths, return the directories strictly between this\n * user's home directory and each leaf. The home dir itself is excluded, as\n * are any paths that fall outside the home dir.\n */\n private ancestorDirsUnderHome(paths: string[]): string[] {\n const dirs = new Set<string>();\n const homePrefix = `${this.homeDir}/`;\n for (const p of paths) {\n let dir = p.slice(0, p.lastIndexOf(\"/\"));\n while (dir.length > this.homeDir.length && dir.startsWith(homePrefix)) {\n dirs.add(dir);\n dir = dir.slice(0, dir.lastIndexOf(\"/\"));\n }\n }\n return [...dirs];\n }\n\n /**\n * Add this user to a group.\n *\n * @param groupname - Name of the group to join\n * @param opts - Optional parameters.\n */\n async addToGroup(\n groupname: string,\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n validateName(groupname, \"group name\");\n await this.sandbox.addUserToGroup(this.username, groupname, opts);\n }\n\n /**\n * Remove this user from a group.\n *\n * @param groupname - Name of the group to leave\n * @param opts - Optional parameters.\n */\n async removeFromGroup(\n groupname: string,\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n validateName(groupname, \"group name\");\n await this.sandbox.removeUserFromGroup(this.username, groupname, opts);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAmBA,IAAa,cAAb,MAAqD;CAmBnD,YAAY,EACV,SACA,YAIC;AACD,OAAK,UAAU;AACf,OAAK,WAAW;AAGhB,OAAK,UAAU,aAAa,SAAS,UAAU,SAAS;;;;;;;;CAS1D,AAAQ,iBAAiB,QAKW;EAClC,MAAM,aAAa,OAAO,QAAQ,OAAO,OAAO,EAAE,CAAC;EACnD,MAAM,UACJ,WAAW,SAAS,IAChB,CAAC,OAAO,GAAG,WAAW,KAAK,CAAC,GAAG,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC,GACnD,EAAE;EAER,MAAM,MAAM,OAAO,OAAO,KAAK;AAc/B,SAAO;GACL,KAAK;GACL,MAAM;IACJ;IACA,KAAK;IACL;IACA;IACA;IACA;IACA;IACA;IACA,GAAG;IACH,OAAO;IACP,GAAI,OAAO,QAAQ,EAAE;IACtB;GACF;;;;;;CAOH,AAAQ,YAAY,QAAsB;AACxC,SAAOA,OAAK,WAAW,IAAI,GAAGA,SAAO,GAAG,KAAK,QAAQ,GAAGA;;CAmC1D,MAAM,WACJ,iBACA,MACA,MACoC;AACpC,MAAI,OAAO,oBAAoB,UAAU;GACvC,MAAMC,YAAU,KAAK,iBAAiB;IACpC,KAAK;IACL;IACD,CAAC;AAGF,UAAO,KAAK,QAAQ,WAAW;IAC7B,GAAGA;IACH,QAAQ,MAAM;IACd,WAAW,MAAM;IAClB,CAAC;;EAGJ,MAAM,SAAS;AAKf,MAAI,OAAO,KACT,QAAO,KAAK,QAAQ,WAAW,EAC7B,GAAG,QACJ,CAA0C;EAG7C,MAAM,UAAU,KAAK,iBAAiB;GACpC,KAAK,OAAO;GACZ,MAAM,OAAO;GACb,KAAK,OAAO;GACZ,KAAK,OAAO;GACb,CAAC;AAEF,SAAO,KAAK,QAAQ,WAAW;GAC7B,KAAK,QAAQ;GACb,MAAM,QAAQ;GAId,UAAU,OAAO;GACjB,QAAQ,OAAO;GACf,QAAQ,OAAO;GACf,QAAQ,OAAO;GACf,WAAW,OAAO;GACnB,CAA0C;;;;;;;;;;;;CAa7C,MAAM,WACJ,OACA,MACA;EAEA,MAAM,gBAAgB,MAAM,KAAK,OAAO;GACtC,GAAG;GACH,MAAM,KAAK,YAAY,EAAE,KAAK;GAC/B,EAAE;AAIH,QAAM,KAAK,QAAQ,WAAW,eAAe,KAAK;EAElD,MAAM,QAAQ,cAAc,KAAK,MAAM,EAAE,KAAK;AAC9C,MAAI,MAAM,WAAW,EAAG;AAGxB,QAAM,KAAK,aACT,OACA,GAAG,KAAK,SAAS,GAAG,MAAM,KAAK,aAAa,MAAM,OAAO,IACzD,MAAM,OACP;EAOD,MAAM,OAAO,KAAK,sBAAsB,MAAM;AAC9C,MAAI,KAAK,SAAS,GAAG;GACnB,MAAM,EAAE,UAAU,MAAM,KAAK,QAAQ,eAAe,KAAK;AACzD,SAAM,KAAK,aACT,MACA,GAAG,KAAK,SAAS,GAAG,SACpB,MAAM,OACP;AACD,SAAM,KAAK,aAAa,MAAM,OAAO,MAAM,OAAO;;;;;;;;;;CAWtD,MAAM,SACJ,MACA,MACuC;AAGvC;EACA,MAAM,SAAS,MAAM,KAAK,UAAU,MAAM,KAAK;AAC/C,SAAO,WAAW,OAAO,OAAOC,gBAAS,KAAK,CAAC,OAAO,CAAC;;;;;;;;;CAUzD,MAAM,iBACJ,MACA,MACwB;AACxB,SAAO,KAAK,UAAU,MAAM,KAAK;;;;;;;;;;CAWnC,MAAM,aACJ,KACA,KACA,MACwB;AAGxB;EACA,MAAMC,WAAS,MAAM,KAAK,SAAS,KAAK,KAAK;AAC7C,MAAIA,aAAW,KAAM,QAAO;EAE5B,MAAM,4BAAkB,IAAI,OAAO,IAAI,IAAI,KAAK;AAChD,MAAI,MAAM,eACR,gDAAoB,QAAQ,EAAE,EAAE,WAAW,MAAM,CAAC;AAEpD,sCAAeA,oCAA0B,QAAQ,EAAE,EACjD,QAAQ,MAAM,QACf,CAAC;AACF,SAAO;;;;;;;;;;;;;CAcT,MAAc,UACZ,MACA,MACwB;EACxB,MAAMH,SAAO,KAAK,KAAK,WAAW,IAAI,GAClC,KAAK,OACL,GAAG,KAAK,OAAO,KAAK,QAAQ,GAAG,KAAK;EACxC,MAAM,SAAS,MAAM,KAAK,WAAW;GACnC,KAAK;GACL,MAAM,CAACA,OAAK;GACZ,QAAQ,MAAM;GACf,CAAC;AACF,MAAI,OAAO,aAAa,GAAG;GACzB,MAAM,SAAS,MAAM,OAAO,QAAQ;AACpC,OAAI,6BAA6B,KAAK,OAAO,CAAE,QAAO;AACtD,SAAM,IAAI,MAAM,kBAAkBA,OAAK,IAAI,SAAS;;AAEtD,SAAO,OAAO,KAAK,MAAM,OAAO,QAAQ,EAAE,SAAS;;;;;;;;CASrD,MAAM,MAAM,QAAc,MAAgD;EACxE,MAAM,UAAU,KAAK,YAAYA,OAAK;AACtC,QAAM,KAAK,QAAQ,MAAM,SAAS,KAAK;EAIvC,MAAM,OAAO,CAAC,SAAS,GAAG,KAAK,sBAAsB,CAAC,QAAQ,CAAC,CAAC;EAChE,MAAM,EAAE,UAAU,MAAM,KAAK,QAAQ,eAAe,KAAK;AACzD,QAAM,KAAK,aACT,MACA,GAAG,KAAK,SAAS,GAAG,SACpB,MAAM,OACP;AACD,QAAM,KAAK,aAAa,MAAM,OAAO,MAAM,OAAO;;;;;;;;;;CAWpD,AAAQ,aAAa,QAAuC;AAC1D,MAAI,CAAC,KAAK,oBACR,MAAK,sBAAsB,KAAK,oBAAoB,OAAO,CAAC,OACzD,QAAQ;AAEP,QAAK,sBAAsB;AAC3B,SAAM;IAET;AAEH,SAAO,KAAK;;CAGd,MAAc,oBAAoB,QAAuC;EACvE,MAAM,SAAS,MAAM,KAAK,QAAQ,WAAW;GAC3C,KAAK;GACL,MAAM,CAAC,OAAO,KAAK,SAAS;GAC5B;GACD,CAAC;AACF,MAAI,OAAO,aAAa,GAAG;GACzB,MAAM,SAAS,MAAM,OAAO,QAAQ;AACpC,SAAM,IAAI,MACR,2CAA2C,KAAK,SAAS,KAAK,SAC/D;;EAEH,MAAM,SAAS,MAAM,OAAO,QAAQ,EAAE,MAAM;AAC5C,MAAI,CAAC,MACH,OAAM,IAAI,MACR,2CAA2C,KAAK,SAAS,GAC1D;AAEH,SAAO;;;;;CAMT,MAAc,aACZ,OACA,WACA,QACe;EACf,MAAM,QAAQ,MAAM,KAAK,QAAQ,WAAW;GAC1C,KAAK;GACL,MAAM,CAAC,WAAW,GAAG,MAAM;GAC3B,MAAM;GACN;GACD,CAAC;AACF,MAAI,MAAM,aAAa,GAAG;GACxB,MAAM,SAAS,MAAM,MAAM,QAAQ;AACnC,SAAM,IAAI,MACR,8BAA8B,MAAM,KAAK,KAAK,CAAC,IAAI,SACpD;;;;;;CAOL,MAAc,aACZ,OACA,MACA,QACe;EACf,MAAM,QAAQ,MAAM,KAAK,QAAQ,WAAW;GAC1C,KAAK;GACL,MAAM,CAAC,MAAM,GAAG,MAAM;GACtB,MAAM;GACN;GACD,CAAC;AACF,MAAI,MAAM,aAAa,GAAG;GACxB,MAAM,SAAS,MAAM,MAAM,QAAQ;AACnC,SAAM,IAAI,MACR,gCAAgC,MAAM,KAAK,KAAK,CAAC,IAAI,SACtD;;;;;;;;CASL,AAAQ,sBAAsB,OAA2B;EACvD,MAAM,uBAAO,IAAI,KAAa;EAC9B,MAAM,aAAa,GAAG,KAAK,QAAQ;AACnC,OAAK,MAAM,KAAK,OAAO;GACrB,IAAI,MAAM,EAAE,MAAM,GAAG,EAAE,YAAY,IAAI,CAAC;AACxC,UAAO,IAAI,SAAS,KAAK,QAAQ,UAAU,IAAI,WAAW,WAAW,EAAE;AACrE,SAAK,IAAI,IAAI;AACb,UAAM,IAAI,MAAM,GAAG,IAAI,YAAY,IAAI,CAAC;;;AAG5C,SAAO,CAAC,GAAG,KAAK;;;;;;;;CASlB,MAAM,WACJ,WACA,MACe;AACf,qCAAa,WAAW,aAAa;AACrC,QAAM,KAAK,QAAQ,eAAe,KAAK,UAAU,WAAW,KAAK;;;;;;;;CASnE,MAAM,gBACJ,WACA,MACe;AACf,qCAAa,WAAW,aAAa;AACrC,QAAM,KAAK,QAAQ,oBAAoB,KAAK,UAAU,WAAW,KAAK"} |
| import { Command, CommandFinished } from "./command.cjs"; | ||
| import { ExecutionContext } from "./execution-context.cjs"; | ||
| import { RunCommandParams } from "./session.cjs"; | ||
| import { Sandbox } from "./sandbox.cjs"; | ||
| //#region src/sandbox-user.d.ts | ||
| /** | ||
| * A user context within a sandbox. | ||
| * | ||
| * All file and command operations default to running as this user. | ||
| * Created via {@link Sandbox.createUser} or {@link Sandbox.asUser}. | ||
| * | ||
| * @hideconstructor | ||
| */ | ||
| declare class SandboxUser implements ExecutionContext { | ||
| /** | ||
| * The Linux username. | ||
| */ | ||
| readonly username: string; | ||
| /** | ||
| * The user's home directory (e.g., `/home/alice`). | ||
| */ | ||
| readonly homeDir: string; | ||
| private readonly sandbox; | ||
| /** | ||
| * Memoized lookup of this user's primary group. | ||
| * See {@link SandboxUser.primaryGroup}. | ||
| */ | ||
| private primaryGroupPromise?; | ||
| constructor({ | ||
| sandbox, | ||
| username | ||
| }: { | ||
| sandbox: Sandbox; | ||
| username: string; | ||
| }); | ||
| /** | ||
| * Build the wrapped command args to run as this user via `sudo -u`. | ||
| * | ||
| * When `env` is provided, injects `env KEY=VAL ...` so that environment | ||
| * variables survive the `sudo -u` transition. | ||
| */ | ||
| private buildUserCommand; | ||
| /** | ||
| * Resolve a path relative to this user's home directory. | ||
| * Absolute paths are returned as-is. | ||
| */ | ||
| private resolvePath; | ||
| /** | ||
| * Start executing a command as this user. | ||
| * | ||
| * @param command - The command to execute. | ||
| * @param args - Arguments to pass to the command. | ||
| * @param opts - Optional parameters. | ||
| * @returns A {@link CommandFinished} result once execution is done. | ||
| */ | ||
| runCommand(command: string, args?: string[], opts?: { | ||
| signal?: AbortSignal; | ||
| timeoutMs?: number; | ||
| }): Promise<CommandFinished>; | ||
| /** | ||
| * Start executing a command as this user in detached mode. | ||
| * | ||
| * @param params - The command parameters. | ||
| * @returns A {@link Command} instance for the running command. | ||
| */ | ||
| runCommand(params: RunCommandParams & { | ||
| detached: true; | ||
| }): Promise<Command>; | ||
| /** | ||
| * Start executing a command as this user. | ||
| * | ||
| * @param params - The command parameters. | ||
| * @returns A {@link CommandFinished} result once execution is done. | ||
| */ | ||
| runCommand(params: RunCommandParams): Promise<CommandFinished>; | ||
| /** | ||
| * Write files to this user's home directory (or absolute paths). | ||
| * Files are written via the sandbox HTTP API then chowned to this user. | ||
| * | ||
| * The HTTP API can write to user home dirs because they are group-owned | ||
| * by the sandbox's default user group with `770` permissions. | ||
| * | ||
| * @param files - Array of files with path, content, and optional mode | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| writeFiles(files: { | ||
| path: string; | ||
| content: string | Uint8Array; | ||
| mode?: number; | ||
| }[], opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<void>; | ||
| /** | ||
| * Read a file from this user's context as a stream. | ||
| * | ||
| * @param file - File to read, with path and optional cwd | ||
| * @param opts - Optional parameters. | ||
| * @returns A ReadableStream of the file contents, or null if not found | ||
| */ | ||
| readFile(file: { | ||
| path: string; | ||
| cwd?: string; | ||
| }, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<NodeJS.ReadableStream | null>; | ||
| /** | ||
| * Read a file from this user's context as a Buffer. | ||
| * | ||
| * @param file - File to read, with path and optional cwd | ||
| * @param opts - Optional parameters. | ||
| * @returns The file contents as a Buffer, or null if not found | ||
| */ | ||
| readFileToBuffer(file: { | ||
| path: string; | ||
| cwd?: string; | ||
| }, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<Buffer | null>; | ||
| /** | ||
| * Download a file from this user's context to the local filesystem. | ||
| * | ||
| * @param src - Source file in the sandbox | ||
| * @param dst - Destination on the local machine | ||
| * @param opts - Optional parameters. | ||
| * @returns The absolute path to the written file, or null if not found | ||
| */ | ||
| downloadFile(src: { | ||
| path: string; | ||
| cwd?: string; | ||
| }, dst: { | ||
| path: string; | ||
| cwd?: string; | ||
| }, opts?: { | ||
| mkdirRecursive?: boolean; | ||
| signal?: AbortSignal; | ||
| }): Promise<string | null>; | ||
| /** | ||
| * Read a file as this user and return its bytes, or null if it does not | ||
| * exist. | ||
| * | ||
| * Reads via `sudo -u <user> base64` rather than the HTTP file API: on stock | ||
| * runtimes the API runs as `vercel-sandbox` and cannot read files this user | ||
| * has kept private (e.g. mode `600`), whereas reading as the user always | ||
| * honours the user's own permissions. The payload is base64-encoded because | ||
| * the command output channel is UTF-8 only and would otherwise corrupt | ||
| * binary files. | ||
| */ | ||
| private catAsUser; | ||
| /** | ||
| * Create a directory owned by this user. | ||
| * | ||
| * @param path - Path of the directory to create | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| mkDir(path: string, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<void>; | ||
| /** | ||
| * This user's primary group. Users created via {@link Sandbox.createUser} | ||
| * get a group named after them, but {@link Sandbox.asUser} accepts | ||
| * pre-existing users whose primary group can differ (e.g. system users), so | ||
| * we resolve it from the sandbox rather than assuming `<username>`. | ||
| * | ||
| * The result is memoized for the lifetime of this instance. | ||
| */ | ||
| private primaryGroup; | ||
| private resolvePrimaryGroup; | ||
| /** | ||
| * Run `chown <ownership> <paths...>` as root, throwing on failure. | ||
| */ | ||
| private chownOrThrow; | ||
| /** | ||
| * Run `chmod <mode> <paths...>` as root, throwing on failure. | ||
| */ | ||
| private chmodOrThrow; | ||
| /** | ||
| * Given absolute leaf paths, return the directories strictly between this | ||
| * user's home directory and each leaf. The home dir itself is excluded, as | ||
| * are any paths that fall outside the home dir. | ||
| */ | ||
| private ancestorDirsUnderHome; | ||
| /** | ||
| * Add this user to a group. | ||
| * | ||
| * @param groupname - Name of the group to join | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| addToGroup(groupname: string, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<void>; | ||
| /** | ||
| * Remove this user from a group. | ||
| * | ||
| * @param groupname - Name of the group to leave | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| removeFromGroup(groupname: string, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<void>; | ||
| } | ||
| //#endregion | ||
| export { SandboxUser }; | ||
| //# sourceMappingURL=sandbox-user.d.cts.map |
| import { Command, CommandFinished } from "./command.js"; | ||
| import { ExecutionContext } from "./execution-context.js"; | ||
| import { RunCommandParams } from "./session.js"; | ||
| import { Sandbox } from "./sandbox.js"; | ||
| //#region src/sandbox-user.d.ts | ||
| /** | ||
| * A user context within a sandbox. | ||
| * | ||
| * All file and command operations default to running as this user. | ||
| * Created via {@link Sandbox.createUser} or {@link Sandbox.asUser}. | ||
| * | ||
| * @hideconstructor | ||
| */ | ||
| declare class SandboxUser implements ExecutionContext { | ||
| /** | ||
| * The Linux username. | ||
| */ | ||
| readonly username: string; | ||
| /** | ||
| * The user's home directory (e.g., `/home/alice`). | ||
| */ | ||
| readonly homeDir: string; | ||
| private readonly sandbox; | ||
| /** | ||
| * Memoized lookup of this user's primary group. | ||
| * See {@link SandboxUser.primaryGroup}. | ||
| */ | ||
| private primaryGroupPromise?; | ||
| constructor({ | ||
| sandbox, | ||
| username | ||
| }: { | ||
| sandbox: Sandbox; | ||
| username: string; | ||
| }); | ||
| /** | ||
| * Build the wrapped command args to run as this user via `sudo -u`. | ||
| * | ||
| * When `env` is provided, injects `env KEY=VAL ...` so that environment | ||
| * variables survive the `sudo -u` transition. | ||
| */ | ||
| private buildUserCommand; | ||
| /** | ||
| * Resolve a path relative to this user's home directory. | ||
| * Absolute paths are returned as-is. | ||
| */ | ||
| private resolvePath; | ||
| /** | ||
| * Start executing a command as this user. | ||
| * | ||
| * @param command - The command to execute. | ||
| * @param args - Arguments to pass to the command. | ||
| * @param opts - Optional parameters. | ||
| * @returns A {@link CommandFinished} result once execution is done. | ||
| */ | ||
| runCommand(command: string, args?: string[], opts?: { | ||
| signal?: AbortSignal; | ||
| timeoutMs?: number; | ||
| }): Promise<CommandFinished>; | ||
| /** | ||
| * Start executing a command as this user in detached mode. | ||
| * | ||
| * @param params - The command parameters. | ||
| * @returns A {@link Command} instance for the running command. | ||
| */ | ||
| runCommand(params: RunCommandParams & { | ||
| detached: true; | ||
| }): Promise<Command>; | ||
| /** | ||
| * Start executing a command as this user. | ||
| * | ||
| * @param params - The command parameters. | ||
| * @returns A {@link CommandFinished} result once execution is done. | ||
| */ | ||
| runCommand(params: RunCommandParams): Promise<CommandFinished>; | ||
| /** | ||
| * Write files to this user's home directory (or absolute paths). | ||
| * Files are written via the sandbox HTTP API then chowned to this user. | ||
| * | ||
| * The HTTP API can write to user home dirs because they are group-owned | ||
| * by the sandbox's default user group with `770` permissions. | ||
| * | ||
| * @param files - Array of files with path, content, and optional mode | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| writeFiles(files: { | ||
| path: string; | ||
| content: string | Uint8Array; | ||
| mode?: number; | ||
| }[], opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<void>; | ||
| /** | ||
| * Read a file from this user's context as a stream. | ||
| * | ||
| * @param file - File to read, with path and optional cwd | ||
| * @param opts - Optional parameters. | ||
| * @returns A ReadableStream of the file contents, or null if not found | ||
| */ | ||
| readFile(file: { | ||
| path: string; | ||
| cwd?: string; | ||
| }, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<NodeJS.ReadableStream | null>; | ||
| /** | ||
| * Read a file from this user's context as a Buffer. | ||
| * | ||
| * @param file - File to read, with path and optional cwd | ||
| * @param opts - Optional parameters. | ||
| * @returns The file contents as a Buffer, or null if not found | ||
| */ | ||
| readFileToBuffer(file: { | ||
| path: string; | ||
| cwd?: string; | ||
| }, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<Buffer | null>; | ||
| /** | ||
| * Download a file from this user's context to the local filesystem. | ||
| * | ||
| * @param src - Source file in the sandbox | ||
| * @param dst - Destination on the local machine | ||
| * @param opts - Optional parameters. | ||
| * @returns The absolute path to the written file, or null if not found | ||
| */ | ||
| downloadFile(src: { | ||
| path: string; | ||
| cwd?: string; | ||
| }, dst: { | ||
| path: string; | ||
| cwd?: string; | ||
| }, opts?: { | ||
| mkdirRecursive?: boolean; | ||
| signal?: AbortSignal; | ||
| }): Promise<string | null>; | ||
| /** | ||
| * Read a file as this user and return its bytes, or null if it does not | ||
| * exist. | ||
| * | ||
| * Reads via `sudo -u <user> base64` rather than the HTTP file API: on stock | ||
| * runtimes the API runs as `vercel-sandbox` and cannot read files this user | ||
| * has kept private (e.g. mode `600`), whereas reading as the user always | ||
| * honours the user's own permissions. The payload is base64-encoded because | ||
| * the command output channel is UTF-8 only and would otherwise corrupt | ||
| * binary files. | ||
| */ | ||
| private catAsUser; | ||
| /** | ||
| * Create a directory owned by this user. | ||
| * | ||
| * @param path - Path of the directory to create | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| mkDir(path: string, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<void>; | ||
| /** | ||
| * This user's primary group. Users created via {@link Sandbox.createUser} | ||
| * get a group named after them, but {@link Sandbox.asUser} accepts | ||
| * pre-existing users whose primary group can differ (e.g. system users), so | ||
| * we resolve it from the sandbox rather than assuming `<username>`. | ||
| * | ||
| * The result is memoized for the lifetime of this instance. | ||
| */ | ||
| private primaryGroup; | ||
| private resolvePrimaryGroup; | ||
| /** | ||
| * Run `chown <ownership> <paths...>` as root, throwing on failure. | ||
| */ | ||
| private chownOrThrow; | ||
| /** | ||
| * Run `chmod <mode> <paths...>` as root, throwing on failure. | ||
| */ | ||
| private chmodOrThrow; | ||
| /** | ||
| * Given absolute leaf paths, return the directories strictly between this | ||
| * user's home directory and each leaf. The home dir itself is excluded, as | ||
| * are any paths that fall outside the home dir. | ||
| */ | ||
| private ancestorDirsUnderHome; | ||
| /** | ||
| * Add this user to a group. | ||
| * | ||
| * @param groupname - Name of the group to join | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| addToGroup(groupname: string, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<void>; | ||
| /** | ||
| * Remove this user from a group. | ||
| * | ||
| * @param groupname - Name of the group to leave | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| removeFromGroup(groupname: string, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<void>; | ||
| } | ||
| //#endregion | ||
| export { SandboxUser }; | ||
| //# sourceMappingURL=sandbox-user.d.ts.map |
| import { validateName } from "./utils/validate-name.js"; | ||
| import { Readable } from "stream"; | ||
| import { dirname, resolve } from "path"; | ||
| import { pipeline } from "stream/promises"; | ||
| import { createWriteStream } from "fs"; | ||
| import { mkdir } from "fs/promises"; | ||
| //#region src/sandbox-user.ts | ||
| /** | ||
| * A user context within a sandbox. | ||
| * | ||
| * All file and command operations default to running as this user. | ||
| * Created via {@link Sandbox.createUser} or {@link Sandbox.asUser}. | ||
| * | ||
| * @hideconstructor | ||
| */ | ||
| var SandboxUser = class { | ||
| constructor({ sandbox, username }) { | ||
| this.sandbox = sandbox; | ||
| this.username = username; | ||
| this.homeDir = username === "root" ? "/root" : `/home/${username}`; | ||
| } | ||
| /** | ||
| * Build the wrapped command args to run as this user via `sudo -u`. | ||
| * | ||
| * When `env` is provided, injects `env KEY=VAL ...` so that environment | ||
| * variables survive the `sudo -u` transition. | ||
| */ | ||
| buildUserCommand(params) { | ||
| const envEntries = Object.entries(params.env ?? {}); | ||
| const envArgs = envEntries.length > 0 ? ["env", ...envEntries.map(([k, v]) => `${k}=${v}`)] : []; | ||
| const cwd = params.cwd ?? this.homeDir; | ||
| return { | ||
| cmd: "sudo", | ||
| args: [ | ||
| "-u", | ||
| this.username, | ||
| "--", | ||
| "bash", | ||
| "-c", | ||
| "cd \"$1\" || exit 1; shift; exec \"$@\"", | ||
| "bash", | ||
| cwd, | ||
| ...envArgs, | ||
| params.cmd, | ||
| ...params.args ?? [] | ||
| ] | ||
| }; | ||
| } | ||
| /** | ||
| * Resolve a path relative to this user's home directory. | ||
| * Absolute paths are returned as-is. | ||
| */ | ||
| resolvePath(path$1) { | ||
| return path$1.startsWith("/") ? path$1 : `${this.homeDir}/${path$1}`; | ||
| } | ||
| async runCommand(commandOrParams, args, opts) { | ||
| if (typeof commandOrParams === "string") { | ||
| const wrapped$1 = this.buildUserCommand({ | ||
| cmd: commandOrParams, | ||
| args | ||
| }); | ||
| return this.sandbox.runCommand({ | ||
| ...wrapped$1, | ||
| signal: opts?.signal, | ||
| timeoutMs: opts?.timeoutMs | ||
| }); | ||
| } | ||
| const params = commandOrParams; | ||
| if (params.sudo) return this.sandbox.runCommand({ ...params }); | ||
| const wrapped = this.buildUserCommand({ | ||
| cmd: params.cmd, | ||
| args: params.args, | ||
| env: params.env, | ||
| cwd: params.cwd | ||
| }); | ||
| return this.sandbox.runCommand({ | ||
| cmd: wrapped.cmd, | ||
| args: wrapped.args, | ||
| detached: params.detached, | ||
| stdout: params.stdout, | ||
| stderr: params.stderr, | ||
| signal: params.signal, | ||
| timeoutMs: params.timeoutMs | ||
| }); | ||
| } | ||
| /** | ||
| * Write files to this user's home directory (or absolute paths). | ||
| * Files are written via the sandbox HTTP API then chowned to this user. | ||
| * | ||
| * The HTTP API can write to user home dirs because they are group-owned | ||
| * by the sandbox's default user group with `770` permissions. | ||
| * | ||
| * @param files - Array of files with path, content, and optional mode | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| async writeFiles(files, opts) { | ||
| const absoluteFiles = files.map((f) => ({ | ||
| ...f, | ||
| path: this.resolvePath(f.path) | ||
| })); | ||
| await this.sandbox.writeFiles(absoluteFiles, opts); | ||
| const paths = absoluteFiles.map((f) => f.path); | ||
| if (paths.length === 0) return; | ||
| await this.chownOrThrow(paths, `${this.username}:${await this.primaryGroup(opts?.signal)}`, opts?.signal); | ||
| const dirs = this.ancestorDirsUnderHome(paths); | ||
| if (dirs.length > 0) { | ||
| const { group } = await this.sandbox.getDefaultUser(opts); | ||
| await this.chownOrThrow(dirs, `${this.username}:${group}`, opts?.signal); | ||
| await this.chmodOrThrow(dirs, "770", opts?.signal); | ||
| } | ||
| } | ||
| /** | ||
| * Read a file from this user's context as a stream. | ||
| * | ||
| * @param file - File to read, with path and optional cwd | ||
| * @param opts - Optional parameters. | ||
| * @returns A ReadableStream of the file contents, or null if not found | ||
| */ | ||
| async readFile(file, opts) { | ||
| "use step"; | ||
| const buffer = await this.catAsUser(file, opts); | ||
| return buffer === null ? null : Readable.from([buffer]); | ||
| } | ||
| /** | ||
| * Read a file from this user's context as a Buffer. | ||
| * | ||
| * @param file - File to read, with path and optional cwd | ||
| * @param opts - Optional parameters. | ||
| * @returns The file contents as a Buffer, or null if not found | ||
| */ | ||
| async readFileToBuffer(file, opts) { | ||
| return this.catAsUser(file, opts); | ||
| } | ||
| /** | ||
| * Download a file from this user's context to the local filesystem. | ||
| * | ||
| * @param src - Source file in the sandbox | ||
| * @param dst - Destination on the local machine | ||
| * @param opts - Optional parameters. | ||
| * @returns The absolute path to the written file, or null if not found | ||
| */ | ||
| async downloadFile(src, dst, opts) { | ||
| "use step"; | ||
| const stream = await this.readFile(src, opts); | ||
| if (stream === null) return null; | ||
| const dstPath = resolve(dst.cwd ?? "", dst.path); | ||
| if (opts?.mkdirRecursive) await mkdir(dirname(dstPath), { recursive: true }); | ||
| await pipeline(stream, createWriteStream(dstPath), { signal: opts?.signal }); | ||
| return dstPath; | ||
| } | ||
| /** | ||
| * Read a file as this user and return its bytes, or null if it does not | ||
| * exist. | ||
| * | ||
| * Reads via `sudo -u <user> base64` rather than the HTTP file API: on stock | ||
| * runtimes the API runs as `vercel-sandbox` and cannot read files this user | ||
| * has kept private (e.g. mode `600`), whereas reading as the user always | ||
| * honours the user's own permissions. The payload is base64-encoded because | ||
| * the command output channel is UTF-8 only and would otherwise corrupt | ||
| * binary files. | ||
| */ | ||
| async catAsUser(file, opts) { | ||
| const path$1 = file.path.startsWith("/") ? file.path : `${file.cwd ?? this.homeDir}/${file.path}`; | ||
| const result = await this.runCommand({ | ||
| cmd: "base64", | ||
| args: [path$1], | ||
| signal: opts?.signal | ||
| }); | ||
| if (result.exitCode !== 0) { | ||
| const stderr = await result.stderr(); | ||
| if (/No such file or directory/i.test(stderr)) return null; | ||
| throw new Error(`Failed to read ${path$1}: ${stderr}`); | ||
| } | ||
| return Buffer.from(await result.stdout(), "base64"); | ||
| } | ||
| /** | ||
| * Create a directory owned by this user. | ||
| * | ||
| * @param path - Path of the directory to create | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| async mkDir(path$1, opts) { | ||
| const absPath = this.resolvePath(path$1); | ||
| await this.sandbox.mkDir(absPath, opts); | ||
| const dirs = [absPath, ...this.ancestorDirsUnderHome([absPath])]; | ||
| const { group } = await this.sandbox.getDefaultUser(opts); | ||
| await this.chownOrThrow(dirs, `${this.username}:${group}`, opts?.signal); | ||
| await this.chmodOrThrow(dirs, "770", opts?.signal); | ||
| } | ||
| /** | ||
| * This user's primary group. Users created via {@link Sandbox.createUser} | ||
| * get a group named after them, but {@link Sandbox.asUser} accepts | ||
| * pre-existing users whose primary group can differ (e.g. system users), so | ||
| * we resolve it from the sandbox rather than assuming `<username>`. | ||
| * | ||
| * The result is memoized for the lifetime of this instance. | ||
| */ | ||
| primaryGroup(signal) { | ||
| if (!this.primaryGroupPromise) this.primaryGroupPromise = this.resolvePrimaryGroup(signal).catch((err) => { | ||
| this.primaryGroupPromise = void 0; | ||
| throw err; | ||
| }); | ||
| return this.primaryGroupPromise; | ||
| } | ||
| async resolvePrimaryGroup(signal) { | ||
| const result = await this.sandbox.runCommand({ | ||
| cmd: "id", | ||
| args: ["-gn", this.username], | ||
| signal | ||
| }); | ||
| if (result.exitCode !== 0) { | ||
| const stderr = await result.stderr(); | ||
| throw new Error(`Failed to resolve the primary group of "${this.username}": ${stderr}`); | ||
| } | ||
| const group = (await result.stdout()).trim(); | ||
| if (!group) throw new Error(`Failed to resolve the primary group of "${this.username}"`); | ||
| return group; | ||
| } | ||
| /** | ||
| * Run `chown <ownership> <paths...>` as root, throwing on failure. | ||
| */ | ||
| async chownOrThrow(paths, ownership, signal) { | ||
| const chown = await this.sandbox.runCommand({ | ||
| cmd: "chown", | ||
| args: [ownership, ...paths], | ||
| sudo: true, | ||
| signal | ||
| }); | ||
| if (chown.exitCode !== 0) { | ||
| const stderr = await chown.stderr(); | ||
| throw new Error(`Failed to set ownership on ${paths.join(", ")}: ${stderr}`); | ||
| } | ||
| } | ||
| /** | ||
| * Run `chmod <mode> <paths...>` as root, throwing on failure. | ||
| */ | ||
| async chmodOrThrow(paths, mode, signal) { | ||
| const chmod = await this.sandbox.runCommand({ | ||
| cmd: "chmod", | ||
| args: [mode, ...paths], | ||
| sudo: true, | ||
| signal | ||
| }); | ||
| if (chmod.exitCode !== 0) { | ||
| const stderr = await chmod.stderr(); | ||
| throw new Error(`Failed to set permissions on ${paths.join(", ")}: ${stderr}`); | ||
| } | ||
| } | ||
| /** | ||
| * Given absolute leaf paths, return the directories strictly between this | ||
| * user's home directory and each leaf. The home dir itself is excluded, as | ||
| * are any paths that fall outside the home dir. | ||
| */ | ||
| ancestorDirsUnderHome(paths) { | ||
| const dirs = /* @__PURE__ */ new Set(); | ||
| const homePrefix = `${this.homeDir}/`; | ||
| for (const p of paths) { | ||
| let dir = p.slice(0, p.lastIndexOf("/")); | ||
| while (dir.length > this.homeDir.length && dir.startsWith(homePrefix)) { | ||
| dirs.add(dir); | ||
| dir = dir.slice(0, dir.lastIndexOf("/")); | ||
| } | ||
| } | ||
| return [...dirs]; | ||
| } | ||
| /** | ||
| * Add this user to a group. | ||
| * | ||
| * @param groupname - Name of the group to join | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| async addToGroup(groupname, opts) { | ||
| validateName(groupname, "group name"); | ||
| await this.sandbox.addUserToGroup(this.username, groupname, opts); | ||
| } | ||
| /** | ||
| * Remove this user from a group. | ||
| * | ||
| * @param groupname - Name of the group to leave | ||
| * @param opts - Optional parameters. | ||
| */ | ||
| async removeFromGroup(groupname, opts) { | ||
| validateName(groupname, "group name"); | ||
| await this.sandbox.removeUserFromGroup(this.username, groupname, opts); | ||
| } | ||
| }; | ||
| //#endregion | ||
| export { SandboxUser }; | ||
| //# sourceMappingURL=sandbox-user.js.map |
| {"version":3,"file":"sandbox-user.js","names":["path","wrapped"],"sources":["../src/sandbox-user.ts"],"sourcesContent":["import { Readable } from \"stream\";\nimport { pipeline } from \"stream/promises\";\nimport { createWriteStream } from \"fs\";\nimport { mkdir } from \"fs/promises\";\nimport { dirname, resolve } from \"path\";\nimport type { Sandbox } from \"./sandbox.js\";\nimport type { RunCommandParams } from \"./session.js\";\nimport type { Command, CommandFinished } from \"./command.js\";\nimport type { ExecutionContext } from \"./execution-context.js\";\nimport { validateName } from \"./utils/validate-name.js\";\n\n/**\n * A user context within a sandbox.\n *\n * All file and command operations default to running as this user.\n * Created via {@link Sandbox.createUser} or {@link Sandbox.asUser}.\n *\n * @hideconstructor\n */\nexport class SandboxUser implements ExecutionContext {\n /**\n * The Linux username.\n */\n readonly username: string;\n\n /**\n * The user's home directory (e.g., `/home/alice`).\n */\n readonly homeDir: string;\n\n private readonly sandbox: Sandbox;\n\n /**\n * Memoized lookup of this user's primary group.\n * See {@link SandboxUser.primaryGroup}.\n */\n private primaryGroupPromise?: Promise<string>;\n\n constructor({\n sandbox,\n username,\n }: {\n sandbox: Sandbox;\n username: string;\n }) {\n this.sandbox = sandbox;\n this.username = username;\n // `root`'s home is `/root`, not `/home/root`; every other user's home\n // follows the `/home/<username>` convention.\n this.homeDir = username === \"root\" ? \"/root\" : `/home/${username}`;\n }\n\n /**\n * Build the wrapped command args to run as this user via `sudo -u`.\n *\n * When `env` is provided, injects `env KEY=VAL ...` so that environment\n * variables survive the `sudo -u` transition.\n */\n private buildUserCommand(params: {\n cmd: string;\n args?: string[];\n env?: Record<string, string>;\n cwd?: string;\n }): { cmd: string; args: string[] } {\n const envEntries = Object.entries(params.env ?? {});\n const envArgs =\n envEntries.length > 0\n ? [\"env\", ...envEntries.map(([k, v]) => `${k}=${v}`)]\n : [];\n\n const cwd = params.cwd ?? this.homeDir;\n\n // Run as the target user via `sudo -u`, changing into `cwd` first.\n //\n // We can't set the directory via the sandbox API's `cwd` (the backend cd's\n // there before exec, and SUID binaries like sudo cannot start from a `770`\n // home dir), nor via `sudo --chdir` (the sandbox's sudoers policy forbids\n // the `-D` option). Instead we cd inside a `bash -c` wrapper.\n //\n // `cwd`, the command, its args, and any `env KEY=VAL` are passed as\n // separate positional parameters to `bash -c` (`$1` is `cwd`; `$@` after\n // the shift is the command). Because they are argv elements rather than\n // text spliced into the script, they are never re-parsed by the shell —\n // injection-safe by construction.\n return {\n cmd: \"sudo\",\n args: [\n \"-u\",\n this.username,\n \"--\",\n \"bash\",\n \"-c\",\n 'cd \"$1\" || exit 1; shift; exec \"$@\"',\n \"bash\", // $0 placeholder for `bash -c`\n cwd,\n ...envArgs,\n params.cmd,\n ...(params.args ?? []),\n ],\n };\n }\n\n /**\n * Resolve a path relative to this user's home directory.\n * Absolute paths are returned as-is.\n */\n private resolvePath(path: string): string {\n return path.startsWith(\"/\") ? path : `${this.homeDir}/${path}`;\n }\n\n /**\n * Start executing a command as this user.\n *\n * @param command - The command to execute.\n * @param args - Arguments to pass to the command.\n * @param opts - Optional parameters.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n async runCommand(\n command: string,\n args?: string[],\n opts?: { signal?: AbortSignal; timeoutMs?: number },\n ): Promise<CommandFinished>;\n\n /**\n * Start executing a command as this user 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 as this user.\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; timeoutMs?: number },\n ): Promise<Command | CommandFinished> {\n if (typeof commandOrParams === \"string\") {\n const wrapped = this.buildUserCommand({\n cmd: commandOrParams,\n args,\n });\n // Don't pass cwd to the sandbox API — the bash -c wrapper cd's for us.\n // Don't pass sudo: true — the default user already has sudo privileges.\n return this.sandbox.runCommand({\n ...wrapped,\n signal: opts?.signal,\n timeoutMs: opts?.timeoutMs,\n });\n }\n\n const params = commandOrParams;\n\n // When sudo: true is passed, delegate directly to root (skip user wrapping).\n // Don't default cwd to homeDir — the backend can't exec SUID binaries\n // from directories with restricted permissions.\n if (params.sudo) {\n return this.sandbox.runCommand({\n ...params,\n } as RunCommandParams & { detached: true });\n }\n\n const wrapped = this.buildUserCommand({\n cmd: params.cmd,\n args: params.args,\n env: params.env,\n cwd: params.cwd,\n });\n\n return this.sandbox.runCommand({\n cmd: wrapped.cmd,\n args: wrapped.args,\n // Don't pass cwd — the bash -c wrapper cd's for us (see buildUserCommand)\n // Don't pass sudo: true — the default user already has sudo privileges\n // env is already baked into the wrapped command via `env KEY=VAL`\n detached: params.detached,\n stdout: params.stdout,\n stderr: params.stderr,\n signal: params.signal,\n timeoutMs: params.timeoutMs,\n } as RunCommandParams & { detached: true });\n }\n\n /**\n * Write files to this user's home directory (or absolute paths).\n * Files are written via the sandbox HTTP API then chowned to this user.\n *\n * The HTTP API can write to user home dirs because they are group-owned\n * by the sandbox's default user group with `770` permissions.\n *\n * @param files - Array of files with path, content, and optional mode\n * @param opts - Optional parameters.\n */\n async writeFiles(\n files: { path: string; content: string | Uint8Array; mode?: number }[],\n opts?: { signal?: AbortSignal },\n ) {\n // Resolve relative paths to user's home directory\n const absoluteFiles = files.map((f) => ({\n ...f,\n path: this.resolvePath(f.path),\n }));\n\n // Write via the HTTP API (works because home dirs are group-owned\n // by the default user's group)\n await this.sandbox.writeFiles(absoluteFiles, opts);\n\n const paths = absoluteFiles.map((f) => f.path);\n if (paths.length === 0) return;\n\n // The files themselves belong to the user outright.\n await this.chownOrThrow(\n paths,\n `${this.username}:${await this.primaryGroup(opts?.signal)}`,\n opts?.signal,\n );\n\n // Any directories the write implicitly created (e.g. `data/` in\n // `data/config.json`) are owned by the default user. Hand them to the user\n // but keep them group-owned by the default user's group with mode 770 —\n // matching the home dir — so the HTTP API can still traverse and write into\n // them later.\n const dirs = this.ancestorDirsUnderHome(paths);\n if (dirs.length > 0) {\n const { group } = await this.sandbox.getDefaultUser(opts);\n await this.chownOrThrow(\n dirs,\n `${this.username}:${group}`,\n opts?.signal,\n );\n await this.chmodOrThrow(dirs, \"770\", opts?.signal);\n }\n }\n\n /**\n * Read a file from this user's context as a stream.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @returns A ReadableStream of the file contents, or null if not found\n */\n async readFile(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<NodeJS.ReadableStream | null> {\n // `\"use step\"`: touches a Node built-in (`stream`), which the @workflow\n // compiler only permits inside step functions (matches Session.readFile).\n \"use step\";\n const buffer = await this.catAsUser(file, opts);\n return buffer === null ? null : Readable.from([buffer]);\n }\n\n /**\n * Read a file from this user's context as a Buffer.\n *\n * @param file - File to read, with path and optional cwd\n * @param opts - Optional parameters.\n * @returns The file contents as a Buffer, or null if not found\n */\n async readFileToBuffer(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<Buffer | null> {\n return this.catAsUser(file, opts);\n }\n\n /**\n * Download a file from this user's context to the local filesystem.\n *\n * @param src - Source file in the sandbox\n * @param dst - Destination on the local machine\n * @param opts - Optional parameters.\n * @returns The absolute path to the written file, or null if 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\"`: touches Node built-ins (`fs`, `path`), which the @workflow\n // compiler only permits inside step functions (matches Session.downloadFile).\n \"use step\";\n const stream = await this.readFile(src, opts);\n if (stream === null) return null;\n\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 }\n\n /**\n * Read a file as this user and return its bytes, or null if it does not\n * exist.\n *\n * Reads via `sudo -u <user> base64` rather than the HTTP file API: on stock\n * runtimes the API runs as `vercel-sandbox` and cannot read files this user\n * has kept private (e.g. mode `600`), whereas reading as the user always\n * honours the user's own permissions. The payload is base64-encoded because\n * the command output channel is UTF-8 only and would otherwise corrupt\n * binary files.\n */\n private async catAsUser(\n file: { path: string; cwd?: string },\n opts?: { signal?: AbortSignal },\n ): Promise<Buffer | null> {\n const path = file.path.startsWith(\"/\")\n ? file.path\n : `${file.cwd ?? this.homeDir}/${file.path}`;\n const result = await this.runCommand({\n cmd: \"base64\",\n args: [path],\n signal: opts?.signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n if (/No such file or directory/i.test(stderr)) return null;\n throw new Error(`Failed to read ${path}: ${stderr}`);\n }\n return Buffer.from(await result.stdout(), \"base64\");\n }\n\n /**\n * Create a directory owned by this user.\n *\n * @param path - Path of the directory to create\n * @param opts - Optional parameters.\n */\n async mkDir(path: string, opts?: { signal?: AbortSignal }): Promise<void> {\n const absPath = this.resolvePath(path);\n await this.sandbox.mkDir(absPath, opts);\n // Own the created directory plus any parents the mkdir created under the\n // home dir. Group-owned by the default user's group with mode 770 so the\n // HTTP file API can write into it (matching the home dir).\n const dirs = [absPath, ...this.ancestorDirsUnderHome([absPath])];\n const { group } = await this.sandbox.getDefaultUser(opts);\n await this.chownOrThrow(\n dirs,\n `${this.username}:${group}`,\n opts?.signal,\n );\n await this.chmodOrThrow(dirs, \"770\", opts?.signal);\n }\n\n /**\n * This user's primary group. Users created via {@link Sandbox.createUser}\n * get a group named after them, but {@link Sandbox.asUser} accepts\n * pre-existing users whose primary group can differ (e.g. system users), so\n * we resolve it from the sandbox rather than assuming `<username>`.\n *\n * The result is memoized for the lifetime of this instance.\n */\n private primaryGroup(signal?: AbortSignal): Promise<string> {\n if (!this.primaryGroupPromise) {\n this.primaryGroupPromise = this.resolvePrimaryGroup(signal).catch(\n (err) => {\n // Don't cache failures — allow a later call to retry.\n this.primaryGroupPromise = undefined;\n throw err;\n },\n );\n }\n return this.primaryGroupPromise;\n }\n\n private async resolvePrimaryGroup(signal?: AbortSignal): Promise<string> {\n const result = await this.sandbox.runCommand({\n cmd: \"id\",\n args: [\"-gn\", this.username],\n signal,\n });\n if (result.exitCode !== 0) {\n const stderr = await result.stderr();\n throw new Error(\n `Failed to resolve the primary group of \"${this.username}\": ${stderr}`,\n );\n }\n const group = (await result.stdout()).trim();\n if (!group) {\n throw new Error(\n `Failed to resolve the primary group of \"${this.username}\"`,\n );\n }\n return group;\n }\n\n /**\n * Run `chown <ownership> <paths...>` as root, throwing on failure.\n */\n private async chownOrThrow(\n paths: string[],\n ownership: string,\n signal?: AbortSignal,\n ): Promise<void> {\n const chown = await this.sandbox.runCommand({\n cmd: \"chown\",\n args: [ownership, ...paths],\n sudo: true,\n signal,\n });\n if (chown.exitCode !== 0) {\n const stderr = await chown.stderr();\n throw new Error(\n `Failed to set ownership on ${paths.join(\", \")}: ${stderr}`,\n );\n }\n }\n\n /**\n * Run `chmod <mode> <paths...>` as root, throwing on failure.\n */\n private async chmodOrThrow(\n paths: string[],\n mode: string,\n signal?: AbortSignal,\n ): Promise<void> {\n const chmod = await this.sandbox.runCommand({\n cmd: \"chmod\",\n args: [mode, ...paths],\n sudo: true,\n signal,\n });\n if (chmod.exitCode !== 0) {\n const stderr = await chmod.stderr();\n throw new Error(\n `Failed to set permissions on ${paths.join(\", \")}: ${stderr}`,\n );\n }\n }\n\n /**\n * Given absolute leaf paths, return the directories strictly between this\n * user's home directory and each leaf. The home dir itself is excluded, as\n * are any paths that fall outside the home dir.\n */\n private ancestorDirsUnderHome(paths: string[]): string[] {\n const dirs = new Set<string>();\n const homePrefix = `${this.homeDir}/`;\n for (const p of paths) {\n let dir = p.slice(0, p.lastIndexOf(\"/\"));\n while (dir.length > this.homeDir.length && dir.startsWith(homePrefix)) {\n dirs.add(dir);\n dir = dir.slice(0, dir.lastIndexOf(\"/\"));\n }\n }\n return [...dirs];\n }\n\n /**\n * Add this user to a group.\n *\n * @param groupname - Name of the group to join\n * @param opts - Optional parameters.\n */\n async addToGroup(\n groupname: string,\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n validateName(groupname, \"group name\");\n await this.sandbox.addUserToGroup(this.username, groupname, opts);\n }\n\n /**\n * Remove this user from a group.\n *\n * @param groupname - Name of the group to leave\n * @param opts - Optional parameters.\n */\n async removeFromGroup(\n groupname: string,\n opts?: { signal?: AbortSignal },\n ): Promise<void> {\n validateName(groupname, \"group name\");\n await this.sandbox.removeUserFromGroup(this.username, groupname, opts);\n }\n}\n"],"mappings":"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"} |
| //#region src/utils/validate-name.ts | ||
| const VALID_NAME_RE = /^[a-z_][a-z0-9_-]*$/; | ||
| const MAX_NAME_LENGTH = 32; | ||
| /** | ||
| * Validate a Linux username or group name. | ||
| * Throws if the name is invalid or could be used for command injection. | ||
| */ | ||
| function validateName(name, kind) { | ||
| if (!name) throw new Error(`Invalid ${kind}: must not be empty`); | ||
| if (name.length > MAX_NAME_LENGTH) throw new Error(`Invalid ${kind} "${name}": must be at most ${MAX_NAME_LENGTH} characters`); | ||
| if (!VALID_NAME_RE.test(name)) throw new Error(`Invalid ${kind} "${name}": must match ${VALID_NAME_RE} (lowercase letters, digits, hyphens, underscores)`); | ||
| } | ||
| //#endregion | ||
| exports.validateName = validateName; | ||
| //# sourceMappingURL=validate-name.cjs.map |
| {"version":3,"file":"validate-name.cjs","names":[],"sources":["../../src/utils/validate-name.ts"],"sourcesContent":["const VALID_NAME_RE = /^[a-z_][a-z0-9_-]*$/;\nconst MAX_NAME_LENGTH = 32;\n\n/**\n * Validate a Linux username or group name.\n * Throws if the name is invalid or could be used for command injection.\n */\nexport function validateName(name: string, kind: \"username\" | \"group name\") {\n if (!name) {\n throw new Error(`Invalid ${kind}: must not be empty`);\n }\n if (name.length > MAX_NAME_LENGTH) {\n throw new Error(\n `Invalid ${kind} \"${name}\": must be at most ${MAX_NAME_LENGTH} characters`,\n );\n }\n if (!VALID_NAME_RE.test(name)) {\n throw new Error(\n `Invalid ${kind} \"${name}\": must match ${VALID_NAME_RE} (lowercase letters, digits, hyphens, underscores)`,\n );\n }\n}\n"],"mappings":";;AAAA,MAAM,gBAAgB;AACtB,MAAM,kBAAkB;;;;;AAMxB,SAAgB,aAAa,MAAc,MAAiC;AAC1E,KAAI,CAAC,KACH,OAAM,IAAI,MAAM,WAAW,KAAK,qBAAqB;AAEvD,KAAI,KAAK,SAAS,gBAChB,OAAM,IAAI,MACR,WAAW,KAAK,IAAI,KAAK,qBAAqB,gBAAgB,aAC/D;AAEH,KAAI,CAAC,cAAc,KAAK,KAAK,CAC3B,OAAM,IAAI,MACR,WAAW,KAAK,IAAI,KAAK,gBAAgB,cAAc,oDACxD"} |
| //#region src/utils/validate-name.ts | ||
| const VALID_NAME_RE = /^[a-z_][a-z0-9_-]*$/; | ||
| const MAX_NAME_LENGTH = 32; | ||
| /** | ||
| * Validate a Linux username or group name. | ||
| * Throws if the name is invalid or could be used for command injection. | ||
| */ | ||
| function validateName(name, kind) { | ||
| if (!name) throw new Error(`Invalid ${kind}: must not be empty`); | ||
| if (name.length > MAX_NAME_LENGTH) throw new Error(`Invalid ${kind} "${name}": must be at most ${MAX_NAME_LENGTH} characters`); | ||
| if (!VALID_NAME_RE.test(name)) throw new Error(`Invalid ${kind} "${name}": must match ${VALID_NAME_RE} (lowercase letters, digits, hyphens, underscores)`); | ||
| } | ||
| //#endregion | ||
| export { validateName }; | ||
| //# sourceMappingURL=validate-name.js.map |
| {"version":3,"file":"validate-name.js","names":[],"sources":["../../src/utils/validate-name.ts"],"sourcesContent":["const VALID_NAME_RE = /^[a-z_][a-z0-9_-]*$/;\nconst MAX_NAME_LENGTH = 32;\n\n/**\n * Validate a Linux username or group name.\n * Throws if the name is invalid or could be used for command injection.\n */\nexport function validateName(name: string, kind: \"username\" | \"group name\") {\n if (!name) {\n throw new Error(`Invalid ${kind}: must not be empty`);\n }\n if (name.length > MAX_NAME_LENGTH) {\n throw new Error(\n `Invalid ${kind} \"${name}\": must be at most ${MAX_NAME_LENGTH} characters`,\n );\n }\n if (!VALID_NAME_RE.test(name)) {\n throw new Error(\n `Invalid ${kind} \"${name}\": must match ${VALID_NAME_RE} (lowercase letters, digits, hyphens, underscores)`,\n );\n }\n}\n"],"mappings":";AAAA,MAAM,gBAAgB;AACtB,MAAM,kBAAkB;;;;;AAMxB,SAAgB,aAAa,MAAc,MAAiC;AAC1E,KAAI,CAAC,KACH,OAAM,IAAI,MAAM,WAAW,KAAK,qBAAqB;AAEvD,KAAI,KAAK,SAAS,gBAChB,OAAM,IAAI,MACR,WAAW,KAAK,IAAI,KAAK,qBAAqB,gBAAgB,aAC/D;AAEH,KAAI,CAAC,cAAc,KAAK,KAAK,CAC3B,OAAM,IAAI,MACR,WAAW,KAAK,IAAI,KAAK,gBAAgB,cAAc,oDACxD"} |
@@ -105,3 +105,2 @@ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs'); | ||
| keepLastSnapshots: params.keepLastSnapshots, | ||
| mounts: params.mounts, | ||
| ...privateParams | ||
@@ -253,27 +252,2 @@ }), | ||
| } | ||
| async listDrives(params) { | ||
| return require_base_client.parseOrThrow(require_validators.DrivesResponse, await this.request(`/v2/sandboxes/drives`, { | ||
| query: { | ||
| projectId: params.projectId, | ||
| limit: params.limit, | ||
| cursor: params.cursor ?? params.until, | ||
| since: params.since, | ||
| sortBy: params.sortBy, | ||
| sortOrder: params.sortOrder, | ||
| namePrefix: params.namePrefix | ||
| }, | ||
| method: "GET", | ||
| signal: params.signal | ||
| })); | ||
| } | ||
| async getOrCreateDrive(params) { | ||
| return require_base_client.parseOrThrow(require_validators.DriveResponse, await this.request(`/v2/sandboxes/drives/${encodeURIComponent(params.name)}`, { | ||
| method: "POST", | ||
| body: JSON.stringify({ | ||
| projectId: params.projectId, | ||
| maxSizeBytes: params.maxSizeBytes | ||
| }), | ||
| signal: params.signal | ||
| })); | ||
| } | ||
| async writeFiles(params) { | ||
@@ -438,10 +412,2 @@ const { writer, response } = this.getFileWriter({ | ||
| } | ||
| async deleteDrive(params) { | ||
| const url = `/v2/sandboxes/drives/${encodeURIComponent(params.name)}`; | ||
| return require_base_client.parseOrThrow(require_validators.DriveResponse, await this.request(url, { | ||
| method: "DELETE", | ||
| query: { projectId: params.projectId }, | ||
| signal: params.signal | ||
| })); | ||
| } | ||
| async updateSandbox(params) { | ||
@@ -448,0 +414,0 @@ return require_base_client.parseOrThrow(require_validators.UpdateSandboxResponse, await this.request(`/v2/sandboxes/${encodeURIComponent(params.name)}`, { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"api-client.cjs","names":["BaseClient","VERSION","getPrivateParams","parseOrThrow","SessionAndRoutesResponse","SandboxAndSessionResponse","toAPINetworkPolicy","z","APIError","StreamError","CommandResponse","CommandFinishedResponse","command","LogLine","InteractiveSessionResponse","EmptyResponse","FileWriter","consumeReadable","SessionsResponse","SnapshotsResponse","SnapshotTreeResponse","DrivesResponse","DriveResponse","normalizePath","json: unknown","Readable","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 InteractiveSessionResponse,\n StopSessionResponse,\n SessionsResponse,\n CommandResponse,\n CommandFinishedResponse,\n EmptyResponse,\n LogLine,\n type LogOutputLine,\n SnapshotsResponse,\n SnapshotTreeResponse,\n SnapshotResponse,\n CreateSnapshotResponse,\n SandboxAndSessionResponse,\n SandboxesPaginationResponse,\n UpdateSandboxResponse,\n DrivesResponse,\n DriveResponse,\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 { toAPINetworkPolicy } from \"../utils/network-policy.js\";\nimport { getPrivateParams, WithPrivate } from \"../utils/types.js\";\nimport { RUNTIMES } from \"../constants.js\";\nimport { type BaseCreateSandboxParams } from \"../sandbox.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 image?: string;\n networkPolicy?: NetworkPolicy;\n env?: Record<string, string>;\n tags?: Record<string, string>;\n snapshotExpiration?: number;\n keepLastSnapshots?: {\n count: number;\n expiration?: number;\n deleteEvicted?: boolean;\n };\n mounts?: BaseCreateSandboxParams[\"mounts\"];\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 image: params.image,\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 keepLastSnapshots: params.keepLastSnapshots,\n mounts: params.mounts,\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 logs?: boolean;\n onLog?: (log: LogOutputLine) => void;\n timeout?: number;\n signal?: AbortSignal;\n }): Promise<{\n command: CommandData;\n finished: Promise<CommandFinishedData>;\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?: false;\n timeout?: number;\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 logs?: boolean;\n onLog?: (log: LogOutputLine) => void;\n timeout?: number;\n signal?: AbortSignal;\n }): Promise<\n | {\n command: CommandData;\n finished: Promise<CommandFinishedData>;\n }\n | Parsed<z.infer<typeof CommandResponse>>\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 logs: params.logs || undefined,\n timeout: params.timeout,\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 while (true) {\n const chunk = await iterator.next();\n if (chunk.done) {\n throw new StreamError(\n \"stream_ended_early\",\n \"Stream ended before command finished\",\n params.sessionId,\n );\n }\n\n if (chunk.value?.command) {\n const { command } = CommandFinishedResponse.parse(chunk.value);\n return command;\n }\n\n const parsed = LogLine.parse(chunk.value);\n if (parsed.stream === \"error\") {\n throw new StreamError(\n parsed.data.code,\n parsed.data.message,\n params.sessionId,\n );\n }\n params.onLog?.(parsed);\n }\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 timeout: params.timeout,\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 openInteractive(params: {\n sessionId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof InteractiveSessionResponse>>> {\n return parseOrThrow(\n InteractiveSessionResponse,\n await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/interactive`,\n {\n method: \"POST\",\n body: JSON.stringify({}),\n signal: params.signal,\n },\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 getSnapshotTree(params: {\n /**\n * The ID or name of the project to which the snapshots belong.\n */\n projectId: string;\n /**\n * The snapshot ID to use as the anchor for the tree traversal.\n */\n snapshotId: string;\n /**\n * Maximum number of nodes to return.\n */\n limit?: number;\n /**\n * Sort order: \"asc\" for descendants, \"desc\" for ancestors.\n */\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SnapshotTreeResponse,\n await this.request(`/v2/sandboxes/snapshots/tree`, {\n query: {\n project: params.projectId,\n snapshotId: params.snapshotId,\n limit: params.limit,\n sortOrder: params.sortOrder,\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async listDrives(params: {\n projectId: string;\n limit?: number;\n cursor?: string | number;\n since?: number | string;\n until?: number | string;\n sortBy?: \"createdAt\" | \"updatedAt\" | \"name\";\n sortOrder?: \"asc\" | \"desc\";\n namePrefix?: string;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n DrivesResponse,\n await this.request(`/v2/sandboxes/drives`, {\n query: {\n projectId: params.projectId,\n limit: params.limit,\n cursor: params.cursor ?? params.until,\n since: params.since,\n sortBy: params.sortBy,\n sortOrder: params.sortOrder,\n namePrefix: params.namePrefix,\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async getOrCreateDrive(params: {\n projectId: string;\n name: string;\n maxSizeBytes?: number;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n DriveResponse,\n await this.request(\n `/v2/sandboxes/drives/${encodeURIComponent(params.name)}`,\n {\n method: \"POST\",\n body: JSON.stringify({\n projectId: params.projectId,\n maxSizeBytes: params.maxSizeBytes,\n }),\n signal: params.signal,\n },\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 // The file endpoint must return the file as an octet-stream. Any other\n // content type (e.g. application/json) indicates an error payload that\n // would otherwise be piped verbatim into the destination file.\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (!response.ok || !contentType.includes(\"application/octet-stream\")) {\n const text = await response.text().catch(() => \"\");\n let json: unknown;\n try {\n json = JSON.parse(text || \"{}\");\n } catch {\n json = undefined;\n }\n throw new APIError(response, {\n message: `Unexpected response reading file: status ${response.status}, content-type ${contentType || \"(none)\"}`,\n json,\n text,\n });\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<LogOutputLine, void, void> &\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 deleteDrive(params: {\n projectId: string;\n name: string;\n signal?: AbortSignal;\n }) {\n const url = `/v2/sandboxes/drives/${encodeURIComponent(params.name)}`;\n return parseOrThrow(\n DriveResponse,\n await this.request(url, {\n method: \"DELETE\",\n query: {\n projectId: params.projectId,\n },\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 ports?: number[];\n snapshotExpiration?: number;\n keepLastSnapshots?: {\n count: number;\n expiration?: number;\n deleteEvicted?: boolean;\n } | null;\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 ports: params.ports,\n snapshotExpiration: params.snapshotExpiration,\n keepLastSnapshots: params.keepLastSnapshots,\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","LogLine","InteractiveSessionResponse","EmptyResponse","FileWriter","consumeReadable","SessionsResponse","SnapshotsResponse","SnapshotTreeResponse","normalizePath","json: unknown","Readable","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 InteractiveSessionResponse,\n StopSessionResponse,\n SessionsResponse,\n CommandResponse,\n CommandFinishedResponse,\n EmptyResponse,\n LogLine,\n type LogOutputLine,\n SnapshotsResponse,\n SnapshotTreeResponse,\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 { toAPINetworkPolicy } 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 image?: string;\n networkPolicy?: NetworkPolicy;\n env?: Record<string, string>;\n tags?: Record<string, string>;\n snapshotExpiration?: number;\n keepLastSnapshots?: {\n count: number;\n expiration?: number;\n deleteEvicted?: boolean;\n };\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 image: params.image,\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 keepLastSnapshots: params.keepLastSnapshots,\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 logs?: boolean;\n onLog?: (log: LogOutputLine) => void;\n timeout?: number;\n signal?: AbortSignal;\n }): Promise<{\n command: CommandData;\n finished: Promise<CommandFinishedData>;\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?: false;\n timeout?: number;\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 logs?: boolean;\n onLog?: (log: LogOutputLine) => void;\n timeout?: number;\n signal?: AbortSignal;\n }): Promise<\n | {\n command: CommandData;\n finished: Promise<CommandFinishedData>;\n }\n | Parsed<z.infer<typeof CommandResponse>>\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 logs: params.logs || undefined,\n timeout: params.timeout,\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 while (true) {\n const chunk = await iterator.next();\n if (chunk.done) {\n throw new StreamError(\n \"stream_ended_early\",\n \"Stream ended before command finished\",\n params.sessionId,\n );\n }\n\n if (chunk.value?.command) {\n const { command } = CommandFinishedResponse.parse(chunk.value);\n return command;\n }\n\n const parsed = LogLine.parse(chunk.value);\n if (parsed.stream === \"error\") {\n throw new StreamError(\n parsed.data.code,\n parsed.data.message,\n params.sessionId,\n );\n }\n params.onLog?.(parsed);\n }\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 timeout: params.timeout,\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 openInteractive(params: {\n sessionId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof InteractiveSessionResponse>>> {\n return parseOrThrow(\n InteractiveSessionResponse,\n await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/interactive`,\n {\n method: \"POST\",\n body: JSON.stringify({}),\n signal: params.signal,\n },\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 getSnapshotTree(params: {\n /**\n * The ID or name of the project to which the snapshots belong.\n */\n projectId: string;\n /**\n * The snapshot ID to use as the anchor for the tree traversal.\n */\n snapshotId: string;\n /**\n * Maximum number of nodes to return.\n */\n limit?: number;\n /**\n * Sort order: \"asc\" for descendants, \"desc\" for ancestors.\n */\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SnapshotTreeResponse,\n await this.request(`/v2/sandboxes/snapshots/tree`, {\n query: {\n project: params.projectId,\n snapshotId: params.snapshotId,\n limit: params.limit,\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 // The file endpoint must return the file as an octet-stream. Any other\n // content type (e.g. application/json) indicates an error payload that\n // would otherwise be piped verbatim into the destination file.\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (!response.ok || !contentType.includes(\"application/octet-stream\")) {\n const text = await response.text().catch(() => \"\");\n let json: unknown;\n try {\n json = JSON.parse(text || \"{}\");\n } catch {\n json = undefined;\n }\n throw new APIError(response, {\n message: `Unexpected response reading file: status ${response.status}, content-type ${contentType || \"(none)\"}`,\n json,\n text,\n });\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<LogOutputLine, void, void> &\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 ports?: number[];\n snapshotExpiration?: number;\n keepLastSnapshots?: {\n count: number;\n expiration?: number;\n deleteEvicted?: boolean;\n } | null;\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 ports: params.ports,\n snapshotExpiration: params.snapshotExpiration,\n keepLastSnapshots: params.keepLastSnapshots,\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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"} |
@@ -7,3 +7,2 @@ import { BaseClient, Parsed, RequestParams } from "./base-client.cjs"; | ||
| import { RUNTIMES } from "../constants.cjs"; | ||
| import { BaseCreateSandboxParams } from "../sandbox.cjs"; | ||
| import { z } from "zod"; | ||
@@ -185,3 +184,2 @@ import { Readable } from "stream"; | ||
| }; | ||
| mounts?: BaseCreateSandboxParams["mounts"]; | ||
| signal?: AbortSignal; | ||
@@ -296,6 +294,2 @@ }>): Promise<Parsed<{ | ||
| tags?: Record<string, string> | undefined; | ||
| mounts?: Record<string, { | ||
| drive: string; | ||
| mode?: "read-only" | "read-write" | undefined; | ||
| }> | undefined; | ||
| snapshotExpiration?: number | undefined; | ||
@@ -753,43 +747,2 @@ keepLastSnapshots?: { | ||
| }>>; | ||
| listDrives(params: { | ||
| projectId: string; | ||
| limit?: number; | ||
| cursor?: string | number; | ||
| since?: number | string; | ||
| until?: number | string; | ||
| sortBy?: "createdAt" | "updatedAt" | "name"; | ||
| sortOrder?: "asc" | "desc"; | ||
| namePrefix?: string; | ||
| signal?: AbortSignal; | ||
| }): Promise<Parsed<{ | ||
| drives: { | ||
| name: string; | ||
| projectId: string; | ||
| maxSizeBytes: number; | ||
| createdAt: number; | ||
| updatedAt: number; | ||
| currentSessionId?: string | undefined; | ||
| currentSandboxName?: string | undefined; | ||
| }[]; | ||
| pagination: { | ||
| count: number; | ||
| next: string | null; | ||
| }; | ||
| }>>; | ||
| getOrCreateDrive(params: { | ||
| projectId: string; | ||
| name: string; | ||
| maxSizeBytes?: number; | ||
| signal?: AbortSignal; | ||
| }): Promise<Parsed<{ | ||
| drive: { | ||
| name: string; | ||
| projectId: string; | ||
| maxSizeBytes: number; | ||
| createdAt: number; | ||
| updatedAt: number; | ||
| currentSessionId?: string | undefined; | ||
| currentSandboxName?: string | undefined; | ||
| }; | ||
| }>>; | ||
| writeFiles(params: { | ||
@@ -976,6 +929,2 @@ sessionId: string; | ||
| tags?: Record<string, string> | undefined; | ||
| mounts?: Record<string, { | ||
| drive: string; | ||
| mode?: "read-only" | "read-write" | undefined; | ||
| }> | undefined; | ||
| snapshotExpiration?: number | undefined; | ||
@@ -1225,6 +1174,2 @@ keepLastSnapshots?: { | ||
| tags?: Record<string, string> | undefined; | ||
| mounts?: Record<string, { | ||
| drive: string; | ||
| mode?: "read-only" | "read-write" | undefined; | ||
| }> | undefined; | ||
| snapshotExpiration?: number | undefined; | ||
@@ -1242,17 +1187,2 @@ keepLastSnapshots?: { | ||
| }>>; | ||
| deleteDrive(params: { | ||
| projectId: string; | ||
| name: string; | ||
| signal?: AbortSignal; | ||
| }): Promise<Parsed<{ | ||
| drive: { | ||
| name: string; | ||
| projectId: string; | ||
| maxSizeBytes: number; | ||
| createdAt: number; | ||
| updatedAt: number; | ||
| currentSessionId?: string | undefined; | ||
| currentSandboxName?: string | undefined; | ||
| }; | ||
| }>>; | ||
| updateSandbox(params: { | ||
@@ -1387,6 +1317,2 @@ name: string; | ||
| tags?: Record<string, string> | undefined; | ||
| mounts?: Record<string, { | ||
| drive: string; | ||
| mode?: "read-only" | "read-write" | undefined; | ||
| }> | undefined; | ||
| snapshotExpiration?: number | undefined; | ||
@@ -1517,6 +1443,2 @@ keepLastSnapshots?: { | ||
| tags?: Record<string, string> | undefined; | ||
| mounts?: Record<string, { | ||
| drive: string; | ||
| mode?: "read-only" | "read-write" | undefined; | ||
| }> | undefined; | ||
| snapshotExpiration?: number | undefined; | ||
@@ -1523,0 +1445,0 @@ keepLastSnapshots?: { |
@@ -7,3 +7,2 @@ import { BaseClient, Parsed, RequestParams } from "./base-client.js"; | ||
| import { RUNTIMES } from "../constants.js"; | ||
| import { BaseCreateSandboxParams } from "../sandbox.js"; | ||
| import { z } from "zod"; | ||
@@ -185,3 +184,2 @@ import { Readable } from "stream"; | ||
| }; | ||
| mounts?: BaseCreateSandboxParams["mounts"]; | ||
| signal?: AbortSignal; | ||
@@ -296,6 +294,2 @@ }>): Promise<Parsed<{ | ||
| tags?: Record<string, string> | undefined; | ||
| mounts?: Record<string, { | ||
| drive: string; | ||
| mode?: "read-only" | "read-write" | undefined; | ||
| }> | undefined; | ||
| snapshotExpiration?: number | undefined; | ||
@@ -753,43 +747,2 @@ keepLastSnapshots?: { | ||
| }>>; | ||
| listDrives(params: { | ||
| projectId: string; | ||
| limit?: number; | ||
| cursor?: string | number; | ||
| since?: number | string; | ||
| until?: number | string; | ||
| sortBy?: "createdAt" | "updatedAt" | "name"; | ||
| sortOrder?: "asc" | "desc"; | ||
| namePrefix?: string; | ||
| signal?: AbortSignal; | ||
| }): Promise<Parsed<{ | ||
| drives: { | ||
| name: string; | ||
| projectId: string; | ||
| maxSizeBytes: number; | ||
| createdAt: number; | ||
| updatedAt: number; | ||
| currentSessionId?: string | undefined; | ||
| currentSandboxName?: string | undefined; | ||
| }[]; | ||
| pagination: { | ||
| count: number; | ||
| next: string | null; | ||
| }; | ||
| }>>; | ||
| getOrCreateDrive(params: { | ||
| projectId: string; | ||
| name: string; | ||
| maxSizeBytes?: number; | ||
| signal?: AbortSignal; | ||
| }): Promise<Parsed<{ | ||
| drive: { | ||
| name: string; | ||
| projectId: string; | ||
| maxSizeBytes: number; | ||
| createdAt: number; | ||
| updatedAt: number; | ||
| currentSessionId?: string | undefined; | ||
| currentSandboxName?: string | undefined; | ||
| }; | ||
| }>>; | ||
| writeFiles(params: { | ||
@@ -976,6 +929,2 @@ sessionId: string; | ||
| tags?: Record<string, string> | undefined; | ||
| mounts?: Record<string, { | ||
| drive: string; | ||
| mode?: "read-only" | "read-write" | undefined; | ||
| }> | undefined; | ||
| snapshotExpiration?: number | undefined; | ||
@@ -1225,6 +1174,2 @@ keepLastSnapshots?: { | ||
| tags?: Record<string, string> | undefined; | ||
| mounts?: Record<string, { | ||
| drive: string; | ||
| mode?: "read-only" | "read-write" | undefined; | ||
| }> | undefined; | ||
| snapshotExpiration?: number | undefined; | ||
@@ -1242,17 +1187,2 @@ keepLastSnapshots?: { | ||
| }>>; | ||
| deleteDrive(params: { | ||
| projectId: string; | ||
| name: string; | ||
| signal?: AbortSignal; | ||
| }): Promise<Parsed<{ | ||
| drive: { | ||
| name: string; | ||
| projectId: string; | ||
| maxSizeBytes: number; | ||
| createdAt: number; | ||
| updatedAt: number; | ||
| currentSessionId?: string | undefined; | ||
| currentSandboxName?: string | undefined; | ||
| }; | ||
| }>>; | ||
| updateSandbox(params: { | ||
@@ -1387,6 +1317,2 @@ name: string; | ||
| tags?: Record<string, string> | undefined; | ||
| mounts?: Record<string, { | ||
| drive: string; | ||
| mode?: "read-only" | "read-write" | undefined; | ||
| }> | undefined; | ||
| snapshotExpiration?: number | undefined; | ||
@@ -1517,6 +1443,2 @@ keepLastSnapshots?: { | ||
| tags?: Record<string, string> | undefined; | ||
| mounts?: Record<string, { | ||
| drive: string; | ||
| mode?: "read-only" | "read-write" | undefined; | ||
| }> | undefined; | ||
| snapshotExpiration?: number | undefined; | ||
@@ -1523,0 +1445,0 @@ keepLastSnapshots?: { |
| import { APIError, StreamError } from "./api-error.js"; | ||
| import { BaseClient, parseOrThrow } from "./base-client.js"; | ||
| import { CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, DriveResponse, DrivesResponse, EmptyResponse, InteractiveSessionResponse, LogLine, SandboxAndSessionResponse, SandboxesPaginationResponse, SessionAndRoutesResponse, SessionResponse, SessionsResponse, SnapshotResponse, SnapshotTreeResponse, SnapshotsResponse, StopSessionResponse, UpdateSandboxResponse } from "./validators.js"; | ||
| import { CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, EmptyResponse, InteractiveSessionResponse, LogLine, SandboxAndSessionResponse, SandboxesPaginationResponse, SessionAndRoutesResponse, SessionResponse, SessionsResponse, SnapshotResponse, SnapshotTreeResponse, SnapshotsResponse, StopSessionResponse, UpdateSandboxResponse } from "./validators.js"; | ||
| import { FileWriter } from "./file-writer.js"; | ||
@@ -102,3 +102,2 @@ import { VERSION } from "../version.js"; | ||
| keepLastSnapshots: params.keepLastSnapshots, | ||
| mounts: params.mounts, | ||
| ...privateParams | ||
@@ -250,27 +249,2 @@ }), | ||
| } | ||
| async listDrives(params) { | ||
| return parseOrThrow(DrivesResponse, await this.request(`/v2/sandboxes/drives`, { | ||
| query: { | ||
| projectId: params.projectId, | ||
| limit: params.limit, | ||
| cursor: params.cursor ?? params.until, | ||
| since: params.since, | ||
| sortBy: params.sortBy, | ||
| sortOrder: params.sortOrder, | ||
| namePrefix: params.namePrefix | ||
| }, | ||
| method: "GET", | ||
| signal: params.signal | ||
| })); | ||
| } | ||
| async getOrCreateDrive(params) { | ||
| return parseOrThrow(DriveResponse, await this.request(`/v2/sandboxes/drives/${encodeURIComponent(params.name)}`, { | ||
| method: "POST", | ||
| body: JSON.stringify({ | ||
| projectId: params.projectId, | ||
| maxSizeBytes: params.maxSizeBytes | ||
| }), | ||
| signal: params.signal | ||
| })); | ||
| } | ||
| async writeFiles(params) { | ||
@@ -435,10 +409,2 @@ const { writer, response } = this.getFileWriter({ | ||
| } | ||
| async deleteDrive(params) { | ||
| const url = `/v2/sandboxes/drives/${encodeURIComponent(params.name)}`; | ||
| return parseOrThrow(DriveResponse, await this.request(url, { | ||
| method: "DELETE", | ||
| query: { projectId: params.projectId }, | ||
| signal: params.signal | ||
| })); | ||
| } | ||
| async updateSandbox(params) { | ||
@@ -445,0 +411,0 @@ return parseOrThrow(UpdateSandboxResponse, await this.request(`/v2/sandboxes/${encodeURIComponent(params.name)}`, { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"api-client.js","names":["command","json: unknown","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 InteractiveSessionResponse,\n StopSessionResponse,\n SessionsResponse,\n CommandResponse,\n CommandFinishedResponse,\n EmptyResponse,\n LogLine,\n type LogOutputLine,\n SnapshotsResponse,\n SnapshotTreeResponse,\n SnapshotResponse,\n CreateSnapshotResponse,\n SandboxAndSessionResponse,\n SandboxesPaginationResponse,\n UpdateSandboxResponse,\n DrivesResponse,\n DriveResponse,\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 { toAPINetworkPolicy } from \"../utils/network-policy.js\";\nimport { getPrivateParams, WithPrivate } from \"../utils/types.js\";\nimport { RUNTIMES } from \"../constants.js\";\nimport { type BaseCreateSandboxParams } from \"../sandbox.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 image?: string;\n networkPolicy?: NetworkPolicy;\n env?: Record<string, string>;\n tags?: Record<string, string>;\n snapshotExpiration?: number;\n keepLastSnapshots?: {\n count: number;\n expiration?: number;\n deleteEvicted?: boolean;\n };\n mounts?: BaseCreateSandboxParams[\"mounts\"];\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 image: params.image,\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 keepLastSnapshots: params.keepLastSnapshots,\n mounts: params.mounts,\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 logs?: boolean;\n onLog?: (log: LogOutputLine) => void;\n timeout?: number;\n signal?: AbortSignal;\n }): Promise<{\n command: CommandData;\n finished: Promise<CommandFinishedData>;\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?: false;\n timeout?: number;\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 logs?: boolean;\n onLog?: (log: LogOutputLine) => void;\n timeout?: number;\n signal?: AbortSignal;\n }): Promise<\n | {\n command: CommandData;\n finished: Promise<CommandFinishedData>;\n }\n | Parsed<z.infer<typeof CommandResponse>>\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 logs: params.logs || undefined,\n timeout: params.timeout,\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 while (true) {\n const chunk = await iterator.next();\n if (chunk.done) {\n throw new StreamError(\n \"stream_ended_early\",\n \"Stream ended before command finished\",\n params.sessionId,\n );\n }\n\n if (chunk.value?.command) {\n const { command } = CommandFinishedResponse.parse(chunk.value);\n return command;\n }\n\n const parsed = LogLine.parse(chunk.value);\n if (parsed.stream === \"error\") {\n throw new StreamError(\n parsed.data.code,\n parsed.data.message,\n params.sessionId,\n );\n }\n params.onLog?.(parsed);\n }\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 timeout: params.timeout,\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 openInteractive(params: {\n sessionId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof InteractiveSessionResponse>>> {\n return parseOrThrow(\n InteractiveSessionResponse,\n await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/interactive`,\n {\n method: \"POST\",\n body: JSON.stringify({}),\n signal: params.signal,\n },\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 getSnapshotTree(params: {\n /**\n * The ID or name of the project to which the snapshots belong.\n */\n projectId: string;\n /**\n * The snapshot ID to use as the anchor for the tree traversal.\n */\n snapshotId: string;\n /**\n * Maximum number of nodes to return.\n */\n limit?: number;\n /**\n * Sort order: \"asc\" for descendants, \"desc\" for ancestors.\n */\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SnapshotTreeResponse,\n await this.request(`/v2/sandboxes/snapshots/tree`, {\n query: {\n project: params.projectId,\n snapshotId: params.snapshotId,\n limit: params.limit,\n sortOrder: params.sortOrder,\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async listDrives(params: {\n projectId: string;\n limit?: number;\n cursor?: string | number;\n since?: number | string;\n until?: number | string;\n sortBy?: \"createdAt\" | \"updatedAt\" | \"name\";\n sortOrder?: \"asc\" | \"desc\";\n namePrefix?: string;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n DrivesResponse,\n await this.request(`/v2/sandboxes/drives`, {\n query: {\n projectId: params.projectId,\n limit: params.limit,\n cursor: params.cursor ?? params.until,\n since: params.since,\n sortBy: params.sortBy,\n sortOrder: params.sortOrder,\n namePrefix: params.namePrefix,\n },\n method: \"GET\",\n signal: params.signal,\n }),\n );\n }\n\n async getOrCreateDrive(params: {\n projectId: string;\n name: string;\n maxSizeBytes?: number;\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n DriveResponse,\n await this.request(\n `/v2/sandboxes/drives/${encodeURIComponent(params.name)}`,\n {\n method: \"POST\",\n body: JSON.stringify({\n projectId: params.projectId,\n maxSizeBytes: params.maxSizeBytes,\n }),\n signal: params.signal,\n },\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 // The file endpoint must return the file as an octet-stream. Any other\n // content type (e.g. application/json) indicates an error payload that\n // would otherwise be piped verbatim into the destination file.\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (!response.ok || !contentType.includes(\"application/octet-stream\")) {\n const text = await response.text().catch(() => \"\");\n let json: unknown;\n try {\n json = JSON.parse(text || \"{}\");\n } catch {\n json = undefined;\n }\n throw new APIError(response, {\n message: `Unexpected response reading file: status ${response.status}, content-type ${contentType || \"(none)\"}`,\n json,\n text,\n });\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<LogOutputLine, void, void> &\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 deleteDrive(params: {\n projectId: string;\n name: string;\n signal?: AbortSignal;\n }) {\n const url = `/v2/sandboxes/drives/${encodeURIComponent(params.name)}`;\n return parseOrThrow(\n DriveResponse,\n await this.request(url, {\n method: \"DELETE\",\n query: {\n projectId: params.projectId,\n },\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 ports?: number[];\n snapshotExpiration?: number;\n keepLastSnapshots?: {\n count: number;\n expiration?: number;\n deleteEvicted?: boolean;\n } | null;\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 ports: params.ports,\n snapshotExpiration: params.snapshotExpiration,\n keepLastSnapshots: params.keepLastSnapshots,\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","json: unknown","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 InteractiveSessionResponse,\n StopSessionResponse,\n SessionsResponse,\n CommandResponse,\n CommandFinishedResponse,\n EmptyResponse,\n LogLine,\n type LogOutputLine,\n SnapshotsResponse,\n SnapshotTreeResponse,\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 { toAPINetworkPolicy } 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 image?: string;\n networkPolicy?: NetworkPolicy;\n env?: Record<string, string>;\n tags?: Record<string, string>;\n snapshotExpiration?: number;\n keepLastSnapshots?: {\n count: number;\n expiration?: number;\n deleteEvicted?: boolean;\n };\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 image: params.image,\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 keepLastSnapshots: params.keepLastSnapshots,\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 logs?: boolean;\n onLog?: (log: LogOutputLine) => void;\n timeout?: number;\n signal?: AbortSignal;\n }): Promise<{\n command: CommandData;\n finished: Promise<CommandFinishedData>;\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?: false;\n timeout?: number;\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 logs?: boolean;\n onLog?: (log: LogOutputLine) => void;\n timeout?: number;\n signal?: AbortSignal;\n }): Promise<\n | {\n command: CommandData;\n finished: Promise<CommandFinishedData>;\n }\n | Parsed<z.infer<typeof CommandResponse>>\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 logs: params.logs || undefined,\n timeout: params.timeout,\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 while (true) {\n const chunk = await iterator.next();\n if (chunk.done) {\n throw new StreamError(\n \"stream_ended_early\",\n \"Stream ended before command finished\",\n params.sessionId,\n );\n }\n\n if (chunk.value?.command) {\n const { command } = CommandFinishedResponse.parse(chunk.value);\n return command;\n }\n\n const parsed = LogLine.parse(chunk.value);\n if (parsed.stream === \"error\") {\n throw new StreamError(\n parsed.data.code,\n parsed.data.message,\n params.sessionId,\n );\n }\n params.onLog?.(parsed);\n }\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 timeout: params.timeout,\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 openInteractive(params: {\n sessionId: string;\n signal?: AbortSignal;\n }): Promise<Parsed<z.infer<typeof InteractiveSessionResponse>>> {\n return parseOrThrow(\n InteractiveSessionResponse,\n await this.request(\n `/v2/sandboxes/sessions/${params.sessionId}/interactive`,\n {\n method: \"POST\",\n body: JSON.stringify({}),\n signal: params.signal,\n },\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 getSnapshotTree(params: {\n /**\n * The ID or name of the project to which the snapshots belong.\n */\n projectId: string;\n /**\n * The snapshot ID to use as the anchor for the tree traversal.\n */\n snapshotId: string;\n /**\n * Maximum number of nodes to return.\n */\n limit?: number;\n /**\n * Sort order: \"asc\" for descendants, \"desc\" for ancestors.\n */\n sortOrder?: \"asc\" | \"desc\";\n signal?: AbortSignal;\n }) {\n return parseOrThrow(\n SnapshotTreeResponse,\n await this.request(`/v2/sandboxes/snapshots/tree`, {\n query: {\n project: params.projectId,\n snapshotId: params.snapshotId,\n limit: params.limit,\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 // The file endpoint must return the file as an octet-stream. Any other\n // content type (e.g. application/json) indicates an error payload that\n // would otherwise be piped verbatim into the destination file.\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (!response.ok || !contentType.includes(\"application/octet-stream\")) {\n const text = await response.text().catch(() => \"\");\n let json: unknown;\n try {\n json = JSON.parse(text || \"{}\");\n } catch {\n json = undefined;\n }\n throw new APIError(response, {\n message: `Unexpected response reading file: status ${response.status}, content-type ${contentType || \"(none)\"}`,\n json,\n text,\n });\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<LogOutputLine, void, void> &\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 ports?: number[];\n snapshotExpiration?: number;\n keepLastSnapshots?: {\n count: number;\n expiration?: number;\n deleteEvicted?: boolean;\n } | null;\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 ports: params.ports,\n snapshotExpiration: params.snapshotExpiration,\n keepLastSnapshots: params.keepLastSnapshots,\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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"} |
@@ -1,2 +0,2 @@ | ||
| import { Command, CommandData, CommandFinishedData, CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, Drive, DriveMetadata, InteractiveSessionResponse, LogError, LogLine, LogLineData, LogOutputLine, Sandbox, SandboxMetaData, SandboxRoute, SandboxRouteData, Session, SessionMetaData, SessionResponse, Snapshot, SnapshotMetadata, SnapshotResponse, SnapshotTreeNode, SnapshotTreeNodeData, StopSessionResponse } from "./validators.js"; | ||
| import { Command, CommandData, CommandFinishedData, CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, InteractiveSessionResponse, LogError, LogLine, LogLineData, LogOutputLine, Sandbox, SandboxMetaData, SandboxRoute, SandboxRouteData, Session, SessionMetaData, SessionResponse, Snapshot, SnapshotMetadata, SnapshotResponse, SnapshotTreeNode, SnapshotTreeNodeData, StopSessionResponse } from "./validators.js"; | ||
| import { APIClient } from "./api-client.js"; |
@@ -1,4 +0,4 @@ | ||
| import { Command, CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, CursorPagination, Drive, DriveResponse, DrivesResponse, EmptyResponse, ForwardRuleValidator, InjectionRuleValidator, InteractiveSessionResponse, LogError, LogLine, LogLineStderr, LogLineStdout, NetworkPolicyResponseValidator, NetworkPolicyRuleValidator, NetworkPolicyTransformValidator, Sandbox, SandboxAndSessionResponse, SandboxRoute, SandboxesPaginationResponse, Session, SessionAndRoutesResponse, SessionResponse, SessionsResponse, Snapshot, SnapshotResponse, SnapshotTreeNode, SnapshotTreeResponse, SnapshotsResponse, StopSessionResponse, UpdateSandboxResponse, V2NetworkPolicyObjectValidator } from "./validators.js"; | ||
| import { Command, CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, CursorPagination, EmptyResponse, ForwardRuleValidator, InjectionRuleValidator, InteractiveSessionResponse, LogError, LogLine, LogLineStderr, LogLineStdout, NetworkPolicyResponseValidator, NetworkPolicyRuleValidator, NetworkPolicyTransformValidator, Sandbox, SandboxAndSessionResponse, SandboxRoute, SandboxesPaginationResponse, Session, SessionAndRoutesResponse, SessionResponse, SessionsResponse, Snapshot, SnapshotResponse, SnapshotTreeNode, SnapshotTreeResponse, SnapshotsResponse, StopSessionResponse, UpdateSandboxResponse, V2NetworkPolicyObjectValidator } from "./validators.js"; | ||
| import { APIClient } from "./api-client.js"; | ||
| export { }; |
@@ -174,16 +174,2 @@ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs'); | ||
| const SnapshotResponse = zod.z.object({ snapshot: Snapshot }); | ||
| const Drive = zod.z.object({ | ||
| name: zod.z.string(), | ||
| projectId: zod.z.string(), | ||
| maxSizeBytes: zod.z.number(), | ||
| currentSessionId: zod.z.string().optional(), | ||
| currentSandboxName: zod.z.string().optional(), | ||
| createdAt: zod.z.number(), | ||
| updatedAt: zod.z.number() | ||
| }); | ||
| const DrivesResponse = zod.z.object({ | ||
| drives: zod.z.array(Drive), | ||
| pagination: CursorPagination | ||
| }); | ||
| const DriveResponse = zod.z.object({ drive: Drive }); | ||
| const Sandbox = zod.z.object({ | ||
@@ -211,6 +197,2 @@ name: zod.z.string(), | ||
| tags: zod.z.record(zod.z.string(), zod.z.string()).optional(), | ||
| mounts: zod.z.record(zod.z.string(), zod.z.object({ | ||
| drive: zod.z.string(), | ||
| mode: zod.z.enum(["read-only", "read-write"]).optional() | ||
| })).optional(), | ||
| snapshotExpiration: zod.z.number().optional(), | ||
@@ -249,5 +231,2 @@ keepLastSnapshots: zod.z.object({ | ||
| exports.CursorPagination = CursorPagination; | ||
| exports.Drive = Drive; | ||
| exports.DriveResponse = DriveResponse; | ||
| exports.DrivesResponse = DrivesResponse; | ||
| exports.EmptyResponse = EmptyResponse; | ||
@@ -254,0 +233,0 @@ exports.ForwardRuleValidator = ForwardRuleValidator; |
@@ -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\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(), 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(), 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.string(), 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 lastUsedAt: z.number().optional(),\n creationMethod: z.string().optional(),\n parentId: z.string().optional(),\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 // optional because the duration was not preserved for older commands\n durationMs: z.number().optional(),\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 type InteractiveSessionData = z.infer<typeof InteractiveSessionResponse>;\n\nexport const InteractiveSessionResponse = z.object({\n url: z.string(),\n token: z.string(),\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]);\nexport type LogLineData = z.infer<typeof LogLine>;\nexport type LogOutputLine = Exclude<LogLineData, z.infer<typeof LogError>>;\n\nexport const SnapshotsResponse = z.object({\n snapshots: z.array(Snapshot),\n pagination: CursorPagination,\n});\n\nexport const SnapshotTreeNode = z.object({\n snapshot: Snapshot,\n siblings: z.array(Snapshot),\n count: z.string(),\n});\n\nexport type SnapshotTreeNodeData = z.infer<typeof SnapshotTreeNode>;\n\nexport const SnapshotTreeResponse = z.object({\n snapshots: z.array(SnapshotTreeNode),\n anchor: SnapshotTreeNode.optional(),\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 Drive = z.object({\n name: z.string(),\n projectId: z.string(),\n maxSizeBytes: z.number(),\n currentSessionId: z.string().optional(),\n currentSandboxName: z.string().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n});\n\nexport type DriveMetadata = z.infer<typeof Drive>;\n\nexport const DrivesResponse = z.object({\n drives: z.array(Drive),\n pagination: CursorPagination,\n});\n\nexport const DriveResponse = z.object({\n drive: Drive,\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 expiresAt: z.number().optional(),\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(), z.string()).optional(),\n mounts: z\n .record(\n z.string(),\n z.object({\n drive: z.string(),\n mode: z.enum([\"read-only\", \"read-write\"]).optional(),\n }),\n )\n .optional(),\n snapshotExpiration: z.number().optional(),\n keepLastSnapshots: z\n .object({\n count: z.number(),\n expiration: z.number().optional(),\n deleteEvicted: z.boolean().optional(),\n })\n .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 routes: z.array(SandboxRoute).optional(),\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(), 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(), 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.string(), 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 lastUsedAt: z.number().optional(),\n creationMethod: z.string().optional(),\n parentId: z.string().optional(),\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 // optional because the duration was not preserved for older commands\n durationMs: z.number().optional(),\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 type InteractiveSessionData = z.infer<typeof InteractiveSessionResponse>;\n\nexport const InteractiveSessionResponse = z.object({\n url: z.string(),\n token: z.string(),\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]);\nexport type LogLineData = z.infer<typeof LogLine>;\nexport type LogOutputLine = Exclude<LogLineData, z.infer<typeof LogError>>;\n\nexport const SnapshotsResponse = z.object({\n snapshots: z.array(Snapshot),\n pagination: CursorPagination,\n});\n\nexport const SnapshotTreeNode = z.object({\n snapshot: Snapshot,\n siblings: z.array(Snapshot),\n count: z.string(),\n});\n\nexport type SnapshotTreeNodeData = z.infer<typeof SnapshotTreeNode>;\n\nexport const SnapshotTreeResponse = z.object({\n snapshots: z.array(SnapshotTreeNode),\n anchor: SnapshotTreeNode.optional(),\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 expiresAt: z.number().optional(),\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(), z.string()).optional(),\n snapshotExpiration: z.number().optional(),\n keepLastSnapshots: z\n .object({\n count: z.number(),\n expiration: z.number().optional(),\n deleteEvicted: z.boolean().optional(),\n })\n .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 routes: z.array(SandboxRoute).optional(),\n});\n"],"mappings":"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"} |
@@ -520,12 +520,2 @@ import { z } from "zod"; | ||
| }, z.core.$strip>; | ||
| declare const Drive: z.ZodObject<{ | ||
| name: z.ZodString; | ||
| projectId: z.ZodString; | ||
| maxSizeBytes: z.ZodNumber; | ||
| currentSessionId: z.ZodOptional<z.ZodString>; | ||
| currentSandboxName: z.ZodOptional<z.ZodString>; | ||
| createdAt: z.ZodNumber; | ||
| updatedAt: z.ZodNumber; | ||
| }, z.core.$strip>; | ||
| type DriveMetadata = z.infer<typeof Drive>; | ||
| declare const Sandbox: z.ZodObject<{ | ||
@@ -643,9 +633,2 @@ name: z.ZodString; | ||
| tags: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>; | ||
| mounts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ | ||
| drive: z.ZodString; | ||
| mode: z.ZodOptional<z.ZodEnum<{ | ||
| "read-only": "read-only"; | ||
| "read-write": "read-write"; | ||
| }>>; | ||
| }, z.core.$strip>>>; | ||
| snapshotExpiration: z.ZodOptional<z.ZodNumber>; | ||
@@ -889,9 +872,2 @@ keepLastSnapshots: z.ZodOptional<z.ZodObject<{ | ||
| tags: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>; | ||
| mounts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ | ||
| drive: z.ZodString; | ||
| mode: z.ZodOptional<z.ZodEnum<{ | ||
| "read-only": "read-only"; | ||
| "read-write": "read-write"; | ||
| }>>; | ||
| }, z.core.$strip>>>; | ||
| snapshotExpiration: z.ZodOptional<z.ZodNumber>; | ||
@@ -923,3 +899,3 @@ keepLastSnapshots: z.ZodOptional<z.ZodObject<{ | ||
| //#endregion | ||
| export { CommandData, CommandFinishedData, CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, DriveMetadata, InteractiveSessionResponse, LogOutputLine, SandboxMetaData, SandboxRouteData, SessionMetaData, SessionResponse, SnapshotMetadata, SnapshotResponse, SnapshotTreeNodeData, StopSessionResponse }; | ||
| export { CommandData, CommandFinishedData, CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, InteractiveSessionResponse, LogOutputLine, SandboxMetaData, SandboxRouteData, SessionMetaData, SessionResponse, SnapshotMetadata, SnapshotResponse, SnapshotTreeNodeData, StopSessionResponse }; | ||
| //# sourceMappingURL=validators.d.cts.map |
@@ -520,12 +520,2 @@ import { z } from "zod"; | ||
| }, z.core.$strip>; | ||
| declare const Drive: z.ZodObject<{ | ||
| name: z.ZodString; | ||
| projectId: z.ZodString; | ||
| maxSizeBytes: z.ZodNumber; | ||
| currentSessionId: z.ZodOptional<z.ZodString>; | ||
| currentSandboxName: z.ZodOptional<z.ZodString>; | ||
| createdAt: z.ZodNumber; | ||
| updatedAt: z.ZodNumber; | ||
| }, z.core.$strip>; | ||
| type DriveMetadata = z.infer<typeof Drive>; | ||
| declare const Sandbox: z.ZodObject<{ | ||
@@ -643,9 +633,2 @@ name: z.ZodString; | ||
| tags: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>; | ||
| mounts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ | ||
| drive: z.ZodString; | ||
| mode: z.ZodOptional<z.ZodEnum<{ | ||
| "read-only": "read-only"; | ||
| "read-write": "read-write"; | ||
| }>>; | ||
| }, z.core.$strip>>>; | ||
| snapshotExpiration: z.ZodOptional<z.ZodNumber>; | ||
@@ -889,9 +872,2 @@ keepLastSnapshots: z.ZodOptional<z.ZodObject<{ | ||
| tags: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>; | ||
| mounts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ | ||
| drive: z.ZodString; | ||
| mode: z.ZodOptional<z.ZodEnum<{ | ||
| "read-only": "read-only"; | ||
| "read-write": "read-write"; | ||
| }>>; | ||
| }, z.core.$strip>>>; | ||
| snapshotExpiration: z.ZodOptional<z.ZodNumber>; | ||
@@ -923,3 +899,3 @@ keepLastSnapshots: z.ZodOptional<z.ZodObject<{ | ||
| //#endregion | ||
| export { Command, CommandData, CommandFinishedData, CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, Drive, DriveMetadata, InteractiveSessionResponse, LogError, LogLine, LogLineData, LogOutputLine, Sandbox, SandboxMetaData, SandboxRoute, SandboxRouteData, Session, SessionMetaData, SessionResponse, Snapshot, SnapshotMetadata, SnapshotResponse, SnapshotTreeNode, SnapshotTreeNodeData, StopSessionResponse }; | ||
| export { Command, CommandData, CommandFinishedData, CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, InteractiveSessionResponse, LogError, LogLine, LogLineData, LogOutputLine, Sandbox, SandboxMetaData, SandboxRoute, SandboxRouteData, Session, SessionMetaData, SessionResponse, Snapshot, SnapshotMetadata, SnapshotResponse, SnapshotTreeNode, SnapshotTreeNodeData, StopSessionResponse }; | ||
| //# sourceMappingURL=validators.d.ts.map |
@@ -173,16 +173,2 @@ import { z } from "zod"; | ||
| const SnapshotResponse = z.object({ snapshot: Snapshot }); | ||
| const Drive = z.object({ | ||
| name: z.string(), | ||
| projectId: z.string(), | ||
| maxSizeBytes: z.number(), | ||
| currentSessionId: z.string().optional(), | ||
| currentSandboxName: z.string().optional(), | ||
| createdAt: z.number(), | ||
| updatedAt: z.number() | ||
| }); | ||
| const DrivesResponse = z.object({ | ||
| drives: z.array(Drive), | ||
| pagination: CursorPagination | ||
| }); | ||
| const DriveResponse = z.object({ drive: Drive }); | ||
| const Sandbox = z.object({ | ||
@@ -210,6 +196,2 @@ name: z.string(), | ||
| tags: z.record(z.string(), z.string()).optional(), | ||
| mounts: z.record(z.string(), z.object({ | ||
| drive: z.string(), | ||
| mode: z.enum(["read-only", "read-write"]).optional() | ||
| })).optional(), | ||
| snapshotExpiration: z.number().optional(), | ||
@@ -243,3 +225,3 @@ keepLastSnapshots: z.object({ | ||
| //#endregion | ||
| export { Command, CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, CursorPagination, Drive, DriveResponse, DrivesResponse, EmptyResponse, ForwardRuleValidator, InjectionRuleValidator, InteractiveSessionResponse, LogError, LogLine, LogLineStderr, LogLineStdout, NetworkPolicyResponseValidator, NetworkPolicyRuleValidator, NetworkPolicyTransformValidator, Sandbox, SandboxAndSessionResponse, SandboxRoute, SandboxesPaginationResponse, Session, SessionAndRoutesResponse, SessionResponse, SessionsResponse, Snapshot, SnapshotResponse, SnapshotTreeNode, SnapshotTreeResponse, SnapshotsResponse, StopSessionResponse, UpdateSandboxResponse, V2NetworkPolicyObjectValidator }; | ||
| export { Command, CommandFinishedResponse, CommandResponse, CreateSnapshotResponse, CursorPagination, EmptyResponse, ForwardRuleValidator, InjectionRuleValidator, InteractiveSessionResponse, LogError, LogLine, LogLineStderr, LogLineStdout, NetworkPolicyResponseValidator, NetworkPolicyRuleValidator, NetworkPolicyTransformValidator, Sandbox, SandboxAndSessionResponse, SandboxRoute, SandboxesPaginationResponse, Session, SessionAndRoutesResponse, SessionResponse, SessionsResponse, Snapshot, SnapshotResponse, SnapshotTreeNode, SnapshotTreeResponse, 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\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(), 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(), 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.string(), 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 lastUsedAt: z.number().optional(),\n creationMethod: z.string().optional(),\n parentId: z.string().optional(),\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 // optional because the duration was not preserved for older commands\n durationMs: z.number().optional(),\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 type InteractiveSessionData = z.infer<typeof InteractiveSessionResponse>;\n\nexport const InteractiveSessionResponse = z.object({\n url: z.string(),\n token: z.string(),\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]);\nexport type LogLineData = z.infer<typeof LogLine>;\nexport type LogOutputLine = Exclude<LogLineData, z.infer<typeof LogError>>;\n\nexport const SnapshotsResponse = z.object({\n snapshots: z.array(Snapshot),\n pagination: CursorPagination,\n});\n\nexport const SnapshotTreeNode = z.object({\n snapshot: Snapshot,\n siblings: z.array(Snapshot),\n count: z.string(),\n});\n\nexport type SnapshotTreeNodeData = z.infer<typeof SnapshotTreeNode>;\n\nexport const SnapshotTreeResponse = z.object({\n snapshots: z.array(SnapshotTreeNode),\n anchor: SnapshotTreeNode.optional(),\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 Drive = z.object({\n name: z.string(),\n projectId: z.string(),\n maxSizeBytes: z.number(),\n currentSessionId: z.string().optional(),\n currentSandboxName: z.string().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n});\n\nexport type DriveMetadata = z.infer<typeof Drive>;\n\nexport const DrivesResponse = z.object({\n drives: z.array(Drive),\n pagination: CursorPagination,\n});\n\nexport const DriveResponse = z.object({\n drive: Drive,\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 expiresAt: z.number().optional(),\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(), z.string()).optional(),\n mounts: z\n .record(\n z.string(),\n z.object({\n drive: z.string(),\n mode: z.enum([\"read-only\", \"read-write\"]).optional(),\n }),\n )\n .optional(),\n snapshotExpiration: z.number().optional(),\n keepLastSnapshots: z\n .object({\n count: z.number(),\n expiration: z.number().optional(),\n deleteEvicted: z.boolean().optional(),\n })\n .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 routes: z.array(SandboxRoute).optional(),\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(), 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(), 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.string(), 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 lastUsedAt: z.number().optional(),\n creationMethod: z.string().optional(),\n parentId: z.string().optional(),\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 // optional because the duration was not preserved for older commands\n durationMs: z.number().optional(),\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 type InteractiveSessionData = z.infer<typeof InteractiveSessionResponse>;\n\nexport const InteractiveSessionResponse = z.object({\n url: z.string(),\n token: z.string(),\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]);\nexport type LogLineData = z.infer<typeof LogLine>;\nexport type LogOutputLine = Exclude<LogLineData, z.infer<typeof LogError>>;\n\nexport const SnapshotsResponse = z.object({\n snapshots: z.array(Snapshot),\n pagination: CursorPagination,\n});\n\nexport const SnapshotTreeNode = z.object({\n snapshot: Snapshot,\n siblings: z.array(Snapshot),\n count: z.string(),\n});\n\nexport type SnapshotTreeNodeData = z.infer<typeof SnapshotTreeNode>;\n\nexport const SnapshotTreeResponse = z.object({\n snapshots: z.array(SnapshotTreeNode),\n anchor: SnapshotTreeNode.optional(),\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 expiresAt: z.number().optional(),\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(), z.string()).optional(),\n snapshotExpiration: z.number().optional(),\n keepLastSnapshots: z\n .object({\n count: z.number(),\n expiration: z.number().optional(),\n deleteEvicted: z.boolean().optional(),\n })\n .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 routes: z.array(SandboxRoute).optional(),\n});\n"],"mappings":"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"} |
+2
-2
@@ -6,4 +6,4 @@ const require_api_error = require('./api-client/api-error.cjs'); | ||
| const require_filesystem = require('./filesystem.cjs'); | ||
| const require_sandbox_user = require('./sandbox-user.cjs'); | ||
| const require_sandbox = require('./sandbox.cjs'); | ||
| const require_drive = require('./drive.cjs'); | ||
| const require_proxy = require('./proxy.cjs'); | ||
@@ -14,5 +14,5 @@ | ||
| exports.CommandFinished = require_command.CommandFinished; | ||
| exports.Drive = require_drive.Drive; | ||
| exports.FileSystem = require_filesystem.FileSystem; | ||
| exports.Sandbox = require_sandbox.Sandbox; | ||
| exports.SandboxUser = require_sandbox_user.SandboxUser; | ||
| exports.Session = require_session.Session; | ||
@@ -19,0 +19,0 @@ exports.Snapshot = require_snapshot.Snapshot; |
+4
-3
@@ -6,7 +6,8 @@ import { APIError, StreamError } from "./api-client/api-error.cjs"; | ||
| import { SerializedSnapshot, Snapshot } from "./snapshot.cjs"; | ||
| import { ExecutionContext } from "./execution-context.cjs"; | ||
| import { Session } from "./session.cjs"; | ||
| import { FileSystem } from "./filesystem.cjs"; | ||
| import { Sandbox, SandboxMountMode, SandboxMounts, SerializedSandbox } from "./sandbox.cjs"; | ||
| import { Drive, SerializedDrive } from "./drive.cjs"; | ||
| import { SandboxUser } from "./sandbox-user.cjs"; | ||
| import { Sandbox, SerializedSandbox } from "./sandbox.cjs"; | ||
| import { InvalidRequestProxyHandler, ProxyHandler, ProxyMeta, defineSandboxProxy } from "./proxy.cjs"; | ||
| export { APIError, Command, CommandFinished, type CommandOutput, Drive, FileSystem, type InvalidRequestProxyHandler, type NetworkPolicy, type NetworkPolicyKeyValueMatcher, type NetworkPolicyMatch, type NetworkPolicyMatcher, type NetworkPolicyRule, type NetworkTransformer, type ProxyHandler, type ProxyMeta, Sandbox, type SandboxMountMode, type SandboxMounts, type SerializedCommand, type SerializedCommandFinished, type SerializedDrive, type SerializedSandbox, type SerializedSnapshot, Session, Snapshot, type SnapshotTreeNodeData, StreamError, defineSandboxProxy }; | ||
| export { APIError, Command, CommandFinished, type CommandOutput, type ExecutionContext, FileSystem, type InvalidRequestProxyHandler, type NetworkPolicy, type NetworkPolicyKeyValueMatcher, type NetworkPolicyMatch, type NetworkPolicyMatcher, type NetworkPolicyRule, type NetworkTransformer, type ProxyHandler, type ProxyMeta, Sandbox, SandboxUser, type SerializedCommand, type SerializedCommandFinished, type SerializedSandbox, type SerializedSnapshot, Session, Snapshot, type SnapshotTreeNodeData, StreamError, defineSandboxProxy }; |
+4
-3
@@ -6,7 +6,8 @@ import { APIError, StreamError } from "./api-client/api-error.js"; | ||
| import { SerializedSnapshot, Snapshot } from "./snapshot.js"; | ||
| import { ExecutionContext } from "./execution-context.js"; | ||
| import { Session } from "./session.js"; | ||
| import { FileSystem } from "./filesystem.js"; | ||
| import { Sandbox, SandboxMountMode, SandboxMounts, SerializedSandbox } from "./sandbox.js"; | ||
| import { Drive, SerializedDrive } from "./drive.js"; | ||
| import { SandboxUser } from "./sandbox-user.js"; | ||
| import { Sandbox, SerializedSandbox } from "./sandbox.js"; | ||
| import { InvalidRequestProxyHandler, ProxyHandler, ProxyMeta, defineSandboxProxy } from "./proxy.js"; | ||
| export { APIError, Command, CommandFinished, type CommandOutput, Drive, FileSystem, type InvalidRequestProxyHandler, type NetworkPolicy, type NetworkPolicyKeyValueMatcher, type NetworkPolicyMatch, type NetworkPolicyMatcher, type NetworkPolicyRule, type NetworkTransformer, type ProxyHandler, type ProxyMeta, Sandbox, type SandboxMountMode, type SandboxMounts, type SerializedCommand, type SerializedCommandFinished, type SerializedDrive, type SerializedSandbox, type SerializedSnapshot, Session, Snapshot, type SnapshotTreeNodeData, StreamError, defineSandboxProxy }; | ||
| export { APIError, Command, CommandFinished, type CommandOutput, type ExecutionContext, FileSystem, type InvalidRequestProxyHandler, type NetworkPolicy, type NetworkPolicyKeyValueMatcher, type NetworkPolicyMatch, type NetworkPolicyMatcher, type NetworkPolicyRule, type NetworkTransformer, type ProxyHandler, type ProxyMeta, Sandbox, SandboxUser, type SerializedCommand, type SerializedCommandFinished, type SerializedSandbox, type SerializedSnapshot, Session, Snapshot, type SnapshotTreeNodeData, StreamError, defineSandboxProxy }; |
+2
-2
@@ -6,6 +6,6 @@ import { APIError, StreamError } from "./api-client/api-error.js"; | ||
| import { FileSystem } from "./filesystem.js"; | ||
| import { SandboxUser } from "./sandbox-user.js"; | ||
| import { Sandbox } from "./sandbox.js"; | ||
| import { Drive } from "./drive.js"; | ||
| import { defineSandboxProxy } from "./proxy.js"; | ||
| export { APIError, Command, CommandFinished, Drive, FileSystem, Sandbox, Session, Snapshot, StreamError, defineSandboxProxy }; | ||
| export { APIError, Command, CommandFinished, FileSystem, Sandbox, SandboxUser, Session, Snapshot, StreamError, defineSandboxProxy }; |
+244
-7
@@ -11,2 +11,4 @@ const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs'); | ||
| const require_filesystem = require('./filesystem.cjs'); | ||
| const require_validate_name = require('./utils/validate-name.cjs'); | ||
| const require_sandbox_user = require('./sandbox-user.cjs'); | ||
| let __workflow_serde = require("@workflow/serde"); | ||
@@ -176,8 +178,2 @@ let node_timers_promises = require("node:timers/promises"); | ||
| /** | ||
| * Drives mounted on the sandbox, keyed by mount path. | ||
| */ | ||
| get mounts() { | ||
| return this.sandbox.mounts; | ||
| } | ||
| /** | ||
| * The default network policy of this sandbox. | ||
@@ -331,3 +327,2 @@ */ | ||
| tags: params?.tags, | ||
| mounts: params?.mounts, | ||
| snapshotExpiration: params?.snapshotExpiration, | ||
@@ -795,2 +790,244 @@ keepLastSnapshots: params?.keepLastSnapshots, | ||
| /** | ||
| * The user that non-`sudo` commands and the HTTP file API run as, together | ||
| * with that user's primary group. This depends on the sandbox image: stock | ||
| * runtimes default to `vercel-sandbox`, while Ubuntu-based images default to | ||
| * `root`. The multi-user helpers group-own home and shared directories by | ||
| * this user's group so the file API can traverse them, so we resolve it from | ||
| * the running sandbox rather than assuming a fixed name. | ||
| * | ||
| * The result is memoized for the lifetime of this instance. | ||
| * | ||
| * @internal | ||
| */ | ||
| getDefaultUser(opts) { | ||
| if (!this.defaultUserPromise) this.defaultUserPromise = this.resolveDefaultUser(opts).catch((err) => { | ||
| this.defaultUserPromise = void 0; | ||
| throw err; | ||
| }); | ||
| return this.defaultUserPromise; | ||
| } | ||
| async resolveDefaultUser(opts) { | ||
| const result = await this.runCommand({ | ||
| cmd: "sh", | ||
| args: ["-c", "id -un; id -gn"], | ||
| signal: opts?.signal | ||
| }); | ||
| if (result.exitCode !== 0) { | ||
| const stderr = await result.stderr(); | ||
| throw new Error(`Failed to resolve the default sandbox user: ${stderr}`); | ||
| } | ||
| const [username, group] = (await result.stdout()).trim().split("\n"); | ||
| if (!username || !group) throw new Error(`Failed to resolve the default sandbox user (got "${username}:${group}")`); | ||
| return { | ||
| username, | ||
| group | ||
| }; | ||
| } | ||
| /** | ||
| * Create a new Linux user in this sandbox with an isolated home directory. | ||
| * | ||
| * The home directory is group-owned by the sandbox's default user group with | ||
| * `770` permissions, so the SDK's HTTP file API can read/write directly. | ||
| * Other users cannot access this user's home directory since they are not in | ||
| * that group. | ||
| * | ||
| * @param username - Linux username (lowercase letters, digits, hyphens, underscores) | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * @returns A {@link SandboxUser} instance for the created user. | ||
| * | ||
| * @example | ||
| * const alice = await sandbox.createUser("alice"); | ||
| * await alice.runCommand("whoami"); // "alice" | ||
| * await alice.writeFiles([{ path: "hello.txt", content: Buffer.from("hi") }]); | ||
| */ | ||
| async createUser(username, opts) { | ||
| require_validate_name.validateName(username, "username"); | ||
| const { group: defaultGroup } = await this.getDefaultUser(opts); | ||
| const useradd = await this.runCommand({ | ||
| cmd: "useradd", | ||
| args: [ | ||
| "-m", | ||
| "-s", | ||
| "/bin/bash", | ||
| username | ||
| ], | ||
| sudo: true, | ||
| signal: opts?.signal | ||
| }); | ||
| if (useradd.exitCode !== 0) { | ||
| const stderr = await useradd.stderr(); | ||
| throw new Error(`Failed to create user "${username}": ${stderr}`); | ||
| } | ||
| const chown = await this.runCommand({ | ||
| cmd: "chown", | ||
| args: [`${username}:${defaultGroup}`, `/home/${username}`], | ||
| sudo: true, | ||
| signal: opts?.signal | ||
| }); | ||
| if (chown.exitCode !== 0) { | ||
| const stderr = await chown.stderr(); | ||
| throw new Error(`Failed to set ownership on /home/${username}: ${stderr}`); | ||
| } | ||
| const chmod = await this.runCommand({ | ||
| cmd: "chmod", | ||
| args: ["770", `/home/${username}`], | ||
| sudo: true, | ||
| signal: opts?.signal | ||
| }); | ||
| if (chmod.exitCode !== 0) { | ||
| const stderr = await chmod.stderr(); | ||
| throw new Error(`Failed to set permissions on /home/${username}: ${stderr}`); | ||
| } | ||
| return new require_sandbox_user.SandboxUser({ | ||
| sandbox: this, | ||
| username | ||
| }); | ||
| } | ||
| /** | ||
| * Get a user handle without creating the user. | ||
| * Assumes the user already exists in the sandbox. | ||
| * | ||
| * @param username - Linux username | ||
| * @returns A {@link SandboxUser} instance. | ||
| * | ||
| * @example | ||
| * const root = sandbox.asUser("root"); | ||
| * await root.runCommand("whoami"); // "root" | ||
| */ | ||
| asUser(username) { | ||
| require_validate_name.validateName(username, "username"); | ||
| return new require_sandbox_user.SandboxUser({ | ||
| sandbox: this, | ||
| username | ||
| }); | ||
| } | ||
| /** | ||
| * Create a new Linux group with a shared directory. | ||
| * | ||
| * Creates a shared directory at `/shared/<groupname>` with setgid permissions | ||
| * (`2770`), so files created inside it automatically inherit the group. | ||
| * All group members can read and write files in the shared directory. | ||
| * | ||
| * @param groupname - Group name (lowercase letters, digits, hyphens, underscores) | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * @returns An object with the group name and shared directory path. | ||
| * | ||
| * @example | ||
| * const devs = await sandbox.createGroup("devs"); | ||
| * console.log(devs.sharedDir); // "/shared/devs" | ||
| * await sandbox.addUserToGroup("alice", "devs"); | ||
| */ | ||
| async createGroup(groupname, opts) { | ||
| require_validate_name.validateName(groupname, "group name"); | ||
| const { username: defaultUser } = await this.getDefaultUser(opts); | ||
| const sharedDir = `/shared/${groupname}`; | ||
| const groupadd = await this.runCommand({ | ||
| cmd: "groupadd", | ||
| args: [groupname], | ||
| sudo: true, | ||
| signal: opts?.signal | ||
| }); | ||
| if (groupadd.exitCode !== 0) { | ||
| const stderr = await groupadd.stderr(); | ||
| throw new Error(`Failed to create group "${groupname}": ${stderr}`); | ||
| } | ||
| const mkdirResult = await this.runCommand({ | ||
| cmd: "mkdir", | ||
| args: ["-p", sharedDir], | ||
| sudo: true, | ||
| signal: opts?.signal | ||
| }); | ||
| if (mkdirResult.exitCode !== 0) { | ||
| const stderr = await mkdirResult.stderr(); | ||
| throw new Error(`Failed to create shared directory ${sharedDir}: ${stderr}`); | ||
| } | ||
| const chown = await this.runCommand({ | ||
| cmd: "chown", | ||
| args: [`${defaultUser}:${groupname}`, sharedDir], | ||
| sudo: true, | ||
| signal: opts?.signal | ||
| }); | ||
| if (chown.exitCode !== 0) { | ||
| const stderr = await chown.stderr(); | ||
| throw new Error(`Failed to set ownership on ${sharedDir}: ${stderr}`); | ||
| } | ||
| const chmod = await this.runCommand({ | ||
| cmd: "chmod", | ||
| args: ["2770", sharedDir], | ||
| sudo: true, | ||
| signal: opts?.signal | ||
| }); | ||
| if (chmod.exitCode !== 0) { | ||
| const stderr = await chmod.stderr(); | ||
| throw new Error(`Failed to set permissions on ${sharedDir}: ${stderr}`); | ||
| } | ||
| return { | ||
| groupname, | ||
| sharedDir | ||
| }; | ||
| } | ||
| /** | ||
| * Add a user to a group. | ||
| * | ||
| * After joining, the user can read and write files in the group's | ||
| * shared directory at `/shared/<groupname>`. | ||
| * | ||
| * @param username - The user to add | ||
| * @param groupname - The group to add the user to | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * | ||
| * @example | ||
| * await sandbox.addUserToGroup("alice", "devs"); | ||
| */ | ||
| async addUserToGroup(username, groupname, opts) { | ||
| require_validate_name.validateName(username, "username"); | ||
| require_validate_name.validateName(groupname, "group name"); | ||
| const result = await this.runCommand({ | ||
| cmd: "usermod", | ||
| args: [ | ||
| "-aG", | ||
| groupname, | ||
| username | ||
| ], | ||
| sudo: true, | ||
| signal: opts?.signal | ||
| }); | ||
| if (result.exitCode !== 0) { | ||
| const stderr = await result.stderr(); | ||
| throw new Error(`Failed to add "${username}" to group "${groupname}": ${stderr}`); | ||
| } | ||
| } | ||
| /** | ||
| * Remove a user from a group. | ||
| * | ||
| * @param username - The user to remove | ||
| * @param groupname - The group to remove the user from | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * | ||
| * @example | ||
| * await sandbox.removeUserFromGroup("alice", "devs"); | ||
| */ | ||
| async removeUserFromGroup(username, groupname, opts) { | ||
| require_validate_name.validateName(username, "username"); | ||
| require_validate_name.validateName(groupname, "group name"); | ||
| const result = await this.runCommand({ | ||
| cmd: "gpasswd", | ||
| args: [ | ||
| "-d", | ||
| username, | ||
| groupname | ||
| ], | ||
| sudo: true, | ||
| signal: opts?.signal | ||
| }); | ||
| if (result.exitCode !== 0) { | ||
| const stderr = await result.stderr(); | ||
| throw new Error(`Failed to remove "${username}" from group "${groupname}": ${stderr}`); | ||
| } | ||
| } | ||
| /** | ||
| * Create a snapshot from this currently running sandbox. New sandboxes can | ||
@@ -797,0 +1034,0 @@ * then be created from this snapshot using {@link Sandbox.createFromSnapshot}. |
+115
-36
@@ -11,4 +11,6 @@ import { Paginator } from "./utils/paginator.cjs"; | ||
| import { SandboxSnapshot } from "./utils/sandbox-snapshot.cjs"; | ||
| import { ExecutionContext } from "./execution-context.cjs"; | ||
| import { RunCommandParams, Session } from "./session.cjs"; | ||
| import { FileSystem } from "./filesystem.cjs"; | ||
| import { SandboxUser } from "./sandbox-user.cjs"; | ||
| import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from "@workflow/serde"; | ||
@@ -90,26 +92,2 @@ | ||
| /** | ||
| * List of drives to attach to the sandbox, keyed by the desired mount path. | ||
| * The drive must be created beforehand with `Drive.getOrCreate`. | ||
| * | ||
| * The mount paths must be absolute and cannot overlap with each other. | ||
| * | ||
| * @example | ||
| * const drive = await Drive.getOrCreate({ name: "my-drive" }); | ||
| * const sandbox = await Sandbox.create({ | ||
| * mounts: { | ||
| * "/data": { drive: drive.name, mode: "read-write" }, | ||
| * }, | ||
| * }); | ||
| */ | ||
| mounts?: Record<string, { | ||
| /** | ||
| * The drive name to mount. | ||
| */ | ||
| drive: string; | ||
| /** | ||
| * Mount mode. Defaults to `read-write` if unspecified. | ||
| */ | ||
| mode?: "read-only" | "read-write"; | ||
| }>; | ||
| /** | ||
| * An AbortSignal to cancel sandbox creation. | ||
@@ -154,4 +132,2 @@ */ | ||
| } | ||
| type SandboxMounts = NonNullable<BaseCreateSandboxParams["mounts"]>; | ||
| type SandboxMountMode = NonNullable<SandboxMounts[string]["mode"]>; | ||
| /** | ||
@@ -267,3 +243,3 @@ * `runtime` and `image` are mutually exclusive: a sandbox starts from either a | ||
| */ | ||
| declare class Sandbox { | ||
| declare class Sandbox implements ExecutionContext { | ||
| private _client; | ||
@@ -288,2 +264,7 @@ private readonly projectId; | ||
| /** | ||
| * Memoized lookup of the sandbox's default user and its primary group. | ||
| * See {@link Sandbox.getDefaultUser}. | ||
| */ | ||
| private defaultUserPromise?; | ||
| /** | ||
| * A `node:fs/promises`-compatible API for interacting with the sandbox filesystem. | ||
@@ -384,6 +365,2 @@ * | ||
| /** | ||
| * Drives mounted on the sandbox, keyed by mount path. | ||
| */ | ||
| get mounts(): SandboxMounts | undefined; | ||
| /** | ||
| * The default network policy of this sandbox. | ||
@@ -546,6 +523,2 @@ */ | ||
| tags?: Record<string, string> | undefined; | ||
| mounts?: Record<string, { | ||
| drive: string; | ||
| mode?: "read-only" | "read-write" | undefined; | ||
| }> | undefined; | ||
| snapshotExpiration?: number | undefined; | ||
@@ -916,2 +889,108 @@ keepLastSnapshots?: { | ||
| /** | ||
| * The user that non-`sudo` commands and the HTTP file API run as, together | ||
| * with that user's primary group. This depends on the sandbox image: stock | ||
| * runtimes default to `vercel-sandbox`, while Ubuntu-based images default to | ||
| * `root`. The multi-user helpers group-own home and shared directories by | ||
| * this user's group so the file API can traverse them, so we resolve it from | ||
| * the running sandbox rather than assuming a fixed name. | ||
| * | ||
| * The result is memoized for the lifetime of this instance. | ||
| * | ||
| * @internal | ||
| */ | ||
| getDefaultUser(opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<{ | ||
| username: string; | ||
| group: string; | ||
| }>; | ||
| private resolveDefaultUser; | ||
| /** | ||
| * Create a new Linux user in this sandbox with an isolated home directory. | ||
| * | ||
| * The home directory is group-owned by the sandbox's default user group with | ||
| * `770` permissions, so the SDK's HTTP file API can read/write directly. | ||
| * Other users cannot access this user's home directory since they are not in | ||
| * that group. | ||
| * | ||
| * @param username - Linux username (lowercase letters, digits, hyphens, underscores) | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * @returns A {@link SandboxUser} instance for the created user. | ||
| * | ||
| * @example | ||
| * const alice = await sandbox.createUser("alice"); | ||
| * await alice.runCommand("whoami"); // "alice" | ||
| * await alice.writeFiles([{ path: "hello.txt", content: Buffer.from("hi") }]); | ||
| */ | ||
| createUser(username: string, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<SandboxUser>; | ||
| /** | ||
| * Get a user handle without creating the user. | ||
| * Assumes the user already exists in the sandbox. | ||
| * | ||
| * @param username - Linux username | ||
| * @returns A {@link SandboxUser} instance. | ||
| * | ||
| * @example | ||
| * const root = sandbox.asUser("root"); | ||
| * await root.runCommand("whoami"); // "root" | ||
| */ | ||
| asUser(username: "root" | (string & {})): SandboxUser; | ||
| /** | ||
| * Create a new Linux group with a shared directory. | ||
| * | ||
| * Creates a shared directory at `/shared/<groupname>` with setgid permissions | ||
| * (`2770`), so files created inside it automatically inherit the group. | ||
| * All group members can read and write files in the shared directory. | ||
| * | ||
| * @param groupname - Group name (lowercase letters, digits, hyphens, underscores) | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * @returns An object with the group name and shared directory path. | ||
| * | ||
| * @example | ||
| * const devs = await sandbox.createGroup("devs"); | ||
| * console.log(devs.sharedDir); // "/shared/devs" | ||
| * await sandbox.addUserToGroup("alice", "devs"); | ||
| */ | ||
| createGroup(groupname: string, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<{ | ||
| groupname: string; | ||
| sharedDir: string; | ||
| }>; | ||
| /** | ||
| * Add a user to a group. | ||
| * | ||
| * After joining, the user can read and write files in the group's | ||
| * shared directory at `/shared/<groupname>`. | ||
| * | ||
| * @param username - The user to add | ||
| * @param groupname - The group to add the user to | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * | ||
| * @example | ||
| * await sandbox.addUserToGroup("alice", "devs"); | ||
| */ | ||
| addUserToGroup(username: string, groupname: string, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<void>; | ||
| /** | ||
| * Remove a user from a group. | ||
| * | ||
| * @param username - The user to remove | ||
| * @param groupname - The group to remove the user from | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * | ||
| * @example | ||
| * await sandbox.removeUserFromGroup("alice", "devs"); | ||
| */ | ||
| removeUserFromGroup(username: string, groupname: string, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<void>; | ||
| /** | ||
| * Create a snapshot from this currently running sandbox. New sandboxes can | ||
@@ -1133,3 +1212,3 @@ * then be created from this snapshot using {@link Sandbox.createFromSnapshot}. | ||
| //#endregion | ||
| export { BaseCreateSandboxParams, Sandbox, SandboxMountMode, SandboxMounts, SerializedSandbox }; | ||
| export { Sandbox, SerializedSandbox }; | ||
| //# sourceMappingURL=sandbox.d.cts.map |
+115
-36
@@ -12,4 +12,6 @@ import { Paginator } from "./utils/paginator.js"; | ||
| import { SandboxSnapshot } from "./utils/sandbox-snapshot.js"; | ||
| import { ExecutionContext } from "./execution-context.js"; | ||
| import { RunCommandParams, Session } from "./session.js"; | ||
| import { FileSystem } from "./filesystem.js"; | ||
| import { SandboxUser } from "./sandbox-user.js"; | ||
| import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from "@workflow/serde"; | ||
@@ -91,26 +93,2 @@ | ||
| /** | ||
| * List of drives to attach to the sandbox, keyed by the desired mount path. | ||
| * The drive must be created beforehand with `Drive.getOrCreate`. | ||
| * | ||
| * The mount paths must be absolute and cannot overlap with each other. | ||
| * | ||
| * @example | ||
| * const drive = await Drive.getOrCreate({ name: "my-drive" }); | ||
| * const sandbox = await Sandbox.create({ | ||
| * mounts: { | ||
| * "/data": { drive: drive.name, mode: "read-write" }, | ||
| * }, | ||
| * }); | ||
| */ | ||
| mounts?: Record<string, { | ||
| /** | ||
| * The drive name to mount. | ||
| */ | ||
| drive: string; | ||
| /** | ||
| * Mount mode. Defaults to `read-write` if unspecified. | ||
| */ | ||
| mode?: "read-only" | "read-write"; | ||
| }>; | ||
| /** | ||
| * An AbortSignal to cancel sandbox creation. | ||
@@ -155,4 +133,2 @@ */ | ||
| } | ||
| type SandboxMounts = NonNullable<BaseCreateSandboxParams["mounts"]>; | ||
| type SandboxMountMode = NonNullable<SandboxMounts[string]["mode"]>; | ||
| /** | ||
@@ -268,3 +244,3 @@ * `runtime` and `image` are mutually exclusive: a sandbox starts from either a | ||
| */ | ||
| declare class Sandbox { | ||
| declare class Sandbox implements ExecutionContext { | ||
| private _client; | ||
@@ -289,2 +265,7 @@ private readonly projectId; | ||
| /** | ||
| * Memoized lookup of the sandbox's default user and its primary group. | ||
| * See {@link Sandbox.getDefaultUser}. | ||
| */ | ||
| private defaultUserPromise?; | ||
| /** | ||
| * A `node:fs/promises`-compatible API for interacting with the sandbox filesystem. | ||
@@ -385,6 +366,2 @@ * | ||
| /** | ||
| * Drives mounted on the sandbox, keyed by mount path. | ||
| */ | ||
| get mounts(): SandboxMounts | undefined; | ||
| /** | ||
| * The default network policy of this sandbox. | ||
@@ -547,6 +524,2 @@ */ | ||
| tags?: Record<string, string> | undefined; | ||
| mounts?: Record<string, { | ||
| drive: string; | ||
| mode?: "read-only" | "read-write" | undefined; | ||
| }> | undefined; | ||
| snapshotExpiration?: number | undefined; | ||
@@ -917,2 +890,108 @@ keepLastSnapshots?: { | ||
| /** | ||
| * The user that non-`sudo` commands and the HTTP file API run as, together | ||
| * with that user's primary group. This depends on the sandbox image: stock | ||
| * runtimes default to `vercel-sandbox`, while Ubuntu-based images default to | ||
| * `root`. The multi-user helpers group-own home and shared directories by | ||
| * this user's group so the file API can traverse them, so we resolve it from | ||
| * the running sandbox rather than assuming a fixed name. | ||
| * | ||
| * The result is memoized for the lifetime of this instance. | ||
| * | ||
| * @internal | ||
| */ | ||
| getDefaultUser(opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<{ | ||
| username: string; | ||
| group: string; | ||
| }>; | ||
| private resolveDefaultUser; | ||
| /** | ||
| * Create a new Linux user in this sandbox with an isolated home directory. | ||
| * | ||
| * The home directory is group-owned by the sandbox's default user group with | ||
| * `770` permissions, so the SDK's HTTP file API can read/write directly. | ||
| * Other users cannot access this user's home directory since they are not in | ||
| * that group. | ||
| * | ||
| * @param username - Linux username (lowercase letters, digits, hyphens, underscores) | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * @returns A {@link SandboxUser} instance for the created user. | ||
| * | ||
| * @example | ||
| * const alice = await sandbox.createUser("alice"); | ||
| * await alice.runCommand("whoami"); // "alice" | ||
| * await alice.writeFiles([{ path: "hello.txt", content: Buffer.from("hi") }]); | ||
| */ | ||
| createUser(username: string, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<SandboxUser>; | ||
| /** | ||
| * Get a user handle without creating the user. | ||
| * Assumes the user already exists in the sandbox. | ||
| * | ||
| * @param username - Linux username | ||
| * @returns A {@link SandboxUser} instance. | ||
| * | ||
| * @example | ||
| * const root = sandbox.asUser("root"); | ||
| * await root.runCommand("whoami"); // "root" | ||
| */ | ||
| asUser(username: "root" | (string & {})): SandboxUser; | ||
| /** | ||
| * Create a new Linux group with a shared directory. | ||
| * | ||
| * Creates a shared directory at `/shared/<groupname>` with setgid permissions | ||
| * (`2770`), so files created inside it automatically inherit the group. | ||
| * All group members can read and write files in the shared directory. | ||
| * | ||
| * @param groupname - Group name (lowercase letters, digits, hyphens, underscores) | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * @returns An object with the group name and shared directory path. | ||
| * | ||
| * @example | ||
| * const devs = await sandbox.createGroup("devs"); | ||
| * console.log(devs.sharedDir); // "/shared/devs" | ||
| * await sandbox.addUserToGroup("alice", "devs"); | ||
| */ | ||
| createGroup(groupname: string, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<{ | ||
| groupname: string; | ||
| sharedDir: string; | ||
| }>; | ||
| /** | ||
| * Add a user to a group. | ||
| * | ||
| * After joining, the user can read and write files in the group's | ||
| * shared directory at `/shared/<groupname>`. | ||
| * | ||
| * @param username - The user to add | ||
| * @param groupname - The group to add the user to | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * | ||
| * @example | ||
| * await sandbox.addUserToGroup("alice", "devs"); | ||
| */ | ||
| addUserToGroup(username: string, groupname: string, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<void>; | ||
| /** | ||
| * Remove a user from a group. | ||
| * | ||
| * @param username - The user to remove | ||
| * @param groupname - The group to remove the user from | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * | ||
| * @example | ||
| * await sandbox.removeUserFromGroup("alice", "devs"); | ||
| */ | ||
| removeUserFromGroup(username: string, groupname: string, opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<void>; | ||
| /** | ||
| * Create a snapshot from this currently running sandbox. New sandboxes can | ||
@@ -1134,3 +1213,3 @@ * then be created from this snapshot using {@link Sandbox.createFromSnapshot}. | ||
| //#endregion | ||
| export { BaseCreateSandboxParams, Sandbox, SandboxMountMode, SandboxMounts, SerializedSandbox }; | ||
| export { Sandbox, SerializedSandbox }; | ||
| //# sourceMappingURL=sandbox.d.ts.map |
+244
-7
@@ -10,2 +10,4 @@ import { APIError } from "./api-client/api-error.js"; | ||
| import { FileSystem } from "./filesystem.js"; | ||
| import { validateName } from "./utils/validate-name.js"; | ||
| import { SandboxUser } from "./sandbox-user.js"; | ||
| import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from "@workflow/serde"; | ||
@@ -175,8 +177,2 @@ import { setTimeout } from "node:timers/promises"; | ||
| /** | ||
| * Drives mounted on the sandbox, keyed by mount path. | ||
| */ | ||
| get mounts() { | ||
| return this.sandbox.mounts; | ||
| } | ||
| /** | ||
| * The default network policy of this sandbox. | ||
@@ -330,3 +326,2 @@ */ | ||
| tags: params?.tags, | ||
| mounts: params?.mounts, | ||
| snapshotExpiration: params?.snapshotExpiration, | ||
@@ -794,2 +789,244 @@ keepLastSnapshots: params?.keepLastSnapshots, | ||
| /** | ||
| * The user that non-`sudo` commands and the HTTP file API run as, together | ||
| * with that user's primary group. This depends on the sandbox image: stock | ||
| * runtimes default to `vercel-sandbox`, while Ubuntu-based images default to | ||
| * `root`. The multi-user helpers group-own home and shared directories by | ||
| * this user's group so the file API can traverse them, so we resolve it from | ||
| * the running sandbox rather than assuming a fixed name. | ||
| * | ||
| * The result is memoized for the lifetime of this instance. | ||
| * | ||
| * @internal | ||
| */ | ||
| getDefaultUser(opts) { | ||
| if (!this.defaultUserPromise) this.defaultUserPromise = this.resolveDefaultUser(opts).catch((err) => { | ||
| this.defaultUserPromise = void 0; | ||
| throw err; | ||
| }); | ||
| return this.defaultUserPromise; | ||
| } | ||
| async resolveDefaultUser(opts) { | ||
| const result = await this.runCommand({ | ||
| cmd: "sh", | ||
| args: ["-c", "id -un; id -gn"], | ||
| signal: opts?.signal | ||
| }); | ||
| if (result.exitCode !== 0) { | ||
| const stderr = await result.stderr(); | ||
| throw new Error(`Failed to resolve the default sandbox user: ${stderr}`); | ||
| } | ||
| const [username, group] = (await result.stdout()).trim().split("\n"); | ||
| if (!username || !group) throw new Error(`Failed to resolve the default sandbox user (got "${username}:${group}")`); | ||
| return { | ||
| username, | ||
| group | ||
| }; | ||
| } | ||
| /** | ||
| * Create a new Linux user in this sandbox with an isolated home directory. | ||
| * | ||
| * The home directory is group-owned by the sandbox's default user group with | ||
| * `770` permissions, so the SDK's HTTP file API can read/write directly. | ||
| * Other users cannot access this user's home directory since they are not in | ||
| * that group. | ||
| * | ||
| * @param username - Linux username (lowercase letters, digits, hyphens, underscores) | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * @returns A {@link SandboxUser} instance for the created user. | ||
| * | ||
| * @example | ||
| * const alice = await sandbox.createUser("alice"); | ||
| * await alice.runCommand("whoami"); // "alice" | ||
| * await alice.writeFiles([{ path: "hello.txt", content: Buffer.from("hi") }]); | ||
| */ | ||
| async createUser(username, opts) { | ||
| validateName(username, "username"); | ||
| const { group: defaultGroup } = await this.getDefaultUser(opts); | ||
| const useradd = await this.runCommand({ | ||
| cmd: "useradd", | ||
| args: [ | ||
| "-m", | ||
| "-s", | ||
| "/bin/bash", | ||
| username | ||
| ], | ||
| sudo: true, | ||
| signal: opts?.signal | ||
| }); | ||
| if (useradd.exitCode !== 0) { | ||
| const stderr = await useradd.stderr(); | ||
| throw new Error(`Failed to create user "${username}": ${stderr}`); | ||
| } | ||
| const chown = await this.runCommand({ | ||
| cmd: "chown", | ||
| args: [`${username}:${defaultGroup}`, `/home/${username}`], | ||
| sudo: true, | ||
| signal: opts?.signal | ||
| }); | ||
| if (chown.exitCode !== 0) { | ||
| const stderr = await chown.stderr(); | ||
| throw new Error(`Failed to set ownership on /home/${username}: ${stderr}`); | ||
| } | ||
| const chmod = await this.runCommand({ | ||
| cmd: "chmod", | ||
| args: ["770", `/home/${username}`], | ||
| sudo: true, | ||
| signal: opts?.signal | ||
| }); | ||
| if (chmod.exitCode !== 0) { | ||
| const stderr = await chmod.stderr(); | ||
| throw new Error(`Failed to set permissions on /home/${username}: ${stderr}`); | ||
| } | ||
| return new SandboxUser({ | ||
| sandbox: this, | ||
| username | ||
| }); | ||
| } | ||
| /** | ||
| * Get a user handle without creating the user. | ||
| * Assumes the user already exists in the sandbox. | ||
| * | ||
| * @param username - Linux username | ||
| * @returns A {@link SandboxUser} instance. | ||
| * | ||
| * @example | ||
| * const root = sandbox.asUser("root"); | ||
| * await root.runCommand("whoami"); // "root" | ||
| */ | ||
| asUser(username) { | ||
| validateName(username, "username"); | ||
| return new SandboxUser({ | ||
| sandbox: this, | ||
| username | ||
| }); | ||
| } | ||
| /** | ||
| * Create a new Linux group with a shared directory. | ||
| * | ||
| * Creates a shared directory at `/shared/<groupname>` with setgid permissions | ||
| * (`2770`), so files created inside it automatically inherit the group. | ||
| * All group members can read and write files in the shared directory. | ||
| * | ||
| * @param groupname - Group name (lowercase letters, digits, hyphens, underscores) | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * @returns An object with the group name and shared directory path. | ||
| * | ||
| * @example | ||
| * const devs = await sandbox.createGroup("devs"); | ||
| * console.log(devs.sharedDir); // "/shared/devs" | ||
| * await sandbox.addUserToGroup("alice", "devs"); | ||
| */ | ||
| async createGroup(groupname, opts) { | ||
| validateName(groupname, "group name"); | ||
| const { username: defaultUser } = await this.getDefaultUser(opts); | ||
| const sharedDir = `/shared/${groupname}`; | ||
| const groupadd = await this.runCommand({ | ||
| cmd: "groupadd", | ||
| args: [groupname], | ||
| sudo: true, | ||
| signal: opts?.signal | ||
| }); | ||
| if (groupadd.exitCode !== 0) { | ||
| const stderr = await groupadd.stderr(); | ||
| throw new Error(`Failed to create group "${groupname}": ${stderr}`); | ||
| } | ||
| const mkdirResult = await this.runCommand({ | ||
| cmd: "mkdir", | ||
| args: ["-p", sharedDir], | ||
| sudo: true, | ||
| signal: opts?.signal | ||
| }); | ||
| if (mkdirResult.exitCode !== 0) { | ||
| const stderr = await mkdirResult.stderr(); | ||
| throw new Error(`Failed to create shared directory ${sharedDir}: ${stderr}`); | ||
| } | ||
| const chown = await this.runCommand({ | ||
| cmd: "chown", | ||
| args: [`${defaultUser}:${groupname}`, sharedDir], | ||
| sudo: true, | ||
| signal: opts?.signal | ||
| }); | ||
| if (chown.exitCode !== 0) { | ||
| const stderr = await chown.stderr(); | ||
| throw new Error(`Failed to set ownership on ${sharedDir}: ${stderr}`); | ||
| } | ||
| const chmod = await this.runCommand({ | ||
| cmd: "chmod", | ||
| args: ["2770", sharedDir], | ||
| sudo: true, | ||
| signal: opts?.signal | ||
| }); | ||
| if (chmod.exitCode !== 0) { | ||
| const stderr = await chmod.stderr(); | ||
| throw new Error(`Failed to set permissions on ${sharedDir}: ${stderr}`); | ||
| } | ||
| return { | ||
| groupname, | ||
| sharedDir | ||
| }; | ||
| } | ||
| /** | ||
| * Add a user to a group. | ||
| * | ||
| * After joining, the user can read and write files in the group's | ||
| * shared directory at `/shared/<groupname>`. | ||
| * | ||
| * @param username - The user to add | ||
| * @param groupname - The group to add the user to | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * | ||
| * @example | ||
| * await sandbox.addUserToGroup("alice", "devs"); | ||
| */ | ||
| async addUserToGroup(username, groupname, opts) { | ||
| validateName(username, "username"); | ||
| validateName(groupname, "group name"); | ||
| const result = await this.runCommand({ | ||
| cmd: "usermod", | ||
| args: [ | ||
| "-aG", | ||
| groupname, | ||
| username | ||
| ], | ||
| sudo: true, | ||
| signal: opts?.signal | ||
| }); | ||
| if (result.exitCode !== 0) { | ||
| const stderr = await result.stderr(); | ||
| throw new Error(`Failed to add "${username}" to group "${groupname}": ${stderr}`); | ||
| } | ||
| } | ||
| /** | ||
| * Remove a user from a group. | ||
| * | ||
| * @param username - The user to remove | ||
| * @param groupname - The group to remove the user from | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * | ||
| * @example | ||
| * await sandbox.removeUserFromGroup("alice", "devs"); | ||
| */ | ||
| async removeUserFromGroup(username, groupname, opts) { | ||
| validateName(username, "username"); | ||
| validateName(groupname, "group name"); | ||
| const result = await this.runCommand({ | ||
| cmd: "gpasswd", | ||
| args: [ | ||
| "-d", | ||
| username, | ||
| groupname | ||
| ], | ||
| sudo: true, | ||
| signal: opts?.signal | ||
| }); | ||
| if (result.exitCode !== 0) { | ||
| const stderr = await result.stderr(); | ||
| throw new Error(`Failed to remove "${username}" from group "${groupname}": ${stderr}`); | ||
| } | ||
| } | ||
| /** | ||
| * Create a snapshot from this currently running sandbox. New sandboxes can | ||
@@ -796,0 +1033,0 @@ * then be created from this snapshot using {@link Sandbox.createFromSnapshot}. |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"session.cjs","names":["getCredentials","APIClient","WORKFLOW_SERIALIZE","WORKFLOW_DESERIALIZE","toSandboxSnapshot","Command","params: RunCommandParams","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 * Maximum time in milliseconds the command may run before it is killed with\n * SIGKILL. The timeout is enforced by the sandbox at exec time, so it applies\n * whether or not the command is awaited (including `detached: true`).\n */\n timeoutMs?: number;\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 /** @internal */\n updateRoutes(routes: SandboxRouteData[]): void {\n (this as { routes: SandboxRouteData[] }).routes = routes;\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 * @param opts.timeoutMs - Maximum time in milliseconds to wait for the\n * command to complete. On expiry the process is killed with SIGKILL.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n async runCommand(\n command: string,\n args?: string[],\n opts?: { signal?: AbortSignal; timeoutMs?: number },\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; timeoutMs?: number },\n ): Promise<Command | CommandFinished> {\n \"use step\";\n const client = await this.ensureClient();\n const params: RunCommandParams =\n typeof commandOrParams === \"string\"\n ? {\n cmd: commandOrParams,\n args,\n signal: opts?.signal,\n timeoutMs: opts?.timeoutMs,\n }\n : commandOrParams;\n const wait = params.detached ? false : true;\n const shouldPipeLogs = Boolean(params.stdout || params.stderr);\n\n if (wait) {\n let stdout = \"\", stderr = \"\";\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 logs: true,\n onLog: (log) => {\n if (log.stream === \"stdout\") {\n stdout += log.data;\n params.stdout?.write(log.data);\n } else {\n stderr += log.data;\n params.stderr?.write(log.data);\n }\n },\n timeout: params.timeoutMs,\n signal: params.signal,\n });\n\n const finished = await commandStream.finished;\n\n return new CommandFinished({\n client,\n sessionId: this.session.id,\n cmd: finished,\n exitCode: finished.exitCode ?? 0,\n durationMs: finished.durationMs,\n output: { stdout, stderr },\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 timeout: params.timeoutMs,\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 if (shouldPipeLogs) {\n (async () => {\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 (params.stderr ?? params.stdout)?.emit(\"error\", err);\n }\n })();\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 * Open an interactive shell session. Returns the WebSocket URL and token the\n * client uses to connect to the controller-hosted PTY.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n */\n async openInteractive(opts?: {\n signal?: AbortSignal;\n }): Promise<{ url: string; token: string }> {\n \"use step\";\n const client = await this.ensureClient();\n const { json } = await client.openInteractive({\n sessionId: this.session.id,\n signal: opts?.signal,\n });\n return { url: json.url, token: json.token };\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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"} | ||
| {"version":3,"file":"session.cjs","names":["getCredentials","APIClient","WORKFLOW_SERIALIZE","WORKFLOW_DESERIALIZE","toSandboxSnapshot","Command","params: RunCommandParams","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\";\nimport type { ExecutionContext } from \"./execution-context.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 * Maximum time in milliseconds the command may run before it is killed with\n * SIGKILL. The timeout is enforced by the sandbox at exec time, so it applies\n * whether or not the command is awaited (including `detached: true`).\n */\n timeoutMs?: number;\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 implements ExecutionContext {\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 /** @internal */\n updateRoutes(routes: SandboxRouteData[]): void {\n (this as { routes: SandboxRouteData[] }).routes = routes;\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 * @param opts.timeoutMs - Maximum time in milliseconds to wait for the\n * command to complete. On expiry the process is killed with SIGKILL.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n async runCommand(\n command: string,\n args?: string[],\n opts?: { signal?: AbortSignal; timeoutMs?: number },\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; timeoutMs?: number },\n ): Promise<Command | CommandFinished> {\n \"use step\";\n const client = await this.ensureClient();\n const params: RunCommandParams =\n typeof commandOrParams === \"string\"\n ? {\n cmd: commandOrParams,\n args,\n signal: opts?.signal,\n timeoutMs: opts?.timeoutMs,\n }\n : commandOrParams;\n const wait = params.detached ? false : true;\n const shouldPipeLogs = Boolean(params.stdout || params.stderr);\n\n if (wait) {\n let stdout = \"\", stderr = \"\";\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 logs: true,\n onLog: (log) => {\n if (log.stream === \"stdout\") {\n stdout += log.data;\n params.stdout?.write(log.data);\n } else {\n stderr += log.data;\n params.stderr?.write(log.data);\n }\n },\n timeout: params.timeoutMs,\n signal: params.signal,\n });\n\n const finished = await commandStream.finished;\n\n return new CommandFinished({\n client,\n sessionId: this.session.id,\n cmd: finished,\n exitCode: finished.exitCode ?? 0,\n durationMs: finished.durationMs,\n output: { stdout, stderr },\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 timeout: params.timeoutMs,\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 if (shouldPipeLogs) {\n (async () => {\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 (params.stderr ?? params.stdout)?.emit(\"error\", err);\n }\n })();\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 * Open an interactive shell session. Returns the WebSocket URL and token the\n * client uses to connect to the controller-hosted PTY.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n */\n async openInteractive(opts?: {\n signal?: AbortSignal;\n }): Promise<{ url: string; token: string }> {\n \"use step\";\n const client = await this.ensureClient();\n const { json } = await client.openInteractive({\n sessionId: this.session.id,\n signal: opts?.signal,\n });\n return { url: json.url, token: json.token };\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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"} |
@@ -7,2 +7,3 @@ import { SandboxMetaData, SandboxRouteData, SessionMetaData, SnapshotMetadata } from "./api-client/validators.cjs"; | ||
| import { SandboxSnapshot } from "./utils/sandbox-snapshot.cjs"; | ||
| import { ExecutionContext } from "./execution-context.cjs"; | ||
| import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from "@workflow/serde"; | ||
@@ -70,3 +71,3 @@ import { Writable } from "stream"; | ||
| */ | ||
| declare class Session { | ||
| declare class Session implements ExecutionContext { | ||
| private _client; | ||
@@ -73,0 +74,0 @@ /** |
@@ -8,2 +8,3 @@ import { SandboxMetaData, SandboxRouteData, SessionMetaData, SnapshotMetadata } from "./api-client/validators.js"; | ||
| import { SandboxSnapshot } from "./utils/sandbox-snapshot.js"; | ||
| import { ExecutionContext } from "./execution-context.js"; | ||
| import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from "@workflow/serde"; | ||
@@ -70,3 +71,3 @@ import { Writable } from "stream"; | ||
| */ | ||
| declare class Session { | ||
| declare class Session implements ExecutionContext { | ||
| private _client; | ||
@@ -73,0 +74,0 @@ /** |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"session.js","names":["params: RunCommandParams","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 * Maximum time in milliseconds the command may run before it is killed with\n * SIGKILL. The timeout is enforced by the sandbox at exec time, so it applies\n * whether or not the command is awaited (including `detached: true`).\n */\n timeoutMs?: number;\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 /** @internal */\n updateRoutes(routes: SandboxRouteData[]): void {\n (this as { routes: SandboxRouteData[] }).routes = routes;\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 * @param opts.timeoutMs - Maximum time in milliseconds to wait for the\n * command to complete. On expiry the process is killed with SIGKILL.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n async runCommand(\n command: string,\n args?: string[],\n opts?: { signal?: AbortSignal; timeoutMs?: number },\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; timeoutMs?: number },\n ): Promise<Command | CommandFinished> {\n \"use step\";\n const client = await this.ensureClient();\n const params: RunCommandParams =\n typeof commandOrParams === \"string\"\n ? {\n cmd: commandOrParams,\n args,\n signal: opts?.signal,\n timeoutMs: opts?.timeoutMs,\n }\n : commandOrParams;\n const wait = params.detached ? false : true;\n const shouldPipeLogs = Boolean(params.stdout || params.stderr);\n\n if (wait) {\n let stdout = \"\", stderr = \"\";\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 logs: true,\n onLog: (log) => {\n if (log.stream === \"stdout\") {\n stdout += log.data;\n params.stdout?.write(log.data);\n } else {\n stderr += log.data;\n params.stderr?.write(log.data);\n }\n },\n timeout: params.timeoutMs,\n signal: params.signal,\n });\n\n const finished = await commandStream.finished;\n\n return new CommandFinished({\n client,\n sessionId: this.session.id,\n cmd: finished,\n exitCode: finished.exitCode ?? 0,\n durationMs: finished.durationMs,\n output: { stdout, stderr },\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 timeout: params.timeoutMs,\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 if (shouldPipeLogs) {\n (async () => {\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 (params.stderr ?? params.stdout)?.emit(\"error\", err);\n }\n })();\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 * Open an interactive shell session. Returns the WebSocket URL and token the\n * client uses to connect to the controller-hosted PTY.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n */\n async openInteractive(opts?: {\n signal?: AbortSignal;\n }): Promise<{ url: string; token: string }> {\n \"use step\";\n const client = await this.ensureClient();\n const { json } = await client.openInteractive({\n sessionId: this.session.id,\n signal: opts?.signal,\n });\n return { url: json.url, token: json.token };\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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"} | ||
| {"version":3,"file":"session.js","names":["params: RunCommandParams","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\";\nimport type { ExecutionContext } from \"./execution-context.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 * Maximum time in milliseconds the command may run before it is killed with\n * SIGKILL. The timeout is enforced by the sandbox at exec time, so it applies\n * whether or not the command is awaited (including `detached: true`).\n */\n timeoutMs?: number;\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 implements ExecutionContext {\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 /** @internal */\n updateRoutes(routes: SandboxRouteData[]): void {\n (this as { routes: SandboxRouteData[] }).routes = routes;\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 * @param opts.timeoutMs - Maximum time in milliseconds to wait for the\n * command to complete. On expiry the process is killed with SIGKILL.\n * @returns A {@link CommandFinished} result once execution is done.\n */\n async runCommand(\n command: string,\n args?: string[],\n opts?: { signal?: AbortSignal; timeoutMs?: number },\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; timeoutMs?: number },\n ): Promise<Command | CommandFinished> {\n \"use step\";\n const client = await this.ensureClient();\n const params: RunCommandParams =\n typeof commandOrParams === \"string\"\n ? {\n cmd: commandOrParams,\n args,\n signal: opts?.signal,\n timeoutMs: opts?.timeoutMs,\n }\n : commandOrParams;\n const wait = params.detached ? false : true;\n const shouldPipeLogs = Boolean(params.stdout || params.stderr);\n\n if (wait) {\n let stdout = \"\", stderr = \"\";\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 logs: true,\n onLog: (log) => {\n if (log.stream === \"stdout\") {\n stdout += log.data;\n params.stdout?.write(log.data);\n } else {\n stderr += log.data;\n params.stderr?.write(log.data);\n }\n },\n timeout: params.timeoutMs,\n signal: params.signal,\n });\n\n const finished = await commandStream.finished;\n\n return new CommandFinished({\n client,\n sessionId: this.session.id,\n cmd: finished,\n exitCode: finished.exitCode ?? 0,\n durationMs: finished.durationMs,\n output: { stdout, stderr },\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 timeout: params.timeoutMs,\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 if (shouldPipeLogs) {\n (async () => {\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 (params.stderr ?? params.stdout)?.emit(\"error\", err);\n }\n })();\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 * Open an interactive shell session. Returns the WebSocket URL and token the\n * client uses to connect to the controller-hosted PTY.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n */\n async openInteractive(opts?: {\n signal?: AbortSignal;\n }): Promise<{ url: string; token: string }> {\n \"use step\";\n const client = await this.ensureClient();\n const { json } = await client.openInteractive({\n sessionId: this.session.id,\n signal: opts?.signal,\n });\n return { url: json.url, token: json.token };\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
-1
| //#region src/version.ts | ||
| const VERSION = "2.6.0-beta.0"; | ||
| const VERSION = "2.6.0"; | ||
@@ -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.6.0-beta.0\";\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.6.0\";\n"],"mappings":";;AACA,MAAa,UAAU"} |
+1
-1
| //#region src/version.ts | ||
| const VERSION = "2.6.0-beta.0"; | ||
| const VERSION = "2.6.0"; | ||
@@ -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.6.0-beta.0\";\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.6.0\";\n"],"mappings":";AACA,MAAa,UAAU"} |
+1
-1
| { | ||
| "name": "@vercel/sandbox", | ||
| "version": "2.6.0-beta.0", | ||
| "version": "2.6.0", | ||
| "description": "Software Development Kit for Vercel Sandbox", | ||
@@ -5,0 +5,0 @@ "type": "module", |
+241
-0
@@ -282,2 +282,243 @@ # Vercel Sandbox | ||
| ## Multi-user | ||
| Sandboxes support creating isolated Linux users with their own home directories, | ||
| file permissions, and process ownership. This is useful for multi-agent workflows | ||
| where each agent needs its own workspace, or for simulating multi-user | ||
| environments. | ||
| > **Note:** The sandbox image must have `/bin/bash` installed. It is the login | ||
| > shell for created users and is used to wrap commands that run as a user. The | ||
| > stock Vercel Sandbox images include it. | ||
| ### Creating users | ||
| ```typescript | ||
| import { Sandbox } from "@vercel/sandbox"; | ||
| const sandbox = await Sandbox.create(); | ||
| // Creates /home/alice with isolated permissions | ||
| const alice = await sandbox.createUser("alice"); | ||
| alice.username; // "alice" | ||
| alice.homeDir; // "/home/alice" | ||
| ``` | ||
| `createUser` sets up: | ||
| - A Linux user with `/bin/bash` as the default shell | ||
| - A home directory at `/home/<username>` group-owned by the sandbox's default user group with `770` permissions | ||
| ### Running commands as a user | ||
| All commands run as the user by default, with the working directory set to their | ||
| home: | ||
| ```typescript | ||
| const alice = await sandbox.createUser("alice"); | ||
| const whoami = await alice.runCommand("whoami"); | ||
| await whoami.stdout(); // "alice\n" | ||
| const pwd = await alice.runCommand("pwd"); | ||
| await pwd.stdout(); // "/home/alice\n" | ||
| ``` | ||
| You can pass environment variables, override the working directory, or use the | ||
| full `RunCommandParams` interface: | ||
| ```typescript | ||
| // Environment variables | ||
| await alice.runCommand({ | ||
| cmd: "node", | ||
| args: ["-e", "console.log(process.env.API_KEY)"], | ||
| env: { API_KEY: "secret" }, | ||
| }); | ||
| // Custom working directory | ||
| await alice.runCommand({ cmd: "ls", cwd: "/tmp" }); | ||
| // Detached mode for long-running processes | ||
| const server = await alice.runCommand({ | ||
| cmd: "node", | ||
| args: ["server.js"], | ||
| detached: true, | ||
| }); | ||
| ``` | ||
| To escalate to root, pass `sudo: true`: | ||
| ```typescript | ||
| await alice.runCommand({ | ||
| cmd: "dnf", | ||
| args: ["install", "-y", "git"], | ||
| sudo: true, | ||
| }); | ||
| ``` | ||
| ### File operations | ||
| `writeFiles`, `readFile`, `readFileToBuffer`, and `mkDir` all resolve relative | ||
| paths against the user's home directory. Written files are owned by the user: | ||
| ```typescript | ||
| const alice = await sandbox.createUser("alice"); | ||
| // Writes to /home/alice/app.js, owned by alice:alice | ||
| await alice.writeFiles([ | ||
| { path: "app.js", content: Buffer.from('console.log("hi")') }, | ||
| ]); | ||
| // Read it back | ||
| const buf = await alice.readFileToBuffer({ path: "app.js" }); | ||
| buf?.toString(); // 'console.log("hi")' | ||
| // Stream reads | ||
| const stream = await alice.readFile({ path: "app.js" }); | ||
| // Create directories owned by the user | ||
| await alice.mkDir("projects/my-app"); | ||
| // Absolute paths also work | ||
| await alice.writeFiles([ | ||
| { path: "/tmp/output.txt", content: Buffer.from("data") }, | ||
| ]); | ||
| ``` | ||
| ### File isolation | ||
| Users cannot access each other's home directories: | ||
| ```typescript | ||
| const alice = await sandbox.createUser("alice"); | ||
| const bob = await sandbox.createUser("bob"); | ||
| await alice.writeFiles([ | ||
| { path: "secret.txt", content: Buffer.from("alice only") }, | ||
| ]); | ||
| // Bob cannot read, list, or write to alice's home | ||
| const cat = await bob.runCommand({ | ||
| cmd: "cat", | ||
| args: ["/home/alice/secret.txt"], | ||
| }); | ||
| cat.exitCode; // non-zero — Permission denied | ||
| ``` | ||
| **The SDK can read all users' files** because home directories are group-owned | ||
| by the sandbox's default user group. Both `SandboxUser` methods and direct | ||
| `sandbox` methods work: | ||
| ```typescript | ||
| // Via SandboxUser (relative paths resolve to home dir) | ||
| const buf = await alice.readFileToBuffer({ path: "secret.txt" }); | ||
| buf?.toString(); // "alice only" | ||
| // Via sandbox directly (absolute path required) | ||
| const buf2 = await sandbox.readFileToBuffer({ path: "/home/alice/secret.txt" }); | ||
| buf2?.toString(); // "alice only" | ||
| ``` | ||
| ### Groups and shared directories | ||
| Create groups to let users collaborate through a shared directory: | ||
| ```typescript | ||
| const devs = await sandbox.createGroup("devs"); | ||
| devs.sharedDir; // "/shared/devs" | ||
| await sandbox.addUserToGroup("alice", "devs"); | ||
| await sandbox.addUserToGroup("bob", "devs"); | ||
| // Alice writes to the shared directory | ||
| await alice.runCommand({ | ||
| cmd: "bash", | ||
| args: ["-c", 'echo "spec v2" > /shared/devs/spec.txt'], | ||
| }); | ||
| // Bob can read it — files inherit group ownership via setgid | ||
| const spec = await bob.runCommand({ | ||
| cmd: "cat", | ||
| args: ["/shared/devs/spec.txt"], | ||
| }); | ||
| await spec.stdout(); // "spec v2\n" | ||
| // Non-members are blocked | ||
| const charlie = await sandbox.createUser("charlie"); | ||
| const ls = await charlie.runCommand({ cmd: "ls", args: ["/shared/devs"] }); | ||
| ls.exitCode; // non-zero — Permission denied | ||
| ``` | ||
| Shared directories use setgid (`2770`), so files created inside them | ||
| automatically inherit the group. All group members get read/write access. | ||
| Convenience methods are available on `SandboxUser`: | ||
| ```typescript | ||
| await alice.addToGroup("devs"); | ||
| await alice.removeFromGroup("devs"); | ||
| ``` | ||
| ### Using `asUser` for existing users | ||
| If a user already exists (e.g., from a snapshot or manual creation), use | ||
| `asUser` to get a handle without re-creating: | ||
| ```typescript | ||
| const existing = sandbox.asUser("bob"); | ||
| await existing.runCommand("whoami"); // "bob" | ||
| ``` | ||
| ### Username validation | ||
| Usernames and group names must match `/^[a-z_][a-z0-9_-]*$/` and be at most 32 | ||
| characters. Invalid names throw an error immediately: | ||
| ```typescript | ||
| sandbox.asUser("Alice"); // throws — uppercase | ||
| sandbox.asUser("user name"); // throws — space | ||
| sandbox.asUser("$(whoami)"); // throws — special characters | ||
| sandbox.asUser("a".repeat(33)); // throws — too long | ||
| ``` | ||
| ### Multi-agent example | ||
| ```typescript | ||
| const sandbox = await Sandbox.create(); | ||
| // Each agent gets its own isolated workspace | ||
| const researcher = await sandbox.createUser("researcher"); | ||
| const coder = await sandbox.createUser("coder"); | ||
| const reviewer = await sandbox.createUser("reviewer"); | ||
| // Shared workspace for collaboration | ||
| await sandbox.createGroup("project"); | ||
| await sandbox.addUserToGroup("researcher", "project"); | ||
| await sandbox.addUserToGroup("coder", "project"); | ||
| await sandbox.addUserToGroup("reviewer", "project"); | ||
| // Researcher writes findings to shared dir | ||
| await researcher.runCommand({ | ||
| cmd: "bash", | ||
| args: ["-c", 'echo "API spec v2" > /shared/project/spec.txt'], | ||
| }); | ||
| // Coder reads spec, writes code in their own home | ||
| const spec = await coder.runCommand({ | ||
| cmd: "cat", | ||
| args: ["/shared/project/spec.txt"], | ||
| }); | ||
| await coder.writeFiles([ | ||
| { path: "app.js", content: Buffer.from(`// ${await spec.stdout()}`) }, | ||
| ]); | ||
| // Reviewer can read the shared spec but not coder's private files | ||
| const blocked = await reviewer.runCommand({ | ||
| cmd: "cat", | ||
| args: ["/home/coder/app.js"], | ||
| }); | ||
| blocked.exitCode; // non-zero — isolation enforced | ||
| ``` | ||
| [create-token]: https://vercel.com/account/settings/tokens | ||
@@ -284,0 +525,0 @@ [hive]: https://vercel.com/blog/a-deep-dive-into-hive-vercels-builds-infrastructure |
-194
| 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"); | ||
| //#region src/drive.ts | ||
| /** | ||
| * A Drive is a persistent, bottomless storage that can be attached and detached to Sandboxes. | ||
| * Drives can be mounted as read-write or read-only, at a configurable path with `Sandbox.create()`. | ||
| * | ||
| * Use {@link Drive.getOrCreate} to construct. | ||
| * @hideconstructor | ||
| */ | ||
| var Drive = class Drive { | ||
| /** | ||
| * Lazily resolve credentials and construct an API client. | ||
| * @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; | ||
| } | ||
| /** | ||
| * The name of the drive. | ||
| */ | ||
| get name() { | ||
| return this.drive.name; | ||
| } | ||
| /** | ||
| * The project ID that owns the drive. | ||
| */ | ||
| get projectId() { | ||
| return this._projectId; | ||
| } | ||
| /** | ||
| * The maximum drive size in bytes. | ||
| */ | ||
| get maxSize() { | ||
| return this.drive.maxSizeBytes; | ||
| } | ||
| /** | ||
| * Current session ID the drive is attached to, if any. | ||
| */ | ||
| get currentSessionId() { | ||
| return this.drive.currentSessionId; | ||
| } | ||
| /** | ||
| * Current sandbox name the drive is attached to, if any. | ||
| */ | ||
| get currentSandboxName() { | ||
| return this.drive.currentSandboxName; | ||
| } | ||
| /** | ||
| * Timestamp when the drive was created. | ||
| */ | ||
| get createdAt() { | ||
| return new Date(this.drive.createdAt); | ||
| } | ||
| /** | ||
| * Timestamp when the drive was last updated. | ||
| */ | ||
| get updatedAt() { | ||
| return new Date(this.drive.updatedAt); | ||
| } | ||
| /** | ||
| * Serialize a Drive instance to plain data for @workflow/serde. | ||
| * | ||
| * @param instance - The Drive instance to serialize | ||
| * @returns A plain object containing drive metadata | ||
| */ | ||
| static [__workflow_serde.WORKFLOW_SERIALIZE](instance) { | ||
| return { | ||
| drive: instance.drive, | ||
| projectId: instance._projectId | ||
| }; | ||
| } | ||
| /** | ||
| * Deserialize a Drive 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 drive data | ||
| * @returns The reconstructed Drive instance | ||
| */ | ||
| static [__workflow_serde.WORKFLOW_DESERIALIZE](data) { | ||
| return new Drive({ | ||
| drive: data.drive, | ||
| projectId: data.projectId | ||
| }); | ||
| } | ||
| constructor({ client, drive, projectId }) { | ||
| this._client = null; | ||
| this._client = client ?? null; | ||
| this.drive = drive; | ||
| this._projectId = projectId ?? drive.projectId; | ||
| } | ||
| /** | ||
| * Allow to get a list of drives for a team narrowed to the given params. | ||
| * It returns both the drives and the pagination metadata to allow getting | ||
| * the next page of results. | ||
| * | ||
| * The returned object is async-iterable to auto-paginate through all pages: | ||
| * | ||
| * ```ts | ||
| * const result = await Drive.list({ limit: 10 }); | ||
| * for await (const drive of result) { ... } | ||
| * // or: await result.toArray(); | ||
| * // or: for await (const page of result.pages()) { ... } | ||
| * ``` | ||
| */ | ||
| static async list(params) { | ||
| "use step"; | ||
| 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 fetchPage = async (cursor) => { | ||
| const response = await client.listDrives({ | ||
| ...credentials, | ||
| ...params, | ||
| ...cursor !== void 0 && { cursor } | ||
| }); | ||
| return { | ||
| ...response.json, | ||
| drives: response.json.drives.map((drive) => new Drive({ | ||
| client, | ||
| drive, | ||
| projectId: credentials.projectId | ||
| })) | ||
| }; | ||
| }; | ||
| return require_paginator.attachPaginator(await fetchPage(params?.cursor ?? params?.until), { | ||
| itemsKey: "drives", | ||
| fetchNext: fetchPage, | ||
| signal: params?.signal | ||
| }); | ||
| } | ||
| /** | ||
| * Retrieve an existing drive, or create a new one if it doesn't exists. | ||
| * | ||
| * @param params - Get/create parameters and optional credentials. | ||
| * @returns A promise resolving to the {@link Drive}. | ||
| */ | ||
| static async getOrCreate(params) { | ||
| "use step"; | ||
| const credentials = await require_get_credentials.getCredentials(params); | ||
| const client = new require_api_client.APIClient({ | ||
| teamId: credentials.teamId, | ||
| token: credentials.token, | ||
| fetch: params.fetch | ||
| }); | ||
| return new Drive({ | ||
| client, | ||
| drive: (await client.getOrCreateDrive({ | ||
| projectId: credentials.projectId, | ||
| name: params.name, | ||
| maxSizeBytes: params.maxSize, | ||
| signal: params.signal | ||
| })).json.drive, | ||
| projectId: credentials.projectId | ||
| }); | ||
| } | ||
| /** | ||
| * Delete this drive. The drive must not be attached to any sandbox. | ||
| * This operation is irreversible and will delete all data stored in the drive. | ||
| * | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * @returns A promise that resolves once the drive has been deleted. | ||
| */ | ||
| async delete(opts) { | ||
| "use step"; | ||
| this.drive = (await (await this.ensureClient()).deleteDrive({ | ||
| projectId: this._projectId, | ||
| name: this.drive.name, | ||
| signal: opts?.signal | ||
| })).json.drive; | ||
| } | ||
| }; | ||
| //#endregion | ||
| exports.Drive = Drive; | ||
| //# sourceMappingURL=drive.cjs.map |
| {"version":3,"file":"drive.cjs","names":["getCredentials","APIClient","WORKFLOW_SERIALIZE","WORKFLOW_DESERIALIZE","attachPaginator"],"sources":["../src/drive.ts"],"sourcesContent":["import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport type { WithFetchOptions } from \"./api-client/api-client.js\";\nimport type { DriveMetadata } 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 SerializedDrive {\n drive: DriveMetadata;\n projectId?: string;\n}\n\n/** @inline */\ninterface GetOrCreateDriveParams {\n /**\n * The name of the drive to get or create. Must be unique within the project.\n */\n name: string;\n /**\n * Maximum drive size in bytes. If omitted, a default of 100 GiB is used.\n */\n maxSize?: number;\n /**\n * An AbortSignal to cancel the operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * A Drive is a persistent, bottomless storage that can be attached and detached to Sandboxes.\n * Drives can be mounted as read-write or read-only, at a configurable path with `Sandbox.create()`.\n *\n * Use {@link Drive.getOrCreate} to construct.\n * @hideconstructor\n */\nexport class Drive {\n private _client: APIClient | null = null;\n private drive: DriveMetadata;\n private readonly _projectId: string;\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 the drive.\n */\n public get name(): string {\n return this.drive.name;\n }\n\n /**\n * The project ID that owns the drive.\n */\n public get projectId(): string {\n return this._projectId;\n }\n\n /**\n * The maximum drive size in bytes.\n */\n public get maxSize(): number {\n return this.drive.maxSizeBytes;\n }\n\n /**\n * Current session ID the drive is attached to, if any.\n */\n public get currentSessionId(): string | undefined {\n return this.drive.currentSessionId;\n }\n\n /**\n * Current sandbox name the drive is attached to, if any.\n */\n public get currentSandboxName(): string | undefined {\n return this.drive.currentSandboxName;\n }\n\n /**\n * Timestamp when the drive was created.\n */\n public get createdAt(): Date {\n return new Date(this.drive.createdAt);\n }\n\n /**\n * Timestamp when the drive was last updated.\n */\n public get updatedAt(): Date {\n return new Date(this.drive.updatedAt);\n }\n\n /**\n * Serialize a Drive instance to plain data for @workflow/serde.\n *\n * @param instance - The Drive instance to serialize\n * @returns A plain object containing drive metadata\n */\n static [WORKFLOW_SERIALIZE](instance: Drive): SerializedDrive {\n return {\n drive: instance.drive,\n projectId: instance._projectId,\n };\n }\n\n /**\n * Deserialize a Drive 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 drive data\n * @returns The reconstructed Drive instance\n */\n static [WORKFLOW_DESERIALIZE](data: SerializedDrive): Drive {\n return new Drive({\n drive: data.drive,\n projectId: data.projectId,\n });\n }\n\n constructor({\n client,\n drive,\n projectId,\n }: {\n client?: APIClient;\n drive: DriveMetadata;\n projectId?: string;\n }) {\n this._client = client ?? null;\n this.drive = drive;\n this._projectId = projectId ?? drive.projectId;\n }\n\n /**\n * Allow to get a list of drives for a team narrowed to the given params.\n * It returns both the drives 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 Drive.list({ limit: 10 });\n * for await (const drive 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[\"listDrives\"]>[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 | number) => {\n const response = await client.listDrives({\n ...credentials,\n ...params,\n ...(cursor !== undefined && { cursor }),\n });\n return {\n ...response.json,\n drives: response.json.drives.map(\n (drive) =>\n new Drive({\n client,\n drive,\n projectId: credentials.projectId,\n }),\n ),\n };\n };\n const firstPage = await fetchPage(params?.cursor ?? params?.until);\n return attachPaginator(firstPage, {\n itemsKey: \"drives\",\n fetchNext: fetchPage,\n signal: params?.signal,\n });\n }\n\n /**\n * Retrieve an existing drive, or create a new one if it doesn't exists.\n *\n * @param params - Get/create parameters and optional credentials.\n * @returns A promise resolving to the {@link Drive}.\n */\n static async getOrCreate(\n params: (\n | GetOrCreateDriveParams\n | (GetOrCreateDriveParams & Credentials)\n ) &\n WithFetchOptions,\n ): Promise<Drive> {\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 response = await client.getOrCreateDrive({\n projectId: credentials.projectId,\n name: params.name,\n maxSizeBytes: params.maxSize,\n signal: params.signal,\n });\n\n return new Drive({\n client,\n drive: response.json.drive,\n projectId: credentials.projectId,\n });\n }\n\n /**\n * Delete this drive. The drive must not be attached to any sandbox.\n * This operation is irreversible and will delete all data stored in the drive.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves once the drive 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.deleteDrive({\n projectId: this._projectId,\n name: this.drive.name,\n signal: opts?.signal,\n });\n\n this.drive = response.json.drive;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAmCA,IAAa,QAAb,MAAa,MAAM;;;;;CASjB,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,OAAe;AACxB,SAAO,KAAK,MAAM;;;;;CAMpB,IAAW,YAAoB;AAC7B,SAAO,KAAK;;;;;CAMd,IAAW,UAAkB;AAC3B,SAAO,KAAK,MAAM;;;;;CAMpB,IAAW,mBAAuC;AAChD,SAAO,KAAK,MAAM;;;;;CAMpB,IAAW,qBAAyC;AAClD,SAAO,KAAK,MAAM;;;;;CAMpB,IAAW,YAAkB;AAC3B,SAAO,IAAI,KAAK,KAAK,MAAM,UAAU;;;;;CAMvC,IAAW,YAAkB;AAC3B,SAAO,IAAI,KAAK,KAAK,MAAM,UAAU;;;;;;;;CASvC,QAAQC,qCAAoB,UAAkC;AAC5D,SAAO;GACL,OAAO,SAAS;GAChB,WAAW,SAAS;GACrB;;;;;;;;;;;CAYH,QAAQC,uCAAsB,MAA8B;AAC1D,SAAO,IAAI,MAAM;GACf,OAAO,KAAK;GACZ,WAAW,KAAK;GACjB,CAAC;;CAGJ,YAAY,EACV,QACA,OACA,aAKC;OAzGK,UAA4B;AA0GlC,OAAK,UAAU,UAAU;AACzB,OAAK,QAAQ;AACb,OAAK,aAAa,aAAa,MAAM;;;;;;;;;;;;;;;;CAiBvC,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,WAA6B;GACpD,MAAM,WAAW,MAAM,OAAO,WAAW;IACvC,GAAG;IACH,GAAG;IACH,GAAI,WAAW,UAAa,EAAE,QAAQ;IACvC,CAAC;AACF,UAAO;IACL,GAAG,SAAS;IACZ,QAAQ,SAAS,KAAK,OAAO,KAC1B,UACC,IAAI,MAAM;KACR;KACA;KACA,WAAW,YAAY;KACxB,CAAC,CACL;IACF;;AAGH,SAAOG,kCADW,MAAM,UAAU,QAAQ,UAAU,QAAQ,MAAM,EAChC;GAChC,UAAU;GACV,WAAW;GACX,QAAQ,QAAQ;GACjB,CAAC;;;;;;;;CASJ,aAAa,YACX,QAKgB;AAChB;EACA,MAAM,cAAc,MAAMJ,uCAAe,OAAO;EAChD,MAAM,SAAS,IAAIC,6BAAU;GAC3B,QAAQ,YAAY;GACpB,OAAO,YAAY;GACnB,OAAO,OAAO;GACf,CAAC;AASF,SAAO,IAAI,MAAM;GACf;GACA,QATe,MAAM,OAAO,iBAAiB;IAC7C,WAAW,YAAY;IACvB,MAAM,OAAO;IACb,cAAc,OAAO;IACrB,QAAQ,OAAO;IAChB,CAAC,EAIgB,KAAK;GACrB,WAAW,YAAY;GACxB,CAAC;;;;;;;;;;CAWJ,MAAM,OAAO,MAAgD;AAC3D;AAQA,OAAK,SANY,OADF,MAAM,KAAK,cAAc,EACV,YAAY;GACxC,WAAW,KAAK;GAChB,MAAM,KAAK,MAAM;GACjB,QAAQ,MAAM;GACf,CAAC,EAEoB,KAAK"} |
-139
| import { Paginator } from "./utils/paginator.cjs"; | ||
| import { DriveMetadata } 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/drive.d.ts | ||
| interface SerializedDrive { | ||
| drive: DriveMetadata; | ||
| projectId?: string; | ||
| } | ||
| /** @inline */ | ||
| interface GetOrCreateDriveParams { | ||
| /** | ||
| * The name of the drive to get or create. Must be unique within the project. | ||
| */ | ||
| name: string; | ||
| /** | ||
| * Maximum drive size in bytes. If omitted, a default of 100 GiB is used. | ||
| */ | ||
| maxSize?: number; | ||
| /** | ||
| * An AbortSignal to cancel the operation. | ||
| */ | ||
| signal?: AbortSignal; | ||
| } | ||
| /** | ||
| * A Drive is a persistent, bottomless storage that can be attached and detached to Sandboxes. | ||
| * Drives can be mounted as read-write or read-only, at a configurable path with `Sandbox.create()`. | ||
| * | ||
| * Use {@link Drive.getOrCreate} to construct. | ||
| * @hideconstructor | ||
| */ | ||
| declare class Drive { | ||
| private _client; | ||
| private drive; | ||
| private readonly _projectId; | ||
| /** | ||
| * Lazily resolve credentials and construct an API client. | ||
| * @internal | ||
| */ | ||
| private ensureClient; | ||
| /** | ||
| * The name of the drive. | ||
| */ | ||
| get name(): string; | ||
| /** | ||
| * The project ID that owns the drive. | ||
| */ | ||
| get projectId(): string; | ||
| /** | ||
| * The maximum drive size in bytes. | ||
| */ | ||
| get maxSize(): number; | ||
| /** | ||
| * Current session ID the drive is attached to, if any. | ||
| */ | ||
| get currentSessionId(): string | undefined; | ||
| /** | ||
| * Current sandbox name the drive is attached to, if any. | ||
| */ | ||
| get currentSandboxName(): string | undefined; | ||
| /** | ||
| * Timestamp when the drive was created. | ||
| */ | ||
| get createdAt(): Date; | ||
| /** | ||
| * Timestamp when the drive was last updated. | ||
| */ | ||
| get updatedAt(): Date; | ||
| /** | ||
| * Serialize a Drive instance to plain data for @workflow/serde. | ||
| * | ||
| * @param instance - The Drive instance to serialize | ||
| * @returns A plain object containing drive metadata | ||
| */ | ||
| static [WORKFLOW_SERIALIZE](instance: Drive): SerializedDrive; | ||
| /** | ||
| * Deserialize a Drive 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 drive data | ||
| * @returns The reconstructed Drive instance | ||
| */ | ||
| static [WORKFLOW_DESERIALIZE](data: SerializedDrive): Drive; | ||
| constructor({ | ||
| client, | ||
| drive, | ||
| projectId | ||
| }: { | ||
| client?: APIClient; | ||
| drive: DriveMetadata; | ||
| projectId?: string; | ||
| }); | ||
| /** | ||
| * Allow to get a list of drives for a team narrowed to the given params. | ||
| * It returns both the drives and the pagination metadata to allow getting | ||
| * the next page of results. | ||
| * | ||
| * The returned object is async-iterable to auto-paginate through all pages: | ||
| * | ||
| * ```ts | ||
| * const result = await Drive.list({ limit: 10 }); | ||
| * for await (const drive of result) { ... } | ||
| * // or: await result.toArray(); | ||
| * // or: for await (const page of result.pages()) { ... } | ||
| * ``` | ||
| */ | ||
| static list(params?: Partial<Parameters<APIClient["listDrives"]>[0]> & Partial<Credentials> & WithFetchOptions): Promise<Paginator<{ | ||
| drives: Drive[]; | ||
| pagination: { | ||
| count: number; | ||
| next: string | null; | ||
| }; | ||
| }, "drives">>; | ||
| /** | ||
| * Retrieve an existing drive, or create a new one if it doesn't exists. | ||
| * | ||
| * @param params - Get/create parameters and optional credentials. | ||
| * @returns A promise resolving to the {@link Drive}. | ||
| */ | ||
| static getOrCreate(params: (GetOrCreateDriveParams | (GetOrCreateDriveParams & Credentials)) & WithFetchOptions): Promise<Drive>; | ||
| /** | ||
| * Delete this drive. The drive must not be attached to any sandbox. | ||
| * This operation is irreversible and will delete all data stored in the drive. | ||
| * | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * @returns A promise that resolves once the drive has been deleted. | ||
| */ | ||
| delete(opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<void>; | ||
| } | ||
| //#endregion | ||
| export { Drive, SerializedDrive }; | ||
| //# sourceMappingURL=drive.d.cts.map |
-140
| import { Paginator } from "./utils/paginator.js"; | ||
| import { DriveMetadata } from "./api-client/validators.js"; | ||
| import { APIClient, WithFetchOptions } from "./api-client/api-client.js"; | ||
| import "./api-client/index.js"; | ||
| import { Credentials } from "./utils/get-credentials.js"; | ||
| import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from "@workflow/serde"; | ||
| //#region src/drive.d.ts | ||
| interface SerializedDrive { | ||
| drive: DriveMetadata; | ||
| projectId?: string; | ||
| } | ||
| /** @inline */ | ||
| interface GetOrCreateDriveParams { | ||
| /** | ||
| * The name of the drive to get or create. Must be unique within the project. | ||
| */ | ||
| name: string; | ||
| /** | ||
| * Maximum drive size in bytes. If omitted, a default of 100 GiB is used. | ||
| */ | ||
| maxSize?: number; | ||
| /** | ||
| * An AbortSignal to cancel the operation. | ||
| */ | ||
| signal?: AbortSignal; | ||
| } | ||
| /** | ||
| * A Drive is a persistent, bottomless storage that can be attached and detached to Sandboxes. | ||
| * Drives can be mounted as read-write or read-only, at a configurable path with `Sandbox.create()`. | ||
| * | ||
| * Use {@link Drive.getOrCreate} to construct. | ||
| * @hideconstructor | ||
| */ | ||
| declare class Drive { | ||
| private _client; | ||
| private drive; | ||
| private readonly _projectId; | ||
| /** | ||
| * Lazily resolve credentials and construct an API client. | ||
| * @internal | ||
| */ | ||
| private ensureClient; | ||
| /** | ||
| * The name of the drive. | ||
| */ | ||
| get name(): string; | ||
| /** | ||
| * The project ID that owns the drive. | ||
| */ | ||
| get projectId(): string; | ||
| /** | ||
| * The maximum drive size in bytes. | ||
| */ | ||
| get maxSize(): number; | ||
| /** | ||
| * Current session ID the drive is attached to, if any. | ||
| */ | ||
| get currentSessionId(): string | undefined; | ||
| /** | ||
| * Current sandbox name the drive is attached to, if any. | ||
| */ | ||
| get currentSandboxName(): string | undefined; | ||
| /** | ||
| * Timestamp when the drive was created. | ||
| */ | ||
| get createdAt(): Date; | ||
| /** | ||
| * Timestamp when the drive was last updated. | ||
| */ | ||
| get updatedAt(): Date; | ||
| /** | ||
| * Serialize a Drive instance to plain data for @workflow/serde. | ||
| * | ||
| * @param instance - The Drive instance to serialize | ||
| * @returns A plain object containing drive metadata | ||
| */ | ||
| static [WORKFLOW_SERIALIZE](instance: Drive): SerializedDrive; | ||
| /** | ||
| * Deserialize a Drive 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 drive data | ||
| * @returns The reconstructed Drive instance | ||
| */ | ||
| static [WORKFLOW_DESERIALIZE](data: SerializedDrive): Drive; | ||
| constructor({ | ||
| client, | ||
| drive, | ||
| projectId | ||
| }: { | ||
| client?: APIClient; | ||
| drive: DriveMetadata; | ||
| projectId?: string; | ||
| }); | ||
| /** | ||
| * Allow to get a list of drives for a team narrowed to the given params. | ||
| * It returns both the drives and the pagination metadata to allow getting | ||
| * the next page of results. | ||
| * | ||
| * The returned object is async-iterable to auto-paginate through all pages: | ||
| * | ||
| * ```ts | ||
| * const result = await Drive.list({ limit: 10 }); | ||
| * for await (const drive of result) { ... } | ||
| * // or: await result.toArray(); | ||
| * // or: for await (const page of result.pages()) { ... } | ||
| * ``` | ||
| */ | ||
| static list(params?: Partial<Parameters<APIClient["listDrives"]>[0]> & Partial<Credentials> & WithFetchOptions): Promise<Paginator<{ | ||
| drives: Drive[]; | ||
| pagination: { | ||
| count: number; | ||
| next: string | null; | ||
| }; | ||
| }, "drives">>; | ||
| /** | ||
| * Retrieve an existing drive, or create a new one if it doesn't exists. | ||
| * | ||
| * @param params - Get/create parameters and optional credentials. | ||
| * @returns A promise resolving to the {@link Drive}. | ||
| */ | ||
| static getOrCreate(params: (GetOrCreateDriveParams | (GetOrCreateDriveParams & Credentials)) & WithFetchOptions): Promise<Drive>; | ||
| /** | ||
| * Delete this drive. The drive must not be attached to any sandbox. | ||
| * This operation is irreversible and will delete all data stored in the drive. | ||
| * | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * @returns A promise that resolves once the drive has been deleted. | ||
| */ | ||
| delete(opts?: { | ||
| signal?: AbortSignal; | ||
| }): Promise<void>; | ||
| } | ||
| //#endregion | ||
| export { Drive, SerializedDrive }; | ||
| //# sourceMappingURL=drive.d.ts.map |
-193
| 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"; | ||
| //#region src/drive.ts | ||
| /** | ||
| * A Drive is a persistent, bottomless storage that can be attached and detached to Sandboxes. | ||
| * Drives can be mounted as read-write or read-only, at a configurable path with `Sandbox.create()`. | ||
| * | ||
| * Use {@link Drive.getOrCreate} to construct. | ||
| * @hideconstructor | ||
| */ | ||
| var Drive = class Drive { | ||
| /** | ||
| * Lazily resolve credentials and construct an API client. | ||
| * @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; | ||
| } | ||
| /** | ||
| * The name of the drive. | ||
| */ | ||
| get name() { | ||
| return this.drive.name; | ||
| } | ||
| /** | ||
| * The project ID that owns the drive. | ||
| */ | ||
| get projectId() { | ||
| return this._projectId; | ||
| } | ||
| /** | ||
| * The maximum drive size in bytes. | ||
| */ | ||
| get maxSize() { | ||
| return this.drive.maxSizeBytes; | ||
| } | ||
| /** | ||
| * Current session ID the drive is attached to, if any. | ||
| */ | ||
| get currentSessionId() { | ||
| return this.drive.currentSessionId; | ||
| } | ||
| /** | ||
| * Current sandbox name the drive is attached to, if any. | ||
| */ | ||
| get currentSandboxName() { | ||
| return this.drive.currentSandboxName; | ||
| } | ||
| /** | ||
| * Timestamp when the drive was created. | ||
| */ | ||
| get createdAt() { | ||
| return new Date(this.drive.createdAt); | ||
| } | ||
| /** | ||
| * Timestamp when the drive was last updated. | ||
| */ | ||
| get updatedAt() { | ||
| return new Date(this.drive.updatedAt); | ||
| } | ||
| /** | ||
| * Serialize a Drive instance to plain data for @workflow/serde. | ||
| * | ||
| * @param instance - The Drive instance to serialize | ||
| * @returns A plain object containing drive metadata | ||
| */ | ||
| static [WORKFLOW_SERIALIZE](instance) { | ||
| return { | ||
| drive: instance.drive, | ||
| projectId: instance._projectId | ||
| }; | ||
| } | ||
| /** | ||
| * Deserialize a Drive 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 drive data | ||
| * @returns The reconstructed Drive instance | ||
| */ | ||
| static [WORKFLOW_DESERIALIZE](data) { | ||
| return new Drive({ | ||
| drive: data.drive, | ||
| projectId: data.projectId | ||
| }); | ||
| } | ||
| constructor({ client, drive, projectId }) { | ||
| this._client = null; | ||
| this._client = client ?? null; | ||
| this.drive = drive; | ||
| this._projectId = projectId ?? drive.projectId; | ||
| } | ||
| /** | ||
| * Allow to get a list of drives for a team narrowed to the given params. | ||
| * It returns both the drives and the pagination metadata to allow getting | ||
| * the next page of results. | ||
| * | ||
| * The returned object is async-iterable to auto-paginate through all pages: | ||
| * | ||
| * ```ts | ||
| * const result = await Drive.list({ limit: 10 }); | ||
| * for await (const drive of result) { ... } | ||
| * // or: await result.toArray(); | ||
| * // or: for await (const page of result.pages()) { ... } | ||
| * ``` | ||
| */ | ||
| static async list(params) { | ||
| "use step"; | ||
| const credentials = await getCredentials(params); | ||
| const client = new APIClient({ | ||
| teamId: credentials.teamId, | ||
| token: credentials.token, | ||
| fetch: params?.fetch | ||
| }); | ||
| const fetchPage = async (cursor) => { | ||
| const response = await client.listDrives({ | ||
| ...credentials, | ||
| ...params, | ||
| ...cursor !== void 0 && { cursor } | ||
| }); | ||
| return { | ||
| ...response.json, | ||
| drives: response.json.drives.map((drive) => new Drive({ | ||
| client, | ||
| drive, | ||
| projectId: credentials.projectId | ||
| })) | ||
| }; | ||
| }; | ||
| return attachPaginator(await fetchPage(params?.cursor ?? params?.until), { | ||
| itemsKey: "drives", | ||
| fetchNext: fetchPage, | ||
| signal: params?.signal | ||
| }); | ||
| } | ||
| /** | ||
| * Retrieve an existing drive, or create a new one if it doesn't exists. | ||
| * | ||
| * @param params - Get/create parameters and optional credentials. | ||
| * @returns A promise resolving to the {@link Drive}. | ||
| */ | ||
| static async getOrCreate(params) { | ||
| "use step"; | ||
| const credentials = await getCredentials(params); | ||
| const client = new APIClient({ | ||
| teamId: credentials.teamId, | ||
| token: credentials.token, | ||
| fetch: params.fetch | ||
| }); | ||
| return new Drive({ | ||
| client, | ||
| drive: (await client.getOrCreateDrive({ | ||
| projectId: credentials.projectId, | ||
| name: params.name, | ||
| maxSizeBytes: params.maxSize, | ||
| signal: params.signal | ||
| })).json.drive, | ||
| projectId: credentials.projectId | ||
| }); | ||
| } | ||
| /** | ||
| * Delete this drive. The drive must not be attached to any sandbox. | ||
| * This operation is irreversible and will delete all data stored in the drive. | ||
| * | ||
| * @param opts - Optional parameters. | ||
| * @param opts.signal - An AbortSignal to cancel the operation. | ||
| * @returns A promise that resolves once the drive has been deleted. | ||
| */ | ||
| async delete(opts) { | ||
| "use step"; | ||
| this.drive = (await (await this.ensureClient()).deleteDrive({ | ||
| projectId: this._projectId, | ||
| name: this.drive.name, | ||
| signal: opts?.signal | ||
| })).json.drive; | ||
| } | ||
| }; | ||
| //#endregion | ||
| export { Drive }; | ||
| //# sourceMappingURL=drive.js.map |
| {"version":3,"file":"drive.js","names":[],"sources":["../src/drive.ts"],"sourcesContent":["import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from \"@workflow/serde\";\nimport type { WithFetchOptions } from \"./api-client/api-client.js\";\nimport type { DriveMetadata } 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 SerializedDrive {\n drive: DriveMetadata;\n projectId?: string;\n}\n\n/** @inline */\ninterface GetOrCreateDriveParams {\n /**\n * The name of the drive to get or create. Must be unique within the project.\n */\n name: string;\n /**\n * Maximum drive size in bytes. If omitted, a default of 100 GiB is used.\n */\n maxSize?: number;\n /**\n * An AbortSignal to cancel the operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * A Drive is a persistent, bottomless storage that can be attached and detached to Sandboxes.\n * Drives can be mounted as read-write or read-only, at a configurable path with `Sandbox.create()`.\n *\n * Use {@link Drive.getOrCreate} to construct.\n * @hideconstructor\n */\nexport class Drive {\n private _client: APIClient | null = null;\n private drive: DriveMetadata;\n private readonly _projectId: string;\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 the drive.\n */\n public get name(): string {\n return this.drive.name;\n }\n\n /**\n * The project ID that owns the drive.\n */\n public get projectId(): string {\n return this._projectId;\n }\n\n /**\n * The maximum drive size in bytes.\n */\n public get maxSize(): number {\n return this.drive.maxSizeBytes;\n }\n\n /**\n * Current session ID the drive is attached to, if any.\n */\n public get currentSessionId(): string | undefined {\n return this.drive.currentSessionId;\n }\n\n /**\n * Current sandbox name the drive is attached to, if any.\n */\n public get currentSandboxName(): string | undefined {\n return this.drive.currentSandboxName;\n }\n\n /**\n * Timestamp when the drive was created.\n */\n public get createdAt(): Date {\n return new Date(this.drive.createdAt);\n }\n\n /**\n * Timestamp when the drive was last updated.\n */\n public get updatedAt(): Date {\n return new Date(this.drive.updatedAt);\n }\n\n /**\n * Serialize a Drive instance to plain data for @workflow/serde.\n *\n * @param instance - The Drive instance to serialize\n * @returns A plain object containing drive metadata\n */\n static [WORKFLOW_SERIALIZE](instance: Drive): SerializedDrive {\n return {\n drive: instance.drive,\n projectId: instance._projectId,\n };\n }\n\n /**\n * Deserialize a Drive 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 drive data\n * @returns The reconstructed Drive instance\n */\n static [WORKFLOW_DESERIALIZE](data: SerializedDrive): Drive {\n return new Drive({\n drive: data.drive,\n projectId: data.projectId,\n });\n }\n\n constructor({\n client,\n drive,\n projectId,\n }: {\n client?: APIClient;\n drive: DriveMetadata;\n projectId?: string;\n }) {\n this._client = client ?? null;\n this.drive = drive;\n this._projectId = projectId ?? drive.projectId;\n }\n\n /**\n * Allow to get a list of drives for a team narrowed to the given params.\n * It returns both the drives 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 Drive.list({ limit: 10 });\n * for await (const drive 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[\"listDrives\"]>[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 | number) => {\n const response = await client.listDrives({\n ...credentials,\n ...params,\n ...(cursor !== undefined && { cursor }),\n });\n return {\n ...response.json,\n drives: response.json.drives.map(\n (drive) =>\n new Drive({\n client,\n drive,\n projectId: credentials.projectId,\n }),\n ),\n };\n };\n const firstPage = await fetchPage(params?.cursor ?? params?.until);\n return attachPaginator(firstPage, {\n itemsKey: \"drives\",\n fetchNext: fetchPage,\n signal: params?.signal,\n });\n }\n\n /**\n * Retrieve an existing drive, or create a new one if it doesn't exists.\n *\n * @param params - Get/create parameters and optional credentials.\n * @returns A promise resolving to the {@link Drive}.\n */\n static async getOrCreate(\n params: (\n | GetOrCreateDriveParams\n | (GetOrCreateDriveParams & Credentials)\n ) &\n WithFetchOptions,\n ): Promise<Drive> {\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 response = await client.getOrCreateDrive({\n projectId: credentials.projectId,\n name: params.name,\n maxSizeBytes: params.maxSize,\n signal: params.signal,\n });\n\n return new Drive({\n client,\n drive: response.json.drive,\n projectId: credentials.projectId,\n });\n }\n\n /**\n * Delete this drive. The drive must not be attached to any sandbox.\n * This operation is irreversible and will delete all data stored in the drive.\n *\n * @param opts - Optional parameters.\n * @param opts.signal - An AbortSignal to cancel the operation.\n * @returns A promise that resolves once the drive 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.deleteDrive({\n projectId: this._projectId,\n name: this.drive.name,\n signal: opts?.signal,\n });\n\n this.drive = response.json.drive;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAmCA,IAAa,QAAb,MAAa,MAAM;;;;;CASjB,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,MAAM;;;;;CAMpB,IAAW,YAAoB;AAC7B,SAAO,KAAK;;;;;CAMd,IAAW,UAAkB;AAC3B,SAAO,KAAK,MAAM;;;;;CAMpB,IAAW,mBAAuC;AAChD,SAAO,KAAK,MAAM;;;;;CAMpB,IAAW,qBAAyC;AAClD,SAAO,KAAK,MAAM;;;;;CAMpB,IAAW,YAAkB;AAC3B,SAAO,IAAI,KAAK,KAAK,MAAM,UAAU;;;;;CAMvC,IAAW,YAAkB;AAC3B,SAAO,IAAI,KAAK,KAAK,MAAM,UAAU;;;;;;;;CASvC,QAAQ,oBAAoB,UAAkC;AAC5D,SAAO;GACL,OAAO,SAAS;GAChB,WAAW,SAAS;GACrB;;;;;;;;;;;CAYH,QAAQ,sBAAsB,MAA8B;AAC1D,SAAO,IAAI,MAAM;GACf,OAAO,KAAK;GACZ,WAAW,KAAK;GACjB,CAAC;;CAGJ,YAAY,EACV,QACA,OACA,aAKC;OAzGK,UAA4B;AA0GlC,OAAK,UAAU,UAAU;AACzB,OAAK,QAAQ;AACb,OAAK,aAAa,aAAa,MAAM;;;;;;;;;;;;;;;;CAiBvC,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,WAA6B;GACpD,MAAM,WAAW,MAAM,OAAO,WAAW;IACvC,GAAG;IACH,GAAG;IACH,GAAI,WAAW,UAAa,EAAE,QAAQ;IACvC,CAAC;AACF,UAAO;IACL,GAAG,SAAS;IACZ,QAAQ,SAAS,KAAK,OAAO,KAC1B,UACC,IAAI,MAAM;KACR;KACA;KACA,WAAW,YAAY;KACxB,CAAC,CACL;IACF;;AAGH,SAAO,gBADW,MAAM,UAAU,QAAQ,UAAU,QAAQ,MAAM,EAChC;GAChC,UAAU;GACV,WAAW;GACX,QAAQ,QAAQ;GACjB,CAAC;;;;;;;;CASJ,aAAa,YACX,QAKgB;AAChB;EACA,MAAM,cAAc,MAAM,eAAe,OAAO;EAChD,MAAM,SAAS,IAAI,UAAU;GAC3B,QAAQ,YAAY;GACpB,OAAO,YAAY;GACnB,OAAO,OAAO;GACf,CAAC;AASF,SAAO,IAAI,MAAM;GACf;GACA,QATe,MAAM,OAAO,iBAAiB;IAC7C,WAAW,YAAY;IACvB,MAAM,OAAO;IACb,cAAc,OAAO;IACrB,QAAQ,OAAO;IAChB,CAAC,EAIgB,KAAK;GACrB,WAAW,YAAY;GACxB,CAAC;;;;;;;;;;CAWJ,MAAM,OAAO,MAAgD;AAC3D;AAQA,OAAK,SANY,OADF,MAAM,KAAK,cAAc,EACV,YAAY;GACxC,WAAW,KAAK;GAChB,MAAM,KAAK,MAAM;GACjB,QAAQ,MAAM;GACf,CAAC,EAEoB,KAAK"} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
1417151
6.16%207
2.99%16355
4.79%0
-100%530
83.39%68
-10.53%30
20%