Skip to content

Commit eef0e5b

Browse files
Fix terminal command execution
- Auto-replace pnpm with npm (pnpm not available in E2B sandboxes) - Add helpful error messages for command not found errors - Improve error handling with proper exit codes - Return 200 status for command errors to display in UI - Better extraction of error information from CommandExitError
1 parent 1d1f883 commit eef0e5b

1 file changed

Lines changed: 42 additions & 12 deletions

File tree

app/api/terminal/route.ts

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,19 @@ export const runtime = 'nodejs'
66
export const dynamic = 'force-dynamic'
77

88
export async function POST(req: NextRequest) {
9+
let workingDirectory = '/home/user'
10+
911
try {
10-
const {
11-
command,
12-
sbxId,
13-
workingDirectory = '/home/user',
12+
const {
13+
command,
14+
sbxId,
15+
workingDirectory: wd = '/home/user',
1416
teamID,
15-
accessToken
17+
accessToken
1618
} = await req.json()
1719

20+
workingDirectory = wd
21+
1822
if (!command || !sbxId) {
1923
return NextResponse.json(
2024
{ error: 'Missing required parameters' },
@@ -33,12 +37,25 @@ export async function POST(req: NextRequest) {
3337
: {}),
3438
})
3539

36-
const fullCommand = `cd "${workingDirectory}" && ${command}`
37-
40+
// Replace pnpm with npm in commands since pnpm isn't available in E2B sandboxes
41+
const sanitizedCommand = command.replace(/\bpnpm\b/g, 'npm')
42+
const fullCommand = `cd "${workingDirectory}" && ${sanitizedCommand}`
43+
3844
const result = await sandbox.commands.run(fullCommand, {
3945
timeoutMs: 30000,
4046
})
4147

48+
// If command failed with 127 (command not found), provide helpful message
49+
if (result.exitCode === 127) {
50+
const commandName = sanitizedCommand.split(' ')[0]
51+
return NextResponse.json({
52+
stdout: result.stdout,
53+
stderr: result.stderr || `Command '${commandName}' not found. Available commands: ls, cd, pwd, cat, echo, node, npm, python3, git`,
54+
exitCode: result.exitCode,
55+
workingDirectory,
56+
})
57+
}
58+
4259
return NextResponse.json({
4360
stdout: result.stdout,
4461
stderr: result.stderr,
@@ -48,13 +65,26 @@ export async function POST(req: NextRequest) {
4865

4966
} catch (error: any) {
5067
console.error('Terminal command error:', error)
51-
68+
69+
// Extract useful error information
70+
let errorMessage = error.message || 'Failed to execute command'
71+
let stderr = errorMessage
72+
73+
// If it's a CommandExitError, extract the actual error
74+
if (error.result) {
75+
stderr = error.result.stderr || error.result.error || errorMessage
76+
errorMessage = `Command failed with exit code ${error.result.exitCode}`
77+
}
78+
5279
return NextResponse.json(
53-
{
54-
error: error.message || 'Failed to execute command',
55-
stderr: error.message || 'Command execution failed'
80+
{
81+
error: errorMessage,
82+
stderr: stderr,
83+
stdout: error.result?.stdout || '',
84+
exitCode: error.result?.exitCode || 1,
85+
workingDirectory,
5686
},
57-
{ status: 500 }
87+
{ status: 200 } // Return 200 so the UI can display the error properly
5888
)
5989
}
6090
}

0 commit comments

Comments
 (0)