Skip to main content
Glama
ooples

MCP Console Automation Server

ConsoleManagerPatch.ts6.38 kB
/** * Patch for ConsoleManager to fix interactive program support * This modifies the sendInput behavior for SSH sessions */ import { InteractiveModeDetector } from './InteractiveModeDetector.js'; export class ConsoleManagerInteractivePatch { private interactiveDetector: InteractiveModeDetector; constructor() { this.interactiveDetector = new InteractiveModeDetector(); } /** * Enhanced sendInput that handles interactive programs correctly * This should replace the existing sendInput method in ConsoleManager */ async sendInputEnhanced( sessionId: string, input: string, session: any, sshChannel: any, addCommandToQueue: (sessionId: string, input: string) => Promise<void>, logger: any ): Promise<void> { // Check if this is an interactive command or session const isInteractiveCommand = this.interactiveDetector.isInteractiveCommand(input); const isInteractiveSession = this.interactiveDetector.isInteractive(sessionId); logger.debug( `Session ${sessionId} - Interactive command: ${isInteractiveCommand}, Interactive session: ${isInteractiveSession}` ); // Track the command this.interactiveDetector.trackCommand(sessionId, input); // For SSH sessions if (sshChannel) { // If in interactive mode or executing an interactive command, send directly if (isInteractiveSession || isInteractiveCommand) { logger.debug( `Sending input directly to interactive SSH session ${sessionId}` ); return new Promise<void>((resolve, reject) => { // Send input directly to SSH channel without queuing const writeCallback = (error?: Error) => { if (error) { logger.error( `Failed to send input to SSH session ${sessionId}:`, error ); reject(error); } else { logger.debug( `Input sent successfully to interactive SSH session ${sessionId}` ); resolve(); } }; // Write the input directly to the SSH stream // For interactive programs, we usually don't add \n automatically // as they handle their own input processing if ( isInteractiveSession && !input.endsWith('\n') && !input.endsWith('\r') ) { // For most interactive programs, we still need to send Enter // but some special keys should be sent as-is const specialKeys = ['\x03', '\x04', '\x1b', '\t']; // Ctrl-C, Ctrl-D, ESC, TAB if (!specialKeys.some((key) => input.includes(key))) { sshChannel.write(input + '\n', writeCallback); } else { sshChannel.write(input, writeCallback); } } else { sshChannel.write(input, writeCallback); } }); } else { // Use the existing command queue for non-interactive commands logger.debug( `Using command queue for non-interactive SSH session ${sessionId}` ); return addCommandToQueue(sessionId, input); } } // For non-SSH sessions, throw error (caller should handle other session types) throw new Error(`Session ${sessionId} is not an SSH session`); } /** * Process output to detect interactive mode transitions */ processOutput(sessionId: string, output: string): void { // Detect if we've entered interactive mode if (this.interactiveDetector.detectInteractiveMode(sessionId, output)) { console.log(`Session ${sessionId} entered interactive mode`); } // Detect if we've exited interactive mode if (this.interactiveDetector.detectInteractiveExit(sessionId, output)) { console.log(`Session ${sessionId} exited interactive mode`); } } /** * Clean up when session ends */ cleanupSession(sessionId: string): void { this.interactiveDetector.clearInteractiveMode(sessionId); } /** * Get the interactive detector instance */ getDetector(): InteractiveModeDetector { return this.interactiveDetector; } } /** * Function to patch the existing ConsoleManager * This should be called during initialization */ export function patchConsoleManagerForInteractive(consoleManager: any): void { const patch = new ConsoleManagerInteractivePatch(); const originalSendInput = consoleManager.sendInput.bind(consoleManager); const originalCleanup = consoleManager.cleanupSession.bind(consoleManager); // Replace sendInput method consoleManager.sendInput = async function ( sessionId: string, input: string ): Promise<void> { const session = this.sessions.get(sessionId); const sshChannel = this.sshChannels.get(sessionId); if (sshChannel) { try { // Use the enhanced input handler for SSH sessions return await patch.sendInputEnhanced( sessionId, input, session, sshChannel, this.addCommandToQueue.bind(this), this.logger ); } catch (error) { // Fall back to original behavior if patch fails this.logger.warn( `Interactive patch failed, using original sendInput:`, error ); return originalSendInput(sessionId, input); } } // Use original implementation for non-SSH sessions return originalSendInput(sessionId, input); }; // Hook into output processing const originalAddToBuffer = consoleManager.addToBuffer?.bind(consoleManager); if (originalAddToBuffer) { consoleManager.addToBuffer = function (sessionId: string, output: any) { // Process output for interactive mode detection if (output && output.data) { patch.processOutput(sessionId, output.data); } return originalAddToBuffer(sessionId, output); }; } // Hook into cleanup consoleManager.cleanupSession = function (sessionId: string) { patch.cleanupSession(sessionId); return originalCleanup(sessionId); }; // Add method to check interactive status consoleManager.isInteractiveSession = function (sessionId: string): boolean { return patch.getDetector().isInteractive(sessionId); }; console.log('ConsoleManager patched for interactive program support'); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ooples/mcp-console-automation'

If you have feedback or need assistance with the MCP directory API, please join our Discord server