diff --git a/apps/api/src/services/terminal_session.py b/apps/api/src/services/terminal_session.py index 4302a25..f66632a 100644 --- a/apps/api/src/services/terminal_session.py +++ b/apps/api/src/services/terminal_session.py @@ -145,38 +145,27 @@ class TerminalSession: # Only resize if dimensions actually changed if cols == self._cols and rows == self._rows: + logger.info(f"resize() skipped for session {self.session_id}: already {cols}x{rows}") return self._cols = cols self._rows = rows logger.info(f"resize() called for session {self.session_id}: {cols}x{rows}") + + # Set host PTY size self._set_terminal_size(cols, rows) # Docker exec doesn't forward PTY resize to the container process, # so we need to explicitly set the size inside the container shell. # Send on every resize so the container shell always matches the frontend. - # Use ANSI escape sequences to hide the command from view: - # - \x1b[?25l: hide cursor - # - \x1b[A\x1b[2K: move up and clear line - # - \x1b[?25h: show cursor + # Use stty -echo to hide command output, then re-enable echo + # Add small delay to ensure shell is ready to receive commands + await asyncio.sleep(0.1) stty_cmd = ( - f"\x1b[?25lstty cols {cols} rows {rows}\n\x1b[A\x1b[2K\x1b[?25h" + f"stty -echo; stty cols {cols} rows {rows}; stty echo\n" ).encode() await self.write_input(stty_cmd) - logger.debug(f"Sent stty resize to container for session {self.session_id}: {cols}x{rows}") - - # Docker exec doesn't forward PTY resize to the container process, - # so we need to explicitly set the size inside the container shell. - # Send on every resize so the container shell always matches the frontend. - # Use ANSI escape sequences to hide the command from view: - # - \x1b[?25l: hide cursor - # - \x1b[A\x1b[2K: move up and clear line - # - \x1b[?25h: show cursor - stty_cmd = ( - f"\x1b[?25lstty cols {cols} rows {rows}\n\x1b[A\x1b[2K\x1b[?25h" - ).encode() - await self.write_input(stty_cmd) - logger.debug(f"Sent stty resize to container for session {self.session_id}: {cols}x{rows}") + logger.info(f"Sent stty resize to container for session {self.session_id}: {cols}x{rows}") async def reset(self) -> None: """Reset the session by killing the process and clearing state."""