@langchain/deno 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -32,6 +32,8 @@ const DENO_SANDBOX_ERROR_SYMBOL = Symbol.for("deno.sandbox.error");
32
32
  * ```
33
33
  */
34
34
  var DenoSandboxError = class DenoSandboxError extends deepagents.SandboxError {
35
+ code;
36
+ cause;
35
37
  [DENO_SANDBOX_ERROR_SYMBOL];
36
38
  /** Error name for instanceof checks and logging */
37
39
  name = "DenoSandboxError";
@@ -332,8 +334,8 @@ var DenoSandbox = class DenoSandbox extends deepagents.BaseSandbox {
332
334
  const results = [];
333
335
  for (const [path, content] of files) try {
334
336
  const parentDir = path.substring(0, path.lastIndexOf("/"));
335
- if (parentDir) await (await sandbox.spawn("/bin/bash", {
336
- args: ["-c", `mkdir -p "${parentDir}"`],
337
+ if (parentDir) await (await sandbox.spawn("/bin/mkdir", {
338
+ args: ["-p", parentDir],
337
339
  stdout: "piped",
338
340
  stderr: "piped"
339
341
  })).output();
@@ -376,8 +378,8 @@ var DenoSandbox = class DenoSandbox extends deepagents.BaseSandbox {
376
378
  const sandbox = this.instance;
377
379
  const results = [];
378
380
  for (const path of paths) try {
379
- const { status, stdoutText } = await (await sandbox.spawn("/bin/bash", {
380
- args: ["-c", `cat "${path}"`],
381
+ const { status, stdoutText } = await (await sandbox.spawn("/bin/cat", {
382
+ args: [path],
381
383
  stdout: "piped",
382
384
  stderr: "piped"
383
385
  })).output();
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["SandboxError","BaseSandbox","#id","#sandbox","#options","Sandbox","#uploadInitialFiles","#mapError","#setFromExisting"],"sources":["../src/types.ts","../src/auth.ts","../src/sandbox.ts"],"sourcesContent":["/**\n * Type definitions for the Deno Sandbox backend.\n *\n * This module contains all type definitions for the @langchain/deno package,\n * including options and error types.\n */\n\nimport type {\n Memory,\n Region,\n SecretConfig,\n SnapshotId,\n SnapshotSlug,\n VolumeId,\n VolumeSlug,\n} from \"@deno/sandbox\";\nimport { type SandboxErrorCode, SandboxError } from \"deepagents\";\n\n/**\n * Supported regions for Deno Deploy sandboxes.\n *\n * Currently available regions:\n * - `ams`: Amsterdam\n * - `ord`: Chicago\n */\nexport type DenoSandboxRegion = Region;\n\n/**\n * Sandbox lifetime configuration.\n *\n * @deprecated Use {@link SandboxTimeout} instead. This type will be removed in a future release.\n *\n * - `\"session\"`: Sandbox shuts down when you close/dispose the client (default)\n * - Duration string: Keep sandbox alive for a specific time (e.g., \"5m\", \"30s\")\n */\nexport type SandboxLifetime = \"session\" | `${number}s` | `${number}m`;\n\n/**\n * Sandbox timeout configuration.\n *\n * - `\"session\"`: Sandbox shuts down when the primary client disconnects (default)\n * - Duration string: Keep sandbox alive for a specific time (e.g., \"600s\", \"20m\")\n *\n * Note: when using a duration, the sandbox will be terminated after the specified\n * time even if clients are still connected.\n */\nexport type SandboxTimeout = \"session\" | `${number}s` | `${number}m`;\n\n/**\n * Configuration options for creating a Deno Sandbox.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * memory: \"1GiB\", // 1GB memory\n * timeout: \"5m\", // 5 minutes\n * region: \"ord\", // Chicago\n * };\n * ```\n */\nexport interface DenoSandboxOptions {\n /**\n * Amount of memory allocated to the sandbox in megabytes.\n *\n * @deprecated Use {@link DenoSandboxOptions.memory} instead. This option will be removed in a future release.\n *\n * Memory limits:\n * - Minimum: 768MB\n * - Maximum: 4096MB\n *\n * @default 768\n */\n memoryMb?: number;\n\n /**\n * The memory size of the sandbox. Supports plain numbers (interpreted as bytes)\n * and human-readable strings with binary (GiB, MiB, KiB) or decimal (GB, MB, kB)\n * units.\n *\n * Takes precedence over the deprecated `memoryMb` option.\n *\n * @example 1342177280\n * @example \"1GiB\"\n * @example \"1280MiB\"\n * @default \"1280MiB\"\n */\n memory?: Memory;\n\n /**\n * Sandbox lifetime configuration.\n *\n * @deprecated Use {@link DenoSandboxOptions.timeout} instead. This option will be removed in a future release.\n *\n * - `\"session\"`: Sandbox shuts down when you close/dispose the client (default)\n * - Duration string: Keep sandbox alive for a specific time (e.g., \"5m\", \"30s\")\n *\n * Supported duration suffixes: `s` (seconds), `m` (minutes).\n *\n * @default \"session\"\n */\n lifetime?: SandboxLifetime;\n\n /**\n * The timeout of the sandbox. When not specified, it defaults to `\"session\"`.\n *\n * Takes precedence over the deprecated `lifetime` option.\n *\n * - `\"session\"`: Sandbox is destroyed when the primary client disconnects.\n * - Duration string: Keep sandbox alive for a specific time (e.g., \"600s\", \"20m\").\n * Note that when this duration has passed, the sandbox will be terminated even\n * if there are still clients connected to it.\n *\n * @example \"session\"\n * @example \"600s\"\n * @example \"20m\"\n * @default \"session\"\n */\n timeout?: SandboxTimeout;\n\n /**\n * Region where the sandbox will be created.\n *\n * If not specified, the sandbox will be created in the default region.\n *\n * @see DenoSandboxRegion for available regions\n */\n region?: DenoSandboxRegion;\n\n /**\n * Initial files to create in the sandbox after initialization.\n *\n * A map of file paths to their contents. Files will be created\n * in the sandbox filesystem before any commands are executed.\n * Parent directories are created automatically.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * memory: \"1GiB\",\n * initialFiles: {\n * \"/home/app/index.js\": \"console.log('Hello')\",\n * \"/home/app/package.json\": '{\"name\": \"test\"}',\n * },\n * };\n * ```\n */\n initialFiles?: Record<string, string>;\n\n /**\n * Authentication configuration for Deno Deploy API.\n *\n * @deprecated Use the top-level {@link DenoSandboxOptions.token} and {@link DenoSandboxOptions.org} options instead.\n * This option will be removed in a future release.\n *\n * ### Environment Variable Setup\n *\n * ```bash\n * # Go to https://app.deno.com -> Settings -> Organization Tokens\n * # Create a new token and set it as environment variable\n * export DENO_DEPLOY_TOKEN=your_token_here\n * ```\n *\n * Or pass the token directly in this auth configuration.\n */\n auth?: {\n /**\n * Deno Deploy access token.\n * If not provided, reads from `DENO_DEPLOY_TOKEN` environment variable.\n */\n token?: string;\n };\n\n /**\n * The Deno Deploy access token that should be used to authenticate requests.\n *\n * - When passing an organization token (starts with `ddo_`), no further\n * organization information is required.\n * - When passing a personal token (starts with `ddp_`), the `org` option\n * must also be provided.\n *\n * If not provided, the `DENO_DEPLOY_TOKEN` environment variable will be used.\n *\n * Takes precedence over the deprecated `auth.token` option.\n */\n token?: string;\n\n /**\n * The Deno Deploy organization slug to operate within.\n *\n * This is required when using a personal access token (starts with `ddp_`).\n * If not provided, the `DENO_DEPLOY_ORG` environment variable will be used.\n */\n org?: string;\n\n /**\n * Environment variables to start the sandbox with, in addition to the default\n * environment variables such as `DENO_DEPLOY_ORGANIZATION_ID`.\n */\n env?: Record<string, string>;\n\n /**\n * Whether to enable debug logging.\n *\n * @default false\n */\n debug?: boolean;\n\n /**\n * Labels to set on the sandbox. Up to 5 labels can be specified.\n * Each label key must be at most 64 bytes, and each label value\n * must be at most 128 bytes.\n */\n labels?: Record<string, string>;\n\n /**\n * A volume or snapshot to use as the root filesystem of the sandbox.\n *\n * If not specified, the default base image will be used. The volume or\n * snapshot must be bootable.\n *\n * - Volumes will be mounted read-write (writes are persisted).\n * - Snapshots will be mounted read-only (writes are not persisted).\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * root: \"my-volume-slug\",\n * };\n * ```\n */\n root?: VolumeId | VolumeSlug | SnapshotId | SnapshotSlug;\n\n /**\n * Volumes to mount on the sandbox.\n *\n * The key is the mount path inside the sandbox, and the value is the\n * volume ID or slug.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * volumes: {\n * \"/data/volume1\": \"volume-slug-or-id-1\",\n * },\n * };\n * ```\n */\n volumes?: Record<string, VolumeId | VolumeSlug>;\n\n /**\n * List of hostnames / IP addresses with optional port numbers that the\n * sandbox can make outbound network requests to.\n *\n * If not specified, no network restrictions are applied.\n *\n * @example []\n * @example [\"example.com\"]\n * @example [\"*.example.com\"]\n * @example [\"example.com:443\"]\n */\n allowNet?: string[];\n\n /**\n * Secret environment variables that are never exposed to sandbox code.\n * The real secret values are injected on the wire when the sandbox makes\n * HTTPS requests to the specified hosts.\n *\n * The key is the environment variable name.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * secrets: {\n * OPENAI_API_KEY: {\n * hosts: [\"api.openai.com\"],\n * value: \"sk-proj-your-real-key\",\n * },\n * },\n * };\n * ```\n */\n secrets?: Record<string, SecretConfig>;\n\n /**\n * Whether to expose SSH access to the sandbox. If true, the sandbox's\n * `ssh` property will be populated once the sandbox is ready.\n *\n * @example\n * ```typescript\n * const sandbox = await DenoSandbox.create({ ssh: true });\n * console.log(sandbox.instance.ssh);\n * // => { username: \"...\", hostname: \"...\" }\n * ```\n */\n ssh?: boolean;\n\n /**\n * The port number to expose for HTTP access. If specified, the sandbox's\n * `url` property will be populated once the sandbox is ready, and can\n * be used to access the sandbox over HTTP.\n *\n * @example\n * ```typescript\n * const sandbox = await DenoSandbox.create({ port: 8080 });\n * console.log(sandbox.instance.url);\n * // => \"http://...\"\n * ```\n */\n port?: number;\n\n /**\n * Override the Sandbox API endpoint URL to use to create and communicate\n * with the sandboxes.\n *\n * The default can also be overridden by setting the `DENO_SANDBOX_ENDPOINT`\n * or `DENO_SANDBOX_BASE_DOMAIN` environment variables.\n */\n sandboxEndpoint?: string | ((region: string) => string);\n\n /**\n * Override the API endpoint to use to connect to Deno Deploy.\n *\n * The default can also be overridden by setting the `DENO_DEPLOY_ENDPOINT`\n * environment variable.\n */\n apiEndpoint?: string;\n}\n\n/**\n * Error codes for Deno Sandbox operations.\n *\n * Used to identify specific error conditions and handle them appropriately.\n */\nexport type DenoSandboxErrorCode =\n | SandboxErrorCode\n /** Authentication failed - check token configuration */\n | \"AUTHENTICATION_FAILED\"\n /** Failed to create sandbox - check options and quotas */\n | \"SANDBOX_CREATION_FAILED\"\n /** Sandbox not found - may have been stopped or expired */\n | \"SANDBOX_NOT_FOUND\"\n /** Resource limits exceeded (CPU, memory, storage) */\n | \"RESOURCE_LIMIT_EXCEEDED\";\n\nconst DENO_SANDBOX_ERROR_SYMBOL = Symbol.for(\"deno.sandbox.error\");\n\n/**\n * Custom error class for Deno Sandbox operations.\n *\n * Provides structured error information including:\n * - Human-readable message\n * - Error code for programmatic handling\n * - Original cause for debugging\n *\n * @example\n * ```typescript\n * try {\n * await sandbox.execute(\"some command\");\n * } catch (error) {\n * if (error instanceof DenoSandboxError) {\n * switch (error.code) {\n * case \"NOT_INITIALIZED\":\n * await sandbox.initialize();\n * break;\n * case \"COMMAND_TIMEOUT\":\n * console.error(\"Command took too long\");\n * break;\n * default:\n * throw error;\n * }\n * }\n * }\n * ```\n */\nexport class DenoSandboxError extends SandboxError {\n [DENO_SANDBOX_ERROR_SYMBOL]: true;\n\n /** Error name for instanceof checks and logging */\n override readonly name = \"DenoSandboxError\";\n\n /**\n * Creates a new DenoSandboxError.\n *\n * @param message - Human-readable error description\n * @param code - Structured error code for programmatic handling\n * @param cause - Original error that caused this error (for debugging)\n */\n constructor(\n message: string,\n public readonly code: DenoSandboxErrorCode,\n public override readonly cause?: Error,\n ) {\n super(message, code as SandboxErrorCode, cause);\n // Maintain proper prototype chain for instanceof checks\n Object.setPrototypeOf(this, DenoSandboxError.prototype);\n }\n\n /**\n * Checks if the error is an instance of DenoSandboxError.\n *\n * @param error - The error to check\n * @returns True if the error is an instance of DenoSandboxError, false otherwise\n */\n static isInstance(error: unknown): error is DenoSandboxError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as Record<symbol, unknown>)[DENO_SANDBOX_ERROR_SYMBOL] === true\n );\n }\n}\n","/**\n * Authentication utilities for Deno Sandbox.\n *\n * This module provides authentication credential resolution for the Deno Sandbox SDK.\n *\n * @packageDocumentation\n */\n\nimport type { DenoSandboxOptions } from \"./types.js\";\nimport { DenoSandboxError } from \"./types.js\";\n\n/**\n * Authentication credentials for Deno Sandbox API.\n */\nexport interface DenoCredentials {\n /** Deno Deploy access token */\n token: string;\n}\n\n/**\n * Get the authentication token for Deno Sandbox API.\n *\n * Authentication is resolved in the following priority order:\n *\n * 1. **Explicit token**: If `options.token` is provided, it is used directly.\n * 2. **DENO_DEPLOY_TOKEN**: Environment variable for Deno Deploy access token.\n *\n * If no token is found, an error is thrown with setup instructions.\n *\n * ## Environment Variable Setup\n *\n * ```bash\n * # Go to https://app.deno.com -> Settings -> Organization Tokens\n * # Create a new token and set it as environment variable\n * export DENO_DEPLOY_TOKEN=your_token_here\n * ```\n *\n * @param options - Optional authentication configuration from DenoSandboxOptions\n * @returns The authentication token string\n * @throws {DenoSandboxError} If no authentication token is available\n *\n * @example\n * ```typescript\n * // With explicit token\n * const token = getAuthToken({ token: \"my-token\" });\n *\n * // Using environment variables (auto-detected)\n * const token = getAuthToken();\n *\n * // From DenoSandboxOptions\n * const options: DenoSandboxOptions = {\n * auth: { token: \"my-token\" }\n * };\n * const token = getAuthToken(options.auth);\n * ```\n */\nexport function getAuthToken(options?: DenoSandboxOptions[\"auth\"]): string {\n // Priority 1: Explicit token in options\n if (options?.token) {\n return options.token;\n }\n\n // Priority 2: DENO_DEPLOY_TOKEN environment variable\n const deployToken = process.env.DENO_DEPLOY_TOKEN;\n if (deployToken) {\n return deployToken;\n }\n\n // No token found - throw descriptive error\n throw new DenoSandboxError(\n \"Deno Deploy authentication required. Provide a token using one of these methods:\\n\\n\" +\n \"1. Set DENO_DEPLOY_TOKEN environment variable:\\n\" +\n \" Go to https://app.deno.com -> Settings -> Organization Tokens\\n\" +\n \" Create a new token and run: export DENO_DEPLOY_TOKEN=your_token_here\\n\\n\" +\n \"2. Pass token directly in options:\\n\" +\n ' new DenoSandbox({ token: \"...\" })',\n \"AUTHENTICATION_FAILED\",\n );\n}\n\n/**\n * Get authentication credentials for Deno Sandbox API.\n *\n * This function returns the credentials needed for the Deno SDK.\n *\n * @param options - Optional authentication configuration from DenoSandboxOptions\n * @returns Complete authentication credentials\n * @throws {DenoSandboxError} If no authentication token is available\n */\nexport function getAuthCredentials(\n options?: DenoSandboxOptions[\"auth\"],\n): DenoCredentials {\n return {\n token: getAuthToken(options),\n };\n}\n","/* eslint-disable no-instanceof/no-instanceof */\n/**\n * Deno Sandbox implementation of the SandboxBackendProtocol.\n *\n * This module provides a Deno Sandbox backend for deepagents, enabling agents\n * to execute commands, read/write files, and manage isolated Linux microVM\n * environments using Deno Deploy's Sandbox infrastructure.\n *\n * @packageDocumentation\n */\n\nimport { Sandbox, type SandboxOptions } from \"@deno/sandbox\";\nimport {\n BaseSandbox,\n type ExecuteResponse,\n type FileDownloadResponse,\n type FileOperationError,\n type FileUploadResponse,\n type BackendFactory,\n} from \"deepagents\";\n\nimport { getAuthCredentials } from \"./auth.js\";\nimport { DenoSandboxError, type DenoSandboxOptions } from \"./types.js\";\n\n/**\n * Deno Sandbox backend for deepagents.\n *\n * Extends `BaseSandbox` to provide command execution, file operations, and\n * sandbox lifecycle management using Deno Deploy's Sandbox SDK.\n *\n * ## Basic Usage\n *\n * ```typescript\n * import { DenoSandbox } from \"@langchain/deno\";\n *\n * // Create and initialize a sandbox\n * const sandbox = await DenoSandbox.create({\n * memory: \"1GiB\",\n * timeout: \"5m\",\n * });\n *\n * try {\n * // Execute commands\n * const result = await sandbox.execute(\"deno --version\");\n * console.log(result.output);\n * } finally {\n * // Always cleanup\n * await sandbox.close();\n * }\n * ```\n *\n * ## Using with DeepAgent\n *\n * ```typescript\n * import { createDeepAgent } from \"deepagents\";\n * import { DenoSandbox } from \"@langchain/deno\";\n *\n * const sandbox = await DenoSandbox.create();\n *\n * const agent = createDeepAgent({\n * model: new ChatAnthropic({ model: \"claude-sonnet-4-20250514\" }),\n * systemPrompt: \"You are a coding assistant with sandbox access.\",\n * backend: sandbox,\n * });\n * ```\n */\nexport class DenoSandbox extends BaseSandbox {\n /** Private reference to the underlying Deno Sandbox instance */\n #sandbox: Sandbox | null = null;\n\n /** Configuration options for this sandbox */\n #options: DenoSandboxOptions;\n\n /** Unique identifier for this sandbox instance */\n #id: string;\n\n /**\n * Get the unique identifier for this sandbox.\n *\n * Before initialization, returns a temporary ID.\n * After initialization, returns the actual Deno sandbox ID.\n */\n get id(): string {\n return this.#id;\n }\n\n /**\n * Get the underlying Deno Sandbox instance.\n *\n * @throws {DenoSandboxError} If the sandbox is not initialized\n *\n * @example\n * ```typescript\n * const sandbox = await DenoSandbox.create();\n * const denoSdk = sandbox.sandbox; // Access the raw SDK\n * ```\n */\n get instance(): Sandbox {\n if (!this.#sandbox) {\n throw new DenoSandboxError(\n \"Sandbox not initialized. Call initialize() or use DenoSandbox.create()\",\n \"NOT_INITIALIZED\",\n );\n }\n return this.#sandbox;\n }\n\n /**\n * Check if the sandbox is initialized and running.\n */\n get isRunning(): boolean {\n return this.#sandbox !== null;\n }\n\n /**\n * Create a new DenoSandbox instance.\n *\n * Note: This only creates the instance. Call `initialize()` to actually\n * create the Deno Sandbox, or use the static `DenoSandbox.create()` method.\n *\n * @param options - Configuration options for the sandbox\n *\n * @example\n * ```typescript\n * // Two-step initialization\n * const sandbox = new DenoSandbox({ memory: \"1GiB\" });\n * await sandbox.initialize();\n *\n * // Or use the factory method\n * const sandbox = await DenoSandbox.create({ memory: \"1GiB\" });\n * ```\n */\n constructor(options: DenoSandboxOptions = {}) {\n super();\n\n this.#options = { ...options };\n\n // Generate temporary ID until initialized\n this.#id = `deno-sandbox-${Date.now()}`;\n }\n\n /**\n * Initialize the sandbox by creating a new Deno Sandbox instance.\n *\n * This method authenticates with Deno Deploy and provisions a new microVM\n * sandbox. After initialization, the `id` property will reflect the\n * actual Deno sandbox ID.\n *\n * @throws {DenoSandboxError} If already initialized (`ALREADY_INITIALIZED`)\n * @throws {DenoSandboxError} If authentication fails (`AUTHENTICATION_FAILED`)\n * @throws {DenoSandboxError} If sandbox creation fails (`SANDBOX_CREATION_FAILED`)\n *\n * @example\n * ```typescript\n * const sandbox = new DenoSandbox();\n * await sandbox.initialize();\n * console.log(`Sandbox ID: ${sandbox.id}`);\n * ```\n */\n async initialize(): Promise<void> {\n // Prevent double initialization\n if (this.#sandbox) {\n throw new DenoSandboxError(\n \"Sandbox is already initialized. Each DenoSandbox instance can only be initialized once.\",\n \"ALREADY_INITIALIZED\",\n );\n }\n\n // Resolve token: top-level `token` > deprecated `auth.token` > env variable\n const resolvedToken =\n this.#options.token ??\n this.#options.auth?.token ??\n getAuthCredentials(this.#options.auth).token;\n\n try {\n // Separate deprecated / custom keys from options that pass through 1:1\n const {\n memoryMb,\n memory,\n lifetime,\n timeout,\n auth: _auth,\n initialFiles: _initialFiles,\n ...passthroughOptions\n } = this.#options;\n\n // Build SDK create options: start with all 1:1 passthrough keys,\n // then layer on the deprecated-to-new mappings.\n const createOptions: SandboxOptions = {\n ...passthroughOptions,\n // `memory` takes precedence over deprecated `memoryMb`\n memory:\n memory ?? (memoryMb !== undefined ? `${memoryMb}MiB` : undefined),\n // `timeout` takes precedence over deprecated `lifetime`\n timeout: timeout ?? lifetime,\n // Resolved token\n token: resolvedToken,\n };\n\n // Create the sandbox\n this.#sandbox = await Sandbox.create(createOptions);\n\n // Update ID to the actual sandbox ID\n this.#id = this.#sandbox.id;\n\n // Upload initial files if provided\n if (this.#options.initialFiles) {\n await this.#uploadInitialFiles(this.#options.initialFiles);\n }\n } catch (error) {\n throw new DenoSandboxError(\n `Failed to create Deno Sandbox: ${error instanceof Error ? error.message : String(error)}`,\n \"SANDBOX_CREATION_FAILED\",\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n /**\n * Upload initial files to the sandbox.\n *\n * @param files - A map of file paths to their string contents\n */\n async #uploadInitialFiles(files: Record<string, string>): Promise<void> {\n const encoder = new TextEncoder();\n const fileEntries: Array<[string, Uint8Array]> = Object.entries(files).map(\n ([path, content]) => [path, encoder.encode(content)],\n );\n\n const results = await this.uploadFiles(fileEntries);\n\n // Check for any errors during upload\n const errors = results.filter((r) => r.error !== null);\n if (errors.length > 0) {\n const errorPaths = errors.map((e) => `${e.path}: ${e.error}`).join(\", \");\n throw new DenoSandboxError(\n `Failed to upload initial files: ${errorPaths}`,\n \"FILE_OPERATION_FAILED\",\n );\n }\n }\n\n /**\n * Execute a command in the sandbox.\n *\n * Commands are run using the sandbox's shell in the configured working directory.\n *\n * @param command - The shell command to execute\n * @returns Execution result with output, exit code, and truncation flag\n * @throws {DenoSandboxError} If the sandbox is not initialized\n *\n * @example\n * ```typescript\n * const result = await sandbox.execute(\"echo 'Hello World'\");\n * console.log(result.output); // \"Hello World\\n\"\n * console.log(result.exitCode); // 0\n * ```\n */\n async execute(command: string): Promise<ExecuteResponse> {\n const sandbox = this.instance; // Throws if not initialized\n\n try {\n // Use spawn with bash to execute the command\n const child = await sandbox.spawn(\"/bin/bash\", {\n args: [\"-c\", command],\n stdout: \"piped\",\n stderr: \"piped\",\n });\n\n // Use output() to get buffered stdout/stderr\n const { status, stdoutText, stderrText } = await child.output();\n\n return {\n output: (stdoutText ?? \"\") + (stderrText ?? \"\"),\n exitCode: status.code ?? 0,\n truncated: false,\n };\n } catch (error) {\n // Check for timeout\n if (error instanceof Error && error.message.includes(\"timeout\")) {\n throw new DenoSandboxError(\n `Command timed out: ${command}`,\n \"COMMAND_TIMEOUT\",\n error,\n );\n }\n\n throw new DenoSandboxError(\n `Command execution failed: ${error instanceof Error ? error.message : String(error)}`,\n \"COMMAND_FAILED\",\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n /**\n * Upload files to the sandbox.\n *\n * Files are written to the sandbox filesystem. Parent directories are\n * created automatically if they don't exist.\n *\n * @param files - Array of [path, content] tuples to upload\n * @returns Upload result for each file, with success or error status\n *\n * @example\n * ```typescript\n * const encoder = new TextEncoder();\n * const results = await sandbox.uploadFiles([\n * [\"src/index.js\", encoder.encode(\"console.log('Hello')\")],\n * [\"package.json\", encoder.encode('{\"name\": \"test\"}')],\n * ]);\n * ```\n */\n async uploadFiles(\n files: Array<[string, Uint8Array]>,\n ): Promise<FileUploadResponse[]> {\n const sandbox = this.instance; // Throws if not initialized\n const results: FileUploadResponse[] = [];\n\n for (const [path, content] of files) {\n try {\n // Ensure parent directory exists using spawn (more reliable than sh template)\n const parentDir = path.substring(0, path.lastIndexOf(\"/\"));\n if (parentDir) {\n const mkdirChild = await sandbox.spawn(\"/bin/bash\", {\n args: [\"-c\", `mkdir -p \"${parentDir}\"`],\n stdout: \"piped\",\n stderr: \"piped\",\n });\n await mkdirChild.output();\n }\n\n // Write the file content\n const textContent = new TextDecoder().decode(content);\n await sandbox.fs.writeTextFile(path, textContent);\n results.push({ path, error: null });\n } catch (error) {\n results.push({ path, error: this.#mapError(error) });\n }\n }\n\n return results;\n }\n\n /**\n * Download files from the sandbox.\n *\n * Each file is read individually, allowing partial success when some\n * files exist and others don't.\n *\n * @param paths - Array of file paths to download\n * @returns Download result for each file, with content or error\n *\n * @example\n * ```typescript\n * const results = await sandbox.downloadFiles([\"src/index.js\", \"missing.txt\"]);\n * for (const result of results) {\n * if (result.content) {\n * console.log(new TextDecoder().decode(result.content));\n * } else {\n * console.error(`Error: ${result.error}`);\n * }\n * }\n * ```\n */\n async downloadFiles(paths: string[]): Promise<FileDownloadResponse[]> {\n const sandbox = this.instance; // Throws if not initialized\n const results: FileDownloadResponse[] = [];\n\n for (const path of paths) {\n try {\n // Use spawn with bash to read file content (same approach as execute())\n const child = await sandbox.spawn(\"/bin/bash\", {\n args: [\"-c\", `cat \"${path}\"`],\n stdout: \"piped\",\n stderr: \"piped\",\n });\n\n const { status, stdoutText } = await child.output();\n\n if (!status.success) {\n results.push({\n path,\n content: null,\n error: \"file_not_found\",\n });\n } else {\n const content = new TextEncoder().encode(stdoutText ?? \"\");\n results.push({\n path,\n content,\n error: null,\n });\n }\n } catch (error) {\n results.push({\n path,\n content: null,\n error: this.#mapError(error),\n });\n }\n }\n\n return results;\n }\n\n /**\n * Close the sandbox and release all resources.\n *\n * After closing, the sandbox cannot be used again. Any unsaved data\n * will be lost.\n *\n * @example\n * ```typescript\n * try {\n * await sandbox.execute(\"deno run build.ts\");\n * } finally {\n * await sandbox.close();\n * }\n * ```\n */\n async close(): Promise<void> {\n if (this.#sandbox) {\n try {\n await this.#sandbox.close();\n } finally {\n this.#sandbox = null;\n }\n }\n }\n\n /**\n * Forcefully terminate the sandbox.\n *\n * Use this when you need to immediately stop the sandbox, even if\n * operations are in progress.\n *\n * @example\n * ```typescript\n * await sandbox.kill();\n * ```\n */\n async kill(): Promise<void> {\n if (this.#sandbox) {\n try {\n await this.#sandbox.kill();\n } finally {\n this.#sandbox = null;\n }\n }\n }\n\n /**\n * Alias for close() to maintain compatibility with other sandbox implementations.\n */\n async stop(): Promise<void> {\n await this.close();\n }\n\n /**\n * Set the sandbox from an existing Deno Sandbox instance.\n * Used internally by the static `connect()` method.\n */\n #setFromExisting(existingSandbox: Sandbox, sandboxId: string): void {\n this.#sandbox = existingSandbox;\n this.#id = sandboxId;\n }\n\n /**\n * Map Deno SDK errors to standardized FileOperationError codes.\n *\n * @param error - The error from the Deno SDK\n * @returns A standardized error code\n */\n #mapError(error: unknown): FileOperationError {\n if (error instanceof Error) {\n const msg = error.message.toLowerCase();\n\n if (msg.includes(\"not found\") || msg.includes(\"enoent\")) {\n return \"file_not_found\";\n }\n if (msg.includes(\"permission\") || msg.includes(\"eacces\")) {\n return \"permission_denied\";\n }\n if (msg.includes(\"directory\") || msg.includes(\"eisdir\")) {\n return \"is_directory\";\n }\n }\n\n return \"invalid_path\";\n }\n\n /**\n * Create and initialize a new DenoSandbox in one step.\n *\n * This is the recommended way to create a sandbox. It combines\n * construction and initialization into a single async operation.\n *\n * @param options - Configuration options for the sandbox\n * @returns An initialized and ready-to-use sandbox\n *\n * @example\n * ```typescript\n * const sandbox = await DenoSandbox.create({\n * memory: \"1GiB\",\n * timeout: \"10m\",\n * region: \"ord\",\n * });\n * ```\n */\n static async create(options?: DenoSandboxOptions): Promise<DenoSandbox> {\n const sandbox = new DenoSandbox(options);\n await sandbox.initialize();\n return sandbox;\n }\n\n /**\n * Reconnect to an existing sandbox by ID.\n *\n * This allows you to resume working with a sandbox that was created\n * earlier with a duration-based lifetime.\n *\n * @param id - The ID of the sandbox to reconnect to\n * @param options - Optional auth configuration (for token)\n * @returns A connected sandbox instance\n *\n * @example\n * ```typescript\n * // Resume a sandbox from a stored ID\n * const sandbox = await DenoSandbox.fromId(\"sandbox-abc123\");\n * const result = await sandbox.execute(\"ls -la\");\n * ```\n */\n static async fromId(\n id: string,\n options?: Pick<\n DenoSandboxOptions,\n \"auth\" | \"token\" | \"org\" | \"apiEndpoint\"\n >,\n ): Promise<DenoSandbox> {\n // Resolve token: top-level `token` > deprecated `auth.token` > env variable\n const resolvedToken =\n options?.token ??\n options?.auth?.token ??\n getAuthCredentials(options?.auth).token;\n\n try {\n const existingSandbox = await Sandbox.connect({\n id,\n token: resolvedToken,\n ...(options?.org !== undefined ? { org: options.org } : {}),\n ...(options?.apiEndpoint !== undefined\n ? { apiEndpoint: options.apiEndpoint }\n : {}),\n });\n\n const denoSandbox = new DenoSandbox();\n // Set the existing sandbox directly (bypass initialize)\n denoSandbox.#setFromExisting(existingSandbox, id);\n\n return denoSandbox;\n } catch (error) {\n throw new DenoSandboxError(\n `Sandbox not found: ${id}`,\n \"SANDBOX_NOT_FOUND\",\n error instanceof Error ? error : undefined,\n );\n }\n }\n}\n\n/**\n * Async factory function type for creating Deno Sandbox instances.\n *\n * This is similar to BackendFactory but supports async creation,\n * which is required for Deno Sandbox since initialization is async.\n */\nexport type AsyncDenoSandboxFactory = () => Promise<DenoSandbox>;\n\n/**\n * Create an async factory function that creates a new Deno Sandbox per invocation.\n *\n * Each call to the factory will create and initialize a new sandbox.\n * This is useful when you want fresh, isolated environments for each\n * agent invocation.\n *\n * **Important**: This returns an async factory. For use with middleware that\n * requires synchronous BackendFactory, use `createDenoSandboxFactoryFromSandbox()`\n * with a pre-created sandbox instead.\n *\n * @param options - Optional configuration for sandbox creation\n * @returns An async factory function that creates new sandboxes\n *\n * @example\n * ```typescript\n * import { DenoSandbox, createDenoSandboxFactory } from \"@langchain/deno\";\n *\n * // Create a factory for new sandboxes\n * const factory = createDenoSandboxFactory({ memory: \"1GiB\" });\n *\n * // Each call creates a new sandbox\n * const sandbox1 = await factory();\n * const sandbox2 = await factory();\n *\n * try {\n * // Use sandboxes...\n * } finally {\n * await sandbox1.close();\n * await sandbox2.close();\n * }\n * ```\n */\nexport function createDenoSandboxFactory(\n options?: DenoSandboxOptions,\n): AsyncDenoSandboxFactory {\n return async () => {\n return await DenoSandbox.create(options);\n };\n}\n\n/**\n * Create a backend factory that reuses an existing Deno Sandbox.\n *\n * This allows multiple agent invocations to share the same sandbox,\n * avoiding the startup overhead of creating new sandboxes.\n *\n * Important: You are responsible for managing the sandbox lifecycle\n * (calling `close()` when done).\n *\n * @param sandbox - An existing DenoSandbox instance (must be initialized)\n * @returns A BackendFactory that returns the provided sandbox\n *\n * @example\n * ```typescript\n * import { createDeepAgent, createFilesystemMiddleware } from \"deepagents\";\n * import { DenoSandbox, createDenoSandboxFactoryFromSandbox } from \"@langchain/deno\";\n *\n * // Create and initialize a sandbox\n * const sandbox = await DenoSandbox.create({ memory: \"1GiB\" });\n *\n * try {\n * const agent = createDeepAgent({\n * model: new ChatAnthropic({ model: \"claude-sonnet-4-20250514\" }),\n * systemPrompt: \"You are a coding assistant.\",\n * middlewares: [\n * createFilesystemMiddleware({\n * backend: createDenoSandboxFactoryFromSandbox(sandbox),\n * }),\n * ],\n * });\n *\n * await agent.invoke({ messages: [...] });\n * } finally {\n * await sandbox.close();\n * }\n * ```\n */\nexport function createDenoSandboxFactoryFromSandbox(\n sandbox: DenoSandbox,\n): BackendFactory {\n return () => sandbox;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["SandboxError","BaseSandbox","#id","#sandbox","#options","Sandbox","#uploadInitialFiles","#mapError","#setFromExisting"],"sources":["../src/types.ts","../src/auth.ts","../src/sandbox.ts"],"sourcesContent":["/**\n * Type definitions for the Deno Sandbox backend.\n *\n * This module contains all type definitions for the @langchain/deno package,\n * including options and error types.\n */\n\nimport type {\n Memory,\n Region,\n SecretConfig,\n SnapshotId,\n SnapshotSlug,\n VolumeId,\n VolumeSlug,\n} from \"@deno/sandbox\";\nimport { type SandboxErrorCode, SandboxError } from \"deepagents\";\n\n/**\n * Supported regions for Deno Deploy sandboxes.\n *\n * Currently available regions:\n * - `ams`: Amsterdam\n * - `ord`: Chicago\n */\nexport type DenoSandboxRegion = Region;\n\n/**\n * Sandbox lifetime configuration.\n *\n * @deprecated Use {@link SandboxTimeout} instead. This type will be removed in a future release.\n *\n * - `\"session\"`: Sandbox shuts down when you close/dispose the client (default)\n * - Duration string: Keep sandbox alive for a specific time (e.g., \"5m\", \"30s\")\n */\nexport type SandboxLifetime = \"session\" | `${number}s` | `${number}m`;\n\n/**\n * Sandbox timeout configuration.\n *\n * - `\"session\"`: Sandbox shuts down when the primary client disconnects (default)\n * - Duration string: Keep sandbox alive for a specific time (e.g., \"600s\", \"20m\")\n *\n * Note: when using a duration, the sandbox will be terminated after the specified\n * time even if clients are still connected.\n */\nexport type SandboxTimeout = \"session\" | `${number}s` | `${number}m`;\n\n/**\n * Configuration options for creating a Deno Sandbox.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * memory: \"1GiB\", // 1GB memory\n * timeout: \"5m\", // 5 minutes\n * region: \"ord\", // Chicago\n * };\n * ```\n */\nexport interface DenoSandboxOptions {\n /**\n * Amount of memory allocated to the sandbox in megabytes.\n *\n * @deprecated Use {@link DenoSandboxOptions.memory} instead. This option will be removed in a future release.\n *\n * Memory limits:\n * - Minimum: 768MB\n * - Maximum: 4096MB\n *\n * @default 768\n */\n memoryMb?: number;\n\n /**\n * The memory size of the sandbox. Supports plain numbers (interpreted as bytes)\n * and human-readable strings with binary (GiB, MiB, KiB) or decimal (GB, MB, kB)\n * units.\n *\n * Takes precedence over the deprecated `memoryMb` option.\n *\n * @example 1342177280\n * @example \"1GiB\"\n * @example \"1280MiB\"\n * @default \"1280MiB\"\n */\n memory?: Memory;\n\n /**\n * Sandbox lifetime configuration.\n *\n * @deprecated Use {@link DenoSandboxOptions.timeout} instead. This option will be removed in a future release.\n *\n * - `\"session\"`: Sandbox shuts down when you close/dispose the client (default)\n * - Duration string: Keep sandbox alive for a specific time (e.g., \"5m\", \"30s\")\n *\n * Supported duration suffixes: `s` (seconds), `m` (minutes).\n *\n * @default \"session\"\n */\n lifetime?: SandboxLifetime;\n\n /**\n * The timeout of the sandbox. When not specified, it defaults to `\"session\"`.\n *\n * Takes precedence over the deprecated `lifetime` option.\n *\n * - `\"session\"`: Sandbox is destroyed when the primary client disconnects.\n * - Duration string: Keep sandbox alive for a specific time (e.g., \"600s\", \"20m\").\n * Note that when this duration has passed, the sandbox will be terminated even\n * if there are still clients connected to it.\n *\n * @example \"session\"\n * @example \"600s\"\n * @example \"20m\"\n * @default \"session\"\n */\n timeout?: SandboxTimeout;\n\n /**\n * Region where the sandbox will be created.\n *\n * If not specified, the sandbox will be created in the default region.\n *\n * @see DenoSandboxRegion for available regions\n */\n region?: DenoSandboxRegion;\n\n /**\n * Initial files to create in the sandbox after initialization.\n *\n * A map of file paths to their contents. Files will be created\n * in the sandbox filesystem before any commands are executed.\n * Parent directories are created automatically.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * memory: \"1GiB\",\n * initialFiles: {\n * \"/home/app/index.js\": \"console.log('Hello')\",\n * \"/home/app/package.json\": '{\"name\": \"test\"}',\n * },\n * };\n * ```\n */\n initialFiles?: Record<string, string>;\n\n /**\n * Authentication configuration for Deno Deploy API.\n *\n * @deprecated Use the top-level {@link DenoSandboxOptions.token} and {@link DenoSandboxOptions.org} options instead.\n * This option will be removed in a future release.\n *\n * ### Environment Variable Setup\n *\n * ```bash\n * # Go to https://app.deno.com -> Settings -> Organization Tokens\n * # Create a new token and set it as environment variable\n * export DENO_DEPLOY_TOKEN=your_token_here\n * ```\n *\n * Or pass the token directly in this auth configuration.\n */\n auth?: {\n /**\n * Deno Deploy access token.\n * If not provided, reads from `DENO_DEPLOY_TOKEN` environment variable.\n */\n token?: string;\n };\n\n /**\n * The Deno Deploy access token that should be used to authenticate requests.\n *\n * - When passing an organization token (starts with `ddo_`), no further\n * organization information is required.\n * - When passing a personal token (starts with `ddp_`), the `org` option\n * must also be provided.\n *\n * If not provided, the `DENO_DEPLOY_TOKEN` environment variable will be used.\n *\n * Takes precedence over the deprecated `auth.token` option.\n */\n token?: string;\n\n /**\n * The Deno Deploy organization slug to operate within.\n *\n * This is required when using a personal access token (starts with `ddp_`).\n * If not provided, the `DENO_DEPLOY_ORG` environment variable will be used.\n */\n org?: string;\n\n /**\n * Environment variables to start the sandbox with, in addition to the default\n * environment variables such as `DENO_DEPLOY_ORGANIZATION_ID`.\n */\n env?: Record<string, string>;\n\n /**\n * Whether to enable debug logging.\n *\n * @default false\n */\n debug?: boolean;\n\n /**\n * Labels to set on the sandbox. Up to 5 labels can be specified.\n * Each label key must be at most 64 bytes, and each label value\n * must be at most 128 bytes.\n */\n labels?: Record<string, string>;\n\n /**\n * A volume or snapshot to use as the root filesystem of the sandbox.\n *\n * If not specified, the default base image will be used. The volume or\n * snapshot must be bootable.\n *\n * - Volumes will be mounted read-write (writes are persisted).\n * - Snapshots will be mounted read-only (writes are not persisted).\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * root: \"my-volume-slug\",\n * };\n * ```\n */\n root?: VolumeId | VolumeSlug | SnapshotId | SnapshotSlug;\n\n /**\n * Volumes to mount on the sandbox.\n *\n * The key is the mount path inside the sandbox, and the value is the\n * volume ID or slug.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * volumes: {\n * \"/data/volume1\": \"volume-slug-or-id-1\",\n * },\n * };\n * ```\n */\n volumes?: Record<string, VolumeId | VolumeSlug>;\n\n /**\n * List of hostnames / IP addresses with optional port numbers that the\n * sandbox can make outbound network requests to.\n *\n * If not specified, no network restrictions are applied.\n *\n * @example []\n * @example [\"example.com\"]\n * @example [\"*.example.com\"]\n * @example [\"example.com:443\"]\n */\n allowNet?: string[];\n\n /**\n * Secret environment variables that are never exposed to sandbox code.\n * The real secret values are injected on the wire when the sandbox makes\n * HTTPS requests to the specified hosts.\n *\n * The key is the environment variable name.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * secrets: {\n * OPENAI_API_KEY: {\n * hosts: [\"api.openai.com\"],\n * value: \"sk-proj-your-real-key\",\n * },\n * },\n * };\n * ```\n */\n secrets?: Record<string, SecretConfig>;\n\n /**\n * Whether to expose SSH access to the sandbox. If true, the sandbox's\n * `ssh` property will be populated once the sandbox is ready.\n *\n * @example\n * ```typescript\n * const sandbox = await DenoSandbox.create({ ssh: true });\n * console.log(sandbox.instance.ssh);\n * // => { username: \"...\", hostname: \"...\" }\n * ```\n */\n ssh?: boolean;\n\n /**\n * The port number to expose for HTTP access. If specified, the sandbox's\n * `url` property will be populated once the sandbox is ready, and can\n * be used to access the sandbox over HTTP.\n *\n * @example\n * ```typescript\n * const sandbox = await DenoSandbox.create({ port: 8080 });\n * console.log(sandbox.instance.url);\n * // => \"http://...\"\n * ```\n */\n port?: number;\n\n /**\n * Override the Sandbox API endpoint URL to use to create and communicate\n * with the sandboxes.\n *\n * The default can also be overridden by setting the `DENO_SANDBOX_ENDPOINT`\n * or `DENO_SANDBOX_BASE_DOMAIN` environment variables.\n */\n sandboxEndpoint?: string | ((region: string) => string);\n\n /**\n * Override the API endpoint to use to connect to Deno Deploy.\n *\n * The default can also be overridden by setting the `DENO_DEPLOY_ENDPOINT`\n * environment variable.\n */\n apiEndpoint?: string;\n}\n\n/**\n * Error codes for Deno Sandbox operations.\n *\n * Used to identify specific error conditions and handle them appropriately.\n */\nexport type DenoSandboxErrorCode =\n | SandboxErrorCode\n /** Authentication failed - check token configuration */\n | \"AUTHENTICATION_FAILED\"\n /** Failed to create sandbox - check options and quotas */\n | \"SANDBOX_CREATION_FAILED\"\n /** Sandbox not found - may have been stopped or expired */\n | \"SANDBOX_NOT_FOUND\"\n /** Resource limits exceeded (CPU, memory, storage) */\n | \"RESOURCE_LIMIT_EXCEEDED\";\n\nconst DENO_SANDBOX_ERROR_SYMBOL = Symbol.for(\"deno.sandbox.error\");\n\n/**\n * Custom error class for Deno Sandbox operations.\n *\n * Provides structured error information including:\n * - Human-readable message\n * - Error code for programmatic handling\n * - Original cause for debugging\n *\n * @example\n * ```typescript\n * try {\n * await sandbox.execute(\"some command\");\n * } catch (error) {\n * if (error instanceof DenoSandboxError) {\n * switch (error.code) {\n * case \"NOT_INITIALIZED\":\n * await sandbox.initialize();\n * break;\n * case \"COMMAND_TIMEOUT\":\n * console.error(\"Command took too long\");\n * break;\n * default:\n * throw error;\n * }\n * }\n * }\n * ```\n */\nexport class DenoSandboxError extends SandboxError {\n [DENO_SANDBOX_ERROR_SYMBOL]: true;\n\n /** Error name for instanceof checks and logging */\n override readonly name = \"DenoSandboxError\";\n\n /**\n * Creates a new DenoSandboxError.\n *\n * @param message - Human-readable error description\n * @param code - Structured error code for programmatic handling\n * @param cause - Original error that caused this error (for debugging)\n */\n constructor(\n message: string,\n public readonly code: DenoSandboxErrorCode,\n public override readonly cause?: Error,\n ) {\n super(message, code as SandboxErrorCode, cause);\n // Maintain proper prototype chain for instanceof checks\n Object.setPrototypeOf(this, DenoSandboxError.prototype);\n }\n\n /**\n * Checks if the error is an instance of DenoSandboxError.\n *\n * @param error - The error to check\n * @returns True if the error is an instance of DenoSandboxError, false otherwise\n */\n static isInstance(error: unknown): error is DenoSandboxError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as Record<symbol, unknown>)[DENO_SANDBOX_ERROR_SYMBOL] === true\n );\n }\n}\n","/**\n * Authentication utilities for Deno Sandbox.\n *\n * This module provides authentication credential resolution for the Deno Sandbox SDK.\n *\n * @packageDocumentation\n */\n\nimport type { DenoSandboxOptions } from \"./types.js\";\nimport { DenoSandboxError } from \"./types.js\";\n\n/**\n * Authentication credentials for Deno Sandbox API.\n */\nexport interface DenoCredentials {\n /** Deno Deploy access token */\n token: string;\n}\n\n/**\n * Get the authentication token for Deno Sandbox API.\n *\n * Authentication is resolved in the following priority order:\n *\n * 1. **Explicit token**: If `options.token` is provided, it is used directly.\n * 2. **DENO_DEPLOY_TOKEN**: Environment variable for Deno Deploy access token.\n *\n * If no token is found, an error is thrown with setup instructions.\n *\n * ## Environment Variable Setup\n *\n * ```bash\n * # Go to https://app.deno.com -> Settings -> Organization Tokens\n * # Create a new token and set it as environment variable\n * export DENO_DEPLOY_TOKEN=your_token_here\n * ```\n *\n * @param options - Optional authentication configuration from DenoSandboxOptions\n * @returns The authentication token string\n * @throws {DenoSandboxError} If no authentication token is available\n *\n * @example\n * ```typescript\n * // With explicit token\n * const token = getAuthToken({ token: \"my-token\" });\n *\n * // Using environment variables (auto-detected)\n * const token = getAuthToken();\n *\n * // From DenoSandboxOptions\n * const options: DenoSandboxOptions = {\n * auth: { token: \"my-token\" }\n * };\n * const token = getAuthToken(options.auth);\n * ```\n */\nexport function getAuthToken(options?: DenoSandboxOptions[\"auth\"]): string {\n // Priority 1: Explicit token in options\n if (options?.token) {\n return options.token;\n }\n\n // Priority 2: DENO_DEPLOY_TOKEN environment variable\n const deployToken = process.env.DENO_DEPLOY_TOKEN;\n if (deployToken) {\n return deployToken;\n }\n\n // No token found - throw descriptive error\n throw new DenoSandboxError(\n \"Deno Deploy authentication required. Provide a token using one of these methods:\\n\\n\" +\n \"1. Set DENO_DEPLOY_TOKEN environment variable:\\n\" +\n \" Go to https://app.deno.com -> Settings -> Organization Tokens\\n\" +\n \" Create a new token and run: export DENO_DEPLOY_TOKEN=your_token_here\\n\\n\" +\n \"2. Pass token directly in options:\\n\" +\n ' new DenoSandbox({ token: \"...\" })',\n \"AUTHENTICATION_FAILED\",\n );\n}\n\n/**\n * Get authentication credentials for Deno Sandbox API.\n *\n * This function returns the credentials needed for the Deno SDK.\n *\n * @param options - Optional authentication configuration from DenoSandboxOptions\n * @returns Complete authentication credentials\n * @throws {DenoSandboxError} If no authentication token is available\n */\nexport function getAuthCredentials(\n options?: DenoSandboxOptions[\"auth\"],\n): DenoCredentials {\n return {\n token: getAuthToken(options),\n };\n}\n","/* oxlint-disable no-instanceof/no-instanceof */\n/**\n * Deno Sandbox implementation of the SandboxBackendProtocol.\n *\n * This module provides a Deno Sandbox backend for deepagents, enabling agents\n * to execute commands, read/write files, and manage isolated Linux microVM\n * environments using Deno Deploy's Sandbox infrastructure.\n *\n * @packageDocumentation\n */\n\nimport { Sandbox, type SandboxOptions } from \"@deno/sandbox\";\nimport {\n BaseSandbox,\n type ExecuteResponse,\n type FileDownloadResponse,\n type FileOperationError,\n type FileUploadResponse,\n type BackendFactory,\n} from \"deepagents\";\n\nimport { getAuthCredentials } from \"./auth.js\";\nimport { DenoSandboxError, type DenoSandboxOptions } from \"./types.js\";\n\n/**\n * Deno Sandbox backend for deepagents.\n *\n * Extends `BaseSandbox` to provide command execution, file operations, and\n * sandbox lifecycle management using Deno Deploy's Sandbox SDK.\n *\n * ## Basic Usage\n *\n * ```typescript\n * import { DenoSandbox } from \"@langchain/deno\";\n *\n * // Create and initialize a sandbox\n * const sandbox = await DenoSandbox.create({\n * memory: \"1GiB\",\n * timeout: \"5m\",\n * });\n *\n * try {\n * // Execute commands\n * const result = await sandbox.execute(\"deno --version\");\n * console.log(result.output);\n * } finally {\n * // Always cleanup\n * await sandbox.close();\n * }\n * ```\n *\n * ## Using with DeepAgent\n *\n * ```typescript\n * import { createDeepAgent } from \"deepagents\";\n * import { DenoSandbox } from \"@langchain/deno\";\n *\n * const sandbox = await DenoSandbox.create();\n *\n * const agent = createDeepAgent({\n * model: new ChatAnthropic({ model: \"claude-sonnet-4-20250514\" }),\n * systemPrompt: \"You are a coding assistant with sandbox access.\",\n * backend: sandbox,\n * });\n * ```\n */\nexport class DenoSandbox extends BaseSandbox {\n /** Private reference to the underlying Deno Sandbox instance */\n #sandbox: Sandbox | null = null;\n\n /** Configuration options for this sandbox */\n #options: DenoSandboxOptions;\n\n /** Unique identifier for this sandbox instance */\n #id: string;\n\n /**\n * Get the unique identifier for this sandbox.\n *\n * Before initialization, returns a temporary ID.\n * After initialization, returns the actual Deno sandbox ID.\n */\n get id(): string {\n return this.#id;\n }\n\n /**\n * Get the underlying Deno Sandbox instance.\n *\n * @throws {DenoSandboxError} If the sandbox is not initialized\n *\n * @example\n * ```typescript\n * const sandbox = await DenoSandbox.create();\n * const denoSdk = sandbox.sandbox; // Access the raw SDK\n * ```\n */\n get instance(): Sandbox {\n if (!this.#sandbox) {\n throw new DenoSandboxError(\n \"Sandbox not initialized. Call initialize() or use DenoSandbox.create()\",\n \"NOT_INITIALIZED\",\n );\n }\n return this.#sandbox;\n }\n\n /**\n * Check if the sandbox is initialized and running.\n */\n get isRunning(): boolean {\n return this.#sandbox !== null;\n }\n\n /**\n * Create a new DenoSandbox instance.\n *\n * Note: This only creates the instance. Call `initialize()` to actually\n * create the Deno Sandbox, or use the static `DenoSandbox.create()` method.\n *\n * @param options - Configuration options for the sandbox\n *\n * @example\n * ```typescript\n * // Two-step initialization\n * const sandbox = new DenoSandbox({ memory: \"1GiB\" });\n * await sandbox.initialize();\n *\n * // Or use the factory method\n * const sandbox = await DenoSandbox.create({ memory: \"1GiB\" });\n * ```\n */\n constructor(options: DenoSandboxOptions = {}) {\n super();\n\n this.#options = { ...options };\n\n // Generate temporary ID until initialized\n this.#id = `deno-sandbox-${Date.now()}`;\n }\n\n /**\n * Initialize the sandbox by creating a new Deno Sandbox instance.\n *\n * This method authenticates with Deno Deploy and provisions a new microVM\n * sandbox. After initialization, the `id` property will reflect the\n * actual Deno sandbox ID.\n *\n * @throws {DenoSandboxError} If already initialized (`ALREADY_INITIALIZED`)\n * @throws {DenoSandboxError} If authentication fails (`AUTHENTICATION_FAILED`)\n * @throws {DenoSandboxError} If sandbox creation fails (`SANDBOX_CREATION_FAILED`)\n *\n * @example\n * ```typescript\n * const sandbox = new DenoSandbox();\n * await sandbox.initialize();\n * console.log(`Sandbox ID: ${sandbox.id}`);\n * ```\n */\n async initialize(): Promise<void> {\n // Prevent double initialization\n if (this.#sandbox) {\n throw new DenoSandboxError(\n \"Sandbox is already initialized. Each DenoSandbox instance can only be initialized once.\",\n \"ALREADY_INITIALIZED\",\n );\n }\n\n // Resolve token: top-level `token` > deprecated `auth.token` > env variable\n const resolvedToken =\n this.#options.token ??\n this.#options.auth?.token ??\n getAuthCredentials(this.#options.auth).token;\n\n try {\n // Separate deprecated / custom keys from options that pass through 1:1\n const {\n memoryMb,\n memory,\n lifetime,\n timeout,\n auth: _auth,\n initialFiles: _initialFiles,\n ...passthroughOptions\n } = this.#options;\n\n // Build SDK create options: start with all 1:1 passthrough keys,\n // then layer on the deprecated-to-new mappings.\n const createOptions: SandboxOptions = {\n ...passthroughOptions,\n // `memory` takes precedence over deprecated `memoryMb`\n memory:\n memory ?? (memoryMb !== undefined ? `${memoryMb}MiB` : undefined),\n // `timeout` takes precedence over deprecated `lifetime`\n timeout: timeout ?? lifetime,\n // Resolved token\n token: resolvedToken,\n };\n\n // Create the sandbox\n this.#sandbox = await Sandbox.create(createOptions);\n\n // Update ID to the actual sandbox ID\n this.#id = this.#sandbox.id;\n\n // Upload initial files if provided\n if (this.#options.initialFiles) {\n await this.#uploadInitialFiles(this.#options.initialFiles);\n }\n } catch (error) {\n throw new DenoSandboxError(\n `Failed to create Deno Sandbox: ${error instanceof Error ? error.message : String(error)}`,\n \"SANDBOX_CREATION_FAILED\",\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n /**\n * Upload initial files to the sandbox.\n *\n * @param files - A map of file paths to their string contents\n */\n async #uploadInitialFiles(files: Record<string, string>): Promise<void> {\n const encoder = new TextEncoder();\n const fileEntries: Array<[string, Uint8Array]> = Object.entries(files).map(\n ([path, content]) => [path, encoder.encode(content)],\n );\n\n const results = await this.uploadFiles(fileEntries);\n\n // Check for any errors during upload\n const errors = results.filter((r) => r.error !== null);\n if (errors.length > 0) {\n const errorPaths = errors.map((e) => `${e.path}: ${e.error}`).join(\", \");\n throw new DenoSandboxError(\n `Failed to upload initial files: ${errorPaths}`,\n \"FILE_OPERATION_FAILED\",\n );\n }\n }\n\n /**\n * Execute a command in the sandbox.\n *\n * Commands are run using the sandbox's shell in the configured working directory.\n *\n * @param command - The shell command to execute\n * @returns Execution result with output, exit code, and truncation flag\n * @throws {DenoSandboxError} If the sandbox is not initialized\n *\n * @example\n * ```typescript\n * const result = await sandbox.execute(\"echo 'Hello World'\");\n * console.log(result.output); // \"Hello World\\n\"\n * console.log(result.exitCode); // 0\n * ```\n */\n async execute(command: string): Promise<ExecuteResponse> {\n const sandbox = this.instance; // Throws if not initialized\n\n try {\n // Use spawn with bash to execute the command\n const child = await sandbox.spawn(\"/bin/bash\", {\n args: [\"-c\", command],\n stdout: \"piped\",\n stderr: \"piped\",\n });\n\n // Use output() to get buffered stdout/stderr\n const { status, stdoutText, stderrText } = await child.output();\n\n return {\n output: (stdoutText ?? \"\") + (stderrText ?? \"\"),\n exitCode: status.code ?? 0,\n truncated: false,\n };\n } catch (error) {\n // Check for timeout\n if (error instanceof Error && error.message.includes(\"timeout\")) {\n throw new DenoSandboxError(\n `Command timed out: ${command}`,\n \"COMMAND_TIMEOUT\",\n error,\n );\n }\n\n throw new DenoSandboxError(\n `Command execution failed: ${error instanceof Error ? error.message : String(error)}`,\n \"COMMAND_FAILED\",\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n /**\n * Upload files to the sandbox.\n *\n * Files are written to the sandbox filesystem. Parent directories are\n * created automatically if they don't exist.\n *\n * @param files - Array of [path, content] tuples to upload\n * @returns Upload result for each file, with success or error status\n *\n * @example\n * ```typescript\n * const encoder = new TextEncoder();\n * const results = await sandbox.uploadFiles([\n * [\"src/index.js\", encoder.encode(\"console.log('Hello')\")],\n * [\"package.json\", encoder.encode('{\"name\": \"test\"}')],\n * ]);\n * ```\n */\n async uploadFiles(\n files: Array<[string, Uint8Array]>,\n ): Promise<FileUploadResponse[]> {\n const sandbox = this.instance; // Throws if not initialized\n const results: FileUploadResponse[] = [];\n\n for (const [path, content] of files) {\n try {\n // Ensure parent directory exists using spawn (more reliable than sh template)\n const parentDir = path.substring(0, path.lastIndexOf(\"/\"));\n if (parentDir) {\n const mkdirChild = await sandbox.spawn(\"/bin/mkdir\", {\n args: [\"-p\", parentDir],\n stdout: \"piped\",\n stderr: \"piped\",\n });\n await mkdirChild.output();\n }\n\n // Write the file content\n const textContent = new TextDecoder().decode(content);\n await sandbox.fs.writeTextFile(path, textContent);\n results.push({ path, error: null });\n } catch (error) {\n results.push({ path, error: this.#mapError(error) });\n }\n }\n\n return results;\n }\n\n /**\n * Download files from the sandbox.\n *\n * Each file is read individually, allowing partial success when some\n * files exist and others don't.\n *\n * @param paths - Array of file paths to download\n * @returns Download result for each file, with content or error\n *\n * @example\n * ```typescript\n * const results = await sandbox.downloadFiles([\"src/index.js\", \"missing.txt\"]);\n * for (const result of results) {\n * if (result.content) {\n * console.log(new TextDecoder().decode(result.content));\n * } else {\n * console.error(`Error: ${result.error}`);\n * }\n * }\n * ```\n */\n async downloadFiles(paths: string[]): Promise<FileDownloadResponse[]> {\n const sandbox = this.instance; // Throws if not initialized\n const results: FileDownloadResponse[] = [];\n\n for (const path of paths) {\n try {\n const child = await sandbox.spawn(\"/bin/cat\", {\n args: [path],\n stdout: \"piped\",\n stderr: \"piped\",\n });\n\n const { status, stdoutText } = await child.output();\n\n if (!status.success) {\n results.push({\n path,\n content: null,\n error: \"file_not_found\",\n });\n } else {\n const content = new TextEncoder().encode(stdoutText ?? \"\");\n results.push({\n path,\n content,\n error: null,\n });\n }\n } catch (error) {\n results.push({\n path,\n content: null,\n error: this.#mapError(error),\n });\n }\n }\n\n return results;\n }\n\n /**\n * Close the sandbox and release all resources.\n *\n * After closing, the sandbox cannot be used again. Any unsaved data\n * will be lost.\n *\n * @example\n * ```typescript\n * try {\n * await sandbox.execute(\"deno run build.ts\");\n * } finally {\n * await sandbox.close();\n * }\n * ```\n */\n async close(): Promise<void> {\n if (this.#sandbox) {\n try {\n await this.#sandbox.close();\n } finally {\n this.#sandbox = null;\n }\n }\n }\n\n /**\n * Forcefully terminate the sandbox.\n *\n * Use this when you need to immediately stop the sandbox, even if\n * operations are in progress.\n *\n * @example\n * ```typescript\n * await sandbox.kill();\n * ```\n */\n async kill(): Promise<void> {\n if (this.#sandbox) {\n try {\n await this.#sandbox.kill();\n } finally {\n this.#sandbox = null;\n }\n }\n }\n\n /**\n * Alias for close() to maintain compatibility with other sandbox implementations.\n */\n async stop(): Promise<void> {\n await this.close();\n }\n\n /**\n * Set the sandbox from an existing Deno Sandbox instance.\n * Used internally by the static `connect()` method.\n */\n #setFromExisting(existingSandbox: Sandbox, sandboxId: string): void {\n this.#sandbox = existingSandbox;\n this.#id = sandboxId;\n }\n\n /**\n * Map Deno SDK errors to standardized FileOperationError codes.\n *\n * @param error - The error from the Deno SDK\n * @returns A standardized error code\n */\n #mapError(error: unknown): FileOperationError {\n if (error instanceof Error) {\n const msg = error.message.toLowerCase();\n\n if (msg.includes(\"not found\") || msg.includes(\"enoent\")) {\n return \"file_not_found\";\n }\n if (msg.includes(\"permission\") || msg.includes(\"eacces\")) {\n return \"permission_denied\";\n }\n if (msg.includes(\"directory\") || msg.includes(\"eisdir\")) {\n return \"is_directory\";\n }\n }\n\n return \"invalid_path\";\n }\n\n /**\n * Create and initialize a new DenoSandbox in one step.\n *\n * This is the recommended way to create a sandbox. It combines\n * construction and initialization into a single async operation.\n *\n * @param options - Configuration options for the sandbox\n * @returns An initialized and ready-to-use sandbox\n *\n * @example\n * ```typescript\n * const sandbox = await DenoSandbox.create({\n * memory: \"1GiB\",\n * timeout: \"10m\",\n * region: \"ord\",\n * });\n * ```\n */\n static async create(options?: DenoSandboxOptions): Promise<DenoSandbox> {\n const sandbox = new DenoSandbox(options);\n await sandbox.initialize();\n return sandbox;\n }\n\n /**\n * Reconnect to an existing sandbox by ID.\n *\n * This allows you to resume working with a sandbox that was created\n * earlier with a duration-based lifetime.\n *\n * @param id - The ID of the sandbox to reconnect to\n * @param options - Optional auth configuration (for token)\n * @returns A connected sandbox instance\n *\n * @example\n * ```typescript\n * // Resume a sandbox from a stored ID\n * const sandbox = await DenoSandbox.fromId(\"sandbox-abc123\");\n * const result = await sandbox.execute(\"ls -la\");\n * ```\n */\n static async fromId(\n id: string,\n options?: Pick<\n DenoSandboxOptions,\n \"auth\" | \"token\" | \"org\" | \"apiEndpoint\"\n >,\n ): Promise<DenoSandbox> {\n // Resolve token: top-level `token` > deprecated `auth.token` > env variable\n const resolvedToken =\n options?.token ??\n options?.auth?.token ??\n getAuthCredentials(options?.auth).token;\n\n try {\n const existingSandbox = await Sandbox.connect({\n id,\n token: resolvedToken,\n ...(options?.org !== undefined ? { org: options.org } : {}),\n ...(options?.apiEndpoint !== undefined\n ? { apiEndpoint: options.apiEndpoint }\n : {}),\n });\n\n const denoSandbox = new DenoSandbox();\n // Set the existing sandbox directly (bypass initialize)\n denoSandbox.#setFromExisting(existingSandbox, id);\n\n return denoSandbox;\n } catch (error) {\n throw new DenoSandboxError(\n `Sandbox not found: ${id}`,\n \"SANDBOX_NOT_FOUND\",\n error instanceof Error ? error : undefined,\n );\n }\n }\n}\n\n/**\n * Async factory function type for creating Deno Sandbox instances.\n *\n * This is similar to BackendFactory but supports async creation,\n * which is required for Deno Sandbox since initialization is async.\n */\nexport type AsyncDenoSandboxFactory = () => Promise<DenoSandbox>;\n\n/**\n * Create an async factory function that creates a new Deno Sandbox per invocation.\n *\n * Each call to the factory will create and initialize a new sandbox.\n * This is useful when you want fresh, isolated environments for each\n * agent invocation.\n *\n * **Important**: This returns an async factory. For use with middleware that\n * requires synchronous BackendFactory, use `createDenoSandboxFactoryFromSandbox()`\n * with a pre-created sandbox instead.\n *\n * @param options - Optional configuration for sandbox creation\n * @returns An async factory function that creates new sandboxes\n *\n * @example\n * ```typescript\n * import { DenoSandbox, createDenoSandboxFactory } from \"@langchain/deno\";\n *\n * // Create a factory for new sandboxes\n * const factory = createDenoSandboxFactory({ memory: \"1GiB\" });\n *\n * // Each call creates a new sandbox\n * const sandbox1 = await factory();\n * const sandbox2 = await factory();\n *\n * try {\n * // Use sandboxes...\n * } finally {\n * await sandbox1.close();\n * await sandbox2.close();\n * }\n * ```\n */\nexport function createDenoSandboxFactory(\n options?: DenoSandboxOptions,\n): AsyncDenoSandboxFactory {\n return async () => {\n return await DenoSandbox.create(options);\n };\n}\n\n/**\n * Create a backend factory that reuses an existing Deno Sandbox.\n *\n * This allows multiple agent invocations to share the same sandbox,\n * avoiding the startup overhead of creating new sandboxes.\n *\n * Important: You are responsible for managing the sandbox lifecycle\n * (calling `close()` when done).\n *\n * @param sandbox - An existing DenoSandbox instance (must be initialized)\n * @returns A BackendFactory that returns the provided sandbox\n *\n * @example\n * ```typescript\n * import { createDeepAgent, createFilesystemMiddleware } from \"deepagents\";\n * import { DenoSandbox, createDenoSandboxFactoryFromSandbox } from \"@langchain/deno\";\n *\n * // Create and initialize a sandbox\n * const sandbox = await DenoSandbox.create({ memory: \"1GiB\" });\n *\n * try {\n * const agent = createDeepAgent({\n * model: new ChatAnthropic({ model: \"claude-sonnet-4-20250514\" }),\n * systemPrompt: \"You are a coding assistant.\",\n * middlewares: [\n * createFilesystemMiddleware({\n * backend: createDenoSandboxFactoryFromSandbox(sandbox),\n * }),\n * ],\n * });\n *\n * await agent.invoke({ messages: [...] });\n * } finally {\n * await sandbox.close();\n * }\n * ```\n */\nexport function createDenoSandboxFactoryFromSandbox(\n sandbox: DenoSandbox,\n): BackendFactory {\n return () => sandbox;\n}\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { Memory, Region, Sandbox, SecretConfig, SnapshotId, SnapshotSlug, VolumeId, VolumeSlug } from "@deno/sandbox";
2
2
  import { BackendFactory, BaseSandbox, ExecuteResponse, FileDownloadResponse, FileUploadResponse, SandboxError, SandboxErrorCode } from "deepagents";
3
-
4
3
  //#region src/types.d.ts
5
4
  /**
6
5
  * Supported regions for Deno Deploy sandboxes.
@@ -294,7 +293,15 @@ interface DenoSandboxOptions {
294
293
  *
295
294
  * Used to identify specific error conditions and handle them appropriately.
296
295
  */
297
- type DenoSandboxErrorCode = SandboxErrorCode /** Authentication failed - check token configuration */ | "AUTHENTICATION_FAILED" /** Failed to create sandbox - check options and quotas */ | "SANDBOX_CREATION_FAILED" /** Sandbox not found - may have been stopped or expired */ | "SANDBOX_NOT_FOUND" /** Resource limits exceeded (CPU, memory, storage) */ | "RESOURCE_LIMIT_EXCEEDED";
296
+ type DenoSandboxErrorCode = SandboxErrorCode |
297
+ /** Authentication failed - check token configuration */
298
+ "AUTHENTICATION_FAILED" |
299
+ /** Failed to create sandbox - check options and quotas */
300
+ "SANDBOX_CREATION_FAILED" |
301
+ /** Sandbox not found - may have been stopped or expired */
302
+ "SANDBOX_NOT_FOUND" |
303
+ /** Resource limits exceeded (CPU, memory, storage) */
304
+ "RESOURCE_LIMIT_EXCEEDED";
298
305
  declare const DENO_SANDBOX_ERROR_SYMBOL: unique symbol;
299
306
  /**
300
307
  * Custom error class for Deno Sandbox operations.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { Memory, Region, Sandbox, SecretConfig, SnapshotId, SnapshotSlug, VolumeId, VolumeSlug } from "@deno/sandbox";
2
2
  import { BackendFactory, BaseSandbox, ExecuteResponse, FileDownloadResponse, FileUploadResponse, SandboxError, SandboxErrorCode } from "deepagents";
3
-
4
3
  //#region src/types.d.ts
5
4
  /**
6
5
  * Supported regions for Deno Deploy sandboxes.
@@ -294,7 +293,15 @@ interface DenoSandboxOptions {
294
293
  *
295
294
  * Used to identify specific error conditions and handle them appropriately.
296
295
  */
297
- type DenoSandboxErrorCode = SandboxErrorCode /** Authentication failed - check token configuration */ | "AUTHENTICATION_FAILED" /** Failed to create sandbox - check options and quotas */ | "SANDBOX_CREATION_FAILED" /** Sandbox not found - may have been stopped or expired */ | "SANDBOX_NOT_FOUND" /** Resource limits exceeded (CPU, memory, storage) */ | "RESOURCE_LIMIT_EXCEEDED";
296
+ type DenoSandboxErrorCode = SandboxErrorCode |
297
+ /** Authentication failed - check token configuration */
298
+ "AUTHENTICATION_FAILED" |
299
+ /** Failed to create sandbox - check options and quotas */
300
+ "SANDBOX_CREATION_FAILED" |
301
+ /** Sandbox not found - may have been stopped or expired */
302
+ "SANDBOX_NOT_FOUND" |
303
+ /** Resource limits exceeded (CPU, memory, storage) */
304
+ "RESOURCE_LIMIT_EXCEEDED";
298
305
  declare const DENO_SANDBOX_ERROR_SYMBOL: unique symbol;
299
306
  /**
300
307
  * Custom error class for Deno Sandbox operations.
package/dist/index.js CHANGED
@@ -31,6 +31,8 @@ const DENO_SANDBOX_ERROR_SYMBOL = Symbol.for("deno.sandbox.error");
31
31
  * ```
32
32
  */
33
33
  var DenoSandboxError = class DenoSandboxError extends SandboxError {
34
+ code;
35
+ cause;
34
36
  [DENO_SANDBOX_ERROR_SYMBOL];
35
37
  /** Error name for instanceof checks and logging */
36
38
  name = "DenoSandboxError";
@@ -331,8 +333,8 @@ var DenoSandbox = class DenoSandbox extends BaseSandbox {
331
333
  const results = [];
332
334
  for (const [path, content] of files) try {
333
335
  const parentDir = path.substring(0, path.lastIndexOf("/"));
334
- if (parentDir) await (await sandbox.spawn("/bin/bash", {
335
- args: ["-c", `mkdir -p "${parentDir}"`],
336
+ if (parentDir) await (await sandbox.spawn("/bin/mkdir", {
337
+ args: ["-p", parentDir],
336
338
  stdout: "piped",
337
339
  stderr: "piped"
338
340
  })).output();
@@ -375,8 +377,8 @@ var DenoSandbox = class DenoSandbox extends BaseSandbox {
375
377
  const sandbox = this.instance;
376
378
  const results = [];
377
379
  for (const path of paths) try {
378
- const { status, stdoutText } = await (await sandbox.spawn("/bin/bash", {
379
- args: ["-c", `cat "${path}"`],
380
+ const { status, stdoutText } = await (await sandbox.spawn("/bin/cat", {
381
+ args: [path],
380
382
  stdout: "piped",
381
383
  stderr: "piped"
382
384
  })).output();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["#id","#sandbox","#options","#uploadInitialFiles","#mapError","#setFromExisting"],"sources":["../src/types.ts","../src/auth.ts","../src/sandbox.ts"],"sourcesContent":["/**\n * Type definitions for the Deno Sandbox backend.\n *\n * This module contains all type definitions for the @langchain/deno package,\n * including options and error types.\n */\n\nimport type {\n Memory,\n Region,\n SecretConfig,\n SnapshotId,\n SnapshotSlug,\n VolumeId,\n VolumeSlug,\n} from \"@deno/sandbox\";\nimport { type SandboxErrorCode, SandboxError } from \"deepagents\";\n\n/**\n * Supported regions for Deno Deploy sandboxes.\n *\n * Currently available regions:\n * - `ams`: Amsterdam\n * - `ord`: Chicago\n */\nexport type DenoSandboxRegion = Region;\n\n/**\n * Sandbox lifetime configuration.\n *\n * @deprecated Use {@link SandboxTimeout} instead. This type will be removed in a future release.\n *\n * - `\"session\"`: Sandbox shuts down when you close/dispose the client (default)\n * - Duration string: Keep sandbox alive for a specific time (e.g., \"5m\", \"30s\")\n */\nexport type SandboxLifetime = \"session\" | `${number}s` | `${number}m`;\n\n/**\n * Sandbox timeout configuration.\n *\n * - `\"session\"`: Sandbox shuts down when the primary client disconnects (default)\n * - Duration string: Keep sandbox alive for a specific time (e.g., \"600s\", \"20m\")\n *\n * Note: when using a duration, the sandbox will be terminated after the specified\n * time even if clients are still connected.\n */\nexport type SandboxTimeout = \"session\" | `${number}s` | `${number}m`;\n\n/**\n * Configuration options for creating a Deno Sandbox.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * memory: \"1GiB\", // 1GB memory\n * timeout: \"5m\", // 5 minutes\n * region: \"ord\", // Chicago\n * };\n * ```\n */\nexport interface DenoSandboxOptions {\n /**\n * Amount of memory allocated to the sandbox in megabytes.\n *\n * @deprecated Use {@link DenoSandboxOptions.memory} instead. This option will be removed in a future release.\n *\n * Memory limits:\n * - Minimum: 768MB\n * - Maximum: 4096MB\n *\n * @default 768\n */\n memoryMb?: number;\n\n /**\n * The memory size of the sandbox. Supports plain numbers (interpreted as bytes)\n * and human-readable strings with binary (GiB, MiB, KiB) or decimal (GB, MB, kB)\n * units.\n *\n * Takes precedence over the deprecated `memoryMb` option.\n *\n * @example 1342177280\n * @example \"1GiB\"\n * @example \"1280MiB\"\n * @default \"1280MiB\"\n */\n memory?: Memory;\n\n /**\n * Sandbox lifetime configuration.\n *\n * @deprecated Use {@link DenoSandboxOptions.timeout} instead. This option will be removed in a future release.\n *\n * - `\"session\"`: Sandbox shuts down when you close/dispose the client (default)\n * - Duration string: Keep sandbox alive for a specific time (e.g., \"5m\", \"30s\")\n *\n * Supported duration suffixes: `s` (seconds), `m` (minutes).\n *\n * @default \"session\"\n */\n lifetime?: SandboxLifetime;\n\n /**\n * The timeout of the sandbox. When not specified, it defaults to `\"session\"`.\n *\n * Takes precedence over the deprecated `lifetime` option.\n *\n * - `\"session\"`: Sandbox is destroyed when the primary client disconnects.\n * - Duration string: Keep sandbox alive for a specific time (e.g., \"600s\", \"20m\").\n * Note that when this duration has passed, the sandbox will be terminated even\n * if there are still clients connected to it.\n *\n * @example \"session\"\n * @example \"600s\"\n * @example \"20m\"\n * @default \"session\"\n */\n timeout?: SandboxTimeout;\n\n /**\n * Region where the sandbox will be created.\n *\n * If not specified, the sandbox will be created in the default region.\n *\n * @see DenoSandboxRegion for available regions\n */\n region?: DenoSandboxRegion;\n\n /**\n * Initial files to create in the sandbox after initialization.\n *\n * A map of file paths to their contents. Files will be created\n * in the sandbox filesystem before any commands are executed.\n * Parent directories are created automatically.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * memory: \"1GiB\",\n * initialFiles: {\n * \"/home/app/index.js\": \"console.log('Hello')\",\n * \"/home/app/package.json\": '{\"name\": \"test\"}',\n * },\n * };\n * ```\n */\n initialFiles?: Record<string, string>;\n\n /**\n * Authentication configuration for Deno Deploy API.\n *\n * @deprecated Use the top-level {@link DenoSandboxOptions.token} and {@link DenoSandboxOptions.org} options instead.\n * This option will be removed in a future release.\n *\n * ### Environment Variable Setup\n *\n * ```bash\n * # Go to https://app.deno.com -> Settings -> Organization Tokens\n * # Create a new token and set it as environment variable\n * export DENO_DEPLOY_TOKEN=your_token_here\n * ```\n *\n * Or pass the token directly in this auth configuration.\n */\n auth?: {\n /**\n * Deno Deploy access token.\n * If not provided, reads from `DENO_DEPLOY_TOKEN` environment variable.\n */\n token?: string;\n };\n\n /**\n * The Deno Deploy access token that should be used to authenticate requests.\n *\n * - When passing an organization token (starts with `ddo_`), no further\n * organization information is required.\n * - When passing a personal token (starts with `ddp_`), the `org` option\n * must also be provided.\n *\n * If not provided, the `DENO_DEPLOY_TOKEN` environment variable will be used.\n *\n * Takes precedence over the deprecated `auth.token` option.\n */\n token?: string;\n\n /**\n * The Deno Deploy organization slug to operate within.\n *\n * This is required when using a personal access token (starts with `ddp_`).\n * If not provided, the `DENO_DEPLOY_ORG` environment variable will be used.\n */\n org?: string;\n\n /**\n * Environment variables to start the sandbox with, in addition to the default\n * environment variables such as `DENO_DEPLOY_ORGANIZATION_ID`.\n */\n env?: Record<string, string>;\n\n /**\n * Whether to enable debug logging.\n *\n * @default false\n */\n debug?: boolean;\n\n /**\n * Labels to set on the sandbox. Up to 5 labels can be specified.\n * Each label key must be at most 64 bytes, and each label value\n * must be at most 128 bytes.\n */\n labels?: Record<string, string>;\n\n /**\n * A volume or snapshot to use as the root filesystem of the sandbox.\n *\n * If not specified, the default base image will be used. The volume or\n * snapshot must be bootable.\n *\n * - Volumes will be mounted read-write (writes are persisted).\n * - Snapshots will be mounted read-only (writes are not persisted).\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * root: \"my-volume-slug\",\n * };\n * ```\n */\n root?: VolumeId | VolumeSlug | SnapshotId | SnapshotSlug;\n\n /**\n * Volumes to mount on the sandbox.\n *\n * The key is the mount path inside the sandbox, and the value is the\n * volume ID or slug.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * volumes: {\n * \"/data/volume1\": \"volume-slug-or-id-1\",\n * },\n * };\n * ```\n */\n volumes?: Record<string, VolumeId | VolumeSlug>;\n\n /**\n * List of hostnames / IP addresses with optional port numbers that the\n * sandbox can make outbound network requests to.\n *\n * If not specified, no network restrictions are applied.\n *\n * @example []\n * @example [\"example.com\"]\n * @example [\"*.example.com\"]\n * @example [\"example.com:443\"]\n */\n allowNet?: string[];\n\n /**\n * Secret environment variables that are never exposed to sandbox code.\n * The real secret values are injected on the wire when the sandbox makes\n * HTTPS requests to the specified hosts.\n *\n * The key is the environment variable name.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * secrets: {\n * OPENAI_API_KEY: {\n * hosts: [\"api.openai.com\"],\n * value: \"sk-proj-your-real-key\",\n * },\n * },\n * };\n * ```\n */\n secrets?: Record<string, SecretConfig>;\n\n /**\n * Whether to expose SSH access to the sandbox. If true, the sandbox's\n * `ssh` property will be populated once the sandbox is ready.\n *\n * @example\n * ```typescript\n * const sandbox = await DenoSandbox.create({ ssh: true });\n * console.log(sandbox.instance.ssh);\n * // => { username: \"...\", hostname: \"...\" }\n * ```\n */\n ssh?: boolean;\n\n /**\n * The port number to expose for HTTP access. If specified, the sandbox's\n * `url` property will be populated once the sandbox is ready, and can\n * be used to access the sandbox over HTTP.\n *\n * @example\n * ```typescript\n * const sandbox = await DenoSandbox.create({ port: 8080 });\n * console.log(sandbox.instance.url);\n * // => \"http://...\"\n * ```\n */\n port?: number;\n\n /**\n * Override the Sandbox API endpoint URL to use to create and communicate\n * with the sandboxes.\n *\n * The default can also be overridden by setting the `DENO_SANDBOX_ENDPOINT`\n * or `DENO_SANDBOX_BASE_DOMAIN` environment variables.\n */\n sandboxEndpoint?: string | ((region: string) => string);\n\n /**\n * Override the API endpoint to use to connect to Deno Deploy.\n *\n * The default can also be overridden by setting the `DENO_DEPLOY_ENDPOINT`\n * environment variable.\n */\n apiEndpoint?: string;\n}\n\n/**\n * Error codes for Deno Sandbox operations.\n *\n * Used to identify specific error conditions and handle them appropriately.\n */\nexport type DenoSandboxErrorCode =\n | SandboxErrorCode\n /** Authentication failed - check token configuration */\n | \"AUTHENTICATION_FAILED\"\n /** Failed to create sandbox - check options and quotas */\n | \"SANDBOX_CREATION_FAILED\"\n /** Sandbox not found - may have been stopped or expired */\n | \"SANDBOX_NOT_FOUND\"\n /** Resource limits exceeded (CPU, memory, storage) */\n | \"RESOURCE_LIMIT_EXCEEDED\";\n\nconst DENO_SANDBOX_ERROR_SYMBOL = Symbol.for(\"deno.sandbox.error\");\n\n/**\n * Custom error class for Deno Sandbox operations.\n *\n * Provides structured error information including:\n * - Human-readable message\n * - Error code for programmatic handling\n * - Original cause for debugging\n *\n * @example\n * ```typescript\n * try {\n * await sandbox.execute(\"some command\");\n * } catch (error) {\n * if (error instanceof DenoSandboxError) {\n * switch (error.code) {\n * case \"NOT_INITIALIZED\":\n * await sandbox.initialize();\n * break;\n * case \"COMMAND_TIMEOUT\":\n * console.error(\"Command took too long\");\n * break;\n * default:\n * throw error;\n * }\n * }\n * }\n * ```\n */\nexport class DenoSandboxError extends SandboxError {\n [DENO_SANDBOX_ERROR_SYMBOL]: true;\n\n /** Error name for instanceof checks and logging */\n override readonly name = \"DenoSandboxError\";\n\n /**\n * Creates a new DenoSandboxError.\n *\n * @param message - Human-readable error description\n * @param code - Structured error code for programmatic handling\n * @param cause - Original error that caused this error (for debugging)\n */\n constructor(\n message: string,\n public readonly code: DenoSandboxErrorCode,\n public override readonly cause?: Error,\n ) {\n super(message, code as SandboxErrorCode, cause);\n // Maintain proper prototype chain for instanceof checks\n Object.setPrototypeOf(this, DenoSandboxError.prototype);\n }\n\n /**\n * Checks if the error is an instance of DenoSandboxError.\n *\n * @param error - The error to check\n * @returns True if the error is an instance of DenoSandboxError, false otherwise\n */\n static isInstance(error: unknown): error is DenoSandboxError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as Record<symbol, unknown>)[DENO_SANDBOX_ERROR_SYMBOL] === true\n );\n }\n}\n","/**\n * Authentication utilities for Deno Sandbox.\n *\n * This module provides authentication credential resolution for the Deno Sandbox SDK.\n *\n * @packageDocumentation\n */\n\nimport type { DenoSandboxOptions } from \"./types.js\";\nimport { DenoSandboxError } from \"./types.js\";\n\n/**\n * Authentication credentials for Deno Sandbox API.\n */\nexport interface DenoCredentials {\n /** Deno Deploy access token */\n token: string;\n}\n\n/**\n * Get the authentication token for Deno Sandbox API.\n *\n * Authentication is resolved in the following priority order:\n *\n * 1. **Explicit token**: If `options.token` is provided, it is used directly.\n * 2. **DENO_DEPLOY_TOKEN**: Environment variable for Deno Deploy access token.\n *\n * If no token is found, an error is thrown with setup instructions.\n *\n * ## Environment Variable Setup\n *\n * ```bash\n * # Go to https://app.deno.com -> Settings -> Organization Tokens\n * # Create a new token and set it as environment variable\n * export DENO_DEPLOY_TOKEN=your_token_here\n * ```\n *\n * @param options - Optional authentication configuration from DenoSandboxOptions\n * @returns The authentication token string\n * @throws {DenoSandboxError} If no authentication token is available\n *\n * @example\n * ```typescript\n * // With explicit token\n * const token = getAuthToken({ token: \"my-token\" });\n *\n * // Using environment variables (auto-detected)\n * const token = getAuthToken();\n *\n * // From DenoSandboxOptions\n * const options: DenoSandboxOptions = {\n * auth: { token: \"my-token\" }\n * };\n * const token = getAuthToken(options.auth);\n * ```\n */\nexport function getAuthToken(options?: DenoSandboxOptions[\"auth\"]): string {\n // Priority 1: Explicit token in options\n if (options?.token) {\n return options.token;\n }\n\n // Priority 2: DENO_DEPLOY_TOKEN environment variable\n const deployToken = process.env.DENO_DEPLOY_TOKEN;\n if (deployToken) {\n return deployToken;\n }\n\n // No token found - throw descriptive error\n throw new DenoSandboxError(\n \"Deno Deploy authentication required. Provide a token using one of these methods:\\n\\n\" +\n \"1. Set DENO_DEPLOY_TOKEN environment variable:\\n\" +\n \" Go to https://app.deno.com -> Settings -> Organization Tokens\\n\" +\n \" Create a new token and run: export DENO_DEPLOY_TOKEN=your_token_here\\n\\n\" +\n \"2. Pass token directly in options:\\n\" +\n ' new DenoSandbox({ token: \"...\" })',\n \"AUTHENTICATION_FAILED\",\n );\n}\n\n/**\n * Get authentication credentials for Deno Sandbox API.\n *\n * This function returns the credentials needed for the Deno SDK.\n *\n * @param options - Optional authentication configuration from DenoSandboxOptions\n * @returns Complete authentication credentials\n * @throws {DenoSandboxError} If no authentication token is available\n */\nexport function getAuthCredentials(\n options?: DenoSandboxOptions[\"auth\"],\n): DenoCredentials {\n return {\n token: getAuthToken(options),\n };\n}\n","/* eslint-disable no-instanceof/no-instanceof */\n/**\n * Deno Sandbox implementation of the SandboxBackendProtocol.\n *\n * This module provides a Deno Sandbox backend for deepagents, enabling agents\n * to execute commands, read/write files, and manage isolated Linux microVM\n * environments using Deno Deploy's Sandbox infrastructure.\n *\n * @packageDocumentation\n */\n\nimport { Sandbox, type SandboxOptions } from \"@deno/sandbox\";\nimport {\n BaseSandbox,\n type ExecuteResponse,\n type FileDownloadResponse,\n type FileOperationError,\n type FileUploadResponse,\n type BackendFactory,\n} from \"deepagents\";\n\nimport { getAuthCredentials } from \"./auth.js\";\nimport { DenoSandboxError, type DenoSandboxOptions } from \"./types.js\";\n\n/**\n * Deno Sandbox backend for deepagents.\n *\n * Extends `BaseSandbox` to provide command execution, file operations, and\n * sandbox lifecycle management using Deno Deploy's Sandbox SDK.\n *\n * ## Basic Usage\n *\n * ```typescript\n * import { DenoSandbox } from \"@langchain/deno\";\n *\n * // Create and initialize a sandbox\n * const sandbox = await DenoSandbox.create({\n * memory: \"1GiB\",\n * timeout: \"5m\",\n * });\n *\n * try {\n * // Execute commands\n * const result = await sandbox.execute(\"deno --version\");\n * console.log(result.output);\n * } finally {\n * // Always cleanup\n * await sandbox.close();\n * }\n * ```\n *\n * ## Using with DeepAgent\n *\n * ```typescript\n * import { createDeepAgent } from \"deepagents\";\n * import { DenoSandbox } from \"@langchain/deno\";\n *\n * const sandbox = await DenoSandbox.create();\n *\n * const agent = createDeepAgent({\n * model: new ChatAnthropic({ model: \"claude-sonnet-4-20250514\" }),\n * systemPrompt: \"You are a coding assistant with sandbox access.\",\n * backend: sandbox,\n * });\n * ```\n */\nexport class DenoSandbox extends BaseSandbox {\n /** Private reference to the underlying Deno Sandbox instance */\n #sandbox: Sandbox | null = null;\n\n /** Configuration options for this sandbox */\n #options: DenoSandboxOptions;\n\n /** Unique identifier for this sandbox instance */\n #id: string;\n\n /**\n * Get the unique identifier for this sandbox.\n *\n * Before initialization, returns a temporary ID.\n * After initialization, returns the actual Deno sandbox ID.\n */\n get id(): string {\n return this.#id;\n }\n\n /**\n * Get the underlying Deno Sandbox instance.\n *\n * @throws {DenoSandboxError} If the sandbox is not initialized\n *\n * @example\n * ```typescript\n * const sandbox = await DenoSandbox.create();\n * const denoSdk = sandbox.sandbox; // Access the raw SDK\n * ```\n */\n get instance(): Sandbox {\n if (!this.#sandbox) {\n throw new DenoSandboxError(\n \"Sandbox not initialized. Call initialize() or use DenoSandbox.create()\",\n \"NOT_INITIALIZED\",\n );\n }\n return this.#sandbox;\n }\n\n /**\n * Check if the sandbox is initialized and running.\n */\n get isRunning(): boolean {\n return this.#sandbox !== null;\n }\n\n /**\n * Create a new DenoSandbox instance.\n *\n * Note: This only creates the instance. Call `initialize()` to actually\n * create the Deno Sandbox, or use the static `DenoSandbox.create()` method.\n *\n * @param options - Configuration options for the sandbox\n *\n * @example\n * ```typescript\n * // Two-step initialization\n * const sandbox = new DenoSandbox({ memory: \"1GiB\" });\n * await sandbox.initialize();\n *\n * // Or use the factory method\n * const sandbox = await DenoSandbox.create({ memory: \"1GiB\" });\n * ```\n */\n constructor(options: DenoSandboxOptions = {}) {\n super();\n\n this.#options = { ...options };\n\n // Generate temporary ID until initialized\n this.#id = `deno-sandbox-${Date.now()}`;\n }\n\n /**\n * Initialize the sandbox by creating a new Deno Sandbox instance.\n *\n * This method authenticates with Deno Deploy and provisions a new microVM\n * sandbox. After initialization, the `id` property will reflect the\n * actual Deno sandbox ID.\n *\n * @throws {DenoSandboxError} If already initialized (`ALREADY_INITIALIZED`)\n * @throws {DenoSandboxError} If authentication fails (`AUTHENTICATION_FAILED`)\n * @throws {DenoSandboxError} If sandbox creation fails (`SANDBOX_CREATION_FAILED`)\n *\n * @example\n * ```typescript\n * const sandbox = new DenoSandbox();\n * await sandbox.initialize();\n * console.log(`Sandbox ID: ${sandbox.id}`);\n * ```\n */\n async initialize(): Promise<void> {\n // Prevent double initialization\n if (this.#sandbox) {\n throw new DenoSandboxError(\n \"Sandbox is already initialized. Each DenoSandbox instance can only be initialized once.\",\n \"ALREADY_INITIALIZED\",\n );\n }\n\n // Resolve token: top-level `token` > deprecated `auth.token` > env variable\n const resolvedToken =\n this.#options.token ??\n this.#options.auth?.token ??\n getAuthCredentials(this.#options.auth).token;\n\n try {\n // Separate deprecated / custom keys from options that pass through 1:1\n const {\n memoryMb,\n memory,\n lifetime,\n timeout,\n auth: _auth,\n initialFiles: _initialFiles,\n ...passthroughOptions\n } = this.#options;\n\n // Build SDK create options: start with all 1:1 passthrough keys,\n // then layer on the deprecated-to-new mappings.\n const createOptions: SandboxOptions = {\n ...passthroughOptions,\n // `memory` takes precedence over deprecated `memoryMb`\n memory:\n memory ?? (memoryMb !== undefined ? `${memoryMb}MiB` : undefined),\n // `timeout` takes precedence over deprecated `lifetime`\n timeout: timeout ?? lifetime,\n // Resolved token\n token: resolvedToken,\n };\n\n // Create the sandbox\n this.#sandbox = await Sandbox.create(createOptions);\n\n // Update ID to the actual sandbox ID\n this.#id = this.#sandbox.id;\n\n // Upload initial files if provided\n if (this.#options.initialFiles) {\n await this.#uploadInitialFiles(this.#options.initialFiles);\n }\n } catch (error) {\n throw new DenoSandboxError(\n `Failed to create Deno Sandbox: ${error instanceof Error ? error.message : String(error)}`,\n \"SANDBOX_CREATION_FAILED\",\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n /**\n * Upload initial files to the sandbox.\n *\n * @param files - A map of file paths to their string contents\n */\n async #uploadInitialFiles(files: Record<string, string>): Promise<void> {\n const encoder = new TextEncoder();\n const fileEntries: Array<[string, Uint8Array]> = Object.entries(files).map(\n ([path, content]) => [path, encoder.encode(content)],\n );\n\n const results = await this.uploadFiles(fileEntries);\n\n // Check for any errors during upload\n const errors = results.filter((r) => r.error !== null);\n if (errors.length > 0) {\n const errorPaths = errors.map((e) => `${e.path}: ${e.error}`).join(\", \");\n throw new DenoSandboxError(\n `Failed to upload initial files: ${errorPaths}`,\n \"FILE_OPERATION_FAILED\",\n );\n }\n }\n\n /**\n * Execute a command in the sandbox.\n *\n * Commands are run using the sandbox's shell in the configured working directory.\n *\n * @param command - The shell command to execute\n * @returns Execution result with output, exit code, and truncation flag\n * @throws {DenoSandboxError} If the sandbox is not initialized\n *\n * @example\n * ```typescript\n * const result = await sandbox.execute(\"echo 'Hello World'\");\n * console.log(result.output); // \"Hello World\\n\"\n * console.log(result.exitCode); // 0\n * ```\n */\n async execute(command: string): Promise<ExecuteResponse> {\n const sandbox = this.instance; // Throws if not initialized\n\n try {\n // Use spawn with bash to execute the command\n const child = await sandbox.spawn(\"/bin/bash\", {\n args: [\"-c\", command],\n stdout: \"piped\",\n stderr: \"piped\",\n });\n\n // Use output() to get buffered stdout/stderr\n const { status, stdoutText, stderrText } = await child.output();\n\n return {\n output: (stdoutText ?? \"\") + (stderrText ?? \"\"),\n exitCode: status.code ?? 0,\n truncated: false,\n };\n } catch (error) {\n // Check for timeout\n if (error instanceof Error && error.message.includes(\"timeout\")) {\n throw new DenoSandboxError(\n `Command timed out: ${command}`,\n \"COMMAND_TIMEOUT\",\n error,\n );\n }\n\n throw new DenoSandboxError(\n `Command execution failed: ${error instanceof Error ? error.message : String(error)}`,\n \"COMMAND_FAILED\",\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n /**\n * Upload files to the sandbox.\n *\n * Files are written to the sandbox filesystem. Parent directories are\n * created automatically if they don't exist.\n *\n * @param files - Array of [path, content] tuples to upload\n * @returns Upload result for each file, with success or error status\n *\n * @example\n * ```typescript\n * const encoder = new TextEncoder();\n * const results = await sandbox.uploadFiles([\n * [\"src/index.js\", encoder.encode(\"console.log('Hello')\")],\n * [\"package.json\", encoder.encode('{\"name\": \"test\"}')],\n * ]);\n * ```\n */\n async uploadFiles(\n files: Array<[string, Uint8Array]>,\n ): Promise<FileUploadResponse[]> {\n const sandbox = this.instance; // Throws if not initialized\n const results: FileUploadResponse[] = [];\n\n for (const [path, content] of files) {\n try {\n // Ensure parent directory exists using spawn (more reliable than sh template)\n const parentDir = path.substring(0, path.lastIndexOf(\"/\"));\n if (parentDir) {\n const mkdirChild = await sandbox.spawn(\"/bin/bash\", {\n args: [\"-c\", `mkdir -p \"${parentDir}\"`],\n stdout: \"piped\",\n stderr: \"piped\",\n });\n await mkdirChild.output();\n }\n\n // Write the file content\n const textContent = new TextDecoder().decode(content);\n await sandbox.fs.writeTextFile(path, textContent);\n results.push({ path, error: null });\n } catch (error) {\n results.push({ path, error: this.#mapError(error) });\n }\n }\n\n return results;\n }\n\n /**\n * Download files from the sandbox.\n *\n * Each file is read individually, allowing partial success when some\n * files exist and others don't.\n *\n * @param paths - Array of file paths to download\n * @returns Download result for each file, with content or error\n *\n * @example\n * ```typescript\n * const results = await sandbox.downloadFiles([\"src/index.js\", \"missing.txt\"]);\n * for (const result of results) {\n * if (result.content) {\n * console.log(new TextDecoder().decode(result.content));\n * } else {\n * console.error(`Error: ${result.error}`);\n * }\n * }\n * ```\n */\n async downloadFiles(paths: string[]): Promise<FileDownloadResponse[]> {\n const sandbox = this.instance; // Throws if not initialized\n const results: FileDownloadResponse[] = [];\n\n for (const path of paths) {\n try {\n // Use spawn with bash to read file content (same approach as execute())\n const child = await sandbox.spawn(\"/bin/bash\", {\n args: [\"-c\", `cat \"${path}\"`],\n stdout: \"piped\",\n stderr: \"piped\",\n });\n\n const { status, stdoutText } = await child.output();\n\n if (!status.success) {\n results.push({\n path,\n content: null,\n error: \"file_not_found\",\n });\n } else {\n const content = new TextEncoder().encode(stdoutText ?? \"\");\n results.push({\n path,\n content,\n error: null,\n });\n }\n } catch (error) {\n results.push({\n path,\n content: null,\n error: this.#mapError(error),\n });\n }\n }\n\n return results;\n }\n\n /**\n * Close the sandbox and release all resources.\n *\n * After closing, the sandbox cannot be used again. Any unsaved data\n * will be lost.\n *\n * @example\n * ```typescript\n * try {\n * await sandbox.execute(\"deno run build.ts\");\n * } finally {\n * await sandbox.close();\n * }\n * ```\n */\n async close(): Promise<void> {\n if (this.#sandbox) {\n try {\n await this.#sandbox.close();\n } finally {\n this.#sandbox = null;\n }\n }\n }\n\n /**\n * Forcefully terminate the sandbox.\n *\n * Use this when you need to immediately stop the sandbox, even if\n * operations are in progress.\n *\n * @example\n * ```typescript\n * await sandbox.kill();\n * ```\n */\n async kill(): Promise<void> {\n if (this.#sandbox) {\n try {\n await this.#sandbox.kill();\n } finally {\n this.#sandbox = null;\n }\n }\n }\n\n /**\n * Alias for close() to maintain compatibility with other sandbox implementations.\n */\n async stop(): Promise<void> {\n await this.close();\n }\n\n /**\n * Set the sandbox from an existing Deno Sandbox instance.\n * Used internally by the static `connect()` method.\n */\n #setFromExisting(existingSandbox: Sandbox, sandboxId: string): void {\n this.#sandbox = existingSandbox;\n this.#id = sandboxId;\n }\n\n /**\n * Map Deno SDK errors to standardized FileOperationError codes.\n *\n * @param error - The error from the Deno SDK\n * @returns A standardized error code\n */\n #mapError(error: unknown): FileOperationError {\n if (error instanceof Error) {\n const msg = error.message.toLowerCase();\n\n if (msg.includes(\"not found\") || msg.includes(\"enoent\")) {\n return \"file_not_found\";\n }\n if (msg.includes(\"permission\") || msg.includes(\"eacces\")) {\n return \"permission_denied\";\n }\n if (msg.includes(\"directory\") || msg.includes(\"eisdir\")) {\n return \"is_directory\";\n }\n }\n\n return \"invalid_path\";\n }\n\n /**\n * Create and initialize a new DenoSandbox in one step.\n *\n * This is the recommended way to create a sandbox. It combines\n * construction and initialization into a single async operation.\n *\n * @param options - Configuration options for the sandbox\n * @returns An initialized and ready-to-use sandbox\n *\n * @example\n * ```typescript\n * const sandbox = await DenoSandbox.create({\n * memory: \"1GiB\",\n * timeout: \"10m\",\n * region: \"ord\",\n * });\n * ```\n */\n static async create(options?: DenoSandboxOptions): Promise<DenoSandbox> {\n const sandbox = new DenoSandbox(options);\n await sandbox.initialize();\n return sandbox;\n }\n\n /**\n * Reconnect to an existing sandbox by ID.\n *\n * This allows you to resume working with a sandbox that was created\n * earlier with a duration-based lifetime.\n *\n * @param id - The ID of the sandbox to reconnect to\n * @param options - Optional auth configuration (for token)\n * @returns A connected sandbox instance\n *\n * @example\n * ```typescript\n * // Resume a sandbox from a stored ID\n * const sandbox = await DenoSandbox.fromId(\"sandbox-abc123\");\n * const result = await sandbox.execute(\"ls -la\");\n * ```\n */\n static async fromId(\n id: string,\n options?: Pick<\n DenoSandboxOptions,\n \"auth\" | \"token\" | \"org\" | \"apiEndpoint\"\n >,\n ): Promise<DenoSandbox> {\n // Resolve token: top-level `token` > deprecated `auth.token` > env variable\n const resolvedToken =\n options?.token ??\n options?.auth?.token ??\n getAuthCredentials(options?.auth).token;\n\n try {\n const existingSandbox = await Sandbox.connect({\n id,\n token: resolvedToken,\n ...(options?.org !== undefined ? { org: options.org } : {}),\n ...(options?.apiEndpoint !== undefined\n ? { apiEndpoint: options.apiEndpoint }\n : {}),\n });\n\n const denoSandbox = new DenoSandbox();\n // Set the existing sandbox directly (bypass initialize)\n denoSandbox.#setFromExisting(existingSandbox, id);\n\n return denoSandbox;\n } catch (error) {\n throw new DenoSandboxError(\n `Sandbox not found: ${id}`,\n \"SANDBOX_NOT_FOUND\",\n error instanceof Error ? error : undefined,\n );\n }\n }\n}\n\n/**\n * Async factory function type for creating Deno Sandbox instances.\n *\n * This is similar to BackendFactory but supports async creation,\n * which is required for Deno Sandbox since initialization is async.\n */\nexport type AsyncDenoSandboxFactory = () => Promise<DenoSandbox>;\n\n/**\n * Create an async factory function that creates a new Deno Sandbox per invocation.\n *\n * Each call to the factory will create and initialize a new sandbox.\n * This is useful when you want fresh, isolated environments for each\n * agent invocation.\n *\n * **Important**: This returns an async factory. For use with middleware that\n * requires synchronous BackendFactory, use `createDenoSandboxFactoryFromSandbox()`\n * with a pre-created sandbox instead.\n *\n * @param options - Optional configuration for sandbox creation\n * @returns An async factory function that creates new sandboxes\n *\n * @example\n * ```typescript\n * import { DenoSandbox, createDenoSandboxFactory } from \"@langchain/deno\";\n *\n * // Create a factory for new sandboxes\n * const factory = createDenoSandboxFactory({ memory: \"1GiB\" });\n *\n * // Each call creates a new sandbox\n * const sandbox1 = await factory();\n * const sandbox2 = await factory();\n *\n * try {\n * // Use sandboxes...\n * } finally {\n * await sandbox1.close();\n * await sandbox2.close();\n * }\n * ```\n */\nexport function createDenoSandboxFactory(\n options?: DenoSandboxOptions,\n): AsyncDenoSandboxFactory {\n return async () => {\n return await DenoSandbox.create(options);\n };\n}\n\n/**\n * Create a backend factory that reuses an existing Deno Sandbox.\n *\n * This allows multiple agent invocations to share the same sandbox,\n * avoiding the startup overhead of creating new sandboxes.\n *\n * Important: You are responsible for managing the sandbox lifecycle\n * (calling `close()` when done).\n *\n * @param sandbox - An existing DenoSandbox instance (must be initialized)\n * @returns A BackendFactory that returns the provided sandbox\n *\n * @example\n * ```typescript\n * import { createDeepAgent, createFilesystemMiddleware } from \"deepagents\";\n * import { DenoSandbox, createDenoSandboxFactoryFromSandbox } from \"@langchain/deno\";\n *\n * // Create and initialize a sandbox\n * const sandbox = await DenoSandbox.create({ memory: \"1GiB\" });\n *\n * try {\n * const agent = createDeepAgent({\n * model: new ChatAnthropic({ model: \"claude-sonnet-4-20250514\" }),\n * systemPrompt: \"You are a coding assistant.\",\n * middlewares: [\n * createFilesystemMiddleware({\n * backend: createDenoSandboxFactoryFromSandbox(sandbox),\n * }),\n * ],\n * });\n *\n * await agent.invoke({ messages: [...] });\n * } finally {\n * await sandbox.close();\n * }\n * ```\n */\nexport function createDenoSandboxFactoryFromSandbox(\n sandbox: DenoSandbox,\n): BackendFactory {\n return () => sandbox;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["#id","#sandbox","#options","#uploadInitialFiles","#mapError","#setFromExisting"],"sources":["../src/types.ts","../src/auth.ts","../src/sandbox.ts"],"sourcesContent":["/**\n * Type definitions for the Deno Sandbox backend.\n *\n * This module contains all type definitions for the @langchain/deno package,\n * including options and error types.\n */\n\nimport type {\n Memory,\n Region,\n SecretConfig,\n SnapshotId,\n SnapshotSlug,\n VolumeId,\n VolumeSlug,\n} from \"@deno/sandbox\";\nimport { type SandboxErrorCode, SandboxError } from \"deepagents\";\n\n/**\n * Supported regions for Deno Deploy sandboxes.\n *\n * Currently available regions:\n * - `ams`: Amsterdam\n * - `ord`: Chicago\n */\nexport type DenoSandboxRegion = Region;\n\n/**\n * Sandbox lifetime configuration.\n *\n * @deprecated Use {@link SandboxTimeout} instead. This type will be removed in a future release.\n *\n * - `\"session\"`: Sandbox shuts down when you close/dispose the client (default)\n * - Duration string: Keep sandbox alive for a specific time (e.g., \"5m\", \"30s\")\n */\nexport type SandboxLifetime = \"session\" | `${number}s` | `${number}m`;\n\n/**\n * Sandbox timeout configuration.\n *\n * - `\"session\"`: Sandbox shuts down when the primary client disconnects (default)\n * - Duration string: Keep sandbox alive for a specific time (e.g., \"600s\", \"20m\")\n *\n * Note: when using a duration, the sandbox will be terminated after the specified\n * time even if clients are still connected.\n */\nexport type SandboxTimeout = \"session\" | `${number}s` | `${number}m`;\n\n/**\n * Configuration options for creating a Deno Sandbox.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * memory: \"1GiB\", // 1GB memory\n * timeout: \"5m\", // 5 minutes\n * region: \"ord\", // Chicago\n * };\n * ```\n */\nexport interface DenoSandboxOptions {\n /**\n * Amount of memory allocated to the sandbox in megabytes.\n *\n * @deprecated Use {@link DenoSandboxOptions.memory} instead. This option will be removed in a future release.\n *\n * Memory limits:\n * - Minimum: 768MB\n * - Maximum: 4096MB\n *\n * @default 768\n */\n memoryMb?: number;\n\n /**\n * The memory size of the sandbox. Supports plain numbers (interpreted as bytes)\n * and human-readable strings with binary (GiB, MiB, KiB) or decimal (GB, MB, kB)\n * units.\n *\n * Takes precedence over the deprecated `memoryMb` option.\n *\n * @example 1342177280\n * @example \"1GiB\"\n * @example \"1280MiB\"\n * @default \"1280MiB\"\n */\n memory?: Memory;\n\n /**\n * Sandbox lifetime configuration.\n *\n * @deprecated Use {@link DenoSandboxOptions.timeout} instead. This option will be removed in a future release.\n *\n * - `\"session\"`: Sandbox shuts down when you close/dispose the client (default)\n * - Duration string: Keep sandbox alive for a specific time (e.g., \"5m\", \"30s\")\n *\n * Supported duration suffixes: `s` (seconds), `m` (minutes).\n *\n * @default \"session\"\n */\n lifetime?: SandboxLifetime;\n\n /**\n * The timeout of the sandbox. When not specified, it defaults to `\"session\"`.\n *\n * Takes precedence over the deprecated `lifetime` option.\n *\n * - `\"session\"`: Sandbox is destroyed when the primary client disconnects.\n * - Duration string: Keep sandbox alive for a specific time (e.g., \"600s\", \"20m\").\n * Note that when this duration has passed, the sandbox will be terminated even\n * if there are still clients connected to it.\n *\n * @example \"session\"\n * @example \"600s\"\n * @example \"20m\"\n * @default \"session\"\n */\n timeout?: SandboxTimeout;\n\n /**\n * Region where the sandbox will be created.\n *\n * If not specified, the sandbox will be created in the default region.\n *\n * @see DenoSandboxRegion for available regions\n */\n region?: DenoSandboxRegion;\n\n /**\n * Initial files to create in the sandbox after initialization.\n *\n * A map of file paths to their contents. Files will be created\n * in the sandbox filesystem before any commands are executed.\n * Parent directories are created automatically.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * memory: \"1GiB\",\n * initialFiles: {\n * \"/home/app/index.js\": \"console.log('Hello')\",\n * \"/home/app/package.json\": '{\"name\": \"test\"}',\n * },\n * };\n * ```\n */\n initialFiles?: Record<string, string>;\n\n /**\n * Authentication configuration for Deno Deploy API.\n *\n * @deprecated Use the top-level {@link DenoSandboxOptions.token} and {@link DenoSandboxOptions.org} options instead.\n * This option will be removed in a future release.\n *\n * ### Environment Variable Setup\n *\n * ```bash\n * # Go to https://app.deno.com -> Settings -> Organization Tokens\n * # Create a new token and set it as environment variable\n * export DENO_DEPLOY_TOKEN=your_token_here\n * ```\n *\n * Or pass the token directly in this auth configuration.\n */\n auth?: {\n /**\n * Deno Deploy access token.\n * If not provided, reads from `DENO_DEPLOY_TOKEN` environment variable.\n */\n token?: string;\n };\n\n /**\n * The Deno Deploy access token that should be used to authenticate requests.\n *\n * - When passing an organization token (starts with `ddo_`), no further\n * organization information is required.\n * - When passing a personal token (starts with `ddp_`), the `org` option\n * must also be provided.\n *\n * If not provided, the `DENO_DEPLOY_TOKEN` environment variable will be used.\n *\n * Takes precedence over the deprecated `auth.token` option.\n */\n token?: string;\n\n /**\n * The Deno Deploy organization slug to operate within.\n *\n * This is required when using a personal access token (starts with `ddp_`).\n * If not provided, the `DENO_DEPLOY_ORG` environment variable will be used.\n */\n org?: string;\n\n /**\n * Environment variables to start the sandbox with, in addition to the default\n * environment variables such as `DENO_DEPLOY_ORGANIZATION_ID`.\n */\n env?: Record<string, string>;\n\n /**\n * Whether to enable debug logging.\n *\n * @default false\n */\n debug?: boolean;\n\n /**\n * Labels to set on the sandbox. Up to 5 labels can be specified.\n * Each label key must be at most 64 bytes, and each label value\n * must be at most 128 bytes.\n */\n labels?: Record<string, string>;\n\n /**\n * A volume or snapshot to use as the root filesystem of the sandbox.\n *\n * If not specified, the default base image will be used. The volume or\n * snapshot must be bootable.\n *\n * - Volumes will be mounted read-write (writes are persisted).\n * - Snapshots will be mounted read-only (writes are not persisted).\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * root: \"my-volume-slug\",\n * };\n * ```\n */\n root?: VolumeId | VolumeSlug | SnapshotId | SnapshotSlug;\n\n /**\n * Volumes to mount on the sandbox.\n *\n * The key is the mount path inside the sandbox, and the value is the\n * volume ID or slug.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * volumes: {\n * \"/data/volume1\": \"volume-slug-or-id-1\",\n * },\n * };\n * ```\n */\n volumes?: Record<string, VolumeId | VolumeSlug>;\n\n /**\n * List of hostnames / IP addresses with optional port numbers that the\n * sandbox can make outbound network requests to.\n *\n * If not specified, no network restrictions are applied.\n *\n * @example []\n * @example [\"example.com\"]\n * @example [\"*.example.com\"]\n * @example [\"example.com:443\"]\n */\n allowNet?: string[];\n\n /**\n * Secret environment variables that are never exposed to sandbox code.\n * The real secret values are injected on the wire when the sandbox makes\n * HTTPS requests to the specified hosts.\n *\n * The key is the environment variable name.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * secrets: {\n * OPENAI_API_KEY: {\n * hosts: [\"api.openai.com\"],\n * value: \"sk-proj-your-real-key\",\n * },\n * },\n * };\n * ```\n */\n secrets?: Record<string, SecretConfig>;\n\n /**\n * Whether to expose SSH access to the sandbox. If true, the sandbox's\n * `ssh` property will be populated once the sandbox is ready.\n *\n * @example\n * ```typescript\n * const sandbox = await DenoSandbox.create({ ssh: true });\n * console.log(sandbox.instance.ssh);\n * // => { username: \"...\", hostname: \"...\" }\n * ```\n */\n ssh?: boolean;\n\n /**\n * The port number to expose for HTTP access. If specified, the sandbox's\n * `url` property will be populated once the sandbox is ready, and can\n * be used to access the sandbox over HTTP.\n *\n * @example\n * ```typescript\n * const sandbox = await DenoSandbox.create({ port: 8080 });\n * console.log(sandbox.instance.url);\n * // => \"http://...\"\n * ```\n */\n port?: number;\n\n /**\n * Override the Sandbox API endpoint URL to use to create and communicate\n * with the sandboxes.\n *\n * The default can also be overridden by setting the `DENO_SANDBOX_ENDPOINT`\n * or `DENO_SANDBOX_BASE_DOMAIN` environment variables.\n */\n sandboxEndpoint?: string | ((region: string) => string);\n\n /**\n * Override the API endpoint to use to connect to Deno Deploy.\n *\n * The default can also be overridden by setting the `DENO_DEPLOY_ENDPOINT`\n * environment variable.\n */\n apiEndpoint?: string;\n}\n\n/**\n * Error codes for Deno Sandbox operations.\n *\n * Used to identify specific error conditions and handle them appropriately.\n */\nexport type DenoSandboxErrorCode =\n | SandboxErrorCode\n /** Authentication failed - check token configuration */\n | \"AUTHENTICATION_FAILED\"\n /** Failed to create sandbox - check options and quotas */\n | \"SANDBOX_CREATION_FAILED\"\n /** Sandbox not found - may have been stopped or expired */\n | \"SANDBOX_NOT_FOUND\"\n /** Resource limits exceeded (CPU, memory, storage) */\n | \"RESOURCE_LIMIT_EXCEEDED\";\n\nconst DENO_SANDBOX_ERROR_SYMBOL = Symbol.for(\"deno.sandbox.error\");\n\n/**\n * Custom error class for Deno Sandbox operations.\n *\n * Provides structured error information including:\n * - Human-readable message\n * - Error code for programmatic handling\n * - Original cause for debugging\n *\n * @example\n * ```typescript\n * try {\n * await sandbox.execute(\"some command\");\n * } catch (error) {\n * if (error instanceof DenoSandboxError) {\n * switch (error.code) {\n * case \"NOT_INITIALIZED\":\n * await sandbox.initialize();\n * break;\n * case \"COMMAND_TIMEOUT\":\n * console.error(\"Command took too long\");\n * break;\n * default:\n * throw error;\n * }\n * }\n * }\n * ```\n */\nexport class DenoSandboxError extends SandboxError {\n [DENO_SANDBOX_ERROR_SYMBOL]: true;\n\n /** Error name for instanceof checks and logging */\n override readonly name = \"DenoSandboxError\";\n\n /**\n * Creates a new DenoSandboxError.\n *\n * @param message - Human-readable error description\n * @param code - Structured error code for programmatic handling\n * @param cause - Original error that caused this error (for debugging)\n */\n constructor(\n message: string,\n public readonly code: DenoSandboxErrorCode,\n public override readonly cause?: Error,\n ) {\n super(message, code as SandboxErrorCode, cause);\n // Maintain proper prototype chain for instanceof checks\n Object.setPrototypeOf(this, DenoSandboxError.prototype);\n }\n\n /**\n * Checks if the error is an instance of DenoSandboxError.\n *\n * @param error - The error to check\n * @returns True if the error is an instance of DenoSandboxError, false otherwise\n */\n static isInstance(error: unknown): error is DenoSandboxError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as Record<symbol, unknown>)[DENO_SANDBOX_ERROR_SYMBOL] === true\n );\n }\n}\n","/**\n * Authentication utilities for Deno Sandbox.\n *\n * This module provides authentication credential resolution for the Deno Sandbox SDK.\n *\n * @packageDocumentation\n */\n\nimport type { DenoSandboxOptions } from \"./types.js\";\nimport { DenoSandboxError } from \"./types.js\";\n\n/**\n * Authentication credentials for Deno Sandbox API.\n */\nexport interface DenoCredentials {\n /** Deno Deploy access token */\n token: string;\n}\n\n/**\n * Get the authentication token for Deno Sandbox API.\n *\n * Authentication is resolved in the following priority order:\n *\n * 1. **Explicit token**: If `options.token` is provided, it is used directly.\n * 2. **DENO_DEPLOY_TOKEN**: Environment variable for Deno Deploy access token.\n *\n * If no token is found, an error is thrown with setup instructions.\n *\n * ## Environment Variable Setup\n *\n * ```bash\n * # Go to https://app.deno.com -> Settings -> Organization Tokens\n * # Create a new token and set it as environment variable\n * export DENO_DEPLOY_TOKEN=your_token_here\n * ```\n *\n * @param options - Optional authentication configuration from DenoSandboxOptions\n * @returns The authentication token string\n * @throws {DenoSandboxError} If no authentication token is available\n *\n * @example\n * ```typescript\n * // With explicit token\n * const token = getAuthToken({ token: \"my-token\" });\n *\n * // Using environment variables (auto-detected)\n * const token = getAuthToken();\n *\n * // From DenoSandboxOptions\n * const options: DenoSandboxOptions = {\n * auth: { token: \"my-token\" }\n * };\n * const token = getAuthToken(options.auth);\n * ```\n */\nexport function getAuthToken(options?: DenoSandboxOptions[\"auth\"]): string {\n // Priority 1: Explicit token in options\n if (options?.token) {\n return options.token;\n }\n\n // Priority 2: DENO_DEPLOY_TOKEN environment variable\n const deployToken = process.env.DENO_DEPLOY_TOKEN;\n if (deployToken) {\n return deployToken;\n }\n\n // No token found - throw descriptive error\n throw new DenoSandboxError(\n \"Deno Deploy authentication required. Provide a token using one of these methods:\\n\\n\" +\n \"1. Set DENO_DEPLOY_TOKEN environment variable:\\n\" +\n \" Go to https://app.deno.com -> Settings -> Organization Tokens\\n\" +\n \" Create a new token and run: export DENO_DEPLOY_TOKEN=your_token_here\\n\\n\" +\n \"2. Pass token directly in options:\\n\" +\n ' new DenoSandbox({ token: \"...\" })',\n \"AUTHENTICATION_FAILED\",\n );\n}\n\n/**\n * Get authentication credentials for Deno Sandbox API.\n *\n * This function returns the credentials needed for the Deno SDK.\n *\n * @param options - Optional authentication configuration from DenoSandboxOptions\n * @returns Complete authentication credentials\n * @throws {DenoSandboxError} If no authentication token is available\n */\nexport function getAuthCredentials(\n options?: DenoSandboxOptions[\"auth\"],\n): DenoCredentials {\n return {\n token: getAuthToken(options),\n };\n}\n","/* oxlint-disable no-instanceof/no-instanceof */\n/**\n * Deno Sandbox implementation of the SandboxBackendProtocol.\n *\n * This module provides a Deno Sandbox backend for deepagents, enabling agents\n * to execute commands, read/write files, and manage isolated Linux microVM\n * environments using Deno Deploy's Sandbox infrastructure.\n *\n * @packageDocumentation\n */\n\nimport { Sandbox, type SandboxOptions } from \"@deno/sandbox\";\nimport {\n BaseSandbox,\n type ExecuteResponse,\n type FileDownloadResponse,\n type FileOperationError,\n type FileUploadResponse,\n type BackendFactory,\n} from \"deepagents\";\n\nimport { getAuthCredentials } from \"./auth.js\";\nimport { DenoSandboxError, type DenoSandboxOptions } from \"./types.js\";\n\n/**\n * Deno Sandbox backend for deepagents.\n *\n * Extends `BaseSandbox` to provide command execution, file operations, and\n * sandbox lifecycle management using Deno Deploy's Sandbox SDK.\n *\n * ## Basic Usage\n *\n * ```typescript\n * import { DenoSandbox } from \"@langchain/deno\";\n *\n * // Create and initialize a sandbox\n * const sandbox = await DenoSandbox.create({\n * memory: \"1GiB\",\n * timeout: \"5m\",\n * });\n *\n * try {\n * // Execute commands\n * const result = await sandbox.execute(\"deno --version\");\n * console.log(result.output);\n * } finally {\n * // Always cleanup\n * await sandbox.close();\n * }\n * ```\n *\n * ## Using with DeepAgent\n *\n * ```typescript\n * import { createDeepAgent } from \"deepagents\";\n * import { DenoSandbox } from \"@langchain/deno\";\n *\n * const sandbox = await DenoSandbox.create();\n *\n * const agent = createDeepAgent({\n * model: new ChatAnthropic({ model: \"claude-sonnet-4-20250514\" }),\n * systemPrompt: \"You are a coding assistant with sandbox access.\",\n * backend: sandbox,\n * });\n * ```\n */\nexport class DenoSandbox extends BaseSandbox {\n /** Private reference to the underlying Deno Sandbox instance */\n #sandbox: Sandbox | null = null;\n\n /** Configuration options for this sandbox */\n #options: DenoSandboxOptions;\n\n /** Unique identifier for this sandbox instance */\n #id: string;\n\n /**\n * Get the unique identifier for this sandbox.\n *\n * Before initialization, returns a temporary ID.\n * After initialization, returns the actual Deno sandbox ID.\n */\n get id(): string {\n return this.#id;\n }\n\n /**\n * Get the underlying Deno Sandbox instance.\n *\n * @throws {DenoSandboxError} If the sandbox is not initialized\n *\n * @example\n * ```typescript\n * const sandbox = await DenoSandbox.create();\n * const denoSdk = sandbox.sandbox; // Access the raw SDK\n * ```\n */\n get instance(): Sandbox {\n if (!this.#sandbox) {\n throw new DenoSandboxError(\n \"Sandbox not initialized. Call initialize() or use DenoSandbox.create()\",\n \"NOT_INITIALIZED\",\n );\n }\n return this.#sandbox;\n }\n\n /**\n * Check if the sandbox is initialized and running.\n */\n get isRunning(): boolean {\n return this.#sandbox !== null;\n }\n\n /**\n * Create a new DenoSandbox instance.\n *\n * Note: This only creates the instance. Call `initialize()` to actually\n * create the Deno Sandbox, or use the static `DenoSandbox.create()` method.\n *\n * @param options - Configuration options for the sandbox\n *\n * @example\n * ```typescript\n * // Two-step initialization\n * const sandbox = new DenoSandbox({ memory: \"1GiB\" });\n * await sandbox.initialize();\n *\n * // Or use the factory method\n * const sandbox = await DenoSandbox.create({ memory: \"1GiB\" });\n * ```\n */\n constructor(options: DenoSandboxOptions = {}) {\n super();\n\n this.#options = { ...options };\n\n // Generate temporary ID until initialized\n this.#id = `deno-sandbox-${Date.now()}`;\n }\n\n /**\n * Initialize the sandbox by creating a new Deno Sandbox instance.\n *\n * This method authenticates with Deno Deploy and provisions a new microVM\n * sandbox. After initialization, the `id` property will reflect the\n * actual Deno sandbox ID.\n *\n * @throws {DenoSandboxError} If already initialized (`ALREADY_INITIALIZED`)\n * @throws {DenoSandboxError} If authentication fails (`AUTHENTICATION_FAILED`)\n * @throws {DenoSandboxError} If sandbox creation fails (`SANDBOX_CREATION_FAILED`)\n *\n * @example\n * ```typescript\n * const sandbox = new DenoSandbox();\n * await sandbox.initialize();\n * console.log(`Sandbox ID: ${sandbox.id}`);\n * ```\n */\n async initialize(): Promise<void> {\n // Prevent double initialization\n if (this.#sandbox) {\n throw new DenoSandboxError(\n \"Sandbox is already initialized. Each DenoSandbox instance can only be initialized once.\",\n \"ALREADY_INITIALIZED\",\n );\n }\n\n // Resolve token: top-level `token` > deprecated `auth.token` > env variable\n const resolvedToken =\n this.#options.token ??\n this.#options.auth?.token ??\n getAuthCredentials(this.#options.auth).token;\n\n try {\n // Separate deprecated / custom keys from options that pass through 1:1\n const {\n memoryMb,\n memory,\n lifetime,\n timeout,\n auth: _auth,\n initialFiles: _initialFiles,\n ...passthroughOptions\n } = this.#options;\n\n // Build SDK create options: start with all 1:1 passthrough keys,\n // then layer on the deprecated-to-new mappings.\n const createOptions: SandboxOptions = {\n ...passthroughOptions,\n // `memory` takes precedence over deprecated `memoryMb`\n memory:\n memory ?? (memoryMb !== undefined ? `${memoryMb}MiB` : undefined),\n // `timeout` takes precedence over deprecated `lifetime`\n timeout: timeout ?? lifetime,\n // Resolved token\n token: resolvedToken,\n };\n\n // Create the sandbox\n this.#sandbox = await Sandbox.create(createOptions);\n\n // Update ID to the actual sandbox ID\n this.#id = this.#sandbox.id;\n\n // Upload initial files if provided\n if (this.#options.initialFiles) {\n await this.#uploadInitialFiles(this.#options.initialFiles);\n }\n } catch (error) {\n throw new DenoSandboxError(\n `Failed to create Deno Sandbox: ${error instanceof Error ? error.message : String(error)}`,\n \"SANDBOX_CREATION_FAILED\",\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n /**\n * Upload initial files to the sandbox.\n *\n * @param files - A map of file paths to their string contents\n */\n async #uploadInitialFiles(files: Record<string, string>): Promise<void> {\n const encoder = new TextEncoder();\n const fileEntries: Array<[string, Uint8Array]> = Object.entries(files).map(\n ([path, content]) => [path, encoder.encode(content)],\n );\n\n const results = await this.uploadFiles(fileEntries);\n\n // Check for any errors during upload\n const errors = results.filter((r) => r.error !== null);\n if (errors.length > 0) {\n const errorPaths = errors.map((e) => `${e.path}: ${e.error}`).join(\", \");\n throw new DenoSandboxError(\n `Failed to upload initial files: ${errorPaths}`,\n \"FILE_OPERATION_FAILED\",\n );\n }\n }\n\n /**\n * Execute a command in the sandbox.\n *\n * Commands are run using the sandbox's shell in the configured working directory.\n *\n * @param command - The shell command to execute\n * @returns Execution result with output, exit code, and truncation flag\n * @throws {DenoSandboxError} If the sandbox is not initialized\n *\n * @example\n * ```typescript\n * const result = await sandbox.execute(\"echo 'Hello World'\");\n * console.log(result.output); // \"Hello World\\n\"\n * console.log(result.exitCode); // 0\n * ```\n */\n async execute(command: string): Promise<ExecuteResponse> {\n const sandbox = this.instance; // Throws if not initialized\n\n try {\n // Use spawn with bash to execute the command\n const child = await sandbox.spawn(\"/bin/bash\", {\n args: [\"-c\", command],\n stdout: \"piped\",\n stderr: \"piped\",\n });\n\n // Use output() to get buffered stdout/stderr\n const { status, stdoutText, stderrText } = await child.output();\n\n return {\n output: (stdoutText ?? \"\") + (stderrText ?? \"\"),\n exitCode: status.code ?? 0,\n truncated: false,\n };\n } catch (error) {\n // Check for timeout\n if (error instanceof Error && error.message.includes(\"timeout\")) {\n throw new DenoSandboxError(\n `Command timed out: ${command}`,\n \"COMMAND_TIMEOUT\",\n error,\n );\n }\n\n throw new DenoSandboxError(\n `Command execution failed: ${error instanceof Error ? error.message : String(error)}`,\n \"COMMAND_FAILED\",\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n /**\n * Upload files to the sandbox.\n *\n * Files are written to the sandbox filesystem. Parent directories are\n * created automatically if they don't exist.\n *\n * @param files - Array of [path, content] tuples to upload\n * @returns Upload result for each file, with success or error status\n *\n * @example\n * ```typescript\n * const encoder = new TextEncoder();\n * const results = await sandbox.uploadFiles([\n * [\"src/index.js\", encoder.encode(\"console.log('Hello')\")],\n * [\"package.json\", encoder.encode('{\"name\": \"test\"}')],\n * ]);\n * ```\n */\n async uploadFiles(\n files: Array<[string, Uint8Array]>,\n ): Promise<FileUploadResponse[]> {\n const sandbox = this.instance; // Throws if not initialized\n const results: FileUploadResponse[] = [];\n\n for (const [path, content] of files) {\n try {\n // Ensure parent directory exists using spawn (more reliable than sh template)\n const parentDir = path.substring(0, path.lastIndexOf(\"/\"));\n if (parentDir) {\n const mkdirChild = await sandbox.spawn(\"/bin/mkdir\", {\n args: [\"-p\", parentDir],\n stdout: \"piped\",\n stderr: \"piped\",\n });\n await mkdirChild.output();\n }\n\n // Write the file content\n const textContent = new TextDecoder().decode(content);\n await sandbox.fs.writeTextFile(path, textContent);\n results.push({ path, error: null });\n } catch (error) {\n results.push({ path, error: this.#mapError(error) });\n }\n }\n\n return results;\n }\n\n /**\n * Download files from the sandbox.\n *\n * Each file is read individually, allowing partial success when some\n * files exist and others don't.\n *\n * @param paths - Array of file paths to download\n * @returns Download result for each file, with content or error\n *\n * @example\n * ```typescript\n * const results = await sandbox.downloadFiles([\"src/index.js\", \"missing.txt\"]);\n * for (const result of results) {\n * if (result.content) {\n * console.log(new TextDecoder().decode(result.content));\n * } else {\n * console.error(`Error: ${result.error}`);\n * }\n * }\n * ```\n */\n async downloadFiles(paths: string[]): Promise<FileDownloadResponse[]> {\n const sandbox = this.instance; // Throws if not initialized\n const results: FileDownloadResponse[] = [];\n\n for (const path of paths) {\n try {\n const child = await sandbox.spawn(\"/bin/cat\", {\n args: [path],\n stdout: \"piped\",\n stderr: \"piped\",\n });\n\n const { status, stdoutText } = await child.output();\n\n if (!status.success) {\n results.push({\n path,\n content: null,\n error: \"file_not_found\",\n });\n } else {\n const content = new TextEncoder().encode(stdoutText ?? \"\");\n results.push({\n path,\n content,\n error: null,\n });\n }\n } catch (error) {\n results.push({\n path,\n content: null,\n error: this.#mapError(error),\n });\n }\n }\n\n return results;\n }\n\n /**\n * Close the sandbox and release all resources.\n *\n * After closing, the sandbox cannot be used again. Any unsaved data\n * will be lost.\n *\n * @example\n * ```typescript\n * try {\n * await sandbox.execute(\"deno run build.ts\");\n * } finally {\n * await sandbox.close();\n * }\n * ```\n */\n async close(): Promise<void> {\n if (this.#sandbox) {\n try {\n await this.#sandbox.close();\n } finally {\n this.#sandbox = null;\n }\n }\n }\n\n /**\n * Forcefully terminate the sandbox.\n *\n * Use this when you need to immediately stop the sandbox, even if\n * operations are in progress.\n *\n * @example\n * ```typescript\n * await sandbox.kill();\n * ```\n */\n async kill(): Promise<void> {\n if (this.#sandbox) {\n try {\n await this.#sandbox.kill();\n } finally {\n this.#sandbox = null;\n }\n }\n }\n\n /**\n * Alias for close() to maintain compatibility with other sandbox implementations.\n */\n async stop(): Promise<void> {\n await this.close();\n }\n\n /**\n * Set the sandbox from an existing Deno Sandbox instance.\n * Used internally by the static `connect()` method.\n */\n #setFromExisting(existingSandbox: Sandbox, sandboxId: string): void {\n this.#sandbox = existingSandbox;\n this.#id = sandboxId;\n }\n\n /**\n * Map Deno SDK errors to standardized FileOperationError codes.\n *\n * @param error - The error from the Deno SDK\n * @returns A standardized error code\n */\n #mapError(error: unknown): FileOperationError {\n if (error instanceof Error) {\n const msg = error.message.toLowerCase();\n\n if (msg.includes(\"not found\") || msg.includes(\"enoent\")) {\n return \"file_not_found\";\n }\n if (msg.includes(\"permission\") || msg.includes(\"eacces\")) {\n return \"permission_denied\";\n }\n if (msg.includes(\"directory\") || msg.includes(\"eisdir\")) {\n return \"is_directory\";\n }\n }\n\n return \"invalid_path\";\n }\n\n /**\n * Create and initialize a new DenoSandbox in one step.\n *\n * This is the recommended way to create a sandbox. It combines\n * construction and initialization into a single async operation.\n *\n * @param options - Configuration options for the sandbox\n * @returns An initialized and ready-to-use sandbox\n *\n * @example\n * ```typescript\n * const sandbox = await DenoSandbox.create({\n * memory: \"1GiB\",\n * timeout: \"10m\",\n * region: \"ord\",\n * });\n * ```\n */\n static async create(options?: DenoSandboxOptions): Promise<DenoSandbox> {\n const sandbox = new DenoSandbox(options);\n await sandbox.initialize();\n return sandbox;\n }\n\n /**\n * Reconnect to an existing sandbox by ID.\n *\n * This allows you to resume working with a sandbox that was created\n * earlier with a duration-based lifetime.\n *\n * @param id - The ID of the sandbox to reconnect to\n * @param options - Optional auth configuration (for token)\n * @returns A connected sandbox instance\n *\n * @example\n * ```typescript\n * // Resume a sandbox from a stored ID\n * const sandbox = await DenoSandbox.fromId(\"sandbox-abc123\");\n * const result = await sandbox.execute(\"ls -la\");\n * ```\n */\n static async fromId(\n id: string,\n options?: Pick<\n DenoSandboxOptions,\n \"auth\" | \"token\" | \"org\" | \"apiEndpoint\"\n >,\n ): Promise<DenoSandbox> {\n // Resolve token: top-level `token` > deprecated `auth.token` > env variable\n const resolvedToken =\n options?.token ??\n options?.auth?.token ??\n getAuthCredentials(options?.auth).token;\n\n try {\n const existingSandbox = await Sandbox.connect({\n id,\n token: resolvedToken,\n ...(options?.org !== undefined ? { org: options.org } : {}),\n ...(options?.apiEndpoint !== undefined\n ? { apiEndpoint: options.apiEndpoint }\n : {}),\n });\n\n const denoSandbox = new DenoSandbox();\n // Set the existing sandbox directly (bypass initialize)\n denoSandbox.#setFromExisting(existingSandbox, id);\n\n return denoSandbox;\n } catch (error) {\n throw new DenoSandboxError(\n `Sandbox not found: ${id}`,\n \"SANDBOX_NOT_FOUND\",\n error instanceof Error ? error : undefined,\n );\n }\n }\n}\n\n/**\n * Async factory function type for creating Deno Sandbox instances.\n *\n * This is similar to BackendFactory but supports async creation,\n * which is required for Deno Sandbox since initialization is async.\n */\nexport type AsyncDenoSandboxFactory = () => Promise<DenoSandbox>;\n\n/**\n * Create an async factory function that creates a new Deno Sandbox per invocation.\n *\n * Each call to the factory will create and initialize a new sandbox.\n * This is useful when you want fresh, isolated environments for each\n * agent invocation.\n *\n * **Important**: This returns an async factory. For use with middleware that\n * requires synchronous BackendFactory, use `createDenoSandboxFactoryFromSandbox()`\n * with a pre-created sandbox instead.\n *\n * @param options - Optional configuration for sandbox creation\n * @returns An async factory function that creates new sandboxes\n *\n * @example\n * ```typescript\n * import { DenoSandbox, createDenoSandboxFactory } from \"@langchain/deno\";\n *\n * // Create a factory for new sandboxes\n * const factory = createDenoSandboxFactory({ memory: \"1GiB\" });\n *\n * // Each call creates a new sandbox\n * const sandbox1 = await factory();\n * const sandbox2 = await factory();\n *\n * try {\n * // Use sandboxes...\n * } finally {\n * await sandbox1.close();\n * await sandbox2.close();\n * }\n * ```\n */\nexport function createDenoSandboxFactory(\n options?: DenoSandboxOptions,\n): AsyncDenoSandboxFactory {\n return async () => {\n return await DenoSandbox.create(options);\n };\n}\n\n/**\n * Create a backend factory that reuses an existing Deno Sandbox.\n *\n * This allows multiple agent invocations to share the same sandbox,\n * avoiding the startup overhead of creating new sandboxes.\n *\n * Important: You are responsible for managing the sandbox lifecycle\n * (calling `close()` when done).\n *\n * @param sandbox - An existing DenoSandbox instance (must be initialized)\n * @returns A BackendFactory that returns the provided sandbox\n *\n * @example\n * ```typescript\n * import { createDeepAgent, createFilesystemMiddleware } from \"deepagents\";\n * import { DenoSandbox, createDenoSandboxFactoryFromSandbox } from \"@langchain/deno\";\n *\n * // Create and initialize a sandbox\n * const sandbox = await DenoSandbox.create({ memory: \"1GiB\" });\n *\n * try {\n * const agent = createDeepAgent({\n * model: new ChatAnthropic({ model: \"claude-sonnet-4-20250514\" }),\n * systemPrompt: \"You are a coding assistant.\",\n * middlewares: [\n * createFilesystemMiddleware({\n * backend: createDenoSandboxFactoryFromSandbox(sandbox),\n * }),\n * ],\n * });\n *\n * await agent.invoke({ messages: [...] });\n * } finally {\n * await sandbox.close();\n * }\n * ```\n */\nexport function createDenoSandboxFactoryFromSandbox(\n sandbox: DenoSandbox,\n): BackendFactory {\n return () => sandbox;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/deno",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Deno Sandbox backend for deepagents",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -30,19 +30,19 @@
30
30
  "@deno/sandbox": "^0.13.2"
31
31
  },
32
32
  "peerDependencies": {
33
- "deepagents": ">=1.6.0"
33
+ "deepagents": ">=1.12.0-rc.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@tsconfig/recommended": "^1.0.13",
37
- "@types/node": "^25.1.0",
37
+ "@types/node": "^26.1.0",
38
38
  "@vitest/coverage-v8": "^4.0.18",
39
39
  "dotenv": "^17.2.3",
40
- "tsdown": "^0.21.4",
40
+ "tsdown": "^0.22.1",
41
41
  "tsx": "^4.21.0",
42
- "typescript": "^5.9.3",
42
+ "typescript": "^7.0.2",
43
43
  "vitest": "^4.0.18",
44
- "deepagents": "1.8.6",
45
- "@langchain/sandbox-standard-tests": "0.1.0"
44
+ "deepagents": "1.12.2",
45
+ "@langchain/sandbox-standard-tests": "2.0.0"
46
46
  },
47
47
  "exports": {
48
48
  ".": {