UNPKG

sf-agent-framework

Version:

AI Agent Orchestration Framework for Salesforce Development - Two-phase architecture with 70% context reduction

463 lines (383 loc) 13.2 kB
/** * Visualization Engine for SF-Agent Framework * Creates ASCII art dashboards and progress visualizations */ class VisualizationEngine { constructor(options = {}) { this.options = { type: options.type || 'ascii', // ascii, text, or both width: options.width || 80, colors: options.colors !== false, // Enable colors by default ...options, }; // ANSI color codes this.colors = { reset: '\x1b[0m', bright: '\x1b[1m', red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[34m', magenta: '\x1b[35m', cyan: '\x1b[36m', white: '\x1b[37m', }; } /** * Render main dashboard */ renderDashboard(data) { if (this.options.type === 'text') { return this.renderTextDashboard(data); } return this.renderASCIIDashboard(data); } /** * Render ASCII art dashboard */ renderASCIIDashboard(data) { const width = this.options.width; const lines = []; // Header lines.push(this.renderHeader(data.workflow, width)); lines.push(''); // Progress bar lines.push(this.renderProgressSection(data.progress, data.phase, data.status)); lines.push(''); // Team status if (data.team && data.team.length > 0) { lines.push(this.renderTeamSection(data.team)); lines.push(''); } // Active tasks if (data.tasks && data.tasks.length > 0) { lines.push(this.renderTasksSection(data.tasks, 'Active Tasks')); lines.push(''); } // Completed tasks if (data.completedTasks && data.completedTasks.length > 0) { lines.push(this.renderCompletedSection(data.completedTasks)); lines.push(''); } // Live feed if (data.events) { lines.push(this.renderLiveFeed(data.events)); } return lines.join('\n'); } /** * Render header with workflow name */ renderHeader(workflowName, width) { const title = ` ${workflowName.toUpperCase()} ORCHESTRATION `; const padding = Math.max(0, width - title.length - 2); const leftPad = Math.floor(padding / 2); const rightPad = padding - leftPad; const lines = [ '╔' + '═'.repeat(width - 2) + '╗', '║' + ' '.repeat(leftPad) + this.color('bright', title) + ' '.repeat(rightPad) + '║', '╚' + '═'.repeat(width - 2) + '╝', ]; return lines.join('\n'); } /** * Render progress section */ renderProgressSection(progress, phase, status) { const lines = []; // Progress bar const validProgress = progress !== undefined && !isNaN(progress) ? progress : 0; const bar = this.renderProgressBar(validProgress, 40); lines.push( `${this.color('cyan', '▶ PROGRESS:')} ${bar} ${this.color('bright', `${validProgress}%`)}` ); // Current phase if (phase) { lines.push(`${this.color('cyan', '▶ PHASE:')} ${phase}`); } // Status if (status) { const statusColor = status.includes('Error') ? 'red' : status.includes('Complete') ? 'green' : 'yellow'; lines.push(`${this.color('cyan', '▶ STATUS:')} ${this.color(statusColor, status)}`); } return lines.join('\n'); } /** * Render progress bar */ renderProgressBar(progress, width = 20) { // Handle undefined or NaN progress const validProgress = progress !== undefined && !isNaN(progress) ? progress : 0; const filled = Math.floor((validProgress / 100) * width); const empty = Math.max(0, width - filled); const filledChar = '█'; const emptyChar = '░'; const bar = this.color('green', filledChar.repeat(filled)) + this.color('white', emptyChar.repeat(empty)); return `[${bar}]`; } /** * Render team section with status */ renderTeamSection(team) { const lines = []; lines.push('═══════════════════════════════════════════════════════════════════════════'); lines.push(this.color('bright', 'TEAM STATUS DASHBOARD')); lines.push('═══════════════════════════════════════════════════════════════════════════'); team.forEach((member) => { const statusIcon = this.getStatusIcon(member.status); const statusColor = this.getStatusColor(member.status); lines.push(''); lines.push(`${member.icon || '👤'} ${this.color('bright', member.name)} (${member.role})`); lines.push( ` Status: ${this.color(statusColor, statusIcon + ' ' + member.status.toUpperCase())}` ); if (member.currentTask) { lines.push(` Task: ${member.currentTask}`); if (member.progress > 0) { const miniBar = this.renderProgressBar(member.progress, 15); lines.push(` Progress: ${miniBar} ${member.progress}%`); } } else { lines.push(` Task: ${this.color('white', 'Available for assignment')}`); } }); lines.push('═══════════════════════════════════════════════════════════════════════════'); return lines.join('\n'); } /** * Render tasks section */ renderTasksSection(tasks, title) { const lines = []; lines.push(this.renderSectionHeader(title)); // Create table const table = this.createTable( ['Task', 'Agent', 'Progress', 'Status'], tasks.map((task) => [ task.name || 'Unknown', task.agent || 'Unassigned', `${task.progress || 0}%`, task.status || 'In Progress', ]) ); lines.push(table); return lines.join('\n'); } /** * Render completed tasks section */ renderCompletedSection(completedTasks) { const lines = []; lines.push(this.renderSectionHeader('Recently Completed')); // Show last 5 completed tasks const recent = completedTasks.slice(-5); recent.forEach((task) => { lines.push(` ${this.color('green', '✓')} ${task.name || task}`); }); return lines.join('\n'); } /** * Render live activity feed */ renderLiveFeed(events) { const lines = []; lines.push(this.color('cyan', '📡 LIVE ACTIVITY FEED:')); lines.push('─'.repeat(this.options.width - 5)); // Show last 5 events const recent = events.slice(-5); recent.forEach((event) => { const time = new Date(event.timestamp).toLocaleTimeString(); lines.push(`[${time}] ${event.icon || '•'} ${event.message}`); }); return lines.join('\n'); } /** * Render section header */ renderSectionHeader(title) { const header = ` ${title} `; const lineWidth = 40; const padding = Math.max(0, lineWidth - header.length); const leftPad = Math.floor(padding / 2); const rightPad = padding - leftPad; return '─'.repeat(leftPad) + this.color('bright', header) + '─'.repeat(rightPad); } /** * Create a simple ASCII table */ createTable(headers, rows) { const colWidths = headers.map((h, i) => { const maxWidth = Math.max(h.length, ...rows.map((r) => (r[i] || '').toString().length)); return Math.min(maxWidth, 20); // Cap at 20 chars }); const lines = []; // Header lines.push('┌' + colWidths.map((w) => '─'.repeat(w + 2)).join('┬') + '┐'); lines.push( '│ ' + headers.map((h, i) => this.color('bright', h.padEnd(colWidths[i]))).join(' │ ') + ' │' ); lines.push('├' + colWidths.map((w) => '─'.repeat(w + 2)).join('┼') + '┤'); // Rows rows.forEach((row) => { lines.push( '│ ' + row .map((cell, i) => (cell || '').toString().substring(0, colWidths[i]).padEnd(colWidths[i]) ) .join(' │ ') + ' │' ); }); // Footer lines.push('└' + colWidths.map((w) => '─'.repeat(w + 2)).join('┴') + '┘'); return lines.join('\n'); } /** * Render text-only dashboard (simpler version) */ renderTextDashboard(data) { const lines = []; lines.push(`=== ${data.workflow} ===`); lines.push(`Progress: ${data.progress}%`); lines.push(`Phase: ${data.phase || 'N/A'}`); lines.push(`Status: ${data.status || 'Running'}`); if (data.team) { lines.push('\nTeam Status:'); data.team.forEach((member) => { lines.push(` - ${member.name}: ${member.status}`); }); } return lines.join('\n'); } /** * Render workflow summary */ renderSummary(data) { const lines = []; lines.push(''); lines.push('╔══════════════════════════════════════════════════════════════════════════╗'); lines.push('║' + this.center('WORKFLOW COMPLETE', 76) + '║'); lines.push('╚══════════════════════════════════════════════════════════════════════════╝'); lines.push(''); lines.push(`${this.color('green', '✅')} Workflow: ${data.workflow}`); lines.push(`${this.color('green', '✅')} Duration: ${this.formatDuration(data.duration)}`); lines.push( `${this.color('green', '✅')} Tasks Completed: ${data.completedTasks ? data.completedTasks.length : 0}` ); lines.push(`${this.color('green', '✅')} Result: ${data.result}`); if (data.team) { lines.push(''); lines.push('Team Performance:'); data.team.forEach((member) => { const icon = member.progress === 100 ? '✅' : '🔄'; lines.push(` ${icon} ${member.name}: ${member.progress}% complete`); }); } return lines.join('\n'); } /** * Show error visualization */ showError(error) { const lines = []; lines.push(''); lines.push( this.color( 'red', '╔══════════════════════════════════════════════════════════════════════════╗' ) ); lines.push(this.color('red', '║' + this.center('ERROR OCCURRED', 76) + '║')); lines.push( this.color( 'red', '╚══════════════════════════════════════════════════════════════════════════╝' ) ); lines.push(''); lines.push(this.color('red', '❌ Simulation stopped due to error:')); lines.push(this.color('yellow', ` ${error}`)); lines.push(''); console.log(lines.join('\n')); } /** * Get status icon */ getStatusIcon(status) { const icons = { available: '🟢', working: '🔄', in_progress: '🔄', complete: '✅', error: '❌', paused: '⏸️', waiting: '⏳', }; return icons[status.toLowerCase()] || '•'; } /** * Get status color */ getStatusColor(status) { const colors = { available: 'green', working: 'yellow', in_progress: 'yellow', complete: 'green', error: 'red', paused: 'white', waiting: 'cyan', }; return colors[status.toLowerCase()] || 'white'; } /** * Apply color if enabled */ color(colorName, text) { if (!this.options.colors) return text; const colorCode = this.colors[colorName] || ''; return colorCode + text + this.colors.reset; } /** * Center text */ center(text, width) { const padding = Math.max(0, width - text.length); const leftPad = Math.floor(padding / 2); const rightPad = padding - leftPad; return ' '.repeat(leftPad) + text + ' '.repeat(rightPad); } /** * Format duration */ formatDuration(ms) { if (ms < 1000) return `${ms}ms`; if (ms < 60000) return `${Math.round(ms / 1000)} seconds`; if (ms < 3600000) return `${Math.round(ms / 60000)} minutes`; return `${(ms / 3600000).toFixed(1)} hours`; } /** * Create a command menu visualization */ renderCommandMenu(commands) { const lines = []; lines.push(''); lines.push(this.renderSectionHeader('Available Commands')); lines.push(''); commands.forEach((cmd, index) => { const num = this.color('cyan', `${index + 1}.`); const command = this.color('yellow', `\`${cmd.command}\``); lines.push(` ${num} ${command} - ${cmd.description}`); }); lines.push(''); lines.push('Please select an option (1-' + commands.length + ') or type command.'); return lines.join('\n'); } } module.exports = VisualizationEngine;