UNPKG

claude-flow

Version:

Enterprise-grade AI agent orchestration with WASM-powered ReasoningBank memory and AgentDB vector database (always uses latest agentic-flow)

248 lines (212 loc) 6.99 kB
import { getErrorMessage } from '../utils/error-handler.js'; /** * VSCode Extension Bridge for Terminal Integration * * This file provides the bridge between Claude-Flow and VSCode extension API * for terminal management and output capture. * * NOTE: This file is only used when Claude-Flow is packaged as a VS Code extension. * It is excluded from the main CLI build. If you need to use this in a VS Code * extension context, install @types/vscode as a devDependency. */ import * as vscode from 'vscode'; /** * Terminal output processors registry */ const terminalOutputProcessors = new Map<string, (data: string) => void>(); /** * Active terminals registry */ const activeTerminals = new Map<string, vscode.Terminal>(); /** * Terminal write emulators for output capture */ const terminalWriteEmulators = new Map<vscode.Terminal, vscode.EventEmitter<string>>(); /** * Initialize the VSCode terminal bridge */ export function initializeTerminalBridge(context: vscode.ExtensionContext): void { // Inject VSCode API into global scope for Claude-Flow (globalThis as any).vscode = vscode; // Register terminal output processor function (globalThis as any).registerTerminalOutputProcessor = ( terminalId: string, processor: (data: string) => void, ) => { terminalOutputProcessors.set(terminalId, processor); }; // Override terminal creation to capture output const originalCreateTerminal = vscode.window.createTerminal; (vscode.window as any).createTerminal = function (options: vscode.TerminalOptions) { const terminal = originalCreateTerminal.call(vscode.window, options) as vscode.Terminal; // Create write emulator for this terminal const writeEmulator = new vscode.EventEmitter<string>(); terminalWriteEmulators.set(terminal, writeEmulator); // Find terminal ID from name const match = options.name?.match(/Claude-Flow Terminal ([\w-]+)/); if (match) { const terminalId = match[1]; activeTerminals.set(terminalId, terminal); // Set up output capture captureTerminalOutput(terminal, terminalId); } return terminal; }; // Clean up on terminal close context.subscriptions.push( vscode.window.onDidCloseTerminal((terminal: vscode.Terminal) => { // Find and remove from registries for (const [id, term] of activeTerminals.entries()) { if (term === terminal) { activeTerminals.delete(id); terminalOutputProcessors.delete(id); break; } } // Clean up write emulator const emulator = terminalWriteEmulators.get(terminal); if (emulator) { emulator.dispose(); terminalWriteEmulators.delete(terminal); } }), ); } /** * Capture terminal output using various methods */ function captureTerminalOutput(terminal: vscode.Terminal, terminalId: string): void { // Method 1: Use terminal.sendText override to capture commands const originalSendText = terminal.sendText; (terminal as any).sendText = function (text: string, addNewLine?: boolean) { // Call original method originalSendText.call(terminal, text, addNewLine); // Process command const processor = terminalOutputProcessors.get(terminalId); if (processor && text) { // Simulate command echo processor(text + (addNewLine !== false ? '\n' : '')); } }; // Method 2: Use proposed API if available if ('onDidWriteData' in terminal) { const writeDataEvent = (terminal as any).onDidWriteData; if (writeDataEvent) { writeDataEvent((data: string) => { const processor = terminalOutputProcessors.get(terminalId); if (processor) { processor(data); } }); } } // Method 3: Use terminal renderer if available setupTerminalRenderer(terminal, terminalId); } /** * Set up terminal renderer for output capture */ function setupTerminalRenderer(terminal: vscode.Terminal, terminalId: string): void { // Check if terminal renderer API is available if (vscode.window.registerTerminalProfileProvider) { // This is a more advanced method that requires additional setup // It would involve creating a custom terminal profile that captures output // For now, we'll use a simpler approach with periodic output checking let lastOutput = ''; const checkOutput = setInterval(() => { // This is a placeholder - actual implementation would depend on // available VSCode APIs for reading terminal content // Check if terminal is still active if (!activeTerminals.has(terminalId)) { clearInterval(checkOutput); } }, 100); } } /** * Create a terminal with output capture */ export async function createCapturedTerminal( name: string, shellPath?: string, shellArgs?: string[], ): Promise<{ terminal: vscode.Terminal; onData: vscode.Event<string>; }> { const writeEmulator = new vscode.EventEmitter<string>(); const terminal = vscode.window.createTerminal({ name, shellPath, shellArgs, }); terminalWriteEmulators.set(terminal, writeEmulator); return { terminal, onData: writeEmulator.event, }; } /** * Send command to terminal and capture output */ export async function executeTerminalCommand( terminal: vscode.Terminal, command: string, timeout: number = 30000, ): Promise<string> { return new Promise((resolve, reject) => { const writeEmulator = terminalWriteEmulators.get(terminal); if (!writeEmulator) { reject(new Error('No write emulator for terminal')); return; } let output = ''; const marker = `__COMMAND_COMPLETE_${Date.now()}__`; // Set up output listener const disposable = writeEmulator.event((data: string) => { output += data; if (output.includes(marker)) { // Command completed disposable.dispose(); const result = output.substring(0, output.indexOf(marker)); resolve(result); } }); // Set timeout const timer = setTimeout(() => { disposable.dispose(); reject(new Error('Command timeout')); }, timeout); // Execute command with marker terminal.sendText(`${command} && echo "${marker}"`); // Clear timeout on success writeEmulator.event(() => { if (output.includes(marker)) { clearTimeout(timer); } }); }); } /** * Get terminal by ID */ export function getTerminalById(terminalId: string): vscode.Terminal | undefined { return activeTerminals.get(terminalId); } /** * Dispose all terminal resources */ export function disposeTerminalBridge(): void { // Clean up all terminals for (const terminal of activeTerminals.values()) { terminal.dispose(); } activeTerminals.clear(); // Clean up processors terminalOutputProcessors.clear(); // Clean up write emulators for (const emulator of terminalWriteEmulators.values()) { emulator.dispose(); } terminalWriteEmulators.clear(); }