Skip to main content
Glama

In Memoria

console-progress.ts5.5 kB
import { ProgressTracker, ProgressUpdate } from './progress-tracker.js'; export class ConsoleProgressRenderer { private tracker: ProgressTracker; private isActive: boolean = false; private renderTimer: NodeJS.Timeout | null = null; private lastOutput: string = ''; private hasRendered: boolean = false; private isCompleted: boolean = false; private hasShownCompletionMessage: boolean = false; constructor(tracker: ProgressTracker) { this.tracker = tracker; this.setupListeners(); } private setupListeners(): void { // No need for phaseStart listener since we show all phases from the beginning // Listen for completion to handle final state properly this.tracker.on('complete', () => { this.isCompleted = true; if (this.isActive) { this.render(); // One final render with complete state } }); } start(): void { this.isActive = true; this.hasRendered = false; this.isCompleted = false; this.hasShownCompletionMessage = false; // Always render immediately to show initial state this.render(); // Update reasonably frequently for smooth progress - every 500ms this.renderTimer = setInterval(() => { if (this.isActive) { this.render(); } }, 500); } stop(): void { // Ensure one final render with completion state this.renderFinal(); // Clean up if (this.renderTimer) { clearInterval(this.renderTimer); this.renderTimer = null; } this.isActive = false; } private render(): void { if (!this.isActive) return; // Get all lines const allLines = this.tracker.getConsoleStatus(); if (allLines.length === 0) return; // Filter out phases that haven't started (all zeros) unless we're completed const lines = this.isCompleted ? allLines : allLines.filter((line, idx) => { // Always keep the overall progress line (first line) if (idx === 0) return true; // Keep lines that have progress or are complete return !line.includes('0% (0/') || line.includes('✓'); }); if (lines.length <= 1) return; // Only overall line, don't render yet // Build the output with separator const output = '\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n' + lines.join('\n') + '\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'; // Only update if something changed if (output !== this.lastOutput) { if (this.hasRendered) { // Move cursor up to the beginning of our progress display and overwrite const lineCount = this.lastOutput.split('\n').length; process.stderr.write(`\x1b[${lineCount}A`); // Move up process.stderr.write('\x1b[0J'); // Clear from cursor to end of screen } process.stderr.write(output + '\n'); this.lastOutput = output; this.hasRendered = true; } } private renderFinal(): void { if (!this.hasRendered) return; this.isCompleted = true; this.render(); // Show completion message only once if (!this.hasShownCompletionMessage) { process.stderr.write('✅ Learning Complete!\n\n'); this.hasShownCompletionMessage = true; } } private formatElapsed(elapsed: number): string { const seconds = Math.floor(elapsed / 1000); const minutes = Math.floor(seconds / 60); if (minutes > 0) { return `${minutes}m ${seconds % 60}s`; } else { return `${seconds}s`; } } // Static helper for simple progress bars static renderSimpleBar(current: number, total: number, width: number = 40, message?: string): string { const percentage = total > 0 ? (current / total) : 0; const filled = Math.floor(percentage * width); const empty = width - filled; // Use ASCII characters in MCP mode, Unicode in terminal mode const isMCPMode = process.env.MCP_SERVER === 'true'; const bar = isMCPMode ? '='.repeat(filled) + '-'.repeat(empty) // ASCII: [====----] : '█'.repeat(filled) + '░'.repeat(empty); // Unicode: [████░░░░] const percent = (percentage * 100).toFixed(1); const baseText = `[${bar}] ${percent}% (${current}/${total})`; return message ? `${message}: ${baseText}` : baseText; } // For MCP server - return progress as structured data getProgressData(): { overall: number; phases: Array<{ name: string; current: number; total: number; percentage: number; eta?: string; }>; elapsed: number; isComplete: boolean; } { const overall = this.tracker.getProgress(); const phases = []; for (const [name, _] of (this.tracker as any).phases) { const phaseProgress = this.tracker.getProgress(name); if (phaseProgress) { phases.push({ name, current: phaseProgress.current, total: phaseProgress.total, percentage: phaseProgress.percentage, eta: phaseProgress.eta }); } } return { overall: overall?.percentage || 0, phases, elapsed: overall?.elapsed || 0, isComplete: overall?.percentage === 100 }; } }

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/pi22by7/In-Memoria'

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