# @stateset/sandbox-sdk

TypeScript SDK for the StateSet Sandbox API. Create isolated Kubernetes-based execution environments with pre-installed AI tools for running Claude Code agents and StateSet commerce operations.

## Get Started

1. **Register** to get your API key:
```bash
curl -X POST https://sandbox.stateset.io/api/v1/register \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Ada",
    "last_name": "Lovelace",
    "email": "you@company.com",
    "organization_name": "Your Company",
    "use_case": "CI automation"
  }'
```

2. **Install** the SDK:
```bash
npm install @stateset/sandbox-sdk
```

3. **Use** the SDK (see Quick Start below)

## Features

- **Isolated Environments**: Each sandbox runs in its own Kubernetes pod with resource limits
- **Pre-installed CLIs**: Claude Code (`claude`) and StateSet CLI (`stateset`) ready to use
- **File Operations**: Read, write, and manage files in the sandbox workspace
- **Streaming Execution**: Real-time stdout/stderr streaming for long-running commands
- **Auto-cleanup**: Sandboxes automatically terminate after configurable timeout
- **Durable Agents**: Control replayable long-running workflows and resume event streams
- **Multi-Agent Orchestration**: Run specialized agents concurrently with retries and synthesis

## Pre-installed Tools

Every sandbox includes:

| Tool | Command | Description |
|------|---------|-------------|
| Claude Code | `claude` | AI coding assistant with file/bash tools |
| StateSet CLI | `stateset` | AI-powered commerce operations |
| Node.js | `node`, `npm` | JavaScript runtime (v22) |
| TypeScript | `tsc`, `tsx` | TypeScript compiler and runner |
| Python | `python3`, `pip` | Python runtime with AI packages |
| Git | `git`, `gh` | Version control with GitHub CLI |
| Go | `go` | Go programming language |
| Rust | `cargo`, `rustc` | Rust programming language |

## Installation

```bash
npm install @stateset/sandbox-sdk
```

## Quick Start

```typescript
import { StateSetSandbox } from '@stateset/sandbox-sdk';

const sandbox = new StateSetSandbox({
  baseUrl: 'https://api.sandbox.stateset.app',
  authToken: 'sk-sandbox-your-api-key',
  timeout: 120000
});

// Create a sandbox
const instance = await sandbox.create({
  cpus: '2',
  memory: '4Gi',
  timeout_seconds: 600
});

// Execute a command
const result = await sandbox.execute(instance.sandbox_id, {
  command: 'echo "Hello World"'
});
console.log(result.stdout); // "Hello World\n"

// Clean up
await sandbox.stop(instance.sandbox_id);
```

## Advanced Client

For checkpoints, artifacts, webhooks, templates, and metrics, use `StateSetSandboxExtended`:

```typescript
import { StateSetSandboxExtended } from '@stateset/sandbox-sdk';

const client = new StateSetSandboxExtended({
  baseUrl: 'https://api.sandbox.stateset.app',
  authToken: 'sk-sandbox-your-api-key'
});
```

## Durable and parallel agents

`DurableRunClient` connects to the `next-temporal-rs` workflow control plane. It
supports sandbox loops, active-horizon agents, lifecycle signals, budget extensions,
and resumable SSE streams.

```typescript
import { DurableRunClient } from '@stateset/sandbox-sdk';

const durable = new DurableRunClient({
  baseUrl: 'https://workflows.example.com',
  apiKey: 'engine-key',
});
const run = await durable.startSandboxAgentLoop({
  brand_id: 'brand_123',
  request_id: 'request_123',
  loop: {
    session: { isolation: 'gvisor', budget: { iteration_limit: 20 } },
    commands: [['node', '/workspace/agent.js']],
  },
});
const stream = await durable.openEventStream(run.workflow_id, { lastEventId: 0 });
```

For parallel work, pass an `AgentRunner` to `MultiAgentOrchestrator`; it bounds
concurrency, retries failed workers, supports cancellation and fail-fast scheduling,
and can launch a final synthesis agent.

## Examples

### Run Claude Code Agent

```typescript
const instance = await sandbox.create({ timeout_seconds: 600 });

try {
  // Claude Code is pre-installed with ANTHROPIC_API_KEY
  const result = await sandbox.execute(instance.sandbox_id, {
    command: 'claude -p "Create a REST API with Express.js" --allowedTools "Write,Bash,Read"'
  });
  console.log(result.stdout);
} finally {
  await sandbox.stop(instance.sandbox_id);
}
```

### Run StateSet Commerce CLI

```typescript
const instance = await sandbox.create({ timeout_seconds: 300 });

try {
  // Check StateSet CLI version
  const version = await sandbox.execute(instance.sandbox_id, {
    command: 'stateset --version'
  });
  console.log(version.stdout); // @stateset/cli v0.1.2

  // Run commerce operations
  const result = await sandbox.execute(instance.sandbox_id, {
    command: 'stateset "show me all customers" --format json'
  });
  console.log(result.stdout);
} finally {
  await sandbox.stop(instance.sandbox_id);
}
```

### StateSet CLI Agents

The StateSet CLI includes specialized agents for commerce operations:

```typescript
// Inventory management
await sandbox.execute(id, { command: 'stateset --agent inventory "check stock levels for SKU-001"' });

// Order processing
await sandbox.execute(id, { command: 'stateset --agent orders "list recent orders"' });

// Analytics
await sandbox.execute(id, { command: 'stateset --agent analytics "show revenue trends"' });

// Returns processing
await sandbox.execute(id, { command: 'stateset --agent returns "process RMA for order 12345"' });

// Checkout flow
await sandbox.execute(id, { command: 'stateset --agent checkout "create cart for alice@example.com"' });
```

### File Operations

```typescript
// Write source file
await sandbox.writeFile(instance.sandbox_id, '/workspace/main.ts', `
export function greet(name: string): string {
  return \`Hello, \${name}!\`;
}
`);

// Compile TypeScript
await sandbox.execute(instance.sandbox_id, {
  command: 'npx tsc main.ts'
});

// Read compiled output
const jsContent = await sandbox.readFileContent(instance.sandbox_id, '/workspace/main.js');
console.log(jsContent);
```

### Streaming Output

```typescript
await sandbox.executeStream(
  instance.sandbox_id,
  { command: 'npm install && npm run build' },
  {
    onStdout: (data) => process.stdout.write(data),
    onStderr: (data) => process.stderr.write(data),
    onExit: (code) => console.log('Exit code:', code),
    onError: (err) => console.error('Error:', err)
  }
);
```

## API Reference

### StateSetSandbox

Main client for interacting with the sandbox API.

#### Constructor

```typescript
const sandbox = new StateSetSandbox({
  baseUrl: string;      // Sandbox controller URL
  authToken: string;    // API key or JWT token
  orgId?: string;       // Organization ID (deprecated, ignored for API key auth)
  timeout?: number;     // Request timeout in ms (default: 30000)
});
```

#### Methods

| Method | Description |
|--------|-------------|
| `create(options?)` | Create a new sandbox instance |
| `get(sandboxId)` | Get sandbox details |
| `list()` | List all sandboxes for the authenticated organization |
| `execute(sandboxId, options)` | Execute a command (non-streaming) |
| `executeStream(sandboxId, options, callbacks)` | Execute with streaming output |
| `writeFile(sandboxId, path, content)` | Write a file to the sandbox |
| `writeFiles(sandboxId, files)` | Write multiple files (base64 encoded) |
| `readFile(sandboxId, path)` | Read a file (returns base64) |
| `readFileContent(sandboxId, path)` | Read a file (returns decoded string) |
| `stop(sandboxId)` | Stop and delete a sandbox |

### AgentRunner

High-level helper for running Claude Code agents in sandboxes.

```typescript
import { StateSetSandbox, AgentRunner } from '@stateset/sandbox-sdk';

const sandbox = new StateSetSandbox({
  baseUrl: 'https://api.sandbox.stateset.app',
  authToken: 'sk-sandbox-xxx'
});

const agent = new AgentRunner({
  sandbox,
  anthropicApiKey: process.env.ANTHROPIC_API_KEY!,
  sandboxOptions: {
    cpus: '2',
    memory: '4Gi',
    timeout_seconds: 600
  }
});

// Run agent and auto-cleanup
const { exitCode, events } = await agent.runAndCleanup({
  prompt: 'Create a TypeScript function that calculates fibonacci numbers',
  maxTurns: 10,
  allowedTools: ['Write', 'Bash', 'Read']
}, {
  onEvent: (event) => console.log('Event:', event),
  onStdout: (data) => process.stdout.write(data)
});
```

## Types

### Sandbox

```typescript
interface Sandbox {
  sandbox_id: string;
  org_id: string;
  session_id: string;
  status: 'creating' | 'running' | 'terminating' | 'terminated' | 'error';
  pod_ip?: string;
  created_at: string;
  expires_at: string;
}
```

### CreateSandboxOptions

```typescript
interface CreateSandboxOptions {
  cpus?: string;           // CPU limit (default: "2")
  memory?: string;         // Memory limit (default: "2Gi")
  timeout_seconds?: number; // Auto-terminate timeout (default: 600)
  env?: Record<string, string>; // Environment variables
}
```

### ExecuteResult

```typescript
interface ExecuteResult {
  exit_code: number;
  stdout: string;
  stderr: string;
}
```

### StreamCallbacks

```typescript
interface StreamCallbacks {
  onStdout?: (data: string) => void;
  onStderr?: (data: string) => void;
  onExit?: (code: number) => void;
  onError?: (error: Error) => void;
}
```

## Error Handling

```typescript
try {
  const instance = await sandbox.create();
  // ...
} catch (error) {
  if (error.message.includes('Maximum sandboxes')) {
    console.error('Too many sandboxes running');
  } else if (error.message.includes('timeout')) {
    console.error('Request timed out');
  } else {
    throw error;
  }
}
```

## Environment Variables

```bash
export SANDBOX_URL=https://api.sandbox.stateset.app
export SANDBOX_API_KEY=sk-sandbox-your-key
```

## Requirements

- Node.js ^20.19.0 or >=22.12.0
- API key from StateSet

## License

MIT
