diff --git a/apps/api/src/services/terminal_session.py b/apps/api/src/services/terminal_session.py index 034b86f..0557a8b 100644 --- a/apps/api/src/services/terminal_session.py +++ b/apps/api/src/services/terminal_session.py @@ -5,6 +5,7 @@ import logging import os import pty import select +import signal import struct import fcntl import time @@ -153,14 +154,18 @@ class TerminalSession: self._set_terminal_size(cols, rows) # Docker exec -it creates its own PTY inside the container, - # so host PTY resize doesn't propagate. We must send stty manually. - # To hide the command from the user: - # 1. \r moves cursor to start of current line (overwrites prompt) - # 2. stty command executes silently (no output on success) - # 3. \r moves cursor back to start, hiding the echoed command - stty_cmd = f"\rstty cols {cols} rows {rows}\r".encode() - await self.write_input(stty_cmd) - logger.debug(f"Sent stty resize to container for session {self.session_id}: {cols}x{rows}") + # so host PTY resize doesn't propagate to the container shell. + # Send SIGWINCH to the docker exec process on the host. + # Docker exec forwards signals to the container process, which should + # cause the container's shell to re-read its terminal size. + if self.process and self.process.pid: + try: + os.kill(self.process.pid, signal.SIGWINCH) + logger.debug(f"Sent SIGWINCH to docker exec process {self.process.pid} for session {self.session_id}") + except ProcessLookupError: + logger.warning(f"docker exec process {self.process.pid} not found for session {self.session_id}") + except Exception as e: + logger.warning(f"Failed to send SIGWINCH: {e}") async def reset(self) -> None: """Reset the session by killing the process and clearing state."""