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

173 lines (172 loc) 5.87 kB
import { logger } from '../utils/logger.js'; export class AuthCache { static instance; cache = new Map(); failureTracking = new Map(); TTL_SETTINGS = { gemini: 6 * 60 * 60 * 1000, aistudio: 24 * 60 * 60 * 1000, claude: 12 * 60 * 60 * 1000, }; FAILURE_BACKOFF = { delays: [30 * 1000, 60 * 1000, 5 * 60 * 1000], maxDelay: 5 * 60 * 1000, resetAfter: 24 * 60 * 60 * 1000, jitterFactor: 0.1 }; constructor() { } static getInstance() { if (!AuthCache.instance) { AuthCache.instance = new AuthCache(); } return AuthCache.instance; } set(service, auth) { let ttl; let expiry; if (auth.success) { ttl = this.TTL_SETTINGS[service]; expiry = Date.now() + ttl; this.failureTracking.delete(service); logger.debug('Authentication success cached', { service, ttl: ttl / 1000 / 60, expiryTime: new Date(expiry).toISOString(), }); } else { const tracking = this.getOrCreateFailureTracking(service); tracking.count++; tracking.lastFailureTime = Date.now(); ttl = this.calculateFailureTTL(tracking.count); expiry = Date.now() + ttl; logger.warn('Authentication failure cached with backoff', { service, failureCount: tracking.count, ttl: ttl / 1000, nextRetryTime: new Date(expiry).toLocaleTimeString(), error: auth.error, }); } this.cache.set(service, { auth, expiry, service, }); } get(service) { const cached = this.cache.get(service); if (!cached) { logger.debug('No cached auth found', { service }); return null; } const now = Date.now(); const isExpired = now >= cached.expiry; if (isExpired) { logger.debug('Cached auth expired', { service, expiredSince: (now - cached.expiry) / 1000 / 60, }); this.cache.delete(service); return null; } const timeToExpiry = (cached.expiry - now) / 1000 / 60; logger.debug('Using cached auth', { service, timeToExpiry: timeToExpiry.toFixed(1), success: cached.auth.success, }); return cached.auth; } invalidate(service) { const removed = this.cache.delete(service); logger.debug('Authentication cache invalidated', { service, removed }); } clear() { const count = this.cache.size; this.cache.clear(); logger.debug('All authentication cache cleared', { count }); } getStats() { const now = Date.now(); const services = Object.keys(this.TTL_SETTINGS).map(service => { const cached = this.cache.get(service); if (!cached) { return { service, cached: false }; } const timeToExpiry = (cached.expiry - now) / 1000 / 60; return { service, cached: true, timeToExpiry: timeToExpiry > 0 ? timeToExpiry : 0, success: cached.auth.success, }; }); return { totalEntries: this.cache.size, services, }; } cleanup() { const now = Date.now(); let removedCount = 0; for (const [service, cached] of this.cache.entries()) { if (now >= cached.expiry) { this.cache.delete(service); removedCount++; logger.debug('Expired auth cache removed', { service, expiredSince: (now - cached.expiry) / 1000 / 60, }); } } if (removedCount > 0) { logger.debug('Auth cache cleanup completed', { removedCount }); } return removedCount; } isCached(service) { const cached = this.cache.get(service); return cached ? Date.now() < cached.expiry : false; } forceRefresh(service) { this.invalidate(service); this.failureTracking.delete(service); logger.info('Forced authentication refresh', { service }); } getOrCreateFailureTracking(service) { if (!this.failureTracking.has(service)) { this.failureTracking.set(service, { count: 0, firstFailureTime: Date.now(), lastFailureTime: Date.now() }); } const tracking = this.failureTracking.get(service); if (Date.now() - tracking.firstFailureTime > this.FAILURE_BACKOFF.resetAfter) { tracking.count = 0; tracking.firstFailureTime = Date.now(); } return tracking; } calculateFailureTTL(failureCount) { const index = Math.min(failureCount - 1, this.FAILURE_BACKOFF.delays.length - 1); const baseDelay = this.FAILURE_BACKOFF.delays[index] || this.FAILURE_BACKOFF.maxDelay; const jitter = baseDelay * this.FAILURE_BACKOFF.jitterFactor; const randomJitter = (Math.random() - 0.5) * 2 * jitter; return Math.round(baseDelay + randomJitter); } getFailureInfo(service) { const tracking = this.failureTracking.get(service); if (!tracking) return null; const cached = this.cache.get(service); if (cached && !cached.auth.success) { return { count: tracking.count, nextRetryTime: new Date(cached.expiry) }; } return { count: tracking.count }; } }