Set PTY size explicitly in python bridge fallback

This commit is contained in:
blamouche 2026-03-13 23:37:40 +01:00
parent 5839ab5c1b
commit 7e663cb0d7
7 changed files with 82 additions and 5 deletions

View file

@ -8,3 +8,4 @@
- Proactive terminal handshake injection can be echoed back as raw input in bridge mode; prefer startup-config mitigations over escape-sequence injection.
- If injected terminal response sequences are echoed as raw text, stop injection and instead disable Codex startup prompts/warnings via config flags to reduce hidden interactive blockers.
- For interactive Codex/TUI output in embedded xterm, avoid forcing `convertEol: true`; it can distort line rendering and split characters across lines.
- Python PTY bridge must set terminal size explicitly (`TIOCSWINSZ`) using requested cols/rows; otherwise interactive TUI output can degrade into letter-per-line rendering.

View file

@ -28,3 +28,4 @@
| 2026-03-13 20:15:06 CET | agent | Added proactive Codex startup terminal handshake (immediate + delayed retry) to address blank bridge startup, with validation and governance updates. | `main.ts`, `.prompt-hub/lessons.md`, `npm run build`, `npm test`, `.prompt-hub/todo/todo-20260313-201421-codex-proactive-handshake.md`, `.prompt-hub/version.md`, `.prompt-hub/releases.md` | success | Commit and push for user retest. |
| 2026-03-13 23:24:39 CET | agent | Removed ineffective Codex escape injection (echoed as raw input) and switched to startup prompt-suppression flags in Codex launch command; validated build/tests. | `main.ts`, `.prompt-hub/lessons.md`, `npm run build`, `npm test`, `.prompt-hub/todo/todo-20260313-232410-codex-disable-startup-prompts.md`, `.prompt-hub/version.md`, `.prompt-hub/releases.md` | success | Commit and push for user retest. |
| 2026-03-13 23:30:56 CET | agent | Fixed broken split-character Codex rendering by disabling forced xterm EOL conversion (`convertEol: false`); validated build/tests and updated governance files. | `main.ts`, `.prompt-hub/lessons.md`, `npm run build`, `npm test`, `.prompt-hub/todo/todo-20260313-233016-fix-codex-split-characters-output.md`, `.prompt-hub/version.md`, `.prompt-hub/releases.md` | success | Commit and push for user verification. |
| 2026-03-13 23:37:35 CET | agent | Fixed letter-per-line Codex output by propagating cols/rows to python PTY bridge and applying `TIOCSWINSZ` in explicit PTY fork/exec bridge path; validated compile/smoke/build/tests. | `pty-proxy.js`, `pty-bridge.py`, `.prompt-hub/lessons.md`, `python3 -m py_compile pty-bridge.py`, `python3 pty-bridge.py <payload>`, `npm run build`, `npm test`, `.prompt-hub/todo/todo-20260313-233621-fix-codex-pty-size-python-bridge.md`, `.prompt-hub/version.md`, `.prompt-hub/releases.md` | success | Commit and push for user retest. |

View file

@ -1,5 +1,9 @@
# Releases
## 0.1.12 - 2026-03-13
- Fixed python PTY fallback sizing by propagating requested cols/rows from the proxy and applying them in `pty-bridge.py` via `TIOCSWINSZ` at startup.
- Reworked python bridge execution from `pty.spawn` convenience mode to explicit PTY fork/exec streaming for deterministic terminal sizing.
## 0.1.11 - 2026-03-13
- Fixed split-character / broken newline rendering in embedded Codex output by disabling forced xterm EOL conversion (`convertEol: false`).

View file

@ -0,0 +1,19 @@
# Task: Fix Codex letter-per-line output in python PTY bridge
## Context
- Codex output appears as one character per line in embedded terminal.
- Likely PTY size negotiation issue in python fallback bridge.
## Plan
- [x] Pass cols/rows through proxy payload to python bridge.
- [x] Replace bridge spawn path with explicit PTY fork + winsize setup.
- [x] Validate build/tests and update traceability.
## Review
- Updated `pty-proxy.js` to pass normalized `cols`/`rows` into python bridge payload.
- Reworked `pty-bridge.py` to use explicit PTY fork/exec and apply terminal size (`TIOCSWINSZ`) before streaming.
- Validation:
- `python3 -m py_compile pty-bridge.py` passed.
- `python3 pty-bridge.py <payload>` smoke test passed (`OK_BRIDGE`).
- `npm run build` passed.
- `npm test` passed (`12/12` tests).

View file

@ -1 +1 @@
0.1.11
0.1.12

View file

@ -3,7 +3,11 @@ import base64
import json
import os
import pty
import fcntl
import select
import struct
import sys
import termios
def decode_payload(raw: str):
@ -21,6 +25,42 @@ def as_exit_code(status: int) -> int:
return 1
def set_winsize(fd: int, rows: int, cols: int) -> None:
packed = struct.pack("HHHH", rows, cols, 0, 0)
fcntl.ioctl(fd, termios.TIOCSWINSZ, packed)
def stream_pty(pid: int, fd: int) -> int:
stdin_open = True
while True:
readers = [fd]
if stdin_open:
readers.append(sys.stdin.fileno())
ready, _, _ = select.select(readers, [], [])
if fd in ready:
try:
chunk = os.read(fd, 4096)
except OSError:
chunk = b""
if not chunk:
break
os.write(sys.stdout.fileno(), chunk)
if stdin_open and sys.stdin.fileno() in ready:
try:
chunk = os.read(sys.stdin.fileno(), 4096)
except OSError:
chunk = b""
if not chunk:
stdin_open = False
else:
os.write(fd, chunk)
_, status = os.waitpid(pid, 0)
return as_exit_code(status)
def main() -> int:
payload = decode_payload(sys.argv[1] if len(sys.argv) > 1 else "")
@ -35,6 +75,10 @@ def main() -> int:
os.environ[str(key)] = str(value)
launches = payload.get("launches") or []
cols = int(payload.get("cols") or 120)
rows = int(payload.get("rows") or 30)
cols = max(20, cols)
rows = max(10, rows)
if not launches:
print("[py-bridge-error] no launch specs provided", file=sys.stderr)
return 1
@ -45,8 +89,12 @@ def main() -> int:
args = launch.get("args") or []
argv = [str(file)] + [str(x) for x in args]
try:
status = pty.spawn(argv)
return as_exit_code(status)
pid, fd = pty.fork()
if pid == 0:
os.execvpe(str(file), argv, os.environ)
set_winsize(fd, rows, cols)
return stream_pty(pid, fd)
except OSError as error:
failures.append(f"{file}: {error}")

View file

@ -139,7 +139,9 @@ function spawnPythonBridge(payload, launches) {
JSON.stringify({
cwd: payload.cwd,
env: payload.env,
launches
launches,
cols: payload.cols,
rows: payload.rows
}),
"utf8"
).toString("base64");
@ -185,7 +187,9 @@ async function main() {
term = spawnPythonBridge(
{
cwd: payload.cwd,
env: fallbackEnv
env: fallbackEnv,
cols: Math.max(20, Number(payload.cols) || 120),
rows: Math.max(10, Number(payload.rows) || 30)
},
launches
);