fix: use docker exec -i instead of -it so host PTY resize propagates to container

Removes -t flag from docker exec so it uses our PTY slave directly instead
of creating its own PTY inside the container. This allows TIOCSWINSZ on the
host PTY master to propagate naturally to the container shell via SIGWINCH.

Also removes all stty command injection logic since resize now works natively.
This commit is contained in:
Fusion
2026-05-24 20:35:07 +02:00
parent 45fb0c753c
commit 29e4bed9e6
+6 -13
View File
@@ -61,11 +61,15 @@ class TerminalSession:
logger.info(f"Starting terminal session {self.session_id} for container {self.container_id} with initial size {self._cols}x{self._rows}")
# Start docker exec with the slave fd as stdin/stdout/stderr
# Using -it because the slave fd IS a TTY
# Using -i (interactive) but NOT -t (tty) because:
# 1. The slave fd IS a TTY
# 2. docker exec -t creates its OWN PTY inside the container
# 3. This prevents host PTY resize from propagating to the container shell
# By using only -i, docker exec uses our PTY slave directly
self.process = await asyncio.create_subprocess_exec(
"docker",
"exec",
"-it",
"-i",
"-e",
"TERM=xterm",
self.container_id,
@@ -151,17 +155,6 @@ class TerminalSession:
self._rows = rows
logger.info(f"resize() called for session {self.session_id}: {cols}x{rows}")
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 stty -echo to prevent the command from being visible, then clear the line.
stty_cmd = (
f"stty -echo; stty cols {cols} rows {rows}; stty echo\n"
f"\x1b[A\x1b[M" # Move up 1 line and delete it (clears the stty command)
).encode()
await self.write_input(stty_cmd)
logger.debug(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."""