diff --git a/README.md b/README.md
index b5bbbcdf..3f8092b1 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
Chat with AI to build React apps instantly. An example app made by the [Firecrawl](https://firecrawl.dev/?ref=open-lovable-github) team. For a complete cloud solution, check out [Lovable.dev](https://lovable.dev/) ❤️.
-
+
## Setup
@@ -64,4 +64,4 @@ Open [http://localhost:3000](http://localhost:3000)
## License
-MIT
\ No newline at end of file
+MIT
diff --git a/app/api/create-zip/route.ts b/app/api/create-zip/route.ts
index 91418c43..fd3c3ed0 100644
--- a/app/api/create-zip/route.ts
+++ b/app/api/create-zip/route.ts
@@ -2,68 +2,294 @@ import { NextResponse } from 'next/server';
declare global {
var activeSandbox: any;
+ var activeSandboxProvider: any;
}
export async function POST() {
try {
- if (!global.activeSandbox) {
- return NextResponse.json({
- success: false,
- error: 'No active sandbox'
+ // Check both V2 provider (new) and V1 sandbox (legacy) patterns
+ const provider = global.activeSandboxProvider;
+ const sandbox = global.activeSandbox;
+
+ if (!provider && !sandbox) {
+ return NextResponse.json({
+ success: false,
+ error: 'No active sandbox'
}, { status: 400 });
}
-
+
console.log('[create-zip] Creating project zip...');
-
- // Create zip file in sandbox using standard commands
- const zipResult = await global.activeSandbox.runCommand({
- cmd: 'bash',
- args: ['-c', `zip -r /tmp/project.zip . -x "node_modules/*" ".git/*" ".next/*" "dist/*" "build/*" "*.log"`]
- });
-
- if (zipResult.exitCode !== 0) {
- const error = await zipResult.stderr();
- throw new Error(`Failed to create zip: ${error}`);
- }
-
- const sizeResult = await global.activeSandbox.runCommand({
- cmd: 'bash',
- args: ['-c', `ls -la /tmp/project.zip | awk '{print $5}'`]
- });
-
- const fileSize = await sizeResult.stdout();
- console.log(`[create-zip] Created project.zip (${fileSize.trim()} bytes)`);
-
- // Read the zip file and convert to base64
- const readResult = await global.activeSandbox.runCommand({
- cmd: 'base64',
- args: ['/tmp/project.zip']
- });
-
- if (readResult.exitCode !== 0) {
- const error = await readResult.stderr();
- throw new Error(`Failed to read zip file: ${error}`);
+
+ // Detect provider type
+ const isE2B = provider && provider.constructor.name === 'E2BProvider';
+ const isVercel = provider && provider.constructor.name === 'VercelProvider';
+ const isV1Sandbox = !provider && sandbox;
+
+ console.log('[create-zip] Provider type:', { isE2B, isVercel, isV1Sandbox });
+
+ if (isE2B && provider.sandbox) {
+ // E2B Provider - use Python code execution to avoid command parsing issues
+ try {
+ console.log('[create-zip] Using E2B Python-based zip creation');
+
+ // Create zip using Python's zipfile module
+ const zipCreationResult = await provider.sandbox.runCode(`
+import zipfile
+import os
+import base64
+from pathlib import Path
+
+# Change to app directory
+os.chdir('/home/user/app')
+
+# Create zip file
+zip_path = '/tmp/project.zip'
+exclude_patterns = ['node_modules', '.git', '.next', 'dist', 'build', '*.log', '__pycache__', '*.pyc']
+
+def should_exclude(path):
+ path_str = str(path)
+ for pattern in exclude_patterns:
+ if pattern in path_str:
+ return True
+ if pattern.startswith('*') and path_str.endswith(pattern[1:]):
+ return True
+ return False
+
+with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
+ for root, dirs, files in os.walk('.'):
+ # Filter out excluded directories
+ dirs[:] = [d for d in dirs if not should_exclude(d)]
+
+ for file in files:
+ file_path = os.path.join(root, file)
+ if not should_exclude(file_path):
+ # Add file to zip with relative path
+ arcname = os.path.relpath(file_path, '.')
+ zipf.write(file_path, arcname)
+
+# Get file size
+file_size = os.path.getsize(zip_path)
+print(f"ZIP_SIZE:{file_size}")
+
+# Read and encode to base64
+with open(zip_path, 'rb') as f:
+ zip_content = f.read()
+ base64_content = base64.b64encode(zip_content).decode('utf-8')
+ print(f"BASE64_START:{base64_content}:BASE64_END")
+ `);
+
+ // Parse the output to extract base64 content
+ const output = zipCreationResult.logs.stdout.join('\n');
+
+ // Extract file size
+ const sizeMatch = output.match(/ZIP_SIZE:(\d+)/);
+ const fileSize = sizeMatch ? sizeMatch[1] : 'unknown';
+ console.log(`[create-zip] Created project.zip (${fileSize} bytes)`);
+
+ // Extract base64 content (using [\s\S] instead of 's' flag for compatibility)
+ const base64Match = output.match(/BASE64_START:([\s\S]*?):BASE64_END/);
+ if (!base64Match) {
+ throw new Error('Failed to extract base64 content from Python output');
+ }
+
+ const base64Content = base64Match[1].trim();
+
+ // Create a data URL for download
+ const dataUrl = `data:application/zip;base64,${base64Content}`;
+
+ return NextResponse.json({
+ success: true,
+ dataUrl,
+ fileName: 'e2b-sandbox-project.zip',
+ message: 'Zip file created successfully'
+ });
+
+ } catch (error) {
+ console.error('[create-zip] E2B Provider error:', error);
+ throw error;
+ }
+
+ } else if (isVercel && provider) {
+ // Vercel Provider - use correct working directory
+ try {
+ console.log('[create-zip] Using Vercel Provider with /vercel/sandbox path');
+
+ // Install zip utility using dnf package manager with sudo
+ console.log('[create-zip] Installing zip utility...');
+ const installResult = await provider.sandbox.runCommand({
+ cmd: 'dnf',
+ args: ['install', '-y', 'zip'],
+ sudo: true
+ });
+
+ // Create zip file
+ const zipResult = await provider.sandbox.runCommand({
+ cmd: 'zip',
+ args: ['-r', '/tmp/project.zip', '.', '-x', 'node_modules/*', '.git/*', '.next/*', 'dist/*', 'build/*', '*.log'],
+ cwd: '/vercel/sandbox'
+ });
+
+ // Handle stdout and stderr - they might be functions in Vercel SDK
+ let stderr = '';
+ try {
+ if (typeof zipResult.stderr === 'function') {
+ stderr = await zipResult.stderr();
+ } else {
+ stderr = zipResult.stderr || '';
+ }
+ } catch (e) {
+ stderr = '';
+ }
+
+ if (zipResult.exitCode !== 0) {
+ throw new Error(`Failed to create zip: ${stderr}`);
+ }
+
+ const sizeResult = await provider.sandbox.runCommand({
+ cmd: 'sh',
+ args: ['-c', 'ls -la /tmp/project.zip | awk \'{print $5}\'']
+ });
+
+ let fileSize = '';
+ try {
+ if (typeof sizeResult.stdout === 'function') {
+ fileSize = (await sizeResult.stdout()).trim();
+ } else {
+ fileSize = (sizeResult.stdout || '').trim();
+ }
+ } catch (e) {
+ fileSize = 'unknown';
+ }
+ console.log(`[create-zip] Created project.zip (${fileSize} bytes)`);
+
+ // Read the zip file and convert to base64
+ const readResult = await provider.sandbox.runCommand({
+ cmd: 'base64',
+ args: ['/tmp/project.zip']
+ });
+
+ let readStderr = '';
+ try {
+ if (typeof readResult.stderr === 'function') {
+ readStderr = await readResult.stderr();
+ } else {
+ readStderr = readResult.stderr || '';
+ }
+ } catch (e) {
+ readStderr = '';
+ }
+
+ if (readResult.exitCode !== 0) {
+ throw new Error(`Failed to read zip file: ${readStderr}`);
+ }
+
+ let base64Content = '';
+ try {
+ if (typeof readResult.stdout === 'function') {
+ base64Content = (await readResult.stdout()).trim();
+ } else {
+ base64Content = (readResult.stdout || '').trim();
+ }
+ } catch (e) {
+ throw new Error('Failed to get base64 content from command result');
+ }
+
+ // Create a data URL for download
+ const dataUrl = `data:application/zip;base64,${base64Content}`;
+
+ return NextResponse.json({
+ success: true,
+ dataUrl,
+ fileName: 'vercel-sandbox-project.zip',
+ message: 'Zip file created successfully'
+ });
+
+ } catch (error) {
+ console.error('[create-zip] Vercel Provider error:', error);
+ throw error;
+ }
+
+ } else if (isV1Sandbox) {
+ // V1 Sandbox pattern - uses object with cmd/args (legacy)
+ try {
+ const zipResult = await sandbox.runCommand({
+ cmd: 'bash',
+ args: ['-c', `zip -r /tmp/project.zip . -x "node_modules/*" ".git/*" ".next/*" "dist/*" "build/*" "*.log"`]
+ });
+
+ // Handle potential function-based stdout/stderr (Vercel SDK pattern)
+ const exitCode = zipResult.exitCode;
+ let stderr = '';
+
+ if (typeof zipResult.stderr === 'function') {
+ stderr = await zipResult.stderr();
+ } else {
+ stderr = zipResult.stderr || '';
+ }
+
+ if (exitCode !== 0) {
+ throw new Error(`Failed to create zip: ${stderr}`);
+ }
+
+ const sizeResult = await sandbox.runCommand({
+ cmd: 'bash',
+ args: ['-c', `ls -la /tmp/project.zip | awk '{print $5}'`]
+ });
+
+ let fileSize = '';
+ if (typeof sizeResult.stdout === 'function') {
+ fileSize = await sizeResult.stdout();
+ } else {
+ fileSize = sizeResult.stdout || '';
+ }
+ console.log(`[create-zip] Created project.zip (${fileSize.trim()} bytes)`);
+
+ // Read the zip file and convert to base64
+ const readResult = await sandbox.runCommand({
+ cmd: 'base64',
+ args: ['/tmp/project.zip']
+ });
+
+ if (readResult.exitCode !== 0) {
+ let error = '';
+ if (typeof readResult.stderr === 'function') {
+ error = await readResult.stderr();
+ } else {
+ error = readResult.stderr || 'Unknown error';
+ }
+ throw new Error(`Failed to read zip file: ${error}`);
+ }
+
+ let base64Content = '';
+ if (typeof readResult.stdout === 'function') {
+ base64Content = (await readResult.stdout()).trim();
+ } else {
+ base64Content = (readResult.stdout || '').trim();
+ }
+
+ // Create a data URL for download
+ const dataUrl = `data:application/zip;base64,${base64Content}`;
+
+ return NextResponse.json({
+ success: true,
+ dataUrl,
+ fileName: 'vercel-sandbox-project.zip',
+ message: 'Zip file created successfully'
+ });
+
+ } catch (error) {
+ console.error('[create-zip] V1 Sandbox error:', error);
+ throw error;
+ }
}
-
- const base64Content = (await readResult.stdout()).trim();
-
- // Create a data URL for download
- const dataUrl = `data:application/zip;base64,${base64Content}`;
-
- return NextResponse.json({
- success: true,
- dataUrl,
- fileName: 'vercel-sandbox-project.zip',
- message: 'Zip file created successfully'
- });
-
+
} catch (error) {
console.error('[create-zip] Error:', error);
return NextResponse.json(
- {
- success: false,
- error: (error as Error).message
- },
+ {
+ success: false,
+ error: (error as Error).message
+ },
{ status: 500 }
);
}
diff --git a/app/api/generate-ai-code-stream/route.ts b/app/api/generate-ai-code-stream/route.ts
index 6a8f6aac..9929d9b7 100644
--- a/app/api/generate-ai-code-stream/route.ts
+++ b/app/api/generate-ai-code-stream/route.ts
@@ -44,6 +44,11 @@ const openai = createOpenAI({
baseURL: isUsingAIGateway ? aiGatewayBaseURL : process.env.OPENAI_BASE_URL,
});
+const aiStupidLevel = createOpenAI({
+ apiKey: process.env.AI_STUPID_LEVEL_API_KEY,
+ baseURL: 'https://aistupidlevel.info/v1',
+});
+
// Helper function to analyze user preferences from conversation history
function analyzeUserPreferences(messages: ConversationMessage[]): {
commonPatterns: string[];
@@ -1216,11 +1221,13 @@ MORPH FAST APPLY MODE (EDIT-ONLY):
const isAnthropic = model.startsWith('anthropic/');
const isGoogle = model.startsWith('google/');
const isOpenAI = model.startsWith('openai/');
+ const isAIStupidLevel = model.startsWith('aistupidlevel/');
const isKimiGroq = model === 'moonshotai/kimi-k2-instruct-0905';
const modelProvider = isAnthropic ? anthropic :
(isOpenAI ? openai :
(isGoogle ? googleGenerativeAI :
- (isKimiGroq ? groq : groq)));
+ (isAIStupidLevel ? aiStupidLevel :
+ (isKimiGroq ? groq : groq))));
// Fix model name transformation for different providers
let actualModel: string;
@@ -1228,6 +1235,9 @@ MORPH FAST APPLY MODE (EDIT-ONLY):
actualModel = model.replace('anthropic/', '');
} else if (isOpenAI) {
actualModel = model.replace('openai/', '');
+ } else if (isAIStupidLevel) {
+ // AI Stupid Level uses the full model string
+ actualModel = model.replace('aistupidlevel/', '');
} else if (isKimiGroq) {
// Kimi on Groq - use full model string
actualModel = 'moonshotai/kimi-k2-instruct-0905';
@@ -1238,7 +1248,7 @@ MORPH FAST APPLY MODE (EDIT-ONLY):
actualModel = model;
}
- console.log(`[generate-ai-code-stream] Using provider: ${isAnthropic ? 'Anthropic' : isGoogle ? 'Google' : isOpenAI ? 'OpenAI' : 'Groq'}, model: ${actualModel}`);
+ console.log(`[generate-ai-code-stream] Using provider: ${isAnthropic ? 'Anthropic' : isGoogle ? 'Google' : isOpenAI ? 'OpenAI' : isAIStupidLevel ? 'AI Stupid Level' : 'Groq'}, model: ${actualModel}`);
console.log(`[generate-ai-code-stream] AI Gateway enabled: ${isUsingAIGateway}`);
console.log(`[generate-ai-code-stream] Model string: ${model}`);
@@ -1893,4 +1903,4 @@ Provide the complete file content without any truncation. Include all necessary
error: (error as Error).message
}, { status: 500 });
}
-}
\ No newline at end of file
+}
diff --git a/config/app.config.ts b/config/app.config.ts
index b1a21e8e..ef4f9391 100644
--- a/config/app.config.ts
+++ b/config/app.config.ts
@@ -58,7 +58,13 @@ export const appConfig = {
'openai/gpt-5',
'moonshotai/kimi-k2-instruct-0905',
'anthropic/claude-sonnet-4-20250514',
- 'google/gemini-3-pro-preview'
+ 'google/gemini-2.0-flash-exp',
+ 'aistupidlevel/auto',
+ 'aistupidlevel/auto-coding',
+ 'aistupidlevel/auto-reasoning',
+ 'aistupidlevel/auto-creative',
+ 'aistupidlevel/auto-fastest',
+ 'aistupidlevel/auto-cheapest'
],
// Model display names
@@ -66,7 +72,13 @@ export const appConfig = {
'openai/gpt-5': 'GPT-5',
'moonshotai/kimi-k2-instruct-0905': 'Kimi K2 (Groq)',
'anthropic/claude-sonnet-4-20250514': 'Sonnet 4',
- 'google/gemini-3-pro-preview': 'Gemini 3 Pro (Preview)'
+ 'google/gemini-2.0-flash-exp': 'Gemini 2.0 Flash (Experimental)',
+ 'aistupidlevel/auto': 'AI Stupid Level (Auto)',
+ 'aistupidlevel/auto-coding': 'AI Stupid Level (Coding)',
+ 'aistupidlevel/auto-reasoning': 'AI Stupid Level (Reasoning)',
+ 'aistupidlevel/auto-creative': 'AI Stupid Level (Creative)',
+ 'aistupidlevel/auto-fastest': 'AI Stupid Level (Fastest)',
+ 'aistupidlevel/auto-cheapest': 'AI Stupid Level (Cheapest)'
} as Record,
// Model API configuration
@@ -193,4 +205,4 @@ export function getConfigValue(path: string): any {
return path.split('.').reduce((obj, key) => obj?.[key], appConfig as any);
}
-export default appConfig;
\ No newline at end of file
+export default appConfig;
diff --git a/package.json b/package.json
index 3fcb8be9..ad083896 100644
--- a/package.json
+++ b/package.json
@@ -7,10 +7,7 @@
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
- "lint": "next lint",
- "test:api": "node tests/api-endpoints.test.js",
- "test:code": "node tests/code-execution.test.js",
- "test:all": "npm run test:integration && npm run test:api && npm run test:code"
+ "lint": "next lint"
},
"dependencies": {
"@ai-sdk/anthropic": "^2.0.1",