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

353 lines 13.3 kB
/** * Real-time usage monitoring service * Continuously monitors spending and triggers alerts when thresholds are exceeded */ import { z } from 'zod'; import { readFile, writeFile } from 'fs/promises'; import { join } from 'path'; import { homedir } from 'os'; // Real-time alerts feature removed - simplified monitoring only // Usage monitoring schema export const UsageMonitorConfigSchema = z.object({ enabled: z.boolean().default(true).describe('Enable real-time monitoring'), interval: z.number().default(60).describe('Check interval in seconds'), warningThreshold: z.number().default(0.8).describe('Warning threshold (0.0-1.0)'), criticalThreshold: z.number().default(0.95).describe('Critical threshold (0.0-1.0)'), spikeDetection: z.boolean().default(true).describe('Enable usage spike detection'), spikeThreshold: z.number().default(0.5).describe('Spike threshold as multiplier of average'), autoBreak: z.boolean().default(false).describe('Automatically pause when budget exceeded'), gracePeriod: z.number().default(300).describe('Grace period in seconds before auto-break') }); export const MonitoringSessionSchema = z.object({ sessionId: z.string().describe('Unique session identifier'), startTime: z.string().describe('Session start timestamp'), endTime: z.string().optional().describe('Session end timestamp'), status: z.enum(['active', 'paused', 'stopped']).describe('Session status'), checksPerformed: z.number().default(0).describe('Number of checks performed'), alertsSent: z.number().default(0).describe('Number of alerts sent'), lastCheck: z.string().optional().describe('Last check timestamp'), config: UsageMonitorConfigSchema.describe('Monitoring configuration') }); const HIVE_DIR = join(homedir(), '.hive-ai'); const MONITOR_DB_PATH = join(HIVE_DIR, 'usage-monitor.json'); const BUDGET_DB_PATH = join(HIVE_DIR, 'budget.json'); const COST_DB_PATH = join(HIVE_DIR, 'cost-tracking.json'); // Load monitor database async function loadMonitorDatabase() { try { const data = await readFile(MONITOR_DB_PATH, 'utf-8'); return JSON.parse(data); } catch { return { config: { enabled: true, interval: 60, warningThreshold: 0.8, criticalThreshold: 0.95, spikeDetection: true, spikeThreshold: 0.5, autoBreak: false, gracePeriod: 300 }, currentSession: null, sessions: [], statistics: { totalSessions: 0, totalChecks: 0, totalAlerts: 0, avgCheckInterval: 60, lastUpdated: new Date().toISOString() } }; } } // Save monitor database async function saveMonitorDatabase(db) { db.statistics.lastUpdated = new Date().toISOString(); await writeFile(MONITOR_DB_PATH, JSON.stringify(db, null, 2)); } // Load budget data async function loadBudgetData() { try { const data = await readFile(BUDGET_DB_PATH, 'utf-8'); return JSON.parse(data); } catch { return { budgets: [] }; } } // Load cost data async function loadCostData() { try { const data = await readFile(COST_DB_PATH, 'utf-8'); return JSON.parse(data); } catch { return { usage: [] }; } } // Calculate current spending for budget period function calculateCurrentSpending(usage, budgetType, budgetStart) { const now = new Date(); let startDate; if (budgetStart) { startDate = budgetStart; } else { switch (budgetType) { case 'daily': startDate = new Date(now.getFullYear(), now.getMonth(), now.getDate()); break; case 'weekly': const dayOfWeek = now.getDay(); startDate = new Date(now.getTime() - dayOfWeek * 24 * 60 * 60 * 1000); startDate.setHours(0, 0, 0, 0); break; case 'monthly': startDate = new Date(now.getFullYear(), now.getMonth(), 1); break; case 'total': startDate = new Date(0); // Beginning of time break; default: startDate = new Date(now.getFullYear(), now.getMonth(), now.getDate()); } } return usage .filter(u => new Date(u.timestamp) >= startDate && new Date(u.timestamp) <= now) .reduce((total, u) => total + u.amount, 0); } // Detect usage spikes function detectUsageSpike(usage, spikeThreshold) { if (usage.length < 10) return false; // Need enough data const recent = usage.slice(-5); // Last 5 entries const historical = usage.slice(-50, -5); // Previous 45 entries const recentAvg = recent.reduce((sum, u) => sum + u.amount, 0) / recent.length; const historicalAvg = historical.reduce((sum, u) => sum + u.amount, 0) / historical.length; return recentAvg > historicalAvg * (1 + spikeThreshold); } // Check single budget async function checkBudget(budget, costData) { const currentSpend = calculateCurrentSpending(costData.usage, budget.type); const percentage = (currentSpend / budget.limit) * 100; let alertType = null; if (percentage >= 100) { alertType = 'exceeded'; } else if (percentage >= budget.criticalThreshold * 100) { alertType = 'critical'; } else if (percentage >= budget.warningThreshold * 100) { alertType = 'warning'; } if (alertType) { // Real-time alerts feature removed - using console logging console.warn(`Budget ${alertType}: ${percentage.toFixed(1)}% of ${budget.type} budget used`); console.warn(`Current spend: ${currentSpend}, Limit: ${budget.limit}`); } } // Check for usage spikes async function checkUsageSpikes(config, costData) { if (!config.spikeDetection) return; const isSpike = detectUsageSpike(costData.usage, config.spikeThreshold); if (isSpike) { const recentUsage = costData.usage.slice(-5); const totalRecent = recentUsage.reduce((sum, u) => sum + u.amount, 0); // Real-time alerts feature removed - using console logging console.warn('Usage spike detected: Recent spending significantly higher than average'); console.warn(`Recent spending: ${totalRecent}, Entries analyzed: ${recentUsage.length}`); } } // Perform monitoring check async function performMonitoringCheck(session) { try { const budgetData = await loadBudgetData(); const costData = await loadCostData(); // Check each enabled budget for (const budget of budgetData.budgets.filter(b => b.enabled)) { await checkBudget(budget, costData); } // Check for usage spikes await checkUsageSpikes(session.config, costData); // Update session session.checksPerformed++; session.lastCheck = new Date().toISOString(); } catch (error) { console.error('Error during monitoring check:', error); } } // Start monitoring session export async function startMonitoringSession(config) { try { const monitorDb = await loadMonitorDatabase(); // Stop existing session if any if (monitorDb.currentSession && monitorDb.currentSession.status === 'active') { await stopMonitoringSession(); } // Create new session const sessionId = `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; const sessionConfig = { ...monitorDb.config, ...config }; const newSession = { sessionId, startTime: new Date().toISOString(), status: 'active', checksPerformed: 0, alertsSent: 0, config: sessionConfig }; monitorDb.currentSession = newSession; monitorDb.sessions.push(newSession); monitorDb.statistics.totalSessions++; await saveMonitorDatabase(monitorDb); // Start monitoring loop const monitoringInterval = setInterval(async () => { try { const currentDb = await loadMonitorDatabase(); if (!currentDb.currentSession || currentDb.currentSession.status !== 'active') { clearInterval(monitoringInterval); return; } await performMonitoringCheck(currentDb.currentSession); currentDb.statistics.totalChecks++; await saveMonitorDatabase(currentDb); } catch (error) { console.error('Error in monitoring loop:', error); } }, sessionConfig.interval * 1000); // Store interval reference (in a real implementation, you'd want better process management) process.on('SIGINT', () => { clearInterval(monitoringInterval); stopMonitoringSession(); }); return { success: true, sessionId, message: `Real-time monitoring started (checking every ${sessionConfig.interval}s)` }; } catch (error) { return { success: false, sessionId: '', message: `Failed to start monitoring: ${error instanceof Error ? error.message : 'Unknown error'}` }; } } // Stop monitoring session export async function stopMonitoringSession() { try { const monitorDb = await loadMonitorDatabase(); if (!monitorDb.currentSession) { return { success: false, message: 'No active monitoring session found' }; } const session = monitorDb.currentSession; session.status = 'stopped'; session.endTime = new Date().toISOString(); const sessionDuration = new Date(session.endTime).getTime() - new Date(session.startTime).getTime(); const sessionSummary = { sessionId: session.sessionId, duration: Math.round(sessionDuration / 1000), // seconds checksPerformed: session.checksPerformed, alertsSent: session.alertsSent, avgInterval: sessionDuration / session.checksPerformed / 1000 // actual average interval }; monitorDb.currentSession = null; await saveMonitorDatabase(monitorDb); return { success: true, message: 'Monitoring session stopped', sessionSummary }; } catch (error) { return { success: false, message: `Failed to stop monitoring: ${error instanceof Error ? error.message : 'Unknown error'}` }; } } // Pause monitoring session export async function pauseMonitoringSession() { try { const monitorDb = await loadMonitorDatabase(); if (!monitorDb.currentSession || monitorDb.currentSession.status !== 'active') { return { success: false, message: 'No active monitoring session to pause' }; } monitorDb.currentSession.status = 'paused'; await saveMonitorDatabase(monitorDb); return { success: true, message: 'Monitoring session paused' }; } catch (error) { return { success: false, message: `Failed to pause monitoring: ${error instanceof Error ? error.message : 'Unknown error'}` }; } } // Resume monitoring session export async function resumeMonitoringSession() { try { const monitorDb = await loadMonitorDatabase(); if (!monitorDb.currentSession || monitorDb.currentSession.status !== 'paused') { return { success: false, message: 'No paused monitoring session to resume' }; } monitorDb.currentSession.status = 'active'; await saveMonitorDatabase(monitorDb); // Restart monitoring would need to be handled by the calling code return { success: true, message: 'Monitoring session resumed' }; } catch (error) { return { success: false, message: `Failed to resume monitoring: ${error instanceof Error ? error.message : 'Unknown error'}` }; } } // Get monitoring status export async function getMonitoringStatus() { const monitorDb = await loadMonitorDatabase(); return { isActive: monitorDb.currentSession?.status === 'active', currentSession: monitorDb.currentSession, statistics: monitorDb.statistics }; } // Configure monitoring settings export async function configureMonitoring(config) { try { const monitorDb = await loadMonitorDatabase(); monitorDb.config = { ...monitorDb.config, ...config }; await saveMonitorDatabase(monitorDb); return { success: true, message: 'Monitoring configuration updated' }; } catch (error) { return { success: false, message: `Failed to configure monitoring: ${error instanceof Error ? error.message : 'Unknown error'}` }; } } //# sourceMappingURL=usage-monitor-legacy.js.map