mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 05:17:15 +00:00
In the process tree that the Store app's execution alias starts, a program whose own executable is outside WindowsApps cannot start one inside it. cmd.exe gets "Access is denied" from the packaged node.exe, and npm starts every package bin through cmd.exe. So every npx MCP server died, and so did each `npm exec` in the terminal on a machine without its own Node. A venv's Scripts\python.exe redirector exits 101 when it starts the packaged interpreter, which broke `python -m venv`. A copy of the same files in the writable store starts without error. PM already makes one for Python builds (#135236). That code moves from pm._uv._toolchain to pm.user_facing.writable_copy. pm.user_facing.copies swaps each packaged npm, node and python dir for that copy, made on first use from the verified entry: a copy plus a digest check, no download. Two places use it: - the terminal's fallback dirs (user_facing_path), behind the user's own tools; - Hermes's npx/npm/node for a bare `command: npx` MCP server, which stays Hermes's own as on main. When lazy installs are off, PM makes no copy and the packaged dir stays. Live check on Windows 11 arm64 (Store build 2026.6819.1065.0), in the alias-started process tree: cmd.exe -> npx.cmd -> node.exe fails with "Access is denied" from the package, and passes from a copy of the same node and npm dirs in the user profile.
51 lines
2.2 KiB
Python
51 lines
2.2 KiB
Python
"""Pinned tool acquisition for PM's private Python engine; never worker API."""
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from pm.filesystem import native
|
|
from pm.package import InstallError
|
|
from pm.registry import get_package
|
|
from pm.store import current_target
|
|
|
|
|
|
def _toolchain(*, realize: bool = True, explicit: bool = False) -> tuple[Path, Path] | None:
|
|
"""Resolve uv and Python without host discovery or recursive worker dispatch.
|
|
|
|
A read-only probe never installs. A sealed Windows payload builds on a verified writable
|
|
copy of its Python: uv starts a venv's ``Scripts\\python.exe`` to query it, and that
|
|
redirector runs outside the package, so its target must too. A redirector naming the
|
|
packaged interpreter dies with exit 101 (#135236). Hermes itself still enters those venvs
|
|
on the packaged interpreter (``pm.environments.venv_command``).
|
|
"""
|
|
from pm.install import _installed_location, _lockfile, ensure, sealed
|
|
from pm.user_facing import writable_copy
|
|
|
|
if realize:
|
|
ensure("uv", explicit=explicit)
|
|
|
|
lockfile = _lockfile()
|
|
target = current_target()
|
|
binaries = {}
|
|
for name in ("uv", "python"):
|
|
package = get_package(name)
|
|
location = _installed_location(package, lockfile, target)
|
|
if location is None:
|
|
if not realize:
|
|
return None
|
|
raise InstallError(name, "pinned tool is unavailable", "run `hermes pm install`")
|
|
if name == "python" and target.startswith("win32") and sealed():
|
|
location = writable_copy(name, realize=realize, explicit=explicit,
|
|
why="a writable Python is required for bundled builds")
|
|
if location is None:
|
|
return None
|
|
facts, store = location
|
|
binary = package.binary(store.entry(facts.get(name)["entry"]), target)
|
|
if binary is None or not binary.is_file():
|
|
if not realize:
|
|
return None
|
|
raise InstallError(name, "installed binary is missing", "run `hermes pm install`")
|
|
binaries[name] = binary
|
|
# Callers execute and compare these outside PM's own file calls: the ordinary spelling, not the store's.
|
|
return Path(native(binaries["uv"])), Path(native(binaries["python"]))
|