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:
@@ -452,54 +452,3 @@ class TestUnifiedCronjobTool:
|
||||
assert updated["success"] is True
|
||||
stored = get_job(created["job_id"])
|
||||
assert stored["deliver"] == "telegram"
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Per-job model/provider override resolution
|
||||
# =========================================================================
|
||||
|
||||
from tools.cronjob_tools import _resolve_model_override # noqa: E402
|
||||
|
||||
|
||||
class TestResolveModelOverride:
|
||||
"""`_resolve_model_override` must not silently hijack a job that meant to
|
||||
use a configured custom endpoint (e.g. ``providers.custom`` → cliproxy).
|
||||
Regression for cron jobs with ``provider: "custom"`` falling back to codex.
|
||||
"""
|
||||
|
||||
def test_keeps_bare_custom_when_a_named_entry_exists(self, monkeypatch):
|
||||
import hermes_cli.runtime_provider as rp_mod
|
||||
|
||||
monkeypatch.setattr(rp_mod, "has_named_custom_provider", lambda name: True)
|
||||
provider, model = _resolve_model_override(
|
||||
{"provider": "custom", "model": "gpt-5.4"}
|
||||
)
|
||||
assert provider == "custom"
|
||||
assert model == "gpt-5.4"
|
||||
|
||||
def test_pins_main_provider_when_bare_custom_unresolvable(self, monkeypatch):
|
||||
import hermes_cli.config as cfg_mod
|
||||
import hermes_cli.runtime_provider as rp_mod
|
||||
|
||||
monkeypatch.setattr(rp_mod, "has_named_custom_provider", lambda name: False)
|
||||
monkeypatch.setattr(
|
||||
cfg_mod, "load_config", lambda: {"model": {"provider": "openai-codex"}}
|
||||
)
|
||||
provider, model = _resolve_model_override(
|
||||
{"provider": "custom", "model": "gpt-5.4"}
|
||||
)
|
||||
# No matching custom entry → fall back to pinning the main provider.
|
||||
assert provider == "openai-codex"
|
||||
assert model == "gpt-5.4"
|
||||
|
||||
def test_keeps_explicit_custom_name_unchanged(self, monkeypatch):
|
||||
import hermes_cli.runtime_provider as rp_mod
|
||||
|
||||
# Even if the resolver claims no entry, the canonical "custom:<name>"
|
||||
# form is never stripped or pinned.
|
||||
monkeypatch.setattr(rp_mod, "has_named_custom_provider", lambda name: False)
|
||||
provider, model = _resolve_model_override(
|
||||
{"provider": "custom:cliproxy", "model": "gpt-5.4"}
|
||||
)
|
||||
assert provider == "custom:cliproxy"
|
||||
assert model == "gpt-5.4"
|
||||
|
||||
Reference in New Issue
Block a user