UNPKG

@simonecoelhosfo/optimizely-mcp-server

Version:

Optimizely MCP Server for AI assistants with integrated CLI tools

189 lines • 7.68 kB
/** * Simple Progress Reporter - Clean, straightforward progress display * Shows one overall progress bar and simple console logs */ import * as cliProgress from 'cli-progress'; import chalk from 'chalk'; export class SimpleProgressReporter { multiBar; mainBar = null; totalSteps = 0; currentStep = 0; currentProject = ''; projectNames = new Map(); constructor() { // Use MultiBar to enable proper logging with multibar.log() this.multiBar = new cliProgress.MultiBar({ clearOnComplete: false, hideCursor: true, forceRedraw: false }, cliProgress.Presets.shades_classic); } /** * Start sync operation */ startSync(operation, options) { this.multiBar.log(chalk.bold.blue(`šŸš€ Optimizely Cache Sync\n`)); this.multiBar.log(chalk.gray(`${operation} • ${new Date().toLocaleTimeString()}\n`)); } /** * Set total steps when we know project count and entity count */ setTotalSteps(projectCount, entitiesPerProject, totalSteps) { this.totalSteps = totalSteps || (projectCount * entitiesPerProject); // Create the main progress bar this.mainBar = this.multiBar.create(this.totalSteps, 0, { label: chalk.cyan('Overall Progress') }); if (totalSteps) { this.multiBar.log(chalk.gray(`Total steps: ${this.totalSteps} steps calculated from project platforms and entity configurations\n`)); } else { this.multiBar.log(chalk.gray(`Total steps: ${projectCount} projects Ɨ ${entitiesPerProject} entities = ${this.totalSteps} steps\n`)); } } /** * Update progress for a specific phase */ updateProgress(progress) { const { phase, current, total, message, percent } = progress; // Handle different types of progress messages if (phase === 'projects') { // Project-level messages - extract entity info from message if (message.includes('Syncing project:')) { const projectMatch = message.match(/Syncing project:\s*(.+)/); if (projectMatch) { const projectName = projectMatch[1].trim(); // This should fix the trailing space if (projectName !== this.currentProject) { this.multiBar.log(chalk.bold.cyan(`\nšŸ“¦ ${projectName}:`)); this.currentProject = projectName; } } return; } // Check for entity starting messages if (message.includes('Syncing') && !message.includes('Syncing project:')) { const entityMatch = message.match(/Syncing\s+(\w+)/i); if (entityMatch) { const entityType = entityMatch[1].toLowerCase(); this.multiBar.log(chalk.yellow(` ā³ Starting ${entityType}...`)); } return; } // Check if this is actually an entity completion message disguised as project progress if (message.includes('synced') || message.includes('completed')) { // Extract entity type from the message const entityMatch = message.match(/(\w+)\s+(?:synced|completed)/i); if (entityMatch) { const entityType = entityMatch[1].toLowerCase(); const recordMatch = message.match(/\((\d+) records?\)/); const recordCount = recordMatch ? ` (${recordMatch[1]} records)` : ''; this.multiBar.log(chalk.green(` āœ… ${entityType} completed${recordCount}`)); this.currentStep++; if (this.mainBar) { this.mainBar.update(this.currentStep); } } return; } // Other project messages - just return return; } // Extract project info from phase name const parts = phase.split('_'); let projectId = ''; let entityType = phase; let projectName = 'Unknown Project'; if (parts.length >= 3 && parts[0] === 'project') { projectId = parts[1]; entityType = parts.slice(2).join('_'); } // Extract project name from message const projectMatch = message.match(/^([^:]+):\s*/); if (projectMatch) { projectName = projectMatch[1].trim(); this.projectNames.set(projectId, projectName); } else if (this.projectNames.has(projectId)) { projectName = this.projectNames.get(projectId); } else if (projectId) { projectName = `Project ${projectId}`; } // Show current project if it changed if (projectName !== this.currentProject) { this.multiBar.log(chalk.bold.cyan(`\nšŸ“¦ ${projectName}:`)); this.currentProject = projectName; } // Format entity type for display const entityMap = { 'flags': 'flags', 'experiments': 'experiments', 'campaigns': 'campaigns', 'pages': 'pages', 'audiences': 'audiences', 'events': 'events', 'attributes': 'attributes', 'extensions': 'extensions', 'web_environments': 'web environments', 'environments': 'environments', 'groups': 'groups', 'webhooks': 'webhooks', 'change_history': 'change history', 'list_attributes': 'list attributes', 'features': 'features' }; const displayEntity = entityMap[entityType] || entityType; // Extract record count from message if available const recordMatch = message.match(/\((\d+) records?\)/); const recordCount = recordMatch ? ` (${recordMatch[1]} records)` : ''; // Always show progress updates - simplified if (current === 0 && total === 1) { // Starting entity sync this.multiBar.log(chalk.yellow(` ā³ Starting ${displayEntity}...`)); } else if (percent >= 100) { // Completed entity sync this.multiBar.log(chalk.green(` āœ… ${displayEntity} completed${recordCount}`)); this.currentStep++; if (this.mainBar) { this.mainBar.update(this.currentStep); } } // Always show intermediate progress for any update if (current > 0 && current < total) { this.multiBar.log(chalk.blue(` šŸ“„ ${displayEntity}: ${current}/${total} (${percent.toFixed(0)}%)`)); } } /** * Complete sync operation */ completeSync(result) { this.multiBar.stop(); this.multiBar.log(chalk.bold.green('\n✨ Sync Completed Successfully!')); // Show performance metrics if (result.duration) { this.multiBar.log(chalk.gray(`ā±ļø Completed in ${(result.duration / 1000).toFixed(1)}s`)); } if (this.totalSteps > 0) { this.multiBar.log(chalk.gray(`šŸ“Š Processed ${this.totalSteps} entity sync operations`)); } } /** * Report error */ error(error) { this.multiBar.stop(); this.multiBar.log(chalk.red.bold('\nāŒ Sync Failed!')); this.multiBar.log(chalk.red(error.message)); } /** * Clean up */ dispose() { if (this.multiBar) { this.multiBar.stop(); } } } //# sourceMappingURL=SimpleProgressReporter.js.map