@computesdk/modal 1.0.1 → 1.2.0

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.js CHANGED
@@ -25,6 +25,16 @@ __export(index_exports, {
25
25
  module.exports = __toCommonJS(index_exports);
26
26
  var import_computesdk = require("computesdk");
27
27
  var import_modal = require("modal");
28
+ function detectRuntime(code) {
29
+ if (code.includes("console.log") || code.includes("process.") || code.includes("require(") || code.includes("module.exports") || code.includes("__dirname") || code.includes("__filename") || code.includes("throw new Error") || // JavaScript error throwing
30
+ code.includes("new Error(")) {
31
+ return "node";
32
+ }
33
+ if (code.includes("print(") || code.includes("import ") || code.includes("def ") || code.includes("sys.") || code.includes("json.") || code.includes('f"') || code.includes("f'")) {
34
+ return "python";
35
+ }
36
+ return "python";
37
+ }
28
38
  var modal = (0, import_computesdk.createProvider)({
29
39
  name: "modal",
30
40
  methods: {
@@ -113,26 +123,31 @@ var modal = (0, import_computesdk.createProvider)({
113
123
  runCode: async (modalSandbox, code, runtime) => {
114
124
  const startTime = Date.now();
115
125
  try {
116
- const effectiveRuntime = runtime || // Strong Python indicators
117
- (code.includes("print(") || code.includes("import ") || code.includes("def ") || code.includes("sys.") || code.includes("json.") || code.includes("__") || code.includes('f"') || code.includes("f'") ? "python" : "python");
118
- if (effectiveRuntime !== "python") {
119
- throw new Error("Modal provider currently only supports Python runtime");
126
+ const detectedRuntime = runtime || detectRuntime(code);
127
+ let executionSandbox = modalSandbox.sandbox;
128
+ let command;
129
+ let shouldCleanupSandbox = false;
130
+ if (detectedRuntime === "node") {
131
+ const app = await import_modal.App.lookup("computesdk-modal", { createIfMissing: true });
132
+ const nodeImage = await app.imageFromRegistry("node:20-alpine");
133
+ executionSandbox = await app.createSandbox(nodeImage);
134
+ command = ["node", "-e", code];
135
+ shouldCleanupSandbox = true;
136
+ } else {
137
+ command = ["python3", "-c", code];
120
138
  }
121
- const process2 = await modalSandbox.sandbox.exec(["python3", "-c", code]);
139
+ const process2 = await executionSandbox.exec(command, {
140
+ stdout: "pipe",
141
+ stderr: "pipe"
142
+ });
143
+ const [stdout, stderr] = await Promise.all([
144
+ process2.stdout.readText(),
145
+ process2.stderr.readText()
146
+ ]);
122
147
  const exitCode = await process2.wait();
123
- let stdout = "";
124
- let stderr = "";
125
- if (process2.stdout && typeof process2.stdout.read === "function") {
148
+ if (shouldCleanupSandbox && executionSandbox !== modalSandbox.sandbox) {
126
149
  try {
127
- const stdoutData = await process2.stdout.read();
128
- stdout = typeof stdoutData === "string" ? stdoutData : new TextDecoder().decode(stdoutData);
129
- } catch (e) {
130
- }
131
- }
132
- if (process2.stderr && typeof process2.stderr.read === "function") {
133
- try {
134
- const stderrData = await process2.stderr.read();
135
- stderr = typeof stderrData === "string" ? stderrData : new TextDecoder().decode(stderrData);
150
+ await executionSandbox.terminate();
136
151
  } catch (e) {
137
152
  }
138
153
  }
@@ -159,25 +174,15 @@ var modal = (0, import_computesdk.createProvider)({
159
174
  runCommand: async (modalSandbox, command, args = []) => {
160
175
  const startTime = Date.now();
161
176
  try {
162
- const commandArray = [command, ...args];
163
- const process2 = await modalSandbox.sandbox.exec(commandArray);
177
+ const process2 = await modalSandbox.sandbox.exec([command, ...args], {
178
+ stdout: "pipe",
179
+ stderr: "pipe"
180
+ });
181
+ const [stdout, stderr] = await Promise.all([
182
+ process2.stdout.readText(),
183
+ process2.stderr.readText()
184
+ ]);
164
185
  const exitCode = await process2.wait();
165
- let stdout = "";
166
- let stderr = "";
167
- if (process2.stdout && typeof process2.stdout.read === "function") {
168
- try {
169
- const stdoutData = await process2.stdout.read();
170
- stdout = typeof stdoutData === "string" ? stdoutData : new TextDecoder().decode(stdoutData);
171
- } catch (e) {
172
- }
173
- }
174
- if (process2.stderr && typeof process2.stderr.read === "function") {
175
- try {
176
- const stderrData = await process2.stderr.read();
177
- stderr = typeof stderrData === "string" ? stderrData : new TextDecoder().decode(stderrData);
178
- } catch (e) {
179
- }
180
- }
181
186
  return {
182
187
  stdout: stdout || "",
183
188
  stderr: stderr || "",
@@ -222,6 +227,26 @@ var modal = (0, import_computesdk.createProvider)({
222
227
  }
223
228
  };
224
229
  },
230
+ getUrl: async (modalSandbox, options) => {
231
+ try {
232
+ const tunnels = await modalSandbox.sandbox.tunnels();
233
+ const tunnel = tunnels[options.port];
234
+ if (!tunnel) {
235
+ throw new Error(`No tunnel found for port ${options.port}. Available ports: ${Object.keys(tunnels).join(", ")}`);
236
+ }
237
+ let url = tunnel.url;
238
+ if (options.protocol) {
239
+ const urlObj = new URL(url);
240
+ urlObj.protocol = options.protocol + ":";
241
+ url = urlObj.toString();
242
+ }
243
+ return url;
244
+ } catch (error) {
245
+ throw new Error(
246
+ `Failed to get Modal tunnel URL for port ${options.port}: ${error instanceof Error ? error.message : String(error)}`
247
+ );
248
+ }
249
+ },
225
250
  // Optional filesystem methods - Modal supports filesystem operations
226
251
  filesystem: {
227
252
  readFile: async (modalSandbox, path) => {
@@ -238,14 +263,19 @@ var modal = (0, import_computesdk.createProvider)({
238
263
  return content;
239
264
  } catch (error) {
240
265
  try {
241
- const process2 = await modalSandbox.sandbox.exec(["cat", path]);
242
- await process2.wait();
243
- let content = "";
244
- if (process2.stdout && typeof process2.stdout.read === "function") {
245
- const data = await process2.stdout.read();
246
- content = typeof data === "string" ? data : new TextDecoder().decode(data);
266
+ const process2 = await modalSandbox.sandbox.exec(["cat", path], {
267
+ stdout: "pipe",
268
+ stderr: "pipe"
269
+ });
270
+ const [content, stderr] = await Promise.all([
271
+ process2.stdout.readText(),
272
+ process2.stderr.readText()
273
+ ]);
274
+ const exitCode = await process2.wait();
275
+ if (exitCode !== 0) {
276
+ throw new Error(`cat failed: ${stderr}`);
247
277
  }
248
- return content;
278
+ return content.trim();
249
279
  } catch (fallbackError) {
250
280
  throw new Error(`Failed to read file ${path}: ${error instanceof Error ? error.message : String(error)}`);
251
281
  }
@@ -262,9 +292,18 @@ var modal = (0, import_computesdk.createProvider)({
262
292
  }
263
293
  } catch (error) {
264
294
  try {
265
- const escapedContent = content.replace(/'/g, `'"'"'`);
266
- const process2 = await modalSandbox.sandbox.exec(["sh", "-c", `echo '${escapedContent}' > "${path}"`]);
267
- await process2.wait();
295
+ const process2 = await modalSandbox.sandbox.exec(["sh", "-c", `printf '%s' "${content.replace(/"/g, '\\"')}" > "${path}"`], {
296
+ stdout: "pipe",
297
+ stderr: "pipe"
298
+ });
299
+ const [, stderr] = await Promise.all([
300
+ process2.stdout.readText(),
301
+ process2.stderr.readText()
302
+ ]);
303
+ const exitCode = await process2.wait();
304
+ if (exitCode !== 0) {
305
+ throw new Error(`write failed: ${stderr}`);
306
+ }
268
307
  } catch (fallbackError) {
269
308
  throw new Error(`Failed to write file ${path}: ${error instanceof Error ? error.message : String(error)}`);
270
309
  }
@@ -272,15 +311,17 @@ var modal = (0, import_computesdk.createProvider)({
272
311
  },
273
312
  mkdir: async (modalSandbox, path) => {
274
313
  try {
275
- const process2 = await modalSandbox.sandbox.exec(["mkdir", "-p", path]);
314
+ const process2 = await modalSandbox.sandbox.exec(["mkdir", "-p", path], {
315
+ stdout: "pipe",
316
+ stderr: "pipe"
317
+ });
318
+ const [, stderr] = await Promise.all([
319
+ process2.stdout.readText(),
320
+ process2.stderr.readText()
321
+ ]);
276
322
  const exitCode = await process2.wait();
277
323
  if (exitCode !== 0) {
278
- let stderr = "";
279
- if (process2.stderr && typeof process2.stderr.read === "function") {
280
- const data = await process2.stderr.read();
281
- stderr = typeof data === "string" ? data : new TextDecoder().decode(data);
282
- }
283
- throw new Error(`mkdir failed with exit code ${exitCode}: ${stderr}`);
324
+ throw new Error(`mkdir failed: ${stderr}`);
284
325
  }
285
326
  } catch (error) {
286
327
  throw new Error(`Failed to create directory ${path}: ${error instanceof Error ? error.message : String(error)}`);
@@ -288,20 +329,17 @@ var modal = (0, import_computesdk.createProvider)({
288
329
  },
289
330
  readdir: async (modalSandbox, path) => {
290
331
  try {
291
- const process2 = await modalSandbox.sandbox.exec(["ls", "-la", "--time-style=iso", path]);
332
+ const process2 = await modalSandbox.sandbox.exec(["ls", "-la", "--time-style=iso", path], {
333
+ stdout: "pipe",
334
+ stderr: "pipe"
335
+ });
336
+ const [output, stderr] = await Promise.all([
337
+ process2.stdout.readText(),
338
+ process2.stderr.readText()
339
+ ]);
292
340
  const exitCode = await process2.wait();
293
341
  if (exitCode !== 0) {
294
- let stderr = "";
295
- if (process2.stderr && typeof process2.stderr.read === "function") {
296
- const data = await process2.stderr.read();
297
- stderr = typeof data === "string" ? data : new TextDecoder().decode(data);
298
- }
299
- throw new Error(`ls failed with exit code ${exitCode}: ${stderr}`);
300
- }
301
- let output = "";
302
- if (process2.stdout && typeof process2.stdout.read === "function") {
303
- const data = await process2.stdout.read();
304
- output = typeof data === "string" ? data : new TextDecoder().decode(data);
342
+ throw new Error(`ls failed: ${stderr}`);
305
343
  }
306
344
  const lines = output.split("\n").slice(1);
307
345
  return lines.filter((line) => line.trim()).map((line) => {
@@ -310,10 +348,10 @@ var modal = (0, import_computesdk.createProvider)({
310
348
  const size = parseInt(parts[4]) || 0;
311
349
  const dateStr = (parts[5] || "") + " " + (parts[6] || "");
312
350
  const date = dateStr.trim() ? new Date(dateStr) : /* @__PURE__ */ new Date();
313
- const name = parts.slice(8).join(" ") || "unknown";
351
+ const name = parts.slice(8).join(" ") || parts[parts.length - 1] || "unknown";
314
352
  return {
315
353
  name,
316
- path: `${path}/${name}`,
354
+ path: `${path}/${name}`.replace("//", "/"),
317
355
  isDirectory: permissions.startsWith("d"),
318
356
  size,
319
357
  lastModified: isNaN(date.getTime()) ? /* @__PURE__ */ new Date() : date
@@ -334,20 +372,26 @@ var modal = (0, import_computesdk.createProvider)({
334
372
  },
335
373
  remove: async (modalSandbox, path) => {
336
374
  try {
337
- const process2 = await modalSandbox.sandbox.exec(["rm", "-rf", path]);
375
+ const process2 = await modalSandbox.sandbox.exec(["rm", "-rf", path], {
376
+ stdout: "pipe",
377
+ stderr: "pipe"
378
+ });
379
+ const [, stderr] = await Promise.all([
380
+ process2.stdout.readText(),
381
+ process2.stderr.readText()
382
+ ]);
338
383
  const exitCode = await process2.wait();
339
384
  if (exitCode !== 0) {
340
- let stderr = "";
341
- if (process2.stderr && typeof process2.stderr.read === "function") {
342
- const data = await process2.stderr.read();
343
- stderr = typeof data === "string" ? data : new TextDecoder().decode(data);
344
- }
345
- throw new Error(`rm failed with exit code ${exitCode}: ${stderr}`);
385
+ throw new Error(`rm failed: ${stderr}`);
346
386
  }
347
387
  } catch (error) {
348
388
  throw new Error(`Failed to remove ${path}: ${error instanceof Error ? error.message : String(error)}`);
349
389
  }
350
390
  }
391
+ },
392
+ // Provider-specific typed getInstance method
393
+ getInstance: (sandbox) => {
394
+ return sandbox;
351
395
  }
352
396
  }
353
397
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Modal Provider - Factory-based Implementation\n * \n * Full-featured provider with serverless sandbox execution using the factory pattern.\n * Leverages Modal's JavaScript SDK for real sandbox management.\n * \n * Note: Modal's JavaScript SDK is in alpha. This implementation provides a working\n * foundation but may need updates as the Modal API evolves.\n */\n\nimport { createProvider } from 'computesdk';\nimport type { \n ExecutionResult, \n SandboxInfo, \n Runtime,\n CreateSandboxOptions,\n FileEntry\n} from 'computesdk';\n\n// Import Modal SDK\nimport { App, Sandbox, initializeClient } from 'modal';\n\n/**\n * Modal-specific configuration options\n */\nexport interface ModalConfig {\n /** Modal API token ID - if not provided, will fallback to MODAL_TOKEN_ID environment variable */\n tokenId?: string;\n /** Modal API token secret - if not provided, will fallback to MODAL_TOKEN_SECRET environment variable */\n tokenSecret?: string;\n /** Default runtime environment */\n runtime?: Runtime;\n /** Execution timeout in milliseconds */\n timeout?: number;\n /** Modal environment (sandbox or main) */\n environment?: string;\n}\n\n/**\n * Modal sandbox interface - wraps Modal's Sandbox class\n */\ninterface ModalSandbox {\n sandbox: any; // Modal Sandbox instance (using any due to alpha SDK)\n sandboxId: string;\n}\n\n/**\n * Create a Modal provider instance using the factory pattern\n */\nexport const modal = createProvider<ModalSandbox, ModalConfig>({\n name: 'modal',\n methods: {\n sandbox: {\n // Collection operations (map to compute.sandbox.*)\n create: async (config: ModalConfig, options?: CreateSandboxOptions) => {\n // Validate API credentials\n const tokenId = config.tokenId || (typeof process !== 'undefined' && process.env?.MODAL_TOKEN_ID) || '';\n const tokenSecret = config.tokenSecret || (typeof process !== 'undefined' && process.env?.MODAL_TOKEN_SECRET) || '';\n\n if (!tokenId || !tokenSecret) {\n throw new Error(\n `Missing Modal API credentials. Provide 'tokenId' and 'tokenSecret' in config or set MODAL_TOKEN_ID and MODAL_TOKEN_SECRET environment variables. Get your credentials from https://modal.com/`\n );\n }\n\n try {\n // Initialize Modal client with credentials\n initializeClient({ tokenId, tokenSecret });\n\n let sandbox: any;\n let sandboxId: string;\n\n if (options?.sandboxId) {\n // Reconnect to existing Modal sandbox\n sandbox = await Sandbox.fromId(options.sandboxId);\n sandboxId = options.sandboxId;\n } else {\n // Create new Modal sandbox using working pattern from debug\n const app = await App.lookup('computesdk-modal', { createIfMissing: true });\n const image = await app.imageFromRegistry('python:3.13-slim');\n sandbox = await app.createSandbox(image);\n sandboxId = sandbox.sandboxId;\n }\n\n const modalSandbox: ModalSandbox = {\n sandbox,\n sandboxId\n };\n\n return {\n sandbox: modalSandbox,\n sandboxId\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.message.includes('unauthorized') || error.message.includes('credentials')) {\n throw new Error(\n `Modal authentication failed. Please check your MODAL_TOKEN_ID and MODAL_TOKEN_SECRET environment variables. Get your credentials from https://modal.com/`\n );\n }\n if (error.message.includes('quota') || error.message.includes('limit')) {\n throw new Error(\n `Modal quota exceeded. Please check your usage at https://modal.com/`\n );\n }\n }\n throw new Error(\n `Failed to create Modal sandbox: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n getById: async (config: ModalConfig, sandboxId: string) => {\n const tokenId = config.tokenId || process.env.MODAL_TOKEN_ID!;\n const tokenSecret = config.tokenSecret || process.env.MODAL_TOKEN_SECRET!;\n\n try {\n initializeClient({ tokenId, tokenSecret });\n const sandbox = await Sandbox.fromId(sandboxId);\n\n const modalSandbox: ModalSandbox = {\n sandbox,\n sandboxId\n };\n\n return {\n sandbox: modalSandbox,\n sandboxId\n };\n } catch (error) {\n // Sandbox doesn't exist or can't be accessed\n return null;\n }\n },\n\n list: async (_config: ModalConfig) => {\n throw new Error(\n `Modal provider does not support listing sandboxes. Modal sandboxes are managed individually through the Modal console. Use getById to reconnect to specific sandboxes by ID.`\n );\n },\n\n destroy: async (_config: ModalConfig, sandboxId: string) => {\n try {\n const sandbox = await Sandbox.fromId(sandboxId);\n if (sandbox && typeof sandbox.terminate === 'function') {\n await sandbox.terminate();\n }\n } catch (error) {\n // Sandbox might already be terminated or doesn't exist\n // This is acceptable for destroy operations\n }\n },\n\n // Instance operations (map to individual Sandbox methods)\n runCode: async (modalSandbox: ModalSandbox, code: string, runtime?: Runtime): Promise<ExecutionResult> => {\n const startTime = Date.now();\n\n try {\n // Auto-detect runtime if not specified\n const effectiveRuntime = runtime || (\n // Strong Python indicators\n code.includes('print(') || \n code.includes('import ') ||\n code.includes('def ') ||\n code.includes('sys.') ||\n code.includes('json.') ||\n code.includes('__') ||\n code.includes('f\"') ||\n code.includes(\"f'\")\n ? 'python'\n // Default to Python for Modal (primarily Python-focused)\n : 'python'\n );\n \n // Modal primarily supports Python execution\n if (effectiveRuntime !== 'python') {\n throw new Error('Modal provider currently only supports Python runtime');\n }\n\n // Execute code using Modal's exec method\n // Use python3 with -c flag to execute code directly\n const process = await modalSandbox.sandbox.exec(['python3', '-c', code]);\n \n // Wait for process completion\n const exitCode = await process.wait();\n \n // Read stdout and stderr from the process streams\n let stdout = '';\n let stderr = '';\n \n // Handle Modal's stream objects\n if (process.stdout && typeof process.stdout.read === 'function') {\n try {\n const stdoutData = await process.stdout.read();\n stdout = typeof stdoutData === 'string' ? stdoutData : new TextDecoder().decode(stdoutData);\n } catch (e) {\n // If reading fails, stdout remains empty\n }\n }\n \n if (process.stderr && typeof process.stderr.read === 'function') {\n try {\n const stderrData = await process.stderr.read();\n stderr = typeof stderrData === 'string' ? stderrData : new TextDecoder().decode(stderrData);\n } catch (e) {\n // If reading fails, stderr remains empty\n }\n }\n\n // Check for syntax errors in stderr\n if (exitCode !== 0 && stderr && (\n stderr.includes('SyntaxError') || \n stderr.includes('invalid syntax')\n )) {\n throw new Error(`Syntax error: ${stderr.trim()}`);\n }\n\n return {\n stdout: stdout || '',\n stderr: stderr || '',\n exitCode: exitCode || 0,\n executionTime: Date.now() - startTime,\n sandboxId: modalSandbox.sandboxId,\n provider: 'modal'\n };\n } catch (error) {\n // Handle syntax errors and runtime errors\n if (error instanceof Error && error.message.includes('Syntax error')) {\n throw error; // Re-throw syntax errors\n }\n \n throw new Error(\n `Modal execution failed: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n runCommand: async (modalSandbox: ModalSandbox, command: string, args: string[] = []): Promise<ExecutionResult> => {\n const startTime = Date.now();\n\n try {\n // Execute command using Modal's exec method\n const commandArray = [command, ...args];\n const process = await modalSandbox.sandbox.exec(commandArray);\n \n // Wait for process completion\n const exitCode = await process.wait();\n \n // Read stdout and stderr from the process streams\n let stdout = '';\n let stderr = '';\n \n // Handle Modal's stream objects\n if (process.stdout && typeof process.stdout.read === 'function') {\n try {\n const stdoutData = await process.stdout.read();\n stdout = typeof stdoutData === 'string' ? stdoutData : new TextDecoder().decode(stdoutData);\n } catch (e) {\n // If reading fails, stdout remains empty\n }\n }\n \n if (process.stderr && typeof process.stderr.read === 'function') {\n try {\n const stderrData = await process.stderr.read();\n stderr = typeof stderrData === 'string' ? stderrData : new TextDecoder().decode(stderrData);\n } catch (e) {\n // If reading fails, stderr remains empty\n }\n }\n\n return {\n stdout: stdout || '',\n stderr: stderr || '',\n exitCode: exitCode || 0,\n executionTime: Date.now() - startTime,\n sandboxId: modalSandbox.sandboxId,\n provider: 'modal'\n };\n } catch (error) {\n // For command failures, return error info instead of throwing\n return {\n stdout: '',\n stderr: error instanceof Error ? error.message : String(error),\n exitCode: 127, // Command not found exit code\n executionTime: Date.now() - startTime,\n sandboxId: modalSandbox.sandboxId,\n provider: 'modal'\n };\n }\n },\n\n getInfo: async (modalSandbox: ModalSandbox): Promise<SandboxInfo> => {\n // Get actual sandbox status using Modal's poll method\n let status: 'running' | 'stopped' | 'error' = 'running';\n try {\n const pollResult = await modalSandbox.sandbox.poll();\n if (pollResult !== null) {\n // Sandbox has finished\n status = pollResult === 0 ? 'stopped' : 'error';\n }\n } catch (error) {\n // If polling fails, assume running\n status = 'running';\n }\n\n return {\n id: modalSandbox.sandboxId,\n provider: 'modal',\n runtime: 'python', // Modal default\n status,\n createdAt: new Date(),\n timeout: 300000,\n metadata: {\n modalSandboxId: modalSandbox.sandboxId,\n realModalImplementation: true\n }\n };\n },\n\n // Optional filesystem methods - Modal supports filesystem operations\n filesystem: {\n readFile: async (modalSandbox: ModalSandbox, path: string): Promise<string> => {\n try {\n // Use Modal's file open API to read files\n const file = await modalSandbox.sandbox.open(path);\n \n // Read the entire file content\n let content = '';\n if (file && typeof file.read === 'function') {\n const data = await file.read();\n content = typeof data === 'string' ? data : new TextDecoder().decode(data);\n }\n \n // Close the file if it has a close method\n if (file && typeof file.close === 'function') {\n await file.close();\n }\n \n return content;\n } catch (error) {\n // Fallback to using cat command if direct file API fails\n try {\n const process = await modalSandbox.sandbox.exec(['cat', path]);\n await process.wait();\n \n let content = '';\n if (process.stdout && typeof process.stdout.read === 'function') {\n const data = await process.stdout.read();\n content = typeof data === 'string' ? data : new TextDecoder().decode(data);\n }\n \n return content;\n } catch (fallbackError) {\n throw new Error(`Failed to read file ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n },\n\n writeFile: async (modalSandbox: ModalSandbox, path: string, content: string): Promise<void> => {\n try {\n // Use Modal's file open API to write files\n const file = await modalSandbox.sandbox.open(path);\n \n // Write content to the file\n if (file && typeof file.write === 'function') {\n await file.write(content);\n }\n \n // Close the file if it has a close method\n if (file && typeof file.close === 'function') {\n await file.close();\n }\n } catch (error) {\n // Fallback to using echo command if direct file API fails\n try {\n // Escape content for shell command\n const escapedContent = content.replace(/'/g, \"'\\\"'\\\"'\");\n const process = await modalSandbox.sandbox.exec(['sh', '-c', `echo '${escapedContent}' > \"${path}\"`]);\n await process.wait();\n } catch (fallbackError) {\n throw new Error(`Failed to write file ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n },\n\n mkdir: async (modalSandbox: ModalSandbox, path: string): Promise<void> => {\n try {\n const process = await modalSandbox.sandbox.exec(['mkdir', '-p', path]);\n const exitCode = await process.wait();\n \n if (exitCode !== 0) {\n let stderr = '';\n if (process.stderr && typeof process.stderr.read === 'function') {\n const data = await process.stderr.read();\n stderr = typeof data === 'string' ? data : new TextDecoder().decode(data);\n }\n throw new Error(`mkdir failed with exit code ${exitCode}: ${stderr}`);\n }\n } catch (error) {\n throw new Error(`Failed to create directory ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n\n readdir: async (modalSandbox: ModalSandbox, path: string): Promise<FileEntry[]> => {\n try {\n const process = await modalSandbox.sandbox.exec(['ls', '-la', '--time-style=iso', path]);\n const exitCode = await process.wait();\n \n if (exitCode !== 0) {\n let stderr = '';\n if (process.stderr && typeof process.stderr.read === 'function') {\n const data = await process.stderr.read();\n stderr = typeof data === 'string' ? data : new TextDecoder().decode(data);\n }\n throw new Error(`ls failed with exit code ${exitCode}: ${stderr}`);\n }\n \n let output = '';\n if (process.stdout && typeof process.stdout.read === 'function') {\n const data = await process.stdout.read();\n output = typeof data === 'string' ? data : new TextDecoder().decode(data);\n }\n \n const lines = output.split('\\n').slice(1); // Skip header line\n \n return lines.filter(line => line.trim()).map(line => {\n const parts = line.trim().split(/\\s+/);\n const permissions = parts[0] || '';\n const size = parseInt(parts[4]) || 0;\n const dateStr = (parts[5] || '') + ' ' + (parts[6] || '');\n const date = dateStr.trim() ? new Date(dateStr) : new Date();\n const name = parts.slice(8).join(' ') || 'unknown';\n \n return {\n name,\n path: `${path}/${name}`,\n isDirectory: permissions.startsWith('d'),\n size,\n lastModified: isNaN(date.getTime()) ? new Date() : date\n };\n });\n } catch (error) {\n throw new Error(`Failed to read directory ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n\n exists: async (modalSandbox: ModalSandbox, path: string): Promise<boolean> => {\n try {\n const process = await modalSandbox.sandbox.exec(['test', '-e', path]);\n const exitCode = await process.wait();\n return exitCode === 0;\n } catch (error) {\n return false;\n }\n },\n\n remove: async (modalSandbox: ModalSandbox, path: string): Promise<void> => {\n try {\n const process = await modalSandbox.sandbox.exec(['rm', '-rf', path]);\n const exitCode = await process.wait();\n \n if (exitCode !== 0) {\n let stderr = '';\n if (process.stderr && typeof process.stderr.read === 'function') {\n const data = await process.stderr.read();\n stderr = typeof data === 'string' ? data : new TextDecoder().decode(data);\n }\n throw new Error(`rm failed with exit code ${exitCode}: ${stderr}`);\n }\n } catch (error) {\n throw new Error(`Failed to remove ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n }\n }\n }\n});"],"mappings":"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","names":["process"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Modal Provider - Factory-based Implementation\n * \n * Full-featured provider with serverless sandbox execution using the factory pattern.\n * Leverages Modal's JavaScript SDK for real sandbox management.\n * \n * Note: Modal's JavaScript SDK is in alpha. This implementation provides a working\n * foundation but may need updates as the Modal API evolves.\n */\n\nimport { createProvider } from 'computesdk';\nimport type { \n ExecutionResult, \n SandboxInfo, \n Runtime,\n CreateSandboxOptions,\n FileEntry\n} from 'computesdk';\n\n// Import Modal SDK\nimport { App, Sandbox, initializeClient } from 'modal';\n\n/**\n * Modal-specific configuration options\n */\nexport interface ModalConfig {\n /** Modal API token ID - if not provided, will fallback to MODAL_TOKEN_ID environment variable */\n tokenId?: string;\n /** Modal API token secret - if not provided, will fallback to MODAL_TOKEN_SECRET environment variable */\n tokenSecret?: string;\n /** Default runtime environment */\n runtime?: Runtime;\n /** Execution timeout in milliseconds */\n timeout?: number;\n /** Modal environment (sandbox or main) */\n environment?: string;\n}\n\n/**\n * Modal sandbox interface - wraps Modal's Sandbox class\n */\ninterface ModalSandbox {\n sandbox: any; // Modal Sandbox instance (using any due to alpha SDK)\n sandboxId: string;\n}\n\n/**\n * Detect runtime from code content\n */\nfunction detectRuntime(code: string): Runtime {\n // Strong Node.js indicators\n if (code.includes('console.log') || \n code.includes('process.') ||\n code.includes('require(') ||\n code.includes('module.exports') ||\n code.includes('__dirname') ||\n code.includes('__filename') ||\n code.includes('throw new Error') || // JavaScript error throwing\n code.includes('new Error(')) {\n return 'node';\n }\n\n // Strong Python indicators \n if (code.includes('print(') || \n code.includes('import ') ||\n code.includes('def ') ||\n code.includes('sys.') ||\n code.includes('json.') ||\n code.includes('f\"') ||\n code.includes(\"f'\")) {\n return 'python';\n }\n\n // Default to Python for Modal\n return 'python';\n}\n\n/**\n * Create a Modal provider instance using the factory pattern\n */\nexport const modal = createProvider<ModalSandbox, ModalConfig>({\n name: 'modal',\n methods: {\n sandbox: {\n // Collection operations (map to compute.sandbox.*)\n create: async (config: ModalConfig, options?: CreateSandboxOptions) => {\n // Validate API credentials\n const tokenId = config.tokenId || (typeof process !== 'undefined' && process.env?.MODAL_TOKEN_ID) || '';\n const tokenSecret = config.tokenSecret || (typeof process !== 'undefined' && process.env?.MODAL_TOKEN_SECRET) || '';\n\n if (!tokenId || !tokenSecret) {\n throw new Error(\n `Missing Modal API credentials. Provide 'tokenId' and 'tokenSecret' in config or set MODAL_TOKEN_ID and MODAL_TOKEN_SECRET environment variables. Get your credentials from https://modal.com/`\n );\n }\n\n try {\n // Initialize Modal client with credentials\n initializeClient({ tokenId, tokenSecret });\n\n let sandbox: any;\n let sandboxId: string;\n\n if (options?.sandboxId) {\n // Reconnect to existing Modal sandbox\n sandbox = await Sandbox.fromId(options.sandboxId);\n sandboxId = options.sandboxId;\n } else {\n // Create new Modal sandbox using working pattern from debug\n const app = await App.lookup('computesdk-modal', { createIfMissing: true });\n const image = await app.imageFromRegistry('python:3.13-slim');\n sandbox = await app.createSandbox(image);\n sandboxId = sandbox.sandboxId;\n }\n\n const modalSandbox: ModalSandbox = {\n sandbox,\n sandboxId\n };\n\n return {\n sandbox: modalSandbox,\n sandboxId\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.message.includes('unauthorized') || error.message.includes('credentials')) {\n throw new Error(\n `Modal authentication failed. Please check your MODAL_TOKEN_ID and MODAL_TOKEN_SECRET environment variables. Get your credentials from https://modal.com/`\n );\n }\n if (error.message.includes('quota') || error.message.includes('limit')) {\n throw new Error(\n `Modal quota exceeded. Please check your usage at https://modal.com/`\n );\n }\n }\n throw new Error(\n `Failed to create Modal sandbox: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n getById: async (config: ModalConfig, sandboxId: string) => {\n const tokenId = config.tokenId || process.env.MODAL_TOKEN_ID!;\n const tokenSecret = config.tokenSecret || process.env.MODAL_TOKEN_SECRET!;\n\n try {\n initializeClient({ tokenId, tokenSecret });\n const sandbox = await Sandbox.fromId(sandboxId);\n\n const modalSandbox: ModalSandbox = {\n sandbox,\n sandboxId\n };\n\n return {\n sandbox: modalSandbox,\n sandboxId\n };\n } catch (error) {\n // Sandbox doesn't exist or can't be accessed\n return null;\n }\n },\n\n list: async (_config: ModalConfig) => {\n throw new Error(\n `Modal provider does not support listing sandboxes. Modal sandboxes are managed individually through the Modal console. Use getById to reconnect to specific sandboxes by ID.`\n );\n },\n\n destroy: async (_config: ModalConfig, sandboxId: string) => {\n try {\n const sandbox = await Sandbox.fromId(sandboxId);\n if (sandbox && typeof sandbox.terminate === 'function') {\n await sandbox.terminate();\n }\n } catch (error) {\n // Sandbox might already be terminated or doesn't exist\n // This is acceptable for destroy operations\n }\n },\n\n // Instance operations (map to individual Sandbox methods)\n runCode: async (modalSandbox: ModalSandbox, code: string, runtime?: Runtime): Promise<ExecutionResult> => {\n const startTime = Date.now();\n\n try {\n // Auto-detect runtime from code if not specified\n const detectedRuntime = runtime || detectRuntime(code);\n \n // Create appropriate sandbox and command for the runtime\n let executionSandbox = modalSandbox.sandbox;\n let command: string[];\n let shouldCleanupSandbox = false;\n \n if (detectedRuntime === 'node') {\n // For Node.js execution, create a Node.js sandbox dynamically\n const app = await App.lookup('computesdk-modal', { createIfMissing: true });\n const nodeImage = await app.imageFromRegistry('node:20-alpine');\n executionSandbox = await app.createSandbox(nodeImage);\n command = ['node', '-e', code];\n shouldCleanupSandbox = true; // Clean up temporary Node.js sandbox\n } else {\n // Use existing Python sandbox for Python code\n command = ['python3', '-c', code];\n }\n\n const process = await executionSandbox.exec(command, {\n stdout: 'pipe',\n stderr: 'pipe'\n });\n\n // Use working stream reading pattern from debug\n const [stdout, stderr] = await Promise.all([\n process.stdout.readText(),\n process.stderr.readText()\n ]);\n\n const exitCode = await process.wait();\n \n // Clean up temporary Node.js sandbox if created\n if (shouldCleanupSandbox && executionSandbox !== modalSandbox.sandbox) {\n try {\n await executionSandbox.terminate();\n } catch (e) {\n // Ignore cleanup errors\n }\n }\n\n // Check for syntax errors in stderr\n if (exitCode !== 0 && stderr && (\n stderr.includes('SyntaxError') || \n stderr.includes('invalid syntax')\n )) {\n throw new Error(`Syntax error: ${stderr.trim()}`);\n }\n\n return {\n stdout: stdout || '',\n stderr: stderr || '',\n exitCode: exitCode || 0,\n executionTime: Date.now() - startTime,\n sandboxId: modalSandbox.sandboxId,\n provider: 'modal'\n };\n } catch (error) {\n // Handle syntax errors and runtime errors\n if (error instanceof Error && error.message.includes('Syntax error')) {\n throw error; // Re-throw syntax errors\n }\n \n throw new Error(\n `Modal execution failed: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n runCommand: async (modalSandbox: ModalSandbox, command: string, args: string[] = []): Promise<ExecutionResult> => {\n const startTime = Date.now();\n\n try {\n // Execute command using Modal's exec method with working pattern\n const process = await modalSandbox.sandbox.exec([command, ...args], {\n stdout: 'pipe',\n stderr: 'pipe'\n });\n\n // Use working stream reading pattern from debug\n const [stdout, stderr] = await Promise.all([\n process.stdout.readText(),\n process.stderr.readText()\n ]);\n\n const exitCode = await process.wait();\n\n return {\n stdout: stdout || '',\n stderr: stderr || '',\n exitCode: exitCode || 0,\n executionTime: Date.now() - startTime,\n sandboxId: modalSandbox.sandboxId,\n provider: 'modal'\n };\n } catch (error) {\n // For command failures, return error info instead of throwing\n return {\n stdout: '',\n stderr: error instanceof Error ? error.message : String(error),\n exitCode: 127, // Command not found exit code\n executionTime: Date.now() - startTime,\n sandboxId: modalSandbox.sandboxId,\n provider: 'modal'\n };\n }\n },\n\n getInfo: async (modalSandbox: ModalSandbox): Promise<SandboxInfo> => {\n // Get actual sandbox status using Modal's poll method\n let status: 'running' | 'stopped' | 'error' = 'running';\n try {\n const pollResult = await modalSandbox.sandbox.poll();\n if (pollResult !== null) {\n // Sandbox has finished\n status = pollResult === 0 ? 'stopped' : 'error';\n }\n } catch (error) {\n // If polling fails, assume running\n status = 'running';\n }\n\n return {\n id: modalSandbox.sandboxId,\n provider: 'modal',\n runtime: 'python', // Modal default\n status,\n createdAt: new Date(),\n timeout: 300000,\n metadata: {\n modalSandboxId: modalSandbox.sandboxId,\n realModalImplementation: true\n }\n };\n },\n\n getUrl: async (modalSandbox: ModalSandbox, options: { port: number; protocol?: string }): Promise<string> => {\n try {\n // Use Modal's built-in tunnels method to get tunnel information\n const tunnels = await modalSandbox.sandbox.tunnels();\n const tunnel = tunnels[options.port];\n \n if (!tunnel) {\n throw new Error(`No tunnel found for port ${options.port}. Available ports: ${Object.keys(tunnels).join(', ')}`);\n }\n \n let url = tunnel.url;\n \n // If a specific protocol is requested, replace the URL's protocol\n if (options.protocol) {\n const urlObj = new URL(url);\n urlObj.protocol = options.protocol + ':';\n url = urlObj.toString();\n }\n \n return url;\n } catch (error) {\n throw new Error(\n `Failed to get Modal tunnel URL for port ${options.port}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n // Optional filesystem methods - Modal supports filesystem operations\n filesystem: {\n readFile: async (modalSandbox: ModalSandbox, path: string): Promise<string> => {\n try {\n // Use Modal's file open API to read files\n const file = await modalSandbox.sandbox.open(path);\n \n // Read the entire file content\n let content = '';\n if (file && typeof file.read === 'function') {\n const data = await file.read();\n content = typeof data === 'string' ? data : new TextDecoder().decode(data);\n }\n \n // Close the file if it has a close method\n if (file && typeof file.close === 'function') {\n await file.close();\n }\n \n return content;\n } catch (error) {\n // Fallback to using cat command with working stream pattern\n try {\n const process = await modalSandbox.sandbox.exec(['cat', path], {\n stdout: 'pipe',\n stderr: 'pipe'\n });\n\n const [content, stderr] = await Promise.all([\n process.stdout.readText(),\n process.stderr.readText()\n ]);\n\n const exitCode = await process.wait();\n\n if (exitCode !== 0) {\n throw new Error(`cat failed: ${stderr}`);\n }\n\n return content.trim(); // Remove extra newlines\n } catch (fallbackError) {\n throw new Error(`Failed to read file ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n },\n\n writeFile: async (modalSandbox: ModalSandbox, path: string, content: string): Promise<void> => {\n try {\n // Use Modal's file open API to write files\n const file = await modalSandbox.sandbox.open(path);\n \n // Write content to the file\n if (file && typeof file.write === 'function') {\n await file.write(content);\n }\n \n // Close the file if it has a close method\n if (file && typeof file.close === 'function') {\n await file.close();\n }\n } catch (error) {\n // Fallback to using shell command with proper escaping\n try {\n const process = await modalSandbox.sandbox.exec(['sh', '-c', `printf '%s' \"${content.replace(/\"/g, '\\\\\"')}\" > \"${path}\"`], {\n stdout: 'pipe',\n stderr: 'pipe'\n });\n\n const [, stderr] = await Promise.all([\n process.stdout.readText(),\n process.stderr.readText()\n ]);\n\n const exitCode = await process.wait();\n\n if (exitCode !== 0) {\n throw new Error(`write failed: ${stderr}`);\n }\n } catch (fallbackError) {\n throw new Error(`Failed to write file ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n },\n\n mkdir: async (modalSandbox: ModalSandbox, path: string): Promise<void> => {\n try {\n const process = await modalSandbox.sandbox.exec(['mkdir', '-p', path], {\n stdout: 'pipe',\n stderr: 'pipe'\n });\n\n const [, stderr] = await Promise.all([\n process.stdout.readText(),\n process.stderr.readText()\n ]);\n\n const exitCode = await process.wait();\n\n if (exitCode !== 0) {\n throw new Error(`mkdir failed: ${stderr}`);\n }\n } catch (error) {\n throw new Error(`Failed to create directory ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n\n readdir: async (modalSandbox: ModalSandbox, path: string): Promise<FileEntry[]> => {\n try {\n const process = await modalSandbox.sandbox.exec(['ls', '-la', '--time-style=iso', path], {\n stdout: 'pipe',\n stderr: 'pipe'\n });\n\n const [output, stderr] = await Promise.all([\n process.stdout.readText(),\n process.stderr.readText()\n ]);\n\n const exitCode = await process.wait();\n\n if (exitCode !== 0) {\n throw new Error(`ls failed: ${stderr}`);\n }\n\n const lines = output.split('\\n').slice(1); // Skip header\n\n return lines\n .filter((line: string) => line.trim())\n .map((line: string) => {\n const parts = line.trim().split(/\\s+/);\n const permissions = parts[0] || '';\n const size = parseInt(parts[4]) || 0;\n const dateStr = (parts[5] || '') + ' ' + (parts[6] || '');\n const date = dateStr.trim() ? new Date(dateStr) : new Date();\n const name = parts.slice(8).join(' ') || parts[parts.length - 1] || 'unknown';\n\n return {\n name,\n path: `${path}/${name}`.replace('//', '/'),\n isDirectory: permissions.startsWith('d'),\n size,\n lastModified: isNaN(date.getTime()) ? new Date() : date\n };\n });\n } catch (error) {\n throw new Error(`Failed to read directory ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n\n exists: async (modalSandbox: ModalSandbox, path: string): Promise<boolean> => {\n try {\n const process = await modalSandbox.sandbox.exec(['test', '-e', path]);\n const exitCode = await process.wait();\n return exitCode === 0;\n } catch (error) {\n return false;\n }\n },\n\n remove: async (modalSandbox: ModalSandbox, path: string): Promise<void> => {\n try {\n const process = await modalSandbox.sandbox.exec(['rm', '-rf', path], {\n stdout: 'pipe',\n stderr: 'pipe'\n });\n\n const [, stderr] = await Promise.all([\n process.stdout.readText(),\n process.stderr.readText()\n ]);\n\n const exitCode = await process.wait();\n\n if (exitCode !== 0) {\n throw new Error(`rm failed: ${stderr}`);\n }\n } catch (error) {\n throw new Error(`Failed to remove ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n },\n\n // Provider-specific typed getInstance method\n getInstance: (sandbox: ModalSandbox): ModalSandbox => {\n return sandbox;\n },\n\n }\n }\n});"],"mappings":"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","names":["process"]}
package/dist/index.mjs CHANGED
@@ -1,6 +1,16 @@
1
1
  // src/index.ts
2
2
  import { createProvider } from "computesdk";
3
3
  import { App, Sandbox, initializeClient } from "modal";
4
+ function detectRuntime(code) {
5
+ if (code.includes("console.log") || code.includes("process.") || code.includes("require(") || code.includes("module.exports") || code.includes("__dirname") || code.includes("__filename") || code.includes("throw new Error") || // JavaScript error throwing
6
+ code.includes("new Error(")) {
7
+ return "node";
8
+ }
9
+ if (code.includes("print(") || code.includes("import ") || code.includes("def ") || code.includes("sys.") || code.includes("json.") || code.includes('f"') || code.includes("f'")) {
10
+ return "python";
11
+ }
12
+ return "python";
13
+ }
4
14
  var modal = createProvider({
5
15
  name: "modal",
6
16
  methods: {
@@ -89,26 +99,31 @@ var modal = createProvider({
89
99
  runCode: async (modalSandbox, code, runtime) => {
90
100
  const startTime = Date.now();
91
101
  try {
92
- const effectiveRuntime = runtime || // Strong Python indicators
93
- (code.includes("print(") || code.includes("import ") || code.includes("def ") || code.includes("sys.") || code.includes("json.") || code.includes("__") || code.includes('f"') || code.includes("f'") ? "python" : "python");
94
- if (effectiveRuntime !== "python") {
95
- throw new Error("Modal provider currently only supports Python runtime");
102
+ const detectedRuntime = runtime || detectRuntime(code);
103
+ let executionSandbox = modalSandbox.sandbox;
104
+ let command;
105
+ let shouldCleanupSandbox = false;
106
+ if (detectedRuntime === "node") {
107
+ const app = await App.lookup("computesdk-modal", { createIfMissing: true });
108
+ const nodeImage = await app.imageFromRegistry("node:20-alpine");
109
+ executionSandbox = await app.createSandbox(nodeImage);
110
+ command = ["node", "-e", code];
111
+ shouldCleanupSandbox = true;
112
+ } else {
113
+ command = ["python3", "-c", code];
96
114
  }
97
- const process2 = await modalSandbox.sandbox.exec(["python3", "-c", code]);
115
+ const process2 = await executionSandbox.exec(command, {
116
+ stdout: "pipe",
117
+ stderr: "pipe"
118
+ });
119
+ const [stdout, stderr] = await Promise.all([
120
+ process2.stdout.readText(),
121
+ process2.stderr.readText()
122
+ ]);
98
123
  const exitCode = await process2.wait();
99
- let stdout = "";
100
- let stderr = "";
101
- if (process2.stdout && typeof process2.stdout.read === "function") {
124
+ if (shouldCleanupSandbox && executionSandbox !== modalSandbox.sandbox) {
102
125
  try {
103
- const stdoutData = await process2.stdout.read();
104
- stdout = typeof stdoutData === "string" ? stdoutData : new TextDecoder().decode(stdoutData);
105
- } catch (e) {
106
- }
107
- }
108
- if (process2.stderr && typeof process2.stderr.read === "function") {
109
- try {
110
- const stderrData = await process2.stderr.read();
111
- stderr = typeof stderrData === "string" ? stderrData : new TextDecoder().decode(stderrData);
126
+ await executionSandbox.terminate();
112
127
  } catch (e) {
113
128
  }
114
129
  }
@@ -135,25 +150,15 @@ var modal = createProvider({
135
150
  runCommand: async (modalSandbox, command, args = []) => {
136
151
  const startTime = Date.now();
137
152
  try {
138
- const commandArray = [command, ...args];
139
- const process2 = await modalSandbox.sandbox.exec(commandArray);
153
+ const process2 = await modalSandbox.sandbox.exec([command, ...args], {
154
+ stdout: "pipe",
155
+ stderr: "pipe"
156
+ });
157
+ const [stdout, stderr] = await Promise.all([
158
+ process2.stdout.readText(),
159
+ process2.stderr.readText()
160
+ ]);
140
161
  const exitCode = await process2.wait();
141
- let stdout = "";
142
- let stderr = "";
143
- if (process2.stdout && typeof process2.stdout.read === "function") {
144
- try {
145
- const stdoutData = await process2.stdout.read();
146
- stdout = typeof stdoutData === "string" ? stdoutData : new TextDecoder().decode(stdoutData);
147
- } catch (e) {
148
- }
149
- }
150
- if (process2.stderr && typeof process2.stderr.read === "function") {
151
- try {
152
- const stderrData = await process2.stderr.read();
153
- stderr = typeof stderrData === "string" ? stderrData : new TextDecoder().decode(stderrData);
154
- } catch (e) {
155
- }
156
- }
157
162
  return {
158
163
  stdout: stdout || "",
159
164
  stderr: stderr || "",
@@ -198,6 +203,26 @@ var modal = createProvider({
198
203
  }
199
204
  };
200
205
  },
206
+ getUrl: async (modalSandbox, options) => {
207
+ try {
208
+ const tunnels = await modalSandbox.sandbox.tunnels();
209
+ const tunnel = tunnels[options.port];
210
+ if (!tunnel) {
211
+ throw new Error(`No tunnel found for port ${options.port}. Available ports: ${Object.keys(tunnels).join(", ")}`);
212
+ }
213
+ let url = tunnel.url;
214
+ if (options.protocol) {
215
+ const urlObj = new URL(url);
216
+ urlObj.protocol = options.protocol + ":";
217
+ url = urlObj.toString();
218
+ }
219
+ return url;
220
+ } catch (error) {
221
+ throw new Error(
222
+ `Failed to get Modal tunnel URL for port ${options.port}: ${error instanceof Error ? error.message : String(error)}`
223
+ );
224
+ }
225
+ },
201
226
  // Optional filesystem methods - Modal supports filesystem operations
202
227
  filesystem: {
203
228
  readFile: async (modalSandbox, path) => {
@@ -214,14 +239,19 @@ var modal = createProvider({
214
239
  return content;
215
240
  } catch (error) {
216
241
  try {
217
- const process2 = await modalSandbox.sandbox.exec(["cat", path]);
218
- await process2.wait();
219
- let content = "";
220
- if (process2.stdout && typeof process2.stdout.read === "function") {
221
- const data = await process2.stdout.read();
222
- content = typeof data === "string" ? data : new TextDecoder().decode(data);
242
+ const process2 = await modalSandbox.sandbox.exec(["cat", path], {
243
+ stdout: "pipe",
244
+ stderr: "pipe"
245
+ });
246
+ const [content, stderr] = await Promise.all([
247
+ process2.stdout.readText(),
248
+ process2.stderr.readText()
249
+ ]);
250
+ const exitCode = await process2.wait();
251
+ if (exitCode !== 0) {
252
+ throw new Error(`cat failed: ${stderr}`);
223
253
  }
224
- return content;
254
+ return content.trim();
225
255
  } catch (fallbackError) {
226
256
  throw new Error(`Failed to read file ${path}: ${error instanceof Error ? error.message : String(error)}`);
227
257
  }
@@ -238,9 +268,18 @@ var modal = createProvider({
238
268
  }
239
269
  } catch (error) {
240
270
  try {
241
- const escapedContent = content.replace(/'/g, `'"'"'`);
242
- const process2 = await modalSandbox.sandbox.exec(["sh", "-c", `echo '${escapedContent}' > "${path}"`]);
243
- await process2.wait();
271
+ const process2 = await modalSandbox.sandbox.exec(["sh", "-c", `printf '%s' "${content.replace(/"/g, '\\"')}" > "${path}"`], {
272
+ stdout: "pipe",
273
+ stderr: "pipe"
274
+ });
275
+ const [, stderr] = await Promise.all([
276
+ process2.stdout.readText(),
277
+ process2.stderr.readText()
278
+ ]);
279
+ const exitCode = await process2.wait();
280
+ if (exitCode !== 0) {
281
+ throw new Error(`write failed: ${stderr}`);
282
+ }
244
283
  } catch (fallbackError) {
245
284
  throw new Error(`Failed to write file ${path}: ${error instanceof Error ? error.message : String(error)}`);
246
285
  }
@@ -248,15 +287,17 @@ var modal = createProvider({
248
287
  },
249
288
  mkdir: async (modalSandbox, path) => {
250
289
  try {
251
- const process2 = await modalSandbox.sandbox.exec(["mkdir", "-p", path]);
290
+ const process2 = await modalSandbox.sandbox.exec(["mkdir", "-p", path], {
291
+ stdout: "pipe",
292
+ stderr: "pipe"
293
+ });
294
+ const [, stderr] = await Promise.all([
295
+ process2.stdout.readText(),
296
+ process2.stderr.readText()
297
+ ]);
252
298
  const exitCode = await process2.wait();
253
299
  if (exitCode !== 0) {
254
- let stderr = "";
255
- if (process2.stderr && typeof process2.stderr.read === "function") {
256
- const data = await process2.stderr.read();
257
- stderr = typeof data === "string" ? data : new TextDecoder().decode(data);
258
- }
259
- throw new Error(`mkdir failed with exit code ${exitCode}: ${stderr}`);
300
+ throw new Error(`mkdir failed: ${stderr}`);
260
301
  }
261
302
  } catch (error) {
262
303
  throw new Error(`Failed to create directory ${path}: ${error instanceof Error ? error.message : String(error)}`);
@@ -264,20 +305,17 @@ var modal = createProvider({
264
305
  },
265
306
  readdir: async (modalSandbox, path) => {
266
307
  try {
267
- const process2 = await modalSandbox.sandbox.exec(["ls", "-la", "--time-style=iso", path]);
308
+ const process2 = await modalSandbox.sandbox.exec(["ls", "-la", "--time-style=iso", path], {
309
+ stdout: "pipe",
310
+ stderr: "pipe"
311
+ });
312
+ const [output, stderr] = await Promise.all([
313
+ process2.stdout.readText(),
314
+ process2.stderr.readText()
315
+ ]);
268
316
  const exitCode = await process2.wait();
269
317
  if (exitCode !== 0) {
270
- let stderr = "";
271
- if (process2.stderr && typeof process2.stderr.read === "function") {
272
- const data = await process2.stderr.read();
273
- stderr = typeof data === "string" ? data : new TextDecoder().decode(data);
274
- }
275
- throw new Error(`ls failed with exit code ${exitCode}: ${stderr}`);
276
- }
277
- let output = "";
278
- if (process2.stdout && typeof process2.stdout.read === "function") {
279
- const data = await process2.stdout.read();
280
- output = typeof data === "string" ? data : new TextDecoder().decode(data);
318
+ throw new Error(`ls failed: ${stderr}`);
281
319
  }
282
320
  const lines = output.split("\n").slice(1);
283
321
  return lines.filter((line) => line.trim()).map((line) => {
@@ -286,10 +324,10 @@ var modal = createProvider({
286
324
  const size = parseInt(parts[4]) || 0;
287
325
  const dateStr = (parts[5] || "") + " " + (parts[6] || "");
288
326
  const date = dateStr.trim() ? new Date(dateStr) : /* @__PURE__ */ new Date();
289
- const name = parts.slice(8).join(" ") || "unknown";
327
+ const name = parts.slice(8).join(" ") || parts[parts.length - 1] || "unknown";
290
328
  return {
291
329
  name,
292
- path: `${path}/${name}`,
330
+ path: `${path}/${name}`.replace("//", "/"),
293
331
  isDirectory: permissions.startsWith("d"),
294
332
  size,
295
333
  lastModified: isNaN(date.getTime()) ? /* @__PURE__ */ new Date() : date
@@ -310,20 +348,26 @@ var modal = createProvider({
310
348
  },
311
349
  remove: async (modalSandbox, path) => {
312
350
  try {
313
- const process2 = await modalSandbox.sandbox.exec(["rm", "-rf", path]);
351
+ const process2 = await modalSandbox.sandbox.exec(["rm", "-rf", path], {
352
+ stdout: "pipe",
353
+ stderr: "pipe"
354
+ });
355
+ const [, stderr] = await Promise.all([
356
+ process2.stdout.readText(),
357
+ process2.stderr.readText()
358
+ ]);
314
359
  const exitCode = await process2.wait();
315
360
  if (exitCode !== 0) {
316
- let stderr = "";
317
- if (process2.stderr && typeof process2.stderr.read === "function") {
318
- const data = await process2.stderr.read();
319
- stderr = typeof data === "string" ? data : new TextDecoder().decode(data);
320
- }
321
- throw new Error(`rm failed with exit code ${exitCode}: ${stderr}`);
361
+ throw new Error(`rm failed: ${stderr}`);
322
362
  }
323
363
  } catch (error) {
324
364
  throw new Error(`Failed to remove ${path}: ${error instanceof Error ? error.message : String(error)}`);
325
365
  }
326
366
  }
367
+ },
368
+ // Provider-specific typed getInstance method
369
+ getInstance: (sandbox) => {
370
+ return sandbox;
327
371
  }
328
372
  }
329
373
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Modal Provider - Factory-based Implementation\n * \n * Full-featured provider with serverless sandbox execution using the factory pattern.\n * Leverages Modal's JavaScript SDK for real sandbox management.\n * \n * Note: Modal's JavaScript SDK is in alpha. This implementation provides a working\n * foundation but may need updates as the Modal API evolves.\n */\n\nimport { createProvider } from 'computesdk';\nimport type { \n ExecutionResult, \n SandboxInfo, \n Runtime,\n CreateSandboxOptions,\n FileEntry\n} from 'computesdk';\n\n// Import Modal SDK\nimport { App, Sandbox, initializeClient } from 'modal';\n\n/**\n * Modal-specific configuration options\n */\nexport interface ModalConfig {\n /** Modal API token ID - if not provided, will fallback to MODAL_TOKEN_ID environment variable */\n tokenId?: string;\n /** Modal API token secret - if not provided, will fallback to MODAL_TOKEN_SECRET environment variable */\n tokenSecret?: string;\n /** Default runtime environment */\n runtime?: Runtime;\n /** Execution timeout in milliseconds */\n timeout?: number;\n /** Modal environment (sandbox or main) */\n environment?: string;\n}\n\n/**\n * Modal sandbox interface - wraps Modal's Sandbox class\n */\ninterface ModalSandbox {\n sandbox: any; // Modal Sandbox instance (using any due to alpha SDK)\n sandboxId: string;\n}\n\n/**\n * Create a Modal provider instance using the factory pattern\n */\nexport const modal = createProvider<ModalSandbox, ModalConfig>({\n name: 'modal',\n methods: {\n sandbox: {\n // Collection operations (map to compute.sandbox.*)\n create: async (config: ModalConfig, options?: CreateSandboxOptions) => {\n // Validate API credentials\n const tokenId = config.tokenId || (typeof process !== 'undefined' && process.env?.MODAL_TOKEN_ID) || '';\n const tokenSecret = config.tokenSecret || (typeof process !== 'undefined' && process.env?.MODAL_TOKEN_SECRET) || '';\n\n if (!tokenId || !tokenSecret) {\n throw new Error(\n `Missing Modal API credentials. Provide 'tokenId' and 'tokenSecret' in config or set MODAL_TOKEN_ID and MODAL_TOKEN_SECRET environment variables. Get your credentials from https://modal.com/`\n );\n }\n\n try {\n // Initialize Modal client with credentials\n initializeClient({ tokenId, tokenSecret });\n\n let sandbox: any;\n let sandboxId: string;\n\n if (options?.sandboxId) {\n // Reconnect to existing Modal sandbox\n sandbox = await Sandbox.fromId(options.sandboxId);\n sandboxId = options.sandboxId;\n } else {\n // Create new Modal sandbox using working pattern from debug\n const app = await App.lookup('computesdk-modal', { createIfMissing: true });\n const image = await app.imageFromRegistry('python:3.13-slim');\n sandbox = await app.createSandbox(image);\n sandboxId = sandbox.sandboxId;\n }\n\n const modalSandbox: ModalSandbox = {\n sandbox,\n sandboxId\n };\n\n return {\n sandbox: modalSandbox,\n sandboxId\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.message.includes('unauthorized') || error.message.includes('credentials')) {\n throw new Error(\n `Modal authentication failed. Please check your MODAL_TOKEN_ID and MODAL_TOKEN_SECRET environment variables. Get your credentials from https://modal.com/`\n );\n }\n if (error.message.includes('quota') || error.message.includes('limit')) {\n throw new Error(\n `Modal quota exceeded. Please check your usage at https://modal.com/`\n );\n }\n }\n throw new Error(\n `Failed to create Modal sandbox: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n getById: async (config: ModalConfig, sandboxId: string) => {\n const tokenId = config.tokenId || process.env.MODAL_TOKEN_ID!;\n const tokenSecret = config.tokenSecret || process.env.MODAL_TOKEN_SECRET!;\n\n try {\n initializeClient({ tokenId, tokenSecret });\n const sandbox = await Sandbox.fromId(sandboxId);\n\n const modalSandbox: ModalSandbox = {\n sandbox,\n sandboxId\n };\n\n return {\n sandbox: modalSandbox,\n sandboxId\n };\n } catch (error) {\n // Sandbox doesn't exist or can't be accessed\n return null;\n }\n },\n\n list: async (_config: ModalConfig) => {\n throw new Error(\n `Modal provider does not support listing sandboxes. Modal sandboxes are managed individually through the Modal console. Use getById to reconnect to specific sandboxes by ID.`\n );\n },\n\n destroy: async (_config: ModalConfig, sandboxId: string) => {\n try {\n const sandbox = await Sandbox.fromId(sandboxId);\n if (sandbox && typeof sandbox.terminate === 'function') {\n await sandbox.terminate();\n }\n } catch (error) {\n // Sandbox might already be terminated or doesn't exist\n // This is acceptable for destroy operations\n }\n },\n\n // Instance operations (map to individual Sandbox methods)\n runCode: async (modalSandbox: ModalSandbox, code: string, runtime?: Runtime): Promise<ExecutionResult> => {\n const startTime = Date.now();\n\n try {\n // Auto-detect runtime if not specified\n const effectiveRuntime = runtime || (\n // Strong Python indicators\n code.includes('print(') || \n code.includes('import ') ||\n code.includes('def ') ||\n code.includes('sys.') ||\n code.includes('json.') ||\n code.includes('__') ||\n code.includes('f\"') ||\n code.includes(\"f'\")\n ? 'python'\n // Default to Python for Modal (primarily Python-focused)\n : 'python'\n );\n \n // Modal primarily supports Python execution\n if (effectiveRuntime !== 'python') {\n throw new Error('Modal provider currently only supports Python runtime');\n }\n\n // Execute code using Modal's exec method\n // Use python3 with -c flag to execute code directly\n const process = await modalSandbox.sandbox.exec(['python3', '-c', code]);\n \n // Wait for process completion\n const exitCode = await process.wait();\n \n // Read stdout and stderr from the process streams\n let stdout = '';\n let stderr = '';\n \n // Handle Modal's stream objects\n if (process.stdout && typeof process.stdout.read === 'function') {\n try {\n const stdoutData = await process.stdout.read();\n stdout = typeof stdoutData === 'string' ? stdoutData : new TextDecoder().decode(stdoutData);\n } catch (e) {\n // If reading fails, stdout remains empty\n }\n }\n \n if (process.stderr && typeof process.stderr.read === 'function') {\n try {\n const stderrData = await process.stderr.read();\n stderr = typeof stderrData === 'string' ? stderrData : new TextDecoder().decode(stderrData);\n } catch (e) {\n // If reading fails, stderr remains empty\n }\n }\n\n // Check for syntax errors in stderr\n if (exitCode !== 0 && stderr && (\n stderr.includes('SyntaxError') || \n stderr.includes('invalid syntax')\n )) {\n throw new Error(`Syntax error: ${stderr.trim()}`);\n }\n\n return {\n stdout: stdout || '',\n stderr: stderr || '',\n exitCode: exitCode || 0,\n executionTime: Date.now() - startTime,\n sandboxId: modalSandbox.sandboxId,\n provider: 'modal'\n };\n } catch (error) {\n // Handle syntax errors and runtime errors\n if (error instanceof Error && error.message.includes('Syntax error')) {\n throw error; // Re-throw syntax errors\n }\n \n throw new Error(\n `Modal execution failed: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n runCommand: async (modalSandbox: ModalSandbox, command: string, args: string[] = []): Promise<ExecutionResult> => {\n const startTime = Date.now();\n\n try {\n // Execute command using Modal's exec method\n const commandArray = [command, ...args];\n const process = await modalSandbox.sandbox.exec(commandArray);\n \n // Wait for process completion\n const exitCode = await process.wait();\n \n // Read stdout and stderr from the process streams\n let stdout = '';\n let stderr = '';\n \n // Handle Modal's stream objects\n if (process.stdout && typeof process.stdout.read === 'function') {\n try {\n const stdoutData = await process.stdout.read();\n stdout = typeof stdoutData === 'string' ? stdoutData : new TextDecoder().decode(stdoutData);\n } catch (e) {\n // If reading fails, stdout remains empty\n }\n }\n \n if (process.stderr && typeof process.stderr.read === 'function') {\n try {\n const stderrData = await process.stderr.read();\n stderr = typeof stderrData === 'string' ? stderrData : new TextDecoder().decode(stderrData);\n } catch (e) {\n // If reading fails, stderr remains empty\n }\n }\n\n return {\n stdout: stdout || '',\n stderr: stderr || '',\n exitCode: exitCode || 0,\n executionTime: Date.now() - startTime,\n sandboxId: modalSandbox.sandboxId,\n provider: 'modal'\n };\n } catch (error) {\n // For command failures, return error info instead of throwing\n return {\n stdout: '',\n stderr: error instanceof Error ? error.message : String(error),\n exitCode: 127, // Command not found exit code\n executionTime: Date.now() - startTime,\n sandboxId: modalSandbox.sandboxId,\n provider: 'modal'\n };\n }\n },\n\n getInfo: async (modalSandbox: ModalSandbox): Promise<SandboxInfo> => {\n // Get actual sandbox status using Modal's poll method\n let status: 'running' | 'stopped' | 'error' = 'running';\n try {\n const pollResult = await modalSandbox.sandbox.poll();\n if (pollResult !== null) {\n // Sandbox has finished\n status = pollResult === 0 ? 'stopped' : 'error';\n }\n } catch (error) {\n // If polling fails, assume running\n status = 'running';\n }\n\n return {\n id: modalSandbox.sandboxId,\n provider: 'modal',\n runtime: 'python', // Modal default\n status,\n createdAt: new Date(),\n timeout: 300000,\n metadata: {\n modalSandboxId: modalSandbox.sandboxId,\n realModalImplementation: true\n }\n };\n },\n\n // Optional filesystem methods - Modal supports filesystem operations\n filesystem: {\n readFile: async (modalSandbox: ModalSandbox, path: string): Promise<string> => {\n try {\n // Use Modal's file open API to read files\n const file = await modalSandbox.sandbox.open(path);\n \n // Read the entire file content\n let content = '';\n if (file && typeof file.read === 'function') {\n const data = await file.read();\n content = typeof data === 'string' ? data : new TextDecoder().decode(data);\n }\n \n // Close the file if it has a close method\n if (file && typeof file.close === 'function') {\n await file.close();\n }\n \n return content;\n } catch (error) {\n // Fallback to using cat command if direct file API fails\n try {\n const process = await modalSandbox.sandbox.exec(['cat', path]);\n await process.wait();\n \n let content = '';\n if (process.stdout && typeof process.stdout.read === 'function') {\n const data = await process.stdout.read();\n content = typeof data === 'string' ? data : new TextDecoder().decode(data);\n }\n \n return content;\n } catch (fallbackError) {\n throw new Error(`Failed to read file ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n },\n\n writeFile: async (modalSandbox: ModalSandbox, path: string, content: string): Promise<void> => {\n try {\n // Use Modal's file open API to write files\n const file = await modalSandbox.sandbox.open(path);\n \n // Write content to the file\n if (file && typeof file.write === 'function') {\n await file.write(content);\n }\n \n // Close the file if it has a close method\n if (file && typeof file.close === 'function') {\n await file.close();\n }\n } catch (error) {\n // Fallback to using echo command if direct file API fails\n try {\n // Escape content for shell command\n const escapedContent = content.replace(/'/g, \"'\\\"'\\\"'\");\n const process = await modalSandbox.sandbox.exec(['sh', '-c', `echo '${escapedContent}' > \"${path}\"`]);\n await process.wait();\n } catch (fallbackError) {\n throw new Error(`Failed to write file ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n },\n\n mkdir: async (modalSandbox: ModalSandbox, path: string): Promise<void> => {\n try {\n const process = await modalSandbox.sandbox.exec(['mkdir', '-p', path]);\n const exitCode = await process.wait();\n \n if (exitCode !== 0) {\n let stderr = '';\n if (process.stderr && typeof process.stderr.read === 'function') {\n const data = await process.stderr.read();\n stderr = typeof data === 'string' ? data : new TextDecoder().decode(data);\n }\n throw new Error(`mkdir failed with exit code ${exitCode}: ${stderr}`);\n }\n } catch (error) {\n throw new Error(`Failed to create directory ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n\n readdir: async (modalSandbox: ModalSandbox, path: string): Promise<FileEntry[]> => {\n try {\n const process = await modalSandbox.sandbox.exec(['ls', '-la', '--time-style=iso', path]);\n const exitCode = await process.wait();\n \n if (exitCode !== 0) {\n let stderr = '';\n if (process.stderr && typeof process.stderr.read === 'function') {\n const data = await process.stderr.read();\n stderr = typeof data === 'string' ? data : new TextDecoder().decode(data);\n }\n throw new Error(`ls failed with exit code ${exitCode}: ${stderr}`);\n }\n \n let output = '';\n if (process.stdout && typeof process.stdout.read === 'function') {\n const data = await process.stdout.read();\n output = typeof data === 'string' ? data : new TextDecoder().decode(data);\n }\n \n const lines = output.split('\\n').slice(1); // Skip header line\n \n return lines.filter(line => line.trim()).map(line => {\n const parts = line.trim().split(/\\s+/);\n const permissions = parts[0] || '';\n const size = parseInt(parts[4]) || 0;\n const dateStr = (parts[5] || '') + ' ' + (parts[6] || '');\n const date = dateStr.trim() ? new Date(dateStr) : new Date();\n const name = parts.slice(8).join(' ') || 'unknown';\n \n return {\n name,\n path: `${path}/${name}`,\n isDirectory: permissions.startsWith('d'),\n size,\n lastModified: isNaN(date.getTime()) ? new Date() : date\n };\n });\n } catch (error) {\n throw new Error(`Failed to read directory ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n\n exists: async (modalSandbox: ModalSandbox, path: string): Promise<boolean> => {\n try {\n const process = await modalSandbox.sandbox.exec(['test', '-e', path]);\n const exitCode = await process.wait();\n return exitCode === 0;\n } catch (error) {\n return false;\n }\n },\n\n remove: async (modalSandbox: ModalSandbox, path: string): Promise<void> => {\n try {\n const process = await modalSandbox.sandbox.exec(['rm', '-rf', path]);\n const exitCode = await process.wait();\n \n if (exitCode !== 0) {\n let stderr = '';\n if (process.stderr && typeof process.stderr.read === 'function') {\n const data = await process.stderr.read();\n stderr = typeof data === 'string' ? data : new TextDecoder().decode(data);\n }\n throw new Error(`rm failed with exit code ${exitCode}: ${stderr}`);\n }\n } catch (error) {\n throw new Error(`Failed to remove ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n }\n }\n }\n});"],"mappings":"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","names":["process"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Modal Provider - Factory-based Implementation\n * \n * Full-featured provider with serverless sandbox execution using the factory pattern.\n * Leverages Modal's JavaScript SDK for real sandbox management.\n * \n * Note: Modal's JavaScript SDK is in alpha. This implementation provides a working\n * foundation but may need updates as the Modal API evolves.\n */\n\nimport { createProvider } from 'computesdk';\nimport type { \n ExecutionResult, \n SandboxInfo, \n Runtime,\n CreateSandboxOptions,\n FileEntry\n} from 'computesdk';\n\n// Import Modal SDK\nimport { App, Sandbox, initializeClient } from 'modal';\n\n/**\n * Modal-specific configuration options\n */\nexport interface ModalConfig {\n /** Modal API token ID - if not provided, will fallback to MODAL_TOKEN_ID environment variable */\n tokenId?: string;\n /** Modal API token secret - if not provided, will fallback to MODAL_TOKEN_SECRET environment variable */\n tokenSecret?: string;\n /** Default runtime environment */\n runtime?: Runtime;\n /** Execution timeout in milliseconds */\n timeout?: number;\n /** Modal environment (sandbox or main) */\n environment?: string;\n}\n\n/**\n * Modal sandbox interface - wraps Modal's Sandbox class\n */\ninterface ModalSandbox {\n sandbox: any; // Modal Sandbox instance (using any due to alpha SDK)\n sandboxId: string;\n}\n\n/**\n * Detect runtime from code content\n */\nfunction detectRuntime(code: string): Runtime {\n // Strong Node.js indicators\n if (code.includes('console.log') || \n code.includes('process.') ||\n code.includes('require(') ||\n code.includes('module.exports') ||\n code.includes('__dirname') ||\n code.includes('__filename') ||\n code.includes('throw new Error') || // JavaScript error throwing\n code.includes('new Error(')) {\n return 'node';\n }\n\n // Strong Python indicators \n if (code.includes('print(') || \n code.includes('import ') ||\n code.includes('def ') ||\n code.includes('sys.') ||\n code.includes('json.') ||\n code.includes('f\"') ||\n code.includes(\"f'\")) {\n return 'python';\n }\n\n // Default to Python for Modal\n return 'python';\n}\n\n/**\n * Create a Modal provider instance using the factory pattern\n */\nexport const modal = createProvider<ModalSandbox, ModalConfig>({\n name: 'modal',\n methods: {\n sandbox: {\n // Collection operations (map to compute.sandbox.*)\n create: async (config: ModalConfig, options?: CreateSandboxOptions) => {\n // Validate API credentials\n const tokenId = config.tokenId || (typeof process !== 'undefined' && process.env?.MODAL_TOKEN_ID) || '';\n const tokenSecret = config.tokenSecret || (typeof process !== 'undefined' && process.env?.MODAL_TOKEN_SECRET) || '';\n\n if (!tokenId || !tokenSecret) {\n throw new Error(\n `Missing Modal API credentials. Provide 'tokenId' and 'tokenSecret' in config or set MODAL_TOKEN_ID and MODAL_TOKEN_SECRET environment variables. Get your credentials from https://modal.com/`\n );\n }\n\n try {\n // Initialize Modal client with credentials\n initializeClient({ tokenId, tokenSecret });\n\n let sandbox: any;\n let sandboxId: string;\n\n if (options?.sandboxId) {\n // Reconnect to existing Modal sandbox\n sandbox = await Sandbox.fromId(options.sandboxId);\n sandboxId = options.sandboxId;\n } else {\n // Create new Modal sandbox using working pattern from debug\n const app = await App.lookup('computesdk-modal', { createIfMissing: true });\n const image = await app.imageFromRegistry('python:3.13-slim');\n sandbox = await app.createSandbox(image);\n sandboxId = sandbox.sandboxId;\n }\n\n const modalSandbox: ModalSandbox = {\n sandbox,\n sandboxId\n };\n\n return {\n sandbox: modalSandbox,\n sandboxId\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.message.includes('unauthorized') || error.message.includes('credentials')) {\n throw new Error(\n `Modal authentication failed. Please check your MODAL_TOKEN_ID and MODAL_TOKEN_SECRET environment variables. Get your credentials from https://modal.com/`\n );\n }\n if (error.message.includes('quota') || error.message.includes('limit')) {\n throw new Error(\n `Modal quota exceeded. Please check your usage at https://modal.com/`\n );\n }\n }\n throw new Error(\n `Failed to create Modal sandbox: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n getById: async (config: ModalConfig, sandboxId: string) => {\n const tokenId = config.tokenId || process.env.MODAL_TOKEN_ID!;\n const tokenSecret = config.tokenSecret || process.env.MODAL_TOKEN_SECRET!;\n\n try {\n initializeClient({ tokenId, tokenSecret });\n const sandbox = await Sandbox.fromId(sandboxId);\n\n const modalSandbox: ModalSandbox = {\n sandbox,\n sandboxId\n };\n\n return {\n sandbox: modalSandbox,\n sandboxId\n };\n } catch (error) {\n // Sandbox doesn't exist or can't be accessed\n return null;\n }\n },\n\n list: async (_config: ModalConfig) => {\n throw new Error(\n `Modal provider does not support listing sandboxes. Modal sandboxes are managed individually through the Modal console. Use getById to reconnect to specific sandboxes by ID.`\n );\n },\n\n destroy: async (_config: ModalConfig, sandboxId: string) => {\n try {\n const sandbox = await Sandbox.fromId(sandboxId);\n if (sandbox && typeof sandbox.terminate === 'function') {\n await sandbox.terminate();\n }\n } catch (error) {\n // Sandbox might already be terminated or doesn't exist\n // This is acceptable for destroy operations\n }\n },\n\n // Instance operations (map to individual Sandbox methods)\n runCode: async (modalSandbox: ModalSandbox, code: string, runtime?: Runtime): Promise<ExecutionResult> => {\n const startTime = Date.now();\n\n try {\n // Auto-detect runtime from code if not specified\n const detectedRuntime = runtime || detectRuntime(code);\n \n // Create appropriate sandbox and command for the runtime\n let executionSandbox = modalSandbox.sandbox;\n let command: string[];\n let shouldCleanupSandbox = false;\n \n if (detectedRuntime === 'node') {\n // For Node.js execution, create a Node.js sandbox dynamically\n const app = await App.lookup('computesdk-modal', { createIfMissing: true });\n const nodeImage = await app.imageFromRegistry('node:20-alpine');\n executionSandbox = await app.createSandbox(nodeImage);\n command = ['node', '-e', code];\n shouldCleanupSandbox = true; // Clean up temporary Node.js sandbox\n } else {\n // Use existing Python sandbox for Python code\n command = ['python3', '-c', code];\n }\n\n const process = await executionSandbox.exec(command, {\n stdout: 'pipe',\n stderr: 'pipe'\n });\n\n // Use working stream reading pattern from debug\n const [stdout, stderr] = await Promise.all([\n process.stdout.readText(),\n process.stderr.readText()\n ]);\n\n const exitCode = await process.wait();\n \n // Clean up temporary Node.js sandbox if created\n if (shouldCleanupSandbox && executionSandbox !== modalSandbox.sandbox) {\n try {\n await executionSandbox.terminate();\n } catch (e) {\n // Ignore cleanup errors\n }\n }\n\n // Check for syntax errors in stderr\n if (exitCode !== 0 && stderr && (\n stderr.includes('SyntaxError') || \n stderr.includes('invalid syntax')\n )) {\n throw new Error(`Syntax error: ${stderr.trim()}`);\n }\n\n return {\n stdout: stdout || '',\n stderr: stderr || '',\n exitCode: exitCode || 0,\n executionTime: Date.now() - startTime,\n sandboxId: modalSandbox.sandboxId,\n provider: 'modal'\n };\n } catch (error) {\n // Handle syntax errors and runtime errors\n if (error instanceof Error && error.message.includes('Syntax error')) {\n throw error; // Re-throw syntax errors\n }\n \n throw new Error(\n `Modal execution failed: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n runCommand: async (modalSandbox: ModalSandbox, command: string, args: string[] = []): Promise<ExecutionResult> => {\n const startTime = Date.now();\n\n try {\n // Execute command using Modal's exec method with working pattern\n const process = await modalSandbox.sandbox.exec([command, ...args], {\n stdout: 'pipe',\n stderr: 'pipe'\n });\n\n // Use working stream reading pattern from debug\n const [stdout, stderr] = await Promise.all([\n process.stdout.readText(),\n process.stderr.readText()\n ]);\n\n const exitCode = await process.wait();\n\n return {\n stdout: stdout || '',\n stderr: stderr || '',\n exitCode: exitCode || 0,\n executionTime: Date.now() - startTime,\n sandboxId: modalSandbox.sandboxId,\n provider: 'modal'\n };\n } catch (error) {\n // For command failures, return error info instead of throwing\n return {\n stdout: '',\n stderr: error instanceof Error ? error.message : String(error),\n exitCode: 127, // Command not found exit code\n executionTime: Date.now() - startTime,\n sandboxId: modalSandbox.sandboxId,\n provider: 'modal'\n };\n }\n },\n\n getInfo: async (modalSandbox: ModalSandbox): Promise<SandboxInfo> => {\n // Get actual sandbox status using Modal's poll method\n let status: 'running' | 'stopped' | 'error' = 'running';\n try {\n const pollResult = await modalSandbox.sandbox.poll();\n if (pollResult !== null) {\n // Sandbox has finished\n status = pollResult === 0 ? 'stopped' : 'error';\n }\n } catch (error) {\n // If polling fails, assume running\n status = 'running';\n }\n\n return {\n id: modalSandbox.sandboxId,\n provider: 'modal',\n runtime: 'python', // Modal default\n status,\n createdAt: new Date(),\n timeout: 300000,\n metadata: {\n modalSandboxId: modalSandbox.sandboxId,\n realModalImplementation: true\n }\n };\n },\n\n getUrl: async (modalSandbox: ModalSandbox, options: { port: number; protocol?: string }): Promise<string> => {\n try {\n // Use Modal's built-in tunnels method to get tunnel information\n const tunnels = await modalSandbox.sandbox.tunnels();\n const tunnel = tunnels[options.port];\n \n if (!tunnel) {\n throw new Error(`No tunnel found for port ${options.port}. Available ports: ${Object.keys(tunnels).join(', ')}`);\n }\n \n let url = tunnel.url;\n \n // If a specific protocol is requested, replace the URL's protocol\n if (options.protocol) {\n const urlObj = new URL(url);\n urlObj.protocol = options.protocol + ':';\n url = urlObj.toString();\n }\n \n return url;\n } catch (error) {\n throw new Error(\n `Failed to get Modal tunnel URL for port ${options.port}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n // Optional filesystem methods - Modal supports filesystem operations\n filesystem: {\n readFile: async (modalSandbox: ModalSandbox, path: string): Promise<string> => {\n try {\n // Use Modal's file open API to read files\n const file = await modalSandbox.sandbox.open(path);\n \n // Read the entire file content\n let content = '';\n if (file && typeof file.read === 'function') {\n const data = await file.read();\n content = typeof data === 'string' ? data : new TextDecoder().decode(data);\n }\n \n // Close the file if it has a close method\n if (file && typeof file.close === 'function') {\n await file.close();\n }\n \n return content;\n } catch (error) {\n // Fallback to using cat command with working stream pattern\n try {\n const process = await modalSandbox.sandbox.exec(['cat', path], {\n stdout: 'pipe',\n stderr: 'pipe'\n });\n\n const [content, stderr] = await Promise.all([\n process.stdout.readText(),\n process.stderr.readText()\n ]);\n\n const exitCode = await process.wait();\n\n if (exitCode !== 0) {\n throw new Error(`cat failed: ${stderr}`);\n }\n\n return content.trim(); // Remove extra newlines\n } catch (fallbackError) {\n throw new Error(`Failed to read file ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n },\n\n writeFile: async (modalSandbox: ModalSandbox, path: string, content: string): Promise<void> => {\n try {\n // Use Modal's file open API to write files\n const file = await modalSandbox.sandbox.open(path);\n \n // Write content to the file\n if (file && typeof file.write === 'function') {\n await file.write(content);\n }\n \n // Close the file if it has a close method\n if (file && typeof file.close === 'function') {\n await file.close();\n }\n } catch (error) {\n // Fallback to using shell command with proper escaping\n try {\n const process = await modalSandbox.sandbox.exec(['sh', '-c', `printf '%s' \"${content.replace(/\"/g, '\\\\\"')}\" > \"${path}\"`], {\n stdout: 'pipe',\n stderr: 'pipe'\n });\n\n const [, stderr] = await Promise.all([\n process.stdout.readText(),\n process.stderr.readText()\n ]);\n\n const exitCode = await process.wait();\n\n if (exitCode !== 0) {\n throw new Error(`write failed: ${stderr}`);\n }\n } catch (fallbackError) {\n throw new Error(`Failed to write file ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n },\n\n mkdir: async (modalSandbox: ModalSandbox, path: string): Promise<void> => {\n try {\n const process = await modalSandbox.sandbox.exec(['mkdir', '-p', path], {\n stdout: 'pipe',\n stderr: 'pipe'\n });\n\n const [, stderr] = await Promise.all([\n process.stdout.readText(),\n process.stderr.readText()\n ]);\n\n const exitCode = await process.wait();\n\n if (exitCode !== 0) {\n throw new Error(`mkdir failed: ${stderr}`);\n }\n } catch (error) {\n throw new Error(`Failed to create directory ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n\n readdir: async (modalSandbox: ModalSandbox, path: string): Promise<FileEntry[]> => {\n try {\n const process = await modalSandbox.sandbox.exec(['ls', '-la', '--time-style=iso', path], {\n stdout: 'pipe',\n stderr: 'pipe'\n });\n\n const [output, stderr] = await Promise.all([\n process.stdout.readText(),\n process.stderr.readText()\n ]);\n\n const exitCode = await process.wait();\n\n if (exitCode !== 0) {\n throw new Error(`ls failed: ${stderr}`);\n }\n\n const lines = output.split('\\n').slice(1); // Skip header\n\n return lines\n .filter((line: string) => line.trim())\n .map((line: string) => {\n const parts = line.trim().split(/\\s+/);\n const permissions = parts[0] || '';\n const size = parseInt(parts[4]) || 0;\n const dateStr = (parts[5] || '') + ' ' + (parts[6] || '');\n const date = dateStr.trim() ? new Date(dateStr) : new Date();\n const name = parts.slice(8).join(' ') || parts[parts.length - 1] || 'unknown';\n\n return {\n name,\n path: `${path}/${name}`.replace('//', '/'),\n isDirectory: permissions.startsWith('d'),\n size,\n lastModified: isNaN(date.getTime()) ? new Date() : date\n };\n });\n } catch (error) {\n throw new Error(`Failed to read directory ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n\n exists: async (modalSandbox: ModalSandbox, path: string): Promise<boolean> => {\n try {\n const process = await modalSandbox.sandbox.exec(['test', '-e', path]);\n const exitCode = await process.wait();\n return exitCode === 0;\n } catch (error) {\n return false;\n }\n },\n\n remove: async (modalSandbox: ModalSandbox, path: string): Promise<void> => {\n try {\n const process = await modalSandbox.sandbox.exec(['rm', '-rf', path], {\n stdout: 'pipe',\n stderr: 'pipe'\n });\n\n const [, stderr] = await Promise.all([\n process.stdout.readText(),\n process.stderr.readText()\n ]);\n\n const exitCode = await process.wait();\n\n if (exitCode !== 0) {\n throw new Error(`rm failed: ${stderr}`);\n }\n } catch (error) {\n throw new Error(`Failed to remove ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n },\n\n // Provider-specific typed getInstance method\n getInstance: (sandbox: ModalSandbox): ModalSandbox => {\n return sandbox;\n },\n\n }\n }\n});"],"mappings":";AAUA,SAAS,sBAAsB;AAU/B,SAAS,KAAK,SAAS,wBAAwB;AA6B/C,SAAS,cAAc,MAAuB;AAE5C,MAAI,KAAK,SAAS,aAAa,KAC3B,KAAK,SAAS,UAAU,KACxB,KAAK,SAAS,UAAU,KACxB,KAAK,SAAS,gBAAgB,KAC9B,KAAK,SAAS,WAAW,KACzB,KAAK,SAAS,YAAY,KAC1B,KAAK,SAAS,iBAAiB;AAAA,EAC/B,KAAK,SAAS,YAAY,GAAG;AAC/B,WAAO;AAAA,EACT;AAGA,MAAI,KAAK,SAAS,QAAQ,KACtB,KAAK,SAAS,SAAS,KACvB,KAAK,SAAS,MAAM,KACpB,KAAK,SAAS,MAAM,KACpB,KAAK,SAAS,OAAO,KACrB,KAAK,SAAS,IAAI,KAClB,KAAK,SAAS,IAAI,GAAG;AACvB,WAAO;AAAA,EACT;AAGA,SAAO;AACT;AAKO,IAAM,QAAQ,eAA0C;AAAA,EAC7D,MAAM;AAAA,EACN,SAAS;AAAA,IACP,SAAS;AAAA;AAAA,MAEP,QAAQ,OAAO,QAAqB,YAAmC;AAErE,cAAM,UAAU,OAAO,WAAY,OAAO,YAAY,eAAe,QAAQ,KAAK,kBAAmB;AACrG,cAAM,cAAc,OAAO,eAAgB,OAAO,YAAY,eAAe,QAAQ,KAAK,sBAAuB;AAEjH,YAAI,CAAC,WAAW,CAAC,aAAa;AAC5B,gBAAM,IAAI;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAEA,YAAI;AAEF,2BAAiB,EAAE,SAAS,YAAY,CAAC;AAEzC,cAAI;AACJ,cAAI;AAEJ,cAAI,SAAS,WAAW;AAEtB,sBAAU,MAAM,QAAQ,OAAO,QAAQ,SAAS;AAChD,wBAAY,QAAQ;AAAA,UACtB,OAAO;AAEL,kBAAM,MAAM,MAAM,IAAI,OAAO,oBAAoB,EAAE,iBAAiB,KAAK,CAAC;AAC1E,kBAAM,QAAQ,MAAM,IAAI,kBAAkB,kBAAkB;AAC5D,sBAAU,MAAM,IAAI,cAAc,KAAK;AACvC,wBAAY,QAAQ;AAAA,UACtB;AAEA,gBAAM,eAA6B;AAAA,YACjC;AAAA,YACA;AAAA,UACF;AAEA,iBAAO;AAAA,YACL,SAAS;AAAA,YACT;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,cAAI,iBAAiB,OAAO;AAC1B,gBAAI,MAAM,QAAQ,SAAS,cAAc,KAAK,MAAM,QAAQ,SAAS,aAAa,GAAG;AACnF,oBAAM,IAAI;AAAA,gBACR;AAAA,cACF;AAAA,YACF;AACA,gBAAI,MAAM,QAAQ,SAAS,OAAO,KAAK,MAAM,QAAQ,SAAS,OAAO,GAAG;AACtE,oBAAM,IAAI;AAAA,gBACR;AAAA,cACF;AAAA,YACF;AAAA,UACF;AACA,gBAAM,IAAI;AAAA,YACR,mCAAmC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,UAC3F;AAAA,QACF;AAAA,MACF;AAAA,MAEA,SAAS,OAAO,QAAqB,cAAsB;AACzD,cAAM,UAAU,OAAO,WAAW,QAAQ,IAAI;AAC9C,cAAM,cAAc,OAAO,eAAe,QAAQ,IAAI;AAEtD,YAAI;AACF,2BAAiB,EAAE,SAAS,YAAY,CAAC;AACzC,gBAAM,UAAU,MAAM,QAAQ,OAAO,SAAS;AAE9C,gBAAM,eAA6B;AAAA,YACjC;AAAA,YACA;AAAA,UACF;AAEA,iBAAO;AAAA,YACL,SAAS;AAAA,YACT;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AAEd,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,MAEA,MAAM,OAAO,YAAyB;AACpC,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,MAEA,SAAS,OAAO,SAAsB,cAAsB;AAC1D,YAAI;AACF,gBAAM,UAAU,MAAM,QAAQ,OAAO,SAAS;AAC9C,cAAI,WAAW,OAAO,QAAQ,cAAc,YAAY;AACtD,kBAAM,QAAQ,UAAU;AAAA,UAC1B;AAAA,QACF,SAAS,OAAO;AAAA,QAGhB;AAAA,MACF;AAAA;AAAA,MAGA,SAAS,OAAO,cAA4B,MAAc,YAAgD;AACxG,cAAM,YAAY,KAAK,IAAI;AAE3B,YAAI;AAEF,gBAAM,kBAAkB,WAAW,cAAc,IAAI;AAGrD,cAAI,mBAAmB,aAAa;AACpC,cAAI;AACJ,cAAI,uBAAuB;AAE3B,cAAI,oBAAoB,QAAQ;AAE9B,kBAAM,MAAM,MAAM,IAAI,OAAO,oBAAoB,EAAE,iBAAiB,KAAK,CAAC;AAC1E,kBAAM,YAAY,MAAM,IAAI,kBAAkB,gBAAgB;AAC9D,+BAAmB,MAAM,IAAI,cAAc,SAAS;AACpD,sBAAU,CAAC,QAAQ,MAAM,IAAI;AAC7B,mCAAuB;AAAA,UACzB,OAAO;AAEL,sBAAU,CAAC,WAAW,MAAM,IAAI;AAAA,UAClC;AAEA,gBAAMA,WAAU,MAAM,iBAAiB,KAAK,SAAS;AAAA,YACnD,QAAQ;AAAA,YACR,QAAQ;AAAA,UACV,CAAC;AAGD,gBAAM,CAAC,QAAQ,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,YACzCA,SAAQ,OAAO,SAAS;AAAA,YACxBA,SAAQ,OAAO,SAAS;AAAA,UAC1B,CAAC;AAED,gBAAM,WAAW,MAAMA,SAAQ,KAAK;AAGpC,cAAI,wBAAwB,qBAAqB,aAAa,SAAS;AACrE,gBAAI;AACF,oBAAM,iBAAiB,UAAU;AAAA,YACnC,SAAS,GAAG;AAAA,YAEZ;AAAA,UACF;AAGA,cAAI,aAAa,KAAK,WACpB,OAAO,SAAS,aAAa,KAC7B,OAAO,SAAS,gBAAgB,IAC/B;AACD,kBAAM,IAAI,MAAM,iBAAiB,OAAO,KAAK,CAAC,EAAE;AAAA,UAClD;AAEA,iBAAO;AAAA,YACL,QAAQ,UAAU;AAAA,YAClB,QAAQ,UAAU;AAAA,YAClB,UAAU,YAAY;AAAA,YACtB,eAAe,KAAK,IAAI,IAAI;AAAA,YAC5B,WAAW,aAAa;AAAA,YACxB,UAAU;AAAA,UACZ;AAAA,QACF,SAAS,OAAO;AAEd,cAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,cAAc,GAAG;AACpE,kBAAM;AAAA,UACR;AAEA,gBAAM,IAAI;AAAA,YACR,2BAA2B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,UACnF;AAAA,QACF;AAAA,MACF;AAAA,MAEA,YAAY,OAAO,cAA4B,SAAiB,OAAiB,CAAC,MAAgC;AAChH,cAAM,YAAY,KAAK,IAAI;AAE3B,YAAI;AAEF,gBAAMA,WAAU,MAAM,aAAa,QAAQ,KAAK,CAAC,SAAS,GAAG,IAAI,GAAG;AAAA,YAClE,QAAQ;AAAA,YACR,QAAQ;AAAA,UACV,CAAC;AAGD,gBAAM,CAAC,QAAQ,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,YACzCA,SAAQ,OAAO,SAAS;AAAA,YACxBA,SAAQ,OAAO,SAAS;AAAA,UAC1B,CAAC;AAED,gBAAM,WAAW,MAAMA,SAAQ,KAAK;AAEpC,iBAAO;AAAA,YACL,QAAQ,UAAU;AAAA,YAClB,QAAQ,UAAU;AAAA,YAClB,UAAU,YAAY;AAAA,YACtB,eAAe,KAAK,IAAI,IAAI;AAAA,YAC5B,WAAW,aAAa;AAAA,YACxB,UAAU;AAAA,UACZ;AAAA,QACF,SAAS,OAAO;AAEd,iBAAO;AAAA,YACL,QAAQ;AAAA,YACR,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,YAC7D,UAAU;AAAA;AAAA,YACV,eAAe,KAAK,IAAI,IAAI;AAAA,YAC5B,WAAW,aAAa;AAAA,YACxB,UAAU;AAAA,UACZ;AAAA,QACF;AAAA,MACF;AAAA,MAEA,SAAS,OAAO,iBAAqD;AAEnE,YAAI,SAA0C;AAC9C,YAAI;AACF,gBAAM,aAAa,MAAM,aAAa,QAAQ,KAAK;AACnD,cAAI,eAAe,MAAM;AAEvB,qBAAS,eAAe,IAAI,YAAY;AAAA,UAC1C;AAAA,QACF,SAAS,OAAO;AAEd,mBAAS;AAAA,QACX;AAEA,eAAO;AAAA,UACL,IAAI,aAAa;AAAA,UACjB,UAAU;AAAA,UACV,SAAS;AAAA;AAAA,UACT;AAAA,UACA,WAAW,oBAAI,KAAK;AAAA,UACpB,SAAS;AAAA,UACT,UAAU;AAAA,YACR,gBAAgB,aAAa;AAAA,YAC7B,yBAAyB;AAAA,UAC3B;AAAA,QACF;AAAA,MACF;AAAA,MAEA,QAAQ,OAAO,cAA4B,YAAkE;AAC3G,YAAI;AAEF,gBAAM,UAAU,MAAM,aAAa,QAAQ,QAAQ;AACnD,gBAAM,SAAS,QAAQ,QAAQ,IAAI;AAEnC,cAAI,CAAC,QAAQ;AACX,kBAAM,IAAI,MAAM,4BAA4B,QAAQ,IAAI,sBAAsB,OAAO,KAAK,OAAO,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,UACjH;AAEA,cAAI,MAAM,OAAO;AAGjB,cAAI,QAAQ,UAAU;AACpB,kBAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,mBAAO,WAAW,QAAQ,WAAW;AACrC,kBAAM,OAAO,SAAS;AAAA,UACxB;AAEA,iBAAO;AAAA,QACT,SAAS,OAAO;AACd,gBAAM,IAAI;AAAA,YACR,2CAA2C,QAAQ,IAAI,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,UACpH;AAAA,QACF;AAAA,MACF;AAAA;AAAA,MAGA,YAAY;AAAA,QACV,UAAU,OAAO,cAA4B,SAAkC;AAC7E,cAAI;AAEF,kBAAM,OAAO,MAAM,aAAa,QAAQ,KAAK,IAAI;AAGjD,gBAAI,UAAU;AACd,gBAAI,QAAQ,OAAO,KAAK,SAAS,YAAY;AAC3C,oBAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,wBAAU,OAAO,SAAS,WAAW,OAAO,IAAI,YAAY,EAAE,OAAO,IAAI;AAAA,YAC3E;AAGA,gBAAI,QAAQ,OAAO,KAAK,UAAU,YAAY;AAC5C,oBAAM,KAAK,MAAM;AAAA,YACnB;AAEA,mBAAO;AAAA,UACT,SAAS,OAAO;AAEd,gBAAI;AACF,oBAAMA,WAAU,MAAM,aAAa,QAAQ,KAAK,CAAC,OAAO,IAAI,GAAG;AAAA,gBAC7D,QAAQ;AAAA,gBACR,QAAQ;AAAA,cACV,CAAC;AAED,oBAAM,CAAC,SAAS,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,gBAC1CA,SAAQ,OAAO,SAAS;AAAA,gBACxBA,SAAQ,OAAO,SAAS;AAAA,cAC1B,CAAC;AAED,oBAAM,WAAW,MAAMA,SAAQ,KAAK;AAEpC,kBAAI,aAAa,GAAG;AAClB,sBAAM,IAAI,MAAM,eAAe,MAAM,EAAE;AAAA,cACzC;AAEA,qBAAO,QAAQ,KAAK;AAAA,YACtB,SAAS,eAAe;AACtB,oBAAM,IAAI,MAAM,uBAAuB,IAAI,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,YAC1G;AAAA,UACF;AAAA,QACF;AAAA,QAEA,WAAW,OAAO,cAA4B,MAAc,YAAmC;AAC7F,cAAI;AAEF,kBAAM,OAAO,MAAM,aAAa,QAAQ,KAAK,IAAI;AAGjD,gBAAI,QAAQ,OAAO,KAAK,UAAU,YAAY;AAC5C,oBAAM,KAAK,MAAM,OAAO;AAAA,YAC1B;AAGA,gBAAI,QAAQ,OAAO,KAAK,UAAU,YAAY;AAC5C,oBAAM,KAAK,MAAM;AAAA,YACnB;AAAA,UACF,SAAS,OAAO;AAEd,gBAAI;AACF,oBAAMA,WAAU,MAAM,aAAa,QAAQ,KAAK,CAAC,MAAM,MAAM,gBAAgB,QAAQ,QAAQ,MAAM,KAAK,CAAC,QAAQ,IAAI,GAAG,GAAG;AAAA,gBACzH,QAAQ;AAAA,gBACR,QAAQ;AAAA,cACV,CAAC;AAED,oBAAM,CAAC,EAAE,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,gBACnCA,SAAQ,OAAO,SAAS;AAAA,gBACxBA,SAAQ,OAAO,SAAS;AAAA,cAC1B,CAAC;AAED,oBAAM,WAAW,MAAMA,SAAQ,KAAK;AAEpC,kBAAI,aAAa,GAAG;AAClB,sBAAM,IAAI,MAAM,iBAAiB,MAAM,EAAE;AAAA,cAC3C;AAAA,YACF,SAAS,eAAe;AACtB,oBAAM,IAAI,MAAM,wBAAwB,IAAI,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,YAC3G;AAAA,UACF;AAAA,QACF;AAAA,QAEA,OAAO,OAAO,cAA4B,SAAgC;AACxE,cAAI;AACF,kBAAMA,WAAU,MAAM,aAAa,QAAQ,KAAK,CAAC,SAAS,MAAM,IAAI,GAAG;AAAA,cACrE,QAAQ;AAAA,cACR,QAAQ;AAAA,YACV,CAAC;AAED,kBAAM,CAAC,EAAE,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,cACnCA,SAAQ,OAAO,SAAS;AAAA,cACxBA,SAAQ,OAAO,SAAS;AAAA,YAC1B,CAAC;AAED,kBAAM,WAAW,MAAMA,SAAQ,KAAK;AAEpC,gBAAI,aAAa,GAAG;AAClB,oBAAM,IAAI,MAAM,iBAAiB,MAAM,EAAE;AAAA,YAC3C;AAAA,UACF,SAAS,OAAO;AACd,kBAAM,IAAI,MAAM,8BAA8B,IAAI,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,UACjH;AAAA,QACF;AAAA,QAEA,SAAS,OAAO,cAA4B,SAAuC;AACjF,cAAI;AACF,kBAAMA,WAAU,MAAM,aAAa,QAAQ,KAAK,CAAC,MAAM,OAAO,oBAAoB,IAAI,GAAG;AAAA,cACvF,QAAQ;AAAA,cACR,QAAQ;AAAA,YACV,CAAC;AAED,kBAAM,CAAC,QAAQ,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,cACzCA,SAAQ,OAAO,SAAS;AAAA,cACxBA,SAAQ,OAAO,SAAS;AAAA,YAC1B,CAAC;AAED,kBAAM,WAAW,MAAMA,SAAQ,KAAK;AAEpC,gBAAI,aAAa,GAAG;AAClB,oBAAM,IAAI,MAAM,cAAc,MAAM,EAAE;AAAA,YACxC;AAEA,kBAAM,QAAQ,OAAO,MAAM,IAAI,EAAE,MAAM,CAAC;AAExC,mBAAO,MACJ,OAAO,CAAC,SAAiB,KAAK,KAAK,CAAC,EACpC,IAAI,CAAC,SAAiB;AACrB,oBAAM,QAAQ,KAAK,KAAK,EAAE,MAAM,KAAK;AACrC,oBAAM,cAAc,MAAM,CAAC,KAAK;AAChC,oBAAM,OAAO,SAAS,MAAM,CAAC,CAAC,KAAK;AACnC,oBAAM,WAAW,MAAM,CAAC,KAAK,MAAM,OAAO,MAAM,CAAC,KAAK;AACtD,oBAAM,OAAO,QAAQ,KAAK,IAAI,IAAI,KAAK,OAAO,IAAI,oBAAI,KAAK;AAC3D,oBAAM,OAAO,MAAM,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,MAAM,MAAM,SAAS,CAAC,KAAK;AAEpE,qBAAO;AAAA,gBACL;AAAA,gBACA,MAAM,GAAG,IAAI,IAAI,IAAI,GAAG,QAAQ,MAAM,GAAG;AAAA,gBACzC,aAAa,YAAY,WAAW,GAAG;AAAA,gBACvC;AAAA,gBACA,cAAc,MAAM,KAAK,QAAQ,CAAC,IAAI,oBAAI,KAAK,IAAI;AAAA,cACrD;AAAA,YACF,CAAC;AAAA,UACL,SAAS,OAAO;AACd,kBAAM,IAAI,MAAM,4BAA4B,IAAI,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,UAC/G;AAAA,QACF;AAAA,QAEA,QAAQ,OAAO,cAA4B,SAAmC;AAC5E,cAAI;AACF,kBAAMA,WAAU,MAAM,aAAa,QAAQ,KAAK,CAAC,QAAQ,MAAM,IAAI,CAAC;AACpE,kBAAM,WAAW,MAAMA,SAAQ,KAAK;AACpC,mBAAO,aAAa;AAAA,UACtB,SAAS,OAAO;AACd,mBAAO;AAAA,UACT;AAAA,QACF;AAAA,QAEA,QAAQ,OAAO,cAA4B,SAAgC;AACzE,cAAI;AACF,kBAAMA,WAAU,MAAM,aAAa,QAAQ,KAAK,CAAC,MAAM,OAAO,IAAI,GAAG;AAAA,cACnE,QAAQ;AAAA,cACR,QAAQ;AAAA,YACV,CAAC;AAED,kBAAM,CAAC,EAAE,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,cACnCA,SAAQ,OAAO,SAAS;AAAA,cACxBA,SAAQ,OAAO,SAAS;AAAA,YAC1B,CAAC;AAED,kBAAM,WAAW,MAAMA,SAAQ,KAAK;AAEpC,gBAAI,aAAa,GAAG;AAClB,oBAAM,IAAI,MAAM,cAAc,MAAM,EAAE;AAAA,YACxC;AAAA,UACF,SAAS,OAAO;AACd,kBAAM,IAAI,MAAM,oBAAoB,IAAI,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,UACvG;AAAA,QACF;AAAA,MACF;AAAA;AAAA,MAGA,aAAa,CAAC,YAAwC;AACpD,eAAO;AAAA,MACT;AAAA,IAEF;AAAA,EACF;AACF,CAAC;","names":["process"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@computesdk/modal",
3
- "version": "1.0.1",
3
+ "version": "1.2.0",
4
4
  "description": "Modal provider for ComputeSDK",
5
5
  "author": "Garrison",
6
6
  "license": "MIT",
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "dependencies": {
21
21
  "modal": "^0.3.16",
22
- "computesdk": "1.1.1"
22
+ "computesdk": "1.3.0"
23
23
  },
24
24
  "keywords": [
25
25
  "modal",
@@ -47,7 +47,7 @@
47
47
  "tsup": "^8.0.0",
48
48
  "typescript": "^5.0.0",
49
49
  "vitest": "^1.0.0",
50
- "@computesdk/test-utils": "1.1.0"
50
+ "@computesdk/test-utils": "1.3.0"
51
51
  },
52
52
  "scripts": {
53
53
  "build": "tsup",