diff --git a/hermes_cli/web_server.py b/hermes_cli/web_server.py index 10fec26c5c..5005f4bf5d 100644 --- a/hermes_cli/web_server.py +++ b/hermes_cli/web_server.py @@ -3284,6 +3284,10 @@ def _resolve_chat_argv( Appending ``--resume `` to argv doesn't work because ``ui-tui`` does not parse its argv. + ``HERMES_TUI_GATEWAY_URL`` is injected so the PTY child can attach to + this process's in-memory ``tui_gateway`` instance instead of spawning + its own Python gateway subprocess. + `sidecar_url` (when set) is forwarded as ``HERMES_TUI_SIDECAR_URL`` so the spawned ``tui_gateway.entry`` can mirror dispatcher emits to the dashboard's ``/api/pub`` endpoint (see :func:`pub_ws`). @@ -3310,9 +3314,26 @@ def _resolve_chat_argv( if sidecar_url: env["HERMES_TUI_SIDECAR_URL"] = sidecar_url + if gateway_ws_url := _build_gateway_ws_url(): + env["HERMES_TUI_GATEWAY_URL"] = gateway_ws_url + return list(argv), str(cwd) if cwd else None, env +def _build_gateway_ws_url() -> Optional[str]: + """ws:// URL the PTY child should attach to for JSON-RPC gateway traffic.""" + host = getattr(app.state, "bound_host", None) + port = getattr(app.state, "bound_port", None) + + if not host or not port: + return None + + netloc = f"[{host}]:{port}" if ":" in host and not host.startswith("[") else f"{host}:{port}" + qs = urllib.parse.urlencode({"token": _SESSION_TOKEN}) + + return f"ws://{netloc}/api/ws?{qs}" + + def _build_sidecar_url(channel: str) -> Optional[str]: """ws:// URL the PTY child should publish events to, or None when unbound.""" host = getattr(app.state, "bound_host", None) diff --git a/tests/hermes_cli/test_web_server.py b/tests/hermes_cli/test_web_server.py index 2a6b0e1b4f..18d96459e6 100644 --- a/tests/hermes_cli/test_web_server.py +++ b/tests/hermes_cli/test_web_server.py @@ -2257,3 +2257,23 @@ class TestPtyWebSocket: ): pass assert exc.value.code == 4400 + + +def test_resolve_chat_argv_injects_gateway_ws_url(monkeypatch): + import hermes_cli.main as cli_main + import hermes_cli.web_server as ws + + monkeypatch.setattr( + cli_main, + "_make_tui_argv", + lambda *_args, **_kwargs: (["node", "fake-tui.js"], Path("/tmp")), + ) + monkeypatch.setattr(ws.app.state, "bound_host", "127.0.0.1", raising=False) + monkeypatch.setattr(ws.app.state, "bound_port", 9119, raising=False) + + _argv, _cwd, env = ws._resolve_chat_argv() + + assert env is not None + gateway_url = env.get("HERMES_TUI_GATEWAY_URL", "") + assert gateway_url.startswith("ws://127.0.0.1:9119/api/ws?") + assert "token=" in gateway_url