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
@@ -712,76 +712,6 @@ def test_named_custom_provider_uses_saved_credentials(monkeypatch):
assert resolved["source"] == "custom_provider:Local"
def test_bare_custom_resolves_providers_dict_entry_named_custom(monkeypatch):
"""A request for bare ``provider="custom"`` must resolve a literal
``providers.custom`` entry (e.g. a cliproxy endpoint) instead of falling
through to the global default. Regression for cron jobs stored with
``provider: "custom"`` failing with ``auth_unavailable: providers=codex``.
"""
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
monkeypatch.delenv("OPENROUTER_API_KEY", raising=False)
monkeypatch.setattr(
rp,
"load_config",
lambda: {
"providers": {
"custom": {
"api": "https://cliproxy.example.com/v1",
"api_key": "cliproxy-key",
"default_model": "gpt-5.4",
"name": "CLIProxy",
}
}
},
)
# Reaching resolve_provider for bare custom with a matching entry means the
# named-custom path was bypassed — that is the bug we are fixing.
monkeypatch.setattr(
rp,
"resolve_provider",
lambda *a, **k: (_ for _ in ()).throw(
AssertionError(
"resolve_provider must not be called; providers.custom should match"
)
),
)
resolved = rp.resolve_runtime_provider(requested="custom")
assert resolved["provider"] == "custom"
assert resolved["base_url"] == "https://cliproxy.example.com/v1"
assert resolved["api_key"] == "cliproxy-key"
assert resolved["requested_provider"] == "custom"
def test_bare_custom_without_named_entry_still_falls_through(monkeypatch):
"""No literal providers.custom entry → bare custom keeps the legacy
model.base_url trust-path behavior, unchanged by the fix."""
monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "openrouter")
monkeypatch.setattr(
rp,
"_get_model_config",
lambda: {
"provider": "openrouter",
"base_url": "http://127.0.0.1:8082/v1",
"default": "my-local-model",
},
)
monkeypatch.setattr(
rp,
"load_config",
lambda: {"providers": {"some-other-proxy": {"api": "https://x.example/v1"}}},
)
monkeypatch.delenv("CUSTOM_BASE_URL", raising=False)
monkeypatch.delenv("OPENROUTER_BASE_URL", raising=False)
monkeypatch.setenv("OPENROUTER_API_KEY", "or-key")
resolved = rp.resolve_runtime_provider(requested="custom")
assert resolved["provider"] == "custom"
assert resolved["base_url"] == "http://127.0.0.1:8082/v1"
def test_named_custom_provider_uses_providers_dict_when_list_missing(monkeypatch):
"""After v11→v12 migration deletes custom_providers, resolution should
still find entries in the providers dict via get_compatible_custom_providers."""