opentui(phase3): launcher integration — HERMES_TUI_ENGINE dual-engine

hermes --tui launches the native OpenTUI engine (Bun) when
HERMES_TUI_ENGINE=opentui (env) or display.tui_engine=opentui (config);
Ink stays the default and the shipping path is untouched.

- _resolve_tui_engine() (env > config > ink); refuses opentui on
  Windows/Termux (no Bun) -> falls back to ink with a notice.
- _make_opentui_argv() -> [bun, src/entry.real.tsx] (no build step).
- _bun_bin() with HERMES_BUN override.
- Branch at top of _make_tui_argv BEFORE _ensure_tui_node (Bun-only host
  must not bootstrap Node).
- Gate _launch_tui NODE_OPTIONS/--max-old-space-size on engine==ink (Bun
  is JSC; the V8 flag errors/ignores).

Verified end-to-end via tmux: real hermes --tui -> Bun -> OpenTUI ->
real Python gateway streamed a real reply. No-flag default still ink.
This commit is contained in:
alt-glitch
2026-06-08 11:11:54 +00:00
parent 24f74eb888
commit 2bd9c9b881
741 changed files with 17733 additions and 79889 deletions
-108
View File
@@ -799,111 +799,3 @@ def test_s6_is_running_parses_svstat(
return _sp.CompletedProcess(cmd, 0, "", "")
monkeypatch.setattr("subprocess.run", _svstat_down)
assert S6ServiceManager(scandir=s6_scandir).is_running("gateway-coder") is False
# ---------------------------------------------------------------------------
# S6 stop writes a planned-stop marker (issue #42675)
#
# `hermes gateway stop` inside a container dispatches through
# S6ServiceManager.stop() -> `s6-svc -d`, which SIGTERMs the gateway.
# That SIGTERM is indistinguishable from the one s6/Docker sends on a
# container restart unless we mark the intentional stop first. Without
# the marker, the gateway's shutdown handler can't tell an operator
# stop from a restart kill, and the gateway_state=stopped suppression
# (run.py) would never engage for explicit stops.
# ---------------------------------------------------------------------------
def test_s6_supervised_pid_parses_svstat(monkeypatch, s6_scandir):
"""_supervised_pid extracts the PID from `up (pid NNNN) ...`."""
import subprocess as _sp
def _fake(cmd, **kw):
return _sp.CompletedProcess(cmd, 0, "up (pid 4242) 17 seconds\n", "")
monkeypatch.setattr("subprocess.run", _fake)
mgr = S6ServiceManager(scandir=s6_scandir)
assert mgr._supervised_pid("gateway-coder") == 4242
def test_s6_supervised_pid_none_when_down(monkeypatch, s6_scandir):
"""A down service (`s6-svstat` rc!=0 or no pid) yields None."""
import subprocess as _sp
def _fake(cmd, **kw):
return _sp.CompletedProcess(cmd, 0, "down (exitcode 0) 3 seconds\n", "")
monkeypatch.setattr("subprocess.run", _fake)
mgr = S6ServiceManager(scandir=s6_scandir)
assert mgr._supervised_pid("gateway-coder") is None
def test_s6_stop_writes_planned_stop_marker(monkeypatch, s6_scandir):
"""stop() must mark the supervised PID before `s6-svc -d` so the
gateway recognises the SIGTERM as an intentional stop (#42675)."""
import subprocess as _sp
svc_dir = s6_scandir / "gateway-coder"
svc_dir.mkdir() # so _run_svc doesn't raise GatewayNotRegisteredError
svc_calls: list[list[str]] = []
def _fake(cmd, **kw):
seq = list(cmd) if isinstance(cmd, (list, tuple)) else [str(cmd)]
if seq and seq[0].startswith("/command/"):
seq[0] = seq[0][len("/command/"):]
svc_calls.append(seq)
if seq and seq[0] == "s6-svstat":
return _sp.CompletedProcess(cmd, 0, "up (pid 9090) 5 seconds\n", "")
return _sp.CompletedProcess(cmd, 0, "", "")
monkeypatch.setattr("subprocess.run", _fake)
marked: list[int] = []
monkeypatch.setattr(
"gateway.status.write_planned_stop_marker",
lambda pid: marked.append(pid) or True,
)
mgr = S6ServiceManager(scandir=s6_scandir)
mgr.stop("gateway-coder")
assert marked == [9090], (
f"stop() must write the planned-stop marker for the supervised PID; "
f"marked={marked}"
)
# And it must still issue the down command.
assert any(
cmd[0] == "s6-svc" and "-d" in cmd for cmd in svc_calls
), f"s6-svc -d not invoked; saw: {svc_calls}"
def test_s6_stop_tolerates_marker_write_failure(monkeypatch, s6_scandir):
"""A marker-write failure must not block the stop (best-effort)."""
import subprocess as _sp
svc_dir = s6_scandir / "gateway-coder"
svc_dir.mkdir()
svc_calls: list[list[str]] = []
def _fake(cmd, **kw):
seq = list(cmd) if isinstance(cmd, (list, tuple)) else [str(cmd)]
if seq and seq[0].startswith("/command/"):
seq[0] = seq[0][len("/command/"):]
svc_calls.append(seq)
if seq and seq[0] == "s6-svstat":
return _sp.CompletedProcess(cmd, 0, "up (pid 9090) 5 seconds\n", "")
return _sp.CompletedProcess(cmd, 0, "", "")
monkeypatch.setattr("subprocess.run", _fake)
def _boom(pid):
raise OSError("disk full")
monkeypatch.setattr("gateway.status.write_planned_stop_marker", _boom)
mgr = S6ServiceManager(scandir=s6_scandir)
mgr.stop("gateway-coder") # must not raise
assert any(cmd[0] == "s6-svc" and "-d" in cmd for cmd in svc_calls)