@codivstack/sdk
v2.0.0
Published
Official TypeScript/JavaScript SDK for CodivStack API - Self-hosted BYOC sandbox platform on AWS ECS Fargate
Downloads
366
Maintainers
Readme
@codivstack/sdk
Official TypeScript/JavaScript SDK for CodivStack API. Manage sandboxes, check health, monitor your development environments, and manage workspace files with ease.
Installation
npm install @codivstack/sdkQuick Start
Basic Usage
import { Sandbox, Health } from '@codivstack/sdk';
// Set your API key as an environment variable
// export CODIVSTACK_API_KEY='your-api-key'
// Check API health
const isHealthy = await Health.check();
console.log('API is healthy:', isHealthy);
// List all sandboxes
const { sandboxes, total } = await Sandbox.list();
console.log(`You have ${total} sandboxes`);
// Create a new sandbox - returns SandboxInstance with all properties
const sandbox = await Sandbox.create({
size: 'standard', // One of 11 sizes: nano to massive
name: 'my-sandbox'
});
// Access all response properties directly
console.log('Sandbox ID:', sandbox.sandboxId); // "sandbox-abc123"
console.log('Status:', sandbox.status); // "running"
console.log('Size:', sandbox.size); // "standard"
console.log('Name:', sandbox.displayName); // "my-sandbox"
console.log('Message:', sandbox.message); // "Sandbox created successfully"
console.log('Resources:', sandbox.resources); // { cpu: "1024", memory: "4096" }
// Write files to sandbox workspace
await sandbox.files.write('/index.js', 'console.log("Hello World");');
// Read files from sandbox
const content = await sandbox.files.read('/index.js');
console.log('File content:', content);
// Run a command
const result = await sandbox.commands.run('node index.js');
console.log('Output:', result.stdout);
console.log('Exit code:', result.exitCode);
// Start dev server in background
await sandbox.commands.startBackground('npm run dev -- --host 0.0.0.0');
// Get current status with metrics
const status = await sandbox.getStatus();
console.log('Status:', status.status); // "running"
console.log('CPU:', status.metrics?.cpuPercent); // 25.5
// Stop sandbox - returns full response
const stopResult = await sandbox.stop();
console.log('Stop status:', stopResult.status); // "stopped"
console.log('Message:', stopResult.message); // "Sandbox stopped successfully"
// Start again - returns full response
const startResult = await sandbox.start();
console.log('Start status:', startResult.status); // "running"
console.log('Message:', startResult.message); // "Sandbox started successfully"Complete Example
import { Sandbox, Health, CodivStackError } from '@codivstack/sdk';
async function manageSandbox() {
try {
// Check if API is available
const isHealthy = await Health.check();
if (!isHealthy) {
console.error('API is not available');
return;
}
// List existing sandboxes
const { sandboxes } = await Sandbox.list();
console.log(`Found ${sandboxes.length} existing sandboxes`);
let sandbox;
if (sandboxes.length > 0 && sandboxes[0].status === 'running') {
// Use existing running sandbox
console.log(`Using existing sandbox: ${sandboxes[0].id}`);
sandbox = await Sandbox.get(sandboxes[0].id);
} else {
// Create a new sandbox if none exist
sandbox = await Sandbox.create({
size: 'medium',
name: 'development-sandbox'
});
console.log(`Created new sandbox: ${sandbox.sandboxId}`);
}
// Write some files to the sandbox
await sandbox.files.write('/app.js', 'console.log("Hello from CodivStack!");');
await sandbox.files.write('/package.json', JSON.stringify({
name: 'my-app',
version: '1.0.0',
main: 'app.js'
}, null, 2));
// List files in the workspace
const files = await sandbox.files.list('/');
console.log('Workspace files:', files.map(f => f.name));
// Read a file
const appContent = await sandbox.files.read('/app.js');
console.log('app.js content:', appContent);
// Check status
const status = await sandbox.getStatus();
console.log(`Sandbox status: ${status.status}`);
// Get metrics if running
if (status.status === 'running') {
const metrics = await sandbox.getMetrics();
console.log('Metrics:', metrics);
}
// Stop the sandbox when done
await sandbox.stop();
console.log('Sandbox stopped');
} catch (error) {
if (error instanceof CodivStackError) {
console.error(`API Error (${error.statusCode}):`, error.message);
} else {
console.error('Unexpected error:', error);
}
}
}
manageSandbox();Configuration
The SDK reads configuration from environment variables by default:
CODIVSTACK_API_KEY- Your API key (required for authenticated requests)CODIVSTACK_API_URL- Base URL (defaults tohttps://api.codivstack.dev)
Single configure() call configures everything:
import { configure, Sandbox, AgentClient } from '@codivstack/sdk';
// One config for ALL SDK features (Sandbox, Agent, Health, Admin)
configure({
apiKey: 'your-api-key',
baseUrl: 'https://api.codivstack.dev'
});
// Create Sandbox (uses global config)
const sandbox = await Sandbox.create({ size: 'standard' });
// Create AgentClient (uses global config automatically)
const agent = new AgentClient();
// Use Agent with the sandbox
for await (const event of agent.chat({
sandboxId: sandbox.sandboxId,
message: 'Create a React app'
})) {
if (event.type === 'chunk') {
process.stdout.write(event.text);
}
}You can also use environment variables without calling configure():
export CODIVSTACK_API_KEY="your-api-key"
export CODIVSTACK_API_URL="https://api.codivstack.dev"API Reference
Sandbox
Sandbox.list()
List all sandboxes for the current user.
const { sandboxes, total } = await Sandbox.list();
// Response structure:
// {
// sandboxes: [
// { id, name, status, size, user_id, workspace_id, task_arn, private_ip, ... }
// ],
// total: 5
// }
console.log(`Total: ${total} sandboxes`);
sandboxes.forEach(s => {
console.log(`${s.id}: ${s.status} (${s.size})`);
});Returns: Promise<SandboxListResponse>
Sandbox.create(input)
Create a new sandbox with file management capabilities. Uses warm pool for fast ~2 second startup.
const sandbox = await Sandbox.create({
size: 'standard', // Optional: One of 11 sizes (defaults to 'nano')
name: 'my-sandbox', // Optional: Custom name
max_runtime_minutes: 120, // Optional: Custom runtime limit (defaults to plan limit)
environment: { // Optional: Environment variables
NODE_ENV: 'production'
}
});
// Response properties available directly on sandbox:
console.log(sandbox.sandboxId); // "sandbox-abc123"
console.log(sandbox.status); // "running"
console.log(sandbox.size); // "standard"
console.log(sandbox.displayName); // "my-sandbox"
console.log(sandbox.message); // "Sandbox created successfully"
console.log(sandbox.resources); // { cpu: "1024", memory: "4096" }
// Also includes file and command APIs
await sandbox.files.write('/index.js', 'console.log("Hello");');
await sandbox.commands.run('node index.js');Returns: Promise<SandboxInstance>
Sandbox.get(sandboxId)
Get an existing sandbox instance with file management capabilities.
const sandbox = await Sandbox.get('sandbox-abc123');
// Same properties as create:
console.log(sandbox.sandboxId); // "sandbox-abc123"
console.log(sandbox.status); // "running"
console.log(sandbox.size); // "standard"
// Use files and commands
await sandbox.files.write('/app.js', 'console.log("App");');
await sandbox.commands.run('node app.js');Returns: Promise<SandboxInstance>
Sandbox.getStatus(sandboxId)
Get detailed status and metrics for a sandbox.
const status = await Sandbox.getStatus('sandbox-abc123');
// Response structure:
console.log(status.sandboxId); // "sandbox-abc123"
console.log(status.status); // "running" | "stopped" | "pending" | ...
console.log(status.name); // "my-sandbox"
console.log(status.size); // "standard"
console.log(status.cpu); // "1024"
console.log(status.memory); // "4096"
console.log(status.createdAt); // "2024-01-15T10:30:00Z"
console.log(status.startedAt); // "2024-01-15T10:30:05Z"
console.log(status.maxRuntimeMinutes); // 60
console.log(status.effectiveRuntimeMinutes); // 60
// Metrics (when running)
if (status.metrics) {
console.log(status.metrics.cpuPercent); // 25.5
console.log(status.metrics.memoryPercent); // 45.2
console.log(status.metrics.networkRxBytes); // 1024
console.log(status.metrics.networkTxBytes); // 512
}Returns: Promise<SandboxStatusResponse>
Sandbox.start(sandboxId)
Start a stopped sandbox.
const result = await Sandbox.start('sandbox-abc123');
// Response structure:
console.log(result.sandboxId); // "sandbox-abc123"
console.log(result.status); // "running"
console.log(result.message); // "Sandbox started successfully"
console.log(result.size); // "standard"
console.log(result.displayName); // "my-sandbox"
console.log(result.resources); // { cpu: "1024", memory: "4096" }Returns: Promise<SandboxActionResponse>
Sandbox.stop(sandboxId)
Stop a running sandbox.
const result = await Sandbox.stop('sandbox-abc123');
// Response structure:
console.log(result.sandboxId); // "sandbox-abc123"
console.log(result.status); // "stopped"
console.log(result.message); // "Sandbox stopped successfully"Returns: Promise<SandboxActionResponse>
Sandbox.delete(sandboxId)
Permanently delete a sandbox.
const result = await Sandbox.delete('sandbox-abc123');
// Response structure:
console.log(result.sandboxId); // "sandbox-abc123"
console.log(result.status); // "deleted"
console.log(result.message); // "Sandbox deleted successfully"Returns: Promise<SandboxActionResponse>
Sandbox.getMetrics(sandboxId)
Get real-time metrics for a running sandbox.
const metrics = await Sandbox.getMetrics('sandbox-abc123');
// Response structure:
console.log(metrics.sandboxId); // "sandbox-abc123"
console.log(metrics.name); // "my-sandbox"
console.log(metrics.size); // "standard"
// Detailed metrics
console.log(metrics.metrics.cpuUtilized); // 256 (mCPU)
console.log(metrics.metrics.cpuReserved); // 1024 (mCPU)
console.log(metrics.metrics.cpuPercent); // 25.0
console.log(metrics.metrics.cpuCores); // 1
console.log(metrics.metrics.memoryUtilized); // 1843 (MB)
console.log(metrics.metrics.memoryReserved); // 4096 (MB)
console.log(metrics.metrics.memoryPercent); // 45.0
console.log(metrics.metrics.networkRxBytes); // 1024 (bytes/sec)
console.log(metrics.metrics.networkTxBytes); // 512 (bytes/sec)
console.log(metrics.metrics.timestamp); // "2024-01-15T10:35:00Z"Returns: Promise<SandboxMetricsResponse>
Health
Health.check()
Check if the CodivStack API is healthy.
const isHealthy = await Health.check();Returns: Promise<boolean>
SandboxInstance
When you create or get a sandbox, you receive a SandboxInstance object with properties and methods.
Properties
const sandbox = await Sandbox.create({ size: 'medium', name: 'dev' });
// Available properties:
sandbox.sandboxId // string - "sandbox-abc123"
sandbox.status // string - "running" | "stopped" | ...
sandbox.size // string - "medium"
sandbox.displayName // string - "dev"
sandbox.message // string - "Sandbox created successfully"
sandbox.resources // { cpu: string, memory: string }
sandbox.createdAt // string - "2024-01-15T10:30:00Z"
sandbox.startedAt // string - "2024-01-15T10:30:05Z"
// URL properties:
sandbox.wssUrl // string - "wss://30001-sandbox-abc123.codivstack.dev/terminal"
// Sub-APIs:
sandbox.files // SandboxFiles - File management
sandbox.commands // SandboxCommands - Command executionSecurity Note: Internal infrastructure details (workspaceId, taskArn, privateIp, image) are intentionally hidden from SDK responses for security.
sandbox.getPreviewUrl(port)
Get the public preview URL for your application running on a specific port.
const sandbox = await Sandbox.create({ size: 'standard' });
// Start Vite dev server in background
await sandbox.commands.run('npm run dev -- --host 0.0.0.0', { background: true });
// Get preview URL for different ports:
sandbox.getPreviewUrl(5173); // https://5173-sandbox-abc123.codivstack.dev (Vite)
sandbox.getPreviewUrl(3000); // https://3000-sandbox-abc123.codivstack.dev (React/Next.js)
sandbox.getPreviewUrl(8080); // https://8080-sandbox-abc123.codivstack.dev (Custom)
// Default port is 3000:
sandbox.getPreviewUrl(); // https://3000-sandbox-abc123.codivstack.devReturns: string
Terminal WebSocket URL
The wssUrl property provides direct WebSocket connection to the sandbox terminal (port 30001):
const sandbox = await Sandbox.create({ size: 'standard' });
// Connect to terminal via WebSocket
const ws = new WebSocket(sandbox.wssUrl);
// wss://30001-sandbox-abc123.codivstack.dev/terminal
ws.onopen = () => {
console.log('Terminal connected');
ws.send('ls -la\n'); // Send commands
};
ws.onmessage = (event) => {
console.log('Output:', event.data); // Receive terminal output
};Console API (Read-only Workflow Output)
The Console API provides a way to run dev servers and stream their output without allowing user input. Perfect for showing npm run dev output in a read-only pane.
Console uses the same PTY format as Terminal - you can use the same <Terminal /> component for both! The only difference is Console ignores all input (read-only).
Console vs Terminal
| Feature | Console | Terminal |
|---------|---------|----------|
| Purpose | Workflow output display | Interactive shell |
| Input | ❌ Ignored (read-only) | ✅ Full input |
| Data Format | Raw PTY stream | Raw PTY stream |
| Component | <Terminal /> | <Terminal /> |
| Auto-kill on disconnect | ✅ Yes | ✅ Yes |
| URL Property | sandbox.console.url | sandbox.wssUrl |
sandbox.console.start(options?)
Start a workflow and stream its output.
const sandbox = await Sandbox.create({ size: 'standard' });
// Start default dev server (npm run dev -- --host 0.0.0.0)
await sandbox.console.start();
// Start custom command
await sandbox.console.start({ command: 'npm run build:watch' });
// Start in specific directory
await sandbox.console.start({
command: 'npm run dev',
workdir: '/home/developer/workspace/frontend'
});Returns:
{
success: true,
status: 'running',
pid: 12345,
command: 'npm run dev -- --host 0.0.0.0',
message: 'Console started'
}sandbox.console.stop(options?)
Stop the running workflow.
// Graceful stop (SIGTERM - like Ctrl+C)
await sandbox.console.stop();
// Force kill (SIGKILL)
await sandbox.console.stop({ signal: 'SIGKILL' });
// Graceful with force fallback after 3 seconds
await sandbox.console.stop({ force: true });Returns:
{
success: true,
message: 'Stop signal sent',
pid: 12345,
signal: 'SIGTERM'
}sandbox.console.status()
Get current console status.
const status = await sandbox.console.status();
console.log(status.status); // 'running' | 'stopped' | 'starting' | 'stopping'
console.log(status.command); // 'npm run dev -- --host 0.0.0.0'
console.log(status.pid); // 12345
console.log(status.bufferSize); // Number of buffered lines
console.log(status.listeners); // Number of WebSocket connectionsConsole WebSocket URL
The console.url property provides the WebSocket URL for read-only output streaming.
Recommended: Use the Terminal component (same component works for both Terminal and Console):
import { Terminal } from '@codivstack/sdk/react';
// Terminal (interactive)
<Terminal wsUrl={sandbox.wssUrl} />
// Console (read-only) - same component!
<Terminal wsUrl={sandbox.console.url} />Or connect manually with xterm.js:
import { Terminal } from 'xterm';
const sandbox = await Sandbox.create({ size: 'standard' });
// Start the workflow
await sandbox.console.start();
// Connect to console output (raw PTY data, not JSON!)
const ws = new WebSocket(sandbox.console.url);
// wss://30001-sandbox-abc123.codivstack.dev/console
const terminal = new Terminal();
terminal.open(document.getElementById('console'));
ws.onmessage = (event) => {
// Raw PTY data with ANSI colors - write directly to xterm
terminal.write(event.data);
};
// Process auto-stops when WebSocket disconnects (page refresh, close tab, etc.)Complete Console Example
import { Sandbox } from '@codivstack/sdk';
import { Terminal } from '@codivstack/sdk/react';
// React component example
function DevServerConsole() {
const [sandbox, setSandbox] = useState<SandboxInstance | null>(null);
const [consoleUrl, setConsoleUrl] = useState<string>('');
useEffect(() => {
async function init() {
// Create sandbox
const sb = await Sandbox.create({ size: 'standard' });
setSandbox(sb);
// Write project files
await sb.files.write('/package.json', JSON.stringify({
scripts: { dev: 'vite' },
dependencies: { vite: '^5.0.0' }
}));
// Install dependencies
await sb.commands.run('npm install', { timeout: 120000 });
// Start dev server via console
await sb.console.start({ command: 'npm run dev -- --host 0.0.0.0' });
// Set console URL for Terminal component
setConsoleUrl(sb.console.url);
}
init();
}, []);
if (!consoleUrl) return <div>Loading...</div>;
return (
<div>
<h2>Dev Server Output (Read-only)</h2>
<Terminal wsUrl={consoleUrl} />
{sandbox && (
<a href={sandbox.getPreviewUrl(5173)} target="_blank">
Open Preview
</a>
)}
</div>
);
}Note: Process automatically stops when WebSocket disconnects (page refresh, close tab, navigate away). Manual sandbox.console.stop() is optional.
Real-time File System (FsClient)
The FsClient provides WebSocket-based real-time file system access with live change notifications. Perfect for building IDE-like experiences where you need to react to file changes instantly.
Files API vs FsClient
| Feature | Files API (HTTP) | FsClient (WebSocket) | |---------|------------------|----------------------| | Protocol | HTTP REST | WebSocket | | Real-time events | ❌ No | ✅ Yes (add, change, delete) | | Best for | Simple read/write | IDE, file explorers | | Connection | Per-request | Persistent | | Binary files | ❌ No | ✅ Yes (base64) |
sandbox.connectFs(options?)
Connect to the real-time file system WebSocket.
const sandbox = await Sandbox.create({ size: 'standard' });
// Connect with event handler
const fs = await sandbox.connectFs({
onEvent: (event) => {
console.log(`File ${event.event}: ${event.path}`);
// event.event: 'add' | 'change' | 'unlink' | 'unlinkDir' | 'addDir'
},
onReady: () => console.log('FS connected'),
onClose: () => console.log('FS disconnected'),
onError: (err) => console.error('FS error:', err),
timeout: 30000 // Connection timeout (default: 30s)
});Returns: Promise<FsClient>
fs.listDir(path)
List files and directories.
const entries = await fs.listDir('/src');
// Returns: FsEntry[] with name, kind ('file' | 'dir'), size, modifiedfs.readFile(path, options?)
Read file content (text or binary).
// Auto-detect binary based on extension
const result = await fs.readFile('/package.json');
console.log(result.content); // File content
console.log(result.binary); // false
console.log(result.size); // File size in bytes
// Force binary mode
const binary = await fs.readFile('/image.png', { binary: true });fs.readTextFile(path) / fs.readBinaryFile(path)
Convenience methods for reading files.
// Read text file directly
const content = await fs.readTextFile('/app.js');
// Read binary file as Uint8Array
const imageData = await fs.readBinaryFile('/logo.png');fs.writeFile(path, content, options?)
Write content to a file.
// Write text file
await fs.writeFile('/app.js', 'console.log("Hello");');
// Write binary file (base64 encoded)
await fs.writeFile('/output.png', base64Content, { binary: true });fs.writeTextFile(path, content) / fs.writeBinaryFile(path, data)
Convenience methods for writing files.
// Write text file
await fs.writeTextFile('/config.json', '{"port": 3000}');
// Write binary file from Uint8Array
await fs.writeBinaryFile('/output.png', imageData);fs.delete(path)
Delete a file or directory (recursive for directories).
await fs.delete('/old-file.js');
await fs.delete('/temp-folder'); // Deletes entire folderfs.mkdir(path)
Create a directory (recursive).
await fs.mkdir('/src/components/ui');fs.rename(path, newPath)
Move or rename a file or directory.
await fs.rename('/old-name.js', '/new-name.js');
await fs.rename('/folder', '/renamed-folder');fs.stat(path)
Get file or directory stats.
const stat = await fs.stat('/app.js');
console.log(stat.isFile); // true
console.log(stat.isDirectory); // false
console.log(stat.size); // 1234
console.log(stat.modified); // "2024-01-15T10:30:00Z"
console.log(stat.created); // "2024-01-10T08:00:00Z"fs.getFullTree(path?)
Get the complete file tree (recursive).
const tree = await fs.getFullTree('/src');
// Returns: FsTreeNode[]
// [
// {
// name: 'components',
// path: 'src/components',
// kind: 'dir',
// children: [
// { name: 'Button.tsx', path: 'src/components/Button.tsx', kind: 'file', size: 1234 }
// ]
// }
// ]Note: node_modules, .git, dist, build, .cache, coverage are automatically excluded.
fs.onEvent(callback)
Subscribe to file system events.
// Subscribe to events
const unsubscribe = fs.onEvent((event) => {
switch (event.event) {
case 'add':
console.log(`File created: ${event.path}`);
break;
case 'change':
console.log(`File modified: ${event.path}`);
break;
case 'unlink':
console.log(`File deleted: ${event.path}`);
break;
case 'addDir':
console.log(`Directory created: ${event.path}`);
break;
case 'unlinkDir':
console.log(`Directory deleted: ${event.path}`);
break;
}
});
// Later: unsubscribe
unsubscribe();fs.disconnect()
Disconnect from the WebSocket.
fs.disconnect();
// or
sandbox.disconnectFs();Complete FsClient Example
import { Sandbox, FsClient } from '@codivstack/sdk';
async function buildFileExplorer() {
const sandbox = await Sandbox.create({ size: 'standard' });
// Connect to real-time FS
const fs = await sandbox.connectFs({
onEvent: (event) => {
// Update UI when files change
console.log(`[${event.event}] ${event.path}`);
refreshFileTree();
}
});
// Get initial file tree
const tree = await fs.getFullTree();
renderTree(tree);
// Create a new file
await fs.writeTextFile('/src/NewComponent.tsx', `
export function NewComponent() {
return <div>Hello!</div>;
}
`);
// onEvent will fire with { event: 'add', path: 'src/NewComponent.tsx' }
// Read file content
const content = await fs.readTextFile('/src/NewComponent.tsx');
showInEditor(content);
// Handle binary files (images, etc.)
const imageData = await fs.readBinaryFile('/public/logo.png');
displayImage(imageData);
// Cleanup on unmount
return () => fs.disconnect();
}FsClient Properties
const fs = await sandbox.connectFs();
fs.connected // boolean - Is WebSocket connected?
fs.wsUrl // string - WebSocket URLBinary File Support
FsClient automatically detects binary files by extension and uses base64 encoding:
Supported binary extensions:
- Images:
.png,.jpg,.jpeg,.gif,.webp,.ico,.bmp,.svg - Documents:
.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx - Archives:
.zip,.tar,.gz,.rar,.7z - Media:
.mp3,.mp4,.wav,.avi,.mov,.webm,.ogg - Fonts:
.woff,.woff2,.ttf,.otf,.eot - Other:
.exe,.dll,.so,.dylib,.bin,.dat,.db,.sqlite
import { isBinaryFile, BINARY_EXTENSIONS } from '@codivstack/sdk';
// Check if a file is binary
isBinaryFile('/image.png'); // true
isBinaryFile('/app.js'); // false
// Get all binary extensions
console.log(BINARY_EXTENSIONS); // Set of extensionssandbox.start()
Start this sandbox (returns full response).
const result = await sandbox.start();
console.log(result.sandboxId); // "sandbox-abc123"
console.log(result.status); // "running"
console.log(result.message); // "Sandbox started successfully"
console.log(result.size); // "medium"
// Instance properties are also updated:
console.log(sandbox.status); // "running"Returns: Promise<SandboxActionResponse>
sandbox.stop()
Stop this sandbox (returns full response).
const result = await sandbox.stop();
console.log(result.sandboxId); // "sandbox-abc123"
console.log(result.status); // "stopped"
console.log(result.message); // "Sandbox stopped successfully"
// Instance property is also updated:
console.log(sandbox.status); // "stopped"Returns: Promise<SandboxActionResponse>
sandbox.getStatus()
Get current status and metrics.
const status = await sandbox.getStatus();
console.log(status.sandboxId); // "sandbox-abc123"
console.log(status.status); // "running"
console.log(status.metrics?.cpuPercent); // 25.5
console.log(status.metrics?.memoryPercent);// 45.2
// Instance property is also updated:
console.log(sandbox.status); // "running"Returns: Promise<SandboxStatusResponse>
sandbox.getMetrics()
Get real-time metrics.
const metrics = await sandbox.getMetrics();
console.log(metrics.metrics.cpuPercent); // 25.0
console.log(metrics.metrics.memoryPercent); // 45.0
console.log(metrics.metrics.networkRxBytes); // 1024Returns: Promise<SandboxMetricsResponse>
Language Server Protocol (LSP) Client
The LspClient provides WebSocket-based Language Server Protocol support for TypeScript/JavaScript. Perfect for building IDE-like experiences with autocompletion, hover info, diagnostics, and go-to-definition.
LSP Features
| Feature | Method | Description |
|---------|--------|-------------|
| Autocompletion | completion() | Get code completions at cursor position |
| Hover | hover() | Get type info and documentation on hover |
| Go to Definition | definition() | Jump to where a symbol is defined |
| Find References | references() | Find all usages of a symbol |
| Diagnostics | onDiagnostics | Real-time error and warning notifications |
| Signature Help | signatureHelp() | Function parameter hints |
| Document Symbols | documentSymbols() | List all symbols in a file |
sandbox.connectLsp(options?)
Connect to the Language Server Protocol WebSocket.
const sandbox = await Sandbox.create({ size: 'standard' });
// Connect with diagnostics handler
const lsp = await sandbox.connectLsp({
onDiagnostics: (uri, diagnostics) => {
diagnostics.forEach(d => {
const severity = d.severity === 1 ? 'Error' : d.severity === 2 ? 'Warning' : 'Info';
console.log(`${severity} at line ${d.range.start.line}: ${d.message}`);
});
},
onReady: () => console.log('LSP connected'),
onClose: () => console.log('LSP disconnected'),
onError: (err) => console.error('LSP error:', err),
timeout: 30000 // Connection timeout (default: 30s)
});Returns: Promise<LspClient> (already initialized)
lsp.didOpen(path, content, languageId?)
Open a document for editing. Must be called before getting completions/hover for a file.
// Open a TypeScript file
lsp.didOpen('/src/app.ts', fileContent, 'typescript');
// Open a JavaScript file
lsp.didOpen('/src/utils.js', fileContent, 'javascript');
// Default languageId is 'typescript'
lsp.didOpen('/src/main.ts', fileContent);lsp.didChange(path, content)
Notify that a document has changed.
// When user edits the file
lsp.didChange('/src/app.ts', newContent);lsp.didClose(path)
Close a document when no longer editing.
lsp.didClose('/src/app.ts');lsp.completion(path, line, character)
Get autocompletion suggestions at a position.
// Get completions at line 10, character 15 (0-indexed)
const completions = await lsp.completion('/src/app.ts', 10, 15);
completions.forEach(item => {
console.log(`${item.label} (${item.kind}): ${item.detail || ''}`);
// Example: "useState (Function): Hook for state management"
});Returns: Promise<LspCompletionItem[]>
lsp.hover(path, line, character)
Get hover information (type info, documentation) at a position.
const hover = await lsp.hover('/src/app.ts', 5, 10);
if (hover) {
// hover.contents can be string or markdown
console.log(hover.contents);
// Example: "(method) Array.map<U>(callbackfn: ...): U[]"
}Returns: Promise<LspHover | null>
lsp.definition(path, line, character)
Go to the definition of a symbol.
const locations = await lsp.definition('/src/app.ts', 10, 5);
if (locations && locations.length > 0) {
const loc = locations[0];
console.log(`Definition at ${loc.uri}:${loc.range.start.line}`);
// Example: "Definition at file:///home/developer/workspace/src/utils.ts:25"
}Returns: Promise<LspLocation[] | null>
lsp.references(path, line, character, includeDeclaration?)
Find all references to a symbol.
const refs = await lsp.references('/src/app.ts', 5, 10);
console.log(`Found ${refs.length} references:`);
refs.forEach(ref => {
console.log(` ${ref.uri}:${ref.range.start.line}`);
});Returns: Promise<LspLocation[]>
lsp.signatureHelp(path, line, character)
Get function signature help (parameter hints).
const sig = await lsp.signatureHelp('/src/app.ts', 10, 20);
if (sig && sig.signatures.length > 0) {
console.log('Signature:', sig.signatures[0].label);
console.log('Active param:', sig.activeParameter);
}Returns: Promise<LspSignatureHelp | null>
lsp.documentSymbols(path)
Get all symbols (functions, classes, variables) in a document.
const symbols = await lsp.documentSymbols('/src/app.ts');
symbols.forEach(sym => {
console.log(`${sym.name} (${sym.kind}) at line ${sym.location.range.start.line}`);
// Example: "MyComponent (Function) at line 10"
});Returns: Promise<LspSymbol[]>
lsp.shutdown() and lsp.disconnect()
Clean shutdown and disconnect.
// Shutdown LSP server gracefully
await lsp.shutdown();
// Disconnect WebSocket
lsp.disconnect();
// Or use the sandbox method (does both)
await sandbox.disconnectLsp();Complete LSP Example
import { Sandbox, LspClient, LSP_COMPLETION_KINDS } from '@codivstack/sdk';
async function buildCodeEditor() {
const sandbox = await Sandbox.create({ size: 'standard' });
// Connect to real-time FS for file changes
const fs = await sandbox.connectFs();
// Connect to LSP for language features
const lsp = await sandbox.connectLsp({
onDiagnostics: (uri, diagnostics) => {
// Update editor error markers
updateDiagnostics(uri, diagnostics);
}
});
// Read and open file
const content = await fs.readTextFile('/src/app.ts');
lsp.didOpen('/src/app.ts', content);
// Get completions when user types
async function onCursorChange(line: number, char: number) {
const completions = await lsp.completion('/src/app.ts', line, char);
showCompletionMenu(completions.map(c => ({
label: c.label,
kind: LSP_COMPLETION_KINDS[c.kind || 1],
detail: c.detail
})));
}
// Get hover info on mouse over
async function onHover(line: number, char: number) {
const hover = await lsp.hover('/src/app.ts', line, char);
if (hover) {
showTooltip(hover.contents);
}
}
// Go to definition on Ctrl+Click
async function onCtrlClick(line: number, char: number) {
const defs = await lsp.definition('/src/app.ts', line, char);
if (defs && defs.length > 0) {
navigateTo(defs[0].uri, defs[0].range.start.line);
}
}
// Update LSP when user edits
function onEditorChange(newContent: string) {
lsp.didChange('/src/app.ts', newContent);
}
// Cleanup
return async () => {
await lsp.shutdown();
lsp.disconnect();
fs.disconnect();
};
}LSP Type Helpers
The SDK exports helpful constants for LSP types:
import {
LSP_COMPLETION_KINDS, // Completion item kind names
LSP_SYMBOL_KINDS, // Symbol kind names
LSP_DIAGNOSTIC_SEVERITY // Diagnostic severity names
} from '@codivstack/sdk';
// Usage
const kindName = LSP_COMPLETION_KINDS[item.kind]; // "Function", "Variable", etc.
const severityName = LSP_DIAGNOSTIC_SEVERITY[diag.severity]; // "Error", "Warning", etc.LspClient Properties
const lsp = await sandbox.connectLsp();
lsp.connected // boolean - Is WebSocket connected?
lsp.initialized // boolean - Is LSP server initialized?
lsp.wsUrl // string - WebSocket URLFile Management
All file paths are workspace-relative. The root / maps to /home/developer/workspace/.
Path examples:
/index.js→/home/developer/workspace/index.js/src/app.ts→/home/developer/workspace/src/app.tsexample.txt→/home/developer/workspace/example.txt
sandbox.files.list(path)
List files and directories in a path.
const files = await sandbox.files.list('/src');
// Returns: FileInfo[] with name, path, isDirectory, size, modifiedsandbox.files.read(path)
Read a file or multiple files from the workspace.
// Read single file
const content = await sandbox.files.read('/index.js');
// Read multiple files
const contents = await sandbox.files.read(['/index.js', '/package.json']);
// Returns: { '/index.js': '...', '/package.json': '...' }sandbox.files.write(path, data)
Write single or multiple files to the workspace.
// Write single file
await sandbox.files.write('/app.js', 'console.log("Hello");');
// Write multiple files
await sandbox.files.write([
{ path: '/index.js', data: 'console.log("Main");' },
{ path: '/config.json', data: '{"port": 3000}' }
]);sandbox.files.delete(path)
Delete files or directories from the workspace.
// Delete single file
await sandbox.files.delete('/old.js');
// Delete multiple files
await sandbox.files.delete(['/old.js', '/temp.txt']);sandbox.files.createDirectory(path)
Create a directory in the workspace.
await sandbox.files.createDirectory('/src/components');sandbox.files.move(from, to)
Move or rename a file or directory.
await sandbox.files.move('/old.js', '/new.js');sandbox.files.copy(from, to)
Copy a file.
await sandbox.files.copy('/template.js', '/copy.js');sandbox.files.exists(path)
Check if a file or directory exists.
const exists = await sandbox.files.exists('/index.js');Workspace Download
Download the entire workspace as a tar.gz archive. Useful for initial file sync or backup.
sandbox.files.getWorkspaceInfo()
Get workspace information (size, file count, etc.).
const info = await sandbox.files.getWorkspaceInfo();
console.log(info.path); // "/home/developer/workspace"
console.log(info.size); // 1234567 (bytes)
console.log(info.sizeHuman); // "1.2 MB"
console.log(info.fileCount); // 150
console.log(info.dirCount); // 25
console.log(info.hasNodeModules); // true
console.log(info.timestamp); // "2024-01-15T10:30:00Z"sandbox.files.downloadWorkspace()
Download entire workspace as tar.gz (includes node_modules).
// Download workspace as ArrayBuffer
const tarBuffer = await sandbox.files.downloadWorkspace();
// In Node.js: Save to file
import { writeFileSync } from 'fs';
writeFileSync('workspace.tar.gz', Buffer.from(tarBuffer));
// In browser: Use pako + untar to extract
// const files = await extractTarGz(tarBuffer);sandbox.files.getWorkspaceDownloadUrl()
Get the direct URL to download workspace tar.gz.
const url = sandbox.files.getWorkspaceDownloadUrl();
// -> https://30001-sandbox-xxx.codivstack.dev/workspace/tar
// Use with fetch
const response = await fetch(url);
const buffer = await response.arrayBuffer();
// Or as download link
const a = document.createElement('a');
a.href = url;
a.download = 'workspace.tar.gz';
a.click();Complete Workspace Sync Example
import { Sandbox } from '@codivstack/sdk';
async function syncWorkspace() {
const sandbox = await Sandbox.get('sandbox-abc123');
// Check workspace info first
const info = await sandbox.files.getWorkspaceInfo();
console.log(`Workspace: ${info.sizeHuman}, ${info.fileCount} files`);
if (info.hasNodeModules) {
console.log('Note: node_modules included in download');
}
// Download entire workspace
const tarBuffer = await sandbox.files.downloadWorkspace();
console.log(`Downloaded ${tarBuffer.byteLength} bytes`);
// Save to file (Node.js)
const fs = await import('fs');
fs.writeFileSync('workspace-backup.tar.gz', Buffer.from(tarBuffer));
console.log('Workspace saved to workspace-backup.tar.gz');
}Command Execution
Execute shell commands in your sandbox using the commands API. Perfect for running build scripts, dev servers, and other CLI tools.
sandbox.commands.run(command, options)
Run a shell command and get the output.
// Simple command
const result = await sandbox.commands.run('ls -la');
console.log(result.stdout);
// With timeout (default: 30s)
const result = await sandbox.commands.run('npm test', { timeout: 60000 });
// In specific directory
const result = await sandbox.commands.run('ls', {
workdir: '/home/developer/workspace/src'
});sandbox.commands.startBackground(command)
Start a long-running process in the background (e.g., dev servers).
// Start Vite dev server
await sandbox.commands.startBackground('npm run dev -- --host 0.0.0.0');
// Start Next.js
await sandbox.commands.startBackground('npm run dev');
// Custom port
await sandbox.commands.startBackground('npm run dev -- --port 3000 --host 0.0.0.0');sandbox.commands.npmInstall(packages, options)
Install npm packages.
// Install all dependencies from package.json
await sandbox.commands.npmInstall();
// Install specific packages
await sandbox.commands.npmInstall(['express', 'cors']);
// Install dev dependencies
await sandbox.commands.npmInstall(['typescript', '@types/node'], { dev: true });sandbox.commands.npmRun(script, options)
Run an npm script.
// Run build script
const result = await sandbox.commands.npmRun('build');
// Run dev server in background
await sandbox.commands.npmRun('dev', {
background: true,
args: ['--host', '0.0.0.0']
});
// Run tests with timeout
const testResult = await sandbox.commands.npmRun('test', { timeout: 120000 });sandbox.commands.kill(target)
Kill a process by name or port.
// Kill by process name
await sandbox.commands.kill('node');
// Kill by port number
await sandbox.commands.kill(3000);Static Command Methods
You can also use commands without a SandboxInstance:
import { Sandbox, SandboxCommands } from '@codivstack/sdk';
// Get commands API for a sandbox
const commands = Sandbox.commands('sandbox-abc123');
// Or create directly
const commands = new SandboxCommands('sandbox-abc123');
// Run commands
await commands.run('npm install');
await commands.startBackground('npm run dev -- --host 0.0.0.0');Error Handling
The SDK throws CodivStackError for API errors:
import { Sandbox, CodivStackError } from '@codivstack/sdk';
try {
const sandbox = await Sandbox.create({ size: 'small' });
} catch (error) {
if (error instanceof CodivStackError) {
console.error('Status:', error.statusCode);
console.error('Message:', error.message);
if (error.isUnauthorized()) {
console.error('Authentication failed');
} else if (error.isForbidden()) {
console.error('Permission denied');
}
}
}Types
The SDK exports TypeScript types for all API responses:
import type {
SandboxDetails,
SandboxSummary,
SandboxCreateInput,
SandboxStatusDetail,
SandboxMetrics,
SandboxSize,
SandboxStatus,
} from '@codivstack/sdk';
// Example: Type-safe sandbox creation
const createSandbox = async (size: SandboxSize, name: string): Promise<SandboxDetails> => {
return await Sandbox.create({ size, name });
};Sandbox Sizes
The SDK supports 11 different sandbox sizes:
| Size | CPU (vCPU) | Memory (GB) | Use Case | |---------|------------|-------------|----------| | nano | 0.25 | 0.5 | Minimal tasks, testing | | tiny | 0.5 | 1 | Light workloads | | small | 0.5 | 2 | Small applications | | basic | 1 | 1 | Basic development | | standard| 1 | 2 | Standard development | | medium | 1 | 4 | Medium applications | | large | 2 | 4 | Large applications | | xlarge | 2 | 8 | Heavy workloads | | xxlarge | 4 | 8 | Very heavy workloads | | huge | 4 | 16 | Intensive computing | | massive | 4 | 30 | Maximum resources |
Sandbox Status
Sandboxes can have the following statuses:
'pending'- Sandbox is being created'running'- Sandbox is active and running'stopped'- Sandbox is stopped'error'- Sandbox encountered an error
Requirements
- Node.js >= 18.0.0
- TypeScript >= 5.0.0 (for TypeScript projects)
Environment Variables
| Variable | Description | Required | Default |
|----------|-------------|----------|---------|
| CODIVSTACK_API_KEY | Your CodivStack API key | Yes (for authenticated requests) | - |
| CODIVSTACK_API_BASE | Base URL for the API | No | https://api.codivstack.dev |
Examples
Working with Files
import { Sandbox } from '@codivstack/sdk';
// Create a sandbox and manage files
const sandbox = await Sandbox.create({ size: 'medium' });
// Write multiple files at once
await sandbox.files.write([
{ path: '/app.js', data: 'const express = require("express");' },
{ path: '/package.json', data: JSON.stringify({ name: 'app', version: '1.0.0' }, null, 2) },
{ path: '/README.md', data: '# My App\n\nWelcome to my application!' }
]);
// List all files
const files = await sandbox.files.list('/');
console.log('Created files:', files.map(f => f.name));
// Read multiple files
const contents = await sandbox.files.read(['/app.js', '/package.json']);
console.log('File contents:', contents);
// Copy and move files
await sandbox.files.copy('/app.js', '/app.backup.js');
await sandbox.files.move('/README.md', '/docs/README.md');
// Clean up
await sandbox.stop();List and Filter Sandboxes
import { Sandbox } from '@codivstack/sdk';
const { sandboxes, total } = await Sandbox.list();
const runningSandboxes = sandboxes.filter(s => s.status === 'running');
console.log(`You have ${runningSandboxes.length} running sandboxes out of ${total} total`);Create Multiple Sandboxes
import { Sandbox } from '@codivstack/sdk';
const sizes = ['small', 'medium', 'large'] as const;
const sandboxes = await Promise.all(
sizes.map(size =>
Sandbox.create({
size,
name: `sandbox-${size}`
})
)
);
// Write initial files to each sandbox
for (const sandbox of sandboxes) {
await sandbox.files.write('/index.js', `console.log("Sandbox: ${sandbox.displayName}");`);
}
console.log('Created sandboxes:', sandboxes.map(s => s.sandboxId));Run Dev Server
import { Sandbox } from '@codivstack/sdk';
async function startDevEnvironment() {
// Create a sandbox
const sandbox = await Sandbox.create({
size: 'medium',
name: 'dev-environment'
});
// Write project files
await sandbox.files.write([
{
path: '/package.json',
data: JSON.stringify({
name: 'my-app',
scripts: { dev: 'vite --host 0.0.0.0' },
dependencies: { vite: '^5.0.0' }
}, null, 2)
},
{
path: '/index.html',
data: '<html><body><h1>Hello CodivStack!</h1></body></html>'
}
]);
// Install dependencies
console.log('Installing dependencies...');
await sandbox.commands.npmInstall();
// Start dev server in background
console.log('Starting dev server...');
await sandbox.commands.startBackground('npm run dev');
console.log(`Dev server running at sandbox: ${sandbox.sandboxId}`);
console.log('Access via your sandbox subdomain');
return sandbox;
}Monitor Sandbox Health
import { Sandbox, Health } from '@codivstack/sdk';
async function monitor() {
// Check API health
const apiHealthy = await Health.check();
if (!apiHealthy) {
console.error('API is down');
return;
}
// Check all sandboxes
const { sandboxes } = await Sandbox.list();
for (const sandboxInfo of sandboxes) {
const status = await Sandbox.getStatus(sandboxInfo.id);
console.log(`${sandboxInfo.id}: ${status.status}`);
if (status.status === 'running') {
const metrics = await Sandbox.getMetrics(sandboxInfo.id);
console.log(` CPU: ${metrics.metrics.cpuPercent}%`);
console.log(` Memory: ${metrics.metrics.memoryPercent}%`);
}
}
}React Terminal & Console Components
The SDK includes React components for Terminal (interactive shell) and Console (read-only workflow output).
Installation
When using the React components, you also need to install peer dependencies:
npm install @codivstack/sdk xterm @xterm/addon-fit @xterm/addon-web-linksTheme Object
The SDK provides built-in dark and light themes via a single Theme object:
import { Theme } from '@codivstack/sdk/react';
// Access themes
Theme.dark // Dark terminal theme
Theme.light // Light terminal theme
// Usage with theme detection
const currentTheme = isDarkMode ? Theme.dark : Theme.light;Basic Terminal Usage
import { useState, useEffect } from 'react';
import { Terminal, Theme } from '@codivstack/sdk/react';
import { Sandbox } from '@codivstack/sdk';
function App() {
const [wsUrl, setWsUrl] = useState<string>('');
const [isDark, setIsDark] = useState(true);
useEffect(() => {
async function init() {
const sandbox = await Sandbox.create({ size: 'standard' });
setWsUrl(sandbox.wssUrl);
}
init();
}, []);
if (!wsUrl) return <div>Loading...</div>;
return (
<div style={{ width: '100%', height: '500px' }}>
<Terminal
wsUrl={wsUrl}
theme={isDark ? Theme.dark : Theme.light}
onOpen={() => console.log('Connected!')}
onClose={() => console.log('Disconnected')}
onReady={() => console.log('Terminal ready')}
/>
</div>
);
}Console Usage (Read-only Workflow Output)
Console uses the same Terminal component but connects to the console WebSocket endpoint:
import { Terminal, Theme } from '@codivstack/sdk/react';
import { Sandbox } from '@codivstack/sdk';
function DevServerConsole() {
const [sandbox, setSandbox] = useState<SandboxInstance | null>(null);
const [consoleUrl, setConsoleUrl] = useState<string>('');
useEffect(() => {
async function init() {
const sb = await Sandbox.create({ size: 'standard' });
setSandbox(sb);
// Start dev server via console API
await sb.console.start({ command: 'npm run dev -- --host 0.0.0.0' });
// Set console URL for Terminal component
setConsoleUrl(sb.console.url);
}
init();
}, []);
if (!consoleUrl) return <div>Loading...</div>;
return (
<div style={{ width: '100%', height: '300px' }}>
<h3>Dev Server Output (Read-only)</h3>
<Terminal
wsUrl={consoleUrl}
theme={Theme.dark}
connectedMessage=""
welcomeMessage=""
/>
</div>
);
}useTerminal Hook
For more control, use the useTerminal hook:
import { useTerminal, Theme, getScrollbarStyles } from '@codivstack/sdk/react';
function CustomTerminal({ wsUrl }: { wsUrl: string }) {
const [isDark, setIsDark] = useState(true);
const theme = isDark ? Theme.dark : Theme.light;
const { containerRef, ref, isConnected, isConnecting, isReady, error } = useTerminal({
wsUrl,
theme,
fontSize: 14,
autoReconnect: true,
onOpen: (ws) => console.log('Connected'),
onClose: (event) => console.log('Closed:', event.code),
onData: (data) => console.log('User typed:', data),
onReady: () => console.log('Terminal ready'),
onResize: (cols, rows) => console.log(`Resized: ${cols}x${rows}`),
});
return (
<div>
<style>{getScrollbarStyles(theme)}</style>
<div>
Status: {isConnected ? 'Connected' : isConnecting ? 'Connecting...' : 'Disconnected'}
{isReady && ' (Ready)'}
</div>
{error && <div style={{ color: 'red' }}>Error: {error}</div>}
<div
ref={containerRef}
className="codivstack-terminal"
style={{ height: '400px', backgroundColor: theme.background }}
/>
<button onClick={() => ref.clear()}>Clear</button>
<button onClick={() => ref.fit()}>Fit</button>
<button onClick={() => ref.scrollToBottom()}>Scroll to Bottom</button>
<button onClick={() => ref.disconnect()}>Disconnect</button>
</div>
);
}Scrollbar Styles
The SDK provides a getScrollbarStyles function for custom scrollbar styling:
import { getScrollbarStyles, Theme } from '@codivstack/sdk/react';
// In your component
<style>{getScrollbarStyles(Theme.dark)}</style>
// Or inject globally once
useEffect(() => {
const styleEl = document.createElement('style');
styleEl.textContent = getScrollbarStyles(Theme.dark);
document.head.appendChild(styleEl);
}, []);Terminal Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| wsUrl | string | required | WebSocket URL for terminal connection |
| theme | TerminalTheme | Theme.dark | Terminal color theme |
| fontSize | number | 14 | Font size in pixels |
| fontFamily | string | 'Monaco, Menlo...' | Font family |
| cursorBlink | boolean | true | Enable cursor blinking |
| scrollback | number | 1000 | Number of scrollback lines |
| autoConnect | boolean | true | Connect automatically on mount |
| autoReconnect | boolean | true | Reconnect on disconnect |
| reconnectInterval | number | 3000 | Reconnect delay in ms |
| maxReconnectAttempts | number | 10 | Max reconnection attempts |
| welcomeMessage | string | - | Message shown on init |
| connectedMessage | string | '✓ Connected' | Message on connection |
| disconnectedMessage | string | '✗ Disconnected' | Message on disconnect |
Event Callbacks
| Callback | Type | Description |
|----------|------|-------------|
| onOpen | (ws: WebSocket) => void | WebSocket connected |
| onClose | (event: CloseEvent) => void | WebSocket closed |
| onError | (event: Event) => void | WebSocket error |
| onMessage | (data: string \| ArrayBuffer) => void | Data received |
| onData | (data: string) => void | User typed data |
| onResize | (cols: number, rows: number) => void | Terminal resized |
| onReconnect | (attempt: number) => void | Reconnection attempt |
| onReady | () => void | Terminal initialized and ready |
Custom Themes
You can create custom themes or extend the built-in ones:
import { Theme, TerminalTheme } from '@codivstack/sdk/react';
// Extend existing theme
const customDark: TerminalTheme = {
...Theme.dark,
background: '#1a1b26',
foreground: '#a9b1d6',
};
// Create new theme
const tokyoNight: TerminalTheme = {
background: '#1a1b26',
foreground: '#a9b1d6',
cursor: '#c0caf5',
cursorAccent: '#1a1b26',
selectionBackground: '#33467c',
black: '#32344a',
red: '#f7768e',
green: '#9ece6a',
yellow: '#e0af68',
blue: '#7aa2f7',
magenta: '#bb9af7',
cyan: '#7dcfff',
white: '#a9b1d6',
brightBlack: '#444b6a',
brightRed: '#ff7a93',
brightGreen: '#b9f27c',
brightYellow: '#ff9e64',
brightBlue: '#7da6ff',
brightMagenta: '#bb9af7',
brightCyan: '#0db9d7',
brightWhite: '#acb0d0',
scrollbarSlider: 'rgba(255, 255, 255, 0.2)',
scrollbarHoverSlider: 'rgba(255, 255, 255, 0.3)',
};
<Terminal wsUrl={wsUrl} theme={tokyoNight} />TerminalRef Methods
When using useTerminal, you get access to these methods via ref:
const { ref } = useTerminal({ wsUrl });
ref.write('Hello'); // Write text to terminal
ref.writeln('World'); // Write line to terminal
ref.clear(); // Clear terminal
ref.focus(); // Focus terminal
ref.fit(); // Fit terminal to container
ref.scrollToBottom(); // Scroll to bottom
ref.refresh(); // Refresh terminal display
ref.connect(); // Manually connect
ref.disconnect(); // Disconnect
ref.terminal; // Access underlying xterm.js instance
ref.ws; // Access WebSocket instanceTerminal vs Console Summary
| Feature | Terminal | Console |
|---------|----------|---------|
| Purpose | Interactive shell | Workflow output display |
| Input | Full input support | Read-only (input ignored) |
| URL | sandbox.wssUrl | sandbox.console.url |
| Component | <Terminal /> | <Terminal /> (same!) |
| Start | Auto-connects | sandbox.console.start() first |
| Stop | ref.disconnect() | sandbox.console.stop() |
Complete Example with Both Terminal and Console
import { useState, useEffect } from 'react';
import { Terminal, Theme, useTerminal } from '@codivstack/sdk/react';
import { Sandbox, SandboxInstance } from '@codivstack/sdk';
function IDEPanel() {
const [sandbox, setSandbox] = useState<SandboxInstance | null>(null);
const [isDark, setIsDark] = useState(true);
const theme = isDark ? Theme.dark : Theme.light;
useEffect(() => {
async function init() {
const sb = await Sandbox.create({ size: 'standard' });
setSandbox(sb);
}
init();
}, []);
if (!sandbox) return <div>Creating sandbox...</div>;
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100vh' }}>
{/* Console Panel - Read-only dev server output */}
<div style={{ flex: 1 }}>
<h3>Console (npm run dev)</h3>
<ConsolePanel sandbox={sandbox} theme={theme} />
</div>
{/* Terminal Panel - Interactive shell */}
<div style={{ flex: 1 }}>
<h3>Terminal</h3>
<Terminal
wsUrl={sandbox.wssUrl}
theme={theme}
welcomeMessage="Welcome to CodivStack Terminal!"
/>
</div>
</div>
);
}
function ConsolePanel({ sandbox, theme }: { sandbox: SandboxInstance; theme: TerminalTheme }) {
const [consoleUrl, setConsoleUrl] = useState('');
const [isRunning, setIsRunning] = useState(false);
const startDevServer = async () => {
await sandbox.console.start({ command: 'npm run dev -- --host 0.0.0.0' });
setConsoleUrl(sandbox.console.url);
setIsRunning(true);
};
const stopDevServer = async () => {
await sandbox.console.stop();
setIsRunning(false);
};
return (
<div>
<div>
{!isRunning ? (
<button onClick={startDevServer}>Start Dev Server</button>
) : (
<button onClick={stopDevServer}>Stop</button>
)}
</div>
{consoleUrl && (
<Terminal
wsUrl={consoleUrl}
theme={theme}
connectedMessage=""
welcomeMessage=""
/>
)}
</div>
);
}CodivX AI Agent
The SDK includes an AI Agent client for autonomous code development. The agent supports two backends:
- Inngest AgentKit + Anthropic Claude (Recommended) - Direct Anthropic API integration with Inngest orchestration
- AWS Bedrock (Legacy) - AWS-managed Claude deployment
Inngest AgentKit Integration
CodivX Agent v2.0+ uses Inngest AgentKit with Anthropic Claude (claude-sonnet-4-5-20250929) for real-time AI agent capabilities. Features include:
- SSE Streaming: Token-by-token text streaming to frontend
- Inngest Dashboard: Full visibility into agent runs, tool calls, and metrics
- Direct HTTP: Tool execution via sandbox HTTP API (port 30001)
- Session Management: Conversation continuity with session IDs
Inngest Event Types
The Inngest AgentKit streams structured events via SSE:
| Event Type | Description | Data Fields |
|------------|-------------|-------------|
| run.started | Agent run initiated | sessionId, sandboxId, timestamp |
| agent.start | Agent iteration started | iteration |
| inference.start | LLM inference started | - |
| text.delta | Streaming text chunk | delta (token) |
| reasoning | Claude's thinking process | text (markdown) |
| tool_call.started | Tool execution started | toolName, toolCallId, status, input |
| tool_call.completed | Tool execution finished | toolName, toolCallId, status, success, output, error? |
| agent.finish | Agent iteration completed | output |
| inference.finish | LLM inference completed | usage (tokens) |
| run.completed | Agent run finished | success, text, toolCalls |
| stream.ended | SSE stream closed | - |
| error | Error occurred | error, code? |
| debug | Debug info (when debug=true) | data |
Inngest Streaming Example
import { configure, AgentClient } from '@codivstack/sdk';
configure({
baseUrl: 'https://api.codivstack.com',
apiKey: 'cs_your_api_key'
});
const agent = new AgentClient();
// Streaming chat with Inngest events
for await (const event of agent.chat({
sandboxId: 'sandbox-abc123',
message: 'Create a React todo app'
})) {
switch (event.type) {
// Streaming text (token by token)
case 'text.delta':
process.stdout.write(event.data.delta);
break;
// Claude's reasoning/thinking
case 'reasoning':
console.log(`\n💭 ${event.data.text}`);
break;
// Tool started (e.g., writing file)
case 'tool_call.started':
console.log(`\n⚡ ${event.data.toolName}: ${event.data.status}`);
// status: 'reading' | 'writing' | 'executing' | 'listing' | ...
break;
// Tool completed
case 'tool_call.completed':
const icon = event.data.success ? '✅' : '❌';
console.log(`${icon} ${event.data.toolName}: ${event.data.status}`);
// status: 'read' | 'written' | 'executed' | 'listed' | 'failed' | ...
break;
// Run completed
case 'run.completed':
console.log('\n✓ Done');
break;
// Error
case 'error':
console.error('Error:', event.data.error);
break;
}
}Inngest Callback-Based Streaming
const result = await agent.chatWithCallbacks({
sandboxId: 'sandbox-abc123',
message: 'Read the package.json file'
}, {
onTextDelta: (text) => process.stdout.write(text),
onReasoning: (text) => console.log('💭', text),
onToolCallStarted: (event) => {
console.log(`⚡ ${event.data.toolName} (${event.data.status})`);
},
onToolCallCompleted: (event) => {
const icon = event.data.success ? '✅' : '❌';
console.log(`${icon} ${event.data.toolName} (${event.data.status})`);
},
onRunStarted: (event) => console.log('Started:', event.data.sessionId),
onRunCompleted: (event) => console.log('Completed:', event.data.success),
onError: (err) => console.error('Error:', err.message),
onStreamEnded: () => console.log('Stream ended')
});
console.log('Full response:', result.text);
console.log('Tool calls:', result.toolCallsStarted.length);Inngest Tool Status Flow
Each tool call transitions through statuses:
| Tool | tool_call.started status | tool_call.completed status |
|------|----------------------------|------------------------------|
| readFile | reading | read |
| writeFile | writing | written |
| listDirectory | listing | listed |
| runCommand | executing | executed |
| deleteFile | deleting | deleted |
| startWorkflow | starting | started |
| stopWorkflow | stopping | stopped |
| getWorkspaceInfo | fetching | fetched |
| (any error) | - | failed |
Inngest Dashboard Visibility
CodivX Agent sends events to Inngest Cloud for monitoring:
codivx/chat.started: When a chat session beginscodivx/chat.completed: When a chat session completes successfullycodivx/chat.failed: When a chat session fails
View these in your Inngest Dashboard under the codivx namespace.
Stream Utilities
import {
EventBuffer,
textStream,
reasoningStream,
toolActivityStream
} from '@codivstack/sdk';
// Collect all events with EventBuffer
const buffer = new EventBuffer();
for await (const event of agent.chat({ sandboxId, message })) {
buffer.add(event);
}
console.log('Full text:', buffer.getText());
console.log('Stats:', buffer.getStats());
console.log('Usage:', buffer.getUsage());
// Text-only stream (just the deltas)
for await (const text of textStream(agent.chat({ sandboxId, message }))) {
process.stdout.write(text);
}
// Reasoning-only stream
for await (const reasoning of reasoningStream(agent.chat({ sandboxId, message }))) {
console.log('Thinking:', reasoning);
}
// Tool activity stream (started + completed events)
for await (const event of toolActivityStream(agent.chat({ sandboxId, message }))) {
if (event.type === 'tool_call.started') {
console.log(`Started: ${event.data.toolName}`);
} else if (event.type === 'tool_call.completed') {
console.log(`Completed: ${event.data.toolName} - ${event.data.success}`);
}
}Inngest TypeScript Types
import type {
// New Inngest AgentKit events
AgentEvent,
RunStartedEvent,
AgentStartEvent,
InferenceStartEvent,
TextDeltaEvent,
ReasoningEvent,
ToolCallStartedEvent,
ToolCallCompletedEvent,
AgentFinishEvent,
InferenceFinishEvent,
RunCompletedEvent,
StreamEndedEvent,
ErrorEvent,
DebugEvent,
// Tool status types
ToolExecutingStatus, // 'reading' | 'writing' | 'executing' | ...
ToolCompletedStatus, // 'read' | 'written' | 'executed' | 'failed' | ...
// Request/Response
ChatRequest,
ChatOptions,
AgentConfig,
AgentConfigResponse,
} from '@codivstack/sdk';Server-Side Configuration
For Inngest AgentKit to work, the server requires:
| Environment Variable | Description |
|---------------------|-------------|
| ANTHROPIC_API_KEY | Anthropic Claude API key (required) |
| INNGEST_EVENT_KEY | Inngest Cloud Event Key (optional, for Dashboard) |
| INNGEST_SIGNING_KEY | Inngest Cloud Signing Key (optional) |
Legacy: AWS Bedrock Integration
The legacy Bedrock integration uses event types: thinking, toolCall, toolResult, chunk, done.
Agent Quick Start
Option 1: Use global configure() (Recommended)
Same config works for both Sandbox and Agent:
import { configure, Sandbox, AgentClient } from '@codivstack/sdk';
// Configure ONCE for ALL SDK features
configure({
baseUrl: 'https://api.codivstack.dev',
apiKey: 'cs_your_api_key'
});
// Create sandbox (uses global config)
const sandbox = await Sandbox.create({ size: 'standard' });
// Create agent (uses global config automatically)
const agent = new AgentClient();
// Streaming chat with full event handling
for await (