Skip to content

Commit 01fea09

Browse files
updated file functions
1 parent bf16c34 commit 01fea09

File tree

15 files changed

+242
-287
lines changed

15 files changed

+242
-287
lines changed

app/api/files/content/route.ts

Lines changed: 44 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,65 @@
1-
import { NextRequest, NextResponse } from 'next/server'
1+
import { NextResponse } from 'next/server'
22
import { Sandbox } from '@e2b/code-interpreter'
33

4-
export async function GET(request: NextRequest) {
5-
const searchParams = request.nextUrl.searchParams
6-
const sandboxId = searchParams.get('sandboxId')
7-
const path = searchParams.get('path')
4+
const E2B_API_KEY = process.env.E2B_API_KEY
5+
if (!E2B_API_KEY) {
6+
throw new Error('E2B_API_KEY environment variable not found')
7+
}
88

9-
if (!sandboxId || !path) {
10-
return NextResponse.json(
11-
{ error: 'sandboxId and path are required' },
12-
{ status: 400 },
13-
)
14-
}
9+
const sandboxTimeout = 10 * 60 * 1000
1510

16-
try {
17-
const sandbox = await Sandbox.connect(sandboxId)
18-
const content = await sandbox.files.read(path)
19-
return new NextResponse(content, {
20-
headers: {
21-
'Content-Type': 'text/plain',
22-
},
23-
})
24-
} catch (error) {
25-
console.error('Error fetching file content:', error)
26-
return NextResponse.json(
27-
{ error: 'Failed to fetch file content' },
28-
{ status: 500 },
29-
)
30-
}
11+
async function getSandbox(sessionID: string) {
12+
const sandbox = await Sandbox.create({
13+
apiKey: E2B_API_KEY,
14+
metadata: {
15+
sessionID,
16+
},
17+
timeoutMs: sandboxTimeout,
18+
})
19+
return sandbox
3120
}
3221

33-
export async function POST(request: NextRequest) {
34-
const {
35-
sandboxId,
36-
path,
37-
content,
38-
}: { sandboxId: string; path: string; content: string } = await request.json()
22+
export async function GET(req: Request) {
23+
try {
24+
const { searchParams } = new URL(req.url)
25+
const sessionID = searchParams.get('sessionID')
26+
const path = searchParams.get('path')
3927

40-
if (!sandboxId || !path || content === undefined) {
41-
return NextResponse.json(
42-
{ error: 'sandboxId, path, and content are required' },
43-
{ status: 400 },
44-
)
45-
}
28+
if (!sessionID || !path) {
29+
return NextResponse.json(
30+
{ error: 'sessionID and path are required' },
31+
{ status: 400 },
32+
)
33+
}
4634

47-
try {
48-
const sandbox = await Sandbox.connect(sandboxId)
49-
await sandbox.files.write(path, content)
50-
return NextResponse.json({ success: true })
51-
} catch (error) {
52-
console.error('Error saving file content:', error)
35+
const sandbox = await getSandbox(sessionID)
36+
const content = await sandbox.files.read(path)
37+
return NextResponse.json({ content })
38+
} catch (error: any) {
5339
return NextResponse.json(
54-
{ error: 'Failed to save file content' },
40+
{ error: error.message || 'An unexpected error occurred' },
5541
{ status: 500 },
5642
)
5743
}
5844
}
5945

60-
export async function DELETE(request: NextRequest) {
61-
const searchParams = request.nextUrl.searchParams
62-
const sandboxId = searchParams.get('sandboxId')
63-
const path = searchParams.get('path')
46+
export async function POST(req: Request) {
47+
try {
48+
const { sessionID, path, content } = await req.json()
6449

65-
if (!sandboxId || !path) {
66-
return NextResponse.json(
67-
{ error: 'sandboxId and path are required' },
68-
{ status: 400 },
69-
)
70-
}
50+
if (!sessionID || !path || content === undefined) {
51+
return NextResponse.json(
52+
{ error: 'sessionID, path and content are required' },
53+
{ status: 400 },
54+
)
55+
}
7156

72-
try {
73-
const sandbox = await Sandbox.connect(sandboxId)
74-
await sandbox.files.remove(path)
57+
const sandbox = await getSandbox(sessionID)
58+
await sandbox.files.write(path, content)
7559
return NextResponse.json({ success: true })
76-
} catch (error) {
77-
console.error('Error deleting file:', error)
60+
} catch (error: any) {
7861
return NextResponse.json(
79-
{ error: 'Failed to delete file' },
62+
{ error: error.message || 'An unexpected error occurred' },
8063
{ status: 500 },
8164
)
8265
}

app/api/files/route.ts

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,33 +28,41 @@ async function listFilesRecursively(
2828
return nodes
2929
}
3030

31+
const E2B_API_KEY = process.env.E2B_API_KEY
32+
if (!E2B_API_KEY) {
33+
throw new Error('E2B_API_KEY environment variable not found')
34+
}
35+
36+
const sandboxTimeout = 10 * 60 * 1000
37+
38+
async function getSandbox(sessionID: string) {
39+
const sandbox = await Sandbox.create({
40+
apiKey: E2B_API_KEY,
41+
metadata: {
42+
sessionID,
43+
},
44+
timeoutMs: sandboxTimeout,
45+
})
46+
return sandbox
47+
}
48+
3149
export async function GET(request: NextRequest) {
3250
const searchParams = request.nextUrl.searchParams
33-
const sandboxId = searchParams.get('sandboxId')
51+
const sessionID = searchParams.get('sessionID')
3452

35-
let sandbox: Sandbox | undefined
53+
if (!sessionID) {
54+
return NextResponse.json(
55+
{ error: 'sessionID is required' },
56+
{ status: 400 },
57+
)
58+
}
3659

3760
try {
38-
if (sandboxId) {
39-
sandbox = await Sandbox.connect(sandboxId)
40-
} else {
41-
sandbox = await Sandbox.create('nextjs-developer')
42-
}
43-
61+
const sandbox = await getSandbox(sessionID)
4462
const fileTree = await listFilesRecursively(sandbox, '/')
45-
46-
// Only kill the sandbox if it was created in this request
47-
if (!sandboxId && sandbox) {
48-
await sandbox.kill()
49-
}
50-
5163
return NextResponse.json(fileTree)
5264
} catch (error) {
5365
console.error('Error fetching file tree:', error)
54-
// Ensure sandbox is killed in case of an error during file listing
55-
if (!sandboxId && sandbox) {
56-
await sandbox.kill()
57-
}
5866
return NextResponse.json(
5967
{ error: 'Failed to fetch file tree' },
6068
{ status: 500 },

app/page.tsx

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { LLMModelConfig } from '@/lib/models'
1616
import modelsList from '@/lib/models.json'
1717
import { FragmentSchema, fragmentSchema as schema } from '@/lib/schema'
1818
import { createBrowserClient } from '@/lib/supabase-client'
19-
import templates, { TemplateId } from '@/lib/templates'
19+
import templates, { TemplateId, Templates } from '@/lib/templates'
2020
import { ExecutionResult } from '@/lib/types'
2121
import { cn } from '@/lib/utils'
2222
import { DeepPartial } from 'ai'
@@ -51,16 +51,22 @@ export default function Home() {
5151
const [authView, setAuthView] = useState<ViewType>('sign_in')
5252
const [isRateLimited, setIsRateLimited] = useState(false)
5353
const [errorMessage, setErrorMessage] = useState('')
54+
const [executionResult, setExecutionResult] = useState<ExecutionResult | undefined>(undefined)
5455

5556
const [currentProject, setCurrentProject] = useState<Project | null>(null)
5657
const [isLoadingProject, setIsLoadingProject] = useState(false)
5758

5859
const { session, userTeam } = useAuth(setAuthDialog, setAuthView)
5960

60-
const { executeCode } = useEnhancedChat({
61+
const currentTemplate =
62+
selectedTemplate === 'auto'
63+
? templates
64+
: ({ [selectedTemplate]: templates[selectedTemplate] } as Templates)
65+
66+
const { executeCode: enhancedExecuteCode } = useEnhancedChat({
6167
userID: session?.user?.id,
6268
teamID: userTeam?.id,
63-
template: templates,
69+
template: currentTemplate,
6470
model: modelsList.models.find(m => m.id === languageModel.model)!,
6571
config: languageModel,
6672
})
@@ -82,10 +88,6 @@ export default function Home() {
8288
const currentModel = filteredModels.find(
8389
(model) => model.id === languageModel.model,
8490
)
85-
const currentTemplate =
86-
selectedTemplate === 'auto'
87-
? templates
88-
: { [selectedTemplate]: templates[selectedTemplate] }
8991
const lastMessage = messages[messages.length - 1]
9092

9193
useEffect(() => {
@@ -336,14 +338,9 @@ export default function Home() {
336338
setCurrentPreview({ fragment: undefined, result: undefined })
337339
}
338340

339-
async function handleSaveFile(path: string, content: string) {
340-
await fetch('/api/files/content', {
341-
method: 'POST',
342-
headers: {
343-
'Content-Type': 'application/json',
344-
},
345-
body: JSON.stringify({ path, content }),
346-
})
341+
const executeCode = async (code: string) => {
342+
const result = await enhancedExecuteCode(code)
343+
setExecutionResult(result)
347344
}
348345

349346
return (
@@ -430,21 +427,21 @@ export default function Home() {
430427
/>
431428
</ChatInput>
432429
</div>
433-
<Preview
430+
<Preview
434431
teamID={userTeam?.id}
435432
accessToken={session?.access_token}
436433
selectedTab={currentTab}
437434
onSelectedTabChange={setCurrentTab}
438435
isChatLoading={isLoading}
439436
isPreviewLoading={isPreviewLoading}
440437
fragment={fragment}
441-
result={result as ExecutionResult}
438+
result={executionResult || result as ExecutionResult}
442439
onClose={() => setFragment(undefined)}
443440
code={fragment?.code || ''}
444441
executeCode={executeCode}
445-
selectedFile={selectedFile}
446-
onSave={handleSaveFile}
447-
/>
442+
selectedFile={selectedFile} onSave={function (path: string, content: string): void {
443+
throw new Error('Function not implemented.')
444+
} } />
448445
</div>
449446
</main>
450447
)

components/chat-picker.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,4 @@ export function ChatPicker({
106106
</div>
107107
</div>
108108
)
109-
}
109+
}

components/chat-settings.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,4 +202,4 @@ export function ChatSettings({
202202
</DropdownMenuContent>
203203
</DropdownMenu>
204204
)
205-
}
205+
}

components/code-editor.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Editor, { Monaco } from '@monaco-editor/react'
22
import { useRef } from 'react'
33
import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'
44
import { useTheme } from 'next-themes'
5+
import './code-theme.css'
56

67
export function CodeEditor({
78
code,

components/enhanced-code-interpreter.tsx

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
} from 'lucide-react'
2222
import dynamic from 'next/dynamic'
2323
import Image from 'next/image'
24-
import { useState, useRef, useCallback } from 'react'
24+
import { useState, useRef, useCallback, useEffect } from 'react'
2525

2626
// Dynamically import Monaco Editor to avoid SSR issues
2727
const MonacoEditor = dynamic(() => import('@monaco-editor/react'), {
@@ -296,18 +296,27 @@ export function EnhancedCodeInterpreter({
296296
}: {
297297
result?: ExecutionResultInterpreter
298298
code: string
299-
executeCode: (code: string) => Promise<void>
299+
executeCode: (code: string) => Promise<any>
300300
}) {
301301
const [cells, setCells] = useState<CodeCell[]>([
302302
{
303303
id: '1',
304304
code: initialCode || '# Welcome to the Enhanced Code Interpreter\nprint("Hello, World!")',
305-
results: result?.cellResults,
306-
stdout: result?.stdout,
307-
stderr: result?.stderr
308305
}
309306
])
310307

308+
useEffect(() => {
309+
if (result) {
310+
setCells(cells.map(cell => ({
311+
...cell,
312+
results: result.cellResults,
313+
stdout: result.stdout,
314+
stderr: result.stderr,
315+
error: result.error,
316+
})))
317+
}
318+
}, [result])
319+
311320
const [activeTab, setActiveTab] = useState('notebook')
312321

313322
const addCell = () => {
@@ -334,19 +343,22 @@ export function EnhancedCodeInterpreter({
334343
const cell = cells.find(c => c.id === id)
335344
if (!cell) return
336345

337-
// Mark cell as executing
338346
setCells(cells.map(c =>
339-
c.id === id ? { ...c, isExecuting: true, error: undefined } : c
347+
c.id === id ? { ...c, isExecuting: true, error: undefined, results: [], stdout: [], stderr: [] } : c
340348
))
341349

342350
try {
343-
// Execute the code
344-
await executeCode(cell.code)
351+
const result = await executeCode(cell.code)
345352

346-
// For now, we'll need to handle the result through the existing system
347-
// In a real implementation, we'd want to get the result directly
348353
setCells(cells.map(c =>
349-
c.id === id ? { ...c, isExecuting: false } : c
354+
c.id === id ? {
355+
...c,
356+
isExecuting: false,
357+
results: result.results,
358+
stdout: result.stdout,
359+
stderr: result.stderr,
360+
error: result.error,
361+
} : c
350362
))
351363
} catch (error) {
352364
setCells(cells.map(c =>
@@ -464,4 +476,4 @@ export function EnhancedCodeInterpreter({
464476
</Tabs>
465477
</div>
466478
)
467-
}
479+
}

0 commit comments

Comments
 (0)