fix(cli): try bundled TUI before requiring ui-tui workspace (#67116)
_make_tui_argv() called _ensure_tui_workspace(tui_dir) unconditionally before checking for a prebuilt bundle. That function sys.exit(1)s when ui-tui/ doesn't exist, which it never does on a pip/pipx install — the wheel ships hermes_cli/tui_dist/entry.js but never ships ui-tui/ at all (that directory only exists in a git checkout). Every dashboard Chat tab connection on a pip/pipx install therefore hard-exited before ever reaching _find_bundled_tui(), surfacing as the unhelpful "Chat unavailable: 1" banner despite having a fully valid bundled entry.js on disk. Move the bundled-wheel/HERMES_TUI_DIR shortcut ahead of the workspace check. --dev is unaffected (it never uses the bundled path and still requires the workspace), and the checkout-without-bundle path is unaffected (bundled lookup returns None, falls through to the existing git-restore/npm-install/build flow). Adds a contributors/emails mapping for the original author. Fixes #56665 Co-authored-by: lucaskvasirr <lucaskvasir@duck.com>
This commit is contained in:
parent
11cb9e571f
commit
ca0703feae
3 changed files with 87 additions and 3 deletions
2
contributors/emails/lucaskvasir@duck.com
Normal file
2
contributors/emails/lucaskvasir@duck.com
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
lucaskvasirr
|
||||
# PR #56672 salvage (fix(cli): try bundled TUI before requiring ui-tui workspace; #56665)
|
||||
|
|
@ -1766,10 +1766,14 @@ def _make_tui_argv(tui_dir: Path, tui_dev: bool) -> tuple[list[str], Path]:
|
|||
)
|
||||
sys.exit(1)
|
||||
|
||||
if not ext_dir:
|
||||
_ensure_tui_workspace(tui_dir)
|
||||
|
||||
# 1. Prebuilt bundle (nix / packaged release): just run it.
|
||||
#
|
||||
# This must run BEFORE _ensure_tui_workspace() below. A pip/pipx install
|
||||
# ships hermes_cli/tui_dist/entry.js in the wheel but never ships ui-tui/
|
||||
# at all (that directory only exists in a git checkout) — so requiring
|
||||
# the workspace to exist first made every pip/pipx dashboard Chat tab
|
||||
# connection hard-exit before it ever got a chance to try the bundled
|
||||
# entry.js it already has. See #56665.
|
||||
if not tui_dev:
|
||||
if ext_dir:
|
||||
p = Path(ext_dir)
|
||||
|
|
@ -1783,6 +1787,11 @@ def _make_tui_argv(tui_dir: Path, tui_dev: bool) -> tuple[list[str], Path]:
|
|||
node = _node_bin("node")
|
||||
return [node, "--expose-gc", str(bundled)], bundled.parent
|
||||
|
||||
# No prebuilt bundle available (or --dev, which never uses one) — we're
|
||||
# about to npm install/build from source, so the workspace must exist.
|
||||
if not ext_dir:
|
||||
_ensure_tui_workspace(tui_dir)
|
||||
|
||||
# 2. Normal flow: npm install if needed, always esbuild, then node dist/entry.js.
|
||||
# --dev flow: npm install if needed, then tsx src/entry.tsx.
|
||||
# Existing desktop behaviour runs npm from the workspace root. Termux
|
||||
|
|
|
|||
|
|
@ -361,6 +361,79 @@ def test_make_tui_argv_decodes_dev_prebuild_with_utf8_replace(
|
|||
_assert_utf8_replace_capture(calls[0][1])
|
||||
|
||||
|
||||
def test_make_tui_argv_uses_bundled_tui_when_workspace_missing(
|
||||
tmp_path: Path, main_mod, monkeypatch
|
||||
) -> None:
|
||||
"""pip/pipx install regression (#56665): the wheel ships
|
||||
hermes_cli/tui_dist/entry.js but never ships ui-tui/ (that directory only
|
||||
exists in a git checkout). Before this fix, _make_tui_argv called
|
||||
_ensure_tui_workspace() unconditionally before checking for the bundled
|
||||
entry.js, so every pip/pipx dashboard Chat tab connection hard-exited
|
||||
with `sys.exit(1)` — surfaced to the user as the unhelpful "Chat
|
||||
unavailable: 1" — despite having a perfectly runnable bundled TUI on
|
||||
disk. The bundled-wheel shortcut must be tried first and succeed without
|
||||
ever touching the (missing) ui-tui workspace or git.
|
||||
"""
|
||||
monkeypatch.delenv("HERMES_TUI_DIR", raising=False)
|
||||
monkeypatch.setattr(main_mod, "_ensure_tui_node", lambda: None)
|
||||
|
||||
bundled_entry = tmp_path / "bundled" / "entry.js"
|
||||
bundled_entry.parent.mkdir(parents=True)
|
||||
bundled_entry.write_text("// bundled TUI")
|
||||
monkeypatch.setattr(main_mod, "_find_bundled_tui", lambda: bundled_entry)
|
||||
|
||||
def which(name: str) -> str | None:
|
||||
if name == "node":
|
||||
return "/usr/bin/node"
|
||||
raise AssertionError(f"unexpected shutil.which({name!r}) call — bundled path must not need npm/git")
|
||||
|
||||
monkeypatch.setattr(main_mod.shutil, "which", which)
|
||||
|
||||
def fail_run(*_args, **_kwargs):
|
||||
raise AssertionError("bundled TUI path must not spawn any subprocess (no npm install/build, no git restore)")
|
||||
|
||||
monkeypatch.setattr(main_mod.subprocess, "run", fail_run)
|
||||
|
||||
# ui-tui/ deliberately does not exist under tmp_path, and there is no
|
||||
# .git either — this mirrors a pip/pipx install exactly.
|
||||
tui_dir = tmp_path / "ui-tui"
|
||||
assert not tui_dir.exists()
|
||||
|
||||
argv, cwd = main_mod._make_tui_argv(tui_dir, tui_dev=False)
|
||||
|
||||
assert argv == ["/usr/bin/node", "--expose-gc", str(bundled_entry)]
|
||||
assert cwd == bundled_entry.parent
|
||||
|
||||
|
||||
def test_make_tui_argv_dev_mode_still_requires_workspace_even_with_bundle(
|
||||
tmp_path: Path, main_mod, monkeypatch, capsys
|
||||
) -> None:
|
||||
"""--dev never uses the prebuilt bundle (there's no source to hot-reload
|
||||
from a bundled entry.js), so it must still hit the workspace guard when
|
||||
ui-tui/ is missing — the bundled-first reordering must not weaken --dev.
|
||||
"""
|
||||
monkeypatch.delenv("HERMES_TUI_DIR", raising=False)
|
||||
monkeypatch.setattr(main_mod, "_ensure_tui_node", lambda: None)
|
||||
|
||||
bundled_entry = tmp_path / "bundled" / "entry.js"
|
||||
bundled_entry.parent.mkdir(parents=True)
|
||||
bundled_entry.write_text("// bundled TUI")
|
||||
monkeypatch.setattr(main_mod, "_find_bundled_tui", lambda: bundled_entry)
|
||||
|
||||
def which(name: str) -> str | None:
|
||||
if name == "git":
|
||||
return "/usr/bin/git"
|
||||
raise AssertionError("node/npm lookup must not run when ui-tui is missing")
|
||||
|
||||
monkeypatch.setattr(main_mod.shutil, "which", which)
|
||||
|
||||
with pytest.raises(SystemExit) as exc:
|
||||
main_mod._make_tui_argv(tmp_path / "ui-tui", tui_dev=True)
|
||||
|
||||
assert exc.value.code == 1
|
||||
assert "TUI workspace is missing" in capsys.readouterr().err
|
||||
|
||||
|
||||
def test_make_tui_argv_exits_with_recovery_hint_when_workspace_unrecoverable(
|
||||
tmp_path: Path, main_mod, monkeypatch, capsys
|
||||
) -> None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue