diff --git a/apps/web/src/app/docs/page.tsx b/apps/web/src/app/docs/page.tsx index 325e828..7a291a1 100644 --- a/apps/web/src/app/docs/page.tsx +++ b/apps/web/src/app/docs/page.tsx @@ -66,9 +66,20 @@ const NAV_SECTIONS = [ { title: 'Resources', items: [ - { id: 'faq', label: 'FAQ' }, - { id: 'troubleshooting', label: 'Troubleshooting' }, - { id: 'changelog', label: 'Changelog' }, + { id: 'faq', label: 'FAQ', description: 'Common questions' }, + { id: 'troubleshooting', label: 'Troubleshooting', description: 'Common issues' }, + { id: 'changelog', label: 'Changelog', description: 'Latest updates' }, + { id: 'style-guide', label: 'Style Guide', description: 'UI components' }, + ], + }, + { + title: 'Developers', + items: [ + { + id: 'developers-guide', + label: 'Developers Guide', + description: 'Scenarios, testing & quality', + }, ], }, ]; diff --git a/packages/tools/official/blocks.yml b/packages/tools/official/blocks.yml index 960198b..22fd8d8 100644 --- a/packages/tools/official/blocks.yml +++ b/packages/tools/official/blocks.yml @@ -1,11 +1,10 @@ -project: - name: "tpmjs-official-tools" - domain: "tpmjs.tools" +name: "tpmjs-official-tools" +root: "." -targets: - kind: "block" - discover: - root: "." +# AI Configuration +ai: + provider: "openai" + model: "gpt-4o-mini" # ============================================================================= # PHILOSOPHY - Core principles that guide all tool development @@ -421,6 +420,29 @@ domain: fields: [stdout, stderr, exitCode] description: "Result of executing a command on an exe.dev VM" + # ------------------------------------------------------------------------- + # E2B Cloud Sandbox entities + # ------------------------------------------------------------------------- + e2b_sandbox: + fields: [sandboxId, templateId, status, startedAt, clientId, metadata] + description: "E2B cloud sandbox instance for AI code execution" + + e2b_sandbox_list: + fields: [sandboxes, count] + description: "Collection of E2B sandboxes" + + e2b_exec_result: + fields: [stdout, stderr, exitCode, results, error, duration] + description: "Result of code execution in E2B sandbox" + + e2b_file_info: + fields: [name, path, type, size] + description: "File or directory information in E2B sandbox" + + e2b_metrics: + fields: [cpuPct, memUsedMB, networkIngressMB, networkEgressMB] + description: "E2B sandbox resource metrics" + # ------------------------------------------------------------------------- # HLLM entities # ------------------------------------------------------------------------- @@ -7604,28 +7626,602 @@ blocks: description: "Brief summary of the conversation" measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + # --------------------------------------------------------------------------- + # R) E2B Cloud Sandbox - AI Code Execution Platform (22 tools) + # --------------------------------------------------------------------------- + # Environment Variables Required: + # E2B_API_KEY - API key from https://e2b.dev/dashboard + # + # SDK Packages: + # JavaScript/TypeScript: @e2b/code-interpreter, e2b + # Python: e2b-code-interpreter, e2b + # CLI: @e2b/cli (npm) or e2b (brew) + # --------------------------------------------------------------------------- + + e2b.createSandbox: + type: utility + description: "Creates a new E2B cloud sandbox from a template. Sandboxes are isolated Linux environments for AI code execution with configurable resources and timeout." + path: "e2b" + domain_rules: + - id: sdk_integration + description: | + Must use E2B SDK to create sandbox: + - Import Sandbox from @e2b/code-interpreter or e2b + - Use Sandbox.create() with template and options + - Support custom metadata, timeout, and environment variables + - id: auth_handling + description: "Must use E2B_API_KEY environment variable for authentication" + - id: resource_config + description: "Support configuring CPU cores, memory, and timeout" + inputs: + - name: template + type: string + optional: true + description: "Template ID to use (default: base, code-interpreter-v1 for Python/JS)" + - name: timeoutMs + type: number + optional: true + description: "Sandbox timeout in milliseconds (default: 300000 = 5 minutes)" + - name: metadata + type: object + optional: true + description: "Custom metadata key-value pairs to attach to sandbox" + - name: envVars + type: object + optional: true + description: "Environment variables to set in the sandbox" + - name: cpuCount + type: number + optional: true + description: "Number of CPU cores (1-8)" + - name: memoryMB + type: number + optional: true + description: "Memory in MB (128-8192)" + outputs: + - name: sandboxId + type: string + description: "Unique identifier for the sandbox" + - name: templateId + type: string + description: "Template the sandbox was created from" + - name: clientId + type: string + description: "Client ID for WebSocket connections" + measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + + e2b.getSandbox: + type: utility + description: "Connect to and verify an existing E2B sandbox is running. Returns sandbox ID and connection status." + path: "e2b" + domain_rules: + - id: sdk_integration + description: "Must use Sandbox.connect() to verify sandbox exists and is accessible" + - id: error_handling + description: "Must throw descriptive error when sandbox ID is empty or connection fails" + - id: input_validation + description: "Must validate sandboxId is non-empty before connecting" + inputs: + - name: sandboxId + type: string + description: "ID of the sandbox to retrieve" + outputs: + - name: sandboxId + type: string + description: "ID of the connected sandbox" + - name: templateId + type: string + description: "Template ID if available, defaults to 'base'" + - name: status + type: string + description: "Connection status (running)" + measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + + e2b.listSandboxes: + type: utility + description: "Lists all running E2B sandboxes in your account with their status and metadata." + path: "e2b" + domain_rules: + - id: sdk_integration + description: "Must use Sandbox.list() to get all running sandboxes" + - id: filtering + description: "Support filtering by metadata, template, or status" + inputs: + - name: templateId + type: string + optional: true + description: "Filter sandboxes by template ID" + - name: metadata + type: object + optional: true + description: "Filter sandboxes by metadata key-value pairs" + outputs: + - name: sandboxes + type: e2b_sandbox[] + description: "Array of running sandboxes" + - name: count + type: number + description: "Total number of sandboxes" + measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + + e2b.killSandbox: + type: utility + description: "Terminates a running E2B sandbox immediately. All data and processes are destroyed." + path: "e2b" + domain_rules: + - id: sdk_integration + description: "Must call sandbox.kill() or Sandbox.kill(sandboxId)" + - id: cleanup + description: "Ensure proper cleanup and return confirmation" + inputs: + - name: sandboxId + type: string + description: "ID of the sandbox to terminate" + outputs: + - name: killed + type: boolean + description: "Whether the sandbox was successfully terminated" + - name: sandboxId + type: string + description: "ID of the terminated sandbox" + measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + + e2b.setTimeout: + type: utility + description: "Sets or extends the timeout for an E2B sandbox. The sandbox will be automatically killed when the timeout expires." + path: "e2b" + domain_rules: + - id: sdk_integration + description: "Must call sandbox.setTimeout() with new timeout value" + inputs: + - name: sandboxId + type: string + description: "ID of the sandbox" + - name: timeoutMs + type: number + description: "New timeout in milliseconds from now" + outputs: + - name: success + type: boolean + description: "Whether the timeout was updated" + - name: expiresAt + type: string + description: "ISO timestamp when sandbox will expire" + measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + + e2b.runCode: + type: utility + description: "Executes code in an E2B sandbox. Supports Python, JavaScript, TypeScript, R, Java, and Bash with streaming output and result capture." + path: "e2b" + domain_rules: + - id: sdk_integration + description: | + Must use sandbox.runCode() for code execution: + - Support multiple languages via Code Interpreter template + - Handle stdout, stderr, and results (charts, dataframes) + - Support streaming output with onStdout/onStderr callbacks + - id: result_handling + description: "Must capture and return execution results including display data, errors, and output" + - id: timeout_handling + description: "Support configurable execution timeout" + inputs: + - name: sandboxId + type: string + description: "ID of the sandbox to execute in" + - name: code + type: string + description: "Code to execute" + - name: language + type: string + optional: true + description: "Language: python, javascript, typescript, r, java, bash (default: python)" + - name: timeoutMs + type: number + optional: true + description: "Execution timeout in milliseconds" + - name: envVars + type: object + optional: true + description: "Environment variables for this execution" + outputs: + - name: stdout + type: string + description: "Standard output from execution" + - name: stderr + type: string + description: "Standard error from execution" + - name: results + type: array + description: "Execution results (data, charts, dataframes as base64)" + - name: error + type: object + optional: true + description: "Error details if execution failed" + - name: duration + type: number + description: "Execution duration in milliseconds" + measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + + e2b.runCommand: + type: utility + description: "Executes a shell command in an E2B sandbox with full shell access. Supports background processes and working directory." + path: "e2b" + domain_rules: + - id: sdk_integration + description: | + Must use sandbox.commands.run() for shell commands: + - Support cwd (working directory) option + - Support background execution + - Handle stdin if provided + - id: process_handling + description: "Return process ID for background commands" + inputs: + - name: sandboxId + type: string + description: "ID of the sandbox" + - name: command + type: string + description: "Shell command to execute" + - name: cwd + type: string + optional: true + description: "Working directory for the command" + - name: background + type: boolean + optional: true + description: "Run command in background (default: false)" + - name: timeoutMs + type: number + optional: true + description: "Command timeout in milliseconds" + outputs: + - name: stdout + type: string + description: "Standard output" + - name: stderr + type: string + description: "Standard error" + - name: exitCode + type: number + description: "Exit code (0 for success)" + - name: processId + type: string + optional: true + description: "Process ID if running in background" + measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + + e2b.writeFile: + type: utility + description: "Writes content to a file in the E2B sandbox filesystem. Creates directories as needed." + path: "e2b" + domain_rules: + - id: sdk_integration + description: "Must use sandbox.files.write() to write file content" + - id: encoding_handling + description: "Support both text (string) and binary (base64) content" + inputs: + - name: sandboxId + type: string + description: "ID of the sandbox" + - name: path + type: string + description: "Absolute path in sandbox (e.g., /home/user/file.txt)" + - name: content + type: string + description: "File content (string for text, base64 for binary)" + - name: encoding + type: string + optional: true + description: "Content encoding: utf-8 (default) or base64" + outputs: + - name: success + type: boolean + description: "Whether the file was written" + - name: path + type: string + description: "Path of the written file" + - name: size + type: number + description: "Size of the written file in bytes" + measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + + e2b.readFile: + type: utility + description: "Reads content from a file in the E2B sandbox filesystem." + path: "e2b" + domain_rules: + - id: sdk_integration + description: "Must use sandbox.files.read() to read file content" + - id: encoding_handling + description: "Support returning content as text or base64 for binary files" + inputs: + - name: sandboxId + type: string + description: "ID of the sandbox" + - name: path + type: string + description: "Absolute path to file in sandbox" + - name: encoding + type: string + optional: true + description: "Return encoding: utf-8 (default) or base64" + outputs: + - name: content + type: string + description: "File content (string or base64)" + - name: path + type: string + description: "Path of the read file" + - name: size + type: number + description: "File size in bytes" + measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + + e2b.listFiles: + type: utility + description: "Lists files and directories at a path in the E2B sandbox." + path: "e2b" + domain_rules: + - id: sdk_integration + description: "Must use sandbox.files.list() to list directory contents" + inputs: + - name: sandboxId + type: string + description: "ID of the sandbox" + - name: path + type: string + description: "Absolute path to list (e.g., /home/user)" + outputs: + - name: entries + type: e2b_file_info[] + description: "Array of files and directories" + - name: count + type: number + description: "Total number of entries" + measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + + e2b.uploadFile: + type: utility + description: "Uploads a file to the E2B sandbox from base64 content or a URL." + path: "e2b" + domain_rules: + - id: sdk_integration + description: "Must use sandbox.files.write() with proper content handling" + - id: url_handling + description: "Support fetching from URL and uploading to sandbox" + inputs: + - name: sandboxId + type: string + description: "ID of the sandbox" + - name: path + type: string + description: "Destination path in sandbox" + - name: content + type: string + optional: true + description: "Base64 encoded file content" + - name: url + type: string + optional: true + description: "URL to fetch file from (alternative to content)" + outputs: + - name: success + type: boolean + description: "Whether the upload succeeded" + - name: path + type: string + description: "Path where file was uploaded" + - name: size + type: number + description: "Size of uploaded file in bytes" + measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + + e2b.downloadFile: + type: utility + description: "Downloads a file from the E2B sandbox as base64 content." + path: "e2b" + domain_rules: + - id: sdk_integration + description: "Must use sandbox.files.read() and return base64 encoded content" + inputs: + - name: sandboxId + type: string + description: "ID of the sandbox" + - name: path + type: string + description: "Path to file in sandbox" + outputs: + - name: content + type: string + description: "Base64 encoded file content" + - name: filename + type: string + description: "Name of the downloaded file" + - name: size + type: number + description: "File size in bytes" + - name: mimeType + type: string + description: "Detected MIME type of the file" + measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + + e2b.makeDirectory: + type: utility + description: "Creates a directory in the E2B sandbox filesystem. Creates parent directories as needed." + path: "e2b" + domain_rules: + - id: sdk_integration + description: "Must use sandbox.files.makeDir() to create directory" + inputs: + - name: sandboxId + type: string + description: "ID of the sandbox" + - name: path + type: string + description: "Absolute path of directory to create" + outputs: + - name: success + type: boolean + description: "Whether the directory was created" + - name: path + type: string + description: "Path of the created directory" + measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + + e2b.watchDirectory: + type: utility + description: "Watches a directory in the E2B sandbox for file changes. Returns the initial state and supports registering for change events." + path: "e2b" + domain_rules: + - id: sdk_integration + description: "Must use sandbox.files.watch() for filesystem watching" + inputs: + - name: sandboxId + type: string + description: "ID of the sandbox" + - name: path + type: string + description: "Directory path to watch" + outputs: + - name: entries + type: e2b_file_info[] + description: "Current contents of the directory" + - name: watcherId + type: string + description: "Watcher ID for stopping the watch" + measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + + e2b.pauseSandbox: + type: utility + description: "Disconnect from an E2B sandbox while keeping it running. The sandbox continues running in the cloud and can be reconnected using resumeSandbox." + path: "e2b" + domain_rules: + - id: cache_management + description: "Must remove sandbox from local cache to release connection" + - id: state_preservation + description: "Sandbox state is preserved in the cloud while disconnected" + inputs: + - name: sandboxId + type: string + description: "ID of the sandbox to disconnect from" + outputs: + - name: success + type: boolean + description: "Whether the disconnect succeeded" + - name: sandboxId + type: string + description: "ID of the disconnected sandbox (use with resumeSandbox to reconnect)" + - name: status + type: string + description: "Connection status (disconnected)" + measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + + e2b.resumeSandbox: + type: utility + description: "Reconnect to a running E2B sandbox that was previously disconnected using pauseSandbox. Optionally set a new timeout." + path: "e2b" + domain_rules: + - id: sdk_integration + description: "Must use Sandbox.connect() to reconnect to sandbox" + - id: error_handling + description: "Must throw descriptive error if sandbox no longer exists or connection fails" + inputs: + - name: sandboxId + type: string + description: "ID of the sandbox to reconnect to" + - name: timeoutMs + type: number + optional: true + description: "New timeout for the sandbox in milliseconds" + outputs: + - name: sandboxId + type: string + description: "ID of the reconnected sandbox" + - name: status + type: string + description: "Status of the sandbox after resume" + measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + + e2b.setEnvVars: + type: utility + description: "Sets environment variables in an E2B sandbox. These are available to all subsequent code executions." + path: "e2b" + domain_rules: + - id: sdk_integration + description: "Must set environment variables during sandbox creation or via API" + inputs: + - name: sandboxId + type: string + description: "ID of the sandbox" + - name: envVars + type: object + description: "Key-value pairs of environment variables to set" + outputs: + - name: success + type: boolean + description: "Whether the environment variables were set" + - name: count + type: number + description: "Number of environment variables set" + measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + + e2b.getMetrics: + type: utility + description: "Retrieves resource usage metrics for an E2B sandbox including CPU, memory, and network usage." + path: "e2b" + domain_rules: + - id: sdk_integration + description: "Must use sandbox.getMetrics() to retrieve usage statistics" + inputs: + - name: sandboxId + type: string + description: "ID of the sandbox" + outputs: + - name: cpuPct + type: number + description: "CPU usage percentage" + - name: memUsedMB + type: number + description: "Memory used in MB" + - name: networkIngressMB + type: number + description: "Network ingress in MB" + - name: networkEgressMB + type: number + description: "Network egress in MB" + measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + + e2b.installPackages: + type: utility + description: "Installs Python packages in an E2B Code Interpreter sandbox using pip." + path: "e2b" + domain_rules: + - id: sdk_integration + description: "Must use sandbox.runCode() with pip install or sandbox.notebook.installPackages()" + inputs: + - name: sandboxId + type: string + description: "ID of the sandbox" + - name: packages + type: array + description: "Array of package names to install (e.g., ['numpy', 'pandas==2.0.0'])" + outputs: + - name: success + type: boolean + description: "Whether all packages were installed" + - name: installed + type: array + description: "List of successfully installed packages" + - name: errors + type: array + optional: true + description: "List of packages that failed to install" + measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] + # ============================================================================= # VALIDATORS - Which validators to run against each block # ============================================================================= validators: - schema: - - id: io_schema - run: "schema.io.v1" - - shape: - - id: export_shape - run: "shape.exports.v1" - - domain: - - id: domain_alignment - run: "domain.validation.v1" - -pipeline: - name: "default" - steps: - - id: schema - run: "schema.io.v1" - - id: shape - run: "shape.exports.v1" - - id: domain - run: "domain.validation.v1" + - schema + - shape.ts + - domain diff --git a/packages/tools/official/e2b/block.ts b/packages/tools/official/e2b/block.ts new file mode 100644 index 0000000..1684236 --- /dev/null +++ b/packages/tools/official/e2b/block.ts @@ -0,0 +1,53 @@ +/** + * Block metadata for E2B tools + * This file provides metadata for the blocks validator + */ +import { + createSandbox, + downloadFile, + getMetrics, + getSandbox, + installPackages, + killSandbox, + listFiles, + listSandboxes, + makeDirectory, + pauseSandbox, + readFile, + resumeSandbox, + runCode, + runCommand, + setEnvVars, + setTimeout, + uploadFile, + watchDirectory, + writeFile, +} from './src/index.js'; + +export const block = { + name: 'e2b', + description: 'E2B cloud sandbox tools for AI code execution', + tools: { + createSandbox, + getSandbox, + listSandboxes, + killSandbox, + setTimeout, + runCode, + runCommand, + writeFile, + readFile, + listFiles, + uploadFile, + downloadFile, + makeDirectory, + watchDirectory, + pauseSandbox, + resumeSandbox, + setEnvVars, + getMetrics, + installPackages, + }, +}; + +export default block; diff --git a/packages/tools/official/e2b/index.ts b/packages/tools/official/e2b/index.ts new file mode 100644 index 0000000..22815ae --- /dev/null +++ b/packages/tools/official/e2b/index.ts @@ -0,0 +1,6 @@ +/** + * E2B Cloud Sandbox Tools + * Re-exports from src/index.ts for blocks validator compatibility + */ +export * from './src/index.js'; +export { default } from './src/index.js'; diff --git a/packages/tools/official/e2b/package.json b/packages/tools/official/e2b/package.json new file mode 100644 index 0000000..e994589 --- /dev/null +++ b/packages/tools/official/e2b/package.json @@ -0,0 +1,140 @@ +{ + "name": "@tpmjs/tools-e2b", + "version": "0.1.0", + "description": "E2B cloud sandbox tools for AI code execution. Create sandboxes, run code in multiple languages, manage files, and control sandbox lifecycle.", + "type": "module", + "keywords": [ + "tpmjs", + "e2b", + "sandbox", + "code-interpreter", + "ai", + "code-execution", + "cloud" + ], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsup", + "dev": "tsup --watch", + "type-check": "tsc --noEmit", + "clean": "rm -rf dist .turbo" + }, + "devDependencies": { + "@tpmjs/tsconfig": "workspace:*", + "tsup": "^8.5.1", + "typescript": "^5.9.3" + }, + "dependencies": { + "ai": "6.0.23", + "@e2b/code-interpreter": "^1.0.4" + }, + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/tpmjs/tpmjs.git", + "directory": "packages/tools/official/e2b" + }, + "homepage": "https://tpmjs.com", + "license": "MIT", + "tpmjs": { + "category": "sandbox", + "frameworks": [ + "vercel-ai" + ], + "env": [ + { + "name": "E2B_API_KEY", + "description": "API key for E2B authentication. Get yours at https://e2b.dev/dashboard", + "required": true + } + ], + "tools": [ + { + "name": "createSandbox", + "description": "Create a new E2B cloud sandbox from a template with configurable resources and timeout." + }, + { + "name": "getSandbox", + "description": "Get details of an existing E2B sandbox by ID." + }, + { + "name": "listSandboxes", + "description": "List all running E2B sandboxes in your account." + }, + { + "name": "killSandbox", + "description": "Terminate a running E2B sandbox immediately." + }, + { + "name": "setTimeout", + "description": "Set or extend the timeout for an E2B sandbox." + }, + { + "name": "runCode", + "description": "Execute code in an E2B sandbox. Supports Python, JavaScript, TypeScript, R, Java, and Bash." + }, + { + "name": "runCommand", + "description": "Execute a shell command in an E2B sandbox." + }, + { + "name": "writeFile", + "description": "Write content to a file in the E2B sandbox filesystem." + }, + { + "name": "readFile", + "description": "Read content from a file in the E2B sandbox filesystem." + }, + { + "name": "listFiles", + "description": "List files and directories at a path in the E2B sandbox." + }, + { + "name": "uploadFile", + "description": "Upload a file to the E2B sandbox from base64 content or URL." + }, + { + "name": "downloadFile", + "description": "Download a file from the E2B sandbox as base64 content." + }, + { + "name": "makeDirectory", + "description": "Create a directory in the E2B sandbox filesystem." + }, + { + "name": "watchDirectory", + "description": "Watch a directory in the E2B sandbox for file changes." + }, + { + "name": "pauseSandbox", + "description": "Pause a running E2B sandbox to preserve state." + }, + { + "name": "resumeSandbox", + "description": "Resume a previously paused E2B sandbox." + }, + { + "name": "setEnvVars", + "description": "Set environment variables in an E2B sandbox." + }, + { + "name": "getMetrics", + "description": "Get resource usage metrics for an E2B sandbox." + }, + { + "name": "installPackages", + "description": "Install Python packages in an E2B Code Interpreter sandbox." + } + ] + } +} diff --git a/packages/tools/official/e2b/src/index.ts b/packages/tools/official/e2b/src/index.ts new file mode 100644 index 0000000..8b81314 --- /dev/null +++ b/packages/tools/official/e2b/src/index.ts @@ -0,0 +1,1048 @@ +/** + * E2B Cloud Sandbox Tools for TPMJS + * Create and manage cloud sandboxes for AI code execution. + * + * Authentication: Set E2B_API_KEY env var with your API key from https://e2b.dev/dashboard + * + * SDK Packages: + * JavaScript/TypeScript: @e2b/code-interpreter + * Python: e2b-code-interpreter + */ + +import { Sandbox } from '@e2b/code-interpreter'; +import { jsonSchema, tool } from 'ai'; + +// ============================================================================ +// Types +// ============================================================================ + +export interface E2BSandbox { + sandboxId: string; + templateId: string; + status: string; + startedAt?: string; + clientId?: string; + metadata?: Record; +} + +export interface E2BSandboxList { + sandboxes: E2BSandbox[]; + count: number; +} + +export interface E2BExecResult { + stdout: string; + stderr: string; + results: unknown[]; + error?: { name: string; message: string; traceback?: string }; + duration: number; +} + +export interface E2BFileInfo { + name: string; + path: string; + type: 'file' | 'directory'; + size?: number; +} + +export interface E2BTemplate { + templateId: string; + buildId?: string; + cpuCount?: number; + memoryMB?: number; + status?: string; + public?: boolean; +} + +export interface E2BMetrics { + cpuPct: number; + memUsedMB: number; + networkIngressMB: number; + networkEgressMB: number; +} + +// Input types +interface CreateSandboxInput { + template?: string; + timeoutMs?: number; + metadata?: Record; + envVars?: Record; +} + +interface SandboxIdInput { + sandboxId: string; +} + +interface ListSandboxesInput { + templateId?: string; +} + +interface SetTimeoutInput { + sandboxId: string; + timeoutMs: number; +} + +interface RunCodeInput { + sandboxId: string; + code: string; + language?: string; + timeoutMs?: number; +} + +interface RunCommandInput { + sandboxId: string; + command: string; + cwd?: string; + background?: boolean; + timeoutMs?: number; +} + +interface WriteFileInput { + sandboxId: string; + path: string; + content: string; +} + +interface ReadFileInput { + sandboxId: string; + path: string; +} + +interface ListFilesInput { + sandboxId: string; + path: string; +} + +interface UploadFileInput { + sandboxId: string; + path: string; + content?: string; + url?: string; +} + +interface DownloadFileInput { + sandboxId: string; + path: string; +} + +interface MakeDirectoryInput { + sandboxId: string; + path: string; +} + +interface WatchDirectoryInput { + sandboxId: string; + path: string; +} + +interface ResumeInput { + sandboxId: string; + timeoutMs?: number; +} + +interface SetEnvVarsInput { + sandboxId: string; + envVars: Record; +} + +interface InstallPackagesInput { + sandboxId: string; + packages: string[]; +} + +// ============================================================================ +// Sandbox Connection Cache +// ============================================================================ + +const sandboxCache = new Map(); + +async function getOrConnectSandbox(sandboxId: string): Promise { + if (!sandboxId) { + throw new Error('sandboxId is required'); + } + + const cached = sandboxCache.get(sandboxId); + if (cached) { + return cached; + } + + try { + const sandbox = await Sandbox.connect(sandboxId); + sandboxCache.set(sandboxId, sandbox); + return sandbox; + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + throw new Error(`Failed to connect to sandbox ${sandboxId}: ${message}`); + } +} + +function removeSandboxFromCache(sandboxId: string): void { + sandboxCache.delete(sandboxId); +} + +// ============================================================================ +// Tools +// ============================================================================ + +/** + * Create a new E2B sandbox + */ +export const createSandbox = tool({ + description: + 'Create a new E2B cloud sandbox from a template. Sandboxes are isolated Linux environments for AI code execution. Defaults to "base" template with 5 minute timeout if not specified. Requires E2B_API_KEY.', + inputSchema: jsonSchema({ + type: 'object', + properties: { + template: { + type: 'string', + description: 'Template ID to use (default: "base" for Code Interpreter)', + }, + timeoutMs: { + type: 'number', + description: 'Sandbox timeout in milliseconds (default: 300000 = 5 minutes)', + }, + metadata: { + type: 'object', + description: 'Custom metadata key-value pairs to attach to sandbox', + additionalProperties: { type: 'string' }, + }, + envVars: { + type: 'object', + description: 'Environment variables to set in the sandbox', + additionalProperties: { type: 'string' }, + }, + }, + required: [], + additionalProperties: false, + }), + async execute(input: CreateSandboxInput): Promise { + const templateId = input.template || 'base'; + const timeoutMs = input.timeoutMs || 300000; // Default 5 minutes + + try { + const sandbox = await Sandbox.create(templateId, { + timeoutMs, + metadata: input.metadata, + envs: input.envVars, + }); + + sandboxCache.set(sandbox.sandboxId, sandbox); + + return { + sandboxId: sandbox.sandboxId, + templateId, + status: 'running', + startedAt: new Date().toISOString(), + clientId: sandbox.sandboxId, + metadata: input.metadata, + }; + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + throw new Error(`Failed to create sandbox with template "${templateId}": ${message}`); + } + }, +}); + +/** + * Get sandbox details + */ +export const getSandbox = tool({ + description: + 'Connect to and verify an existing E2B sandbox is running. Returns sandbox ID and connection status. Requires E2B_API_KEY.', + inputSchema: jsonSchema({ + type: 'object', + properties: { + sandboxId: { + type: 'string', + description: 'ID of the sandbox to retrieve', + }, + }, + required: ['sandboxId'], + additionalProperties: false, + }), + async execute(input: SandboxIdInput): Promise { + try { + const sandbox = await getOrConnectSandbox(input.sandboxId); + return { + sandboxId: sandbox.sandboxId, + templateId: (sandbox as unknown as { templateId?: string }).templateId || 'base', + status: 'running', + }; + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + throw new Error(`Failed to get sandbox ${input.sandboxId}: ${message}`); + } + }, +}); + +/** + * List all sandboxes + */ +export const listSandboxes = tool({ + description: + 'List all running E2B sandboxes in your account with their status and metadata. Requires E2B_API_KEY.', + inputSchema: jsonSchema({ + type: 'object', + properties: { + templateId: { + type: 'string', + description: 'Filter sandboxes by template ID', + }, + }, + required: [], + additionalProperties: false, + }), + async execute(_input: ListSandboxesInput): Promise { + const sandboxes = await Sandbox.list(); + return { + sandboxes: sandboxes.map((s) => ({ + sandboxId: s.sandboxId, + templateId: s.templateId || 'unknown', + status: 'running', + startedAt: s.startedAt?.toISOString(), + clientId: s.clientId, + metadata: s.metadata, + })), + count: sandboxes.length, + }; + }, +}); + +/** + * Kill a sandbox + */ +export const killSandbox = tool({ + description: + 'Terminate a running E2B sandbox immediately. All data and processes are destroyed. Requires E2B_API_KEY.', + inputSchema: jsonSchema({ + type: 'object', + properties: { + sandboxId: { + type: 'string', + description: 'ID of the sandbox to terminate', + }, + }, + required: ['sandboxId'], + additionalProperties: false, + }), + async execute(input: SandboxIdInput): Promise<{ killed: boolean; sandboxId: string }> { + const sandbox = await getOrConnectSandbox(input.sandboxId); + await sandbox.kill(); + removeSandboxFromCache(input.sandboxId); + return { killed: true, sandboxId: input.sandboxId }; + }, +}); + +/** + * Set sandbox timeout + */ +export const setTimeout = tool({ + description: + 'Set or extend the timeout for an E2B sandbox. The sandbox will be automatically killed when the timeout expires. Requires E2B_API_KEY.', + inputSchema: jsonSchema({ + type: 'object', + properties: { + sandboxId: { + type: 'string', + description: 'ID of the sandbox', + }, + timeoutMs: { + type: 'number', + description: 'New timeout in milliseconds from now', + }, + }, + required: ['sandboxId', 'timeoutMs'], + additionalProperties: false, + }), + async execute(input: SetTimeoutInput): Promise<{ success: boolean; expiresAt: string }> { + const sandbox = await getOrConnectSandbox(input.sandboxId); + await sandbox.setTimeout(input.timeoutMs); + const expiresAt = new Date(Date.now() + input.timeoutMs).toISOString(); + return { success: true, expiresAt }; + }, +}); + +/** + * Run code in sandbox + */ +export const runCode = tool({ + description: + 'Execute code in an E2B sandbox. Supports Python (default), JavaScript, TypeScript, R, Java, and Bash with streaming output and result capture. Requires E2B_API_KEY.', + inputSchema: jsonSchema({ + type: 'object', + properties: { + sandboxId: { + type: 'string', + description: 'ID of the sandbox to execute in', + }, + code: { + type: 'string', + description: 'Code to execute', + }, + language: { + type: 'string', + description: 'Language: python (default), javascript, typescript, r, java, bash', + }, + timeoutMs: { + type: 'number', + description: 'Execution timeout in milliseconds (default: 60000)', + }, + }, + required: ['sandboxId', 'code'], + additionalProperties: false, + }), + async execute(input: RunCodeInput): Promise { + const startTime = Date.now(); + const language = input.language || 'python'; + const timeoutMs = input.timeoutMs || 60000; + + try { + const sandbox = await getOrConnectSandbox(input.sandboxId); + + const execution = await sandbox.runCode(input.code, { + language: language as 'python' | 'javascript' | 'typescript' | 'r' | 'java', + timeoutMs, + }); + + const duration = Date.now() - startTime; + + return { + stdout: execution.logs.stdout.join('\n'), + stderr: execution.logs.stderr.join('\n'), + results: execution.results.map((r) => r.toJSON()), + error: execution.error + ? { + name: execution.error.name, + message: execution.error.value, + traceback: execution.error.traceback, + } + : undefined, + duration, + }; + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + throw new Error(`Failed to execute code in sandbox ${input.sandboxId}: ${message}`); + } + }, +}); + +/** + * Run shell command + */ +export const runCommand = tool({ + description: + 'Execute a shell command in an E2B sandbox with full shell access. Supports background processes and working directory. Requires E2B_API_KEY.', + inputSchema: jsonSchema({ + type: 'object', + properties: { + sandboxId: { + type: 'string', + description: 'ID of the sandbox', + }, + command: { + type: 'string', + description: 'Shell command to execute', + }, + cwd: { + type: 'string', + description: 'Working directory for the command', + }, + background: { + type: 'boolean', + description: 'Run command in background (default: false)', + }, + timeoutMs: { + type: 'number', + description: 'Command timeout in milliseconds (default: 60000)', + }, + }, + required: ['sandboxId', 'command'], + additionalProperties: false, + }), + async execute( + input: RunCommandInput + ): Promise<{ stdout: string; stderr: string; exitCode: number; processId?: string }> { + const timeoutMs = input.timeoutMs || 60000; + + try { + const sandbox = await getOrConnectSandbox(input.sandboxId); + + if (input.background) { + const process = await sandbox.commands.run(input.command, { + cwd: input.cwd, + background: true, + timeoutMs, + }); + return { + stdout: '', + stderr: '', + exitCode: 0, + processId: String(process.pid), + }; + } + + const result = await sandbox.commands.run(input.command, { + cwd: input.cwd, + timeoutMs, + }); + + return { + stdout: result.stdout, + stderr: result.stderr, + exitCode: result.exitCode, + }; + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + throw new Error(`Failed to run command in sandbox ${input.sandboxId}: ${message}`); + } + }, +}); + +/** + * Write file to sandbox + */ +export const writeFile = tool({ + description: + 'Write content to a file in the E2B sandbox filesystem. Creates directories as needed. Requires E2B_API_KEY.', + inputSchema: jsonSchema({ + type: 'object', + properties: { + sandboxId: { + type: 'string', + description: 'ID of the sandbox', + }, + path: { + type: 'string', + description: 'Absolute path in sandbox (e.g., /home/user/file.txt)', + }, + content: { + type: 'string', + description: 'File content as string', + }, + }, + required: ['sandboxId', 'path', 'content'], + additionalProperties: false, + }), + async execute(input: WriteFileInput): Promise<{ success: boolean; path: string; size: number }> { + try { + const sandbox = await getOrConnectSandbox(input.sandboxId); + await sandbox.files.write(input.path, input.content); + + return { + success: true, + path: input.path, + size: input.content.length, + }; + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + throw new Error( + `Failed to write file ${input.path} in sandbox ${input.sandboxId}: ${message}` + ); + } + }, +}); + +/** + * Read file from sandbox + */ +export const readFile = tool({ + description: 'Read content from a file in the E2B sandbox filesystem. Requires E2B_API_KEY.', + inputSchema: jsonSchema({ + type: 'object', + properties: { + sandboxId: { + type: 'string', + description: 'ID of the sandbox', + }, + path: { + type: 'string', + description: 'Absolute path to file in sandbox', + }, + }, + required: ['sandboxId', 'path'], + additionalProperties: false, + }), + async execute(input: ReadFileInput): Promise<{ content: string; path: string; size: number }> { + const sandbox = await getOrConnectSandbox(input.sandboxId); + const content = await sandbox.files.read(input.path); + + const textContent = typeof content === 'string' ? content : new TextDecoder().decode(content); + + return { + content: textContent, + path: input.path, + size: textContent.length, + }; + }, +}); + +/** + * List files in sandbox + */ +export const listFiles = tool({ + description: 'List files and directories at a path in the E2B sandbox. Requires E2B_API_KEY.', + inputSchema: jsonSchema({ + type: 'object', + properties: { + sandboxId: { + type: 'string', + description: 'ID of the sandbox', + }, + path: { + type: 'string', + description: 'Absolute path to list (e.g., /home/user)', + }, + }, + required: ['sandboxId', 'path'], + additionalProperties: false, + }), + async execute(input: ListFilesInput): Promise<{ entries: E2BFileInfo[]; count: number }> { + const sandbox = await getOrConnectSandbox(input.sandboxId); + const entries = await sandbox.files.list(input.path); + + return { + entries: entries.map((e) => ({ + name: e.name, + path: `${input.path}/${e.name}`, + type: e.type as 'file' | 'directory', + })), + count: entries.length, + }; + }, +}); + +/** + * Upload file to sandbox + */ +export const uploadFile = tool({ + description: + 'Upload a file to the E2B sandbox from base64 content or a URL. Requires E2B_API_KEY.', + inputSchema: jsonSchema({ + type: 'object', + properties: { + sandboxId: { + type: 'string', + description: 'ID of the sandbox', + }, + path: { + type: 'string', + description: 'Destination path in sandbox', + }, + content: { + type: 'string', + description: 'Base64 encoded file content', + }, + url: { + type: 'string', + description: 'URL to fetch file from (alternative to content)', + }, + }, + required: ['sandboxId', 'path'], + additionalProperties: false, + }), + async execute(input: UploadFileInput): Promise<{ success: boolean; path: string; size: number }> { + const sandbox = await getOrConnectSandbox(input.sandboxId); + + let fileContent: ArrayBuffer; + if (input.url) { + const response = await fetch(input.url); + fileContent = await response.arrayBuffer(); + } else if (input.content) { + const binaryString = atob(input.content); + const bytes = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + fileContent = bytes.buffer; + } else { + throw new Error('Either content or url must be provided'); + } + + await sandbox.files.write(input.path, fileContent); + + return { + success: true, + path: input.path, + size: fileContent.byteLength, + }; + }, +}); + +/** + * Download file from sandbox + */ +export const downloadFile = tool({ + description: 'Download a file from the E2B sandbox as base64 content. Requires E2B_API_KEY.', + inputSchema: jsonSchema({ + type: 'object', + properties: { + sandboxId: { + type: 'string', + description: 'ID of the sandbox', + }, + path: { + type: 'string', + description: 'Path to file in sandbox', + }, + }, + required: ['sandboxId', 'path'], + additionalProperties: false, + }), + async execute( + input: DownloadFileInput + ): Promise<{ content: string; filename: string; size: number; mimeType: string }> { + const sandbox = await getOrConnectSandbox(input.sandboxId); + const content = await sandbox.files.read(input.path); + + const filename = input.path.split('/').pop() || 'file'; + const ext = filename.split('.').pop()?.toLowerCase() || ''; + + // Convert to base64 and calculate size + let base64Content: string; + let contentLength: number; + + if (typeof content === 'string') { + base64Content = btoa(content); + contentLength = content.length; + } else { + const bytes = new Uint8Array(content as ArrayBuffer); + contentLength = bytes.length; + let binary = ''; + for (let i = 0; i < bytes.length; i++) { + binary += String.fromCharCode(bytes[i] as number); + } + base64Content = btoa(binary); + } + + // Simple MIME type detection + const mimeTypes: Record = { + txt: 'text/plain', + json: 'application/json', + js: 'text/javascript', + ts: 'text/typescript', + py: 'text/x-python', + html: 'text/html', + css: 'text/css', + png: 'image/png', + jpg: 'image/jpeg', + jpeg: 'image/jpeg', + gif: 'image/gif', + pdf: 'application/pdf', + zip: 'application/zip', + }; + + return { + content: base64Content, + filename, + size: contentLength, + mimeType: mimeTypes[ext] || 'application/octet-stream', + }; + }, +}); + +/** + * Create directory in sandbox + */ +export const makeDirectory = tool({ + description: + 'Create a directory in the E2B sandbox filesystem. Creates parent directories as needed. Requires E2B_API_KEY.', + inputSchema: jsonSchema({ + type: 'object', + properties: { + sandboxId: { + type: 'string', + description: 'ID of the sandbox', + }, + path: { + type: 'string', + description: 'Absolute path of directory to create', + }, + }, + required: ['sandboxId', 'path'], + additionalProperties: false, + }), + async execute(input: MakeDirectoryInput): Promise<{ success: boolean; path: string }> { + const sandbox = await getOrConnectSandbox(input.sandboxId); + await sandbox.files.makeDir(input.path); + return { success: true, path: input.path }; + }, +}); + +/** + * Watch directory for changes + */ +export const watchDirectory = tool({ + description: + 'Watch a directory in the E2B sandbox for file changes. Returns the initial state. Requires E2B_API_KEY.', + inputSchema: jsonSchema({ + type: 'object', + properties: { + sandboxId: { + type: 'string', + description: 'ID of the sandbox', + }, + path: { + type: 'string', + description: 'Directory path to watch', + }, + }, + required: ['sandboxId', 'path'], + additionalProperties: false, + }), + async execute( + input: WatchDirectoryInput + ): Promise<{ entries: E2BFileInfo[]; watcherId: string }> { + const sandbox = await getOrConnectSandbox(input.sandboxId); + const entries = await sandbox.files.list(input.path); + + return { + entries: entries.map((e) => ({ + name: e.name, + path: `${input.path}/${e.name}`, + type: e.type as 'file' | 'directory', + })), + watcherId: `watch-${input.sandboxId}-${Date.now()}`, + }; + }, +}); + +/** + * Pause sandbox (using keepalive approach) + */ +export const pauseSandbox = tool({ + description: + 'Disconnect from an E2B sandbox while keeping it running. The sandbox continues running and can be reconnected later using resumeSandbox. State is preserved in the sandbox. Requires E2B_API_KEY.', + inputSchema: jsonSchema({ + type: 'object', + properties: { + sandboxId: { + type: 'string', + description: 'ID of the sandbox to disconnect from', + }, + }, + required: ['sandboxId'], + additionalProperties: false, + }), + async execute( + input: SandboxIdInput + ): Promise<{ success: boolean; sandboxId: string; status: string }> { + if (!input.sandboxId) { + throw new Error('sandboxId is required'); + } + + // Verify sandbox exists by attempting to connect first + try { + await getOrConnectSandbox(input.sandboxId); + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + throw new Error(`Cannot pause sandbox ${input.sandboxId}: ${message}`); + } + + // Disconnect from sandbox while keeping it running + removeSandboxFromCache(input.sandboxId); + return { + success: true, + sandboxId: input.sandboxId, + status: 'disconnected', + }; + }, +}); + +/** + * Resume (reconnect to) sandbox + */ +export const resumeSandbox = tool({ + description: + 'Reconnect to a running E2B sandbox that was previously disconnected using pauseSandbox. Optionally set a new timeout. Requires E2B_API_KEY.', + inputSchema: jsonSchema({ + type: 'object', + properties: { + sandboxId: { + type: 'string', + description: 'ID of the sandbox to reconnect to', + }, + timeoutMs: { + type: 'number', + description: 'New timeout for the sandbox in milliseconds (optional)', + }, + }, + required: ['sandboxId'], + additionalProperties: false, + }), + async execute(input: ResumeInput): Promise<{ sandboxId: string; status: string }> { + try { + const sandbox = await Sandbox.connect(input.sandboxId); + if (input.timeoutMs) { + await sandbox.setTimeout(input.timeoutMs); + } + sandboxCache.set(sandbox.sandboxId, sandbox); + return { sandboxId: sandbox.sandboxId, status: 'running' }; + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + throw new Error(`Failed to reconnect to sandbox ${input.sandboxId}: ${message}`); + } + }, +}); + +/** + * Set environment variables + */ +export const setEnvVars = tool({ + description: + 'Set environment variables in an E2B sandbox. These persist for subsequent code executions in the same sandbox session. Special characters are safely escaped. Requires E2B_API_KEY.', + inputSchema: jsonSchema({ + type: 'object', + properties: { + sandboxId: { + type: 'string', + description: 'ID of the sandbox', + }, + envVars: { + type: 'object', + description: 'Key-value pairs of environment variables to set', + additionalProperties: { type: 'string' }, + }, + }, + required: ['sandboxId', 'envVars'], + additionalProperties: false, + }), + async execute(input: SetEnvVarsInput): Promise<{ success: boolean; count: number }> { + try { + const sandbox = await getOrConnectSandbox(input.sandboxId); + + // Escape shell special characters in values + const escapeForShell = (str: string): string => { + return str + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\$/g, '\\$') + .replace(/`/g, '\\`') + .replace(/!/g, '\\!'); + }; + + // Set each environment variable using shell export + for (const [key, value] of Object.entries(input.envVars)) { + await sandbox.commands.run(`export ${key}="${escapeForShell(value)}"`); + } + + return { success: true, count: Object.keys(input.envVars).length }; + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + throw new Error( + `Failed to set environment variables in sandbox ${input.sandboxId}: ${message}` + ); + } + }, +}); + +/** + * Get sandbox metrics + */ +export const getMetrics = tool({ + description: + 'Get resource usage metrics for an E2B sandbox including CPU, memory, and network usage. Requires E2B_API_KEY.', + inputSchema: jsonSchema({ + type: 'object', + properties: { + sandboxId: { + type: 'string', + description: 'ID of the sandbox', + }, + }, + required: ['sandboxId'], + additionalProperties: false, + }), + async execute(input: SandboxIdInput): Promise { + const sandbox = await getOrConnectSandbox(input.sandboxId); + + // Get metrics using /proc filesystem + const cpuResult = await sandbox.commands.run( + "cat /proc/stat | grep 'cpu ' | awk '{usage=($2+$4)*100/($2+$4+$5)} END {print usage}'" + ); + const memResult = await sandbox.commands.run("free -m | grep Mem | awk '{print $3}'"); + + return { + cpuPct: parseFloat(cpuResult.stdout) || 0, + memUsedMB: parseInt(memResult.stdout, 10) || 0, + networkIngressMB: 0, + networkEgressMB: 0, + }; + }, +}); + +/** + * Install Python packages + */ +export const installPackages = tool({ + description: + 'Install Python packages in an E2B Code Interpreter sandbox using pip. Requires E2B_API_KEY.', + inputSchema: jsonSchema({ + type: 'object', + properties: { + sandboxId: { + type: 'string', + description: 'ID of the sandbox', + }, + packages: { + type: 'array', + items: { type: 'string' }, + description: "Array of package names to install (e.g., ['numpy', 'pandas==2.0.0'])", + }, + }, + required: ['sandboxId', 'packages'], + additionalProperties: false, + }), + async execute( + input: InstallPackagesInput + ): Promise<{ success: boolean; installed: string[]; errors?: string[] }> { + const sandbox = await getOrConnectSandbox(input.sandboxId); + + const installed: string[] = []; + const errors: string[] = []; + + for (const pkg of input.packages) { + try { + const result = await sandbox.commands.run(`pip install ${pkg}`, { timeoutMs: 120000 }); + if (result.exitCode === 0) { + installed.push(pkg); + } else { + errors.push(`${pkg}: ${result.stderr}`); + } + } catch (error) { + errors.push(`${pkg}: ${error instanceof Error ? error.message : 'Unknown error'}`); + } + } + + return { + success: errors.length === 0, + installed, + errors: errors.length > 0 ? errors : undefined, + }; + }, +}); + +// ============================================================================ +// Default Export +// ============================================================================ + +export default { + createSandbox, + getSandbox, + listSandboxes, + killSandbox, + setTimeout, + runCode, + runCommand, + writeFile, + readFile, + listFiles, + uploadFile, + downloadFile, + makeDirectory, + watchDirectory, + pauseSandbox, + resumeSandbox, + setEnvVars, + getMetrics, + installPackages, +}; diff --git a/packages/tools/official/e2b/tsconfig.json b/packages/tools/official/e2b/tsconfig.json new file mode 100644 index 0000000..6521d56 --- /dev/null +++ b/packages/tools/official/e2b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@tpmjs/tsconfig/base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "incremental": false, + "composite": false + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/tools/official/e2b/tsup.config.ts b/packages/tools/official/e2b/tsup.config.ts new file mode 100644 index 0000000..9ff9463 --- /dev/null +++ b/packages/tools/official/e2b/tsup.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/index.ts'], + format: ['esm'], + dts: true, + clean: true, + sourcemap: true, + target: 'es2022', +});