mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
feat: lazy bootstrap node
This commit is contained in:
parent
39b1336d1f
commit
c4b9750bc1
3 changed files with 303 additions and 2 deletions
|
|
@ -789,8 +789,63 @@ def _hermes_ink_bundle_stale(tui_dir: Path) -> bool:
|
|||
return False
|
||||
|
||||
|
||||
def _ensure_tui_node() -> None:
|
||||
"""Make sure `node` + `npm` are on PATH for the TUI.
|
||||
|
||||
If either is missing and scripts/lib/node-bootstrap.sh is available, source
|
||||
it and call `ensure_node` (fnm/nvm/proto/brew/bundled cascade). After
|
||||
install, capture the resolved node binary path from the bash subprocess
|
||||
and prepend its directory to os.environ["PATH"] so shutil.which finds the
|
||||
new binaries in this Python process — regardless of which version manager
|
||||
was used (nvm, fnm, proto, brew, or the bundled fallback).
|
||||
|
||||
Idempotent no-op when node+npm are already discoverable. Set
|
||||
``HERMES_SKIP_NODE_BOOTSTRAP=1`` to disable auto-install.
|
||||
"""
|
||||
if shutil.which("node") and shutil.which("npm"):
|
||||
return
|
||||
if os.environ.get("HERMES_SKIP_NODE_BOOTSTRAP"):
|
||||
return
|
||||
|
||||
helper = PROJECT_ROOT / "scripts" / "lib" / "node-bootstrap.sh"
|
||||
if not helper.is_file():
|
||||
return
|
||||
|
||||
hermes_home = os.environ.get("HERMES_HOME") or str(Path.home() / ".hermes")
|
||||
try:
|
||||
# Helper writes logs to stderr; we ask bash to print `command -v node`
|
||||
# on stdout once ensure_node succeeds. Subshell PATH edits don't leak
|
||||
# back into Python, so the stdout capture is the bridge.
|
||||
result = subprocess.run(
|
||||
["bash", "-c", f'source "{helper}" >&2 && ensure_node >&2 && command -v node'],
|
||||
env={**os.environ, "HERMES_HOME": hermes_home},
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
except (OSError, subprocess.SubprocessError):
|
||||
return
|
||||
|
||||
parts = os.environ.get("PATH", "").split(os.pathsep)
|
||||
extras: list[Path] = []
|
||||
|
||||
resolved = (result.stdout or "").strip()
|
||||
if resolved:
|
||||
extras.append(Path(resolved).resolve().parent)
|
||||
|
||||
extras.extend([Path(hermes_home) / "node" / "bin", Path.home() / ".local" / "bin"])
|
||||
|
||||
for extra in extras:
|
||||
s = str(extra)
|
||||
if extra.is_dir() and s not in parts:
|
||||
parts.insert(0, s)
|
||||
os.environ["PATH"] = os.pathsep.join(parts)
|
||||
|
||||
|
||||
def _make_tui_argv(tui_dir: Path, tui_dev: bool) -> tuple[list[str], Path]:
|
||||
"""Ink TUI: --dev → tsx src; else node dist (HERMES_TUI_DIR or ui-tui, build when stale)."""
|
||||
_ensure_tui_node()
|
||||
|
||||
def _node_bin(bin: str)-> str:
|
||||
path = shutil.which(bin)
|
||||
if not path:
|
||||
|
|
@ -809,7 +864,8 @@ def _make_tui_argv(tui_dir: Path, tui_dev: bool) -> tuple[list[str], Path]:
|
|||
|
||||
npm = _node_bin("npm")
|
||||
if _tui_need_npm_install(tui_dir):
|
||||
print("Installing TUI dependencies…")
|
||||
if not os.environ.get("HERMES_QUIET"):
|
||||
print("Installing TUI dependencies…")
|
||||
result = subprocess.run(
|
||||
[npm, "install", "--silent", "--no-fund", "--no-audit", "--progress=false"],
|
||||
cwd=str(tui_dir),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue