UNPKG

@sethdouglasford/claude-flow

Version:

Claude Code Flow - Advanced AI-powered development workflows with SPARC methodology

209 lines 7.1 kB
/** * Terminal session management */ import { TerminalCommandError } from "../utils/errors.js"; import { generateId, timeout } from "../utils/helpers.js"; /** * Terminal session wrapper */ export class TerminalSession { terminal; profile; commandTimeout; logger; id; startTime; initialized = false; commandHistory = []; lastCommandTime; outputListeners = new Set(); constructor(terminal, profile, commandTimeout, logger) { this.terminal = terminal; this.profile = profile; this.commandTimeout = commandTimeout; this.logger = logger; this.id = generateId("session"); this.startTime = new Date(); } get lastActivity() { return this.lastCommandTime ?? this.startTime; } async initialize() { if (this.initialized) { return; } this.logger.debug("Initializing terminal session", { sessionId: this.id, agentId: this.profile.id, }); try { // Set up environment await this.setupEnvironment(); // Run initialization commands await this.runInitializationCommands(); this.initialized = true; this.logger.info("Terminal session initialized", { sessionId: this.id, agentId: this.profile.id, }); } catch (error) { this.logger.error("Failed to initialize terminal session", error); throw error; } } async executeCommand(command) { if (!this.initialized) { throw new TerminalCommandError("Session not initialized"); } if (!this.terminal.isAlive()) { throw new TerminalCommandError("Terminal is not alive"); } this.logger.debug("Executing command", { sessionId: this.id, command: command.substring(0, 100), }); try { // Notify listeners of command this.notifyOutputListeners(`$ ${command}\n`); // Execute with timeout const result = await timeout(this.terminal.executeCommand(command), this.commandTimeout, `Command timeout after ${this.commandTimeout}ms`); // Notify listeners of output this.notifyOutputListeners(result); // Update history this.commandHistory.push(command); this.lastCommandTime = new Date(); this.logger.debug("Command executed successfully", { sessionId: this.id, outputLength: result.length, }); return result; } catch (error) { this.logger.error("Command execution failed", { sessionId: this.id, command, error, }); throw new TerminalCommandError("Command execution failed", { command, error, }); } } async cleanup() { this.logger.debug("Cleaning up terminal session", { sessionId: this.id }); try { // Run cleanup commands await this.runCleanupCommands(); } catch (error) { this.logger.warn("Error during session cleanup", { sessionId: this.id, error, }); } } isHealthy() { if (!this.terminal.isAlive()) { return false; } // Check if terminal is responsive if (this.lastCommandTime) { const timeSinceLastCommand = Date.now() - this.lastCommandTime.getTime(); if (timeSinceLastCommand > 300000) { // 5 minutes // Terminal might be stale, do a health check this.performHealthCheck().catch((error) => { this.logger.warn("Health check failed", { sessionId: this.id, error }); }); } } return true; } getCommandHistory() { return [...this.commandHistory]; } async setupEnvironment() { // Set environment variables const envVars = { CLAUDE_FLOW_SESSION: this.id, CLAUDE_FLOW_AGENT: this.profile.id, CLAUDE_FLOW_AGENT_TYPE: this.profile.type, }; for (const [key, value] of Object.entries(envVars)) { await this.terminal.executeCommand(`export ${key}="${value}"`); } // Set working directory if specified if (this.profile.metadata?.workingDirectory) { await this.terminal.executeCommand(`cd "${String(this.profile.metadata.workingDirectory)}"`); } } async runInitializationCommands() { // Run any profile-specific initialization commands if (this.profile.metadata?.initCommands) { const commands = this.profile.metadata.initCommands; for (const command of commands) { await this.terminal.executeCommand(command); } } // Set up command prompt await this.terminal.executeCommand("export PS1=\"[claude-flow]$ \""); } async runCleanupCommands() { // Run any profile-specific cleanup commands if (this.profile.metadata?.cleanupCommands) { const commands = this.profile.metadata.cleanupCommands; for (const command of commands) { try { await this.terminal.executeCommand(command); } catch { // Ignore cleanup errors } } } } async performHealthCheck() { try { const result = await timeout(this.terminal.executeCommand("echo \"HEALTH_CHECK_OK\""), 5000, "Health check timeout"); if (!result.includes("HEALTH_CHECK_OK")) { throw new Error("Invalid health check response"); } this.lastCommandTime = new Date(); } catch (error) { throw new Error(`Health check failed: ${error instanceof Error ? error.message : String(error)}`); } } /** * Stream terminal output */ streamOutput(callback) { this.outputListeners.add(callback); // Set up terminal output listener if supported if (this.terminal.addOutputListener) { this.terminal.addOutputListener(callback); } // Return unsubscribe function return () => { this.outputListeners.delete(callback); if (this.terminal.removeOutputListener) { this.terminal.removeOutputListener(callback); } }; } /** * Notify output listeners */ notifyOutputListeners(output) { this.outputListeners.forEach(listener => { try { listener(output); } catch (error) { this.logger.error("Error in output listener", { sessionId: this.id, error }); } }); } } //# sourceMappingURL=session.js.map