Paste broken code, an AI agent fixes it inside an isolated cloud sandbox, runs it, and streams the results back in real-time.
SSE Stream (real-time events)
React Frontend <───────────────────────────────── Express Backend
│ │
│ POST /api/fix-code │
└───────────────────────────────> │
│
1. Create Vercel Sandbox
2. Install deps (openai, tsx)
3. Write config.json + agent-script.ts
4. Execute: npx tsx agent-script.ts
│
▼
┌─────────────────┐
│ Vercel Sandbox │
│ (Node 22, 2CPU) │
│ │
│ Agent Loop: │
│ ┌─────────────┐ │
│ │ Gemini API │ │
│ │ (tool calls)│ │
│ └──────┬──────┘ │
│ │ │
│ write_file │
│ read_file │
│ run_command │
│ list_files │
│ │ │
│ Retry up to 10x │
│ until code works │
│ │ │
│ output.json │
└────────┬────────┘
│
5. Read output.json
6. Stream result via SSE
7. Destroy sandbox
The agent runs entirely inside the sandbox. The backend orchestrates the lifecycle — it never executes user code on the host machine. The frontend receives real-time progress updates via Server-Sent Events as each step completes.
User code runs in a disposable, isolated cloud environment. Each request gets a fresh sandbox (Node 22, 2 vCPUs, 5-minute lifetime) that is destroyed after use. There's no risk of user code affecting the host server, persisting files, or accessing secrets.
The agent script is self-contained and executes within the same isolation boundary as the user code. This means the AI's tool calls (write_file, run_command, etc.) operate directly on the sandbox filesystem — no network round-trips between the agent and sandbox for each tool call. It's faster and more secure.
Server-Sent Events are simpler than WebSockets for this use case — we only need server-to-client streaming. SSE works over standard HTTP, needs no special infrastructure, and reconnects automatically. The POST-based SSE pattern (using fetch + ReadableStream on the client) avoids the GET-only limitation of EventSource.
The agent uses structured tool calls (write_file, read_file, run_command, list_files) instead of asking the LLM to output bash commands in text. Tool calling gives us:
- Type-safe arguments (validated by the model)
- Reliable parsing (no regex on LLM text output)
- Clear execution boundaries (each tool call is explicit)
- A feedback loop (tool results feed back into the conversation)
| Layer | Technology | Why |
|---|---|---|
| Frontend | React 19 + TypeScript + Vite | Fast dev server, type safety, minimal setup |
| Backend | Express + TypeScript | Lightweight, widely understood, good SSE support |
| Sandbox | Vercel Sandbox (@vercel/sandbox) |
Disposable cloud environments with filesystem + command execution |
| AI Model | Gemini 2.5 Flash Lite | Fast, cheap, good tool-calling support via OpenAI-compatible API |
| Streaming | Server-Sent Events (SSE) | One-way server→client streaming, simpler than WebSockets |
backend/
agent-script.ts # Self-contained agent (runs inside sandbox)
src/
server.ts # Entry point — Express app, middleware, listen
config/
index.ts # Environment variables (PORT, API keys, CORS)
routes/
fix-code.routes.ts # Route definitions (URL → controller mapping)
controllers/
fix-code.controller.ts # Request handling, validation, SSE setup
services/
agent.service.ts # Agent orchestration (install deps, run script, parse output)
sandbox.service.ts # Vercel Sandbox lifecycle (create, read, write, run, destroy)
sse.service.ts # SSE state management (active connections, event broadcasting)
types/
index.ts # Shared TypeScript interfaces
frontend/
src/
App.tsx # Main layout + state management
api.ts # SSE stream client (POST + ReadableStream)
types.ts # Shared TypeScript interfaces
App.css # Full application styles (dark theme)
index.css # Global CSS reset
components/
InputPanel.tsx # Code textarea, instruction input, language toggle
EditsPanel.tsx # Fixed code display with copy button
OutputPanel.tsx # Terminal-style execution output
ActivityLog.tsx # Real-time event feed with color-coded entries
- Node.js 20+
- A Gemini API key
- A Vercel account with Sandbox access
git clone <repo-url>
cd ai-code-exec-sandbox
# Backend
cd backend
npm install
# Frontend
cd ../frontend
npm installCreate backend/.env.local:
GEMINI_API_KEY=your_gemini_api_keyFor Vercel Sandbox auth, run inside backend/:
npx vercel link
npx vercel env pullOr set credentials manually in .env.local:
VERCEL_TOKEN=your_vercel_token
VERCEL_TEAM_ID=your_team_id
VERCEL_PROJECT_ID=your_project_id# Terminal 1 — Backend (port 3001)
cd backend
npm run dev
# Terminal 2 — Frontend (port 1234)
cd frontend
npm run dev| Variable | Required | Description |
|---|---|---|
GEMINI_API_KEY |
Yes | API key for Gemini model |
VERCEL_TOKEN |
Yes | Vercel authentication token (or use vercel link) |
VERCEL_TEAM_ID |
Yes | Vercel team ID |
VERCEL_PROJECT_ID |
Yes | Vercel project ID |
PORT |
No | Backend port (default: 3001) |
CORS_ORIGIN |
No | Allowed frontend origin (default: http://localhost:1234) |
BACKEND_PUBLIC_URL |
No | Public URL for sandbox→backend event streaming. Not needed for local dev — agent iteration events are parsed from stdout instead |
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/fix-code |
Main endpoint. Accepts { code, instruction, language }. Returns SSE stream with real-time events, ending with a result event containing { original_code, fixed_code, output, explanation } |
POST |
/api/agent-event |
Internal endpoint for sandbox→backend event forwarding. Accepts { requestId, event } |
GET |
/health |
Health check. Returns { status: "ok" } |
Events streamed to the frontend during a fix-code request:
| Event | Description |
|---|---|
status |
General status message |
sandbox_created |
Sandbox provisioned (includes sandbox ID) |
installing_deps |
Installing npm packages in sandbox |
deps_installed |
Dependencies installed successfully |
agent_started |
Agent script execution started |
agent_iteration |
Agent iteration progress (e.g., 1/10) |
tool_call |
Agent invoked a tool (write_file, run_command, etc.) |
agent_completed |
Agent finished processing |
result |
Final result with fixed code and output |
sandbox_destroyed |
Sandbox cleaned up |
error |
Error occurred during processing |
- Paste broken code in the input panel
- Enter an instruction (e.g., "fix the bug", "add error handling")
- Select the language (Python or JavaScript)
- Click Fix Code
- Watch the activity log for real-time progress
- See the fixed code (middle panel) and execution output (right panel)