diff --git a/hermes_cli/main.py b/hermes_cli/main.py index be7aac3744..a0ab6894b9 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -826,14 +826,26 @@ def _tui_need_npm_install(root: Path) -> bool: return lock.stat().st_mtime > marker.stat().st_mtime +def _tui_ink_bundle_exists(root: Path) -> bool: + return (root / "node_modules" / "@hermes" / "ink" / "dist" / "ink-bundle.js").is_file() + + +def _tui_runtime_ready(root: Path) -> bool: + return ( + (root / "dist" / "entry.js").exists() + and not _tui_need_npm_install(root) + and _tui_ink_bundle_exists(root) + ) + + def _find_bundled_tui(tui_dir: Path) -> Optional[Path]: """Directory whose dist/entry.js we should run: HERMES_TUI_DIR first, else repo ui-tui.""" env = os.environ.get("HERMES_TUI_DIR") if env: p = Path(env) - if (p / "dist" / "entry.js").exists() and not _tui_need_npm_install(p): + if _tui_runtime_ready(p): return p - if (tui_dir / "dist" / "entry.js").exists() and not _tui_need_npm_install(tui_dir): + if _tui_runtime_ready(tui_dir): return tui_dir return None @@ -842,6 +854,8 @@ def _tui_build_needed(tui_dir: Path) -> bool: entry = tui_dir / "dist" / "entry.js" if not entry.exists(): return True + if _hermes_ink_bundle_stale(tui_dir): + return True dist_m = entry.stat().st_mtime skip = frozenset({"node_modules", "dist"}) for dirpath, dirnames, filenames in os.walk(tui_dir, topdown=True): @@ -958,7 +972,7 @@ def _make_tui_argv(tui_dir: Path, tui_dev: bool) -> tuple[list[str], Path]: ext_dir = os.environ.get("HERMES_TUI_DIR") if ext_dir: p = Path(ext_dir) - if (p / "dist" / "entry.js").exists() and not _tui_need_npm_install(p): + if _tui_runtime_ready(p): node = _node_bin("node") return [node, str(p / "dist" / "entry.js")], p diff --git a/tests/hermes_cli/test_tui_npm_install.py b/tests/hermes_cli/test_tui_npm_install.py index 3f3191ccf3..af75891cea 100644 --- a/tests/hermes_cli/test_tui_npm_install.py +++ b/tests/hermes_cli/test_tui_npm_install.py @@ -19,6 +19,18 @@ def _touch_ink(root: Path) -> None: ink.write_text("{}") +def _touch_entry(root: Path) -> None: + entry = root / "dist" / "entry.js" + entry.parent.mkdir(parents=True, exist_ok=True) + entry.write_text("") + + +def _touch_bundle(root: Path) -> None: + bundle = root / "node_modules" / "@hermes" / "ink" / "dist" / "ink-bundle.js" + bundle.parent.mkdir(parents=True, exist_ok=True) + bundle.write_text("") + + def test_need_install_when_ink_missing(tmp_path: Path, main_mod) -> None: (tmp_path / "package-lock.json").write_text("{}") assert main_mod._tui_need_npm_install(tmp_path) is True @@ -51,3 +63,17 @@ def test_need_install_when_marker_missing(tmp_path: Path, main_mod) -> None: def test_no_install_without_lockfile_when_ink_present(tmp_path: Path, main_mod) -> None: _touch_ink(tmp_path) assert main_mod._tui_need_npm_install(tmp_path) is False + + +def test_build_needed_when_ink_bundle_missing(tmp_path: Path, main_mod) -> None: + _touch_entry(tmp_path) + assert main_mod._tui_build_needed(tmp_path) is True + + +def test_find_bundled_tui_requires_ink_bundle(tmp_path: Path, main_mod) -> None: + _touch_entry(tmp_path) + _touch_ink(tmp_path) + assert main_mod._find_bundled_tui(tmp_path) is None + + _touch_bundle(tmp_path) + assert main_mod._find_bundled_tui(tmp_path) == tmp_path