@langchain/deno 0.0.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/dist/index.cjs ADDED
@@ -0,0 +1,727 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ let _deno_sandbox = require("@deno/sandbox");
3
+ let deepagents = require("deepagents");
4
+
5
+ //#region src/auth.ts
6
+ /**
7
+ * Get the authentication token for Deno Sandbox API.
8
+ *
9
+ * Authentication is resolved in the following priority order:
10
+ *
11
+ * 1. **Explicit token**: If `options.token` is provided, it is used directly.
12
+ * 2. **DENO_DEPLOY_TOKEN**: Environment variable for Deno Deploy access token.
13
+ *
14
+ * If no token is found, an error is thrown with setup instructions.
15
+ *
16
+ * ## Environment Variable Setup
17
+ *
18
+ * ```bash
19
+ * # Go to https://app.deno.com -> Settings -> Organization Tokens
20
+ * # Create a new token and set it as environment variable
21
+ * export DENO_DEPLOY_TOKEN=your_token_here
22
+ * ```
23
+ *
24
+ * @param options - Optional authentication configuration from DenoSandboxOptions
25
+ * @returns The authentication token string
26
+ * @throws {Error} If no authentication token is available
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * // With explicit token
31
+ * const token = getAuthToken({ token: "my-token" });
32
+ *
33
+ * // Using environment variables (auto-detected)
34
+ * const token = getAuthToken();
35
+ *
36
+ * // From DenoSandboxOptions
37
+ * const options: DenoSandboxOptions = {
38
+ * auth: { token: "my-token" }
39
+ * };
40
+ * const token = getAuthToken(options.auth);
41
+ * ```
42
+ */
43
+ function getAuthToken(options) {
44
+ if (options?.token) return options.token;
45
+ const deployToken = process.env.DENO_DEPLOY_TOKEN;
46
+ if (deployToken) return deployToken;
47
+ throw new Error("Deno Deploy authentication required. Provide a token using one of these methods:\n\n1. Set DENO_DEPLOY_TOKEN environment variable:\n Go to https://app.deno.com -> Settings -> Organization Tokens\n Create a new token and run: export DENO_DEPLOY_TOKEN=your_token_here\n\n2. Pass token directly in options:\n new DenoSandbox({ auth: { token: '...' } })");
48
+ }
49
+ /**
50
+ * Get authentication credentials for Deno Sandbox API.
51
+ *
52
+ * This function returns the credentials needed for the Deno SDK.
53
+ *
54
+ * @param options - Optional authentication configuration from DenoSandboxOptions
55
+ * @returns Complete authentication credentials
56
+ * @throws {Error} If no authentication token is available
57
+ */
58
+ function getAuthCredentials(options) {
59
+ return { token: getAuthToken(options) };
60
+ }
61
+
62
+ //#endregion
63
+ //#region src/types.ts
64
+ const DENO_SANDBOX_ERROR_SYMBOL = Symbol.for("deno.sandbox.error");
65
+ /**
66
+ * Custom error class for Deno Sandbox operations.
67
+ *
68
+ * Provides structured error information including:
69
+ * - Human-readable message
70
+ * - Error code for programmatic handling
71
+ * - Original cause for debugging
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * try {
76
+ * await sandbox.execute("some command");
77
+ * } catch (error) {
78
+ * if (error instanceof DenoSandboxError) {
79
+ * switch (error.code) {
80
+ * case "NOT_INITIALIZED":
81
+ * await sandbox.initialize();
82
+ * break;
83
+ * case "COMMAND_TIMEOUT":
84
+ * console.error("Command took too long");
85
+ * break;
86
+ * default:
87
+ * throw error;
88
+ * }
89
+ * }
90
+ * }
91
+ * ```
92
+ */
93
+ var DenoSandboxError = class DenoSandboxError extends Error {
94
+ [DENO_SANDBOX_ERROR_SYMBOL];
95
+ /** Error name for instanceof checks and logging */
96
+ name = "DenoSandboxError";
97
+ /**
98
+ * Creates a new DenoSandboxError.
99
+ *
100
+ * @param message - Human-readable error description
101
+ * @param code - Structured error code for programmatic handling
102
+ * @param cause - Original error that caused this error (for debugging)
103
+ */
104
+ constructor(message, code, cause) {
105
+ super(message);
106
+ this.code = code;
107
+ this.cause = cause;
108
+ Object.setPrototypeOf(this, DenoSandboxError.prototype);
109
+ }
110
+ /**
111
+ * Checks if the error is an instance of DenoSandboxError.
112
+ *
113
+ * @param error - The error to check
114
+ * @returns True if the error is an instance of DenoSandboxError, false otherwise
115
+ */
116
+ static isInstance(error) {
117
+ return typeof error === "object" && error !== null && error[DENO_SANDBOX_ERROR_SYMBOL] === true;
118
+ }
119
+ };
120
+
121
+ //#endregion
122
+ //#region src/sandbox.ts
123
+ /**
124
+ * Deno Sandbox implementation of the SandboxBackendProtocol.
125
+ *
126
+ * This module provides a Deno Sandbox backend for deepagents, enabling agents
127
+ * to execute commands, read/write files, and manage isolated Linux microVM
128
+ * environments using Deno Deploy's Sandbox infrastructure.
129
+ *
130
+ * @packageDocumentation
131
+ */
132
+ /**
133
+ * Deno Sandbox backend for deepagents.
134
+ *
135
+ * Extends `BaseSandbox` to provide command execution, file operations, and
136
+ * sandbox lifecycle management using Deno Deploy's Sandbox SDK.
137
+ *
138
+ * ## Basic Usage
139
+ *
140
+ * ```typescript
141
+ * import { DenoSandbox } from "@langchain/deno";
142
+ *
143
+ * // Create and initialize a sandbox
144
+ * const sandbox = await DenoSandbox.create({
145
+ * memoryMb: 1024,
146
+ * lifetime: "5m",
147
+ * });
148
+ *
149
+ * try {
150
+ * // Execute commands
151
+ * const result = await sandbox.execute("deno --version");
152
+ * console.log(result.output);
153
+ * } finally {
154
+ * // Always cleanup
155
+ * await sandbox.close();
156
+ * }
157
+ * ```
158
+ *
159
+ * ## Using with DeepAgent
160
+ *
161
+ * ```typescript
162
+ * import { createDeepAgent } from "deepagents";
163
+ * import { DenoSandbox } from "@langchain/deno";
164
+ *
165
+ * const sandbox = await DenoSandbox.create();
166
+ *
167
+ * const agent = createDeepAgent({
168
+ * model: new ChatAnthropic({ model: "claude-sonnet-4-20250514" }),
169
+ * systemPrompt: "You are a coding assistant with sandbox access.",
170
+ * backend: sandbox,
171
+ * });
172
+ * ```
173
+ */
174
+ var DenoSandbox = class DenoSandbox extends deepagents.BaseSandbox {
175
+ /** Private reference to the underlying Deno Sandbox instance */
176
+ #sandbox = null;
177
+ /** Configuration options for this sandbox */
178
+ #options;
179
+ /** Unique identifier for this sandbox instance */
180
+ #id;
181
+ /**
182
+ * Get the unique identifier for this sandbox.
183
+ *
184
+ * Before initialization, returns a temporary ID.
185
+ * After initialization, returns the actual Deno sandbox ID.
186
+ */
187
+ get id() {
188
+ return this.#id;
189
+ }
190
+ /**
191
+ * Get the underlying Deno Sandbox instance.
192
+ *
193
+ * @throws {DenoSandboxError} If the sandbox is not initialized
194
+ *
195
+ * @example
196
+ * ```typescript
197
+ * const sandbox = await DenoSandbox.create();
198
+ * const denoSdk = sandbox.sandbox; // Access the raw SDK
199
+ * ```
200
+ */
201
+ get sandbox() {
202
+ if (!this.#sandbox) throw new DenoSandboxError("Sandbox not initialized. Call initialize() or use DenoSandbox.create()", "NOT_INITIALIZED");
203
+ return this.#sandbox;
204
+ }
205
+ /**
206
+ * Check if the sandbox is initialized and running.
207
+ */
208
+ get isRunning() {
209
+ return this.#sandbox !== null;
210
+ }
211
+ /**
212
+ * Create a new DenoSandbox instance.
213
+ *
214
+ * Note: This only creates the instance. Call `initialize()` to actually
215
+ * create the Deno Sandbox, or use the static `DenoSandbox.create()` method.
216
+ *
217
+ * @param options - Configuration options for the sandbox
218
+ *
219
+ * @example
220
+ * ```typescript
221
+ * // Two-step initialization
222
+ * const sandbox = new DenoSandbox({ memoryMb: 1024 });
223
+ * await sandbox.initialize();
224
+ *
225
+ * // Or use the factory method
226
+ * const sandbox = await DenoSandbox.create({ memoryMb: 1024 });
227
+ * ```
228
+ */
229
+ constructor(options = {}) {
230
+ super();
231
+ this.#options = {
232
+ memoryMb: 768,
233
+ lifetime: "session",
234
+ ...options
235
+ };
236
+ this.#id = `deno-sandbox-${Date.now()}`;
237
+ }
238
+ /**
239
+ * Initialize the sandbox by creating a new Deno Sandbox instance.
240
+ *
241
+ * This method authenticates with Deno Deploy and provisions a new microVM
242
+ * sandbox. After initialization, the `id` property will reflect the
243
+ * actual Deno sandbox ID.
244
+ *
245
+ * @throws {DenoSandboxError} If already initialized (`ALREADY_INITIALIZED`)
246
+ * @throws {DenoSandboxError} If authentication fails (`AUTHENTICATION_FAILED`)
247
+ * @throws {DenoSandboxError} If sandbox creation fails (`SANDBOX_CREATION_FAILED`)
248
+ *
249
+ * @example
250
+ * ```typescript
251
+ * const sandbox = new DenoSandbox();
252
+ * await sandbox.initialize();
253
+ * console.log(`Sandbox ID: ${sandbox.id}`);
254
+ * ```
255
+ */
256
+ async initialize() {
257
+ if (this.#sandbox) throw new DenoSandboxError("Sandbox is already initialized. Each DenoSandbox instance can only be initialized once.", "ALREADY_INITIALIZED");
258
+ let credentials;
259
+ try {
260
+ credentials = getAuthCredentials(this.#options.auth);
261
+ } catch (error) {
262
+ throw new DenoSandboxError("Failed to authenticate with Deno Deploy. Check your token configuration.", "AUTHENTICATION_FAILED", error instanceof Error ? error : void 0);
263
+ }
264
+ try {
265
+ process.env.DENO_DEPLOY_TOKEN = credentials.token;
266
+ const createOptions = {};
267
+ if (this.#options.memoryMb !== void 0) createOptions.memoryMb = this.#options.memoryMb;
268
+ if (this.#options.lifetime !== void 0) createOptions.lifetime = this.#options.lifetime;
269
+ if (this.#options.region !== void 0) createOptions.region = this.#options.region;
270
+ this.#sandbox = await _deno_sandbox.Sandbox.create(createOptions);
271
+ this.#id = this.#sandbox.id;
272
+ if (this.#options.initialFiles) await this.#uploadInitialFiles(this.#options.initialFiles);
273
+ } catch (error) {
274
+ throw new DenoSandboxError(`Failed to create Deno Sandbox: ${error instanceof Error ? error.message : String(error)}`, "SANDBOX_CREATION_FAILED", error instanceof Error ? error : void 0);
275
+ }
276
+ }
277
+ /**
278
+ * Upload initial files to the sandbox.
279
+ *
280
+ * @param files - A map of file paths to their string contents
281
+ */
282
+ async #uploadInitialFiles(files) {
283
+ const encoder = new TextEncoder();
284
+ const fileEntries = Object.entries(files).map(([path, content]) => [path, encoder.encode(content)]);
285
+ const errors = (await this.uploadFiles(fileEntries)).filter((r) => r.error !== null);
286
+ if (errors.length > 0) throw new DenoSandboxError(`Failed to upload initial files: ${errors.map((e) => `${e.path}: ${e.error}`).join(", ")}`, "FILE_OPERATION_FAILED");
287
+ }
288
+ /**
289
+ * Execute a command in the sandbox.
290
+ *
291
+ * Commands are run using the sandbox's shell in the configured working directory.
292
+ *
293
+ * @param command - The shell command to execute
294
+ * @returns Execution result with output, exit code, and truncation flag
295
+ * @throws {DenoSandboxError} If the sandbox is not initialized
296
+ *
297
+ * @example
298
+ * ```typescript
299
+ * const result = await sandbox.execute("echo 'Hello World'");
300
+ * console.log(result.output); // "Hello World\n"
301
+ * console.log(result.exitCode); // 0
302
+ * ```
303
+ */
304
+ async execute(command) {
305
+ const sandbox = this.sandbox;
306
+ try {
307
+ const { status, stdoutText, stderrText } = await (await sandbox.spawn("/bin/bash", {
308
+ args: ["-c", command],
309
+ stdout: "piped",
310
+ stderr: "piped"
311
+ })).output();
312
+ return {
313
+ output: (stdoutText ?? "") + (stderrText ?? ""),
314
+ exitCode: status.code ?? 0,
315
+ truncated: false
316
+ };
317
+ } catch (error) {
318
+ if (error instanceof Error && error.message.includes("timeout")) throw new DenoSandboxError(`Command timed out: ${command}`, "COMMAND_TIMEOUT", error);
319
+ throw new DenoSandboxError(`Command execution failed: ${error instanceof Error ? error.message : String(error)}`, "COMMAND_FAILED", error instanceof Error ? error : void 0);
320
+ }
321
+ }
322
+ /**
323
+ * Upload files to the sandbox.
324
+ *
325
+ * Files are written to the sandbox filesystem. Parent directories are
326
+ * created automatically if they don't exist.
327
+ *
328
+ * @param files - Array of [path, content] tuples to upload
329
+ * @returns Upload result for each file, with success or error status
330
+ *
331
+ * @example
332
+ * ```typescript
333
+ * const encoder = new TextEncoder();
334
+ * const results = await sandbox.uploadFiles([
335
+ * ["src/index.js", encoder.encode("console.log('Hello')")],
336
+ * ["package.json", encoder.encode('{"name": "test"}')],
337
+ * ]);
338
+ * ```
339
+ */
340
+ async uploadFiles(files) {
341
+ const sandbox = this.sandbox;
342
+ const results = [];
343
+ for (const [path, content] of files) try {
344
+ const parentDir = path.substring(0, path.lastIndexOf("/"));
345
+ if (parentDir) await (await sandbox.spawn("/bin/bash", {
346
+ args: ["-c", `mkdir -p "${parentDir}"`],
347
+ stdout: "piped",
348
+ stderr: "piped"
349
+ })).output();
350
+ const textContent = new TextDecoder().decode(content);
351
+ await sandbox.writeTextFile(path, textContent);
352
+ results.push({
353
+ path,
354
+ error: null
355
+ });
356
+ } catch (error) {
357
+ results.push({
358
+ path,
359
+ error: this.#mapError(error)
360
+ });
361
+ }
362
+ return results;
363
+ }
364
+ /**
365
+ * Download files from the sandbox.
366
+ *
367
+ * Each file is read individually, allowing partial success when some
368
+ * files exist and others don't.
369
+ *
370
+ * @param paths - Array of file paths to download
371
+ * @returns Download result for each file, with content or error
372
+ *
373
+ * @example
374
+ * ```typescript
375
+ * const results = await sandbox.downloadFiles(["src/index.js", "missing.txt"]);
376
+ * for (const result of results) {
377
+ * if (result.content) {
378
+ * console.log(new TextDecoder().decode(result.content));
379
+ * } else {
380
+ * console.error(`Error: ${result.error}`);
381
+ * }
382
+ * }
383
+ * ```
384
+ */
385
+ async downloadFiles(paths) {
386
+ const sandbox = this.sandbox;
387
+ const results = [];
388
+ for (const path of paths) try {
389
+ const { status, stdoutText } = await (await sandbox.spawn("/bin/bash", {
390
+ args: ["-c", `cat "${path}"`],
391
+ stdout: "piped",
392
+ stderr: "piped"
393
+ })).output();
394
+ if (!status.success) results.push({
395
+ path,
396
+ content: null,
397
+ error: "file_not_found"
398
+ });
399
+ else {
400
+ const content = new TextEncoder().encode(stdoutText ?? "");
401
+ results.push({
402
+ path,
403
+ content,
404
+ error: null
405
+ });
406
+ }
407
+ } catch (error) {
408
+ results.push({
409
+ path,
410
+ content: null,
411
+ error: this.#mapError(error)
412
+ });
413
+ }
414
+ return results;
415
+ }
416
+ /**
417
+ * Read a file's content with line numbers.
418
+ *
419
+ * Override of BaseSandbox.read() to use awk instead of Python,
420
+ * since Deno sandboxes don't have Python installed.
421
+ *
422
+ * @param filePath - Absolute path to the file
423
+ * @param offset - Line offset (0-indexed, default 0)
424
+ * @param limit - Maximum lines to return (default 500)
425
+ * @returns Formatted file content with line numbers, or error message
426
+ */
427
+ async read(filePath, offset = 0, limit = 500) {
428
+ const safeOffset = Number.isFinite(offset) && offset > 0 ? Math.floor(offset) : 0;
429
+ const safeLimit = Number.isFinite(limit) && limit > 0 && limit < Number.MAX_SAFE_INTEGER ? Math.floor(limit) : 500;
430
+ const escapedPath = filePath.replace(/'/g, "'\\''");
431
+ const command = `
432
+ if [ ! -f '${escapedPath}' ]; then
433
+ echo "Error: File not found"
434
+ exit 1
435
+ fi
436
+ if [ ! -s '${escapedPath}' ]; then
437
+ echo "System reminder: File exists but has empty contents"
438
+ exit 0
439
+ fi
440
+ awk -v offset=${safeOffset} -v limit=${safeLimit} '
441
+ NR > offset && NR <= offset + limit {
442
+ printf "%6d\\t%s\\n", NR, $0
443
+ }
444
+ ' '${escapedPath}'
445
+ `;
446
+ const result = await this.execute(command);
447
+ if (result.exitCode !== 0) return `Error: File '${filePath}' not found`;
448
+ return result.output;
449
+ }
450
+ /**
451
+ * Create a new file with content.
452
+ *
453
+ * Override of BaseSandbox.write() to use shell commands instead of Python,
454
+ * since Deno sandboxes don't have Python installed.
455
+ *
456
+ * @param filePath - Absolute path for the new file
457
+ * @param content - File content to write
458
+ * @returns WriteResult with error populated on failure
459
+ */
460
+ async write(filePath, content) {
461
+ const escapedPath = filePath.replace(/'/g, "'\\''");
462
+ if ((await this.execute(`test -f '${escapedPath}'`)).exitCode === 0) return { error: `Cannot write to ${filePath} because it already exists. Read and then make an edit, or write to a new path.` };
463
+ const encoder = new TextEncoder();
464
+ const uploadResult = await this.uploadFiles([[filePath, encoder.encode(content)]]);
465
+ if (uploadResult[0]?.error) return { error: `Failed to write file: ${uploadResult[0].error}` };
466
+ return {
467
+ path: filePath,
468
+ filesUpdate: null
469
+ };
470
+ }
471
+ /**
472
+ * Edit a file by replacing string occurrences.
473
+ *
474
+ * Override of BaseSandbox.edit() to use shell commands instead of Python,
475
+ * since Deno sandboxes don't have Python installed.
476
+ *
477
+ * Uses sed for in-place replacement with proper escaping.
478
+ *
479
+ * @param filePath - Absolute path to the file
480
+ * @param oldString - String to find and replace
481
+ * @param newString - Replacement string
482
+ * @param replaceAll - If true, replace all occurrences (default: false)
483
+ * @returns EditResult with error, path, and occurrences
484
+ */
485
+ async edit(filePath, oldString, newString, replaceAll = false) {
486
+ const escapedPath = filePath.replace(/'/g, "'\\''");
487
+ if ((await this.execute(`test -f '${escapedPath}'`)).exitCode !== 0) return { error: `Error: File '${filePath}' not found` };
488
+ const escapedOldForGrep = oldString.replace(/'/g, "'\\''");
489
+ const countResult = await this.execute(`grep -oF '${escapedOldForGrep}' '${escapedPath}' | wc -l`);
490
+ const count = parseInt(countResult.output.trim(), 10) || 0;
491
+ if (count === 0) return { error: `String not found in file '${filePath}'` };
492
+ if (count > 1 && !replaceAll) return { error: `Multiple occurrences found in '${filePath}'. Use replaceAll=true to replace all.` };
493
+ const awkCommand = `
494
+ OLD=$(echo '${Buffer.from(oldString, "utf-8").toString("base64")}' | base64 -d)
495
+ NEW=$(echo '${Buffer.from(newString, "utf-8").toString("base64")}' | base64 -d)
496
+ awk -v old="$OLD" -v new="$NEW" -v replace_all=${replaceAll ? 1 : 0} '
497
+ {
498
+ if (replace_all) {
499
+ gsub(old, new)
500
+ } else {
501
+ sub(old, new)
502
+ }
503
+ print
504
+ }
505
+ ' '${escapedPath}' > '${escapedPath}.tmp' && mv '${escapedPath}.tmp' '${escapedPath}'
506
+ `;
507
+ if ((await this.execute(awkCommand)).exitCode !== 0) return { error: `Unknown error editing file '${filePath}'` };
508
+ return {
509
+ path: filePath,
510
+ filesUpdate: null,
511
+ occurrences: count
512
+ };
513
+ }
514
+ /**
515
+ * Close the sandbox and release all resources.
516
+ *
517
+ * After closing, the sandbox cannot be used again. Any unsaved data
518
+ * will be lost.
519
+ *
520
+ * @example
521
+ * ```typescript
522
+ * try {
523
+ * await sandbox.execute("deno run build.ts");
524
+ * } finally {
525
+ * await sandbox.close();
526
+ * }
527
+ * ```
528
+ */
529
+ async close() {
530
+ if (this.#sandbox) try {
531
+ await this.#sandbox.close();
532
+ } finally {
533
+ this.#sandbox = null;
534
+ }
535
+ }
536
+ /**
537
+ * Forcefully terminate the sandbox.
538
+ *
539
+ * Use this when you need to immediately stop the sandbox, even if
540
+ * operations are in progress.
541
+ *
542
+ * @example
543
+ * ```typescript
544
+ * await sandbox.kill();
545
+ * ```
546
+ */
547
+ async kill() {
548
+ if (this.#sandbox) try {
549
+ await this.#sandbox.kill();
550
+ } finally {
551
+ this.#sandbox = null;
552
+ }
553
+ }
554
+ /**
555
+ * Alias for close() to maintain compatibility with other sandbox implementations.
556
+ */
557
+ async stop() {
558
+ await this.close();
559
+ }
560
+ /**
561
+ * Set the sandbox from an existing Deno Sandbox instance.
562
+ * Used internally by the static `connect()` method.
563
+ */
564
+ #setFromExisting(existingSandbox, sandboxId) {
565
+ this.#sandbox = existingSandbox;
566
+ this.#id = sandboxId;
567
+ }
568
+ /**
569
+ * Map Deno SDK errors to standardized FileOperationError codes.
570
+ *
571
+ * @param error - The error from the Deno SDK
572
+ * @returns A standardized error code
573
+ */
574
+ #mapError(error) {
575
+ if (error instanceof Error) {
576
+ const msg = error.message.toLowerCase();
577
+ if (msg.includes("not found") || msg.includes("enoent")) return "file_not_found";
578
+ if (msg.includes("permission") || msg.includes("eacces")) return "permission_denied";
579
+ if (msg.includes("directory") || msg.includes("eisdir")) return "is_directory";
580
+ }
581
+ return "invalid_path";
582
+ }
583
+ /**
584
+ * Create and initialize a new DenoSandbox in one step.
585
+ *
586
+ * This is the recommended way to create a sandbox. It combines
587
+ * construction and initialization into a single async operation.
588
+ *
589
+ * @param options - Configuration options for the sandbox
590
+ * @returns An initialized and ready-to-use sandbox
591
+ *
592
+ * @example
593
+ * ```typescript
594
+ * const sandbox = await DenoSandbox.create({
595
+ * memoryMb: 1024,
596
+ * lifetime: "10m",
597
+ * region: "iad",
598
+ * });
599
+ * ```
600
+ */
601
+ static async create(options) {
602
+ const sandbox = new DenoSandbox(options);
603
+ await sandbox.initialize();
604
+ return sandbox;
605
+ }
606
+ /**
607
+ * Reconnect to an existing sandbox by ID.
608
+ *
609
+ * This allows you to resume working with a sandbox that was created
610
+ * earlier with a duration-based lifetime.
611
+ *
612
+ * @param sandboxId - The ID of the sandbox to reconnect to
613
+ * @param options - Optional auth configuration (for token)
614
+ * @returns A connected sandbox instance
615
+ *
616
+ * @example
617
+ * ```typescript
618
+ * // Resume a sandbox from a stored ID
619
+ * const sandbox = await DenoSandbox.connect("sandbox-abc123");
620
+ * const result = await sandbox.execute("ls -la");
621
+ * ```
622
+ */
623
+ static async connect(sandboxId, options) {
624
+ let credentials;
625
+ try {
626
+ credentials = getAuthCredentials(options?.auth);
627
+ } catch (error) {
628
+ throw new DenoSandboxError("Failed to authenticate with Deno Deploy. Check your token configuration.", "AUTHENTICATION_FAILED", error instanceof Error ? error : void 0);
629
+ }
630
+ try {
631
+ process.env.DENO_DEPLOY_TOKEN = credentials.token;
632
+ const existingSandbox = await _deno_sandbox.Sandbox.connect({ id: sandboxId });
633
+ const denoSandbox = new DenoSandbox();
634
+ denoSandbox.#setFromExisting(existingSandbox, sandboxId);
635
+ return denoSandbox;
636
+ } catch (error) {
637
+ throw new DenoSandboxError(`Sandbox not found: ${sandboxId}`, "SANDBOX_NOT_FOUND", error instanceof Error ? error : void 0);
638
+ }
639
+ }
640
+ };
641
+ /**
642
+ * Create an async factory function that creates a new Deno Sandbox per invocation.
643
+ *
644
+ * Each call to the factory will create and initialize a new sandbox.
645
+ * This is useful when you want fresh, isolated environments for each
646
+ * agent invocation.
647
+ *
648
+ * **Important**: This returns an async factory. For use with middleware that
649
+ * requires synchronous BackendFactory, use `createDenoSandboxFactoryFromSandbox()`
650
+ * with a pre-created sandbox instead.
651
+ *
652
+ * @param options - Optional configuration for sandbox creation
653
+ * @returns An async factory function that creates new sandboxes
654
+ *
655
+ * @example
656
+ * ```typescript
657
+ * import { DenoSandbox, createDenoSandboxFactory } from "@langchain/deno";
658
+ *
659
+ * // Create a factory for new sandboxes
660
+ * const factory = createDenoSandboxFactory({ memoryMb: 1024 });
661
+ *
662
+ * // Each call creates a new sandbox
663
+ * const sandbox1 = await factory();
664
+ * const sandbox2 = await factory();
665
+ *
666
+ * try {
667
+ * // Use sandboxes...
668
+ * } finally {
669
+ * await sandbox1.close();
670
+ * await sandbox2.close();
671
+ * }
672
+ * ```
673
+ */
674
+ function createDenoSandboxFactory(options) {
675
+ return async () => {
676
+ return await DenoSandbox.create(options);
677
+ };
678
+ }
679
+ /**
680
+ * Create a backend factory that reuses an existing Deno Sandbox.
681
+ *
682
+ * This allows multiple agent invocations to share the same sandbox,
683
+ * avoiding the startup overhead of creating new sandboxes.
684
+ *
685
+ * Important: You are responsible for managing the sandbox lifecycle
686
+ * (calling `close()` when done).
687
+ *
688
+ * @param sandbox - An existing DenoSandbox instance (must be initialized)
689
+ * @returns A BackendFactory that returns the provided sandbox
690
+ *
691
+ * @example
692
+ * ```typescript
693
+ * import { createDeepAgent, createFilesystemMiddleware } from "deepagents";
694
+ * import { DenoSandbox, createDenoSandboxFactoryFromSandbox } from "@langchain/deno";
695
+ *
696
+ * // Create and initialize a sandbox
697
+ * const sandbox = await DenoSandbox.create({ memoryMb: 1024 });
698
+ *
699
+ * try {
700
+ * const agent = createDeepAgent({
701
+ * model: new ChatAnthropic({ model: "claude-sonnet-4-20250514" }),
702
+ * systemPrompt: "You are a coding assistant.",
703
+ * middlewares: [
704
+ * createFilesystemMiddleware({
705
+ * backend: createDenoSandboxFactoryFromSandbox(sandbox),
706
+ * }),
707
+ * ],
708
+ * });
709
+ *
710
+ * await agent.invoke({ messages: [...] });
711
+ * } finally {
712
+ * await sandbox.close();
713
+ * }
714
+ * ```
715
+ */
716
+ function createDenoSandboxFactoryFromSandbox(sandbox) {
717
+ return () => sandbox;
718
+ }
719
+
720
+ //#endregion
721
+ exports.DenoSandbox = DenoSandbox;
722
+ exports.DenoSandboxError = DenoSandboxError;
723
+ exports.createDenoSandboxFactory = createDenoSandboxFactory;
724
+ exports.createDenoSandboxFactoryFromSandbox = createDenoSandboxFactoryFromSandbox;
725
+ exports.getAuthCredentials = getAuthCredentials;
726
+ exports.getAuthToken = getAuthToken;
727
+ //# sourceMappingURL=index.cjs.map