npm package: @meistrari/sandbox-sdk
Description: SDK for Sandbox - a secure code execution service.
Latest version: 1.2.0

# @meistrari/sandbox-sdk

SDK for Sandbox - a secure code execution service.

## Installation

```bash
npm install @meistrari/sandbox-sdk
# or
bun add @meistrari/sandbox-sdk
```

## Quick Start

```typescript
import { sandbox, DataTokenAuthStrategy } from '@meistrari/sandbox-sdk'

// Create client
const client = sandbox({
    apiUrl: 'https://sandbox.meistrari.com',
    auth: new DataTokenAuthStrategy(dataToken),
})

// Execute code
const run = await client.createRunAndWait({
    code: 'console.log("Hello, World!")',
    language: 'typescript',
})

console.log(run.stdout) // "Hello, World!"
```

## Usage

### Simple Code Execution

```typescript
// One-shot execution (no persistent sandbox)
const run = await client.createRunAndWait({
    code: `
    const result = 1 + 1
    console.log(result)
  `,
    language: 'typescript',
})

console.log(run.stdout) // "2"
console.log(run.exitCode) // 0
console.log(run.isSuccess) // true
```

### Persistent Sandbox

```typescript
import { Sandbox } from '@meistrari/sandbox-sdk'

// Create a persistent sandbox
const sandbox = await Sandbox.create(client, {
    memSize: 512,
    cpuSize: 1.0,
})

// Execute multiple commands in the same sandbox
const result1 = await sandbox.runCode('const x = 42', { language: 'typescript' })
const result2 = await sandbox.runCode('console.log(x)', { language: 'typescript' })

console.log(result2.stdout) // "42"

// Cleanup when done
await sandbox.kill()
```

### File Operations

```typescript
const sandbox = await client.createSandbox()

// Write files
await sandbox.files.write('/home/user/config.json', '{"key": "value"}')
await sandbox.files.write('/home/user/data.bin', binaryData) // Uint8Array

// Read files
const text = await sandbox.files.read('/home/user/config.json')
const binary = await sandbox.files.read('/home/user/data.bin', { format: 'blob' })

// Check if file exists
const exists = await sandbox.files.exists('/home/user/config.json')

// List files
const files = await sandbox.files.list('/home/user')

// Remove files
await sandbox.files.remove('/home/user/config.json')
```

### Shell Commands

```typescript
const sandbox = await client.createSandbox()

// Run shell commands
const { stdout, stderr, exitCode } = await sandbox.commands.run('ls -la')
console.log(stdout)

// Install packages
await sandbox.commands.run('bun add lodash')
```

### Using Libraries

```typescript
const sandbox = await client.createSandbox()

// Execute code with npm libraries (installed automatically)
const result = await sandbox.runCode(`
  import _ from 'lodash'
  import { z } from 'zod'

  const schema = z.object({ name: z.string(), age: z.number() })
  const data = { name: 'John', age: 30, extra: 'ignored' }

  console.log(_.pick(data, ['name', 'age']))
  console.log(schema.parse(data))
`, {
    language: 'typescript',
    libraries: ['lodash', 'zod'],
})

console.log(result.stdout)
// { name: 'John', age: 30 }
// { name: 'John', age: 30 }
```

### Using Environment Variables

```typescript
const sandbox = await client.createSandbox()

const result = await sandbox.runCode(`
  console.log('API Key:', process.env.API_KEY)
  console.log('Environment:', process.env.NODE_ENV)
`, {
    language: 'typescript',
    envs: {
        API_KEY: 'secret-key-123',
        NODE_ENV: 'production',
    },
})
```

### Using Input Files

```typescript
const sandbox = await client.createSandbox()

// Execute code with input files provided inline
const result = await sandbox.runCode(`
  import fs from 'fs'

  const config = JSON.parse(fs.readFileSync('/workspace/user-code/config.json', 'utf8'))
  const data = fs.readFileSync('/workspace/user-code/data.csv', 'utf8')

  console.log('Config:', config)
  console.log('Data lines:', data.split('\\n').length)
`, {
    language: 'typescript',
    files: [
        {
            name: 'config.json',
            content: JSON.stringify({ apiUrl: 'https://api.example.com', timeout: 5000 }),
            encoding: 'utf8',
        },
        {
            name: 'data.csv',
            content: 'name,age\\nJohn,30\\nJane,25',
            encoding: 'utf8',
        },
    ],
})
```

### Complete Example (Libraries + Envs + Files)

```typescript
const sandbox = await client.createSandbox({ memSize: 512 })

const result = await sandbox.runCode(`
  import Papa from 'papaparse'
  import fs from 'fs'

  const csvData = fs.readFileSync('/workspace/user-code/users.csv', 'utf8')
  const parsed = Papa.parse(csvData, { header: true })

  const apiKey = process.env.PARSER_API_KEY
  console.log('Using API Key:', apiKey.substring(0, 4) + '...')
  console.log('Parsed records:', parsed.data.length)
  console.log('First record:', JSON.stringify(parsed.data[0]))
`, {
    language: 'typescript',
    libraries: ['papaparse'],
    envs: {
        PARSER_API_KEY: 'pk_live_abc123xyz',
    },
    files: [
        {
            name: 'users.csv',
            content: 'id,name,email\\n1,John,john@example.com\\n2,Jane,jane@example.com',
            encoding: 'utf8',
        },
    ],
})

await sandbox.kill()
```

### Streaming Output

```typescript
const result = await sandbox.runCode(longRunningScript, {
    language: 'typescript',
    onStdout: ({ line }) => console.log('[stdout]', line),
    onStderr: ({ line }) => console.log('[stderr]', line),
    onError: error => console.error('[error]', error),
})
```

### Connect to Existing Sandbox

```typescript
// Reconnect to a previously created sandbox
const sandbox = await Sandbox.connect(client, existingSandboxId)

// Continue working
await sandbox.runCode('console.log("I am back!")')
```

## API Reference

### `sandbox(config)`

Creates a new Sandbox client instance.

```javascript
const client = sandbox({
  apiUrl: string,           // Sandbox API URL
  auth: AuthStrategy,       // Authentication strategy
  timeout: number,          // Request timeout in ms (default: 300000, optional)
})
```

### `SandboxClient`

#### Methods

| Method | Description |
|--------|-------------|
| `createRun(request)` | Create a code execution run |
| `createRunAndWait(request)` | Create and wait for completion |
| `streamRun(request)` | Create run with SSE streaming |
| `getRun(runId)` | Get run by ID |
| `createSandbox(options)` | Create a persistent sandbox |
| `connectToSandbox(sandboxId)` | Connect to existing sandbox |
| `deleteSandbox(sandboxId)` | Delete a sandbox |

### `Sandbox`

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `sandboxId` | `string` | Unique sandbox identifier |
| `files` | `SandboxFiles` | File operations interface |
| `commands` | `SandboxCommands` | Command execution interface |

#### Methods

| Method | Description |
|--------|-------------|
| `runCode(code, options)` | Execute code in the sandbox |
| `kill()` | Terminate the sandbox |
| `setTimeout(ms)` | Set execution timeout |

### `SandboxRun`

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `id` | `string` | Run ID |
| `sandboxId` | `string?` | Associated sandbox ID |
| `status` | `Status` | Current status |
| `stdout` | `string?` | Standard output |
| `stderr` | `string?` | Standard error |
| `exitCode` | `number?` | Exit code |
| `isComplete` | `boolean` | Whether run has finished |
| `isSuccess` | `boolean` | Whether run succeeded |

### Types

```typescript
type Status = 'pending' | 'running' | 'completed' | 'failed' | 'timeout' | 'cancelled'

interface CreateRunRequest {
    code: string
    language?: 'javascript' | 'typescript' | 'python' | 'bash'
    libraries?: string[]
    env?: Record<string, string>
    files?: FileInput[]
    memSize?: number // MB (default: 512)
    cpuSize?: number // cores (default: 1.0)
    timeoutMs?: number // ms (default: 300000)
    sandboxId?: string // Use existing sandbox
    persistent?: boolean // Keep sandbox after execution
}

interface RunCodeOptions {
    language?: 'typescript' | 'javascript' | 'python' | 'bash'
    envs?: Record<string, string>
    timeoutMs?: number
    libraries?: string[] // npm packages to install
    files?: FileInput[] // input files to provide
    onStdout?: (data: { line: string }) => void
    onStderr?: (data: { line: string }) => void
    onError?: (error: Error) => void
}

interface FileInput {
    name: string // filename
    content: string // file content
    encoding: 'utf8' | 'base64'
    path?: string // optional custom path
}
```

## E2B Compatibility

This SDK is designed to be compatible with E2B's `SandboxLike` interface, making migration straightforward:

```typescript
// E2B
import { Sandbox } from '@e2b/code-interpreter'

// Sandbox (drop-in replacement)
import { Sandbox, sandbox, DataTokenAuthStrategy } from '@meistrari/sandbox-sdk'

const sandbox = await Sandbox.create(templateId, { apiKey })
const client = sandbox({ apiUrl, auth: new DataTokenAuthStrategy(token) })
const sandbox = await Sandbox.create(client)

// Same interface works for both
await sandbox.files.write('/path', content)
await sandbox.runCode(code, { onStdout: ({ line }) => console.log(line) })
await sandbox.kill()
```

## Error Handling

```typescript
import { FetchError } from '@meistrari/sandbox-sdk'

try {
    const run = await client.createRunAndWait({ code: 'invalid(' })
}
catch (error) {
    if (error instanceof FetchError) {
        console.error(`API Error: ${error.message}`)
        console.error(`Status: ${error.status}`)
        console.error(`URL: ${error.url}`)
    }
}
```

## License

MIT