UNPKG

research-cli

Version:

AI-powered research assistant with web search capabilities and beautiful terminal UI

293 lines 8.75 kB
/** * Smart progress controller for research-cli operations */ import { createMultiProgress, createProgressBar, } from "./progress.js"; /** * Intelligent progress manager that adapts to different operation types */ export class ProgressManager { singleBar; multiBar; options; isToolOperationActive = false; toolCount = 0; activeOperations = new Set(); constructor(options = {}) { this.options = { useMultiProgress: false, silent: false, theme: "research", ...options, }; } /** * Handle provider events and update progress accordingly */ handleProviderEvent(event) { if (this.options.silent) return; switch (event.type) { case "start": this.handleStartEvent(event); break; case "tool_call": this.handleToolCallEvent(event); break; case "tool_result": this.handleToolResultEvent(event); break; case "chunk": this.handleChunkEvent(event); break; case "final": this.handleFinalEvent(event); break; case "error": this.handleErrorEvent(event); break; } } handleStartEvent(event) { const data = event.data; const provider = data?.provider || "LLM"; const model = data?.model || "default"; if (this.options.useMultiProgress) { this.ensureMultiProgress(); this.multiBar?.createBar("main", 100, 0, { operation: "Querying", provider, model, stage: "Connecting...", }); } else { this.ensureSingleProgress(); this.singleBar?.start(100, 0, { operation: "Querying", provider, model, stage: "Connecting...", }); } // Update to show we're waiting for response setTimeout(() => { this.updateMainProgress(10, "Waiting for response..."); }, 100); } handleToolCallEvent(event) { const data = event.data; const toolName = data?.name || "tool"; this.isToolOperationActive = true; this.toolCount++; if (this.options.useMultiProgress) { this.ensureMultiProgress(); const toolId = `tool-${this.toolCount}`; this.activeOperations.add(toolId); this.multiBar?.createBar(toolId, 100, 0, { operation: `🔧 ${toolName}`, stage: "Executing...", }); // Update main progress this.updateMainProgress(30, "Using tools..."); } else { this.updateMainProgress(30, `Using ${toolName}...`); } } handleToolResultEvent(event) { const data = event.data; const toolName = data?.name || "tool"; if (this.options.useMultiProgress) { // Complete the most recent tool operation const recentToolId = Array.from(this.activeOperations).pop(); if (recentToolId) { this.multiBar?.updateBar(recentToolId, 100, { stage: "Complete", }); setTimeout(() => { this.multiBar?.removeBar(recentToolId); this.activeOperations.delete(recentToolId); }, 1000); } } else { this.updateMainProgress(60, `Completed ${toolName}`); } // If no more tools are running, update main progress if (this.activeOperations.size === 0) { this.isToolOperationActive = false; this.updateMainProgress(70, "Processing results..."); } } handleChunkEvent(_event) { // First chunk means we're receiving the response if (!this.isToolOperationActive) { this.updateMainProgress(80, "Receiving response..."); } } handleFinalEvent(_event) { this.updateMainProgress(100, "Complete"); // Complete and cleanup after a short delay setTimeout(() => { this.cleanup(); }, 500); } handleErrorEvent(event) { const data = event.data; const message = data?.message || "Error occurred"; this.updateMainProgress(null, `Error: ${message}`); // Cleanup after error setTimeout(() => { this.cleanup(); }, 2000); } updateMainProgress(value, stage) { if (this.multiBar?.active) { this.multiBar.updateBar("main", value, stage ? { stage } : undefined); } else if (this.singleBar?.active) { this.singleBar.update(value, stage ? { stage } : undefined); } } ensureSingleProgress() { if (!this.singleBar) { this.singleBar = createProgressBar(this.options.theme); } } ensureMultiProgress() { if (!this.multiBar) { this.multiBar = createMultiProgress(); } } /** * Enable multi-progress mode for complex operations */ enableMultiProgress() { this.options.useMultiProgress = true; } /** * Disable multi-progress mode */ disableMultiProgress() { this.options.useMultiProgress = false; } /** * Log a message (will appear above progress bars if active) */ log(message) { if (this.options.silent) return; if (this.multiBar?.active) { this.multiBar.log(message); } else { // Fallback to stderr process.stderr.write(`${message}\n`); } } /** * Manually create a progress bar for custom operations */ createCustomProgress(id, total, operation, startValue = 0) { if (this.options.silent) return; if (this.options.useMultiProgress) { this.ensureMultiProgress(); this.multiBar?.createBar(id, total, startValue, { operation, stage: "Starting...", }); } else { this.ensureSingleProgress(); this.singleBar?.start(total, startValue, { operation, stage: "Starting...", }); } } /** * Update a custom progress bar */ updateCustomProgress(id, value, stage) { if (this.options.silent) return; if (this.multiBar?.active) { this.multiBar.updateBar(id, value, stage ? { stage } : undefined); } else if (this.singleBar?.active) { this.singleBar.update(value, stage ? { stage } : undefined); } } /** * Complete a custom progress bar */ completeCustomProgress(id, finalStage = "Complete") { if (this.options.silent) return; if (this.multiBar?.active) { this.multiBar.completeBar(id, finalStage); } else if (this.singleBar?.active) { this.singleBar.complete(finalStage); } } /** * Check if any progress bars are currently active */ get hasActiveProgress() { return (this.singleBar?.active || this.multiBar?.active) ?? false; } /** * Get count of active operations */ get activeOperationCount() { return this.activeOperations.size; } /** * Force cleanup of all progress bars */ cleanup() { this.singleBar?.stop(); this.multiBar?.stop(); this.activeOperations.clear(); this.isToolOperationActive = false; this.toolCount = 0; } /** * Enable silent mode (no progress bars) */ setSilent(silent) { this.options.silent = silent; if (silent) { this.cleanup(); } } } /** * Create a new progress manager instance */ export function createProgressManager(options = {}) { return new ProgressManager(options); } /** * Global progress manager instance for simple usage */ let globalProgressManager; /** * Get or create the global progress manager */ export function getGlobalProgressManager() { if (!globalProgressManager) { globalProgressManager = createProgressManager(); } return globalProgressManager; } /** * Reset the global progress manager */ export function resetGlobalProgressManager() { globalProgressManager?.cleanup(); globalProgressManager = undefined; } //# sourceMappingURL=progress-manager.js.map