codecrucible-synth
Version:
Production-Ready AI Development Platform with Multi-Voice Synthesis, Smithery MCP Integration, Enterprise Security, and Zero-Timeout Reliability
456 lines (453 loc) โข 16.5 kB
JavaScript
import { Sandbox } from '@e2b/code-interpreter';
import { logger } from '../logger.js';
/**
* Core E2B Service for managing sandboxed code execution
*
* This service provides secure, isolated code execution capabilities
* using E2B sandboxes, replacing the unsafe direct execution methods.
*/
export class E2BService {
sandboxPool = new Map();
config;
isInitialized = false;
cleanupTimer = null;
constructor(config) {
this.config = {
apiKey: process.env.E2B_API_KEY || '',
defaultEnvironment: 'base',
sessionTimeout: 3600000, // 1 hour
maxConcurrentSessions: 10,
resourceLimits: {
memory: '512MB',
cpu: '0.5',
diskSpace: '1GB',
timeout: 30000, // 30 seconds
},
...config,
};
if (!this.config.apiKey) {
logger.warn('E2B API key not found. Code execution will be disabled.');
}
}
/**
* Initialize the E2B service
*/
async initialize() {
try {
if (!this.config.apiKey) {
throw new Error('E2B API key is required');
}
// Test connection by creating a temporary sandbox
await this.testConnection();
this.isInitialized = true;
logger.info('โ
E2B service initialized successfully');
// Start cleanup timer for expired sessions
this.startCleanupTimer();
}
catch (error) {
logger.error('โ Failed to initialize E2B service:', error);
throw error;
}
}
/**
* Test E2B connection by creating and destroying a sandbox
*/
async testConnection() {
try {
const testSandbox = await Sandbox.create({
apiKey: this.config.apiKey,
});
const result = await testSandbox.runCode('print("E2B connection test successful")');
if (!result.text || !result.text.includes('successful')) {
throw new Error('E2B connection test failed');
}
// Note: E2B sandboxes auto-cleanup, no explicit close needed
logger.info('๐ E2B connection test passed');
}
catch (error) {
throw new Error(`E2B connection test failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Create a new sandbox for a session
*/
async createSandbox(sessionId, environment) {
if (!this.isInitialized) {
throw new Error('E2B service not initialized');
}
if (this.sandboxPool.has(sessionId)) {
logger.warn(`Sandbox already exists for session ${sessionId}, returning existing`);
return this.sandboxPool.get(sessionId);
}
if (this.sandboxPool.size >= this.config.maxConcurrentSessions) {
// Clean up old sessions to make room
await this.cleanupOldestSession();
}
try {
logger.info(`๐ Creating E2B sandbox for session: ${sessionId}`);
const startTime = Date.now();
const sandbox = await Sandbox.create({
apiKey: this.config.apiKey,
});
const e2bSandbox = {
id: sessionId,
sandbox,
createdAt: new Date(),
lastUsed: new Date(),
resourceLimits: this.config.resourceLimits,
};
this.sandboxPool.set(sessionId, e2bSandbox);
const creationTime = Date.now() - startTime;
logger.info(`โ
Sandbox created for session ${sessionId} in ${creationTime}ms`);
return e2bSandbox;
}
catch (error) {
logger.error(`โ Failed to create sandbox for session ${sessionId}:`, error);
throw new Error(`Failed to create E2B sandbox: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Get existing sandbox for a session
*/
async getSandbox(sessionId) {
const sandbox = this.sandboxPool.get(sessionId);
if (sandbox) {
// Update last used timestamp
sandbox.lastUsed = new Date();
return sandbox;
}
return null;
}
/**
* Get or create sandbox for a session
*/
async getOrCreateSandbox(sessionId) {
const existing = await this.getSandbox(sessionId);
if (existing) {
return existing;
}
return await this.createSandbox(sessionId);
}
/**
* Execute code in a sandbox
*/
async executeCode(sessionId, code, language = 'python') {
const startTime = Date.now();
try {
const e2bSandbox = await this.getOrCreateSandbox(sessionId);
const { sandbox } = e2bSandbox;
logger.info(`๐ง Executing ${language} code in session ${sessionId}`);
logger.debug(`Code to execute: ${code.substring(0, 100)}${code.length > 100 ? '...' : ''}`);
// Execute the code based on language
const result = await this.executeByLanguage(sandbox, code, language);
const executionTime = Date.now() - startTime;
const executionResult = {
success: !result.error,
output: result.text || (result.logs ? result.logs.stdout.join('\n') : ''),
error: result.error?.name || (result.logs ? result.logs.stderr.join('\n') : undefined),
executionTime,
files: result.results?.map((r) => r.filename).filter(Boolean),
};
logger.info(`โ
Code execution completed in ${executionTime}ms - Success: ${executionResult.success}`);
if (executionResult.error) {
logger.warn(`Execution error: ${executionResult.error}`);
}
return executionResult;
}
catch (error) {
const executionTime = Date.now() - startTime;
logger.error(`โ Code execution failed for session ${sessionId}:`, error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown execution error',
executionTime,
};
}
}
/**
* Execute code based on language type
*/
async executeByLanguage(sandbox, code, language) {
switch (language.toLowerCase()) {
case 'python':
case 'py':
return await sandbox.runCode(code);
case 'javascript':
case 'js': {
// Convert to Python execution of JavaScript-like logic where possible
// Note: E2B primarily supports Python, so we convert simple JS to Python
const pythonCode = this.convertJSToPython(code);
return await sandbox.runCode(pythonCode);
}
case 'bash':
case 'shell': {
// Execute bash commands safely
const bashCode = `
import subprocess
import sys
try:
result = subprocess.run(${JSON.stringify(code)}, shell=True, capture_output=True, text=True, timeout=30)
if result.stdout:
print(result.stdout)
if result.stderr:
print(result.stderr, file=sys.stderr)
sys.exit(result.returncode)
except subprocess.TimeoutExpired:
print("Command timed out", file=sys.stderr)
sys.exit(1)
except Exception as e:
print(f"Execution error: {e}", file=sys.stderr)
sys.exit(1)
`;
return await sandbox.runCode(bashCode);
}
default:
throw new Error(`Unsupported language: ${language}`);
}
}
/**
* Convert simple JavaScript to Python (basic conversion)
*/
convertJSToPython(jsCode) {
// Basic JS to Python conversion for simple operations
const pythonCode = jsCode
.replace(/console\.log\(/g, 'print(')
.replace(/let\s+/g, '')
.replace(/const\s+/g, '')
.replace(/var\s+/g, '')
.replace(/===\s/g, '== ')
.replace(/!==\s/g, '!= ')
.replace(/true/g, 'True')
.replace(/false/g, 'False')
.replace(/null/g, 'None');
return pythonCode;
}
/**
* Upload file to sandbox
*/
async uploadFile(sessionId, filePath, content) {
try {
const e2bSandbox = await this.getOrCreateSandbox(sessionId);
// Use Python to write the file
const writeCode = `
with open(${JSON.stringify(filePath)}, 'w') as f:
f.write(${JSON.stringify(content)})
print(f"File written to {${JSON.stringify(filePath)}}")
`;
await e2bSandbox.sandbox.runCode(writeCode);
logger.info(`๐ File uploaded to sandbox ${sessionId}: ${filePath}`);
}
catch (error) {
logger.error(`โ Failed to upload file to session ${sessionId}:`, error);
throw error;
}
}
/**
* Download file from sandbox
*/
async downloadFile(sessionId, filePath) {
try {
const e2bSandbox = await this.getSandbox(sessionId);
if (!e2bSandbox) {
throw new Error(`No sandbox found for session ${sessionId}`);
}
// Use Python to read the file
const readCode = `
try:
with open(${JSON.stringify(filePath)}, 'r') as f:
content = f.read()
print(content)
except FileNotFoundError:
print(f"ERROR: File not found: {${JSON.stringify(filePath)}}")
except Exception as e:
print(f"ERROR: {e}")
`;
const result = await e2bSandbox.sandbox.runCode(readCode);
if (result.text && result.text.startsWith('ERROR:')) {
throw new Error(result.text);
}
logger.info(`๐ฅ File downloaded from sandbox ${sessionId}: ${filePath}`);
return result.text || '';
}
catch (error) {
logger.error(`โ Failed to download file from session ${sessionId}:`, error);
throw error;
}
}
/**
* Destroy a sandbox and clean up resources
*/
async destroySandbox(sessionId) {
try {
const e2bSandbox = this.sandboxPool.get(sessionId);
if (e2bSandbox) {
// Note: E2B sandboxes auto-cleanup, just remove from pool
this.sandboxPool.delete(sessionId);
logger.info(`๐๏ธ Sandbox destroyed for session: ${sessionId}`);
}
}
catch (error) {
logger.error(`โ Failed to destroy sandbox for session ${sessionId}:`, error);
// Still remove from pool even if cleanup failed
this.sandboxPool.delete(sessionId);
}
}
/**
* Clean up the oldest session to make room for new ones
*/
async cleanupOldestSession() {
if (this.sandboxPool.size === 0)
return;
let oldestSession = null;
let oldestTime = Date.now();
for (const [sessionId, sandbox] of this.sandboxPool.entries()) {
if (sandbox.lastUsed.getTime() < oldestTime) {
oldestTime = sandbox.lastUsed.getTime();
oldestSession = sessionId;
}
}
if (oldestSession) {
logger.info(`๐งน Cleaning up oldest session: ${oldestSession}`);
await this.destroySandbox(oldestSession);
}
}
/**
* Start cleanup timer for expired sessions
*/
startCleanupTimer() {
this.cleanupTimer = setInterval(async () => {
await this.cleanupExpiredSessions();
}, 300000); // Clean up every 5 minutes
}
/**
* Stop cleanup timer and cleanup all resources
*/
async shutdown() {
if (this.cleanupTimer) {
clearInterval(this.cleanupTimer);
this.cleanupTimer = null;
}
// Destroy all active sandboxes
const destroyPromises = Array.from(this.sandboxPool.keys()).map(sessionId => this.destroySandbox(sessionId));
await Promise.allSettled(destroyPromises);
this.sandboxPool.clear();
this.isInitialized = false;
logger.info('โ
E2B service shut down successfully');
}
/**
* Clean up expired sessions
*/
async cleanupExpiredSessions() {
const now = Date.now();
const expiredSessions = [];
for (const [sessionId, sandbox] of this.sandboxPool.entries()) {
const timeSinceLastUse = now - sandbox.lastUsed.getTime();
if (timeSinceLastUse > this.config.sessionTimeout) {
expiredSessions.push(sessionId);
}
}
if (expiredSessions.length > 0) {
logger.info(`๐งน Cleaning up ${expiredSessions.length} expired sessions`);
for (const sessionId of expiredSessions) {
await this.destroySandbox(sessionId);
}
}
}
/**
* Install package in a sandbox
*/
async installPackage(sessionId, packageName, language = 'python') {
try {
const e2bSandbox = await this.getOrCreateSandbox(sessionId);
let installCommand;
if (language === 'python') {
installCommand = `
import subprocess
import sys
try:
result = subprocess.run([sys.executable, '-m', 'pip', 'install', '${packageName}'],
capture_output=True, text=True, timeout=60)
if result.returncode == 0:
print(f"Successfully installed {packageName}")
print(result.stdout)
else:
print(f"Failed to install {packageName}")
print(result.stderr)
raise Exception(f"Installation failed: {result.stderr}")
except subprocess.TimeoutExpired:
raise Exception("Package installation timed out")
except Exception as e:
raise Exception(f"Installation error: {str(e)}")
`;
}
else {
// JavaScript package installation via npm (if available in sandbox)
installCommand = `
import subprocess
import sys
try:
result = subprocess.run(['npm', 'install', '${packageName}'],
capture_output=True, text=True, timeout=60)
if result.returncode == 0:
print(f"Successfully installed {packageName}")
print(result.stdout)
else:
print(f"Failed to install {packageName}")
print(result.stderr)
raise Exception(f"Installation failed: {result.stderr}")
except subprocess.TimeoutExpired:
raise Exception("Package installation timed out")
except Exception as e:
raise Exception(f"Installation error: {str(e)}")
`;
}
const result = await e2bSandbox.sandbox.runCode(installCommand);
return {
success: !result.error,
output: result.text || '',
error: result.error?.name,
executionTime: 0, // Not tracking this for package installation
};
}
catch (error) {
logger.error(`โ Failed to install package ${packageName} in session ${sessionId}:`, error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown package installation error',
executionTime: 0,
};
}
}
/**
* Get list of active session IDs
*/
getActiveSessions() {
return Array.from(this.sandboxPool.keys());
}
/**
* Get service statistics
*/
getStats() {
return {
isInitialized: this.isInitialized,
activeSessions: this.sandboxPool.size,
maxConcurrentSessions: this.config.maxConcurrentSessions,
totalSessionsCreated: this.sandboxPool.size, // This could be tracked more accurately
averageSessionAge: this.calculateAverageSessionAge(),
};
}
/**
* Calculate average session age
*/
calculateAverageSessionAge() {
if (this.sandboxPool.size === 0)
return 0;
const now = Date.now();
let totalAge = 0;
for (const sandbox of this.sandboxPool.values()) {
totalAge += now - sandbox.createdAt.getTime();
}
return totalAge / this.sandboxPool.size;
}
}
//# sourceMappingURL=e2b-service.js.map