fix(serve): widen fd-1 sentinel write to BACKEND_PORT_IN_USE sibling site

The same stdout redirect that rerouted the READY sentinel (#96282) also
reroutes the machine-parsed BACKEND_PORT_IN_USE sentinel printed by
_report_port_in_use() — both preflight and probe-to-bind-race callers run
after tui_gateway.server's sys.stdout=sys.stderr swap. Extract the fd-1
write into _write_machine_sentinel_line() and use it at both sentinel
sites; human-facing hint lines stay on print().
This commit is contained in:
kshitijk4poor
2026-08-27 16:56:32 +05:30
committed by kshitij
parent 3516438832
commit 42e1aa39fc
+21 -7
View File
@@ -19515,9 +19515,27 @@ def _port_bind_conflict(host: str, port: int) -> bool:
return False
def _write_machine_sentinel_line(line: str) -> None:
"""Write a machine-parsed sentinel line to the REAL stdout (fd 1).
The serve startup path imports ``tui_gateway.server`` (flush-on-SIGTERM
handlers, #94724) which redirects ``sys.stdout`` to ``sys.stderr`` at
import time to keep stray prints off the JSON-RPC protocol stream. Any
machine-readable sentinel printed after that import via ``print()`` lands
on stderr — invisible to consumers that parse the child's stdout pipe
(the Desktop spawn, scripts). fd 1 is untouched by the Python-level
redirect, so write there; fall back to ``print`` for exotic environments
where fd 1 isn't writable (e.g. closed).
"""
try:
os.write(1, (line + "\n").encode())
except OSError:
print(line, flush=True)
def _report_port_in_use(host: str, port: int) -> None:
"""Print the machine sentinel + a human hint naming likely holders."""
print(_PORT_IN_USE_SENTINEL.format(port=port), flush=True)
_write_machine_sentinel_line(_PORT_IN_USE_SENTINEL.format(port=port))
print(
f" Port {port} on {host} is already in use — likely another "
"'hermes serve' / 'hermes dashboard' backend or the Hermes gateway. "
@@ -19894,12 +19912,8 @@ def start_server(
# still the real stdout — and the Desktop spawn watches
# child.stdout for this sentinel — so write to the fd, not to the
# (redirected) sys.stdout, or the desktop times out after 90s
# against a perfectly healthy backend.
_ready_line = f"{ready_token} port={actual_port}\n"
try:
os.write(1, _ready_line.encode())
except OSError:
print(_ready_line, end="", flush=True)
# against a perfectly healthy backend (#96282).
_write_machine_sentinel_line(f"{ready_token} port={actual_port}")
if headless:
# No SPA, and the JSON-RPC/WS endpoints are auth-gated — don't
# advertise a paste-and-connect URL, just announce the bind.