Skip to content

Commit 7259bd4

Browse files
committed
fix: re-enable OPOST after setraw to fix terminal rendering
1 parent 0a4c1bb commit 7259bd4

2 files changed

Lines changed: 15 additions & 3 deletions

File tree

centml/cli/shell.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@ async def _interactive_session(ws_url, token):
143143
old_settings = termios.tcgetattr(fd)
144144
try:
145145
tty.setraw(fd)
146+
# setraw disables OPOST, which means \n won't be translated to \r\n.
147+
# The remote PTY may send bare \n; re-enable output post-processing
148+
# so the local terminal handles the translation (like xterm.js does).
149+
mode = termios.tcgetattr(fd)
150+
mode[1] = mode[1] | termios.OPOST
151+
termios.tcsetattr(fd, termios.TCSANOW, mode)
146152
rows, cols = shutil.get_terminal_size()
147153

148154
headers = {"Authorization": f"Bearer {token}"}

tests/test_shell.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,13 @@ async def test_restores_terminal_on_exception(self):
306306
), patch("centml.cli.shell.websockets") as mock_ws_mod:
307307

308308
mock_sys.stdin.fileno.return_value = 0
309-
mock_termios.tcgetattr.return_value = ["old_settings"]
309+
# tcgetattr is called twice: once to save, once after setraw for OPOST fix.
310+
# Return a realistic 7-element termios attrs list.
311+
old_attrs = [0, 0, 0, 0, 0, 0, [0] * 32]
312+
mock_termios.tcgetattr.return_value = old_attrs
313+
mock_termios.OPOST = 0x1
314+
mock_termios.TCSANOW = 0
315+
mock_termios.TCSADRAIN = 1
310316

311317
mock_ws_mod.connect = MagicMock(
312318
return_value=AsyncMock(
@@ -318,9 +324,9 @@ async def test_restores_terminal_on_exception(self):
318324
with pytest.raises(ConnectionRefusedError):
319325
await _interactive_session("wss://test/ws", "fake-token")
320326

321-
mock_termios.tcsetattr.assert_called_once()
327+
mock_termios.tcsetattr.assert_called()
322328
restore_call = mock_termios.tcsetattr.call_args
323-
assert restore_call[0][2] == ["old_settings"]
329+
assert restore_call[0][2] == old_attrs
324330

325331

326332
# ===========================================================================

0 commit comments

Comments
 (0)