claude-code-templates 1.24.1 → 1.24.3

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.
@@ -0,0 +1,388 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Cloudflare Sandbox Real-time Monitor
4
+ * Provides real-time monitoring and debugging of Cloudflare sandbox operations
5
+ */
6
+
7
+ import fetch from 'node-fetch';
8
+ import Anthropic from '@anthropic-ai/sdk';
9
+
10
+ interface MonitoringMetrics {
11
+ executionTime: number;
12
+ codeGenerationTime: number;
13
+ sandboxExecutionTime: number;
14
+ memoryUsage?: number;
15
+ success: boolean;
16
+ errorDetails?: string;
17
+ }
18
+
19
+ interface SandboxState {
20
+ status: 'idle' | 'generating' | 'executing' | 'completed' | 'failed';
21
+ currentStep: string;
22
+ progress: number;
23
+ }
24
+
25
+ // ANSI color codes
26
+ const colors = {
27
+ reset: '\x1b[0m',
28
+ bright: '\x1b[1m',
29
+ dim: '\x1b[2m',
30
+ red: '\x1b[31m',
31
+ green: '\x1b[32m',
32
+ yellow: '\x1b[33m',
33
+ blue: '\x1b[34m',
34
+ magenta: '\x1b[35m',
35
+ cyan: '\x1b[36m',
36
+ };
37
+
38
+ function logWithTimestamp(message: string, level: 'INFO' | 'SUCCESS' | 'ERROR' | 'WARNING' = 'INFO') {
39
+ const timestamp = new Date().toLocaleTimeString();
40
+ const icon = {
41
+ INFO: `${colors.blue}ℹ${colors.reset}`,
42
+ SUCCESS: `${colors.green}✓${colors.reset}`,
43
+ ERROR: `${colors.red}✗${colors.reset}`,
44
+ WARNING: `${colors.yellow}⚠${colors.reset}`,
45
+ }[level];
46
+
47
+ const colorCode = {
48
+ INFO: colors.blue,
49
+ SUCCESS: colors.green,
50
+ ERROR: colors.red,
51
+ WARNING: colors.yellow,
52
+ }[level];
53
+
54
+ console.log(`${colors.dim}[${timestamp}]${colors.reset} ${icon} ${colorCode}${message}${colors.reset}`);
55
+ }
56
+
57
+ function printSeparator(char: string = '=', length: number = 60) {
58
+ console.log(colors.dim + char.repeat(length) + colors.reset);
59
+ }
60
+
61
+ async function monitorWorkerHealth(workerUrl: string): Promise<boolean> {
62
+ logWithTimestamp('🔍 Checking Cloudflare Worker health...');
63
+
64
+ try {
65
+ const response = await fetch(workerUrl, {
66
+ method: 'GET',
67
+ signal: AbortSignal.timeout(5000), // 5 second timeout
68
+ });
69
+
70
+ if (response.ok || response.status === 405) {
71
+ logWithTimestamp('✅ Worker is responding', 'SUCCESS');
72
+ logWithTimestamp(` Status: ${response.status} ${response.statusText}`);
73
+ return true;
74
+ } else {
75
+ logWithTimestamp(`⚠️ Worker returned: ${response.status} ${response.statusText}`, 'WARNING');
76
+ return false;
77
+ }
78
+ } catch (error) {
79
+ const errorMsg = error instanceof Error ? error.message : String(error);
80
+ logWithTimestamp(`❌ Worker health check failed: ${errorMsg}`, 'ERROR');
81
+ return false;
82
+ }
83
+ }
84
+
85
+ async function monitorCodeGeneration(
86
+ anthropic: Anthropic,
87
+ prompt: string
88
+ ): Promise<{ code: string; duration: number }> {
89
+ logWithTimestamp('🤖 Starting code generation with Claude...');
90
+
91
+ const startTime = Date.now();
92
+
93
+ try {
94
+ const response = await anthropic.messages.create({
95
+ model: 'claude-sonnet-4-5',
96
+ max_tokens: 4096,
97
+ messages: [
98
+ {
99
+ role: 'user',
100
+ content: `Generate Python code to answer: "${prompt}"
101
+
102
+ Requirements:
103
+ - Use only Python standard library
104
+ - Print the result using print()
105
+ - Keep code simple and safe
106
+ - Include proper error handling
107
+
108
+ Return ONLY the code, no explanations.`,
109
+ },
110
+ ],
111
+ });
112
+
113
+ const duration = Date.now() - startTime;
114
+
115
+ const code = response.content[0]?.type === 'text' ? response.content[0].text : '';
116
+
117
+ if (!code) {
118
+ throw new Error('No code generated');
119
+ }
120
+
121
+ logWithTimestamp(`✅ Code generated in ${duration}ms`, 'SUCCESS');
122
+ logWithTimestamp(` Model: ${response.model}`);
123
+ logWithTimestamp(` Tokens used: ${response.usage.input_tokens} in, ${response.usage.output_tokens} out`);
124
+ logWithTimestamp(` Code length: ${code.length} characters`);
125
+
126
+ if (code.length < 500) {
127
+ console.log('');
128
+ logWithTimestamp('📝 Generated Code Preview:');
129
+ printSeparator('-', 60);
130
+ console.log(colors.cyan + code + colors.reset);
131
+ printSeparator('-', 60);
132
+ console.log('');
133
+ }
134
+
135
+ return { code, duration };
136
+ } catch (error) {
137
+ const errorMsg = error instanceof Error ? error.message : String(error);
138
+ logWithTimestamp(`❌ Code generation failed: ${errorMsg}`, 'ERROR');
139
+ throw error;
140
+ }
141
+ }
142
+
143
+ async function monitorSandboxExecution(
144
+ workerUrl: string,
145
+ question: string
146
+ ): Promise<{ result: any; duration: number }> {
147
+ logWithTimestamp('⚙️ Executing in Cloudflare Sandbox...');
148
+
149
+ const startTime = Date.now();
150
+
151
+ try {
152
+ const response = await fetch(`${workerUrl}/execute`, {
153
+ method: 'POST',
154
+ headers: {
155
+ 'Content-Type': 'application/json',
156
+ },
157
+ body: JSON.stringify({ question }),
158
+ signal: AbortSignal.timeout(60000), // 60 second timeout
159
+ });
160
+
161
+ const duration = Date.now() - startTime;
162
+
163
+ if (!response.ok) {
164
+ const errorText = await response.text();
165
+ throw new Error(`Sandbox returned ${response.status}: ${errorText}`);
166
+ }
167
+
168
+ const result = await response.json();
169
+
170
+ logWithTimestamp(`✅ Sandbox execution completed in ${duration}ms`, 'SUCCESS');
171
+ logWithTimestamp(` Exit code: ${result.success ? '0 (success)' : '1 (failed)'}`);
172
+ logWithTimestamp(` Output length: ${result.output?.length || 0} characters`);
173
+
174
+ if (result.error) {
175
+ logWithTimestamp(` Error output: ${result.error.length} characters`, 'WARNING');
176
+ }
177
+
178
+ return { result, duration };
179
+ } catch (error) {
180
+ const duration = Date.now() - startTime;
181
+ const errorMsg = error instanceof Error ? error.message : String(error);
182
+ logWithTimestamp(`❌ Sandbox execution failed after ${duration}ms: ${errorMsg}`, 'ERROR');
183
+ throw error;
184
+ }
185
+ }
186
+
187
+ function displayMetrics(metrics: MonitoringMetrics) {
188
+ console.log('');
189
+ printSeparator();
190
+ console.log(`${colors.bright}📊 PERFORMANCE METRICS${colors.reset}`);
191
+ printSeparator();
192
+ console.log('');
193
+
194
+ console.log(`${colors.cyan}Total Execution Time:${colors.reset} ${metrics.executionTime}ms`);
195
+ console.log(`${colors.cyan} ├─ Code Generation:${colors.reset} ${metrics.codeGenerationTime}ms`);
196
+ console.log(`${colors.cyan} └─ Sandbox Execution:${colors.reset} ${metrics.sandboxExecutionTime}ms`);
197
+
198
+ if (metrics.memoryUsage) {
199
+ console.log(`${colors.cyan}Memory Usage:${colors.reset} ${metrics.memoryUsage}MB`);
200
+ }
201
+
202
+ console.log('');
203
+ console.log(
204
+ `${colors.cyan}Status:${colors.reset} ${metrics.success ? `${colors.green}Success ✓${colors.reset}` : `${colors.red}Failed ✗${colors.reset}`}`
205
+ );
206
+
207
+ if (metrics.errorDetails) {
208
+ console.log(`${colors.red}Error Details:${colors.reset} ${metrics.errorDetails}`);
209
+ }
210
+
211
+ printSeparator();
212
+ }
213
+
214
+ function displaySystemInfo() {
215
+ console.log('');
216
+ printSeparator();
217
+ console.log(`${colors.bright}🖥️ SYSTEM INFORMATION${colors.reset}`);
218
+ printSeparator();
219
+ console.log('');
220
+
221
+ console.log(`${colors.cyan}Node.js Version:${colors.reset} ${process.version}`);
222
+ console.log(`${colors.cyan}Platform:${colors.reset} ${process.platform}`);
223
+ console.log(`${colors.cyan}Architecture:${colors.reset} ${process.arch}`);
224
+ console.log(
225
+ `${colors.cyan}Memory Usage:${colors.reset} ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024)}MB / ${Math.round(process.memoryUsage().heapTotal / 1024 / 1024)}MB`
226
+ );
227
+
228
+ printSeparator();
229
+ console.log('');
230
+ }
231
+
232
+ async function enhancedSandboxMonitoring(
233
+ prompt: string,
234
+ anthropicApiKey: string,
235
+ workerUrl: string = 'http://localhost:8787'
236
+ ): Promise<boolean> {
237
+ logWithTimestamp('🚀 Starting enhanced Cloudflare sandbox monitoring');
238
+ printSeparator();
239
+
240
+ displaySystemInfo();
241
+
242
+ const metrics: MonitoringMetrics = {
243
+ executionTime: 0,
244
+ codeGenerationTime: 0,
245
+ sandboxExecutionTime: 0,
246
+ success: false,
247
+ };
248
+
249
+ const startTime = Date.now();
250
+
251
+ try {
252
+ // Step 1: Check worker health
253
+ const workerHealthy = await monitorWorkerHealth(workerUrl);
254
+ if (!workerHealthy) {
255
+ logWithTimestamp('⚠️ Worker is not healthy, attempting to continue...', 'WARNING');
256
+ }
257
+
258
+ console.log('');
259
+
260
+ // Step 2: Generate code with monitoring
261
+ const anthropic = new Anthropic({ apiKey: anthropicApiKey });
262
+ const { code, duration: codeGenDuration } = await monitorCodeGeneration(anthropic, prompt);
263
+ metrics.codeGenerationTime = codeGenDuration;
264
+
265
+ console.log('');
266
+
267
+ // Step 3: Execute in sandbox with monitoring
268
+ const { result, duration: execDuration } = await monitorSandboxExecution(workerUrl, prompt);
269
+ metrics.sandboxExecutionTime = execDuration;
270
+
271
+ console.log('');
272
+
273
+ // Display results
274
+ printSeparator();
275
+ console.log(`${colors.bright}🎯 EXECUTION RESULTS${colors.reset}`);
276
+ printSeparator();
277
+ console.log('');
278
+
279
+ console.log(`${colors.cyan}Question:${colors.reset} ${result.question}`);
280
+ console.log('');
281
+
282
+ console.log(`${colors.cyan}Generated Code:${colors.reset}`);
283
+ printSeparator('-', 60);
284
+ console.log(colors.green + result.code + colors.reset);
285
+ printSeparator('-', 60);
286
+ console.log('');
287
+
288
+ if (result.output) {
289
+ console.log(`${colors.cyan}Output:${colors.reset}`);
290
+ console.log(colors.bright + result.output + colors.reset);
291
+ console.log('');
292
+ }
293
+
294
+ if (result.error) {
295
+ console.log(`${colors.red}Error Output:${colors.reset}`);
296
+ console.log(result.error);
297
+ console.log('');
298
+ }
299
+
300
+ metrics.executionTime = Date.now() - startTime;
301
+ metrics.success = result.success;
302
+ metrics.memoryUsage = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
303
+
304
+ displayMetrics(metrics);
305
+
306
+ logWithTimestamp('✅ Monitoring session completed successfully', 'SUCCESS');
307
+ return true;
308
+ } catch (error) {
309
+ metrics.executionTime = Date.now() - startTime;
310
+ metrics.success = false;
311
+ metrics.errorDetails = error instanceof Error ? error.message : String(error);
312
+ metrics.memoryUsage = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
313
+
314
+ console.log('');
315
+ displayMetrics(metrics);
316
+
317
+ logWithTimestamp(`❌ Monitoring session failed: ${metrics.errorDetails}`, 'ERROR');
318
+ return false;
319
+ }
320
+ }
321
+
322
+ async function main() {
323
+ const args = process.argv.slice(2);
324
+
325
+ if (args.length < 1) {
326
+ console.log('Cloudflare Sandbox Monitor');
327
+ console.log('');
328
+ console.log('Usage:');
329
+ console.log(' node monitor.ts <prompt> [anthropic_api_key] [worker_url]');
330
+ console.log('');
331
+ console.log('Examples:');
332
+ console.log(' node monitor.ts "Calculate factorial of 5"');
333
+ console.log(' node monitor.ts "Fibonacci 10" YOUR_API_KEY');
334
+ console.log(' node monitor.ts "Sum array" YOUR_KEY https://your-worker.workers.dev');
335
+ console.log('');
336
+ console.log('Environment Variables:');
337
+ console.log(' ANTHROPIC_API_KEY - Anthropic API key');
338
+ console.log(' CLOUDFLARE_WORKER_URL - Worker endpoint (default: http://localhost:8787)');
339
+ console.log('');
340
+ console.log('This tool provides enhanced monitoring and debugging for Cloudflare sandbox operations.');
341
+ process.exit(1);
342
+ }
343
+
344
+ const prompt = args[0];
345
+ const anthropicApiKey = args[1] || process.env.ANTHROPIC_API_KEY || '';
346
+ const workerUrl = args[2] || process.env.CLOUDFLARE_WORKER_URL || 'http://localhost:8787';
347
+
348
+ if (!anthropicApiKey) {
349
+ logWithTimestamp('❌ Anthropic API key is required', 'ERROR');
350
+ console.log('Provide via command line argument or ANTHROPIC_API_KEY environment variable');
351
+ process.exit(1);
352
+ }
353
+
354
+ console.log('');
355
+ printSeparator('=', 70);
356
+ console.log(`${colors.bright}${colors.cyan} 🎬 CLOUDFLARE SANDBOX MONITOR${colors.reset}`);
357
+ printSeparator('=', 70);
358
+ console.log('');
359
+
360
+ const success = await enhancedSandboxMonitoring(prompt, anthropicApiKey, workerUrl);
361
+
362
+ console.log('');
363
+
364
+ if (success) {
365
+ logWithTimestamp('🎉 Monitoring completed successfully', 'SUCCESS');
366
+ process.exit(0);
367
+ } else {
368
+ logWithTimestamp('💔 Monitoring failed', 'ERROR');
369
+ console.log('');
370
+ console.log('Troubleshooting:');
371
+ console.log('1. Ensure worker is deployed: npx wrangler deploy');
372
+ console.log('2. Check API key is set: npx wrangler secret put ANTHROPIC_API_KEY');
373
+ console.log('3. Verify worker URL is correct');
374
+ console.log('4. Check worker logs: npx wrangler tail');
375
+ console.log('5. Test locally: npm run dev');
376
+ process.exit(1);
377
+ }
378
+ }
379
+
380
+ // Run if executed directly
381
+ if (require.main === module) {
382
+ main().catch((error) => {
383
+ console.error('Fatal error:', error);
384
+ process.exit(1);
385
+ });
386
+ }
387
+
388
+ export { monitorWorkerHealth, monitorCodeGeneration, monitorSandboxExecution };
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "cloudflare-claude-sandbox",
3
+ "version": "1.0.0",
4
+ "description": "Cloudflare Workers sandbox for executing Claude Code with AI-powered code generation",
5
+ "type": "module",
6
+ "main": "src/index.ts",
7
+ "scripts": {
8
+ "dev": "wrangler dev",
9
+ "deploy": "wrangler deploy",
10
+ "tail": "wrangler tail",
11
+ "launch": "tsx launcher.ts",
12
+ "monitor": "tsx monitor.ts",
13
+ "test": "vitest",
14
+ "test:watch": "vitest --watch",
15
+ "type-check": "tsc --noEmit",
16
+ "format": "prettier --write .",
17
+ "format:check": "prettier --check ."
18
+ },
19
+ "dependencies": {
20
+ "@anthropic-ai/sdk": "^0.20.0",
21
+ "@cloudflare/sandbox": "^0.1.0",
22
+ "node-fetch": "^3.3.2"
23
+ },
24
+ "devDependencies": {
25
+ "@cloudflare/vitest-pool-workers": "^0.1.0",
26
+ "@cloudflare/workers-types": "^4.20240129.0",
27
+ "@types/node": "^20.11.0",
28
+ "prettier": "^3.2.4",
29
+ "tsx": "^4.7.0",
30
+ "typescript": "^5.3.3",
31
+ "vitest": "^1.2.0",
32
+ "wrangler": "^3.78.12"
33
+ },
34
+ "engines": {
35
+ "node": ">=16.17.0"
36
+ },
37
+ "keywords": [
38
+ "cloudflare",
39
+ "workers",
40
+ "sandbox",
41
+ "claude",
42
+ "anthropic",
43
+ "ai",
44
+ "code-execution",
45
+ "durable-objects",
46
+ "edge-computing"
47
+ ],
48
+ "author": "Claude Code Templates",
49
+ "license": "MIT",
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "https://github.com/anthropics/claude-code-templates"
53
+ }
54
+ }
@@ -0,0 +1,240 @@
1
+ import { getSandbox, type Sandbox } from '@cloudflare/sandbox';
2
+ import Anthropic from '@anthropic-ai/sdk';
3
+
4
+ export { Sandbox } from '@cloudflare/sandbox';
5
+
6
+ interface Env {
7
+ Sandbox: DurableObjectNamespace<Sandbox>;
8
+ ANTHROPIC_API_KEY: string;
9
+ }
10
+
11
+ interface ExecuteRequest {
12
+ question: string;
13
+ maxTokens?: number;
14
+ timeout?: number;
15
+ language?: 'python' | 'javascript';
16
+ }
17
+
18
+ interface ExecuteResponse {
19
+ success: boolean;
20
+ question: string;
21
+ code: string;
22
+ output: string;
23
+ error: string;
24
+ sandboxId?: string;
25
+ executionTime?: number;
26
+ }
27
+
28
+ /**
29
+ * Main Worker handler
30
+ * Receives code execution requests and orchestrates Claude AI + Cloudflare Sandbox
31
+ */
32
+ export default {
33
+ async fetch(request: Request, env: Env): Promise<Response> {
34
+ const url = new URL(request.url);
35
+
36
+ // CORS headers for browser access
37
+ const corsHeaders = {
38
+ 'Access-Control-Allow-Origin': '*',
39
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
40
+ 'Access-Control-Allow-Headers': 'Content-Type',
41
+ };
42
+
43
+ // Handle OPTIONS request for CORS
44
+ if (request.method === 'OPTIONS') {
45
+ return new Response(null, { headers: corsHeaders });
46
+ }
47
+
48
+ // Root endpoint - return usage instructions
49
+ if (request.method === 'GET' && url.pathname === '/') {
50
+ return new Response(
51
+ JSON.stringify({
52
+ name: 'Cloudflare Claude Code Sandbox',
53
+ version: '1.0.0',
54
+ endpoints: {
55
+ execute: 'POST /execute - Execute code via Claude AI',
56
+ health: 'GET /health - Check worker health',
57
+ },
58
+ usage: {
59
+ example: {
60
+ method: 'POST',
61
+ url: '/execute',
62
+ body: {
63
+ question: 'What is the 10th Fibonacci number?',
64
+ },
65
+ },
66
+ },
67
+ }),
68
+ {
69
+ headers: {
70
+ 'Content-Type': 'application/json',
71
+ ...corsHeaders,
72
+ },
73
+ }
74
+ );
75
+ }
76
+
77
+ // Health check endpoint
78
+ if (request.method === 'GET' && url.pathname === '/health') {
79
+ return new Response(
80
+ JSON.stringify({
81
+ status: 'healthy',
82
+ timestamp: new Date().toISOString(),
83
+ worker: 'cloudflare-claude-sandbox',
84
+ }),
85
+ {
86
+ headers: {
87
+ 'Content-Type': 'application/json',
88
+ ...corsHeaders,
89
+ },
90
+ }
91
+ );
92
+ }
93
+
94
+ // Execute endpoint
95
+ if (request.method === 'POST' && url.pathname === '/execute') {
96
+ const startTime = Date.now();
97
+
98
+ try {
99
+ // Parse request body
100
+ const body = (await request.json()) as ExecuteRequest;
101
+
102
+ if (!body.question) {
103
+ return Response.json(
104
+ { error: 'Question is required' },
105
+ { status: 400, headers: corsHeaders }
106
+ );
107
+ }
108
+
109
+ // Validate API key
110
+ if (!env.ANTHROPIC_API_KEY) {
111
+ return Response.json(
112
+ {
113
+ error: 'ANTHROPIC_API_KEY not configured',
114
+ message: 'Set the API key using: npx wrangler secret put ANTHROPIC_API_KEY',
115
+ },
116
+ { status: 500, headers: corsHeaders }
117
+ );
118
+ }
119
+
120
+ // Initialize Anthropic client
121
+ const anthropic = new Anthropic({
122
+ apiKey: env.ANTHROPIC_API_KEY,
123
+ });
124
+
125
+ // Generate code using Claude
126
+ console.log('Generating code with Claude for:', body.question.substring(0, 100));
127
+
128
+ const language = body.language || 'python';
129
+ const codePrompt =
130
+ language === 'python'
131
+ ? `Generate Python code to answer: "${body.question}"
132
+
133
+ Requirements:
134
+ - Use only Python standard library
135
+ - Print the result using print()
136
+ - Keep code simple and safe
137
+ - Include proper error handling
138
+ - Use descriptive variable names
139
+
140
+ Return ONLY the code, no explanations or markdown formatting.`
141
+ : `Generate JavaScript code to answer: "${body.question}"
142
+
143
+ Requirements:
144
+ - Use only Node.js standard library
145
+ - Print the result using console.log()
146
+ - Keep code simple and safe
147
+ - Include proper error handling
148
+ - Use descriptive variable names
149
+
150
+ Return ONLY the code, no explanations or markdown formatting.`;
151
+
152
+ const codeGeneration = await anthropic.messages.create({
153
+ model: 'claude-sonnet-4-5',
154
+ max_tokens: body.maxTokens || 2048,
155
+ messages: [
156
+ {
157
+ role: 'user',
158
+ content: codePrompt,
159
+ },
160
+ ],
161
+ });
162
+
163
+ const generatedCode =
164
+ codeGeneration.content[0]?.type === 'text' ? codeGeneration.content[0].text : '';
165
+
166
+ if (!generatedCode) {
167
+ return Response.json(
168
+ { error: 'Failed to generate code from Claude' },
169
+ { status: 500, headers: corsHeaders }
170
+ );
171
+ }
172
+
173
+ // Clean up code (remove markdown formatting if present)
174
+ const cleanCode = generatedCode
175
+ .replace(/```(?:python|javascript|js)?\n?/g, '')
176
+ .replace(/```\n?$/g, '')
177
+ .trim();
178
+
179
+ console.log('Code generated, executing in sandbox...');
180
+
181
+ // Execute the code in a sandbox
182
+ // Use a unique ID per request to avoid conflicts
183
+ const sandboxId = `user-${Date.now()}-${Math.random().toString(36).substring(7)}`;
184
+ const sandbox = getSandbox(env.Sandbox, sandboxId);
185
+
186
+ // Determine execution command based on language
187
+ const fileName = language === 'python' ? '/tmp/code.py' : '/tmp/code.js';
188
+ const execCommand =
189
+ language === 'python' ? 'python /tmp/code.py' : 'node /tmp/code.js';
190
+
191
+ // Write code to sandbox and execute
192
+ await sandbox.writeFile(fileName, cleanCode);
193
+
194
+ const result = await sandbox.exec(execCommand, {
195
+ timeout: body.timeout || 30000, // 30 seconds default
196
+ });
197
+
198
+ const executionTime = Date.now() - startTime;
199
+
200
+ const response: ExecuteResponse = {
201
+ success: result.success,
202
+ question: body.question,
203
+ code: cleanCode,
204
+ output: result.stdout || '',
205
+ error: result.stderr || '',
206
+ sandboxId: sandboxId,
207
+ executionTime: executionTime,
208
+ };
209
+
210
+ console.log(
211
+ `Execution completed in ${executionTime}ms. Success: ${result.success}`
212
+ );
213
+
214
+ return Response.json(response, {
215
+ headers: corsHeaders,
216
+ });
217
+ } catch (error: unknown) {
218
+ console.error('Execution error:', error);
219
+
220
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
221
+ const executionTime = Date.now() - startTime;
222
+
223
+ return Response.json(
224
+ {
225
+ error: 'Internal server error',
226
+ message: errorMessage,
227
+ executionTime: executionTime,
228
+ },
229
+ { status: 500, headers: corsHeaders }
230
+ );
231
+ }
232
+ }
233
+
234
+ // Unknown endpoint
235
+ return new Response(
236
+ 'POST /execute with { "question": "your question" }\nGET /health for health check\nGET / for API information',
237
+ { status: 404, headers: corsHeaders }
238
+ );
239
+ },
240
+ };