Skip to content

Commit dec7cb1

Browse files
Juan Castañojuancastano
authored andcommitted
Added template and tweaked e2b logic
1 parent ee53480 commit dec7cb1

File tree

5 files changed

+98
-199
lines changed

5 files changed

+98
-199
lines changed

app/api/generate/route.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { NextRequest, NextResponse } from 'next/server';
33
import { generateApp } from '@/lib/openai';
44
import { repairCode } from '@/lib/benchify';
5-
import { createSandbox, prepareVueEnvironment, deployApp } from '@/lib/e2b';
5+
import { createSandbox } from '@/lib/e2b';
66
import { componentSchema } from '@/lib/schemas';
77
import { benchifyFileSchema } from '@/lib/schemas';
88

@@ -31,25 +31,16 @@ export async function POST(request: NextRequest) {
3131
// // Repair the generated code using Benchify's API
3232
// const repairedFiles = await repairCode(validatedFiles);
3333

34-
// // Set up E2B sandbox for preview
35-
let previewUrl = undefined;
36-
try {
37-
const sandbox = await createSandbox();
38-
await prepareVueEnvironment(sandbox);
39-
const { previewUrl: url } = await deployApp(sandbox, generatedFiles);
40-
previewUrl = url;
41-
} catch (error) {
42-
console.error('Error setting up preview:', error);
43-
}
34+
const { sbxId, template, url } = await createSandbox({ files: generatedFiles });
4435

45-
console.log("Preview URL: ", previewUrl);
36+
console.log("Preview URL: ", url);
4637

4738
// Return the results to the client
4839
return NextResponse.json({
4940
originalFiles: generatedFiles,
5041
// repairedFiles: repairedFiles,
51-
buildOutput: '', // We don't get build output from Benchify in our current setup
52-
previewUrl,
42+
// buildOutput: '', // We don't get build output from Benchify in our current setup
43+
previewUrl: url,
5344
});
5445
} catch (error) {
5546
console.error('Error generating app:', error);

lib/e2b.ts

Lines changed: 49 additions & 185 deletions
Original file line numberDiff line numberDiff line change
@@ -1,199 +1,63 @@
1-
// lib/e2b.ts
2-
import { Sandbox } from '@e2b/sdk';
3-
import { GeneratedFile, DeployResult } from '@/lib/types';
4-
1+
import { Sandbox } from '@e2b/code-interpreter';
2+
import { benchifyFileSchema } from './schemas';
3+
import { z } from 'zod';
54

65
const E2B_API_KEY = process.env.E2B_API_KEY;
76

87
if (!E2B_API_KEY) {
98
throw new Error('E2B_API_KEY is not set');
109
}
1110

12-
// Ensure path has a leading slash
13-
function normalizePath(path: string): string {
14-
return path.startsWith('/') ? path : `/${path}`;
15-
}
16-
17-
// Initialize E2B SDK
18-
export async function createSandbox() {
19-
try {
20-
const sandbox = await Sandbox.create({
21-
apiKey: E2B_API_KEY,
22-
});
23-
24-
return sandbox;
25-
} catch (error: any) {
26-
console.error('E2B Error Details:', {
27-
message: error.message,
28-
status: error.status,
29-
statusText: error.statusText,
30-
data: error.data,
31-
headers: error.headers,
32-
url: error.url
33-
});
34-
throw error;
35-
}
36-
}
37-
38-
// Set up a Vue environment with required dependencies
39-
export async function prepareVueEnvironment(sandbox: Sandbox) {
40-
try {
41-
// Create necessary directories
42-
await sandbox.filesystem.makeDir('/src');
43-
await sandbox.filesystem.makeDir('/src/components');
44-
45-
// Define base configuration files
46-
const packageJson = {
47-
name: "vue-app",
48-
version: "1.0.0",
49-
type: "module",
50-
scripts: {
51-
"dev": "vite",
52-
"build": "vue-tsc && vite build",
53-
"preview": "vite preview"
54-
},
55-
dependencies: {
56-
"vue": "^3.3.0",
57-
"vue-router": "^4.2.0",
58-
"pinia": "^2.1.0",
59-
"@vueuse/core": "^10.5.0"
60-
},
61-
devDependencies: {
62-
"@vitejs/plugin-vue": "^4.4.0",
63-
"typescript": "^5.2.0",
64-
"vite": "^4.5.0",
65-
"vue-tsc": "^1.8.0",
66-
"tailwindcss": "^3.3.0",
67-
"postcss": "^8.4.0",
68-
"autoprefixer": "^10.4.0"
11+
export async function createSandbox({ files }: { files: z.infer<typeof benchifyFileSchema> }) {
12+
const sandbox = await Sandbox.create('vue-dynamic-sandbox', { apiKey: E2B_API_KEY });
13+
14+
// Write all files to the sandbox at once
15+
await sandbox.files.write(
16+
files.map(file => ({
17+
path: `/home/user/app/${file.path}`,
18+
data: file.contents
19+
}))
20+
);
21+
22+
console.log("sandbox created", sandbox.sandboxId);
23+
24+
// Find package.json to check for new dependencies
25+
const packageJsonFile = files.find(file => file.path === 'package.json');
26+
if (packageJsonFile) {
27+
try {
28+
const packageJson = JSON.parse(packageJsonFile.contents);
29+
const dependencies = packageJson.dependencies || {};
30+
const devDependencies = packageJson.devDependencies || {};
31+
32+
// Filter out pre-installed dependencies (vue, tailwindcss, etc.)
33+
const preInstalled = ['vue', 'tailwindcss', 'autoprefixer', 'postcss', 'vite', '@vitejs/plugin-vue', '@vue/compiler-sfc'];
34+
35+
// Get new deps that need to be installed
36+
const newDeps = Object.keys(dependencies).filter(dep => !preInstalled.includes(dep));
37+
const newDevDeps = Object.keys(devDependencies).filter(dep => !preInstalled.includes(dep));
38+
39+
// Install only new dependencies if any exist
40+
if (newDeps.length > 0) {
41+
console.log("Installing new dependencies:", newDeps.join(", "));
42+
await sandbox.commands.run(`cd /home/user/app && npm install --legacy-peer-deps ${newDeps.join(' ')}`);
6943
}
70-
};
71-
72-
// Write initial configuration
73-
await sandbox.filesystem.write('/package.json', JSON.stringify(packageJson, null, 2));
74-
75-
await sandbox.filesystem.write('/vite.config.ts', `import { defineConfig } from 'vite'
76-
import vue from '@vitejs/plugin-vue'
77-
78-
export default defineConfig({
79-
plugins: [vue()],
80-
server: {
81-
host: true,
82-
port: 3000
83-
}
84-
})`);
85-
86-
// Install dependencies with legacy peer deps to avoid conflicts
87-
await sandbox.process.start({
88-
cmd: 'npm install --legacy-peer-deps',
89-
});
90-
91-
// Write Vue app files
92-
await sandbox.filesystem.write('/src/App.vue', `<template>
93-
<div class="min-h-screen">
94-
<router-view />
95-
</div>
96-
</template>
97-
98-
<script setup lang="ts">
99-
// App level setup
100-
</script>`);
10144

102-
await sandbox.filesystem.write('/tailwind.config.js', `/** @type {import('tailwindcss').Config} */
103-
export default {
104-
content: [
105-
"./index.html",
106-
"./src/**/*.{vue,js,ts,jsx,tsx}",
107-
],
108-
theme: {
109-
extend: {},
110-
},
111-
plugins: [],
112-
}`);
113-
114-
await sandbox.filesystem.write('/postcss.config.js', `export default {
115-
plugins: {
116-
tailwindcss: {},
117-
autoprefixer: {},
118-
},
119-
}`);
120-
121-
// Create index files
122-
await sandbox.filesystem.write('/index.html', `<!DOCTYPE html>
123-
<html lang="en">
124-
<head>
125-
<meta charset="UTF-8" />
126-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
127-
<title>Vue App</title>
128-
</head>
129-
<body>
130-
<div id="app"></div>
131-
<script type="module" src="/src/main.ts"></script>
132-
</body>
133-
</html>`);
134-
135-
await sandbox.filesystem.write('/src/main.ts', `import { createApp } from 'vue'
136-
import App from './App.vue'
137-
import './style.css'
138-
139-
createApp(App).mount('#app')`);
140-
141-
await sandbox.filesystem.write('/src/style.css', `@tailwind base;
142-
@tailwind components;
143-
@tailwind utilities;`);
144-
145-
return sandbox;
146-
} catch (error: any) {
147-
console.error('E2B Environment Setup Error:', {
148-
message: error.message,
149-
details: error.details,
150-
command: error.command,
151-
exitCode: error.exitCode,
152-
stdout: error.stdout,
153-
stderr: error.stderr
154-
});
155-
throw error;
156-
}
157-
}
158-
159-
// Deploy the app for preview
160-
export async function deployApp(sandbox: Sandbox, files: GeneratedFile[]): Promise<DeployResult> {
161-
try {
162-
// Write all the generated files
163-
for (const file of files) {
164-
const normalizedPath = normalizePath(file.path);
165-
const dirPath = normalizedPath.split('/').slice(0, -1).join('/');
166-
167-
if (dirPath && dirPath !== '/') {
168-
await sandbox.filesystem.makeDir(dirPath);
45+
if (newDevDeps.length > 0) {
46+
console.log("Installing new dev dependencies:", newDevDeps.join(", "));
47+
await sandbox.commands.run(`cd /home/user/app && npm install --legacy-peer-deps --save-dev ${newDevDeps.join(' ')}`);
16948
}
170-
171-
await sandbox.filesystem.write(normalizedPath, file.contents);
49+
} catch (error) {
50+
console.error("Error parsing package.json:", error);
17251
}
52+
}
17353

174-
console.log('Starting development server...');
175-
// Start the development server
176-
const process = await sandbox.process.start({
177-
cmd: 'npm run dev',
178-
});
54+
// Run the Vite app
55+
await sandbox.commands.run('cd /home/user/app && npx vite');
17956

180-
const previewUrl = `https://${sandbox.id}-3000.code.e2b.dev`;
181-
console.log('Preview URL generated:', previewUrl);
57+
return {
58+
sbxId: sandbox.sandboxId,
59+
template: 'vue-dynamic-sandbox',
60+
url: `https://${sandbox.getHost(5173)}`
61+
};
62+
}
18263

183-
// Return the URL for preview
184-
return {
185-
previewUrl,
186-
process,
187-
};
188-
} catch (error: any) {
189-
console.error('E2B Deployment Error:', {
190-
message: error.message,
191-
sandboxId: sandbox?.id,
192-
status: error.status,
193-
statusText: error.statusText,
194-
data: error.data,
195-
url: error.url
196-
});
197-
throw error;
198-
}
199-
}

templates/vue-template/e2b.Dockerfile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# You can use most Debian-based base images
2+
FROM node:21-slim
3+
4+
# Install dependencies and customize sandbox
5+
WORKDIR /home/user/app
6+
7+
# Pre-install common dependencies to speed up runtime
8+
RUN npm init -y
9+
RUN npm install --legacy-peer-deps vue@3
10+
RUN npm install --legacy-peer-deps --save-dev tailwindcss@3 autoprefixer@10 postcss@8 @vue/compiler-sfc@3 vite @vitejs/plugin-vue
11+
12+
# Set up basic Vite configuration for Vue
13+
RUN npx tailwindcss init
14+
COPY nuxt.config.ts /home/user/app/

templates/vue-template/e2b.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# This is a config for E2B sandbox template.
2+
# You can use template ID (e9devag07oqgiyid3zox) or template name (vue-dynamic-sandbox) to create a sandbox:
3+
4+
# Python SDK
5+
# from e2b import Sandbox, AsyncSandbox
6+
# sandbox = Sandbox("vue-dynamic-sandbox") # Sync sandbox
7+
# sandbox = await AsyncSandbox.create("vue-dynamic-sandbox") # Async sandbox
8+
9+
# JS SDK
10+
# import { Sandbox } from 'e2b'
11+
# const sandbox = await Sandbox.create('vue-dynamic-sandbox')
12+
13+
team_id = "35f2ed91-a6af-4e6c-a693-9e9e244fcdbd"
14+
dockerfile = "e2b.Dockerfile"
15+
template_name = "vue-dynamic-sandbox"
16+
template_id = "e9devag07oqgiyid3zox"

templates/vue-template/nuxt.config.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// https://nuxt.com/docs/api/configuration/nuxt-config
2+
// @ts-ignore Ignored to pass Vercel deployment
3+
export default defineNuxtConfig({
4+
compatibilityDate: '2024-04-03',
5+
devtools: { enabled: false },
6+
modules: ['@nuxtjs/tailwindcss'],
7+
vite: {
8+
server: {
9+
hmr: {
10+
protocol: 'wss'
11+
}
12+
}
13+
}
14+
})

0 commit comments

Comments
 (0)