npm package: @shopkit/sandbox
Description: TypeScript client for BoxLite sandbox - live code preview with Vite dev server
Author: Shopkit Team
Latest version: 0.1.0

# @shopkit/sandbox

TypeScript client for BoxLite sandbox - live code preview with Vite dev server.

## Overview

This package provides a TypeScript client for the [BoxLite](https://github.com/ericshang98/perfect-web-clone) 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

```bash
bun add @shopkit/sandbox
```

## Prerequisites

The BoxLite backend must be running:

```bash
# 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:5100
```

## Usage

### Basic Usage with BoxLiteClient

```typescript
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

```typescript
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

```typescript
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

```typescript
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

1. Clone BoxLite backend:
   ```bash
   git clone https://github.com/ericshang98/perfect-web-clone
   cd perfect-web-clone/backend
   ```

2. Deploy to Railway:
   ```bash
   railway init
   railway up
   ```

3. Set environment variables in Railway dashboard

4. Configure admin app:
   ```env
   NEXT_PUBLIC_BOXLITE_URL=https://your-boxlite.up.railway.app
   ```

### Docker Deployment

See [BoxLite Production Deployment Guide](../../docs/deployment/boxlite-production.md) for detailed Docker, Railway, Render, and AWS/GCP deployment instructions.

## How It Works

1. **Admin App** calls BoxLiteClient to create a sandbox
2. **BoxLite Backend** creates an isolated environment with Vite
3. **Files are written** to the sandbox via REST API
4. **Vite dev server** starts, providing hot-reload preview
5. **WebSocket connection** streams real-time updates
6. **AI agent** can capture screenshots to verify generated code

## License

MIT