@langchain/deno 0.0.1 → 0.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) LangChain, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/dist/index.cjs CHANGED
@@ -61,6 +61,12 @@ function getAuthCredentials(options) {
61
61
 
62
62
  //#endregion
63
63
  //#region src/types.ts
64
+ /**
65
+ * Type definitions for the Deno Sandbox backend.
66
+ *
67
+ * This module contains all type definitions for the @langchain/deno package,
68
+ * including options and error types.
69
+ */
64
70
  const DENO_SANDBOX_ERROR_SYMBOL = Symbol.for("deno.sandbox.error");
65
71
  /**
66
72
  * Custom error class for Deno Sandbox operations.
@@ -90,7 +96,7 @@ const DENO_SANDBOX_ERROR_SYMBOL = Symbol.for("deno.sandbox.error");
90
96
  * }
91
97
  * ```
92
98
  */
93
- var DenoSandboxError = class DenoSandboxError extends Error {
99
+ var DenoSandboxError = class DenoSandboxError extends deepagents.SandboxError {
94
100
  [DENO_SANDBOX_ERROR_SYMBOL];
95
101
  /** Error name for instanceof checks and logging */
96
102
  name = "DenoSandboxError";
@@ -102,7 +108,7 @@ var DenoSandboxError = class DenoSandboxError extends Error {
102
108
  * @param cause - Original error that caused this error (for debugging)
103
109
  */
104
110
  constructor(message, code, cause) {
105
- super(message);
111
+ super(message, code, cause);
106
112
  this.code = code;
107
113
  this.cause = cause;
108
114
  Object.setPrototypeOf(this, DenoSandboxError.prototype);
@@ -198,7 +204,7 @@ var DenoSandbox = class DenoSandbox extends deepagents.BaseSandbox {
198
204
  * const denoSdk = sandbox.sandbox; // Access the raw SDK
199
205
  * ```
200
206
  */
201
- get sandbox() {
207
+ get instance() {
202
208
  if (!this.#sandbox) throw new DenoSandboxError("Sandbox not initialized. Call initialize() or use DenoSandbox.create()", "NOT_INITIALIZED");
203
209
  return this.#sandbox;
204
210
  }
@@ -302,7 +308,7 @@ var DenoSandbox = class DenoSandbox extends deepagents.BaseSandbox {
302
308
  * ```
303
309
  */
304
310
  async execute(command) {
305
- const sandbox = this.sandbox;
311
+ const sandbox = this.instance;
306
312
  try {
307
313
  const { status, stdoutText, stderrText } = await (await sandbox.spawn("/bin/bash", {
308
314
  args: ["-c", command],
@@ -338,7 +344,7 @@ var DenoSandbox = class DenoSandbox extends deepagents.BaseSandbox {
338
344
  * ```
339
345
  */
340
346
  async uploadFiles(files) {
341
- const sandbox = this.sandbox;
347
+ const sandbox = this.instance;
342
348
  const results = [];
343
349
  for (const [path, content] of files) try {
344
350
  const parentDir = path.substring(0, path.lastIndexOf("/"));
@@ -383,7 +389,7 @@ var DenoSandbox = class DenoSandbox extends deepagents.BaseSandbox {
383
389
  * ```
384
390
  */
385
391
  async downloadFiles(paths) {
386
- const sandbox = this.sandbox;
392
+ const sandbox = this.instance;
387
393
  const results = [];
388
394
  for (const path of paths) try {
389
395
  const { status, stdoutText } = await (await sandbox.spawn("/bin/bash", {
@@ -609,18 +615,18 @@ awk -v old="$OLD" -v new="$NEW" -v replace_all=${replaceAll ? 1 : 0} '
609
615
  * This allows you to resume working with a sandbox that was created
610
616
  * earlier with a duration-based lifetime.
611
617
  *
612
- * @param sandboxId - The ID of the sandbox to reconnect to
618
+ * @param id - The ID of the sandbox to reconnect to
613
619
  * @param options - Optional auth configuration (for token)
614
620
  * @returns A connected sandbox instance
615
621
  *
616
622
  * @example
617
623
  * ```typescript
618
624
  * // Resume a sandbox from a stored ID
619
- * const sandbox = await DenoSandbox.connect("sandbox-abc123");
625
+ * const sandbox = await DenoSandbox.fromId("sandbox-abc123");
620
626
  * const result = await sandbox.execute("ls -la");
621
627
  * ```
622
628
  */
623
- static async connect(sandboxId, options) {
629
+ static async fromId(id, options) {
624
630
  let credentials;
625
631
  try {
626
632
  credentials = getAuthCredentials(options?.auth);
@@ -629,12 +635,12 @@ awk -v old="$OLD" -v new="$NEW" -v replace_all=${replaceAll ? 1 : 0} '
629
635
  }
630
636
  try {
631
637
  process.env.DENO_DEPLOY_TOKEN = credentials.token;
632
- const existingSandbox = await _deno_sandbox.Sandbox.connect({ id: sandboxId });
638
+ const existingSandbox = await _deno_sandbox.Sandbox.connect({ id });
633
639
  const denoSandbox = new DenoSandbox();
634
- denoSandbox.#setFromExisting(existingSandbox, sandboxId);
640
+ denoSandbox.#setFromExisting(existingSandbox, id);
635
641
  return denoSandbox;
636
642
  } catch (error) {
637
- throw new DenoSandboxError(`Sandbox not found: ${sandboxId}`, "SANDBOX_NOT_FOUND", error instanceof Error ? error : void 0);
643
+ throw new DenoSandboxError(`Sandbox not found: ${id}`, "SANDBOX_NOT_FOUND", error instanceof Error ? error : void 0);
638
644
  }
639
645
  }
640
646
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["BaseSandbox","#id","#sandbox","#options","Sandbox","#uploadInitialFiles","#mapError","#setFromExisting"],"sources":["../src/auth.ts","../src/types.ts","../src/sandbox.ts"],"sourcesContent":["/**\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\";\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 {Error} 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 Error(\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({ auth: { token: '...' } })\",\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 {Error} 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","/**\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\n/**\n * Supported regions for Deno Deploy sandboxes.\n *\n * Currently available regions:\n * - `ams`: Amsterdam\n * - `ord`: Chicago\n */\nexport type DenoSandboxRegion = \"ams\" | \"ord\";\n\n/**\n * Sandbox lifetime configuration.\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 * Configuration options for creating a Deno Sandbox.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * memoryMb: 1024, // 1GB memory\n * lifetime: \"5m\", // 5 minutes\n * region: \"iad\", // US East\n * };\n * ```\n */\nexport interface DenoSandboxOptions {\n /**\n * Amount of memory allocated to the sandbox in megabytes.\n *\n * Memory limits:\n * - Minimum: 768MB\n * - Maximum: 4096MB\n *\n * @default 768\n */\n memoryMb?: number;\n\n /**\n * Sandbox lifetime configuration.\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 * 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 * memoryMb: 1024,\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 * ### 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/**\n * Error codes for Deno Sandbox operations.\n *\n * Used to identify specific error conditions and handle them appropriately.\n */\nexport type DenoSandboxErrorCode =\n /** Sandbox has not been initialized - call initialize() first */\n | \"NOT_INITIALIZED\"\n /** Sandbox is already initialized - cannot initialize twice */\n | \"ALREADY_INITIALIZED\"\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 /** Command execution timed out */\n | \"COMMAND_TIMEOUT\"\n /** Command execution failed */\n | \"COMMAND_FAILED\"\n /** File operation (read/write) failed */\n | \"FILE_OPERATION_FAILED\"\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 Error {\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);\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","/* 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 } from \"@deno/sandbox\";\nimport {\n BaseSandbox,\n type EditResult,\n type ExecuteResponse,\n type FileDownloadResponse,\n type FileOperationError,\n type FileUploadResponse,\n type BackendFactory,\n type WriteResult,\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 * memoryMb: 1024,\n * lifetime: \"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 sandbox(): 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({ memoryMb: 1024 });\n * await sandbox.initialize();\n *\n * // Or use the factory method\n * const sandbox = await DenoSandbox.create({ memoryMb: 1024 });\n * ```\n */\n constructor(options: DenoSandboxOptions = {}) {\n super();\n\n // Set defaults\n this.#options = {\n memoryMb: 768,\n lifetime: \"session\",\n ...options,\n };\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 // Get authentication credentials\n let credentials: { token: string };\n try {\n credentials = getAuthCredentials(this.#options.auth);\n } catch (error) {\n throw new DenoSandboxError(\n \"Failed to authenticate with Deno Deploy. Check your token configuration.\",\n \"AUTHENTICATION_FAILED\",\n error instanceof Error ? error : undefined,\n );\n }\n\n try {\n // Set the token in environment for the SDK\n process.env.DENO_DEPLOY_TOKEN = credentials.token;\n\n // Build SDK create options\n const createOptions: Parameters<typeof Sandbox.create>[0] = {};\n\n // Add optional memory configuration\n if (this.#options.memoryMb !== undefined) {\n createOptions.memoryMb = this.#options.memoryMb;\n }\n\n // Add optional lifetime configuration\n if (this.#options.lifetime !== undefined) {\n createOptions.lifetime = this.#options.lifetime;\n }\n\n // Add optional region configuration\n if (this.#options.region !== undefined) {\n createOptions.region = this.#options.region;\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.sandbox; // 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.sandbox; // 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.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.sandbox; // 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 // Override BaseSandbox methods that use Python with pure shell implementations\n // Deno sandboxes don't have Python installed, only basic Unix tools\n // ============================================================================\n\n /**\n * Read a file's content with line numbers.\n *\n * Override of BaseSandbox.read() to use awk instead of Python,\n * since Deno sandboxes don't have Python installed.\n *\n * @param filePath - Absolute path to the file\n * @param offset - Line offset (0-indexed, default 0)\n * @param limit - Maximum lines to return (default 500)\n * @returns Formatted file content with line numbers, or error message\n */\n override async read(\n filePath: string,\n offset: number = 0,\n limit: number = 500,\n ): Promise<string> {\n // Coerce offset and limit to safe non-negative integers\n const safeOffset =\n Number.isFinite(offset) && offset > 0 ? Math.floor(offset) : 0;\n const safeLimit =\n Number.isFinite(limit) && limit > 0 && limit < Number.MAX_SAFE_INTEGER\n ? Math.floor(limit)\n : 500;\n\n // Escape path for shell\n const escapedPath = filePath.replace(/'/g, \"'\\\\''\");\n\n // Build shell command using awk for portable line number formatting\n // First check if file exists, then format with line numbers\n const command = `\nif [ ! -f '${escapedPath}' ]; then\n echo \"Error: File not found\"\n exit 1\nfi\nif [ ! -s '${escapedPath}' ]; then\n echo \"System reminder: File exists but has empty contents\"\n exit 0\nfi\nawk -v offset=${safeOffset} -v limit=${safeLimit} '\n NR > offset && NR <= offset + limit {\n printf \"%6d\\\\t%s\\\\n\", NR, $0\n }\n' '${escapedPath}'\n`;\n\n const result = await this.execute(command);\n\n if (result.exitCode !== 0) {\n return `Error: File '${filePath}' not found`;\n }\n\n return result.output;\n }\n\n /**\n * Create a new file with content.\n *\n * Override of BaseSandbox.write() to use shell commands instead of Python,\n * since Deno sandboxes don't have Python installed.\n *\n * @param filePath - Absolute path for the new file\n * @param content - File content to write\n * @returns WriteResult with error populated on failure\n */\n override async write(\n filePath: string,\n content: string,\n ): Promise<WriteResult> {\n // Escape path for shell\n const escapedPath = filePath.replace(/'/g, \"'\\\\''\");\n\n // Check if file already exists\n const checkResult = await this.execute(`test -f '${escapedPath}'`);\n if (checkResult.exitCode === 0) {\n return {\n error: `Cannot write to ${filePath} because it already exists. Read and then make an edit, or write to a new path.`,\n };\n }\n\n // Use uploadFiles for reliable content writing (handles binary, special chars, etc.)\n const encoder = new TextEncoder();\n const uploadResult = await this.uploadFiles([\n [filePath, encoder.encode(content)],\n ]);\n\n if (uploadResult[0]?.error) {\n return { error: `Failed to write file: ${uploadResult[0].error}` };\n }\n\n return { path: filePath, filesUpdate: null };\n }\n\n /**\n * Edit a file by replacing string occurrences.\n *\n * Override of BaseSandbox.edit() to use shell commands instead of Python,\n * since Deno sandboxes don't have Python installed.\n *\n * Uses sed for in-place replacement with proper escaping.\n *\n * @param filePath - Absolute path to the file\n * @param oldString - String to find and replace\n * @param newString - Replacement string\n * @param replaceAll - If true, replace all occurrences (default: false)\n * @returns EditResult with error, path, and occurrences\n */\n override async edit(\n filePath: string,\n oldString: string,\n newString: string,\n replaceAll: boolean = false,\n ): Promise<EditResult> {\n // Escape path for shell\n const escapedPath = filePath.replace(/'/g, \"'\\\\''\");\n\n // Check if file exists\n const checkResult = await this.execute(`test -f '${escapedPath}'`);\n if (checkResult.exitCode !== 0) {\n return { error: `Error: File '${filePath}' not found` };\n }\n\n // Count occurrences first using grep -F (fixed string)\n // Escape old string for grep\n const escapedOldForGrep = oldString.replace(/'/g, \"'\\\\''\");\n const countResult = await this.execute(\n `grep -oF '${escapedOldForGrep}' '${escapedPath}' | wc -l`,\n );\n const count = parseInt(countResult.output.trim(), 10) || 0;\n\n if (count === 0) {\n return { error: `String not found in file '${filePath}'` };\n }\n\n if (count > 1 && !replaceAll) {\n return {\n error: `Multiple occurrences found in '${filePath}'. Use replaceAll=true to replace all.`,\n };\n }\n\n // Perform the replacement using sed\n // Use a delimiter that's unlikely to be in the strings (we'll use \\x00 if available, otherwise |)\n // For safety, we'll use awk which handles arbitrary strings better than sed\n\n // Base64 encode both strings to safely pass them to awk\n const oldB64 = Buffer.from(oldString, \"utf-8\").toString(\"base64\");\n const newB64 = Buffer.from(newString, \"utf-8\").toString(\"base64\");\n\n // Use awk with base64 decoding for safe string replacement\n const awkCommand = `\nOLD=$(echo '${oldB64}' | base64 -d)\nNEW=$(echo '${newB64}' | base64 -d)\nawk -v old=\"$OLD\" -v new=\"$NEW\" -v replace_all=${replaceAll ? 1 : 0} '\n{\n if (replace_all) {\n gsub(old, new)\n } else {\n sub(old, new)\n }\n print\n}\n' '${escapedPath}' > '${escapedPath}.tmp' && mv '${escapedPath}.tmp' '${escapedPath}'\n`;\n\n const editResult = await this.execute(awkCommand);\n\n if (editResult.exitCode !== 0) {\n return { error: `Unknown error editing file '${filePath}'` };\n }\n\n return { path: filePath, filesUpdate: null, occurrences: count };\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 // Static Factory Methods\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 * memoryMb: 1024,\n * lifetime: \"10m\",\n * region: \"iad\",\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 sandboxId - 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.connect(\"sandbox-abc123\");\n * const result = await sandbox.execute(\"ls -la\");\n * ```\n */\n static async connect(\n sandboxId: string,\n options?: Pick<DenoSandboxOptions, \"auth\">,\n ): Promise<DenoSandbox> {\n // Get authentication credentials\n let credentials: { token: string };\n try {\n credentials = getAuthCredentials(options?.auth);\n } catch (error) {\n throw new DenoSandboxError(\n \"Failed to authenticate with Deno Deploy. Check your token configuration.\",\n \"AUTHENTICATION_FAILED\",\n error instanceof Error ? error : undefined,\n );\n }\n\n try {\n // Set the token in environment for the SDK\n process.env.DENO_DEPLOY_TOKEN = credentials.token;\n\n const existingSandbox = await Sandbox.connect({ id: sandboxId });\n\n const denoSandbox = new DenoSandbox();\n // Set the existing sandbox directly (bypass initialize)\n denoSandbox.#setFromExisting(existingSandbox, sandboxId);\n\n return denoSandbox;\n } catch (error) {\n throw new DenoSandboxError(\n `Sandbox not found: ${sandboxId}`,\n \"SANDBOX_NOT_FOUND\",\n error instanceof Error ? error : undefined,\n );\n }\n }\n}\n\n// ============================================================================\n// Factory Functions\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({ memoryMb: 1024 });\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({ memoryMb: 1024 });\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/auth.ts","../src/types.ts","../src/sandbox.ts"],"sourcesContent":["/**\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\";\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 {Error} 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 Error(\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({ auth: { token: '...' } })\",\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 {Error} 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","/**\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 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 = \"ams\" | \"ord\";\n\n/**\n * Sandbox lifetime configuration.\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 * Configuration options for creating a Deno Sandbox.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * memoryMb: 1024, // 1GB memory\n * lifetime: \"5m\", // 5 minutes\n * region: \"iad\", // US East\n * };\n * ```\n */\nexport interface DenoSandboxOptions {\n /**\n * Amount of memory allocated to the sandbox in megabytes.\n *\n * Memory limits:\n * - Minimum: 768MB\n * - Maximum: 4096MB\n *\n * @default 768\n */\n memoryMb?: number;\n\n /**\n * Sandbox lifetime configuration.\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 * 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 * memoryMb: 1024,\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 * ### 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/**\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","/* 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 } from \"@deno/sandbox\";\nimport {\n BaseSandbox,\n type EditResult,\n type ExecuteResponse,\n type FileDownloadResponse,\n type FileOperationError,\n type FileUploadResponse,\n type BackendFactory,\n type WriteResult,\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 * memoryMb: 1024,\n * lifetime: \"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({ memoryMb: 1024 });\n * await sandbox.initialize();\n *\n * // Or use the factory method\n * const sandbox = await DenoSandbox.create({ memoryMb: 1024 });\n * ```\n */\n constructor(options: DenoSandboxOptions = {}) {\n super();\n\n // Set defaults\n this.#options = {\n memoryMb: 768,\n lifetime: \"session\",\n ...options,\n };\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 // Get authentication credentials\n let credentials: { token: string };\n try {\n credentials = getAuthCredentials(this.#options.auth);\n } catch (error) {\n throw new DenoSandboxError(\n \"Failed to authenticate with Deno Deploy. Check your token configuration.\",\n \"AUTHENTICATION_FAILED\",\n error instanceof Error ? error : undefined,\n );\n }\n\n try {\n // Set the token in environment for the SDK\n process.env.DENO_DEPLOY_TOKEN = credentials.token;\n\n // Build SDK create options\n const createOptions: Parameters<typeof Sandbox.create>[0] = {};\n\n // Add optional memory configuration\n if (this.#options.memoryMb !== undefined) {\n createOptions.memoryMb = this.#options.memoryMb;\n }\n\n // Add optional lifetime configuration\n if (this.#options.lifetime !== undefined) {\n createOptions.lifetime = this.#options.lifetime;\n }\n\n // Add optional region configuration\n if (this.#options.region !== undefined) {\n createOptions.region = this.#options.region;\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.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 // Override BaseSandbox methods that use Python with pure shell implementations\n // Deno sandboxes don't have Python installed, only basic Unix tools\n // ============================================================================\n\n /**\n * Read a file's content with line numbers.\n *\n * Override of BaseSandbox.read() to use awk instead of Python,\n * since Deno sandboxes don't have Python installed.\n *\n * @param filePath - Absolute path to the file\n * @param offset - Line offset (0-indexed, default 0)\n * @param limit - Maximum lines to return (default 500)\n * @returns Formatted file content with line numbers, or error message\n */\n override async read(\n filePath: string,\n offset: number = 0,\n limit: number = 500,\n ): Promise<string> {\n // Coerce offset and limit to safe non-negative integers\n const safeOffset =\n Number.isFinite(offset) && offset > 0 ? Math.floor(offset) : 0;\n const safeLimit =\n Number.isFinite(limit) && limit > 0 && limit < Number.MAX_SAFE_INTEGER\n ? Math.floor(limit)\n : 500;\n\n // Escape path for shell\n const escapedPath = filePath.replace(/'/g, \"'\\\\''\");\n\n // Build shell command using awk for portable line number formatting\n // First check if file exists, then format with line numbers\n const command = `\nif [ ! -f '${escapedPath}' ]; then\n echo \"Error: File not found\"\n exit 1\nfi\nif [ ! -s '${escapedPath}' ]; then\n echo \"System reminder: File exists but has empty contents\"\n exit 0\nfi\nawk -v offset=${safeOffset} -v limit=${safeLimit} '\n NR > offset && NR <= offset + limit {\n printf \"%6d\\\\t%s\\\\n\", NR, $0\n }\n' '${escapedPath}'\n`;\n\n const result = await this.execute(command);\n\n if (result.exitCode !== 0) {\n return `Error: File '${filePath}' not found`;\n }\n\n return result.output;\n }\n\n /**\n * Create a new file with content.\n *\n * Override of BaseSandbox.write() to use shell commands instead of Python,\n * since Deno sandboxes don't have Python installed.\n *\n * @param filePath - Absolute path for the new file\n * @param content - File content to write\n * @returns WriteResult with error populated on failure\n */\n override async write(\n filePath: string,\n content: string,\n ): Promise<WriteResult> {\n // Escape path for shell\n const escapedPath = filePath.replace(/'/g, \"'\\\\''\");\n\n // Check if file already exists\n const checkResult = await this.execute(`test -f '${escapedPath}'`);\n if (checkResult.exitCode === 0) {\n return {\n error: `Cannot write to ${filePath} because it already exists. Read and then make an edit, or write to a new path.`,\n };\n }\n\n // Use uploadFiles for reliable content writing (handles binary, special chars, etc.)\n const encoder = new TextEncoder();\n const uploadResult = await this.uploadFiles([\n [filePath, encoder.encode(content)],\n ]);\n\n if (uploadResult[0]?.error) {\n return { error: `Failed to write file: ${uploadResult[0].error}` };\n }\n\n return { path: filePath, filesUpdate: null };\n }\n\n /**\n * Edit a file by replacing string occurrences.\n *\n * Override of BaseSandbox.edit() to use shell commands instead of Python,\n * since Deno sandboxes don't have Python installed.\n *\n * Uses sed for in-place replacement with proper escaping.\n *\n * @param filePath - Absolute path to the file\n * @param oldString - String to find and replace\n * @param newString - Replacement string\n * @param replaceAll - If true, replace all occurrences (default: false)\n * @returns EditResult with error, path, and occurrences\n */\n override async edit(\n filePath: string,\n oldString: string,\n newString: string,\n replaceAll: boolean = false,\n ): Promise<EditResult> {\n // Escape path for shell\n const escapedPath = filePath.replace(/'/g, \"'\\\\''\");\n\n // Check if file exists\n const checkResult = await this.execute(`test -f '${escapedPath}'`);\n if (checkResult.exitCode !== 0) {\n return { error: `Error: File '${filePath}' not found` };\n }\n\n // Count occurrences first using grep -F (fixed string)\n // Escape old string for grep\n const escapedOldForGrep = oldString.replace(/'/g, \"'\\\\''\");\n const countResult = await this.execute(\n `grep -oF '${escapedOldForGrep}' '${escapedPath}' | wc -l`,\n );\n const count = parseInt(countResult.output.trim(), 10) || 0;\n\n if (count === 0) {\n return { error: `String not found in file '${filePath}'` };\n }\n\n if (count > 1 && !replaceAll) {\n return {\n error: `Multiple occurrences found in '${filePath}'. Use replaceAll=true to replace all.`,\n };\n }\n\n // Perform the replacement using sed\n // Use a delimiter that's unlikely to be in the strings (we'll use \\x00 if available, otherwise |)\n // For safety, we'll use awk which handles arbitrary strings better than sed\n\n // Base64 encode both strings to safely pass them to awk\n const oldB64 = Buffer.from(oldString, \"utf-8\").toString(\"base64\");\n const newB64 = Buffer.from(newString, \"utf-8\").toString(\"base64\");\n\n // Use awk with base64 decoding for safe string replacement\n const awkCommand = `\nOLD=$(echo '${oldB64}' | base64 -d)\nNEW=$(echo '${newB64}' | base64 -d)\nawk -v old=\"$OLD\" -v new=\"$NEW\" -v replace_all=${replaceAll ? 1 : 0} '\n{\n if (replace_all) {\n gsub(old, new)\n } else {\n sub(old, new)\n }\n print\n}\n' '${escapedPath}' > '${escapedPath}.tmp' && mv '${escapedPath}.tmp' '${escapedPath}'\n`;\n\n const editResult = await this.execute(awkCommand);\n\n if (editResult.exitCode !== 0) {\n return { error: `Unknown error editing file '${filePath}'` };\n }\n\n return { path: filePath, filesUpdate: null, occurrences: count };\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 * memoryMb: 1024,\n * lifetime: \"10m\",\n * region: \"iad\",\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<DenoSandboxOptions, \"auth\">,\n ): Promise<DenoSandbox> {\n // Get authentication credentials\n let credentials: { token: string };\n try {\n credentials = getAuthCredentials(options?.auth);\n } catch (error) {\n throw new DenoSandboxError(\n \"Failed to authenticate with Deno Deploy. Check your token configuration.\",\n \"AUTHENTICATION_FAILED\",\n error instanceof Error ? error : undefined,\n );\n }\n\n try {\n // Set the token in environment for the SDK\n process.env.DENO_DEPLOY_TOKEN = credentials.token;\n\n const existingSandbox = await Sandbox.connect({ id });\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({ memoryMb: 1024 });\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({ memoryMb: 1024 });\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDA,SAAgB,aAAa,SAA8C;AAEzE,KAAI,SAAS,MACX,QAAO,QAAQ;CAIjB,MAAM,cAAc,QAAQ,IAAI;AAChC,KAAI,YACF,QAAO;AAIT,OAAM,IAAI,MACR,sWAMD;;;;;;;;;;;AAYH,SAAgB,mBACd,SACiB;AACjB,QAAO,EACL,OAAO,aAAa,QAAQ,EAC7B;;;;;;;;;;;ACqCH,MAAM,4BAA4B,OAAO,IAAI,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BlE,IAAa,mBAAb,MAAa,yBAAyBA,wBAAa;CACjD,CAAC;;CAGD,AAAkB,OAAO;;;;;;;;CASzB,YACE,SACA,AAAgB,MAChB,AAAyB,OACzB;AACA,QAAM,SAAS,MAA0B,MAAM;EAH/B;EACS;AAIzB,SAAO,eAAe,MAAM,iBAAiB,UAAU;;;;;;;;CASzD,OAAO,WAAW,OAA2C;AAC3D,SACE,OAAO,UAAU,YACjB,UAAU,QACT,MAAkC,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC5HxE,IAAa,cAAb,MAAa,oBAAoBC,uBAAY;;CAE3C,WAA2B;;CAG3B;;CAGA;;;;;;;CAQA,IAAI,KAAa;AACf,SAAO,MAAKC;;;;;;;;;;;;;CAcd,IAAI,WAAoB;AACtB,MAAI,CAAC,MAAKC,QACR,OAAM,IAAI,iBACR,0EACA,kBACD;AAEH,SAAO,MAAKA;;;;;CAMd,IAAI,YAAqB;AACvB,SAAO,MAAKA,YAAa;;;;;;;;;;;;;;;;;;;;CAqB3B,YAAY,UAA8B,EAAE,EAAE;AAC5C,SAAO;AAGP,QAAKC,UAAW;GACd,UAAU;GACV,UAAU;GACV,GAAG;GACJ;AAGD,QAAKF,KAAM,gBAAgB,KAAK,KAAK;;;;;;;;;;;;;;;;;;;;CAqBvC,MAAM,aAA4B;AAEhC,MAAI,MAAKC,QACP,OAAM,IAAI,iBACR,2FACA,sBACD;EAIH,IAAI;AACJ,MAAI;AACF,iBAAc,mBAAmB,MAAKC,QAAS,KAAK;WAC7C,OAAO;AACd,SAAM,IAAI,iBACR,4EACA,yBACA,iBAAiB,QAAQ,QAAQ,OAClC;;AAGH,MAAI;AAEF,WAAQ,IAAI,oBAAoB,YAAY;GAG5C,MAAM,gBAAsD,EAAE;AAG9D,OAAI,MAAKA,QAAS,aAAa,OAC7B,eAAc,WAAW,MAAKA,QAAS;AAIzC,OAAI,MAAKA,QAAS,aAAa,OAC7B,eAAc,WAAW,MAAKA,QAAS;AAIzC,OAAI,MAAKA,QAAS,WAAW,OAC3B,eAAc,SAAS,MAAKA,QAAS;AAIvC,SAAKD,UAAW,MAAME,sBAAQ,OAAO,cAAc;AAGnD,SAAKH,KAAM,MAAKC,QAAS;AAGzB,OAAI,MAAKC,QAAS,aAChB,OAAM,MAAKE,mBAAoB,MAAKF,QAAS,aAAa;WAErD,OAAO;AACd,SAAM,IAAI,iBACR,kCAAkC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IACxF,2BACA,iBAAiB,QAAQ,QAAQ,OAClC;;;;;;;;CASL,OAAME,mBAAoB,OAA8C;EACtE,MAAM,UAAU,IAAI,aAAa;EACjC,MAAM,cAA2C,OAAO,QAAQ,MAAM,CAAC,KACpE,CAAC,MAAM,aAAa,CAAC,MAAM,QAAQ,OAAO,QAAQ,CAAC,CACrD;EAKD,MAAM,UAHU,MAAM,KAAK,YAAY,YAAY,EAG5B,QAAQ,MAAM,EAAE,UAAU,KAAK;AACtD,MAAI,OAAO,SAAS,EAElB,OAAM,IAAI,iBACR,mCAFiB,OAAO,KAAK,MAAM,GAAG,EAAE,KAAK,IAAI,EAAE,QAAQ,CAAC,KAAK,KAAK,IAGtE,wBACD;;;;;;;;;;;;;;;;;;CAoBL,MAAM,QAAQ,SAA2C;EACvD,MAAM,UAAU,KAAK;AAErB,MAAI;GASF,MAAM,EAAE,QAAQ,YAAY,eAAe,OAP7B,MAAM,QAAQ,MAAM,aAAa;IAC7C,MAAM,CAAC,MAAM,QAAQ;IACrB,QAAQ;IACR,QAAQ;IACT,CAAC,EAGqD,QAAQ;AAE/D,UAAO;IACL,SAAS,cAAc,OAAO,cAAc;IAC5C,UAAU,OAAO,QAAQ;IACzB,WAAW;IACZ;WACM,OAAO;AAEd,OAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,UAAU,CAC7D,OAAM,IAAI,iBACR,sBAAsB,WACtB,mBACA,MACD;AAGH,SAAM,IAAI,iBACR,6BAA6B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IACnF,kBACA,iBAAiB,QAAQ,QAAQ,OAClC;;;;;;;;;;;;;;;;;;;;;CAsBL,MAAM,YACJ,OAC+B;EAC/B,MAAM,UAAU,KAAK;EACrB,MAAM,UAAgC,EAAE;AAExC,OAAK,MAAM,CAAC,MAAM,YAAY,MAC5B,KAAI;GAEF,MAAM,YAAY,KAAK,UAAU,GAAG,KAAK,YAAY,IAAI,CAAC;AAC1D,OAAI,UAMF,QALmB,MAAM,QAAQ,MAAM,aAAa;IAClD,MAAM,CAAC,MAAM,aAAa,UAAU,GAAG;IACvC,QAAQ;IACR,QAAQ;IACT,CAAC,EACe,QAAQ;GAI3B,MAAM,cAAc,IAAI,aAAa,CAAC,OAAO,QAAQ;AACrD,SAAM,QAAQ,cAAc,MAAM,YAAY;AAC9C,WAAQ,KAAK;IAAE;IAAM,OAAO;IAAM,CAAC;WAC5B,OAAO;AACd,WAAQ,KAAK;IAAE;IAAM,OAAO,MAAKC,SAAU,MAAM;IAAE,CAAC;;AAIxD,SAAO;;;;;;;;;;;;;;;;;;;;;;;CAwBT,MAAM,cAAc,OAAkD;EACpE,MAAM,UAAU,KAAK;EACrB,MAAM,UAAkC,EAAE;AAE1C,OAAK,MAAM,QAAQ,MACjB,KAAI;GAQF,MAAM,EAAE,QAAQ,eAAe,OANjB,MAAM,QAAQ,MAAM,aAAa;IAC7C,MAAM,CAAC,MAAM,QAAQ,KAAK,GAAG;IAC7B,QAAQ;IACR,QAAQ;IACT,CAAC,EAEyC,QAAQ;AAEnD,OAAI,CAAC,OAAO,QACV,SAAQ,KAAK;IACX;IACA,SAAS;IACT,OAAO;IACR,CAAC;QACG;IACL,MAAM,UAAU,IAAI,aAAa,CAAC,OAAO,cAAc,GAAG;AAC1D,YAAQ,KAAK;KACX;KACA;KACA,OAAO;KACR,CAAC;;WAEG,OAAO;AACd,WAAQ,KAAK;IACX;IACA,SAAS;IACT,OAAO,MAAKA,SAAU,MAAM;IAC7B,CAAC;;AAIN,SAAO;;;;;;;;;;;;;CAmBT,MAAe,KACb,UACA,SAAiB,GACjB,QAAgB,KACC;EAEjB,MAAM,aACJ,OAAO,SAAS,OAAO,IAAI,SAAS,IAAI,KAAK,MAAM,OAAO,GAAG;EAC/D,MAAM,YACJ,OAAO,SAAS,MAAM,IAAI,QAAQ,KAAK,QAAQ,OAAO,mBAClD,KAAK,MAAM,MAAM,GACjB;EAGN,MAAM,cAAc,SAAS,QAAQ,MAAM,QAAQ;EAInD,MAAM,UAAU;aACP,YAAY;;;;aAIZ,YAAY;;;;gBAIT,WAAW,YAAY,UAAU;;;;KAI5C,YAAY;;EAGb,MAAM,SAAS,MAAM,KAAK,QAAQ,QAAQ;AAE1C,MAAI,OAAO,aAAa,EACtB,QAAO,gBAAgB,SAAS;AAGlC,SAAO,OAAO;;;;;;;;;;;;CAahB,MAAe,MACb,UACA,SACsB;EAEtB,MAAM,cAAc,SAAS,QAAQ,MAAM,QAAQ;AAInD,OADoB,MAAM,KAAK,QAAQ,YAAY,YAAY,GAAG,EAClD,aAAa,EAC3B,QAAO,EACL,OAAO,mBAAmB,SAAS,kFACpC;EAIH,MAAM,UAAU,IAAI,aAAa;EACjC,MAAM,eAAe,MAAM,KAAK,YAAY,CAC1C,CAAC,UAAU,QAAQ,OAAO,QAAQ,CAAC,CACpC,CAAC;AAEF,MAAI,aAAa,IAAI,MACnB,QAAO,EAAE,OAAO,yBAAyB,aAAa,GAAG,SAAS;AAGpE,SAAO;GAAE,MAAM;GAAU,aAAa;GAAM;;;;;;;;;;;;;;;;CAiB9C,MAAe,KACb,UACA,WACA,WACA,aAAsB,OACD;EAErB,MAAM,cAAc,SAAS,QAAQ,MAAM,QAAQ;AAInD,OADoB,MAAM,KAAK,QAAQ,YAAY,YAAY,GAAG,EAClD,aAAa,EAC3B,QAAO,EAAE,OAAO,gBAAgB,SAAS,cAAc;EAKzD,MAAM,oBAAoB,UAAU,QAAQ,MAAM,QAAQ;EAC1D,MAAM,cAAc,MAAM,KAAK,QAC7B,aAAa,kBAAkB,KAAK,YAAY,WACjD;EACD,MAAM,QAAQ,SAAS,YAAY,OAAO,MAAM,EAAE,GAAG,IAAI;AAEzD,MAAI,UAAU,EACZ,QAAO,EAAE,OAAO,6BAA6B,SAAS,IAAI;AAG5D,MAAI,QAAQ,KAAK,CAAC,WAChB,QAAO,EACL,OAAO,kCAAkC,SAAS,yCACnD;EAYH,MAAM,aAAa;cAJJ,OAAO,KAAK,WAAW,QAAQ,CAAC,SAAS,SAAS,CAKhD;cAJF,OAAO,KAAK,WAAW,QAAQ,CAAC,SAAS,SAAS,CAKhD;iDAC4B,aAAa,IAAI,EAAE;;;;;;;;;KAS/D,YAAY,OAAO,YAAY,eAAe,YAAY,SAAS,YAAY;;AAKhF,OAFmB,MAAM,KAAK,QAAQ,WAAW,EAElC,aAAa,EAC1B,QAAO,EAAE,OAAO,+BAA+B,SAAS,IAAI;AAG9D,SAAO;GAAE,MAAM;GAAU,aAAa;GAAM,aAAa;GAAO;;;;;;;;;;;;;;;;;CAkBlE,MAAM,QAAuB;AAC3B,MAAI,MAAKJ,QACP,KAAI;AACF,SAAM,MAAKA,QAAS,OAAO;YACnB;AACR,SAAKA,UAAW;;;;;;;;;;;;;;CAgBtB,MAAM,OAAsB;AAC1B,MAAI,MAAKA,QACP,KAAI;AACF,SAAM,MAAKA,QAAS,MAAM;YAClB;AACR,SAAKA,UAAW;;;;;;CAQtB,MAAM,OAAsB;AAC1B,QAAM,KAAK,OAAO;;;;;;CAOpB,iBAAiB,iBAA0B,WAAyB;AAClE,QAAKA,UAAW;AAChB,QAAKD,KAAM;;;;;;;;CASb,UAAU,OAAoC;AAC5C,MAAI,iBAAiB,OAAO;GAC1B,MAAM,MAAM,MAAM,QAAQ,aAAa;AAEvC,OAAI,IAAI,SAAS,YAAY,IAAI,IAAI,SAAS,SAAS,CACrD,QAAO;AAET,OAAI,IAAI,SAAS,aAAa,IAAI,IAAI,SAAS,SAAS,CACtD,QAAO;AAET,OAAI,IAAI,SAAS,YAAY,IAAI,IAAI,SAAS,SAAS,CACrD,QAAO;;AAIX,SAAO;;;;;;;;;;;;;;;;;;;;CAqBT,aAAa,OAAO,SAAoD;EACtE,MAAM,UAAU,IAAI,YAAY,QAAQ;AACxC,QAAM,QAAQ,YAAY;AAC1B,SAAO;;;;;;;;;;;;;;;;;;;CAoBT,aAAa,OACX,IACA,SACsB;EAEtB,IAAI;AACJ,MAAI;AACF,iBAAc,mBAAmB,SAAS,KAAK;WACxC,OAAO;AACd,SAAM,IAAI,iBACR,4EACA,yBACA,iBAAiB,QAAQ,QAAQ,OAClC;;AAGH,MAAI;AAEF,WAAQ,IAAI,oBAAoB,YAAY;GAE5C,MAAM,kBAAkB,MAAMG,sBAAQ,QAAQ,EAAE,IAAI,CAAC;GAErD,MAAM,cAAc,IAAI,aAAa;AAErC,gBAAYG,gBAAiB,iBAAiB,GAAG;AAEjD,UAAO;WACA,OAAO;AACd,SAAM,IAAI,iBACR,sBAAsB,MACtB,qBACA,iBAAiB,QAAQ,QAAQ,OAClC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CP,SAAgB,yBACd,SACyB;AACzB,QAAO,YAAY;AACjB,SAAO,MAAM,YAAY,OAAO,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyC5C,SAAgB,oCACd,SACgB;AAChB,cAAa"}
package/dist/index.d.cts CHANGED
@@ -1,13 +1,7 @@
1
1
  import { Sandbox } from "@deno/sandbox";
2
- import { BackendFactory, BaseSandbox, EditResult, ExecuteResponse, FileDownloadResponse, FileUploadResponse, WriteResult } from "deepagents";
2
+ import { BackendFactory, BaseSandbox, EditResult, ExecuteResponse, FileDownloadResponse, FileUploadResponse, SandboxError, SandboxErrorCode, WriteResult } from "deepagents";
3
3
 
4
4
  //#region src/types.d.ts
5
- /**
6
- * Type definitions for the Deno Sandbox backend.
7
- *
8
- * This module contains all type definitions for the @langchain/deno package,
9
- * including options and error types.
10
- */
11
5
  /**
12
6
  * Supported regions for Deno Deploy sandboxes.
13
7
  *
@@ -110,7 +104,7 @@ interface DenoSandboxOptions {
110
104
  *
111
105
  * Used to identify specific error conditions and handle them appropriately.
112
106
  */
113
- type DenoSandboxErrorCode = /** Sandbox has not been initialized - call initialize() first */"NOT_INITIALIZED" /** Sandbox is already initialized - cannot initialize twice */ | "ALREADY_INITIALIZED" /** 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" /** Command execution timed out */ | "COMMAND_TIMEOUT" /** Command execution failed */ | "COMMAND_FAILED" /** File operation (read/write) failed */ | "FILE_OPERATION_FAILED" /** Resource limits exceeded (CPU, memory, storage) */ | "RESOURCE_LIMIT_EXCEEDED";
107
+ 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";
114
108
  declare const DENO_SANDBOX_ERROR_SYMBOL: unique symbol;
115
109
  /**
116
110
  * Custom error class for Deno Sandbox operations.
@@ -140,7 +134,7 @@ declare const DENO_SANDBOX_ERROR_SYMBOL: unique symbol;
140
134
  * }
141
135
  * ```
142
136
  */
143
- declare class DenoSandboxError extends Error {
137
+ declare class DenoSandboxError extends SandboxError {
144
138
  readonly code: DenoSandboxErrorCode;
145
139
  readonly cause?: Error | undefined;
146
140
  [DENO_SANDBOX_ERROR_SYMBOL]: true;
@@ -226,7 +220,7 @@ declare class DenoSandbox extends BaseSandbox {
226
220
  * const denoSdk = sandbox.sandbox; // Access the raw SDK
227
221
  * ```
228
222
  */
229
- get sandbox(): Sandbox;
223
+ get instance(): Sandbox;
230
224
  /**
231
225
  * Check if the sandbox is initialized and running.
232
226
  */
@@ -422,18 +416,18 @@ declare class DenoSandbox extends BaseSandbox {
422
416
  * This allows you to resume working with a sandbox that was created
423
417
  * earlier with a duration-based lifetime.
424
418
  *
425
- * @param sandboxId - The ID of the sandbox to reconnect to
419
+ * @param id - The ID of the sandbox to reconnect to
426
420
  * @param options - Optional auth configuration (for token)
427
421
  * @returns A connected sandbox instance
428
422
  *
429
423
  * @example
430
424
  * ```typescript
431
425
  * // Resume a sandbox from a stored ID
432
- * const sandbox = await DenoSandbox.connect("sandbox-abc123");
426
+ * const sandbox = await DenoSandbox.fromId("sandbox-abc123");
433
427
  * const result = await sandbox.execute("ls -la");
434
428
  * ```
435
429
  */
436
- static connect(sandboxId: string, options?: Pick<DenoSandboxOptions, "auth">): Promise<DenoSandbox>;
430
+ static fromId(id: string, options?: Pick<DenoSandboxOptions, "auth">): Promise<DenoSandbox>;
437
431
  }
438
432
  /**
439
433
  * Async factory function type for creating Deno Sandbox instances.
package/dist/index.d.ts CHANGED
@@ -1,13 +1,7 @@
1
1
  import { Sandbox } from "@deno/sandbox";
2
- import { BackendFactory, BaseSandbox, EditResult, ExecuteResponse, FileDownloadResponse, FileUploadResponse, WriteResult } from "deepagents";
2
+ import { BackendFactory, BaseSandbox, EditResult, ExecuteResponse, FileDownloadResponse, FileUploadResponse, SandboxError, SandboxErrorCode, WriteResult } from "deepagents";
3
3
 
4
4
  //#region src/types.d.ts
5
- /**
6
- * Type definitions for the Deno Sandbox backend.
7
- *
8
- * This module contains all type definitions for the @langchain/deno package,
9
- * including options and error types.
10
- */
11
5
  /**
12
6
  * Supported regions for Deno Deploy sandboxes.
13
7
  *
@@ -110,7 +104,7 @@ interface DenoSandboxOptions {
110
104
  *
111
105
  * Used to identify specific error conditions and handle them appropriately.
112
106
  */
113
- type DenoSandboxErrorCode = /** Sandbox has not been initialized - call initialize() first */"NOT_INITIALIZED" /** Sandbox is already initialized - cannot initialize twice */ | "ALREADY_INITIALIZED" /** 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" /** Command execution timed out */ | "COMMAND_TIMEOUT" /** Command execution failed */ | "COMMAND_FAILED" /** File operation (read/write) failed */ | "FILE_OPERATION_FAILED" /** Resource limits exceeded (CPU, memory, storage) */ | "RESOURCE_LIMIT_EXCEEDED";
107
+ 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";
114
108
  declare const DENO_SANDBOX_ERROR_SYMBOL: unique symbol;
115
109
  /**
116
110
  * Custom error class for Deno Sandbox operations.
@@ -140,7 +134,7 @@ declare const DENO_SANDBOX_ERROR_SYMBOL: unique symbol;
140
134
  * }
141
135
  * ```
142
136
  */
143
- declare class DenoSandboxError extends Error {
137
+ declare class DenoSandboxError extends SandboxError {
144
138
  readonly code: DenoSandboxErrorCode;
145
139
  readonly cause?: Error | undefined;
146
140
  [DENO_SANDBOX_ERROR_SYMBOL]: true;
@@ -226,7 +220,7 @@ declare class DenoSandbox extends BaseSandbox {
226
220
  * const denoSdk = sandbox.sandbox; // Access the raw SDK
227
221
  * ```
228
222
  */
229
- get sandbox(): Sandbox;
223
+ get instance(): Sandbox;
230
224
  /**
231
225
  * Check if the sandbox is initialized and running.
232
226
  */
@@ -422,18 +416,18 @@ declare class DenoSandbox extends BaseSandbox {
422
416
  * This allows you to resume working with a sandbox that was created
423
417
  * earlier with a duration-based lifetime.
424
418
  *
425
- * @param sandboxId - The ID of the sandbox to reconnect to
419
+ * @param id - The ID of the sandbox to reconnect to
426
420
  * @param options - Optional auth configuration (for token)
427
421
  * @returns A connected sandbox instance
428
422
  *
429
423
  * @example
430
424
  * ```typescript
431
425
  * // Resume a sandbox from a stored ID
432
- * const sandbox = await DenoSandbox.connect("sandbox-abc123");
426
+ * const sandbox = await DenoSandbox.fromId("sandbox-abc123");
433
427
  * const result = await sandbox.execute("ls -la");
434
428
  * ```
435
429
  */
436
- static connect(sandboxId: string, options?: Pick<DenoSandboxOptions, "auth">): Promise<DenoSandbox>;
430
+ static fromId(id: string, options?: Pick<DenoSandboxOptions, "auth">): Promise<DenoSandbox>;
437
431
  }
438
432
  /**
439
433
  * Async factory function type for creating Deno Sandbox instances.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Sandbox } from "@deno/sandbox";
2
- import { BaseSandbox } from "deepagents";
2
+ import { BaseSandbox, SandboxError } from "deepagents";
3
3
 
4
4
  //#region src/auth.ts
5
5
  /**
@@ -60,6 +60,12 @@ function getAuthCredentials(options) {
60
60
 
61
61
  //#endregion
62
62
  //#region src/types.ts
63
+ /**
64
+ * Type definitions for the Deno Sandbox backend.
65
+ *
66
+ * This module contains all type definitions for the @langchain/deno package,
67
+ * including options and error types.
68
+ */
63
69
  const DENO_SANDBOX_ERROR_SYMBOL = Symbol.for("deno.sandbox.error");
64
70
  /**
65
71
  * Custom error class for Deno Sandbox operations.
@@ -89,7 +95,7 @@ const DENO_SANDBOX_ERROR_SYMBOL = Symbol.for("deno.sandbox.error");
89
95
  * }
90
96
  * ```
91
97
  */
92
- var DenoSandboxError = class DenoSandboxError extends Error {
98
+ var DenoSandboxError = class DenoSandboxError extends SandboxError {
93
99
  [DENO_SANDBOX_ERROR_SYMBOL];
94
100
  /** Error name for instanceof checks and logging */
95
101
  name = "DenoSandboxError";
@@ -101,7 +107,7 @@ var DenoSandboxError = class DenoSandboxError extends Error {
101
107
  * @param cause - Original error that caused this error (for debugging)
102
108
  */
103
109
  constructor(message, code, cause) {
104
- super(message);
110
+ super(message, code, cause);
105
111
  this.code = code;
106
112
  this.cause = cause;
107
113
  Object.setPrototypeOf(this, DenoSandboxError.prototype);
@@ -197,7 +203,7 @@ var DenoSandbox = class DenoSandbox extends BaseSandbox {
197
203
  * const denoSdk = sandbox.sandbox; // Access the raw SDK
198
204
  * ```
199
205
  */
200
- get sandbox() {
206
+ get instance() {
201
207
  if (!this.#sandbox) throw new DenoSandboxError("Sandbox not initialized. Call initialize() or use DenoSandbox.create()", "NOT_INITIALIZED");
202
208
  return this.#sandbox;
203
209
  }
@@ -301,7 +307,7 @@ var DenoSandbox = class DenoSandbox extends BaseSandbox {
301
307
  * ```
302
308
  */
303
309
  async execute(command) {
304
- const sandbox = this.sandbox;
310
+ const sandbox = this.instance;
305
311
  try {
306
312
  const { status, stdoutText, stderrText } = await (await sandbox.spawn("/bin/bash", {
307
313
  args: ["-c", command],
@@ -337,7 +343,7 @@ var DenoSandbox = class DenoSandbox extends BaseSandbox {
337
343
  * ```
338
344
  */
339
345
  async uploadFiles(files) {
340
- const sandbox = this.sandbox;
346
+ const sandbox = this.instance;
341
347
  const results = [];
342
348
  for (const [path, content] of files) try {
343
349
  const parentDir = path.substring(0, path.lastIndexOf("/"));
@@ -382,7 +388,7 @@ var DenoSandbox = class DenoSandbox extends BaseSandbox {
382
388
  * ```
383
389
  */
384
390
  async downloadFiles(paths) {
385
- const sandbox = this.sandbox;
391
+ const sandbox = this.instance;
386
392
  const results = [];
387
393
  for (const path of paths) try {
388
394
  const { status, stdoutText } = await (await sandbox.spawn("/bin/bash", {
@@ -608,18 +614,18 @@ awk -v old="$OLD" -v new="$NEW" -v replace_all=${replaceAll ? 1 : 0} '
608
614
  * This allows you to resume working with a sandbox that was created
609
615
  * earlier with a duration-based lifetime.
610
616
  *
611
- * @param sandboxId - The ID of the sandbox to reconnect to
617
+ * @param id - The ID of the sandbox to reconnect to
612
618
  * @param options - Optional auth configuration (for token)
613
619
  * @returns A connected sandbox instance
614
620
  *
615
621
  * @example
616
622
  * ```typescript
617
623
  * // Resume a sandbox from a stored ID
618
- * const sandbox = await DenoSandbox.connect("sandbox-abc123");
624
+ * const sandbox = await DenoSandbox.fromId("sandbox-abc123");
619
625
  * const result = await sandbox.execute("ls -la");
620
626
  * ```
621
627
  */
622
- static async connect(sandboxId, options) {
628
+ static async fromId(id, options) {
623
629
  let credentials;
624
630
  try {
625
631
  credentials = getAuthCredentials(options?.auth);
@@ -628,12 +634,12 @@ awk -v old="$OLD" -v new="$NEW" -v replace_all=${replaceAll ? 1 : 0} '
628
634
  }
629
635
  try {
630
636
  process.env.DENO_DEPLOY_TOKEN = credentials.token;
631
- const existingSandbox = await Sandbox.connect({ id: sandboxId });
637
+ const existingSandbox = await Sandbox.connect({ id });
632
638
  const denoSandbox = new DenoSandbox();
633
- denoSandbox.#setFromExisting(existingSandbox, sandboxId);
639
+ denoSandbox.#setFromExisting(existingSandbox, id);
634
640
  return denoSandbox;
635
641
  } catch (error) {
636
- throw new DenoSandboxError(`Sandbox not found: ${sandboxId}`, "SANDBOX_NOT_FOUND", error instanceof Error ? error : void 0);
642
+ throw new DenoSandboxError(`Sandbox not found: ${id}`, "SANDBOX_NOT_FOUND", error instanceof Error ? error : void 0);
637
643
  }
638
644
  }
639
645
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["#id","#sandbox","#options","#uploadInitialFiles","#mapError","#setFromExisting"],"sources":["../src/auth.ts","../src/types.ts","../src/sandbox.ts"],"sourcesContent":["/**\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\";\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 {Error} 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 Error(\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({ auth: { token: '...' } })\",\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 {Error} 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","/**\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\n/**\n * Supported regions for Deno Deploy sandboxes.\n *\n * Currently available regions:\n * - `ams`: Amsterdam\n * - `ord`: Chicago\n */\nexport type DenoSandboxRegion = \"ams\" | \"ord\";\n\n/**\n * Sandbox lifetime configuration.\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 * Configuration options for creating a Deno Sandbox.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * memoryMb: 1024, // 1GB memory\n * lifetime: \"5m\", // 5 minutes\n * region: \"iad\", // US East\n * };\n * ```\n */\nexport interface DenoSandboxOptions {\n /**\n * Amount of memory allocated to the sandbox in megabytes.\n *\n * Memory limits:\n * - Minimum: 768MB\n * - Maximum: 4096MB\n *\n * @default 768\n */\n memoryMb?: number;\n\n /**\n * Sandbox lifetime configuration.\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 * 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 * memoryMb: 1024,\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 * ### 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/**\n * Error codes for Deno Sandbox operations.\n *\n * Used to identify specific error conditions and handle them appropriately.\n */\nexport type DenoSandboxErrorCode =\n /** Sandbox has not been initialized - call initialize() first */\n | \"NOT_INITIALIZED\"\n /** Sandbox is already initialized - cannot initialize twice */\n | \"ALREADY_INITIALIZED\"\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 /** Command execution timed out */\n | \"COMMAND_TIMEOUT\"\n /** Command execution failed */\n | \"COMMAND_FAILED\"\n /** File operation (read/write) failed */\n | \"FILE_OPERATION_FAILED\"\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 Error {\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);\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","/* 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 } from \"@deno/sandbox\";\nimport {\n BaseSandbox,\n type EditResult,\n type ExecuteResponse,\n type FileDownloadResponse,\n type FileOperationError,\n type FileUploadResponse,\n type BackendFactory,\n type WriteResult,\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 * memoryMb: 1024,\n * lifetime: \"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 sandbox(): 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({ memoryMb: 1024 });\n * await sandbox.initialize();\n *\n * // Or use the factory method\n * const sandbox = await DenoSandbox.create({ memoryMb: 1024 });\n * ```\n */\n constructor(options: DenoSandboxOptions = {}) {\n super();\n\n // Set defaults\n this.#options = {\n memoryMb: 768,\n lifetime: \"session\",\n ...options,\n };\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 // Get authentication credentials\n let credentials: { token: string };\n try {\n credentials = getAuthCredentials(this.#options.auth);\n } catch (error) {\n throw new DenoSandboxError(\n \"Failed to authenticate with Deno Deploy. Check your token configuration.\",\n \"AUTHENTICATION_FAILED\",\n error instanceof Error ? error : undefined,\n );\n }\n\n try {\n // Set the token in environment for the SDK\n process.env.DENO_DEPLOY_TOKEN = credentials.token;\n\n // Build SDK create options\n const createOptions: Parameters<typeof Sandbox.create>[0] = {};\n\n // Add optional memory configuration\n if (this.#options.memoryMb !== undefined) {\n createOptions.memoryMb = this.#options.memoryMb;\n }\n\n // Add optional lifetime configuration\n if (this.#options.lifetime !== undefined) {\n createOptions.lifetime = this.#options.lifetime;\n }\n\n // Add optional region configuration\n if (this.#options.region !== undefined) {\n createOptions.region = this.#options.region;\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.sandbox; // 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.sandbox; // 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.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.sandbox; // 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 // Override BaseSandbox methods that use Python with pure shell implementations\n // Deno sandboxes don't have Python installed, only basic Unix tools\n // ============================================================================\n\n /**\n * Read a file's content with line numbers.\n *\n * Override of BaseSandbox.read() to use awk instead of Python,\n * since Deno sandboxes don't have Python installed.\n *\n * @param filePath - Absolute path to the file\n * @param offset - Line offset (0-indexed, default 0)\n * @param limit - Maximum lines to return (default 500)\n * @returns Formatted file content with line numbers, or error message\n */\n override async read(\n filePath: string,\n offset: number = 0,\n limit: number = 500,\n ): Promise<string> {\n // Coerce offset and limit to safe non-negative integers\n const safeOffset =\n Number.isFinite(offset) && offset > 0 ? Math.floor(offset) : 0;\n const safeLimit =\n Number.isFinite(limit) && limit > 0 && limit < Number.MAX_SAFE_INTEGER\n ? Math.floor(limit)\n : 500;\n\n // Escape path for shell\n const escapedPath = filePath.replace(/'/g, \"'\\\\''\");\n\n // Build shell command using awk for portable line number formatting\n // First check if file exists, then format with line numbers\n const command = `\nif [ ! -f '${escapedPath}' ]; then\n echo \"Error: File not found\"\n exit 1\nfi\nif [ ! -s '${escapedPath}' ]; then\n echo \"System reminder: File exists but has empty contents\"\n exit 0\nfi\nawk -v offset=${safeOffset} -v limit=${safeLimit} '\n NR > offset && NR <= offset + limit {\n printf \"%6d\\\\t%s\\\\n\", NR, $0\n }\n' '${escapedPath}'\n`;\n\n const result = await this.execute(command);\n\n if (result.exitCode !== 0) {\n return `Error: File '${filePath}' not found`;\n }\n\n return result.output;\n }\n\n /**\n * Create a new file with content.\n *\n * Override of BaseSandbox.write() to use shell commands instead of Python,\n * since Deno sandboxes don't have Python installed.\n *\n * @param filePath - Absolute path for the new file\n * @param content - File content to write\n * @returns WriteResult with error populated on failure\n */\n override async write(\n filePath: string,\n content: string,\n ): Promise<WriteResult> {\n // Escape path for shell\n const escapedPath = filePath.replace(/'/g, \"'\\\\''\");\n\n // Check if file already exists\n const checkResult = await this.execute(`test -f '${escapedPath}'`);\n if (checkResult.exitCode === 0) {\n return {\n error: `Cannot write to ${filePath} because it already exists. Read and then make an edit, or write to a new path.`,\n };\n }\n\n // Use uploadFiles for reliable content writing (handles binary, special chars, etc.)\n const encoder = new TextEncoder();\n const uploadResult = await this.uploadFiles([\n [filePath, encoder.encode(content)],\n ]);\n\n if (uploadResult[0]?.error) {\n return { error: `Failed to write file: ${uploadResult[0].error}` };\n }\n\n return { path: filePath, filesUpdate: null };\n }\n\n /**\n * Edit a file by replacing string occurrences.\n *\n * Override of BaseSandbox.edit() to use shell commands instead of Python,\n * since Deno sandboxes don't have Python installed.\n *\n * Uses sed for in-place replacement with proper escaping.\n *\n * @param filePath - Absolute path to the file\n * @param oldString - String to find and replace\n * @param newString - Replacement string\n * @param replaceAll - If true, replace all occurrences (default: false)\n * @returns EditResult with error, path, and occurrences\n */\n override async edit(\n filePath: string,\n oldString: string,\n newString: string,\n replaceAll: boolean = false,\n ): Promise<EditResult> {\n // Escape path for shell\n const escapedPath = filePath.replace(/'/g, \"'\\\\''\");\n\n // Check if file exists\n const checkResult = await this.execute(`test -f '${escapedPath}'`);\n if (checkResult.exitCode !== 0) {\n return { error: `Error: File '${filePath}' not found` };\n }\n\n // Count occurrences first using grep -F (fixed string)\n // Escape old string for grep\n const escapedOldForGrep = oldString.replace(/'/g, \"'\\\\''\");\n const countResult = await this.execute(\n `grep -oF '${escapedOldForGrep}' '${escapedPath}' | wc -l`,\n );\n const count = parseInt(countResult.output.trim(), 10) || 0;\n\n if (count === 0) {\n return { error: `String not found in file '${filePath}'` };\n }\n\n if (count > 1 && !replaceAll) {\n return {\n error: `Multiple occurrences found in '${filePath}'. Use replaceAll=true to replace all.`,\n };\n }\n\n // Perform the replacement using sed\n // Use a delimiter that's unlikely to be in the strings (we'll use \\x00 if available, otherwise |)\n // For safety, we'll use awk which handles arbitrary strings better than sed\n\n // Base64 encode both strings to safely pass them to awk\n const oldB64 = Buffer.from(oldString, \"utf-8\").toString(\"base64\");\n const newB64 = Buffer.from(newString, \"utf-8\").toString(\"base64\");\n\n // Use awk with base64 decoding for safe string replacement\n const awkCommand = `\nOLD=$(echo '${oldB64}' | base64 -d)\nNEW=$(echo '${newB64}' | base64 -d)\nawk -v old=\"$OLD\" -v new=\"$NEW\" -v replace_all=${replaceAll ? 1 : 0} '\n{\n if (replace_all) {\n gsub(old, new)\n } else {\n sub(old, new)\n }\n print\n}\n' '${escapedPath}' > '${escapedPath}.tmp' && mv '${escapedPath}.tmp' '${escapedPath}'\n`;\n\n const editResult = await this.execute(awkCommand);\n\n if (editResult.exitCode !== 0) {\n return { error: `Unknown error editing file '${filePath}'` };\n }\n\n return { path: filePath, filesUpdate: null, occurrences: count };\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 // Static Factory Methods\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 * memoryMb: 1024,\n * lifetime: \"10m\",\n * region: \"iad\",\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 sandboxId - 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.connect(\"sandbox-abc123\");\n * const result = await sandbox.execute(\"ls -la\");\n * ```\n */\n static async connect(\n sandboxId: string,\n options?: Pick<DenoSandboxOptions, \"auth\">,\n ): Promise<DenoSandbox> {\n // Get authentication credentials\n let credentials: { token: string };\n try {\n credentials = getAuthCredentials(options?.auth);\n } catch (error) {\n throw new DenoSandboxError(\n \"Failed to authenticate with Deno Deploy. Check your token configuration.\",\n \"AUTHENTICATION_FAILED\",\n error instanceof Error ? error : undefined,\n );\n }\n\n try {\n // Set the token in environment for the SDK\n process.env.DENO_DEPLOY_TOKEN = credentials.token;\n\n const existingSandbox = await Sandbox.connect({ id: sandboxId });\n\n const denoSandbox = new DenoSandbox();\n // Set the existing sandbox directly (bypass initialize)\n denoSandbox.#setFromExisting(existingSandbox, sandboxId);\n\n return denoSandbox;\n } catch (error) {\n throw new DenoSandboxError(\n `Sandbox not found: ${sandboxId}`,\n \"SANDBOX_NOT_FOUND\",\n error instanceof Error ? error : undefined,\n );\n }\n }\n}\n\n// ============================================================================\n// Factory Functions\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({ memoryMb: 1024 });\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({ memoryMb: 1024 });\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/auth.ts","../src/types.ts","../src/sandbox.ts"],"sourcesContent":["/**\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\";\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 {Error} 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 Error(\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({ auth: { token: '...' } })\",\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 {Error} 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","/**\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 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 = \"ams\" | \"ord\";\n\n/**\n * Sandbox lifetime configuration.\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 * Configuration options for creating a Deno Sandbox.\n *\n * @example\n * ```typescript\n * const options: DenoSandboxOptions = {\n * memoryMb: 1024, // 1GB memory\n * lifetime: \"5m\", // 5 minutes\n * region: \"iad\", // US East\n * };\n * ```\n */\nexport interface DenoSandboxOptions {\n /**\n * Amount of memory allocated to the sandbox in megabytes.\n *\n * Memory limits:\n * - Minimum: 768MB\n * - Maximum: 4096MB\n *\n * @default 768\n */\n memoryMb?: number;\n\n /**\n * Sandbox lifetime configuration.\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 * 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 * memoryMb: 1024,\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 * ### 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/**\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","/* 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 } from \"@deno/sandbox\";\nimport {\n BaseSandbox,\n type EditResult,\n type ExecuteResponse,\n type FileDownloadResponse,\n type FileOperationError,\n type FileUploadResponse,\n type BackendFactory,\n type WriteResult,\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 * memoryMb: 1024,\n * lifetime: \"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({ memoryMb: 1024 });\n * await sandbox.initialize();\n *\n * // Or use the factory method\n * const sandbox = await DenoSandbox.create({ memoryMb: 1024 });\n * ```\n */\n constructor(options: DenoSandboxOptions = {}) {\n super();\n\n // Set defaults\n this.#options = {\n memoryMb: 768,\n lifetime: \"session\",\n ...options,\n };\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 // Get authentication credentials\n let credentials: { token: string };\n try {\n credentials = getAuthCredentials(this.#options.auth);\n } catch (error) {\n throw new DenoSandboxError(\n \"Failed to authenticate with Deno Deploy. Check your token configuration.\",\n \"AUTHENTICATION_FAILED\",\n error instanceof Error ? error : undefined,\n );\n }\n\n try {\n // Set the token in environment for the SDK\n process.env.DENO_DEPLOY_TOKEN = credentials.token;\n\n // Build SDK create options\n const createOptions: Parameters<typeof Sandbox.create>[0] = {};\n\n // Add optional memory configuration\n if (this.#options.memoryMb !== undefined) {\n createOptions.memoryMb = this.#options.memoryMb;\n }\n\n // Add optional lifetime configuration\n if (this.#options.lifetime !== undefined) {\n createOptions.lifetime = this.#options.lifetime;\n }\n\n // Add optional region configuration\n if (this.#options.region !== undefined) {\n createOptions.region = this.#options.region;\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.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 // Override BaseSandbox methods that use Python with pure shell implementations\n // Deno sandboxes don't have Python installed, only basic Unix tools\n // ============================================================================\n\n /**\n * Read a file's content with line numbers.\n *\n * Override of BaseSandbox.read() to use awk instead of Python,\n * since Deno sandboxes don't have Python installed.\n *\n * @param filePath - Absolute path to the file\n * @param offset - Line offset (0-indexed, default 0)\n * @param limit - Maximum lines to return (default 500)\n * @returns Formatted file content with line numbers, or error message\n */\n override async read(\n filePath: string,\n offset: number = 0,\n limit: number = 500,\n ): Promise<string> {\n // Coerce offset and limit to safe non-negative integers\n const safeOffset =\n Number.isFinite(offset) && offset > 0 ? Math.floor(offset) : 0;\n const safeLimit =\n Number.isFinite(limit) && limit > 0 && limit < Number.MAX_SAFE_INTEGER\n ? Math.floor(limit)\n : 500;\n\n // Escape path for shell\n const escapedPath = filePath.replace(/'/g, \"'\\\\''\");\n\n // Build shell command using awk for portable line number formatting\n // First check if file exists, then format with line numbers\n const command = `\nif [ ! -f '${escapedPath}' ]; then\n echo \"Error: File not found\"\n exit 1\nfi\nif [ ! -s '${escapedPath}' ]; then\n echo \"System reminder: File exists but has empty contents\"\n exit 0\nfi\nawk -v offset=${safeOffset} -v limit=${safeLimit} '\n NR > offset && NR <= offset + limit {\n printf \"%6d\\\\t%s\\\\n\", NR, $0\n }\n' '${escapedPath}'\n`;\n\n const result = await this.execute(command);\n\n if (result.exitCode !== 0) {\n return `Error: File '${filePath}' not found`;\n }\n\n return result.output;\n }\n\n /**\n * Create a new file with content.\n *\n * Override of BaseSandbox.write() to use shell commands instead of Python,\n * since Deno sandboxes don't have Python installed.\n *\n * @param filePath - Absolute path for the new file\n * @param content - File content to write\n * @returns WriteResult with error populated on failure\n */\n override async write(\n filePath: string,\n content: string,\n ): Promise<WriteResult> {\n // Escape path for shell\n const escapedPath = filePath.replace(/'/g, \"'\\\\''\");\n\n // Check if file already exists\n const checkResult = await this.execute(`test -f '${escapedPath}'`);\n if (checkResult.exitCode === 0) {\n return {\n error: `Cannot write to ${filePath} because it already exists. Read and then make an edit, or write to a new path.`,\n };\n }\n\n // Use uploadFiles for reliable content writing (handles binary, special chars, etc.)\n const encoder = new TextEncoder();\n const uploadResult = await this.uploadFiles([\n [filePath, encoder.encode(content)],\n ]);\n\n if (uploadResult[0]?.error) {\n return { error: `Failed to write file: ${uploadResult[0].error}` };\n }\n\n return { path: filePath, filesUpdate: null };\n }\n\n /**\n * Edit a file by replacing string occurrences.\n *\n * Override of BaseSandbox.edit() to use shell commands instead of Python,\n * since Deno sandboxes don't have Python installed.\n *\n * Uses sed for in-place replacement with proper escaping.\n *\n * @param filePath - Absolute path to the file\n * @param oldString - String to find and replace\n * @param newString - Replacement string\n * @param replaceAll - If true, replace all occurrences (default: false)\n * @returns EditResult with error, path, and occurrences\n */\n override async edit(\n filePath: string,\n oldString: string,\n newString: string,\n replaceAll: boolean = false,\n ): Promise<EditResult> {\n // Escape path for shell\n const escapedPath = filePath.replace(/'/g, \"'\\\\''\");\n\n // Check if file exists\n const checkResult = await this.execute(`test -f '${escapedPath}'`);\n if (checkResult.exitCode !== 0) {\n return { error: `Error: File '${filePath}' not found` };\n }\n\n // Count occurrences first using grep -F (fixed string)\n // Escape old string for grep\n const escapedOldForGrep = oldString.replace(/'/g, \"'\\\\''\");\n const countResult = await this.execute(\n `grep -oF '${escapedOldForGrep}' '${escapedPath}' | wc -l`,\n );\n const count = parseInt(countResult.output.trim(), 10) || 0;\n\n if (count === 0) {\n return { error: `String not found in file '${filePath}'` };\n }\n\n if (count > 1 && !replaceAll) {\n return {\n error: `Multiple occurrences found in '${filePath}'. Use replaceAll=true to replace all.`,\n };\n }\n\n // Perform the replacement using sed\n // Use a delimiter that's unlikely to be in the strings (we'll use \\x00 if available, otherwise |)\n // For safety, we'll use awk which handles arbitrary strings better than sed\n\n // Base64 encode both strings to safely pass them to awk\n const oldB64 = Buffer.from(oldString, \"utf-8\").toString(\"base64\");\n const newB64 = Buffer.from(newString, \"utf-8\").toString(\"base64\");\n\n // Use awk with base64 decoding for safe string replacement\n const awkCommand = `\nOLD=$(echo '${oldB64}' | base64 -d)\nNEW=$(echo '${newB64}' | base64 -d)\nawk -v old=\"$OLD\" -v new=\"$NEW\" -v replace_all=${replaceAll ? 1 : 0} '\n{\n if (replace_all) {\n gsub(old, new)\n } else {\n sub(old, new)\n }\n print\n}\n' '${escapedPath}' > '${escapedPath}.tmp' && mv '${escapedPath}.tmp' '${escapedPath}'\n`;\n\n const editResult = await this.execute(awkCommand);\n\n if (editResult.exitCode !== 0) {\n return { error: `Unknown error editing file '${filePath}'` };\n }\n\n return { path: filePath, filesUpdate: null, occurrences: count };\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 * memoryMb: 1024,\n * lifetime: \"10m\",\n * region: \"iad\",\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<DenoSandboxOptions, \"auth\">,\n ): Promise<DenoSandbox> {\n // Get authentication credentials\n let credentials: { token: string };\n try {\n credentials = getAuthCredentials(options?.auth);\n } catch (error) {\n throw new DenoSandboxError(\n \"Failed to authenticate with Deno Deploy. Check your token configuration.\",\n \"AUTHENTICATION_FAILED\",\n error instanceof Error ? error : undefined,\n );\n }\n\n try {\n // Set the token in environment for the SDK\n process.env.DENO_DEPLOY_TOKEN = credentials.token;\n\n const existingSandbox = await Sandbox.connect({ id });\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({ memoryMb: 1024 });\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({ memoryMb: 1024 });\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,21 +1,11 @@
1
1
  {
2
2
  "name": "@langchain/deno",
3
- "version": "0.0.1",
3
+ "version": "0.1.1",
4
4
  "description": "Deno Sandbox backend for deepagents",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
8
  "type": "module",
9
- "scripts": {
10
- "build": "tsdown",
11
- "clean": "rm -rf dist/ .tsdown/",
12
- "dev": "tsc --watch",
13
- "typecheck": "tsc --noEmit",
14
- "prepublishOnly": "pnpm build",
15
- "test": "vitest run",
16
- "test:unit": "vitest run",
17
- "test:int": "vitest run --mode int"
18
- },
19
9
  "repository": {
20
10
  "type": "git",
21
11
  "url": "git+https://github.com/langchain-ai/deepagentsjs.git"
@@ -40,7 +30,7 @@
40
30
  "@deno/sandbox": "^0.2.2"
41
31
  },
42
32
  "peerDependencies": {
43
- "deepagents": "workspace:*"
33
+ "deepagents": ">=1.6.0"
44
34
  },
45
35
  "devDependencies": {
46
36
  "@tsconfig/recommended": "^1.0.13",
@@ -50,7 +40,9 @@
50
40
  "tsdown": "^0.20.1",
51
41
  "tsx": "^4.21.0",
52
42
  "typescript": "^5.9.3",
53
- "vitest": "^4.0.18"
43
+ "vitest": "^4.0.18",
44
+ "deepagents": "1.7.1",
45
+ "@langchain/standard-tests": "0.0.1"
54
46
  },
55
47
  "exports": {
56
48
  ".": {
@@ -67,5 +59,14 @@
67
59
  },
68
60
  "files": [
69
61
  "dist/**/*"
70
- ]
71
- }
62
+ ],
63
+ "scripts": {
64
+ "build": "tsdown",
65
+ "clean": "rm -rf dist/ .tsdown/",
66
+ "dev": "tsc --watch",
67
+ "typecheck": "tsc --noEmit",
68
+ "test": "vitest run",
69
+ "test:unit": "vitest run",
70
+ "test:int": "vitest run --mode int"
71
+ }
72
+ }