247 lines
8.3 KiB
Python
Raw Normal View History

2025-11-04 08:09:12 +01:00
from pr.multiplexer import get_multiplexer
2025-11-04 07:52:36 +01:00
from pr.tools.interactive_control import (
2025-11-04 08:09:12 +01:00
close_interactive_session,
get_session_status,
list_active_sessions,
read_session_output,
send_input_to_session,
2025-11-04 07:52:36 +01:00
)
from pr.tools.prompt_detection import get_global_detector
from pr.ui import Colors
2025-11-04 08:09:12 +01:00
2025-11-04 07:52:36 +01:00
def show_sessions(args=None):
"""Show all active multiplexer sessions."""
sessions = list_active_sessions()
if not sessions:
print(f"{Colors.YELLOW}No active sessions.{Colors.RESET}")
return
print(f"{Colors.BOLD}Active Sessions:{Colors.RESET}")
print("-" * 80)
for session_name, session_data in sessions.items():
2025-11-04 08:09:12 +01:00
metadata = session_data["metadata"]
output_summary = session_data["output_summary"]
2025-11-04 07:52:36 +01:00
status = get_session_status(session_name)
2025-11-04 08:09:12 +01:00
is_active = status.get("is_active", False) if status else False
2025-11-04 07:52:36 +01:00
status_color = Colors.GREEN if is_active else Colors.RED
2025-11-04 08:09:12 +01:00
print(
f"{Colors.CYAN}{session_name}{Colors.RESET}: {status_color}{metadata.get('process_type', 'unknown')}{Colors.RESET}"
)
2025-11-04 07:52:36 +01:00
2025-11-04 08:09:12 +01:00
if status and "pid" in status:
2025-11-04 07:52:36 +01:00
print(f" PID: {status['pid']}")
print(f" Age: {metadata.get('start_time', 0):.1f}s")
2025-11-04 08:09:12 +01:00
print(
f" Output: {output_summary['stdout_lines']} stdout, {output_summary['stderr_lines']} stderr lines"
)
2025-11-04 07:52:36 +01:00
print(f" Interactions: {metadata.get('interaction_count', 0)}")
print(f" State: {metadata.get('state', 'unknown')}")
print()
2025-11-04 08:09:12 +01:00
2025-11-04 07:52:36 +01:00
def attach_session(args):
"""Attach to a session (show its output and allow interaction)."""
if not args or len(args) < 1:
print(f"{Colors.RED}Usage: attach_session <session_name>{Colors.RESET}")
return
session_name = args[0]
status = get_session_status(session_name)
if not status:
print(f"{Colors.RED}Session '{session_name}' not found.{Colors.RESET}")
return
print(f"{Colors.BOLD}Attaching to session: {session_name}{Colors.RESET}")
print(f"Process type: {status.get('metadata', {}).get('process_type', 'unknown')}")
print("-" * 50)
# Show recent output
try:
output = read_session_output(session_name, lines=20)
2025-11-04 08:09:12 +01:00
if output["stdout"]:
2025-11-04 07:52:36 +01:00
print(f"{Colors.GRAY}Recent stdout:{Colors.RESET}")
2025-11-04 08:09:12 +01:00
for line in output["stdout"].split("\n"):
2025-11-04 07:52:36 +01:00
if line.strip():
print(f" {line}")
2025-11-04 08:09:12 +01:00
if output["stderr"]:
2025-11-04 07:52:36 +01:00
print(f"{Colors.YELLOW}Recent stderr:{Colors.RESET}")
2025-11-04 08:09:12 +01:00
for line in output["stderr"].split("\n"):
2025-11-04 07:52:36 +01:00
if line.strip():
print(f" {line}")
except Exception as e:
print(f"{Colors.RED}Error reading output: {e}{Colors.RESET}")
2025-11-04 08:09:12 +01:00
print(
f"\n{Colors.CYAN}Session is {'active' if status.get('is_active') else 'inactive'}{Colors.RESET}"
)
2025-11-04 07:52:36 +01:00
def detach_session(args):
"""Detach from a session (stop showing its output but keep it running)."""
if not args or len(args) < 1:
print(f"{Colors.RED}Usage: detach_session <session_name>{Colors.RESET}")
return
session_name = args[0]
mux = get_multiplexer(session_name)
if not mux:
print(f"{Colors.RED}Session '{session_name}' not found.{Colors.RESET}")
return
# In this implementation, detaching just means we stop displaying output
# The session continues to run in the background
mux.show_output = False
2025-11-04 08:09:12 +01:00
print(
f"{Colors.GREEN}Detached from session '{session_name}'. It continues running in background.{Colors.RESET}"
)
2025-11-04 07:52:36 +01:00
def kill_session(args):
"""Kill a session forcefully."""
if not args or len(args) < 1:
print(f"{Colors.RED}Usage: kill_session <session_name>{Colors.RESET}")
return
session_name = args[0]
try:
close_interactive_session(session_name)
print(f"{Colors.GREEN}Session '{session_name}' terminated.{Colors.RESET}")
except Exception as e:
2025-11-04 08:10:37 +01:00
print(f"{Colors.RED}Error terminating session '{session_name}': {e}{Colors.RESET}")
2025-11-04 08:09:12 +01:00
2025-11-04 07:52:36 +01:00
def send_command(args):
"""Send a command to a session."""
if not args or len(args) < 2:
print(f"{Colors.RED}Usage: send_command <session_name> <command>{Colors.RESET}")
return
session_name = args[0]
2025-11-04 08:09:12 +01:00
command = " ".join(args[1:])
2025-11-04 07:52:36 +01:00
try:
send_input_to_session(session_name, command)
2025-11-04 08:10:37 +01:00
print(f"{Colors.GREEN}Sent command to '{session_name}': {command}{Colors.RESET}")
2025-11-04 07:52:36 +01:00
except Exception as e:
2025-11-04 08:10:37 +01:00
print(f"{Colors.RED}Error sending command to '{session_name}': {e}{Colors.RESET}")
2025-11-04 08:09:12 +01:00
2025-11-04 07:52:36 +01:00
def show_session_log(args):
"""Show the full log/output of a session."""
if not args or len(args) < 1:
print(f"{Colors.RED}Usage: show_session_log <session_name>{Colors.RESET}")
return
session_name = args[0]
try:
output = read_session_output(session_name) # Get all output
print(f"{Colors.BOLD}Full log for session: {session_name}{Colors.RESET}")
print("=" * 80)
2025-11-04 08:09:12 +01:00
if output["stdout"]:
2025-11-04 07:52:36 +01:00
print(f"{Colors.GRAY}STDOUT:{Colors.RESET}")
2025-11-04 08:09:12 +01:00
print(output["stdout"])
2025-11-04 07:52:36 +01:00
print()
2025-11-04 08:09:12 +01:00
if output["stderr"]:
2025-11-04 07:52:36 +01:00
print(f"{Colors.YELLOW}STDERR:{Colors.RESET}")
2025-11-04 08:09:12 +01:00
print(output["stderr"])
2025-11-04 07:52:36 +01:00
print()
except Exception as e:
print(f"{Colors.RED}Error reading log for '{session_name}': {e}{Colors.RESET}")
2025-11-04 08:09:12 +01:00
2025-11-04 07:52:36 +01:00
def show_session_status(args):
"""Show detailed status of a session."""
if not args or len(args) < 1:
print(f"{Colors.RED}Usage: show_session_status <session_name>{Colors.RESET}")
return
session_name = args[0]
status = get_session_status(session_name)
if not status:
print(f"{Colors.RED}Session '{session_name}' not found.{Colors.RESET}")
return
print(f"{Colors.BOLD}Status for session: {session_name}{Colors.RESET}")
print("-" * 50)
2025-11-04 08:09:12 +01:00
metadata = status.get("metadata", {})
2025-11-04 07:52:36 +01:00
print(f"Process type: {metadata.get('process_type', 'unknown')}")
print(f"Active: {status.get('is_active', False)}")
2025-11-04 08:09:12 +01:00
if "pid" in status:
2025-11-04 07:52:36 +01:00
print(f"PID: {status['pid']}")
print(f"Start time: {metadata.get('start_time', 0):.1f}")
print(f"Last activity: {metadata.get('last_activity', 0):.1f}")
print(f"Interaction count: {metadata.get('interaction_count', 0)}")
print(f"State: {metadata.get('state', 'unknown')}")
2025-11-04 08:09:12 +01:00
output_summary = status.get("output_summary", {})
print(
f"Output lines: {output_summary.get('stdout_lines', 0)} stdout, {output_summary.get('stderr_lines', 0)} stderr"
)
2025-11-04 07:52:36 +01:00
# Show prompt detection info
detector = get_global_detector()
session_info = detector.get_session_info(session_name)
if session_info:
print(f"Current state: {session_info['current_state']}")
print(f"Is waiting for input: {session_info['is_waiting']}")
2025-11-04 08:09:12 +01:00
2025-11-04 07:52:36 +01:00
def list_waiting_sessions(args=None):
"""List sessions that appear to be waiting for input."""
sessions = list_active_sessions()
detector = get_global_detector()
waiting_sessions = []
for session_name in sessions:
if detector.is_waiting_for_input(session_name):
waiting_sessions.append(session_name)
if not waiting_sessions:
2025-11-04 08:10:37 +01:00
print(f"{Colors.GREEN}No sessions are currently waiting for input.{Colors.RESET}")
2025-11-04 07:52:36 +01:00
return
print(f"{Colors.BOLD}Sessions waiting for input:{Colors.RESET}")
for session_name in waiting_sessions:
status = get_session_status(session_name)
if status:
2025-11-04 08:09:12 +01:00
process_type = status.get("metadata", {}).get("process_type", "unknown")
2025-11-04 07:52:36 +01:00
print(f" {Colors.CYAN}{session_name}{Colors.RESET} ({process_type})")
# Show suggestions
session_info = detector.get_session_info(session_name)
if session_info:
suggestions = detector.get_response_suggestions({}, process_type)
if suggestions:
2025-11-04 08:10:37 +01:00
print(f" Suggested inputs: {', '.join(suggestions[:3])}") # Show first 3
2025-11-04 07:52:36 +01:00
print()
2025-11-04 08:09:12 +01:00
2025-11-04 07:52:36 +01:00
# Command registry for the multiplexer commands
MULTIPLEXER_COMMANDS = {
2025-11-04 08:09:12 +01:00
"show_sessions": show_sessions,
"attach_session": attach_session,
"detach_session": detach_session,
"kill_session": kill_session,
"send_command": send_command,
"show_session_log": show_session_log,
"show_session_status": show_session_status,
"list_waiting_sessions": list_waiting_sessions,
}