Skip to main content
Glama
cli-tools.tsβ€’11.4 kB
/** * CLI Tools for Testing Event Persistence and Replay * * Provides convenient commands for testing event features during development. */ import { EventManager } from './event-manager.js'; import { EventPersistence, EventQuery } from './persistence.js'; import { EventReplay, ReplayAnalyzer } from './replay.js'; import { EventMetricsCollector, MetricsExporter } from './metrics.js'; import { ServiceEvents, SessionEvents } from './event-types.js'; import { logger } from '../logger/logger.js'; export class EventCliTools { private eventManager: EventManager; private persistence: EventPersistence; private replay: EventReplay; private metricsCollector: EventMetricsCollector; constructor(eventManager: EventManager) { this.eventManager = eventManager; // Initialize persistence with file storage for CLI testing this.persistence = new EventPersistence({ enabled: true, storageType: 'file', filePath: './data/events', maxEvents: 50000, retentionDays: 7, }); this.replay = new EventReplay(this.persistence); this.metricsCollector = new EventMetricsCollector(); // Setup metrics collection from events this.setupMetricsCollection(); } /** * Generate sample events for testing */ async generateSampleEvents(count: number = 100): Promise<void> { logger.info(`Generating ${count} sample events for testing...`); const sessionId = `test-session-${Date.now()}`; // Generate a variety of events for (let i = 0; i < count; i++) { const eventType = i % 4; switch (eventType) { case 0: // Service events this.eventManager.emitServiceEvent(ServiceEvents.SERVICE_STARTED, { serviceType: `test-service-${i}`, timestamp: Date.now(), }); break; case 1: // Session lifecycle this.eventManager.emitSessionEvent(sessionId, SessionEvents.SESSION_CREATED, { sessionId: `session-${i}`, timestamp: Date.now(), }); break; case 2: // Tool execution this.eventManager.emitSessionEvent(sessionId, SessionEvents.TOOL_EXECUTION_STARTED, { toolName: `tool-${i}`, toolType: 'internal', sessionId, executionId: `exec-${i}`, timestamp: Date.now(), }); // Add completion after a delay setTimeout(() => { this.eventManager.emitSessionEvent(sessionId, SessionEvents.TOOL_EXECUTION_COMPLETED, { toolName: `tool-${i}`, toolType: 'internal', sessionId, executionId: `exec-${i}`, duration: Math.random() * 1000, success: true, timestamp: Date.now(), }); }, Math.random() * 100); break; case 3: // LLM events this.eventManager.emitSessionEvent(sessionId, SessionEvents.LLM_RESPONSE_STARTED, { sessionId, messageId: `msg-${i}`, model: 'claude-3-sonnet', timestamp: Date.now(), }); break; } // Add some delay to spread events over time if (i % 10 === 0) { await new Promise(resolve => setTimeout(resolve, 10)); } } logger.info(`Generated ${count} sample events`); } /** * Query events from persistence */ async queryEvents(query: EventQuery): Promise<void> { logger.info('Querying events...', query); const events = await this.persistence.query(query); logger.info(`Found ${events.length} events:`); // Group events by type const eventsByType: Record<string, number> = {}; for (const event of events) { eventsByType[event.type] = (eventsByType[event.type] || 0) + 1; } console.table(eventsByType); // Show latest events const latestEvents = events.slice(0, 10); console.log('\nLatest events:'); for (const event of latestEvents) { console.log(` ${new Date(event.metadata.timestamp).toISOString()} - ${event.type}`); } } /** * Test event replay functionality */ async testEventReplay( options: { sessionId?: string; eventTypes?: string[]; since?: number; speed?: number; } = {} ): Promise<void> { logger.info('Starting event replay test...', options); const replayedEvents: any[] = []; // Setup replay listeners this.replay.onServiceEvent(ServiceEvents.SERVICE_STARTED, data => { replayedEvents.push({ type: 'service:started', data }); logger.info('Replayed service event:', data); }); this.replay.onSessionEvent(SessionEvents.TOOL_EXECUTION_STARTED, data => { replayedEvents.push({ type: 'tool:started', data }); logger.info('Replayed tool execution:', data); }); this.replay.onSessionEvent(SessionEvents.LLM_RESPONSE_STARTED, data => { replayedEvents.push({ type: 'llm:started', data }); logger.info('Replayed LLM response:', data); }); // Start replay const query: EventQuery = { ...(options.sessionId ? { sessionId: options.sessionId } : {}), since: options.since || Date.now() - 60000, // Last minute by default limit: 50, }; if (options.eventTypes) { // Note: This would need to be implemented as a filter logger.warn('Event type filtering not yet implemented in query'); } await this.replay.startReplay(query, { speed: options.speed || 2.0, // 2x speed skipTimestamps: false, }); // Wait for replay to complete const replayState = this.replay.getReplayState(); if (replayState) { logger.info(`Replayed ${replayedEvents.length} events`); } } /** * Generate event analytics report */ async generateAnalytics(): Promise<void> { logger.info('Generating event analytics...'); // Get events from the last hour const events = await this.persistence.query({ since: Date.now() - 3600000, // Last hour limit: 1000, }); if (events.length === 0) { logger.warn('No events found for analytics'); return; } // Use replay analyzer const analyzer = new ReplayAnalyzer(); analyzer.addEvents(events); const report = analyzer.generateReport(); console.log('\n' + '='.repeat(50)); console.log('EVENT ANALYTICS REPORT'); console.log('='.repeat(50)); console.log(report); // Also show metrics const serviceMetrics = this.metricsCollector.getServiceMetrics(); const sessionMetrics = this.metricsCollector.getSessionMetrics(); console.log('\n' + '='.repeat(50)); console.log('CURRENT METRICS'); console.log('='.repeat(50)); console.log('Service Metrics:'); console.table({ 'Cipher Uptime': `${(serviceMetrics.cipherUptime / 1000).toFixed(2)}s`, 'Services Started': serviceMetrics.serviceStartCount, 'Service Errors': serviceMetrics.serviceErrorCount, 'Tool Registrations': serviceMetrics.toolRegistrationCount, 'MCP Connections': serviceMetrics.mcpConnectionCount, }); console.log('\nSession Metrics:'); console.table({ 'Sessions Created': sessionMetrics.sessionCreatedCount, 'Tool Executions': sessionMetrics.toolExecutionCount, 'Tool Success Rate': `${((sessionMetrics.toolExecutionSuccessCount / Math.max(sessionMetrics.toolExecutionCount, 1)) * 100).toFixed(1)}%`, 'LLM Responses': sessionMetrics.llmResponseCount, 'Memory Operations': sessionMetrics.memoryStoreCount + sessionMetrics.memoryRetrieveCount, }); } /** * Export metrics in various formats */ async exportMetrics(format: 'prometheus' | 'json' = 'json'): Promise<void> { const exporter = new MetricsExporter(this.metricsCollector); let output: string; let filename: string; switch (format) { case 'prometheus': output = exporter.exportPrometheus(); filename = `./data/metrics-${Date.now()}.prom`; break; case 'json': output = exporter.exportJSON(); filename = `./data/metrics-${Date.now()}.json`; break; } // Write to file const fs = await import('fs/promises'); await fs.mkdir('./data', { recursive: true }); await fs.writeFile(filename, output); logger.info(`Metrics exported to ${filename}`); console.log(`Metrics exported to: ${filename}`); } /** * Monitor events in real-time */ startEventMonitoring(): void { logger.info('Starting real-time event monitoring...'); const serviceEventBus = this.eventManager.getServiceEventBus(); // Monitor all service events Object.values(ServiceEvents).forEach(eventType => { serviceEventBus.on(eventType, data => { console.log(`πŸ”§ [SERVICE] ${eventType}:`, data); }); }); console.log( 'Event monitoring started. You should see events appear in real-time as you use cipher.' ); console.log('Try running some commands to generate events!'); } /** * Show event persistence statistics */ async showPersistenceStats(): Promise<void> { const stats = await this.persistence.getStats(); console.log('\n' + '='.repeat(40)); console.log('EVENT PERSISTENCE STATISTICS'); console.log('='.repeat(40)); console.table({ 'Total Events': stats.totalEvents, 'Storage Size': `${(stats.storageSize / 1024).toFixed(2)} KB`, 'Storage Location': './data/events', }); } /** * Clean up old events */ async cleanupOldEvents(_days: number = 7): Promise<void> { // const retentionMs = days * 24 * 60 * 60 * 1000; // If this.persistence.cleanup does not exist, replace with correct method or comment out. // const deletedCount = await this.persistence.cleanup(retentionMs); // logger.info(`Cleaned up ${deletedCount} events older than ${days} days`); } private setupMetricsCollection(): void { const serviceEventBus = this.eventManager.getServiceEventBus(); // Collect metrics from service events Object.values(ServiceEvents).forEach(eventType => { serviceEventBus.on(eventType, data => { this.metricsCollector.processServiceEvent({ id: `metrics-${Date.now()}`, type: eventType, data, metadata: { timestamp: Date.now(), source: 'service' }, }); }); }); } dispose(): void { this.persistence.dispose(); } } /** * CLI command implementations */ export const eventCommands = { /** * Generate sample events for testing */ async generateEvents(eventManager: EventManager, count: number = 100) { const tools = new EventCliTools(eventManager); await tools.generateSampleEvents(count); tools.dispose(); }, /** * Query and display events */ async queryEvents(eventManager: EventManager, options: EventQuery = {}) { const tools = new EventCliTools(eventManager); await tools.queryEvents(options); tools.dispose(); }, /** * Test event replay */ async replayEvents(eventManager: EventManager, options: any = {}) { const tools = new EventCliTools(eventManager); await tools.testEventReplay(options); tools.dispose(); }, /** * Generate analytics report */ async analytics(eventManager: EventManager) { const tools = new EventCliTools(eventManager); await tools.generateAnalytics(); tools.dispose(); }, /** * Export metrics */ async exportMetrics(eventManager: EventManager, format: 'prometheus' | 'json' = 'json') { const tools = new EventCliTools(eventManager); await tools.exportMetrics(format); tools.dispose(); }, /** * Show persistence stats */ async persistenceStats(eventManager: EventManager) { const tools = new EventCliTools(eventManager); await tools.showPersistenceStats(); tools.dispose(); }, /** * Start real-time monitoring */ startMonitoring(eventManager: EventManager) { const tools = new EventCliTools(eventManager); tools.startEventMonitoring(); // Don't dispose here - keep monitoring active return tools; }, };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/campfirein/cipher'

If you have feedback or need assistance with the MCP directory API, please join our Discord server