import subprocess import threading import time from pr.multiplexer import create_multiplexer, get_multiplexer, close_multiplexer, get_all_multiplexer_states def start_interactive_session(command, session_name=None, process_type='generic'): """ Start an interactive session in a dedicated multiplexer. Args: command: The command to run (list or string) session_name: Optional name for the session process_type: Type of process (ssh, vim, apt, etc.) Returns: session_name: The name of the created session """ name, mux = create_multiplexer(session_name) mux.update_metadata('process_type', process_type) # Start the process if isinstance(command, str): command = command.split() try: process = subprocess.Popen( command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1 ) mux.process = process mux.update_metadata('pid', process.pid) # Set process type and handler detected_type = detect_process_type(command) mux.set_process_type(detected_type) # Start output readers stdout_thread = threading.Thread(target=_read_output, args=(process.stdout, mux.write_stdout), daemon=True) stderr_thread = threading.Thread(target=_read_output, args=(process.stderr, mux.write_stderr), daemon=True) stdout_thread.start() stderr_thread.start() mux.stdout_thread = stdout_thread mux.stderr_thread = stderr_thread return name except Exception as e: close_multiplexer(name) raise e def _read_output(stream, write_func): """Read from a stream and write to multiplexer buffer.""" try: for line in iter(stream.readline, ''): if line: write_func(line.rstrip('\n')) except Exception as e: print(f"Error reading output: {e}") def send_input_to_session(session_name, input_data): """ Send input to an interactive session. Args: session_name: Name of the session input_data: Input string to send """ mux = get_multiplexer(session_name) if not mux: raise ValueError(f"Session {session_name} not found") if not hasattr(mux, 'process') or mux.process.poll() is not None: raise ValueError(f"Session {session_name} is not active") try: mux.process.stdin.write(input_data + '\n') mux.process.stdin.flush() except Exception as e: raise RuntimeError(f"Failed to send input to session {session_name}: {e}") def read_session_output(session_name, lines=None): """ Read output from a session. Args: session_name: Name of the session lines: Number of recent lines to return (None for all) Returns: dict: {'stdout': str, 'stderr': str} """ mux = get_multiplexer(session_name) if not mux: raise ValueError(f"Session {session_name} not found") output = mux.get_all_output() if lines is not None: # Return last N lines stdout_lines = output['stdout'].split('\n')[-lines:] if output['stdout'] else [] stderr_lines = output['stderr'].split('\n')[-lines:] if output['stderr'] else [] output = { 'stdout': '\n'.join(stdout_lines), 'stderr': '\n'.join(stderr_lines) } return output def list_active_sessions(): """ List all active interactive sessions. Returns: dict: Session states """ return get_all_multiplexer_states() def get_session_status(session_name): """ Get detailed status of a session. Args: session_name: Name of the session Returns: dict: Session metadata and status """ mux = get_multiplexer(session_name) if not mux: return None status = mux.get_metadata() status['is_active'] = hasattr(mux, 'process') and mux.process.poll() is None if status['is_active']: status['pid'] = mux.process.pid status['output_summary'] = { 'stdout_lines': len(mux.stdout_buffer), 'stderr_lines': len(mux.stderr_buffer) } return status def close_interactive_session(session_name): """ Close an interactive session. """ try: mux = get_multiplexer(session_name) if mux: mux.process.kill() close_multiplexer(session_name) return {"status": "success"} except Exception as e: return {"status": "error", "error": str(e)}