From d4e7dd609da643af19d55b8b3162bbb152d39d5b Mon Sep 17 00:00:00 2001 From: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com> Date: Sat, 20 Jun 2026 02:12:16 +0530 Subject: [PATCH] refactor(windows): tidy managed-node resolver helpers Behavior-preserving cleanups on the managed-node resolver: - Hoist _candidate_node_command_names() out of the inner dir loop in find_hermes_node_executable (computed once, not per directory). - Drop redundant os.environ.copy() at the two with_hermes_node_path( os.environ.copy()) sites \u2014 the helper already copies os.environ when called with no argument (verified env-equivalent). - Add reciprocal keep-in-sync comments between iter_hermes_node_dirs() (hermes_constants.py) and hermesManagedNodePathEntries() (electron main.cjs), which mirror the same platform-ordering rule across the Python/Node boundary. --- apps/desktop/electron/main.cjs | 3 +++ gateway/platforms/whatsapp.py | 3 ++- hermes_cli/main.py | 3 ++- hermes_constants.py | 6 +++++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/desktop/electron/main.cjs b/apps/desktop/electron/main.cjs index 3961760bcaa..db573a1e0d2 100644 --- a/apps/desktop/electron/main.cjs +++ b/apps/desktop/electron/main.cjs @@ -270,6 +270,9 @@ function resolveHermesHome() { const HERMES_HOME = resolveHermesHome() function hermesManagedNodePathEntries() { + // NOTE: keep this ordering in sync with iter_hermes_node_dirs() in + // hermes_constants.py — this Node main process cannot import the Python + // module, so the platform-ordering rule is mirrored here. const root = path.join(HERMES_HOME, 'node') const bin = path.join(root, 'bin') const entries = IS_WINDOWS ? [root, bin] : [bin, root] diff --git a/gateway/platforms/whatsapp.py b/gateway/platforms/whatsapp.py index 9e18500c49b..d6490662684 100644 --- a/gateway/platforms/whatsapp.py +++ b/gateway/platforms/whatsapp.py @@ -492,7 +492,8 @@ class WhatsAppAdapter(WhatsAppBehaviorMixin, BasePlatformAdapter): # Build bridge subprocess environment. # Pass WHATSAPP_REPLY_PREFIX from config.yaml so the Node bridge # can use it without the user needing to set a separate env var. - bridge_env = with_hermes_node_path(os.environ.copy()) + # with_hermes_node_path() copies os.environ when called with no arg. + bridge_env = with_hermes_node_path() if self._reply_prefix is not None: bridge_env["WHATSAPP_REPLY_PREFIX"] = self._reply_prefix # Pass the profile-aware cache directories so the bridge writes diff --git a/hermes_cli/main.py b/hermes_cli/main.py index 0870d1586f3..064b69277f6 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -5407,7 +5407,8 @@ def cmd_gui(args: argparse.Namespace): from hermes_constants import find_node_executable, with_hermes_node_path - env = with_hermes_node_path(os.environ.copy()) + # with_hermes_node_path() copies os.environ when called with no arg. + env = with_hermes_node_path() if getattr(args, "fake_boot", False): env["HERMES_DESKTOP_BOOT_FAKE"] = "1" if getattr(args, "ignore_existing", False): diff --git a/hermes_constants.py b/hermes_constants.py index 48be65d2781..738d4c224cc 100644 --- a/hermes_constants.py +++ b/hermes_constants.py @@ -254,6 +254,9 @@ def iter_hermes_node_dirs(home: Path | None = None) -> list[Path]: root = home or get_hermes_home() dirs = [root / "node"] bin_dir = root / "node" / "bin" + # NOTE: keep this ordering in sync with hermesManagedNodePathEntries() in + # apps/desktop/electron/main.cjs — the Electron main process is Node and + # cannot import this module, so the platform-ordering rule is mirrored there. if sys.platform == "win32": return dirs + [bin_dir] return [bin_dir] + dirs @@ -276,8 +279,9 @@ def _candidate_node_command_names(command: str) -> list[str]: def find_hermes_node_executable(command: str) -> str | None: """Return a Hermes-managed Node/npm executable path, if installed.""" + names = _candidate_node_command_names(command) for directory in iter_hermes_node_dirs(): - for name in _candidate_node_command_names(command): + for name in names: candidate = directory / name if candidate.is_file() and ( sys.platform == "win32" or os.access(candidate, os.X_OK)