@computesdk/modal 1.0.1 → 1.1.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 +93 -73
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +93 -73
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
|
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
|
-
|
|
124
|
-
let stderr = "";
|
|
125
|
-
if (process2.stdout && typeof process2.stdout.read === "function") {
|
|
126
|
-
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") {
|
|
148
|
+
if (shouldCleanupSandbox && executionSandbox !== modalSandbox.sandbox) {
|
|
133
149
|
try {
|
|
134
|
-
|
|
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
|
|
163
|
-
|
|
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 || "",
|
|
@@ -238,14 +243,19 @@ var modal = (0, import_computesdk.createProvider)({
|
|
|
238
243
|
return content;
|
|
239
244
|
} catch (error) {
|
|
240
245
|
try {
|
|
241
|
-
const process2 = await modalSandbox.sandbox.exec(["cat", path]
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
246
|
+
const process2 = await modalSandbox.sandbox.exec(["cat", path], {
|
|
247
|
+
stdout: "pipe",
|
|
248
|
+
stderr: "pipe"
|
|
249
|
+
});
|
|
250
|
+
const [content, stderr] = await Promise.all([
|
|
251
|
+
process2.stdout.readText(),
|
|
252
|
+
process2.stderr.readText()
|
|
253
|
+
]);
|
|
254
|
+
const exitCode = await process2.wait();
|
|
255
|
+
if (exitCode !== 0) {
|
|
256
|
+
throw new Error(`cat failed: ${stderr}`);
|
|
247
257
|
}
|
|
248
|
-
return content;
|
|
258
|
+
return content.trim();
|
|
249
259
|
} catch (fallbackError) {
|
|
250
260
|
throw new Error(`Failed to read file ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
251
261
|
}
|
|
@@ -262,9 +272,18 @@ var modal = (0, import_computesdk.createProvider)({
|
|
|
262
272
|
}
|
|
263
273
|
} catch (error) {
|
|
264
274
|
try {
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
275
|
+
const process2 = await modalSandbox.sandbox.exec(["sh", "-c", `printf '%s' "${content.replace(/"/g, '\\"')}" > "${path}"`], {
|
|
276
|
+
stdout: "pipe",
|
|
277
|
+
stderr: "pipe"
|
|
278
|
+
});
|
|
279
|
+
const [, stderr] = await Promise.all([
|
|
280
|
+
process2.stdout.readText(),
|
|
281
|
+
process2.stderr.readText()
|
|
282
|
+
]);
|
|
283
|
+
const exitCode = await process2.wait();
|
|
284
|
+
if (exitCode !== 0) {
|
|
285
|
+
throw new Error(`write failed: ${stderr}`);
|
|
286
|
+
}
|
|
268
287
|
} catch (fallbackError) {
|
|
269
288
|
throw new Error(`Failed to write file ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
270
289
|
}
|
|
@@ -272,15 +291,17 @@ var modal = (0, import_computesdk.createProvider)({
|
|
|
272
291
|
},
|
|
273
292
|
mkdir: async (modalSandbox, path) => {
|
|
274
293
|
try {
|
|
275
|
-
const process2 = await modalSandbox.sandbox.exec(["mkdir", "-p", path]
|
|
294
|
+
const process2 = await modalSandbox.sandbox.exec(["mkdir", "-p", path], {
|
|
295
|
+
stdout: "pipe",
|
|
296
|
+
stderr: "pipe"
|
|
297
|
+
});
|
|
298
|
+
const [, stderr] = await Promise.all([
|
|
299
|
+
process2.stdout.readText(),
|
|
300
|
+
process2.stderr.readText()
|
|
301
|
+
]);
|
|
276
302
|
const exitCode = await process2.wait();
|
|
277
303
|
if (exitCode !== 0) {
|
|
278
|
-
|
|
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}`);
|
|
304
|
+
throw new Error(`mkdir failed: ${stderr}`);
|
|
284
305
|
}
|
|
285
306
|
} catch (error) {
|
|
286
307
|
throw new Error(`Failed to create directory ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -288,20 +309,17 @@ var modal = (0, import_computesdk.createProvider)({
|
|
|
288
309
|
},
|
|
289
310
|
readdir: async (modalSandbox, path) => {
|
|
290
311
|
try {
|
|
291
|
-
const process2 = await modalSandbox.sandbox.exec(["ls", "-la", "--time-style=iso", path]
|
|
312
|
+
const process2 = await modalSandbox.sandbox.exec(["ls", "-la", "--time-style=iso", path], {
|
|
313
|
+
stdout: "pipe",
|
|
314
|
+
stderr: "pipe"
|
|
315
|
+
});
|
|
316
|
+
const [output, stderr] = await Promise.all([
|
|
317
|
+
process2.stdout.readText(),
|
|
318
|
+
process2.stderr.readText()
|
|
319
|
+
]);
|
|
292
320
|
const exitCode = await process2.wait();
|
|
293
321
|
if (exitCode !== 0) {
|
|
294
|
-
|
|
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);
|
|
322
|
+
throw new Error(`ls failed: ${stderr}`);
|
|
305
323
|
}
|
|
306
324
|
const lines = output.split("\n").slice(1);
|
|
307
325
|
return lines.filter((line) => line.trim()).map((line) => {
|
|
@@ -310,10 +328,10 @@ var modal = (0, import_computesdk.createProvider)({
|
|
|
310
328
|
const size = parseInt(parts[4]) || 0;
|
|
311
329
|
const dateStr = (parts[5] || "") + " " + (parts[6] || "");
|
|
312
330
|
const date = dateStr.trim() ? new Date(dateStr) : /* @__PURE__ */ new Date();
|
|
313
|
-
const name = parts.slice(8).join(" ") || "unknown";
|
|
331
|
+
const name = parts.slice(8).join(" ") || parts[parts.length - 1] || "unknown";
|
|
314
332
|
return {
|
|
315
333
|
name,
|
|
316
|
-
path: `${path}/${name}
|
|
334
|
+
path: `${path}/${name}`.replace("//", "/"),
|
|
317
335
|
isDirectory: permissions.startsWith("d"),
|
|
318
336
|
size,
|
|
319
337
|
lastModified: isNaN(date.getTime()) ? /* @__PURE__ */ new Date() : date
|
|
@@ -334,15 +352,17 @@ var modal = (0, import_computesdk.createProvider)({
|
|
|
334
352
|
},
|
|
335
353
|
remove: async (modalSandbox, path) => {
|
|
336
354
|
try {
|
|
337
|
-
const process2 = await modalSandbox.sandbox.exec(["rm", "-rf", path]
|
|
355
|
+
const process2 = await modalSandbox.sandbox.exec(["rm", "-rf", path], {
|
|
356
|
+
stdout: "pipe",
|
|
357
|
+
stderr: "pipe"
|
|
358
|
+
});
|
|
359
|
+
const [, stderr] = await Promise.all([
|
|
360
|
+
process2.stdout.readText(),
|
|
361
|
+
process2.stderr.readText()
|
|
362
|
+
]);
|
|
338
363
|
const exitCode = await process2.wait();
|
|
339
364
|
if (exitCode !== 0) {
|
|
340
|
-
|
|
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}`);
|
|
365
|
+
throw new Error(`rm failed: ${stderr}`);
|
|
346
366
|
}
|
|
347
367
|
} catch (error) {
|
|
348
368
|
throw new Error(`Failed to remove ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
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 // 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 }\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
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
|
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
|
-
|
|
100
|
-
let stderr = "";
|
|
101
|
-
if (process2.stdout && typeof process2.stdout.read === "function") {
|
|
102
|
-
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") {
|
|
124
|
+
if (shouldCleanupSandbox && executionSandbox !== modalSandbox.sandbox) {
|
|
109
125
|
try {
|
|
110
|
-
|
|
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
|
|
139
|
-
|
|
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 || "",
|
|
@@ -214,14 +219,19 @@ var modal = createProvider({
|
|
|
214
219
|
return content;
|
|
215
220
|
} catch (error) {
|
|
216
221
|
try {
|
|
217
|
-
const process2 = await modalSandbox.sandbox.exec(["cat", path]
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
222
|
+
const process2 = await modalSandbox.sandbox.exec(["cat", path], {
|
|
223
|
+
stdout: "pipe",
|
|
224
|
+
stderr: "pipe"
|
|
225
|
+
});
|
|
226
|
+
const [content, stderr] = await Promise.all([
|
|
227
|
+
process2.stdout.readText(),
|
|
228
|
+
process2.stderr.readText()
|
|
229
|
+
]);
|
|
230
|
+
const exitCode = await process2.wait();
|
|
231
|
+
if (exitCode !== 0) {
|
|
232
|
+
throw new Error(`cat failed: ${stderr}`);
|
|
223
233
|
}
|
|
224
|
-
return content;
|
|
234
|
+
return content.trim();
|
|
225
235
|
} catch (fallbackError) {
|
|
226
236
|
throw new Error(`Failed to read file ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
227
237
|
}
|
|
@@ -238,9 +248,18 @@ var modal = createProvider({
|
|
|
238
248
|
}
|
|
239
249
|
} catch (error) {
|
|
240
250
|
try {
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
251
|
+
const process2 = await modalSandbox.sandbox.exec(["sh", "-c", `printf '%s' "${content.replace(/"/g, '\\"')}" > "${path}"`], {
|
|
252
|
+
stdout: "pipe",
|
|
253
|
+
stderr: "pipe"
|
|
254
|
+
});
|
|
255
|
+
const [, stderr] = await Promise.all([
|
|
256
|
+
process2.stdout.readText(),
|
|
257
|
+
process2.stderr.readText()
|
|
258
|
+
]);
|
|
259
|
+
const exitCode = await process2.wait();
|
|
260
|
+
if (exitCode !== 0) {
|
|
261
|
+
throw new Error(`write failed: ${stderr}`);
|
|
262
|
+
}
|
|
244
263
|
} catch (fallbackError) {
|
|
245
264
|
throw new Error(`Failed to write file ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
246
265
|
}
|
|
@@ -248,15 +267,17 @@ var modal = createProvider({
|
|
|
248
267
|
},
|
|
249
268
|
mkdir: async (modalSandbox, path) => {
|
|
250
269
|
try {
|
|
251
|
-
const process2 = await modalSandbox.sandbox.exec(["mkdir", "-p", path]
|
|
270
|
+
const process2 = await modalSandbox.sandbox.exec(["mkdir", "-p", path], {
|
|
271
|
+
stdout: "pipe",
|
|
272
|
+
stderr: "pipe"
|
|
273
|
+
});
|
|
274
|
+
const [, stderr] = await Promise.all([
|
|
275
|
+
process2.stdout.readText(),
|
|
276
|
+
process2.stderr.readText()
|
|
277
|
+
]);
|
|
252
278
|
const exitCode = await process2.wait();
|
|
253
279
|
if (exitCode !== 0) {
|
|
254
|
-
|
|
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}`);
|
|
280
|
+
throw new Error(`mkdir failed: ${stderr}`);
|
|
260
281
|
}
|
|
261
282
|
} catch (error) {
|
|
262
283
|
throw new Error(`Failed to create directory ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -264,20 +285,17 @@ var modal = createProvider({
|
|
|
264
285
|
},
|
|
265
286
|
readdir: async (modalSandbox, path) => {
|
|
266
287
|
try {
|
|
267
|
-
const process2 = await modalSandbox.sandbox.exec(["ls", "-la", "--time-style=iso", path]
|
|
288
|
+
const process2 = await modalSandbox.sandbox.exec(["ls", "-la", "--time-style=iso", path], {
|
|
289
|
+
stdout: "pipe",
|
|
290
|
+
stderr: "pipe"
|
|
291
|
+
});
|
|
292
|
+
const [output, stderr] = await Promise.all([
|
|
293
|
+
process2.stdout.readText(),
|
|
294
|
+
process2.stderr.readText()
|
|
295
|
+
]);
|
|
268
296
|
const exitCode = await process2.wait();
|
|
269
297
|
if (exitCode !== 0) {
|
|
270
|
-
|
|
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);
|
|
298
|
+
throw new Error(`ls failed: ${stderr}`);
|
|
281
299
|
}
|
|
282
300
|
const lines = output.split("\n").slice(1);
|
|
283
301
|
return lines.filter((line) => line.trim()).map((line) => {
|
|
@@ -286,10 +304,10 @@ var modal = createProvider({
|
|
|
286
304
|
const size = parseInt(parts[4]) || 0;
|
|
287
305
|
const dateStr = (parts[5] || "") + " " + (parts[6] || "");
|
|
288
306
|
const date = dateStr.trim() ? new Date(dateStr) : /* @__PURE__ */ new Date();
|
|
289
|
-
const name = parts.slice(8).join(" ") || "unknown";
|
|
307
|
+
const name = parts.slice(8).join(" ") || parts[parts.length - 1] || "unknown";
|
|
290
308
|
return {
|
|
291
309
|
name,
|
|
292
|
-
path: `${path}/${name}
|
|
310
|
+
path: `${path}/${name}`.replace("//", "/"),
|
|
293
311
|
isDirectory: permissions.startsWith("d"),
|
|
294
312
|
size,
|
|
295
313
|
lastModified: isNaN(date.getTime()) ? /* @__PURE__ */ new Date() : date
|
|
@@ -310,15 +328,17 @@ var modal = createProvider({
|
|
|
310
328
|
},
|
|
311
329
|
remove: async (modalSandbox, path) => {
|
|
312
330
|
try {
|
|
313
|
-
const process2 = await modalSandbox.sandbox.exec(["rm", "-rf", path]
|
|
331
|
+
const process2 = await modalSandbox.sandbox.exec(["rm", "-rf", path], {
|
|
332
|
+
stdout: "pipe",
|
|
333
|
+
stderr: "pipe"
|
|
334
|
+
});
|
|
335
|
+
const [, stderr] = await Promise.all([
|
|
336
|
+
process2.stdout.readText(),
|
|
337
|
+
process2.stderr.readText()
|
|
338
|
+
]);
|
|
314
339
|
const exitCode = await process2.wait();
|
|
315
340
|
if (exitCode !== 0) {
|
|
316
|
-
|
|
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}`);
|
|
341
|
+
throw new Error(`rm failed: ${stderr}`);
|
|
322
342
|
}
|
|
323
343
|
} catch (error) {
|
|
324
344
|
throw new Error(`Failed to remove ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
package/dist/index.mjs.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":";AAUA,SAAS,sBAAsB;AAU/B,SAAS,KAAK,SAAS,wBAAwB;AA6BxC,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,mBAAmB;AAAA,WAEvB,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,KAClB,KAAK,SAAS,IAAI,IACd,WAEA;AAIN,cAAI,qBAAqB,UAAU;AACjC,kBAAM,IAAI,MAAM,uDAAuD;AAAA,UACzE;AAIA,gBAAMA,WAAU,MAAM,aAAa,QAAQ,KAAK,CAAC,WAAW,MAAM,IAAI,CAAC;AAGvE,gBAAM,WAAW,MAAMA,SAAQ,KAAK;AAGpC,cAAI,SAAS;AACb,cAAI,SAAS;AAGb,cAAIA,SAAQ,UAAU,OAAOA,SAAQ,OAAO,SAAS,YAAY;AAC/D,gBAAI;AACF,oBAAM,aAAa,MAAMA,SAAQ,OAAO,KAAK;AAC7C,uBAAS,OAAO,eAAe,WAAW,aAAa,IAAI,YAAY,EAAE,OAAO,UAAU;AAAA,YAC5F,SAAS,GAAG;AAAA,YAEZ;AAAA,UACF;AAEA,cAAIA,SAAQ,UAAU,OAAOA,SAAQ,OAAO,SAAS,YAAY;AAC/D,gBAAI;AACF,oBAAM,aAAa,MAAMA,SAAQ,OAAO,KAAK;AAC7C,uBAAS,OAAO,eAAe,WAAW,aAAa,IAAI,YAAY,EAAE,OAAO,UAAU;AAAA,YAC5F,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,gBAAM,eAAe,CAAC,SAAS,GAAG,IAAI;AACtC,gBAAMA,WAAU,MAAM,aAAa,QAAQ,KAAK,YAAY;AAG5D,gBAAM,WAAW,MAAMA,SAAQ,KAAK;AAGpC,cAAI,SAAS;AACb,cAAI,SAAS;AAGb,cAAIA,SAAQ,UAAU,OAAOA,SAAQ,OAAO,SAAS,YAAY;AAC/D,gBAAI;AACF,oBAAM,aAAa,MAAMA,SAAQ,OAAO,KAAK;AAC7C,uBAAS,OAAO,eAAe,WAAW,aAAa,IAAI,YAAY,EAAE,OAAO,UAAU;AAAA,YAC5F,SAAS,GAAG;AAAA,YAEZ;AAAA,UACF;AAEA,cAAIA,SAAQ,UAAU,OAAOA,SAAQ,OAAO,SAAS,YAAY;AAC/D,gBAAI;AACF,oBAAM,aAAa,MAAMA,SAAQ,OAAO,KAAK;AAC7C,uBAAS,OAAO,eAAe,WAAW,aAAa,IAAI,YAAY,EAAE,OAAO,UAAU;AAAA,YAC5F,SAAS,GAAG;AAAA,YAEZ;AAAA,UACF;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,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;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,CAAC;AAC7D,oBAAMA,SAAQ,KAAK;AAEnB,kBAAI,UAAU;AACd,kBAAIA,SAAQ,UAAU,OAAOA,SAAQ,OAAO,SAAS,YAAY;AAC/D,sBAAM,OAAO,MAAMA,SAAQ,OAAO,KAAK;AACvC,0BAAU,OAAO,SAAS,WAAW,OAAO,IAAI,YAAY,EAAE,OAAO,IAAI;AAAA,cAC3E;AAEA,qBAAO;AAAA,YACT,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;AAEF,oBAAM,iBAAiB,QAAQ,QAAQ,MAAM,OAAS;AACtD,oBAAMA,WAAU,MAAM,aAAa,QAAQ,KAAK,CAAC,MAAM,MAAM,SAAS,cAAc,QAAQ,IAAI,GAAG,CAAC;AACpG,oBAAMA,SAAQ,KAAK;AAAA,YACrB,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,CAAC;AACrE,kBAAM,WAAW,MAAMA,SAAQ,KAAK;AAEpC,gBAAI,aAAa,GAAG;AAClB,kBAAI,SAAS;AACb,kBAAIA,SAAQ,UAAU,OAAOA,SAAQ,OAAO,SAAS,YAAY;AAC/D,sBAAM,OAAO,MAAMA,SAAQ,OAAO,KAAK;AACvC,yBAAS,OAAO,SAAS,WAAW,OAAO,IAAI,YAAY,EAAE,OAAO,IAAI;AAAA,cAC1E;AACA,oBAAM,IAAI,MAAM,+BAA+B,QAAQ,KAAK,MAAM,EAAE;AAAA,YACtE;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,CAAC;AACvF,kBAAM,WAAW,MAAMA,SAAQ,KAAK;AAEpC,gBAAI,aAAa,GAAG;AAClB,kBAAI,SAAS;AACb,kBAAIA,SAAQ,UAAU,OAAOA,SAAQ,OAAO,SAAS,YAAY;AAC/D,sBAAM,OAAO,MAAMA,SAAQ,OAAO,KAAK;AACvC,yBAAS,OAAO,SAAS,WAAW,OAAO,IAAI,YAAY,EAAE,OAAO,IAAI;AAAA,cAC1E;AACA,oBAAM,IAAI,MAAM,4BAA4B,QAAQ,KAAK,MAAM,EAAE;AAAA,YACnE;AAEA,gBAAI,SAAS;AACb,gBAAIA,SAAQ,UAAU,OAAOA,SAAQ,OAAO,SAAS,YAAY;AAC/D,oBAAM,OAAO,MAAMA,SAAQ,OAAO,KAAK;AACvC,uBAAS,OAAO,SAAS,WAAW,OAAO,IAAI,YAAY,EAAE,OAAO,IAAI;AAAA,YAC1E;AAEA,kBAAM,QAAQ,OAAO,MAAM,IAAI,EAAE,MAAM,CAAC;AAExC,mBAAO,MAAM,OAAO,UAAQ,KAAK,KAAK,CAAC,EAAE,IAAI,UAAQ;AACnD,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;AAEzC,qBAAO;AAAA,gBACL;AAAA,gBACA,MAAM,GAAG,IAAI,IAAI,IAAI;AAAA,gBACrB,aAAa,YAAY,WAAW,GAAG;AAAA,gBACvC;AAAA,gBACA,cAAc,MAAM,KAAK,QAAQ,CAAC,IAAI,oBAAI,KAAK,IAAI;AAAA,cACrD;AAAA,YACF,CAAC;AAAA,UACH,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,CAAC;AACnE,kBAAM,WAAW,MAAMA,SAAQ,KAAK;AAEpC,gBAAI,aAAa,GAAG;AAClB,kBAAI,SAAS;AACb,kBAAIA,SAAQ,UAAU,OAAOA,SAAQ,OAAO,SAAS,YAAY;AAC/D,sBAAM,OAAO,MAAMA,SAAQ,OAAO,KAAK;AACvC,yBAAS,OAAO,SAAS,WAAW,OAAO,IAAI,YAAY,EAAE,OAAO,IAAI;AAAA,cAC1E;AACA,oBAAM,IAAI,MAAM,4BAA4B,QAAQ,KAAK,MAAM,EAAE;AAAA,YACnE;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,IACF;AAAA,EACF;AACF,CAAC;","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 // 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 }\n});"],"mappings":"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","names":["process"]}
|