fix(terminal): stop heartbeat pong leaking into the PTY as input
The WebSocket control-frame heuristic only recognized resize/ack/reset,
so the frontend's heartbeat reply {"type":"pong"} fell through to
write_input() and was typed into the shell / pi every ~30s. That garbage
corrupted the foreground app: stray text in the input line, rerenders,
and scroll-position resets (visible on resize/scroll redraws).
Treat any text frame that parses to a JSON object carrying a "type" field
as control traffic that must NEVER reach the PTY: handle known types and
ignore unknown ones. Keystrokes, bracketed-paste content, and plain text
are still forwarded as raw input.
This commit is contained in:
@@ -334,22 +334,20 @@ async def _write_loop(session_ref: SessionRef, websocket, instance_id: str) -> N
|
|||||||
await session.write_input(message["bytes"])
|
await session.write_input(message["bytes"])
|
||||||
elif "text" in message:
|
elif "text" in message:
|
||||||
text = message["text"]
|
text = message["text"]
|
||||||
# Treat a text frame as a control message only when it
|
# A text frame that parses to a JSON object with a
|
||||||
# is a JSON object carrying a known "type". Anything
|
# "type" field is a control message and must NEVER be
|
||||||
# else — including JSON-shaped pastes — is forwarded as
|
# written to the PTY (e.g. the heartbeat {"type":"pong"}
|
||||||
# raw terminal input so multiline and bracketed-paste
|
# must be consumed, not typed into the shell/pi). Handle
|
||||||
# content is never silently swallowed or misrouted.
|
# known types and ignore unknown ones. Everything else
|
||||||
|
# (keystrokes, bracketed-paste content, plain text) is
|
||||||
|
# forwarded as raw terminal input.
|
||||||
ctrl = None
|
ctrl = None
|
||||||
if text.startswith("{"):
|
if text.startswith("{"):
|
||||||
try:
|
try:
|
||||||
parsed = json.loads(text)
|
parsed = json.loads(text)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
parsed = None
|
parsed = None
|
||||||
if isinstance(parsed, dict) and parsed.get("type") in (
|
if isinstance(parsed, dict) and "type" in parsed:
|
||||||
"resize",
|
|
||||||
"ack",
|
|
||||||
"reset",
|
|
||||||
):
|
|
||||||
ctrl = parsed
|
ctrl = parsed
|
||||||
|
|
||||||
if ctrl is None:
|
if ctrl is None:
|
||||||
|
|||||||
Reference in New Issue
Block a user