@langchain/modal 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,636 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ let modal = require("modal");
3
+ let deepagents = require("deepagents");
4
+
5
+ //#region src/auth.ts
6
+ /**
7
+ * Get authentication credentials for Modal API.
8
+ *
9
+ * Credentials are resolved in the following priority order:
10
+ *
11
+ * 1. **Explicit options**: If `options.tokenId` and/or `options.tokenSecret` are provided,
12
+ * they are used directly.
13
+ * 2. **Environment variables**: `MODAL_TOKEN_ID` and `MODAL_TOKEN_SECRET` are used as fallbacks.
14
+ *
15
+ * ## Environment Variable Setup
16
+ *
17
+ * ```bash
18
+ * # Go to https://modal.com/settings/tokens
19
+ * # Create a new token and set the environment variables
20
+ * export MODAL_TOKEN_ID=your_token_id
21
+ * export MODAL_TOKEN_SECRET=your_token_secret
22
+ * ```
23
+ *
24
+ * @param options - Optional authentication configuration from ModalSandboxOptions
25
+ * @returns Complete authentication credentials
26
+ * @throws {Error} If any credentials are missing
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * // With explicit credentials
31
+ * const creds = getAuthCredentials({ tokenId: "...", tokenSecret: "..." });
32
+ *
33
+ * // Using environment variables (auto-detected)
34
+ * const creds = getAuthCredentials();
35
+ *
36
+ * // From ModalSandboxOptions
37
+ * const options: ModalSandboxOptions = {
38
+ * auth: { tokenId: "...", tokenSecret: "..." }
39
+ * };
40
+ * const creds = getAuthCredentials(options.auth);
41
+ * ```
42
+ */
43
+ function getAuthCredentials(options) {
44
+ const tokenId = options?.tokenId || process.env.MODAL_TOKEN_ID;
45
+ const tokenSecret = options?.tokenSecret || process.env.MODAL_TOKEN_SECRET;
46
+ const missingTokenId = !tokenId;
47
+ const missingTokenSecret = !tokenSecret;
48
+ if (missingTokenId || missingTokenSecret) {
49
+ const missing = [];
50
+ if (missingTokenId) missing.push("MODAL_TOKEN_ID");
51
+ if (missingTokenSecret) missing.push("MODAL_TOKEN_SECRET");
52
+ throw new Error(`Modal authentication required. Missing: ${missing.join(", ")}.\n\nProvide credentials using one of these methods:
53
+
54
+ 1. Set environment variables:
55
+ Go to https://modal.com/settings/tokens
56
+ Create a new token and run:
57
+ export MODAL_TOKEN_ID=your_token_id
58
+ export MODAL_TOKEN_SECRET=your_token_secret
59
+
60
+ 2. Pass credentials directly in options:
61
+ new ModalSandbox({ auth: { tokenId: '...', tokenSecret: '...' } })`);
62
+ }
63
+ return {
64
+ tokenId,
65
+ tokenSecret
66
+ };
67
+ }
68
+
69
+ //#endregion
70
+ //#region src/types.ts
71
+ const MODAL_SANDBOX_ERROR_SYMBOL = Symbol.for("modal.sandbox.error");
72
+ /**
73
+ * Custom error class for Modal Sandbox operations.
74
+ *
75
+ * Provides structured error information including:
76
+ * - Human-readable message
77
+ * - Error code for programmatic handling
78
+ * - Original cause for debugging
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * try {
83
+ * await sandbox.execute("some command");
84
+ * } catch (error) {
85
+ * if (error instanceof ModalSandboxError) {
86
+ * switch (error.code) {
87
+ * case "NOT_INITIALIZED":
88
+ * await sandbox.initialize();
89
+ * break;
90
+ * case "COMMAND_TIMEOUT":
91
+ * console.error("Command took too long");
92
+ * break;
93
+ * default:
94
+ * throw error;
95
+ * }
96
+ * }
97
+ * }
98
+ * ```
99
+ */
100
+ var ModalSandboxError = class ModalSandboxError extends Error {
101
+ [MODAL_SANDBOX_ERROR_SYMBOL];
102
+ /** Error name for instanceof checks and logging */
103
+ name = "ModalSandboxError";
104
+ /**
105
+ * Creates a new ModalSandboxError.
106
+ *
107
+ * @param message - Human-readable error description
108
+ * @param code - Structured error code for programmatic handling
109
+ * @param cause - Original error that caused this error (for debugging)
110
+ */
111
+ constructor(message, code, cause) {
112
+ super(message);
113
+ this.code = code;
114
+ this.cause = cause;
115
+ Object.setPrototypeOf(this, ModalSandboxError.prototype);
116
+ }
117
+ /**
118
+ * Checks if the error is an instance of ModalSandboxError.
119
+ *
120
+ * @param error - The error to check
121
+ * @returns True if the error is an instance of ModalSandboxError, false otherwise
122
+ */
123
+ static isInstance(error) {
124
+ return typeof error === "object" && error !== null && error[MODAL_SANDBOX_ERROR_SYMBOL] === true;
125
+ }
126
+ };
127
+
128
+ //#endregion
129
+ //#region src/sandbox.ts
130
+ /**
131
+ * Modal Sandbox implementation of the SandboxBackendProtocol.
132
+ *
133
+ * This module provides a Modal Sandbox backend for deepagents, enabling agents
134
+ * to execute commands, read/write files, and manage isolated container
135
+ * environments using Modal's serverless infrastructure.
136
+ *
137
+ * @packageDocumentation
138
+ */
139
+ /**
140
+ * Modal Sandbox backend for deepagents.
141
+ *
142
+ * Extends `BaseSandbox` to provide command execution, file operations, and
143
+ * sandbox lifecycle management using Modal's serverless infrastructure.
144
+ *
145
+ * ## Basic Usage
146
+ *
147
+ * ```typescript
148
+ * import { ModalSandbox } from "@langchain/modal";
149
+ *
150
+ * // Create and initialize a sandbox
151
+ * const sandbox = await ModalSandbox.create({
152
+ * imageName: "python:3.12-slim",
153
+ * timeout: 600,
154
+ * });
155
+ *
156
+ * try {
157
+ * // Execute commands
158
+ * const result = await sandbox.execute("python --version");
159
+ * console.log(result.output);
160
+ * } finally {
161
+ * // Always cleanup
162
+ * await sandbox.close();
163
+ * }
164
+ * ```
165
+ *
166
+ * ## Using with DeepAgent
167
+ *
168
+ * ```typescript
169
+ * import { createDeepAgent } from "deepagents";
170
+ * import { ModalSandbox } from "@langchain/modal";
171
+ *
172
+ * const sandbox = await ModalSandbox.create();
173
+ *
174
+ * const agent = createDeepAgent({
175
+ * model: new ChatAnthropic({ model: "claude-sonnet-4-20250514" }),
176
+ * systemPrompt: "You are a coding assistant with sandbox access.",
177
+ * backend: sandbox,
178
+ * });
179
+ * ```
180
+ */
181
+ var ModalSandbox = class ModalSandbox extends deepagents.BaseSandbox {
182
+ /** Private reference to the Modal client */
183
+ #client = null;
184
+ /** Private reference to the Modal App */
185
+ #app = null;
186
+ /** Private reference to the underlying Modal Sandbox instance */
187
+ #sandbox = null;
188
+ /** Configuration options for this sandbox */
189
+ #options;
190
+ /** Unique identifier for this sandbox instance */
191
+ #id;
192
+ /**
193
+ * Get the unique identifier for this sandbox.
194
+ *
195
+ * Before initialization, returns a temporary ID.
196
+ * After initialization, returns the actual Modal sandbox ID.
197
+ */
198
+ get id() {
199
+ return this.#id;
200
+ }
201
+ /**
202
+ * Get the underlying Modal Sandbox instance.
203
+ *
204
+ * @throws {ModalSandboxError} If the sandbox is not initialized
205
+ *
206
+ * @example
207
+ * ```typescript
208
+ * const sandbox = await ModalSandbox.create();
209
+ * const modalInstance = sandbox.instance; // Access the raw Modal Sandbox
210
+ * ```
211
+ */
212
+ get instance() {
213
+ if (!this.#sandbox) throw new ModalSandboxError("Sandbox not initialized. Call initialize() or use ModalSandbox.create()", "NOT_INITIALIZED");
214
+ return this.#sandbox;
215
+ }
216
+ /**
217
+ * Get the underlying Modal client instance.
218
+ *
219
+ * @throws {ModalSandboxError} If the sandbox is not initialized
220
+ *
221
+ * @example
222
+ * ```typescript
223
+ * const sandbox = await ModalSandbox.create();
224
+ * const modalClient = sandbox.client; // Access the raw Modal client
225
+ * ```
226
+ */
227
+ get client() {
228
+ if (!this.#client) throw new ModalSandboxError("Sandbox not initialized. Call initialize() or use ModalSandbox.create()", "NOT_INITIALIZED");
229
+ return this.#client;
230
+ }
231
+ /**
232
+ * Check if the sandbox is initialized and running.
233
+ */
234
+ get isRunning() {
235
+ return this.#sandbox !== null;
236
+ }
237
+ /**
238
+ * Create a new ModalSandbox instance.
239
+ *
240
+ * Note: This only creates the instance. Call `initialize()` to actually
241
+ * create the Modal Sandbox, or use the static `ModalSandbox.create()` method.
242
+ *
243
+ * @param options - Configuration options for the sandbox
244
+ *
245
+ * @example
246
+ * ```typescript
247
+ * // Two-step initialization
248
+ * const sandbox = new ModalSandbox({ imageName: "python:3.12-slim" });
249
+ * await sandbox.initialize();
250
+ *
251
+ * // Or use the factory method
252
+ * const sandbox = await ModalSandbox.create({ imageName: "python:3.12-slim" });
253
+ * ```
254
+ */
255
+ constructor(options = {}) {
256
+ super();
257
+ this.#options = {
258
+ appName: "deepagents-sandbox",
259
+ imageName: "alpine:3.21",
260
+ ...options
261
+ };
262
+ this.#id = `modal-sandbox-${Date.now()}`;
263
+ }
264
+ /**
265
+ * Initialize the sandbox by creating a new Modal Sandbox instance.
266
+ *
267
+ * This method authenticates with Modal and provisions a new sandbox container.
268
+ * After initialization, the `id` property will reflect the actual Modal sandbox ID.
269
+ *
270
+ * @throws {ModalSandboxError} If already initialized (`ALREADY_INITIALIZED`)
271
+ * @throws {ModalSandboxError} If authentication fails (`AUTHENTICATION_FAILED`)
272
+ * @throws {ModalSandboxError} If sandbox creation fails (`SANDBOX_CREATION_FAILED`)
273
+ *
274
+ * @example
275
+ * ```typescript
276
+ * const sandbox = new ModalSandbox();
277
+ * await sandbox.initialize();
278
+ * console.log(`Sandbox ID: ${sandbox.id}`);
279
+ * ```
280
+ */
281
+ async initialize() {
282
+ if (this.#sandbox) throw new ModalSandboxError("Sandbox is already initialized. Each ModalSandbox instance can only be initialized once.", "ALREADY_INITIALIZED");
283
+ try {
284
+ getAuthCredentials(this.#options.auth);
285
+ } catch (error) {
286
+ throw new ModalSandboxError("Failed to authenticate with Modal. Check your token configuration.", "AUTHENTICATION_FAILED", error instanceof Error ? error : void 0);
287
+ }
288
+ try {
289
+ this.#client = new modal.ModalClient();
290
+ this.#app = await this.#client.apps.fromName(this.#options.appName ?? "deepagents-sandbox", { createIfMissing: true });
291
+ const image = this.#client.images.fromRegistry(this.#options.imageName ?? "alpine:3.21");
292
+ const { appName: _appName, imageName: _imageName, initialFiles: _initialFiles, auth: _auth, volumes: volumeNames, secrets: secretNames, ...sdkOptions } = this.#options;
293
+ const createOptions = { ...sdkOptions };
294
+ if (volumeNames !== void 0) {
295
+ const volumeObjects = {};
296
+ for (const [mountPath, volumeName] of Object.entries(volumeNames)) volumeObjects[mountPath] = await this.#client.volumes.fromName(volumeName, { createIfMissing: false });
297
+ createOptions.volumes = volumeObjects;
298
+ }
299
+ if (secretNames !== void 0 && secretNames.length > 0) {
300
+ const secretObjects = [];
301
+ for (const secretName of secretNames) {
302
+ const secret = await this.#client.secrets.fromName(secretName);
303
+ secretObjects.push(secret);
304
+ }
305
+ createOptions.secrets = secretObjects;
306
+ }
307
+ this.#sandbox = await this.#client.sandboxes.create(this.#app, image, createOptions);
308
+ this.#id = this.#sandbox.sandboxId;
309
+ if (this.#options.initialFiles) await this.#uploadInitialFiles(this.#options.initialFiles);
310
+ } catch (error) {
311
+ if (ModalSandboxError.isInstance(error)) throw error;
312
+ throw new ModalSandboxError(`Failed to create Modal Sandbox: ${error instanceof Error ? error.message : String(error)}`, "SANDBOX_CREATION_FAILED", error instanceof Error ? error : void 0);
313
+ }
314
+ }
315
+ /**
316
+ * Upload initial files to the sandbox during initialization.
317
+ * This is a private helper method used by initialize().
318
+ *
319
+ * @param files - Record of file paths to contents
320
+ */
321
+ async #uploadInitialFiles(files) {
322
+ const encoder = new TextEncoder();
323
+ const filesToUpload = [];
324
+ for (const [filePath, content] of Object.entries(files)) {
325
+ const normalizedPath = filePath.startsWith("/") ? filePath.slice(1) : filePath;
326
+ const data = typeof content === "string" ? encoder.encode(content) : content;
327
+ filesToUpload.push([normalizedPath, data]);
328
+ }
329
+ const errors = (await this.uploadFiles(filesToUpload)).filter((r) => r.error !== null);
330
+ if (errors.length > 0) throw new ModalSandboxError(`Failed to upload initial files: ${errors.map((e) => e.path).join(", ")}`, "FILE_OPERATION_FAILED");
331
+ }
332
+ /**
333
+ * Execute a command in the sandbox.
334
+ *
335
+ * Commands are run using bash -c to execute the command string.
336
+ *
337
+ * @param command - The shell command to execute
338
+ * @returns Execution result with output, exit code, and truncation flag
339
+ * @throws {ModalSandboxError} If the sandbox is not initialized
340
+ *
341
+ * @example
342
+ * ```typescript
343
+ * const result = await sandbox.execute("echo 'Hello World'");
344
+ * console.log(result.output); // "Hello World\n"
345
+ * console.log(result.exitCode); // 0
346
+ * ```
347
+ */
348
+ async execute(command) {
349
+ const sandbox = this.instance;
350
+ try {
351
+ const process = await sandbox.exec([
352
+ "bash",
353
+ "-c",
354
+ command
355
+ ], {
356
+ stdout: "pipe",
357
+ stderr: "pipe"
358
+ });
359
+ const [stdout, stderr] = await Promise.all([process.stdout.readText(), process.stderr.readText()]);
360
+ const exitCode = await process.wait();
361
+ return {
362
+ output: stdout + stderr,
363
+ exitCode: exitCode ?? 0,
364
+ truncated: false
365
+ };
366
+ } catch (error) {
367
+ if (error instanceof Error && error.message.includes("timeout")) throw new ModalSandboxError(`Command timed out: ${command}`, "COMMAND_TIMEOUT", error);
368
+ throw new ModalSandboxError(`Command execution failed: ${error instanceof Error ? error.message : String(error)}`, "COMMAND_FAILED", error instanceof Error ? error : void 0);
369
+ }
370
+ }
371
+ /**
372
+ * Upload files to the sandbox.
373
+ *
374
+ * Files are written to the sandbox filesystem using Modal's file API.
375
+ * Parent directories are created automatically if they don't exist.
376
+ *
377
+ * @param files - Array of [path, content] tuples to upload
378
+ * @returns Upload result for each file, with success or error status
379
+ *
380
+ * @example
381
+ * ```typescript
382
+ * const encoder = new TextEncoder();
383
+ * const results = await sandbox.uploadFiles([
384
+ * ["src/index.js", encoder.encode("console.log('Hello')")],
385
+ * ["package.json", encoder.encode('{"name": "test"}')],
386
+ * ]);
387
+ * ```
388
+ */
389
+ async uploadFiles(files) {
390
+ const sandbox = this.instance;
391
+ const results = [];
392
+ for (const [path, content] of files) try {
393
+ const parentDir = path.substring(0, path.lastIndexOf("/"));
394
+ if (parentDir) await sandbox.exec([
395
+ "mkdir",
396
+ "-p",
397
+ parentDir
398
+ ], {
399
+ stdout: "pipe",
400
+ stderr: "pipe"
401
+ }).then((p) => p.wait());
402
+ const writeHandle = await sandbox.open(path, "w");
403
+ await writeHandle.write(content);
404
+ await writeHandle.close();
405
+ results.push({
406
+ path,
407
+ error: null
408
+ });
409
+ } catch (error) {
410
+ results.push({
411
+ path,
412
+ error: this.#mapError(error)
413
+ });
414
+ }
415
+ return results;
416
+ }
417
+ /**
418
+ * Download files from the sandbox.
419
+ *
420
+ * Each file is read individually using Modal's file API, allowing
421
+ * partial success when some files exist and others don't.
422
+ *
423
+ * @param paths - Array of file paths to download
424
+ * @returns Download result for each file, with content or error
425
+ *
426
+ * @example
427
+ * ```typescript
428
+ * const results = await sandbox.downloadFiles(["src/index.js", "missing.txt"]);
429
+ * for (const result of results) {
430
+ * if (result.content) {
431
+ * console.log(new TextDecoder().decode(result.content));
432
+ * } else {
433
+ * console.error(`Error: ${result.error}`);
434
+ * }
435
+ * }
436
+ * ```
437
+ */
438
+ async downloadFiles(paths) {
439
+ const sandbox = this.instance;
440
+ const results = [];
441
+ for (const path of paths) try {
442
+ const readHandle = await sandbox.open(path, "r");
443
+ const content = await readHandle.read();
444
+ await readHandle.close();
445
+ results.push({
446
+ path,
447
+ content: new Uint8Array(content),
448
+ error: null
449
+ });
450
+ } catch (error) {
451
+ results.push({
452
+ path,
453
+ content: null,
454
+ error: this.#mapError(error)
455
+ });
456
+ }
457
+ return results;
458
+ }
459
+ /**
460
+ * Close the sandbox and release all resources.
461
+ *
462
+ * After closing, the sandbox cannot be used again. This terminates
463
+ * the sandbox container on Modal.
464
+ *
465
+ * @example
466
+ * ```typescript
467
+ * try {
468
+ * await sandbox.execute("npm run build");
469
+ * } finally {
470
+ * await sandbox.close();
471
+ * }
472
+ * ```
473
+ */
474
+ async close() {
475
+ if (this.#sandbox) try {
476
+ await this.#sandbox.terminate();
477
+ } finally {
478
+ this.#sandbox = null;
479
+ this.#app = null;
480
+ this.#client = null;
481
+ }
482
+ }
483
+ /**
484
+ * Terminate the sandbox.
485
+ *
486
+ * Alias for close() for Modal SDK compatibility.
487
+ *
488
+ * @example
489
+ * ```typescript
490
+ * await sandbox.terminate();
491
+ * ```
492
+ */
493
+ async terminate() {
494
+ await this.close();
495
+ }
496
+ /**
497
+ * Alias for close() to maintain compatibility with other sandbox implementations.
498
+ */
499
+ async stop() {
500
+ await this.close();
501
+ }
502
+ /**
503
+ * Poll the sandbox status to check if it has finished running.
504
+ *
505
+ * @returns The exit code if the sandbox has finished, or null if still running
506
+ */
507
+ async poll() {
508
+ if (!this.#sandbox) return null;
509
+ return this.#sandbox.poll();
510
+ }
511
+ /**
512
+ * Wait for the sandbox to finish running.
513
+ *
514
+ * @returns The exit code of the sandbox
515
+ * @throws {ModalSandboxError} If the sandbox is not initialized
516
+ */
517
+ async wait() {
518
+ return this.instance.wait();
519
+ }
520
+ /**
521
+ * Set the sandbox from an existing Modal Sandbox instance.
522
+ * Used internally by the static `fromId()` and `fromName()` methods.
523
+ */
524
+ #setFromExisting(client, existingSandbox, sandboxId) {
525
+ this.#client = client;
526
+ this.#sandbox = existingSandbox;
527
+ this.#id = sandboxId;
528
+ }
529
+ /**
530
+ * Map Modal SDK errors to standardized FileOperationError codes.
531
+ *
532
+ * @param error - The error from the Modal SDK
533
+ * @returns A standardized error code
534
+ */
535
+ #mapError(error) {
536
+ if (error instanceof Error) {
537
+ const msg = error.message.toLowerCase();
538
+ if (msg.includes("not found") || msg.includes("enoent")) return "file_not_found";
539
+ if (msg.includes("permission") || msg.includes("eacces")) return "permission_denied";
540
+ if (msg.includes("directory") || msg.includes("eisdir")) return "is_directory";
541
+ }
542
+ return "invalid_path";
543
+ }
544
+ /**
545
+ * Create and initialize a new ModalSandbox in one step.
546
+ *
547
+ * This is the recommended way to create a sandbox. It combines
548
+ * construction and initialization into a single async operation.
549
+ *
550
+ * @param options - Configuration options for the sandbox
551
+ * @returns An initialized and ready-to-use sandbox
552
+ *
553
+ * @example
554
+ * ```typescript
555
+ * const sandbox = await ModalSandbox.create({
556
+ * imageName: "python:3.12-slim",
557
+ * timeout: 600,
558
+ * memory: 2048,
559
+ * });
560
+ * ```
561
+ */
562
+ static async create(options) {
563
+ const sandbox = new ModalSandbox(options);
564
+ await sandbox.initialize();
565
+ return sandbox;
566
+ }
567
+ /**
568
+ * Reconnect to an existing sandbox by ID.
569
+ *
570
+ * This allows you to resume working with a sandbox that was created
571
+ * earlier and is still running.
572
+ *
573
+ * @param sandboxId - The ID of the sandbox to reconnect to
574
+ * @param options - Optional auth configuration
575
+ * @returns A connected sandbox instance
576
+ *
577
+ * @example
578
+ * ```typescript
579
+ * // Resume a sandbox from a stored ID
580
+ * const sandbox = await ModalSandbox.fromId("sb-abc123");
581
+ * const result = await sandbox.execute("ls -la");
582
+ * ```
583
+ */
584
+ static async fromId(sandboxId, options) {
585
+ try {
586
+ getAuthCredentials(options?.auth);
587
+ } catch (error) {
588
+ throw new ModalSandboxError("Failed to authenticate with Modal. Check your token configuration.", "AUTHENTICATION_FAILED", error instanceof Error ? error : void 0);
589
+ }
590
+ try {
591
+ const client = new modal.ModalClient();
592
+ const existingSandbox = await client.sandboxes.fromId(sandboxId);
593
+ const modalSandbox = new ModalSandbox(options);
594
+ modalSandbox.#setFromExisting(client, existingSandbox, sandboxId);
595
+ return modalSandbox;
596
+ } catch (error) {
597
+ throw new ModalSandboxError(`Sandbox not found: ${sandboxId}`, "SANDBOX_NOT_FOUND", error instanceof Error ? error : void 0);
598
+ }
599
+ }
600
+ /**
601
+ * Get a running sandbox by name from a deployed app.
602
+ *
603
+ * @param appName - The name of the Modal app
604
+ * @param sandboxName - The name of the sandbox
605
+ * @param options - Optional auth configuration
606
+ * @returns A connected sandbox instance
607
+ *
608
+ * @example
609
+ * ```typescript
610
+ * const sandbox = await ModalSandbox.fromName("my-app", "my-sandbox");
611
+ * const result = await sandbox.execute("ls -la");
612
+ * ```
613
+ */
614
+ static async fromName(appName, sandboxName, options) {
615
+ try {
616
+ getAuthCredentials(options?.auth);
617
+ } catch (error) {
618
+ throw new ModalSandboxError("Failed to authenticate with Modal. Check your token configuration.", "AUTHENTICATION_FAILED", error instanceof Error ? error : void 0);
619
+ }
620
+ try {
621
+ const client = new modal.ModalClient();
622
+ const existingSandbox = await client.sandboxes.fromName(appName, sandboxName);
623
+ const modalSandbox = new ModalSandbox(options);
624
+ modalSandbox.#setFromExisting(client, existingSandbox, existingSandbox.sandboxId);
625
+ return modalSandbox;
626
+ } catch (error) {
627
+ throw new ModalSandboxError(`Sandbox not found: ${appName}/${sandboxName}`, "SANDBOX_NOT_FOUND", error instanceof Error ? error : void 0);
628
+ }
629
+ }
630
+ };
631
+
632
+ //#endregion
633
+ exports.ModalSandbox = ModalSandbox;
634
+ exports.ModalSandboxError = ModalSandboxError;
635
+ exports.getAuthCredentials = getAuthCredentials;
636
+ //# sourceMappingURL=index.cjs.map