{"version":3,"file":"sandbox.cjs","names":["APIError","fromAPINetworkPolicy","NotSupportedError","WebixApiClient","Session","FileSystem","sleepWithSignal"],"sources":["../src/sandbox.ts"],"sourcesContent":["import type {\n  SessionMetaData,\n  SandboxRouteData,\n  SandboxMetaData,\n  SnapshotMetadata,\n} from \"./api-client/index.js\";\nimport { WebixApiClient, NotSupportedError } from \"./api-client/index.js\";\nimport type { WebixHostOptions } from \"./webix/host.js\";\nimport { APIError } from \"./api-client/api-error.js\";\nimport type { RUNTIMES } from \"./constants.js\";\nimport { Session, type RunCommandParams } from \"./session.js\";\nimport type { Command, CommandFinished } from \"./command.js\";\nimport type { Snapshot } from \"./snapshot.js\";\nimport type { SandboxSnapshot } from \"./utils/sandbox-snapshot.js\";\nimport type {\n  NetworkPolicy,\n  NetworkPolicyKeyValueMatcher,\n  NetworkPolicyMatch,\n  NetworkPolicyMatcher,\n} from \"./network-policy.js\";\nimport { fromAPINetworkPolicy } from \"./utils/network-policy.js\";\nimport { attachPaginator } from \"./utils/paginator.js\";\nimport { sleepWithSignal } from \"./utils/browser-primitives.js\";\nimport { FileSystem } from \"./filesystem.js\";\n\nexport type {\n  NetworkPolicy,\n  NetworkPolicyKeyValueMatcher,\n  NetworkPolicyMatch,\n  NetworkPolicyMatcher,\n};\n\n/** @inline */\nexport interface BaseCreateSandboxParams extends WebixHostOptions {\n  /**\n   * The name of the sandbox. If omitted, a random name will be generated.\n   */\n  name?: string;\n  /**\n   * The source of the sandbox.\n   *\n   * Omit this parameter start a sandbox without a source.\n   *\n   * For git sources:\n   * - `depth`: Creates shallow clones with limited commit history (minimum: 1)\n   * - `revision`: Clones and checks out a specific commit, branch, or tag\n   */\n  source?:\n    | {\n        type: \"git\";\n        url: string;\n        depth?: number;\n        revision?: string;\n      }\n    | {\n        type: \"git\";\n        url: string;\n        username: string;\n        password: string;\n        depth?: number;\n        revision?: string;\n      }\n    | { type: \"tarball\"; url: string };\n  /**\n   * Array of port numbers to expose from the sandbox. Sandboxes can\n   * expose up to 4 ports.\n   */\n  ports?: number[];\n  /**\n   * Timeout in milliseconds before the sandbox auto-terminates.\n   */\n  timeout?: number;\n  /**\n   * Resources to allocate to the sandbox.\n   *\n   * Your sandbox will get the amount of vCPUs you specify here and\n   * 2048 MB of memory per vCPU.\n   */\n  resources?: { vcpus: number };\n  /**\n   * The runtime of the sandbox, currently only `node24`, `node22`, `node26` and `python3.13` are supported.\n   * If not specified, the default runtime `node24` will be used.\n   */\n  runtime?: RUNTIMES | (string & {});\n  /**\n   * Network policy to define network restrictions for the sandbox.\n   * Defaults to full internet access if not specified.\n   */\n  networkPolicy?: NetworkPolicy;\n  /**\n   * Default environment variables for the sandbox.\n   * These are inherited by all commands unless overridden with\n   * the `env` option in `runCommand`.\n   *\n   * @example\n   * const sandbox = await Sandbox.create({\n   *   env: { NODE_ENV: \"production\", API_KEY: \"secret\" },\n   * });\n   * // All commands will have NODE_ENV and API_KEY set\n   * await sandbox.runCommand(\"node\", [\"app.js\"]);\n   */\n  env?: Record<string, string>;\n  /**\n   * Key-value tags to associate with the sandbox. Maximum 5 tags.\n   * @example { env: \"staging\", team: \"infra\" }\n   */\n  tags?: Record<string, string>;\n\n  /**\n   * An AbortSignal to cancel sandbox creation.\n   */\n  signal?: AbortSignal;\n  /**\n   * Enable or disable automatic restore of the filesystem between sessions.\n   */\n  persistent?: boolean;\n  /**\n   * Default snapshot expiration in milliseconds.\n   * When set, snapshots created for this sandbox will expire after this duration.\n   * Use `0` for no expiration.\n   */\n  snapshotExpiration?: number;\n  /**\n   * Retention policy that keeps only the N most recent snapshots of this\n   * sandbox. Older snapshots are evicted when a new one is created.\n   */\n  keepLastSnapshots?: {\n    /**\n     * Number of snapshots to keep (1-10).\n     */\n    count: number;\n    /**\n     * Expiration in milliseconds applied to kept snapshots.\n     * Use `0` for no expiration. Falls back to `snapshotExpiration` when omitted.\n     */\n    expiration?: number;\n    /**\n     * When `true` (the default), evicted snapshots are deleted immediately;\n     * when `false`, they keep the default expiration.\n     */\n    deleteEvicted?: boolean;\n  };\n  /**\n   * Called when the sandbox session is resumed (e.g., after a snapshot restore).\n   * Use this to re-warm caches, restore transient state, or run other setup logic.\n   */\n  onResume?: (sandbox: Sandbox) => Promise<void>;\n}\n\nexport type CreateSandboxParams =\n  | BaseCreateSandboxParams\n  | (Omit<BaseCreateSandboxParams, \"runtime\" | \"source\"> & {\n      source: { type: \"snapshot\"; snapshotId: string };\n    });\n\n/**\n * Parameters for {@link Sandbox.fork}.\n *\n * The fork inherits the source sandbox's current filesystem snapshot and copies\n * as many config fields from the source as the server exposes. Any field set\n * here acts as an override of the copied value.\n *\n * `env` is not copied (encrypted server-side); pass it explicitly to set\n * environment variables on the fork. `runtime` is not exposed: when the\n * source has a snapshot, it is inherited; otherwise it is copied from the\n * source sandbox.\n * @inline\n */\nexport type ForkSandboxParams = Omit<\n  BaseCreateSandboxParams,\n  \"source\" | \"runtime\"\n> & {\n  /**\n   * Name of the source sandbox to fork from.\n   */\n  sourceSandbox: string;\n};\n\n/** @inline */\ninterface GetSandboxParams {\n  /**\n   * The name of the sandbox.\n   */\n  name: string;\n  /**\n   * Whether to resume an existing session. Defaults to true.\n   */\n  resume?: boolean;\n  /**\n   * An AbortSignal to cancel the operation.\n   */\n  signal?: AbortSignal;\n  /**\n   * Called when the sandbox session is resumed (e.g., after a snapshot restore).\n   * Use this to re-warm caches, restore transient state, or run other setup logic.\n   */\n  onResume?: (sandbox: Sandbox) => Promise<void>;\n}\n\n/**\n * Extends both {@link BaseCreateSandboxParams} and {@link GetSandboxParams}\n * (minus `name`, which is required on get but optional here) so that any\n * new parameter added to either flow is picked up automatically. The\n * structural overlap on `signal` / `onResume` is intentional — both\n * interfaces declare them with identical optional types.\n * @inline\n */\ninterface GetOrCreateSandboxParams\n  extends BaseCreateSandboxParams,\n    Omit<GetSandboxParams, \"name\"> {\n  /**\n   * Called once after a sandbox is freshly created (not when an existing\n   * sandbox is retrieved). Use this for one-time setup such as seeding\n   * files or warming caches. The returned promise is awaited before\n   * {@link Sandbox.getOrCreate} resolves.\n   */\n  onCreate?: (sandbox: Sandbox) => Promise<void>;\n}\n\nfunction isSandboxStoppedError(err: unknown): boolean {\n  return err instanceof APIError && err.response.status === 410;\n}\n\nfunction isNotFoundError(err: unknown): boolean {\n  return err instanceof APIError && err.response.status === 404;\n}\n\nfunction isSnapshotNotFoundError(err: unknown): boolean {\n  return (\n    err instanceof APIError &&\n    err.response.status === 410 &&\n    (err.json as any)?.error?.code === \"snapshot_not_found\"\n  );\n}\n\nfunction isSandboxStoppingError(err: unknown): boolean {\n  return (\n    err instanceof APIError &&\n    err.response.status === 422 &&\n    (err.json as any)?.error?.code === \"sandbox_stopping\"\n  );\n}\n\nfunction isSandboxSnapshottingError(err: unknown): boolean {\n  return (\n    err instanceof APIError &&\n    err.response.status === 422 &&\n    (err.json as any)?.error?.code === \"sandbox_snapshotting\"\n  );\n}\n\n/**\n * Serialized representation of a Sandbox for @workflow/serde.\n * Fields `metadata` and `routes` are the original wire format from main.\n * Fields `sandboxMetadata` and `projectId` are added for named-sandboxes.\n */\nexport interface SerializedSandbox {\n  metadata: SandboxSnapshot;\n  routes: SandboxRouteData[];\n  sandboxMetadata?: SandboxMetaData;\n  projectId?: string;\n}\n\n// ============================================================================\n// Sandbox class\n// ============================================================================\n\n/**\n * A Sandbox is a persistent, isolated Linux MicroVMs to run commands in.\n * Use {@link Sandbox.create} or {@link Sandbox.get} to construct.\n * @hideconstructor\n */\nexport class Sandbox {\n  private _client: WebixApiClient | null = null;\n\n  /**\n   * In-flight resume promise, used to deduplicate concurrent resume calls.\n   */\n  private resumePromise: Promise<void> | null = null;\n\n  /**\n   * Internal Session instance for the current VM.\n   */\n  private session: Session | undefined;\n\n  /**\n   * Internal metadata about the sandbox.\n   */\n  private sandbox: SandboxMetaData;\n\n  /**\n   * Hook that will be executed when a new session is created during resume.\n   */\n  private readonly onResume?: (sandbox: Sandbox) => Promise<void>;\n\n  /**\n   * A `node:fs/promises`-compatible API for interacting with the sandbox filesystem.\n   *\n   * @example\n   * const content = await sandbox.fs.readFile('/etc/hostname', 'utf8');\n   * await sandbox.fs.writeFile('/tmp/hello.txt', 'Hello, world!');\n   * const files = await sandbox.fs.readdir('/tmp');\n   * const stats = await sandbox.fs.stat('/tmp/hello.txt');\n   */\n  public readonly fs: FileSystem;\n\n  /**\n   * Return the in-browser sandbox client bound to this sandbox's live host.\n   * @internal\n   */\n  private async ensureClient(): Promise<WebixApiClient> {\n    if (this._client) return this._client;\n    throw new Error(\n      \"Sandbox is not attached to a live in-browser host (was it structurally cloned?).\",\n    );\n  }\n\n  /**\n   * The name of this sandbox.\n   */\n  public get name(): string {\n    return this.sandbox.name;\n  }\n\n  /**\n   * Routes from ports to subdomains.\n   * @hidden\n   */\n  public get routes(): SandboxRouteData[] {\n    return this.currentSession().routes;\n  }\n\n  /**\n   * Whether the sandbox persists the state.\n   */\n  public get persistent(): boolean {\n    return this.sandbox.persistent;\n  }\n\n  /**\n   * The region this sandbox runs in.\n   */\n  public get region(): string | undefined {\n    return this.sandbox.region;\n  }\n\n  /**\n   * Number of virtual CPUs allocated.\n   */\n  public get vcpus(): number | undefined {\n    return this.sandbox.vcpus;\n  }\n\n  /**\n   * Memory allocated in MB.\n   */\n  public get memory(): number | undefined {\n    return this.sandbox.memory;\n  }\n\n  /** Runtime identifier (e.g. \"node24\", \"python3.13\"). */\n  public get runtime(): string | undefined {\n    return this.sandbox.runtime;\n  }\n\n  /**\n   * Cumulative egress bytes across all sessions.\n   */\n  public get totalEgressBytes(): number | undefined {\n    return this.sandbox.totalEgressBytes;\n  }\n\n  /**\n   * Cumulative ingress bytes across all sessions.\n   */\n  public get totalIngressBytes(): number | undefined {\n    return this.sandbox.totalIngressBytes;\n  }\n\n  /**\n   * Cumulative active CPU duration in milliseconds across all sessions.\n   */\n  public get totalActiveCpuDurationMs(): number | undefined {\n    return this.sandbox.totalActiveCpuDurationMs;\n  }\n\n  /**\n   * Cumulative wall-clock duration in milliseconds across all sessions.\n   */\n  public get totalDurationMs(): number | undefined {\n    return this.sandbox.totalDurationMs;\n  }\n\n  /**\n   * When this sandbox was last updated.\n   */\n  public get updatedAt(): Date {\n    return new Date(this.sandbox.updatedAt);\n  }\n\n  /**\n   * When the sandbox status was last updated.\n   */\n  public get statusUpdatedAt(): Date | undefined {\n    return this.sandbox.statusUpdatedAt\n      ? new Date(this.sandbox.statusUpdatedAt)\n      : undefined;\n  }\n\n  /**\n   * When this sandbox was created.\n   */\n  public get createdAt(): Date {\n    return new Date(this.sandbox.createdAt);\n  }\n\n  /**\n   * Interactive port.\n   */\n  public get interactivePort(): number | undefined {\n    return this.currentSession().interactivePort;\n  }\n\n  /**\n   * The status of the current session.\n   */\n  public get status(): SessionMetaData[\"status\"] {\n    return this.currentSession().status;\n  }\n\n  /**\n   * The default timeout of this sandbox in milliseconds.\n   */\n  public get timeout(): number | undefined {\n    return this.sandbox.timeout;\n  }\n\n  /**\n   * Key-value tags attached to the sandbox.\n   */\n  public get tags(): Record<string, string> | undefined {\n    return this.sandbox.tags;\n  }\n\n  /**\n   * The default network policy of this sandbox.\n   */\n  public get networkPolicy(): NetworkPolicy | undefined {\n    return this.sandbox.networkPolicy\n      ? fromAPINetworkPolicy(this.sandbox.networkPolicy)\n      : undefined;\n  }\n\n  /**\n   * If the session was created from a snapshot, the ID of that snapshot.\n   */\n  public get sourceSnapshotId(): string | undefined {\n    return this.currentSession().sourceSnapshotId;\n  }\n\n  /**\n   * The current snapshot ID of this sandbox, if any.\n   */\n  public get currentSnapshotId(): string | undefined {\n    return this.sandbox.currentSnapshotId;\n  }\n\n  /**\n   * The default snapshot expiration in milliseconds, if set.\n   */\n  public get snapshotExpiration(): number | undefined {\n    return this.sandbox.snapshotExpiration;\n  }\n\n  /**\n   * The snapshot retention policy (`keep-last-snapshots`) currently configured\n   * on this sandbox, if any.\n   */\n  public get keepLastSnapshots():\n    | { count: number; expiration?: number; deleteEvicted?: boolean }\n    | undefined {\n    return this.sandbox.keepLastSnapshots;\n  }\n\n  /**\n   * The amount of CPU used by the session. Only reported once the VM is stopped.\n   */\n  public get activeCpuUsageMs(): number | undefined {\n    return this.currentSession().activeCpuUsageMs;\n  }\n\n  /**\n   * The amount of network data used by the session. Only reported once the VM is stopped.\n   */\n  public get networkTransfer():\n    | { ingress: number; egress: number }\n    | undefined {\n    return this.currentSession().networkTransfer;\n  }\n\n  /**\n   * Allow to get a list of sandboxes for a team narrowed to the given params.\n   * It returns both the sandboxes and the pagination metadata to allow getting\n   * the next page of results.\n   *\n   * The returned object is async-iterable to auto-paginate through all pages:\n   *\n   * ```ts\n   * const result = await Sandbox.list({ namePrefix: \"ci-\" });\n   * for await (const sandbox of result) { ... }\n   * // or: await result.toArray();\n   * // or: for await (const page of result.pages()) { ... }\n   * ```\n   */\n  static async list(): Promise<never> {\n    throw new NotSupportedError(\n      \"Sandbox.list is unavailable in the self-contained backend: there is no remote account registry. Each in-browser sandbox exists only in the page that created it.\",\n    );\n  }\n\n  /**\n   * Create a new sandbox.\n   *\n   * @param params - Creation parameters and optional credentials.\n   * @returns A promise resolving to the created {@link Sandbox}.\n   * @example\n   * <caption>Create a sandbox with default options</caption>\n   * const sandbox = await Sandbox.create();\n   *\n   * @example\n   * <caption>Create a sandbox and drop it in the end of the block</caption>\n   * async function fn() {\n   *   await using const sandbox = await Sandbox.create();\n   *   // Sandbox automatically stopped at the end of the lexical scope\n   * }\n   */\n  static async create(\n    params?: CreateSandboxParams,\n  ): Promise<Sandbox & AsyncDisposable> {\n    if (params?.source) {\n      throw new NotSupportedError(\n        \"Sandbox sources (git/tarball/snapshot URLs) are not supported by the in-browser backend; seed files with writeFiles() instead.\",\n      );\n    }\n    const runtime =\n      params && \"runtime\" in params ? (params.runtime as string) : undefined;\n    const client = new WebixApiClient({\n      wasmUrl: params?.wasmUrl,\n      glueUrl: params?.glueUrl,\n      rootfsUrl: params?.rootfsUrl,\n      rootfsTarBytes: params?.rootfsTarBytes,\n      wasmPath: params?.wasmPath,\n      gluePath: params?.gluePath,\n      name: params?.name,\n      runtime,\n    });\n\n    const response = await client.createSandbox({\n      name: params?.name,\n      runtime,\n    });\n\n    return new DisposableSandbox({\n      client,\n      session: response.json.session,\n      sandbox: response.json.sandbox,\n      routes: response.json.routes,\n      onResume: params?.onResume,\n    });\n  }\n\n  /**\n   * Forking, named retrieval, and get-or-create are unavailable in the\n   * self-contained backend: there is no remote registry of sandboxes. Each\n   * in-browser sandbox exists only within the page that called create().\n   */\n  static async fork(): Promise<never> {\n    throw new NotSupportedError(\"Sandbox.fork is unavailable in the self-contained in-browser backend.\");\n  }\n\n  static async get(): Promise<never> {\n    throw new NotSupportedError(\"Sandbox.get is unavailable in the self-contained in-browser backend; keep the Sandbox returned by create().\");\n  }\n\n  static async getOrCreate(): Promise<never> {\n    throw new NotSupportedError(\"Sandbox.getOrCreate is unavailable in the self-contained in-browser backend; use create().\");\n  }\n\n  /**\n   * Create a new Sandbox instance.\n   *\n   * @param params.client - Optional API client. If not provided, will be lazily created using global credentials.\n   * @param params.routes - Port-to-subdomain mappings for exposed ports\n   * @param params.sandbox - Sandbox snapshot metadata\n   */\n  constructor({\n    client,\n    routes,\n    session,\n    sandbox,\n    onResume,\n  }: {\n    client?: WebixApiClient;\n    routes: SandboxRouteData[];\n    session?: SessionMetaData;\n    sandbox: SandboxMetaData;\n    onResume?: (sandbox: Sandbox) => Promise<void>;\n  }) {\n    this._client = client ?? null;\n    if (session) {\n      this.session = new Session({ client: client!, routes, session });\n    }\n    this.sandbox = sandbox;\n    this.onResume = onResume;\n    this.fs = new FileSystem(this);\n  }\n\n  /**\n   * Get the current session (the running VM) for this sandbox.\n   *\n   * @returns The {@link Session} instance.\n   */\n  currentSession(): Session {\n    if (!this.session) {\n      throw new Error(\"No active session. Run a command or call resume first.\");\n    }\n    return this.session;\n  }\n\n  /**\n   * Resume this sandbox by creating a new session via `getSandbox`.\n   */\n  private async resume(signal?: AbortSignal): Promise<void> {\n    if (!this.resumePromise) {\n      this.resumePromise = this.doResume(signal).finally(() => {\n        this.resumePromise = null;\n      });\n    }\n    return this.resumePromise;\n  }\n\n  private async doResume(_signal?: AbortSignal): Promise<void> {\n    const client = await this.ensureClient();\n    const response = await client.getSandbox();\n    this.session = new Session({\n      client,\n      routes: response.json.routes,\n      session: response.json.session,\n    });\n  }\n\n  /**\n   * Poll until the current session reaches a terminal state, then resume.\n   */\n  private async waitForStopAndResume(signal?: AbortSignal): Promise<void> {\n    \"use step\";\n    const client = await this.ensureClient();\n    const pollingInterval = 500;\n    let status = this.session!.status;\n\n    while (status === \"stopping\" || status === \"snapshotting\") {\n      await sleepWithSignal(pollingInterval, signal);\n      const poll = await client.getSession({\n        sessionId: this.session!.sessionId,\n        signal,\n      });\n      this.session = new Session({\n        client,\n        routes: poll.json.routes,\n        session: poll.json.session,\n      });\n      status = poll.json.session.status;\n    }\n    await this.resume(signal);\n  }\n\n  /**\n   * Execute `fn`, and if the session is stopped/stopping/snapshotting, resume and retry.\n   */\n  private async withResume<T>(\n    fn: () => Promise<T>,\n    signal?: AbortSignal,\n  ): Promise<T> {\n    if (!this.session) {\n      await this.resume(signal);\n    }\n    try {\n      return await fn();\n    } catch (err) {\n      if (isSandboxStoppedError(err)) {\n        await this.resume(signal);\n        return fn();\n      }\n      if (isSandboxStoppingError(err) || isSandboxSnapshottingError(err)) {\n        await this.waitForStopAndResume(signal);\n        return fn();\n      }\n      throw err;\n    }\n  }\n\n  /**\n   * Start executing a command in this sandbox.\n   *\n   * @param command - The command to execute.\n   * @param args - Arguments to pass to the command.\n   * @param opts - Optional parameters.\n   * @param opts.signal - An AbortSignal to cancel the command execution.\n   * @returns A {@link CommandFinished} result once execution is done.\n   */\n  async runCommand(\n    command: string,\n    args?: string[],\n    opts?: { signal?: AbortSignal },\n  ): Promise<CommandFinished>;\n  /**\n   * Start executing a command in detached mode.\n   *\n   * @param params - The command parameters.\n   * @returns A {@link Command} instance for the running command.\n   */\n  async runCommand(\n    params: RunCommandParams & { detached: true },\n  ): Promise<Command>;\n\n  /**\n   * Start executing a command in this sandbox.\n   *\n   * @param params - The command parameters.\n   * @returns A {@link CommandFinished} result once execution is done.\n   */\n\n  async runCommand(params: RunCommandParams): Promise<CommandFinished>;\n\n  async runCommand(\n    commandOrParams: string | RunCommandParams,\n    args?: string[],\n    opts?: { signal?: AbortSignal },\n  ): Promise<Command | CommandFinished> {\n    \"use step\";\n    const signal =\n      typeof commandOrParams === \"string\"\n        ? opts?.signal\n        : commandOrParams.signal;\n    return this.withResume(\n      () => this.session!.runCommand(commandOrParams as any, args, opts),\n      signal,\n    );\n  }\n\n  /**\n   * Internal helper to start a command in the sandbox.\n   *\n   * @param params - Command execution parameters.\n   * @returns A {@link Command} or {@link CommandFinished}, depending on `detached`.\n   * @internal\n   */\n  async getCommand(\n    cmdId: string,\n    opts?: { signal?: AbortSignal },\n  ): Promise<Command> {\n    \"use step\";\n    return this.withResume(\n      () => this.session!.getCommand(cmdId, opts),\n      opts?.signal,\n    );\n  }\n\n  /**\n   * Create a directory in the filesystem of this sandbox.\n   *\n   * @param path - Path of the directory to create\n   * @param opts - Optional parameters.\n   * @param opts.signal - An AbortSignal to cancel the operation.\n   */\n  async mkDir(path: string, opts?: { signal?: AbortSignal }): Promise<void> {\n    \"use step\";\n    return this.withResume(\n      () => this.session!.mkDir(path, opts),\n      opts?.signal,\n    );\n  }\n\n  /**\n   * Read a file from the filesystem of this sandbox as a stream.\n   *\n   * @param file - File to read, with path and optional cwd\n   * @param opts - Optional parameters.\n   * @param opts.signal - An AbortSignal to cancel the operation.\n   * @returns A promise that resolves to a ReadableStream containing the file contents, or null if file not found\n   */\n  async readFile(\n    file: { path: string; cwd?: string },\n    opts?: { signal?: AbortSignal },\n  ): Promise<ReadableStream | null> {\n    return this.withResume(\n      () => this.session!.readFile(file, opts),\n      opts?.signal,\n    );\n  }\n\n  /**\n   * Read a file from the filesystem of this sandbox as a Buffer.\n   *\n   * @param file - File to read, with path and optional cwd\n   * @param opts - Optional parameters.\n   * @param opts.signal - An AbortSignal to cancel the operation.\n   * @returns A promise that resolves to the file contents as a Buffer, or null if file not found\n   */\n  async readFileToBuffer(\n    file: { path: string; cwd?: string },\n    opts?: { signal?: AbortSignal },\n  ): Promise<Uint8Array | null> {\n    return this.withResume(\n      () => this.session!.readFileToBuffer(file, opts),\n      opts?.signal,\n    );\n  }\n\n  /**\n   * Download a file from the sandbox to the local filesystem.\n   *\n   * @param src - Source file on the sandbox, with path and optional cwd\n   * @param dst - Destination file on the local machine, with path and optional cwd\n   * @param opts - Optional parameters.\n   * @param opts.mkdirRecursive - If true, create parent directories for the destination if they don't exist.\n   * @param opts.signal - An AbortSignal to cancel the operation.\n   * @returns The absolute path to the written file, or null if the source file was not found\n   */\n  async downloadFile(\n    src: { path: string; cwd?: string },\n    dst: { path: string; cwd?: string },\n    opts?: { mkdirRecursive?: boolean; signal?: AbortSignal },\n  ): Promise<string | null> {\n    \"use step\";\n    return this.withResume(\n      () => this.session!.downloadFile(src, dst, opts),\n      opts?.signal,\n    );\n  }\n\n  /**\n   * Write files to the filesystem of this sandbox.\n   * Defaults to writing to /vercel/sandbox unless an absolute path is specified.\n   * Writes files using the `vercel-sandbox` user.\n   *\n   * @param files - Array of files with path, content, and optional mode (permissions)\n   * @param opts - Optional parameters.\n   * @param opts.signal - An AbortSignal to cancel the operation.\n   * @returns A promise that resolves when the files are written\n   *\n   * @example\n   * // Write an executable script\n   * await sandbox.writeFiles([\n   *   { path: \"/usr/local/bin/myscript\", content: \"#!/bin/bash\\necho hello\", mode: 0o755 }\n   * ]);\n   */\n  async writeFiles(\n    files: {\n      path: string;\n      content: string | Uint8Array;\n      mode?: number;\n    }[],\n    opts?: { signal?: AbortSignal },\n  ) {\n    \"use step\";\n    return this.withResume(\n      () => this.session!.writeFiles(files, opts),\n      opts?.signal,\n    );\n  }\n\n  /**\n   * Get the public domain of a port of this sandbox.\n   *\n   * @param p - Port number to resolve\n   * @returns A full domain (e.g. `https://subdomain.vercel.run`)\n   * @throws If the port has no associated route\n   */\n  domain(p: number): string {\n    return this.currentSession().domain(p);\n  }\n\n  /**\n   * Attach an HTML canvas to this sandbox's framebuffer. Starts a\n   * requestAnimationFrame loop that blits the guest's RGBA framebuffer\n   * (published via the in-guest display producer) zero-copy to the canvas, and\n   * forwards canvas keyboard/mouse events into the guest input device.\n   *\n   * Self-contained in-browser backend only: there is no remote display.\n   *\n   * @param canvas - An HTMLCanvasElement to paint into.\n   * @param opts.fpsCap - Max paint rate (default 60).\n   * @returns A controller with `stats()` and `stop()`.\n   *\n   * @example\n   * const sandbox = await Sandbox.create();\n   * await sandbox.runCommand(\"/usr/bin/Xfbdev\", [\":0\"], { detached: true });\n   * const display = await sandbox.attachDisplay(document.querySelector(\"canvas\"));\n   * // ... later: display.stop();\n   */\n  async attachDisplay(\n    canvas: unknown,\n    opts?: { fpsCap?: number },\n  ): Promise<{ stats: () => unknown; stop: () => void }> {\n    const client = await this.ensureClient();\n    return client.attachDisplay(canvas, opts);\n  }\n\n  /**\n   * Push a single input event into the guest input device (host -> guest).\n   *\n   * @param evt - `{ type: \"key\"|\"motion\"|\"button\", code?, button?, x?, y?, down? }`.\n   * @returns `false` if the running wasm predates the input device.\n   */\n  async pushInput(evt: {\n    type: \"key\" | \"motion\" | \"button\";\n    code?: number;\n    button?: number;\n    x?: number;\n    y?: number;\n    down?: number;\n    value?: number;\n  }): Promise<boolean> {\n    const client = await this.ensureClient();\n    return client.pushInput(evt);\n  }\n\n  /**\n   * Current framebuffer geometry the guest published, or `null` if none yet.\n   */\n  async displayInfo(): Promise<{\n    vaddr: number;\n    width: number;\n    height: number;\n    stride: number;\n    generation: number;\n  } | null> {\n    const client = await this.ensureClient();\n    return client.displayInfo();\n  }\n\n  /**\n   * Snapshot the guest framebuffer pixels as a contiguous RGBA buffer\n   * (assembled page-by-page), or `null` if no guest registered a framebuffer.\n   * For driving a custom blit loop instead of {@link attachDisplay}.\n   */\n  async displayPixels(): Promise<{\n    pixels: Uint8ClampedArray;\n    width: number;\n    height: number;\n    stride: number;\n    generation: number;\n  } | null> {\n    const client = await this.ensureClient();\n    return client.displayPixels();\n  }\n\n  /**\n   * Capability flags of the running Blink build (threads, sockets, framebuffer,\n   * pipe, fork, vectorISA, ...).\n   */\n  async capabilities(): Promise<Record<string, unknown>> {\n    const client = await this.ensureClient();\n    return client.capabilities();\n  }\n\n  /**\n   * Stop the sandbox.\n   *\n   * @param opts - Optional parameters.\n   * @param opts.signal - An AbortSignal to cancel the operation.\n   * @returns The final session state after stopping, with optional snapshot metadata.\n   */\n  async stop(opts?: {\n    signal?: AbortSignal;\n  }): Promise<SandboxSnapshot & { snapshot?: SnapshotMetadata }> {\n    \"use step\";\n    if (!this.session) {\n      throw new Error(\"No active session to stop.\");\n    }\n    const { session, sandbox, snapshot } = await this.session.stop(opts);\n    if (sandbox) {\n      this.sandbox = sandbox;\n    }\n    return Object.assign(session, { snapshot });\n  }\n\n  /**\n   * Update the network policy for this sandbox.\n   *\n   * @deprecated Use {@link Sandbox.update} instead.\n   *\n   * @param networkPolicy - The new network policy to apply.\n   * @param opts - Optional parameters.\n   * @param opts.signal - An AbortSignal to cancel the operation.\n   * @returns A promise that resolves when the network policy is updated.\n   *\n   * @example\n   * // Restrict to specific domains\n   * await sandbox.updateNetworkPolicy({\n   *   allow: [\"*.npmjs.org\", \"github.com\"],\n   * });\n   *\n   * @example\n   * // Inject credentials with per-domain transformers\n   * await sandbox.updateNetworkPolicy({\n   *   allow: {\n   *     \"ai-gateway.vercel.sh\": [{\n   *       transform: [{\n   *         headers: { authorization: \"Bearer ...\" }\n   *       }]\n   *     }],\n   *     \"*\": []\n   *   }\n   * });\n   *\n   * @example\n   * // Deny all network access\n   * await sandbox.updateNetworkPolicy(\"deny-all\");\n   */\n  async updateNetworkPolicy(\n    networkPolicy: NetworkPolicy,\n    opts?: { signal?: AbortSignal },\n  ): Promise<NetworkPolicy> {\n    \"use step\";\n    await this.withResume(\n      () => this.session!.update({ networkPolicy: networkPolicy }, opts),\n      opts?.signal,\n    );\n\n    return this.session!.networkPolicy!;\n  }\n\n  /**\n   * Extend the timeout of the sandbox by the specified duration.\n   *\n   * This allows you to extend the lifetime of a sandbox up until the maximum\n   * execution timeout for your plan.\n   *\n   * @param duration - The duration in milliseconds to extend the timeout by\n   * @param opts - Optional parameters.\n   * @param opts.signal - An AbortSignal to cancel the operation.\n   * @returns A promise that resolves when the timeout is extended\n   *\n   * @example\n   * const sandbox = await Sandbox.create({ timeout: ms('10m') });\n   * // Extends timeout by 5 minutes, to a total of 15 minutes.\n   * await sandbox.extendTimeout(ms('5m'));\n   */\n  async extendTimeout(\n    duration: number,\n    opts?: { signal?: AbortSignal },\n  ): Promise<void> {\n    \"use step\";\n    return this.withResume(\n      () => this.session!.extendTimeout(duration, opts),\n      opts?.signal,\n    );\n  }\n\n  /**\n   * Create a snapshot from this currently running sandbox. New sandboxes can\n   * then be created from this snapshot using {@link Sandbox.createFromSnapshot}.\n   *\n   * Note: this sandbox will be stopped as part of the snapshot creation process.\n   *\n   * @param opts - Optional parameters.\n   * @param opts.expiration - Optional expiration time in milliseconds. Use 0 for no expiration at all.\n   * @param opts.signal - An AbortSignal to cancel the operation.\n   * @returns A promise that resolves to the Snapshot instance\n   */\n  async snapshot(opts?: {\n    expiration?: number;\n    signal?: AbortSignal;\n  }): Promise<Snapshot> {\n    \"use step\";\n    return this.withResume(\n      () => this.session!.snapshot(opts),\n      opts?.signal,\n    );\n  }\n\n  /**\n   * Update the sandbox configuration.\n   *\n   * When `ports` is provided, it is treated as the full desired port list:\n   * any currently exposed port omitted from the array will be deregistered.\n   *\n   * @param params - Fields to update.\n   * @param opts - Optional abort signal.\n   */\n  async update(\n    params: {\n      persistent?: boolean;\n      resources?: { vcpus?: number };\n      timeout?: number;\n      networkPolicy?: NetworkPolicy;\n      tags?: Record<string, string>;\n      ports?: number[];\n      snapshotExpiration?: number;\n      keepLastSnapshots?: {\n        count: number;\n        expiration?: number;\n        deleteEvicted?: boolean;\n      } | null;\n      currentSnapshotId?: string;\n    },\n    opts?: { signal?: AbortSignal },\n  ): Promise<void> {\n    \"use step\";\n    const client = await this.ensureClient();\n    let resources: { vcpus: number; memory: number } | undefined;\n    if (params.resources?.vcpus) {\n      resources = {\n        vcpus: params.resources.vcpus,\n        memory: params.resources.vcpus * 2048,\n      };\n    }\n\n    // Update the sandbox config. This config will be used on the next session.\n    const response = await client.updateSandbox({\n      runtime:\n        params && \"runtime\" in params\n          ? (params as { runtime?: string }).runtime\n          : undefined,\n    });\n    this.sandbox = response.json.sandbox;\n    if (params.ports !== undefined && response.json.routes) {\n      this.session?.updateRoutes(response.json.routes);\n    }\n\n    // Update the current session config. This only applies to network policy.\n    if (params.networkPolicy) {\n      try {\n        return await this.session?.update(\n          { networkPolicy: params.networkPolicy },\n          opts,\n        );\n      } catch (err) {\n        if (isSandboxStoppedError(err) || isSandboxStoppingError(err)) {\n          return;\n        }\n        throw err;\n      }\n    }\n  }\n\n  /**\n   * Delete this sandbox.\n   *\n   * After deletion the instance becomes inert — all further API calls will\n   * throw immediately.\n   */\n  async delete(opts?: { signal?: AbortSignal }): Promise<void> {\n    \"use step\";\n    const client = await this.ensureClient();\n    await client.deleteSandbox();\n  }\n\n  /**\n   * List sessions (VMs) that have been created for this sandbox.\n   *\n   * @param params - Optional pagination parameters.\n   * @returns The list of sessions and pagination metadata.\n   */\n  async listSessions(params?: {\n    limit?: number;\n    cursor?: string;\n    sortOrder?: \"asc\" | \"desc\";\n    signal?: AbortSignal;\n  }) {\n    \"use step\";\n    const client = await this.ensureClient();\n    const response = await client.listSessions();\n    return response.json;\n  }\n\n  /**\n   * List snapshots that belong to this sandbox.\n   *\n   * @param params - Optional pagination parameters.\n   * @returns The list of snapshots and pagination metadata.\n   */\n  async listSnapshots(params?: {\n    limit?: number;\n    cursor?: string;\n    sortOrder?: \"asc\" | \"desc\";\n    signal?: AbortSignal;\n  }) {\n    \"use step\";\n    const client = await this.ensureClient();\n    const response = await client.listSnapshots();\n    return response.json;\n  }\n}\n\n/**\n * A {@link Sandbox} that can automatically be disposed using a `await using` statement.\n *\n * @example\n * {\n *   await using const sandbox = await Sandbox.create();\n * }\n * // Sandbox is automatically stopped here\n */\nclass DisposableSandbox extends Sandbox implements AsyncDisposable {\n  async [Symbol.asyncDispose]() {\n    await this.stop();\n  }\n}\n"],"mappings":"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"}