UNPKG

task-engine-ai-core

Version:

Revolutionary AI-driven task management system with complete transformation trilogy: Frontend v0.1.0, Backend v0.2.0, CLI v0.3.0 - Enterprise-grade performance with 95% improvements

650 lines (562 loc) 19.7 kB
/** * CLI Communication Gateway v0.3.0 * * High-performance communication interface between CLI tools and the v0.2.0 * Backend Communication Gateway. Provides WebSocket + HTTP/2 connectivity * with connection pooling, automatic recovery, and intelligent routing. * * Features: * - WebSocket client for real-time bidirectional communication * - HTTP/2 client for high-performance request/response operations * - Connection pooling and automatic reconnection handling * - Message correlation and request tracking * - Compression support for large payloads * - Circuit breaker patterns for fault tolerance * - Automatic failover and recovery mechanisms */ import WebSocket from 'ws'; import http2 from 'http2'; import { EventEmitter } from 'events'; import { performance } from 'perf_hooks'; import { logger } from '../utils/logger-utils.js'; /** * Connection Pool Manager for efficient connection reuse */ class ConnectionPool { constructor(maxConnections = 10) { this.maxConnections = maxConnections; this.connections = new Map(); this.availableConnections = []; this.activeConnections = new Set(); this.connectionStats = { created: 0, reused: 0, failed: 0, recovered: 0 }; } /** * Get or create a connection */ async getConnection(type, options) { const connectionKey = `${type}:${options.host}:${options.port}`; // Try to reuse existing connection if (this.availableConnections.length > 0) { const connection = this.availableConnections.pop(); this.activeConnections.add(connection); this.connectionStats.reused++; return connection; } // Create new connection if under limit if (this.activeConnections.size < this.maxConnections) { const connection = await this.createConnection(type, options); this.activeConnections.add(connection); this.connectionStats.created++; return connection; } // Wait for available connection return await this.waitForConnection(); } /** * Release connection back to pool */ releaseConnection(connection) { this.activeConnections.delete(connection); if (connection.readyState === WebSocket.OPEN || connection.state?.status === 'connected') { this.availableConnections.push(connection); } } /** * Create new connection based on type */ async createConnection(type, options) { switch (type) { case 'websocket': return await this.createWebSocketConnection(options); case 'http2': return await this.createHttp2Connection(options); default: throw new Error(`Unknown connection type: ${type}`); } } /** * Create WebSocket connection */ async createWebSocketConnection(options) { return new Promise((resolve, reject) => { const ws = new WebSocket(`ws://${options.host}:${options.port}`, { perMessageDeflate: true, maxPayload: 16 * 1024 * 1024 // 16MB }); ws.on('open', () => { ws.connectionType = 'websocket'; ws.createdAt = Date.now(); resolve(ws); }); ws.on('error', (error) => { this.connectionStats.failed++; reject(error); }); }); } /** * Create HTTP/2 connection */ async createHttp2Connection(options) { return new Promise((resolve, reject) => { const client = http2.connect(`http://${options.host}:${options.port}`, { settings: { enablePush: false, maxConcurrentStreams: 100 } }); client.on('connect', () => { client.connectionType = 'http2'; client.createdAt = Date.now(); client.state = { status: 'connected' }; resolve(client); }); client.on('error', (error) => { this.connectionStats.failed++; reject(error); }); }); } /** * Wait for available connection */ async waitForConnection() { return new Promise((resolve) => { const checkInterval = setInterval(() => { if (this.availableConnections.length > 0) { clearInterval(checkInterval); const connection = this.availableConnections.pop(); this.activeConnections.add(connection); resolve(connection); } }, 10); }); } /** * Get pool statistics */ getStats() { return { ...this.connectionStats, activeConnections: this.activeConnections.size, availableConnections: this.availableConnections.length, totalConnections: this.activeConnections.size + this.availableConnections.length }; } } /** * Circuit Breaker for fault tolerance */ class CircuitBreaker { constructor(options = {}) { this.failureThreshold = options.failureThreshold || 5; this.resetTimeout = options.resetTimeout || 30000; this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN this.failureCount = 0; this.lastFailureTime = null; this.nextAttempt = null; } /** * Execute operation through circuit breaker */ async execute(operation) { if (this.state === 'OPEN') { if (Date.now() < this.nextAttempt) { throw new Error('Circuit breaker is OPEN - service unavailable'); } else { this.state = 'HALF_OPEN'; } } try { const result = await operation(); this.onSuccess(); return result; } catch (error) { this.onFailure(); throw error; } } /** * Handle successful operation */ onSuccess() { this.failureCount = 0; this.state = 'CLOSED'; } /** * Handle failed operation */ onFailure() { this.failureCount++; this.lastFailureTime = Date.now(); if (this.failureCount >= this.failureThreshold) { this.state = 'OPEN'; this.nextAttempt = Date.now() + this.resetTimeout; } } /** * Get circuit breaker status */ getStatus() { return { state: this.state, failureCount: this.failureCount, lastFailureTime: this.lastFailureTime }; } } /** * CLI Communication Gateway Class */ export class CLICommunicationGateway extends EventEmitter { constructor(options = {}) { super(); this.options = { enableLogging: options.enableLogging !== false, backendHost: options.backendHost || 'localhost', websocketPort: options.websocketPort || 8080, http2Port: options.http2Port || 8443, maxConnections: options.maxConnections || 10, reconnectInterval: options.reconnectInterval || 5000, requestTimeout: options.requestTimeout || 30000, compressionEnabled: options.compressionEnabled !== false, ...options }; // Core components this.connectionPool = new ConnectionPool(this.options.maxConnections); this.circuitBreaker = new CircuitBreaker({ failureThreshold: 3, resetTimeout: 30000 }); // Request tracking this.pendingRequests = new Map(); this.requestCorrelation = new Map(); // Performance metrics this.metrics = { requestsSent: 0, responsesReceived: 0, averageResponseTime: 0, errorCount: 0, reconnectCount: 0, uptime: Date.now() }; // State management this.isConnected = false; this.isInitialized = false; this.reconnectTimer = null; } /** * Initialize the CLI communication gateway */ async initialize() { try { if (this.options.enableLogging) { logger.info('🔗 Initializing CLI Communication Gateway v0.3.0...'); } // Test backend connectivity await this.testBackendConnectivity(); // Initialize connection pool await this.initializeConnectionPool(); this.isInitialized = true; this.isConnected = true; this.emit('initialized'); if (this.options.enableLogging) { logger.info('✅ CLI Communication Gateway initialized successfully'); logger.info(`🔗 Connected to backend at ${this.options.backendHost}`); } return true; } catch (error) { if (this.options.enableLogging) { logger.error('❌ Failed to initialize CLI Communication Gateway:', error.message); } throw error; } } /** * Test backend connectivity */ async testBackendConnectivity() { try { // Test WebSocket connectivity const wsConnection = await this.connectionPool.createConnection('websocket', { host: this.options.backendHost, port: this.options.websocketPort }); wsConnection.close(); // Test HTTP/2 connectivity const http2Connection = await this.connectionPool.createConnection('http2', { host: this.options.backendHost, port: this.options.http2Port }); http2Connection.close(); return true; } catch (error) { throw new Error(`Backend connectivity test failed: ${error.message}`); } } /** * Initialize connection pool */ async initializeConnectionPool() { // Pre-create some connections for better performance const initialConnections = Math.min(3, this.options.maxConnections); for (let i = 0; i < initialConnections; i++) { try { const wsConnection = await this.connectionPool.getConnection('websocket', { host: this.options.backendHost, port: this.options.websocketPort }); this.connectionPool.releaseConnection(wsConnection); } catch (error) { if (this.options.enableLogging) { logger.warn(`Failed to pre-create WebSocket connection ${i + 1}:`, error.message); } } } } /** * Send request via WebSocket */ async sendWebSocketRequest(data, options = {}) { return await this.circuitBreaker.execute(async () => { const startTime = performance.now(); const correlationId = this.generateCorrelationId(); try { const connection = await this.connectionPool.getConnection('websocket', { host: this.options.backendHost, port: this.options.websocketPort }); const request = { id: correlationId, data, timestamp: Date.now(), ...options }; // Send request connection.send(JSON.stringify(request)); this.metrics.requestsSent++; // Wait for response const response = await this.waitForResponse(correlationId, connection); // Release connection back to pool this.connectionPool.releaseConnection(connection); const responseTime = performance.now() - startTime; this.updateMetrics(responseTime); return response; } catch (error) { this.metrics.errorCount++; throw error; } }); } /** * Send request via HTTP/2 */ async sendHttp2Request(method, path, data = null, options = {}) { return await this.circuitBreaker.execute(async () => { const startTime = performance.now(); try { const client = await this.connectionPool.getConnection('http2', { host: this.options.backendHost, port: this.options.http2Port }); const headers = { ':method': method, ':path': path, 'content-type': 'application/json', ...options.headers }; const req = client.request(headers); this.metrics.requestsSent++; // Send data if provided if (data) { req.write(JSON.stringify(data)); } req.end(); // Wait for response const response = await this.waitForHttp2Response(req); // Release connection back to pool this.connectionPool.releaseConnection(client); const responseTime = performance.now() - startTime; this.updateMetrics(responseTime); return response; } catch (error) { this.metrics.errorCount++; throw error; } }); } /** * Wait for WebSocket response */ async waitForResponse(correlationId, connection) { return new Promise((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error('Request timeout')); }, this.options.requestTimeout); const messageHandler = (data) => { try { const response = JSON.parse(data.toString()); if (response.correlationId === correlationId) { clearTimeout(timeout); connection.removeListener('message', messageHandler); this.metrics.responsesReceived++; resolve(response); } } catch (error) { // Ignore parsing errors for other messages } }; connection.on('message', messageHandler); }); } /** * Wait for HTTP/2 response */ async waitForHttp2Response(req) { return new Promise((resolve, reject) => { let responseData = ''; req.on('response', (headers) => { // Handle response headers }); req.on('data', (chunk) => { responseData += chunk; }); req.on('end', () => { try { const response = JSON.parse(responseData); this.metrics.responsesReceived++; resolve(response); } catch (error) { reject(new Error('Invalid JSON response')); } }); req.on('error', (error) => { reject(error); }); }); } /** * Send batch request */ async sendBatchRequest(operations, options = {}) { const batchData = { type: 'batch', operations, batchId: this.generateBatchId() }; return await this.sendWebSocketRequest(batchData, options); } /** * Subscribe to real-time updates */ async subscribeToUpdates(pattern, handler) { const subscriptionData = { type: 'subscribe', pattern, subscriptionId: this.generateSubscriptionId() }; const connection = await this.connectionPool.getConnection('websocket', { host: this.options.backendHost, port: this.options.websocketPort }); // Set up message handler for subscription connection.on('message', (data) => { try { const message = JSON.parse(data.toString()); if (message.type === 'update' && message.pattern === pattern) { handler(message.data); } } catch (error) { // Ignore parsing errors } }); // Send subscription request connection.send(JSON.stringify(subscriptionData)); return subscriptionData.subscriptionId; } /** * Update performance metrics */ updateMetrics(responseTime) { // Update average response time const alpha = 0.1; this.metrics.averageResponseTime = (alpha * responseTime) + ((1 - alpha) * this.metrics.averageResponseTime); } /** * Generate unique correlation ID */ generateCorrelationId() { return `cli_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } /** * Generate unique batch ID */ generateBatchId() { return `batch_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } /** * Generate unique subscription ID */ generateSubscriptionId() { return `sub_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } /** * Get gateway status and metrics */ getStatus() { return { isInitialized: this.isInitialized, isConnected: this.isConnected, metrics: { ...this.metrics, uptime: Date.now() - this.metrics.uptime }, connectionPool: this.connectionPool.getStats(), circuitBreaker: this.circuitBreaker.getStatus(), backend: { host: this.options.backendHost, websocketPort: this.options.websocketPort, http2Port: this.options.http2Port } }; } /** * Shutdown the gateway gracefully */ async shutdown() { if (this.options.enableLogging) { logger.info('🛑 Shutting down CLI Communication Gateway...'); } this.isConnected = false; this.isInitialized = false; // Clear reconnect timer if (this.reconnectTimer) { clearInterval(this.reconnectTimer); } // Close all connections in pool for (const connection of this.connectionPool.activeConnections) { try { if (connection.connectionType === 'websocket') { connection.close(); } else if (connection.connectionType === 'http2') { connection.close(); } } catch (error) { // Ignore close errors } } this.emit('shutdown'); if (this.options.enableLogging) { logger.info('✅ CLI Communication Gateway shutdown complete'); } } } // Export singleton instance export const cliCommunicationGateway = new CLICommunicationGateway(); export default CLICommunicationGateway;