research-cli
Version:
AI-powered research assistant with web search capabilities and beautiful terminal UI
293 lines • 8.75 kB
JavaScript
/**
* 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