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

260 lines (259 loc) 9.44 kB
import { logger } from '../utils/logger.js'; import { safeExecute } from '../utils/errorHandler.js'; import { AuthVerifier } from './AuthVerifier.js'; export class AuthStateManager { authCache = new Map(); authVerifier; monitoringInterval; CACHE_TTL = 3600000; MONITOR_INTERVAL = 1800000; constructor() { this.authVerifier = new AuthVerifier(); } async getAuthStatus(service) { return safeExecute(async () => { const cached = this.authCache.get(service); if (cached && this.isCacheValid(service)) { logger.debug(`Using cached auth status for ${service}`, { isAuthenticated: cached.isAuthenticated, method: cached.method, cacheAge: Date.now() - cached.timestamp, }); const { timestamp: _timestamp, ...status } = cached; return status; } logger.debug(`Fetching fresh auth status for ${service}`); const result = await this.authVerifier.verifyServiceAuth(service); const status = result.status; this.setAuthStatus(service, status); return status; }, { operationName: `get-auth-status-${service}`, layer: service, timeout: 10000, }); } async setAuthStatus(service, status) { const timestampedStatus = { ...status, timestamp: Date.now(), }; this.authCache.set(service, timestampedStatus); logger.debug(`Auth status cached for ${service}`, { isAuthenticated: status.isAuthenticated, method: status.method, }); } async clearAuthCache(service) { if (service) { this.authCache.delete(service); logger.debug(`Auth cache cleared for ${service}`); } else { this.authCache.clear(); logger.debug('Auth cache cleared for all services'); } } async startAuthMonitoring() { if (this.monitoringInterval) { logger.warn('Auth monitoring already started'); return; } logger.info('Starting authentication monitoring', { intervalMs: this.MONITOR_INTERVAL, }); this.monitoringInterval = setInterval(async () => { await this.performPeriodicCheck(); }, this.MONITOR_INTERVAL); await this.performPeriodicCheck(); } async stopAuthMonitoring() { if (this.monitoringInterval) { clearInterval(this.monitoringInterval); this.monitoringInterval = undefined; logger.info('Authentication monitoring stopped'); } } async refreshAllAuthStatus() { return safeExecute(async () => { logger.info('Refreshing authentication status for all services...'); await this.clearAuthCache(); const services = ['gemini', 'aistudio', 'claude']; const results = {}; for (const service of services) { try { results[service] = await this.getAuthStatus(service); } catch (error) { logger.error(`Failed to refresh auth status for ${service}`, { error: error.message }); results[service] = { isAuthenticated: false, method: 'oauth', userInfo: undefined, }; } } logger.info('Authentication status refresh completed', { authenticated: Object.values(results).filter(s => s.isAuthenticated).length, total: services.length, }); return results; }, { operationName: 'refresh-all-auth-status', layer: 'claude', timeout: 30000, }); } getCacheStats() { const totalEntries = this.authCache.size; const services = Array.from(this.authCache.keys()); let validEntries = 0; let expiredEntries = 0; for (const service of services) { if (this.isCacheValid(service)) { validEntries++; } else { expiredEntries++; } } return { totalEntries, validEntries, expiredEntries, services, }; } isServiceAuthenticated(service) { const cached = this.authCache.get(service); return cached?.isAuthenticated === true && this.isCacheValid(service); } getServiceAuthMethod(service) { const cached = this.authCache.get(service); if (cached && this.isCacheValid(service)) { return cached.method; } return undefined; } async checkForAuthIssues() { return safeExecute(async () => { const issues = []; const recommendations = []; const services = ['gemini', 'aistudio', 'claude']; for (const service of services) { try { const status = await this.getAuthStatus(service); if (!status.isAuthenticated) { issues.push(`${service} is not authenticated`); recommendations.push(`Run authentication setup for ${service}`); } else if (status.expiresAt && status.expiresAt < new Date()) { issues.push(`${service} authentication has expired`); recommendations.push(`Refresh authentication for ${service}`); } } catch (error) { issues.push(`Cannot verify ${service} authentication`); recommendations.push(`Check ${service} configuration and try again`); } } if (issues.length === 0) { recommendations.push('All services are properly authenticated'); } else { recommendations.push('Run "cgmb auth --interactive" for guided setup'); } return { hasIssues: issues.length > 0, issues, recommendations, }; }, { operationName: 'check-auth-issues', layer: 'claude', timeout: 15000, }); } async performPeriodicCheck() { try { logger.debug('Performing periodic authentication check...'); const stats = this.getCacheStats(); logger.debug('Auth cache stats', stats); if (stats.expiredEntries > 0) { await this.cleanExpiredEntries(); } const issues = await this.checkForAuthIssues(); if (issues.hasIssues) { logger.warn('Authentication issues detected during periodic check', { issues: issues.issues, }); } } catch (error) { logger.error('Periodic authentication check failed', { error: error.message, }); } } async validateStoredAuth(service) { try { const result = await this.authVerifier.verifyServiceAuth(service); return result.success; } catch { return false; } } isCacheValid(service) { const cached = this.authCache.get(service); if (!cached) { return false; } const age = Date.now() - cached.timestamp; return age < this.CACHE_TTL; } async cleanExpiredEntries() { const expiredServices = []; for (const [service, _status] of this.authCache.entries()) { if (!this.isCacheValid(service)) { expiredServices.push(service); } } for (const service of expiredServices) { this.authCache.delete(service); } if (expiredServices.length > 0) { logger.debug('Cleaned expired auth cache entries', { expiredServices, count: expiredServices.length, }); } } async getAuthReport() { const services = ['gemini', 'aistudio', 'claude']; const serviceDetails = {}; for (const service of services) { const status = await this.getAuthStatus(service); const cached = this.authCache.get(service); serviceDetails[service] = { isAuthenticated: status.isAuthenticated, method: status.method, status: status.isAuthenticated ? 'OK' : 'Not Authenticated', ...(cached ? { cacheAge: Date.now() - cached.timestamp } : {}), }; } const issues = await this.checkForAuthIssues(); return { summary: { totalServices: services.length, authenticatedServices: Object.values(serviceDetails).filter(s => s.isAuthenticated).length, issuesFound: issues.issues.length, }, services: serviceDetails, issues: issues.issues, recommendations: issues.recommendations, }; } }