Skip to content

Commit ec8286b

Browse files
committed
fix: re-enable OPOST after setraw to convert bare \n to \r\n like xterm.js convertEol
1 parent 31d41ae commit ec8286b

1 file changed

Lines changed: 22 additions & 14 deletions

File tree

centml/cli/shell.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,14 @@ async def _interactive_session(ws_url, token):
154154
old_settings = termios.tcgetattr(fd)
155155
try:
156156
tty.setraw(fd)
157+
# Re-enable output post-processing so the local terminal converts
158+
# bare \n to \r\n. The remote PTY may send lone \n (Kubernetes
159+
# exec PTY behavior). xterm.js handles this with convertEol;
160+
# for a raw CLI terminal we need OPOST. Extra \r before \n from
161+
# a remote that already sends \r\n is harmless.
162+
attrs = termios.tcgetattr(fd)
163+
attrs[1] |= termios.OPOST
164+
termios.tcsetattr(fd, termios.TCSANOW, attrs)
157165
rows, cols = shutil.get_terminal_size()
158166

159167
headers = {"Authorization": f"Bearer {token}"}
@@ -173,26 +181,26 @@ def _send_resize():
173181
)
174182

175183
async def _force_initial_redraw():
176-
"""Set PTY dimensions from inside the shell and clear screen.
177-
178-
The WebSocket resize message may not reliably update the
179-
remote PTY size (race with shell startup, ArgoCD buffering).
180-
Sending ``stty rows R cols C`` directly through stdin is
181-
authoritative -- it always works because the shell itself
182-
calls the TIOCSWINSZ ioctl. The leading space keeps the
183-
command out of bash history when HISTCONTROL=ignorespace.
184+
"""Toggle PTY width to force SIGWINCH, then Ctrl+L to redraw.
185+
186+
The initial resize may arrive before the remote shell
187+
starts, so the shell never sees a SIGWINCH. Toggling
188+
cols by +1/-1 guarantees a size change and SIGWINCH.
189+
Ctrl+L then clears the screen so the prompt redraws at
190+
the correct width.
184191
"""
185192
try:
186193
await asyncio.sleep(0.5)
187194
r, c = shutil.get_terminal_size()
188195
await ws.send(
189-
json.dumps(
190-
{
191-
"operation": "stdin",
192-
"data": f" stty rows {r} cols {c}; clear\n",
193-
}
194-
)
196+
json.dumps({"operation": "resize", "rows": r, "cols": c + 1})
197+
)
198+
await asyncio.sleep(0.05)
199+
await ws.send(
200+
json.dumps({"operation": "resize", "rows": r, "cols": c})
195201
)
202+
await asyncio.sleep(0.1)
203+
await ws.send(json.dumps({"operation": "stdin", "data": "\x0c"}))
196204
except Exception:
197205
pass
198206

0 commit comments

Comments
 (0)