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
JavaScript
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,
};
}
}