@bunnyshell/sdk
v0.1.13
Published
Official TypeScript/JavaScript SDK for Bunnyshell Sandboxes - 100% feature coverage
Downloads
69
Maintainers
Readme
Bunnyshell JavaScript/TypeScript SDK
Official TypeScript/JavaScript SDK for Bunnyshell Sandboxes - Execute code safely in isolated microVM environments.
Features
- 🚀 47/47 Features - 100% API coverage
- 🔒 Type Safe - Full TypeScript support with auto-complete
- ⚡ Async/Await - Native Promise-based API
- 🌊 WebSocket Streaming - Real-time code execution, terminal, file watching
- 🎨 Rich Outputs - Automatic capture of plots, images, DataFrames
- 🔐 Security First - Environment variables for secrets, execution timeouts
- 📦 Modern JavaScript - ESM and CommonJS support
- 🎯 Zero Dependencies - Minimal bundle size
Installation
npm install @bunnyshell/sdk
# or
yarn add @bunnyshell/sdk
# or
pnpm add @bunnyshell/sdkQuick Start
import { Sandbox } from '@bunnyshell/sdk';
// Create sandbox
const sandbox = await Sandbox.create({
template: 'code-interpreter',
apiKey: 'your-api-key'
});
try {
// Execute code
const result = await sandbox.runCode(`
import pandas as pd
df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})
print(df.sum())
`);
console.log(result.stdout);
// Output: a 6
// b 15
} finally {
await sandbox.kill();
}Environment Variables (IMPORTANT!)
✅ ALWAYS pass secrets via environment variables:
// ✅ GOOD: Secrets via environment variables
const result = await sandbox.runCode(`
const apiKey = process.env.OPENAI_API_KEY;
// Use apiKey safely...
`, {
env: { OPENAI_API_KEY: 'sk-...' }
});
// ❌ BAD: Never hardcode secrets
// const result = await sandbox.runCode('const apiKey = "sk-..."'); // DON'T!Core Features
Code Execution
// Synchronous execution
const result = await sandbox.runCode("console.log('Hello')");
// With environment variables
const result = await sandbox.runCode(
"console.log(process.env.MY_VAR)",
{ env: { MY_VAR: 'value' } }
);
// Async execution (non-blocking)
const executionId = await sandbox.runCodeAsync("await sleep(10000)");
// Background execution (fire-and-forget)
const executionId = await sandbox.runCodeBackground("// long task...");
// IPython/Jupyter-style
const result = await sandbox.runIPython("df.describe()");
// Multiple languages
const pythonResult = await sandbox.runCode('print("Python")', { language: 'python' });
const jsResult = await sandbox.runCode('console.log("JS")', { language: 'javascript' });
const bashResult = await sandbox.runCode('echo "Bash"', { language: 'bash' });Real-time Streaming (WebSocket)
for await (const message of sandbox.runCodeStream(`
import time
for i in range(5):
print(f"Step {i+1}")
time.sleep(1)
`)) {
if (message.type === 'stdout') {
process.stdout.write(message.data);
} else if (message.type === 'result') {
console.log(`Exit code: ${message.exit_code}`);
}
}File Operations
// Write file
await sandbox.files.write('/workspace/data.txt', 'Hello, World!');
// Read file
const content = await sandbox.files.read('/workspace/data.txt');
// List directory
const files = await sandbox.files.list('/workspace');
files.forEach(file => {
console.log(`${file.name} (${file.sizeKb.toFixed(2)}KB)`);
});
// Upload local file
await sandbox.files.upload('./local.txt', '/workspace/remote.txt');
// Download remote file
await sandbox.files.download('/workspace/remote.txt', './local.txt');
// Check existence
if (await sandbox.files.exists('/workspace/data.txt')) {
console.log('File exists!');
}
// Remove file/directory
await sandbox.files.remove('/workspace/data.txt');
// Create directory
await sandbox.files.mkdir('/workspace/mydir');
// Watch filesystem (WebSocket)
for await (const event of sandbox.files.watch('/workspace')) {
console.log(`${event.event}: ${event.path}`);
}Commands
// Run shell command
const result = await sandbox.commands.run('ls -la /workspace');
console.log(result.stdout);
// With environment variables
const result = await sandbox.commands.run(
'echo "Key: $API_KEY"',
{ env: { API_KEY: 'secret' } }
);
// Custom working directory
const result = await sandbox.commands.run(
'pwd',
{ workingDir: '/workspace/myproject' }
);Environment Variables
// Get all
const envVars = await sandbox.env.getAll();
// Set all (replace)
await sandbox.env.setAll({ API_KEY: 'sk-...', DEBUG: 'true' });
// Update (merge)
await sandbox.env.update({ NEW_VAR: 'value' });
// Delete
await sandbox.env.delete(['VAR_TO_DELETE']);
// Get single
const value = await sandbox.env.get('API_KEY');
// Set single
await sandbox.env.set('API_KEY', 'sk-...');Process Management
// List processes
const processes = await sandbox.listProcesses();
processes.forEach(proc => {
console.log(`${proc.pid}: ${proc.name} (CPU: ${proc.cpu_percent}%)`);
});
// Kill process
await sandbox.killProcess(1234);Metrics & Health
// Get metrics
const metrics = await sandbox.getMetricsSnapshot();
console.log(`Total executions: ${metrics.total_executions}`);
console.log(`Uptime: ${metrics.uptime_seconds}s`);
// Health check
const health = await sandbox.getHealth();
console.log(`Status: ${health.status}`);
// Cache management
const cacheStats = await sandbox.cache.stats();
console.log(`Cache size: ${cacheStats.cache.size}`);
await sandbox.cache.clear();Desktop Automation
// Note: Requires 'desktop' template
const sandbox = await Sandbox.create({ template: 'desktop', apiKey: '...' });
// Mouse operations
await sandbox.desktop.mouseClick({ x: 500, y: 300, button: 'left' });
await sandbox.desktop.mouseMove({ x: 600, y: 400 });
await sandbox.desktop.dragDrop({ fromX: 100, fromY: 100, toX: 300, toY: 300 });
// Keyboard
await sandbox.desktop.keyboardType('Hello, World!');
await sandbox.desktop.keyboardPress('Return');
await sandbox.desktop.keyboardPress('Control_L+c'); // Ctrl+C
// Clipboard
await sandbox.desktop.clipboardSet('text to copy');
const content = await sandbox.desktop.clipboardGet();
// Screenshots
const screenshot = await sandbox.desktop.screenshot();
fs.writeFileSync('screenshot.png', screenshot);
// OCR (text recognition)
const text = await sandbox.desktop.ocr();
console.log(`Found text: ${text}`);
// Find elements
const element = await sandbox.desktop.findElement({ text: 'Button' });
if (element) {
await sandbox.desktop.mouseClick({ x: element.x, y: element.y });
}
// VNC connection
const vncInfo = await sandbox.desktop.getVncUrl();
console.log(`VNC URL: ${vncInfo.url}`);Interactive Terminal (WebSocket)
const terminal = await sandbox.terminal.connect();
// Send commands
await sandbox.terminal.sendInput(terminal, 'ls -la\n');
// Receive output
for await (const message of sandbox.terminal.iterOutput(terminal)) {
if (message.type === 'output') {
process.stdout.write(message.data);
}
}Advanced Configuration
const sandbox = await Sandbox.create({
template: 'code-interpreter',
apiKey: 'your-api-key',
vcpu: 4, // 4 vCPUs
memoryMb: 4096, // 4GB RAM
diskGb: 20, // 20GB disk
region: 'us-west-2', // Specific region
timeout: 600, // 10 minute timeout
envVars: { // Pre-set environment variables
DATABASE_URL: 'postgres://...',
API_KEY: 'sk-...'
}
});Error Handling
import {
BunnyshellError,
AuthenticationError,
NotFoundError,
FileNotFoundError,
CodeExecutionError,
RateLimitError,
ServerError
} from '@bunnyshell/sdk/errors';
try {
const sandbox = await Sandbox.create({
template: 'code-interpreter',
apiKey: '...'
});
try {
const result = await sandbox.runCode("print('Hello')");
} finally {
await sandbox.kill();
}
} catch (error) {
if (error instanceof AuthenticationError) {
console.log(`Auth failed: ${error.message}`);
console.log(`Request ID: ${error.requestId}`);
} else if (error instanceof FileNotFoundError) {
console.log(`File not found: ${error.path}`);
} else if (error instanceof CodeExecutionError) {
console.log(`Code execution failed: ${error.message}`);
} else if (error instanceof RateLimitError) {
console.log(`Rate limited: ${error.message}`);
} else if (error instanceof BunnyshellError) {
console.log(`API error: ${error.message}`);
console.log(`Status code: ${error.statusCode}`);
}
}Best Practices
1. Always Use Try/Finally
// ✅ GOOD: Guaranteed cleanup
const sandbox = await Sandbox.create({ ... });
try {
// Work here
} finally {
await sandbox.kill(); // Always executed
}
// ❌ BAD: May leak if error occurs
const sandbox = await Sandbox.create({ ... });
// ... work ...
await sandbox.kill(); // Might not execute if error!2. Set Timeouts
// Prevent infinite execution
const result = await sandbox.runCode(code, { timeout: 30 });3. Optimize Performance
// Set environment variables once
await sandbox.env.setAll({ API_KEY: 'sk-...', DB_URL: '...' });
// Now available in all executions
for (let i = 0; i < 100; i++) {
await sandbox.runCode('...'); // Env vars already set
}
// Parallel execution
const [r1, r2, r3] = await Promise.all([
sandbox.runCode('print(1)'),
sandbox.runCode('print(2)'),
sandbox.runCode('print(3)')
]);Use Cases
OpenAI: ChatGPT Code Interpreter
async function executeUserCode(userCode: string) {
const sandbox = await Sandbox.create({ template: 'code-interpreter' });
try {
const result = await sandbox.runCode(userCode, { timeout: 30 });
return {
output: result.stdout,
error: result.stderr,
richOutputs: result.rich_outputs // Images, plots
};
} finally {
await sandbox.kill();
}
}Stripe: Payment Reports
const sandbox = await Sandbox.create({ template: 'code-interpreter' });
try {
await sandbox.env.set('STRIPE_SECRET_KEY', process.env.STRIPE_SECRET_KEY!);
const result = await sandbox.runCode(`
import stripe
stripe.api_key = os.environ['STRIPE_SECRET_KEY']
charges = stripe.Charge.list(limit=100)
# Generate report...
`);
} finally {
await sandbox.kill();
}TypeScript Support
Full TypeScript support with auto-complete and type checking:
import { Sandbox, ExecutionResult, FileInfo } from '@bunnyshell/sdk';
const sandbox: Sandbox = await Sandbox.create({ ... });
const result: ExecutionResult = await sandbox.runCode('...');
const files: FileInfo[] = await sandbox.files.list('/workspace');Documentation
- Cookbook: See cookbook/javascript for 10 complete examples
- API Reference: docs.bunnyshell.com
- GitHub: github.com/bunnyshell/sandbox-sdks
License
MIT License - See LICENSE file for details.
Support
- Issues: github.com/bunnyshell/sandbox-sdks/issues
- Email: [email protected]
- Documentation: docs.bunnyshell.com
