claude-flow-tbowman01
Version:
Enterprise-grade AI agent orchestration with ruv-swarm integration (Alpha Release)
206 lines • 7.05 kB
JavaScript
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 "${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