@shopkit/sandbox
v0.1.0
Published
TypeScript client for BoxLite sandbox - live code preview with Vite dev server
Maintainers
Readme
@shopkit/sandbox
TypeScript client for BoxLite sandbox - live code preview with Vite dev server.
Overview
This package provides a TypeScript client for the BoxLite sandbox backend. It enables:
- Live Code Preview: Run React/Vite code in an isolated sandbox
- AI Integration: WebSocket connections for Claude agent interactions
- Screenshot Capture: Capture screenshots for AI self-correction
- Real-time Updates: Stream file changes, terminal output, and build errors
Installation
bun add @shopkit/sandboxPrerequisites
The BoxLite backend must be running:
# Clone the perfect-web-clone repository
git clone https://github.com/ericshang98/perfect-web-clone
cd perfect-web-clone
# Install dependencies
pip install -r backend/requirements.txt
playwright install chromium
# Start the backend
python backend/main.py
# Server runs on http://localhost:5100Usage
Basic Usage with BoxLiteClient
import { BoxLiteClient } from '@shopkit/sandbox';
const client = new BoxLiteClient({
baseUrl: 'http://localhost:5100'
});
// Create a sandbox
const sandbox = await client.createSandbox();
console.log('Sandbox ID:', sandbox.sandboxId);
// Write files
await client.writeFile('/src/App.tsx', `
export default function App() {
return <div className="p-4">Hello World</div>;
}
`);
// Start dev server
await client.startDevServer();
console.log('Preview URL:', sandbox.previewUrl); // http://localhost:8080
// Get screenshot for AI review
const screenshot = await client.getScreenshot();
console.log('Screenshot (base64):', screenshot?.substring(0, 50));
// Check for build errors
const errors = await client.getBuildErrors();
if (errors.length > 0) {
console.log('Build errors:', errors);
}Advanced Usage with SandboxManager
import { SandboxManager } from '@shopkit/sandbox';
const manager = new SandboxManager({
baseUrl: 'http://localhost:5100'
});
// Initialize sandbox
await manager.initialize();
// Connect to agent WebSocket
manager.connectAgentWebSocket({
onStateUpdate: (state) => {
console.log('Sandbox state:', state.status);
console.log('Preview URL:', state.preview_url);
},
onTextDelta: (delta) => {
// Stream AI response
process.stdout.write(delta);
},
onToolCall: (tool) => {
console.log('AI executing tool:', tool.name);
},
onToolResult: (result) => {
console.log('Tool result:', result.success ? 'Success' : 'Failed');
},
onError: (error) => {
console.error('Error:', error);
},
onDone: () => {
console.log('AI finished');
}
});
// Send a message to the AI agent
manager.sendChatMessage('Create a product card component with hover effects');
// Write files directly
await manager.writeFiles({
'/src/components/ProductCard.tsx': productCardCode,
'/src/styles/card.css': cardStyles
});
// Get preview screenshot
const screenshot = await manager.getScreenshot();
// Clean up
await manager.destroy();React Hook Integration
import { useEffect, useState } from 'react';
import { SandboxManager, SandboxState } from '@shopkit/sandbox';
function usePreview(code: string) {
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const [manager] = useState(() => new SandboxManager());
useEffect(() => {
let mounted = true;
async function setup() {
try {
await manager.initialize();
await manager.writeFile('/src/App.tsx', code);
const result = await manager.startDevServer();
if (mounted && result.success) {
setPreviewUrl(result.preview_url || 'http://localhost:8080');
}
} catch (e) {
if (mounted) setError(e.message);
}
}
setup();
return () => {
mounted = false;
manager.destroy();
};
}, [code]);
return { previewUrl, error };
}API Reference
BoxLiteClient
| Method | Description |
|--------|-------------|
| createSandbox(id?) | Create a new sandbox |
| reconnectSandbox(id) | Reconnect to existing sandbox |
| getSandboxState(id?) | Get current sandbox state |
| deleteSandbox(id?) | Delete a sandbox |
| writeFile(path, content, id?) | Write a file |
| writeFiles(files, id?) | Write multiple files |
| readFile(path, id?) | Read a file |
| listFiles(path?, id?) | List directory contents |
| runCommand(cmd, opts?, id?) | Run shell command |
| startDevServer(id?) | Start Vite dev server |
| stopDevServer(id?) | Stop dev server |
| getVisualSummary(id?) | Get visual summary with screenshot |
| getScreenshot(id?) | Get screenshot as base64 |
| getBuildErrors(source?, id?) | Get build/runtime errors |
SandboxManager
Extends BoxLiteClient with WebSocket management and event handling.
| Method | Description |
|--------|-------------|
| initialize(id?) | Initialize new sandbox |
| reconnect(id) | Reconnect to sandbox |
| destroy() | Clean up sandbox |
| connectWebSocket(handlers?) | Connect for real-time updates |
| connectAgentWebSocket(handlers?) | Connect for AI interactions |
| sendChatMessage(msg, sourceId?) | Send message to AI agent |
| setEventHandlers(handlers) | Set event callbacks |
Event Handlers
interface SandboxEventHandlers {
onStateUpdate?: (state: SandboxState) => void;
onTerminalOutput?: (data: { terminal_id: string; data: string }) => void;
onTextDelta?: (delta: string) => void;
onText?: (content: string) => void;
onToolCall?: (data: { id: string; name: string; input: object }) => void;
onToolResult?: (data: { id: string; success: boolean; result: string }) => void;
onError?: (error: string) => void;
onDone?: () => void;
onConnect?: () => void;
onDisconnect?: () => void;
}Environment Variables
Client Configuration (Admin App)
| Variable | Default | Description |
|----------|---------|-------------|
| BOXLITE_URL | http://localhost:5100 | BoxLite server URL (server-side) |
| NEXT_PUBLIC_BOXLITE_URL | http://localhost:5100 | BoxLite server URL (client-side) |
Backend Configuration (BoxLite Server)
| Variable | Default | Description |
|----------|---------|-------------|
| PORT | 5100 | BoxLite server port |
| BOXLITE_DEV_PORT | 8080 | Vite dev server port |
| BOXLITE_SANDBOX_DIR | /tmp/boxlite-sandboxes | Sandbox directory |
| BOXLITE_SINGLETON_MODE | true | Reuse single sandbox |
| ANTHROPIC_API_KEY | - | API key for AI features |
| LOG_LEVEL | INFO | Logging level |
Production Deployment
For production use, the BoxLite backend must be deployed as a separate service:
┌──────────────────┐ ┌──────────────────────────┐
│ Admin App │ │ BoxLite Backend │
│ (Next.js) │◄────►│ (Python/FastAPI) │
│ Vercel/Railway │ REST │ Railway/Render/Docker │
│ │ + │ │
│ │ WS │ │
└──────────────────┘ └──────────────────────────┘Quick Deploy to Railway
Clone BoxLite backend:
git clone https://github.com/ericshang98/perfect-web-clone cd perfect-web-clone/backendDeploy to Railway:
railway init railway upSet environment variables in Railway dashboard
Configure admin app:
NEXT_PUBLIC_BOXLITE_URL=https://your-boxlite.up.railway.app
Docker Deployment
See BoxLite Production Deployment Guide for detailed Docker, Railway, Render, and AWS/GCP deployment instructions.
How It Works
- Admin App calls BoxLiteClient to create a sandbox
- BoxLite Backend creates an isolated environment with Vite
- Files are written to the sandbox via REST API
- Vite dev server starts, providing hot-reload preview
- WebSocket connection streams real-time updates
- AI agent can capture screenshots to verify generated code
License
MIT
