UNPKG

claude-gemini-multimodal-bridge

Version:

Enterprise-grade AI integration bridge connecting Claude Code, Gemini CLI, and Google AI Studio with intelligent routing and advanced multimodal processing capabilities

179 lines (178 loc) 6.88 kB
import { logger } from './logger.js'; export class TimeoutManager { static DEFAULT_TIMEOUT = 120000; static ENVIRONMENT_TIMEOUTS = { fresh_installation: 1.5, mcp_startup: 2.0, authentication: 1.3, }; static async executeWithTimeout(operation, options) { const baseTimeout = options.timeout || this.DEFAULT_TIMEOUT; const multiplier = options.environmentType ? this.ENVIRONMENT_TIMEOUTS[options.environmentType] : 1.0; const finalTimeout = Math.round(baseTimeout * multiplier); const controller = new AbortController(); const startTime = Date.now(); logger.debug('Starting timeout-managed operation', { operation: options.name, baseTimeout, multiplier, finalTimeout, environmentType: options.environmentType || 'standard' }); let timeoutId; let warningTimeoutId; try { if (options.onWarning) { const warningTime = Math.round(finalTimeout * 0.7); warningTimeoutId = setTimeout(() => { const elapsed = Date.now() - startTime; options.onWarning(elapsed); }, warningTime); } const timeoutPromise = new Promise((_, reject) => { timeoutId = setTimeout(() => { controller.abort(); const elapsed = Date.now() - startTime; logger.warn('Operation timeout exceeded', { operation: options.name, timeout: finalTimeout, elapsed, environmentType: options.environmentType }); reject(new Error(`Operation "${options.name}" timed out after ${finalTimeout}ms ` + `(${Math.round(elapsed / 1000)}s elapsed, environment: ${options.environmentType || 'standard'})`)); }, finalTimeout); }); const result = await Promise.race([ operation(controller.signal), timeoutPromise ]); if (timeoutId) { clearTimeout(timeoutId); timeoutId = undefined; } if (warningTimeoutId) { clearTimeout(warningTimeoutId); warningTimeoutId = undefined; } const elapsed = Date.now() - startTime; logger.debug('Operation completed successfully', { operation: options.name, elapsed, timeoutUsed: finalTimeout, efficiency: Math.round((elapsed / finalTimeout) * 100) + '%' }); return result; } catch (error) { if (timeoutId) { clearTimeout(timeoutId); } if (warningTimeoutId) { clearTimeout(warningTimeoutId); } const elapsed = Date.now() - startTime; logger.error('Operation failed', { operation: options.name, elapsed, error: error instanceof Error ? error.message : String(error) }); throw error; } } static detectEnvironment() { const isNewEnvironment = this.isFreshInstallation(); if (isNewEnvironment) { return { type: 'fresh_installation', reason: 'Fresh installation detected - authentication and MCP startup may take longer' }; } const needsMCPStartup = this.needsMCPServerStartup(); if (needsMCPStartup) { return { type: 'mcp_startup', reason: 'MCP server startup required - cold start may take longer' }; } return { type: 'standard', reason: 'Standard environment with warm processes' }; } static isFreshInstallation() { try { const indicators = [ !process.env.HOME || !require('fs').existsSync(require('path').join(process.env.HOME, '.config', 'gemini')), !require('fs').existsSync(require('path').join(process.cwd(), 'logs')), !process.env.CGMB_INITIALIZED, ]; return indicators.filter(Boolean).length >= 2; } catch { return false; } } static needsMCPServerStartup() { try { const { execSync } = require('child_process'); try { const processes = execSync('pgrep -f "ai-studio-mcp-server" || true', { encoding: 'utf8', timeout: 1000 }); return !processes.trim(); } catch { return true; } } catch { return true; } } static wrapCLICommand(command, commandName, baseTimeout = 120000) { const environment = this.detectEnvironment(); return this.executeWithTimeout(async (signal) => { return await command(); }, { name: `CLI-${commandName}`, timeout: baseTimeout, ...(environment.type !== 'standard' && { environmentType: environment.type }), onWarning: (elapsed) => { logger.info(`⚠️ ${commandName} is taking longer than expected (${Math.round(elapsed / 1000)}s)...`); logger.info(`💡 ${environment.reason}`); } }); } static createMCPTimeout(layer, operation, hasFiles = false) { const environment = this.detectEnvironment(); const baseTimeouts = { claude: 300000, gemini: 60000, aistudio: 180000, }; let baseTimeout = baseTimeouts[layer]; if (hasFiles) { baseTimeout = Math.round(baseTimeout * 1.5); } if (operation.includes('generate_image') || operation.includes('image_generation')) { baseTimeout = Math.round(baseTimeout * 0.8); } else if (operation.includes('generate_audio') || operation.includes('audio_generation')) { baseTimeout = Math.round(baseTimeout * 0.6); } return { timeout: baseTimeout, environmentType: environment.type === 'standard' ? 'standard' : environment.type }; } } export async function withTimeout(operation, name, timeoutMs = 120000) { return TimeoutManager.executeWithTimeout(async () => operation(), { name, timeout: timeoutMs }); } export async function withCLITimeout(operation, commandName, baseTimeout = 120000) { return TimeoutManager.wrapCLICommand(operation, commandName, baseTimeout); }