UNPKG

@hivetechs/hive-ai

Version:

Real-time streaming AI consensus platform with HTTP+SSE MCP integration for Claude Code, VS Code, Cursor, and Windsurf - powered by OpenRouter's unified API

188 lines 8.05 kB
/** * Auto-Update System for Model Registry * Handles daily background updates with intelligent timing and user feedback */ import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; import { openRouterClient } from './openrouter-client.js'; export class AutoUpdater { configDir = path.join(os.homedir(), '.hive-ai'); statusFile = path.join(this.configDir, 'update-status.json'); registryFile = path.join(process.cwd(), 'src', 'data', 'openrouter-registry.json'); userRegistryFile = path.join(this.configDir, 'openrouter-registry.json'); constructor() { this.ensureConfigDir(); } ensureConfigDir() { if (!fs.existsSync(this.configDir)) { fs.mkdirSync(this.configDir, { recursive: true }); } } getUpdateStatus() { try { if (fs.existsSync(this.statusFile)) { const data = fs.readFileSync(this.statusFile, 'utf8'); return JSON.parse(data); } } catch (error) { // If file is corrupted, we'll create a new one } // Default status for first run const now = new Date(); return { lastCheck: new Date(0).toISOString(), // Force first check lastUpdate: new Date(0).toISOString(), version: '1.0.0', providerCount: 0, modelCount: 0, nextCheck: new Date(now.getTime() + 24 * 60 * 60 * 1000).toISOString() }; } saveUpdateStatus(status) { try { fs.writeFileSync(this.statusFile, JSON.stringify(status, null, 2)); } catch (error) { console.error('Warning: Could not save update status:', error instanceof Error ? error.message : 'Unknown error'); } } shouldCheckForUpdate() { const status = this.getUpdateStatus(); const now = new Date(); const nextCheck = new Date(status.nextCheck); // Check if it's time for the next scheduled check return now >= nextCheck; } async getCurrentRegistryStats() { try { // Check user registry first, then source registry const registryPath = fs.existsSync(this.userRegistryFile) ? this.userRegistryFile : this.registryFile; if (!fs.existsSync(registryPath)) { return null; } const data = fs.readFileSync(registryPath, 'utf8'); const registry = JSON.parse(data); return { providers: registry.totalProviders || 0, models: registry.totalModels || 0 }; } catch (error) { return null; } } async checkForUpdatesAsync() { const startTime = Date.now(); try { // Get current stats const currentStats = await this.getCurrentRegistryStats(); // Fetch latest data from OpenRouter (with timeout) const providers = await Promise.race([ openRouterClient.getProviderSummary(), new Promise((_, reject) => setTimeout(() => reject(new Error('Update timeout')), 10000) // 10 second timeout ) ]); const newStats = { providers: providers.length, models: providers.reduce((sum, p) => sum + p.modelCount, 0) }; // Determine if we should update const shouldUpdate = !currentStats || newStats.providers !== currentStats.providers || newStats.models !== currentStats.models; if (shouldUpdate) { // Update the user registry const registry = { providers, lastUpdated: new Date().toISOString(), source: 'openrouter', totalProviders: newStats.providers, totalModels: newStats.models, autoUpdated: true }; fs.writeFileSync(this.userRegistryFile, JSON.stringify(registry, null, 2)); // Update build/dist versions try { const buildPath = path.join(process.cwd(), 'build', 'data', 'openrouter-registry.json'); const distPath = path.join(process.cwd(), 'dist', 'data', 'openrouter-registry.json'); for (const targetPath of [buildPath, distPath]) { const targetDir = path.dirname(targetPath); if (!fs.existsSync(targetDir)) { fs.mkdirSync(targetDir, { recursive: true }); } fs.writeFileSync(targetPath, JSON.stringify(registry, null, 2)); } } catch (error) { // Non-critical if build/dist update fails } } // Update status const now = new Date(); const currentStatus = this.getUpdateStatus(); const status = { lastCheck: now.toISOString(), lastUpdate: shouldUpdate ? now.toISOString() : (currentStats ? now.toISOString() : currentStatus.lastUpdate), version: '1.0.0', providerCount: newStats.providers, modelCount: newStats.models, nextCheck: new Date(now.getTime() + 24 * 60 * 60 * 1000).toISOString() // Next day }; this.saveUpdateStatus(status); const timeTaken = Date.now() - startTime; return { updated: shouldUpdate, newModels: shouldUpdate ? (newStats.models - (currentStats?.models || 0)) : 0, newProviders: shouldUpdate ? (newStats.providers - (currentStats?.providers || 0)) : 0, timeTaken, summary: shouldUpdate ? `Updated registry: ${newStats.providers} providers, ${newStats.models} models` : `Registry current: ${newStats.providers} providers, ${newStats.models} models` }; } catch (error) { // On error, schedule next check for 4 hours later instead of 24 hours const now = new Date(); const status = this.getUpdateStatus(); status.lastCheck = now.toISOString(); status.nextCheck = new Date(now.getTime() + 4 * 60 * 60 * 1000).toISOString(); // 4 hours this.saveUpdateStatus(status); return { updated: false, newModels: 0, newProviders: 0, timeTaken: Date.now() - startTime, summary: `Update check failed: ${error instanceof Error ? error.message : 'Unknown error'}` }; } } async performAutoUpdateIfNeeded() { if (!this.shouldCheckForUpdate()) { return null; // No update needed } return await this.checkForUpdatesAsync(); } getLastUpdateInfo() { const status = this.getUpdateStatus(); const lastUpdate = new Date(status.lastUpdate); const now = new Date(); if (lastUpdate.getTime() === 0) { return 'Model registry: Never updated'; } const hoursAgo = Math.floor((now.getTime() - lastUpdate.getTime()) / (1000 * 60 * 60)); if (hoursAgo < 1) { return `Model registry: Updated recently (${status.providerCount} providers, ${status.modelCount} models)`; } else if (hoursAgo < 24) { return `Model registry: Updated ${hoursAgo}h ago (${status.providerCount} providers, ${status.modelCount} models)`; } else { const daysAgo = Math.floor(hoursAgo / 24); return `Model registry: Updated ${daysAgo}d ago (${status.providerCount} providers, ${status.modelCount} models)`; } } } export const autoUpdater = new AutoUpdater(); //# sourceMappingURL=auto-updater.js.map