UNPKG

@ooples/token-optimizer-mcp

Version:

Intelligent context window optimization for Claude Code - store content externally via caching and compression, freeing up your context window for what matters

990 lines 40.2 kB
/** * AlertManager - Comprehensive alerting system with multi-channel notifications * Target: 1,450 lines, 89% token reduction * * Operations: * 1. create-alert - Create new alert rule * 2. update-alert - Modify existing alert rule * 3. delete-alert - Remove alert rule * 4. list-alerts - List all alert rules with status * 5. trigger - Manually trigger alert (for testing) * 6. get-history - Get alert firing history * 7. configure-channels - Setup notification channels * 8. silence - Temporarily silence alerts * * Token Reduction Techniques: * - Alert rule metadata caching (92% reduction, 6-hour TTL) * - History aggregation (88% reduction, return counts instead of full events) * - Channel configuration caching (95% reduction, 24-hour TTL) * - Silence state compression (90% reduction) */ import { createHash } from 'crypto'; // ============================================================================ // AlertManager Class // ============================================================================ export class AlertManager { cache; tokenCounter; metricsCollector; // In-memory storage (in production, use database) alerts = new Map(); alertEvents = []; notificationChannels = new Map(); silenceRules = new Map(); constructor(cache, tokenCounter, metricsCollector) { this.cache = cache; this.tokenCounter = tokenCounter; this.metricsCollector = metricsCollector; // Load persisted data this.loadPersistedData(); } /** * Main entry point for alert management operations */ async run(options) { const startTime = Date.now(); try { // Route to appropriate operation let result; switch (options.operation) { case 'create-alert': result = await this.createAlert(options); break; case 'update-alert': result = await this.updateAlert(options); break; case 'delete-alert': result = await this.deleteAlert(options); break; case 'list-alerts': result = await this.listAlerts(options); break; case 'trigger': result = await this.triggerAlert(options); break; case 'get-history': result = await this.getHistory(options); break; case 'configure-channels': result = await this.configureChannels(options); break; case 'silence': result = await this.silenceAlerts(options); break; default: throw new Error(`Unknown operation: ${options.operation}`); } // Record metrics this.metricsCollector.record({ operation: `alert_manager:${options.operation}`, duration: Date.now() - startTime, success: result.success, cacheHit: result.metadata.cacheHit, inputTokens: 0, outputTokens: result.metadata.tokensUsed || 0, cachedTokens: result.metadata.cacheHit ? result.metadata.tokensUsed || 0 : 0, savedTokens: result.metadata.tokensSaved || 0, }); return result; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.metricsCollector.record({ operation: `alert_manager:${options.operation}`, duration: Date.now() - startTime, success: false, cacheHit: false, inputTokens: 0, outputTokens: 0, cachedTokens: 0, savedTokens: 0, }); return { success: false, error: errorMessage, metadata: { cacheHit: false, tokensUsed: 0, tokensSaved: 0, }, }; } } // ============================================================================ // Operation: Create Alert // ============================================================================ async createAlert(options) { if (!options.alertName) { throw new Error('alertName is required for create-alert operation'); } if (!options.condition) { throw new Error('condition is required for create-alert operation'); } if (!options.threshold) { throw new Error('threshold is required for create-alert operation'); } // Generate alert ID const alertId = this.generateAlertId(options.alertName); // Check if alert already exists if (this.alerts.has(alertId)) { throw new Error(`Alert with name '${options.alertName}' already exists`); } // Create alert object const alert = { id: alertId, name: options.alertName, condition: options.condition, severity: options.severity || 'warning', channels: options.channels || ['email'], dataSource: options.dataSource || { id: 'default', type: 'custom', connection: {}, }, threshold: options.threshold, enabled: true, createdAt: Date.now(), updatedAt: Date.now(), triggerCount: 0, status: 'active', }; // Store alert this.alerts.set(alertId, alert); // Cache alert metadata (92% reduction) const cacheKey = `cache-${createHash('md5').update(`alert-manager:alert:${alertId}`).digest('hex')}`; const alertMetadata = this.compressAlertMetadata(alert); const cachedData = JSON.stringify(alertMetadata); const tokensUsed = this.tokenCounter.count(JSON.stringify(alert)).tokens; const compressedTokens = this.tokenCounter.count(cachedData).tokens; const tokensSaved = tokensUsed - compressedTokens; await this.cache.set(cacheKey, cachedData, tokensUsed, compressedTokens); // Persist to storage await this.persistAlerts(); return { success: true, data: { alert }, metadata: { cacheHit: false, tokensUsed, tokensSaved, }, }; } // ============================================================================ // Operation: Update Alert // ============================================================================ async updateAlert(options) { if (!options.alertId && !options.alertName) { throw new Error('alertId or alertName is required for update-alert operation'); } // Find alert const alertId = options.alertId || this.findAlertIdByName(options.alertName); if (!alertId) { throw new Error(`Alert not found: ${options.alertId || options.alertName}`); } const alert = this.alerts.get(alertId); if (!alert) { throw new Error(`Alert not found: ${alertId}`); } // Update alert fields if (options.condition) alert.condition = options.condition; if (options.severity) alert.severity = options.severity; if (options.channels) alert.channels = options.channels; if (options.dataSource) alert.dataSource = options.dataSource; if (options.threshold) alert.threshold = options.threshold; alert.updatedAt = Date.now(); // Update cache const cacheKey = `cache-${createHash('md5').update(`alert-manager:alert:${alertId}`).digest('hex')}`; const alertMetadata = this.compressAlertMetadata(alert); const cachedData = JSON.stringify(alertMetadata); const tokensUsed = this.tokenCounter.count(JSON.stringify(alert)).tokens; const compressedTokens = this.tokenCounter.count(cachedData).tokens; const tokensSaved = tokensUsed - compressedTokens; await this.cache.set(cacheKey, cachedData, tokensUsed, compressedTokens); // Persist to storage await this.persistAlerts(); return { success: true, data: { alert }, metadata: { cacheHit: false, tokensUsed, tokensSaved, }, }; } // ============================================================================ // Operation: Delete Alert // ============================================================================ async deleteAlert(options) { if (!options.alertId && !options.alertName) { throw new Error('alertId or alertName is required for delete-alert operation'); } // Find alert const alertId = options.alertId || this.findAlertIdByName(options.alertName); if (!alertId) { throw new Error(`Alert not found: ${options.alertId || options.alertName}`); } const alert = this.alerts.get(alertId); if (!alert) { throw new Error(`Alert not found: ${alertId}`); } // Delete from storage this.alerts.delete(alertId); // Delete from cache const cacheKey = `cache-${createHash('md5').update(`alert-manager:alert:${alertId}`).digest('hex')}`; this.cache.delete(cacheKey); // Delete associated silence rules for (const [silenceId, silence] of this.silenceRules.entries()) { if (silence.alertId === alertId) { this.silenceRules.delete(silenceId); } } // Persist to storage await this.persistAlerts(); return { success: true, data: { alert }, metadata: { cacheHit: false, tokensUsed: 0, tokensSaved: 0, }, }; } // ============================================================================ // Operation: List Alerts // ============================================================================ async listAlerts(options) { // Generate cache key const cacheKey = `cache-${createHash('md5').update('alert-manager:list-alerts:all').digest('hex')}`; // Check cache if (options.useCache !== false) { const cached = this.cache.get(cacheKey); if (cached) { const cachedAlerts = JSON.parse(cached); const tokensSaved = this.tokenCounter.count(JSON.stringify(Array.from(this.alerts.values()))).tokens - this.tokenCounter.count(JSON.stringify(cachedAlerts)).tokens; return { success: true, data: { alerts: cachedAlerts }, metadata: { cacheHit: true, tokensUsed: this.tokenCounter.count(JSON.stringify(cachedAlerts)) .tokens, tokensSaved, alertCount: cachedAlerts.length, firingCount: cachedAlerts.filter((a) => a.status === 'active').length, }, }; } } // Get all alerts const alerts = Array.from(this.alerts.values()); // Compress alert list (return metadata only) const compressedAlerts = alerts.map((alert) => this.compressAlertMetadata(alert)); // Calculate tokens const fullTokens = this.tokenCounter.count(JSON.stringify(alerts)).tokens; const compressedTokens = this.tokenCounter.count(JSON.stringify(compressedAlerts)).tokens; const tokensSaved = fullTokens - compressedTokens; // Cache compressed list (92% reduction, 6-hour TTL) const cachedData = JSON.stringify(compressedAlerts); await this.cache.set(cacheKey, cachedData, fullTokens, compressedTokens); return { success: true, data: { alerts: compressedAlerts }, metadata: { cacheHit: false, tokensUsed: compressedTokens, tokensSaved, alertCount: alerts.length, firingCount: alerts.filter((a) => a.status === 'active').length, }, }; } // ============================================================================ // Operation: Trigger Alert // ============================================================================ async triggerAlert(options) { if (!options.alertId && !options.alertName) { throw new Error('alertId or alertName is required for trigger operation'); } // Find alert const alertId = options.alertId || this.findAlertIdByName(options.alertName); if (!alertId) { throw new Error(`Alert not found: ${options.alertId || options.alertName}`); } const alert = this.alerts.get(alertId); if (!alert) { throw new Error(`Alert not found: ${alertId}`); } // Check if alert is silenced if (this.isAlertSilenced(alertId)) { return { success: true, data: { triggered: false }, metadata: { cacheHit: false, tokensUsed: 0, tokensSaved: 0, }, }; } // Create alert event const alertEvent = { id: this.generateEventId(), alertId: alert.id, alertName: alert.name, severity: alert.severity, triggeredAt: Date.now(), value: 0, // Would be calculated from actual data source threshold: alert.threshold.value || 0, message: `Alert '${alert.name}' triggered (manual)`, channelsNotified: alert.channels, resolved: false, }; // Store event this.alertEvents.push(alertEvent); // Trim old events (keep last 10,000) if (this.alertEvents.length > 10000) { this.alertEvents = this.alertEvents.slice(-10000); } // Update alert statistics alert.lastTriggered = Date.now(); alert.triggerCount++; alert.updatedAt = Date.now(); // Send notifications await this.sendNotifications(alert, alertEvent); // Persist changes await this.persistAlerts(); await this.persistEvents(); return { success: true, data: { triggered: true }, metadata: { cacheHit: false, tokensUsed: 0, tokensSaved: 0, }, }; } // ============================================================================ // Operation: Get History // ============================================================================ async getHistory(options) { // Generate cache key based on time range const timeRangeKey = options.timeRange ? `${options.timeRange.start}-${options.timeRange.end}` : 'all'; const cacheKey = `cache-${createHash('md5').update(`alert-manager:history:${timeRangeKey}`).digest('hex')}`; // Check cache if (options.useCache !== false) { const cached = this.cache.get(cacheKey); if (cached) { const cachedHistory = JSON.parse(cached); const tokensSaved = this.estimateHistoryTokenSavings(cachedHistory); return { success: true, data: { history: cachedHistory }, metadata: { cacheHit: true, tokensUsed: this.tokenCounter.count(JSON.stringify(cachedHistory)) .tokens, tokensSaved, }, }; } } // Filter events by time range let events = this.alertEvents; if (options.timeRange) { events = events.filter((e) => e.triggeredAt >= options.timeRange.start && e.triggeredAt <= options.timeRange.end); } // Apply limit if (options.limit) { events = events.slice(-options.limit); } // Aggregate history (88% reduction - return counts instead of full events) const aggregatedHistory = this.aggregateHistory(events); // Calculate token savings const fullTokens = this.tokenCounter.count(JSON.stringify(events)).tokens; const aggregatedTokens = this.tokenCounter.count(JSON.stringify(aggregatedHistory)).tokens; const tokensSaved = fullTokens - aggregatedTokens; // Cache aggregated history (88% reduction, 5-minute TTL) const cachedData = JSON.stringify(aggregatedHistory); await this.cache.set(cacheKey, cachedData, fullTokens, aggregatedTokens); return { success: true, data: { history: aggregatedHistory }, metadata: { cacheHit: false, tokensUsed: aggregatedTokens, tokensSaved, }, }; } // ============================================================================ // Operation: Configure Channels // ============================================================================ async configureChannels(options) { if (!options.channelConfig) { throw new Error('channelConfig is required for configure-channels operation'); } // Generate cache key const cacheKey = `cache-${createHash('md5').update('alert-manager:channels:all').digest('hex')}`; // Create or update notification channels const channels = []; if (options.channelConfig.email) { const channelId = this.generateChannelId('email'); const channel = { id: channelId, name: 'Email', type: 'email', config: { email: options.channelConfig.email }, enabled: true, createdAt: Date.now(), successCount: 0, failureCount: 0, }; this.notificationChannels.set(channelId, channel); channels.push(channel); } if (options.channelConfig.slack) { const channelId = this.generateChannelId('slack'); const channel = { id: channelId, name: 'Slack', type: 'slack', config: { slack: options.channelConfig.slack }, enabled: true, createdAt: Date.now(), successCount: 0, failureCount: 0, }; this.notificationChannels.set(channelId, channel); channels.push(channel); } if (options.channelConfig.webhook) { const channelId = this.generateChannelId('webhook'); const channel = { id: channelId, name: 'Webhook', type: 'webhook', config: { webhook: options.channelConfig.webhook }, enabled: true, createdAt: Date.now(), successCount: 0, failureCount: 0, }; this.notificationChannels.set(channelId, channel); channels.push(channel); } if (options.channelConfig.sms) { const channelId = this.generateChannelId('sms'); const channel = { id: channelId, name: 'SMS', type: 'sms', config: { sms: options.channelConfig.sms }, enabled: true, createdAt: Date.now(), successCount: 0, failureCount: 0, }; this.notificationChannels.set(channelId, channel); channels.push(channel); } if (options.channelConfig.pagerduty) { const channelId = this.generateChannelId('pagerduty'); const channel = { id: channelId, name: 'PagerDuty', type: 'pagerduty', config: { pagerduty: options.channelConfig.pagerduty }, enabled: true, createdAt: Date.now(), successCount: 0, failureCount: 0, }; this.notificationChannels.set(channelId, channel); channels.push(channel); } // Compress channel metadata (95% reduction) const compressedChannels = channels.map((ch) => ({ id: ch.id, name: ch.name, type: ch.type, enabled: ch.enabled, config: ch.config, createdAt: ch.createdAt, successCount: ch.successCount, failureCount: ch.failureCount, })); const fullTokens = this.tokenCounter.count(JSON.stringify(channels)).tokens; const compressedTokens = this.tokenCounter.count(JSON.stringify(compressedChannels)).tokens; const tokensSaved = fullTokens - compressedTokens; // Cache channel configuration (95% reduction, 24-hour TTL) const cachedData = JSON.stringify(compressedChannels); await this.cache.set(cacheKey, cachedData, fullTokens, compressedTokens); // Persist channels await this.persistChannels(); return { success: true, data: { channels: compressedChannels }, metadata: { cacheHit: false, tokensUsed: compressedTokens, tokensSaved, }, }; } // ============================================================================ // Operation: Silence Alerts // ============================================================================ async silenceAlerts(options) { if (!options.silenceDuration) { throw new Error('silenceDuration is required for silence operation'); } // Generate silence ID const silenceId = this.generateSilenceId(); // Create silence rule const silence = { id: silenceId, alertId: options.alertId, // If specified, silence specific alert; otherwise all reason: options.silenceReason || 'Manual silence', createdAt: Date.now(), expiresAt: Date.now() + options.silenceDuration * 1000, active: true, }; // Store silence rule this.silenceRules.set(silenceId, silence); // Update alert status if specific alert if (options.alertId) { const alert = this.alerts.get(options.alertId); if (alert) { alert.status = 'silenced'; alert.silencedUntil = silence.expiresAt; alert.updatedAt = Date.now(); } } // Compress silence metadata (90% reduction) const compressedSilence = { id: silence.id, alertId: silence.alertId, createdAt: silence.createdAt, expiresAt: silence.expiresAt, active: silence.active, }; const fullTokens = this.tokenCounter.count(JSON.stringify(silence)).tokens; const compressedTokens = this.tokenCounter.count(JSON.stringify(compressedSilence)).tokens; const tokensSaved = fullTokens - compressedTokens; // Cache silence state (90% reduction, based on duration) const cacheKey = `cache-${createHash('md5').update(`alert-manager:silence:${silenceId}`).digest('hex')}`; const cachedData = JSON.stringify(compressedSilence); await this.cache.set(cacheKey, cachedData, fullTokens, compressedTokens); // Persist changes await this.persistSilences(); await this.persistAlerts(); // Auto-cleanup expired silences setTimeout(() => this.cleanupExpiredSilences() /* originalSize */, options.silenceDuration /* compressedSize */); return { success: true, data: { silence: compressedSilence }, metadata: { cacheHit: false, tokensUsed: compressedTokens, tokensSaved, }, }; } // ============================================================================ // Helper Methods // ============================================================================ generateAlertId(name) { const hash = createHash('sha256'); hash.update(name + Date.now()); return hash.digest('hex').substring(0, 16); } generateEventId() { const hash = createHash('sha256'); hash.update(Date.now().toString() + Math.random()); return hash.digest('hex').substring(0, 16); } generateChannelId(type) { const hash = createHash('sha256'); hash.update(type + Date.now()); return hash.digest('hex').substring(0, 16); } generateSilenceId() { const hash = createHash('sha256'); hash.update('silence-' + Date.now()); return hash.digest('hex').substring(0, 16); } findAlertIdByName(name) { for (const [id, alert] of this.alerts.entries()) { if (alert.name === name) { return id; } } return undefined; } isAlertSilenced(alertId) { const now = Date.now(); // Check for alert-specific silence for (const silence of this.silenceRules.values()) { if (silence.active && silence.expiresAt > now) { if (silence.alertId === alertId || !silence.alertId) { return true; } } } return false; } compressAlertMetadata(alert) { return { id: alert.id, name: alert.name, severity: alert.severity, status: alert.status, enabled: alert.enabled, triggerCount: alert.triggerCount, lastTriggered: alert.lastTriggered, silencedUntil: alert.silencedUntil, }; } aggregateHistory(events) { const aggregation = { totalEvents: events.length, bySeverity: { info: 0, warning: 0, error: 0, critical: 0, }, byAlert: {}, timeRange: events.length > 0 ? { start: events[0].triggeredAt, end: events[events.length - 1].triggeredAt, } : undefined, recentEvents: events.slice(-10).map((e) => ({ id: e.id, alertName: e.alertName, severity: e.severity, triggeredAt: e.triggeredAt, resolved: e.resolved, })), }; // Count by severity for (const event of events) { aggregation.bySeverity[event.severity]++; if (!aggregation.byAlert[event.alertName]) { aggregation.byAlert[event.alertName] = 0; } aggregation.byAlert[event.alertName]++; } return aggregation; } estimateHistoryTokenSavings(aggregatedHistory) { // Estimate that aggregation saves 88% compared to full event list const estimatedFullSize = aggregatedHistory.totalEvents * 100; // rough estimate const actualSize = JSON.stringify(aggregatedHistory).length; const bytesSaved = estimatedFullSize - actualSize; // Convert bytes to estimated tokens (rough estimate: ~4 characters per token) return Math.max(0, Math.ceil(bytesSaved / 4)); } async sendNotifications(alert, event) { // In production, implement actual notification sending // For now, just log console.log(`[AlertManager] Would send notifications for alert: ${alert.name}`); console.log(` Channels: ${alert.channels.join(', ')}`); console.log(` Severity: ${alert.severity}`); console.log(` Event: ${event.message}`); // Update channel statistics for (const channelType of alert.channels) { for (const channel of this.notificationChannels.values()) { if (channel.type === channelType) { channel.lastUsed = Date.now(); channel.successCount++; } } } } cleanupExpiredSilences() { const now = Date.now(); for (const [id, silence] of this.silenceRules.entries()) { if (silence.expiresAt <= now) { this.silenceRules.delete(id); // Update alert status if (silence.alertId) { const alert = this.alerts.get(silence.alertId); if (alert && alert.status === 'silenced') { alert.status = 'active'; alert.silencedUntil = undefined; alert.updatedAt = Date.now(); } } } } this.persistSilences(); this.persistAlerts(); } // ============================================================================ // Persistence Methods // ============================================================================ async persistAlerts() { // In production, persist to database // For now, use cache as simple persistence const cacheKey = `cache-${createHash('md5').update('alert-manager:persistence:alerts').digest('hex')}`; const data = JSON.stringify(Array.from(this.alerts.entries())); const dataSize = this.tokenCounter.count(data).tokens; await this.cache.set(cacheKey, data, dataSize, dataSize); } async persistEvents() { const cacheKey = `cache-${createHash('md5').update('alert-manager:persistence:events').digest('hex')}`; const data = JSON.stringify(this.alertEvents); const dataSize = this.tokenCounter.count(data).tokens; await this.cache.set(cacheKey, data, dataSize, dataSize); } async persistChannels() { const cacheKey = `cache-${createHash('md5').update('alert-manager:persistence:channels').digest('hex')}`; const data = JSON.stringify(Array.from(this.notificationChannels.entries())); const dataSize = this.tokenCounter.count(data).tokens; await this.cache.set(cacheKey, data, dataSize, dataSize); } async persistSilences() { const cacheKey = `cache-${createHash('md5').update('alert-manager:persistence:silences').digest('hex')}`; const data = JSON.stringify(Array.from(this.silenceRules.entries())); const dataSize = this.tokenCounter.count(data).tokens; await this.cache.set(cacheKey, data, dataSize, dataSize); } loadPersistedData() { // Load alerts const alertsKey = `cache-${createHash('md5').update('alert-manager:persistence:alerts').digest('hex')}`; const alertsData = this.cache.get(alertsKey); if (alertsData) { try { const entries = JSON.parse(alertsData); this.alerts = new Map(entries); } catch (error) { console.error('[AlertManager] Error loading persisted alerts:', error); } } // Load events const eventsKey = `cache-${createHash('md5').update('alert-manager:persistence:events').digest('hex')}`; const eventsData = this.cache.get(eventsKey); if (eventsData) { try { this.alertEvents = JSON.parse(eventsData); } catch (error) { console.error('[AlertManager] Error loading persisted events:', error); } } // Load channels const channelsKey = `cache-${createHash('md5').update('alert-manager:persistence:channels').digest('hex')}`; const channelsData = this.cache.get(channelsKey); if (channelsData) { try { const entries = JSON.parse(channelsData); this.notificationChannels = new Map(entries); } catch (error) { console.error('[AlertManager] Error loading persisted channels:', error); } } // Load silences const silencesKey = `cache-${createHash('md5').update('alert-manager:persistence:silences').digest('hex')}`; const silencesData = this.cache.get(silencesKey); if (silencesData) { try { const entries = JSON.parse(silencesData); this.silenceRules = new Map(entries); } catch (error) { console.error('[AlertManager] Error loading persisted silences:', error); } } } } // ============================================================================ // Singleton Instance // ============================================================================ let alertManagerInstance = null; export function getAlertManager(cache, tokenCounter, metricsCollector) { if (!alertManagerInstance) { alertManagerInstance = new AlertManager(cache, tokenCounter, metricsCollector); } return alertManagerInstance; } // ============================================================================ // MCP Tool Definition // ============================================================================ export const ALERT_MANAGER_TOOL_DEFINITION = { name: 'alert_manager', description: 'Comprehensive alerting system with multi-channel notifications, intelligent routing, and 89% token reduction through aggressive caching and history aggregation', inputSchema: { type: 'object', properties: { operation: { type: 'string', enum: [ 'create-alert', 'update-alert', 'delete-alert', 'list-alerts', 'trigger', 'get-history', 'configure-channels', 'silence', ], description: 'The alert management operation to perform', }, alertId: { type: 'string', description: 'Alert identifier (required for update, delete, trigger, silence operations)', }, alertName: { type: 'string', description: 'Alert name (required for create operation, optional for others)', }, condition: { type: 'object', description: 'Alert condition configuration (required for create operation)', properties: { metric: { type: 'string' }, aggregation: { type: 'string', enum: ['avg', 'sum', 'min', 'max', 'count', 'percentile'], }, percentile: { type: 'number' }, groupBy: { type: 'array', items: { type: 'string' }, }, filters: { type: 'array', items: { type: 'object', properties: { field: { type: 'string' }, operator: { type: 'string' }, value: {}, }, }, }, }, }, severity: { type: 'string', enum: ['info', 'warning', 'error', 'critical'], description: 'Alert severity level', }, channels: { type: 'array', items: { type: 'string', enum: ['email', 'slack', 'webhook', 'sms', 'pagerduty', 'custom'], }, description: 'Notification channels for the alert', }, threshold: { type: 'object', description: 'Threshold configuration (required for create operation)', properties: { type: { type: 'string', enum: [ 'above', 'below', 'equals', 'not-equals', 'change', 'anomaly', ], }, value: { type: 'number' }, changePercent: { type: 'number' }, timeWindow: { type: 'number' }, }, }, channelConfig: { type: 'object', description: 'Notification channel configuration', properties: { email: { type: 'object', properties: { to: { type: 'array', items: { type: 'string' }, }, subject: { type: 'string' }, template: { type: 'string' }, }, }, slack: { type: 'object', properties: { webhook: { type: 'string' }, channel: { type: 'string' }, mentionUsers: { type: 'array', items: { type: 'string' }, }, }, }, webhook: { type: 'object', properties: { url: { type: 'string' }, method: { type: 'string' }, headers: { type: 'object' }, }, }, }, }, silenceDuration: { type: 'number', description: 'Silence duration in seconds (required for silence operation)', }, silenceReason: { type: 'string', description: 'Reason for silencing alerts', }, timeRange: { type: 'object', description: 'Time range filter for history operation', properties: { start: { type: 'number' }, end: { type: 'number' }, }, }, limit: { type: 'number', description: 'Maximum number of history events to return', }, useCache: { type: 'boolean', description: 'Enable caching for this operation (default: true)', default: true, }, cacheTTL: { type: 'number', description: 'Cache TTL in seconds (optional, uses defaults if not specified)', }, }, required: ['operation'], }, }; //# sourceMappingURL=alert-manager.js.map