Files
hermes-agent/hermes_platform/resolver/known_dirs.py
Siddharth Balyan 3aaae8c537 Plugins: apps are found wherever they are installed on Windows, macOS and Linux (#134050)
* feat(platform): app declarations list every place an app may be installed

An app block's location may now be one path or an ordered list. A list
item is a path, where a `*` segment stands for a versioned folder and the
highest version is tried first, or a mapping naming a location kind:
command (PATH), uninstall_registry (InstallLocation plus a file, Windows),
app_bundle (/Applications, ~/Applications), flatpak (app id) and snap.
The directories behind each kind live in known_dirs.

Each kind's OS, key, presence and locator are one LOCATION_KINDS entry in
resolver/app.py. The declaration parser validates against that table, so
a new kind is one entry instead of three coordinated edits in two modules.

With requires.min_version, availability reads the version of every
present copy and returns the first that qualifies, so an old copy found
first does not hide a newer one; if none qualifies it reports the highest
readable version. A Linux block may omit a version source (Linux has
none) and is then gated on presence only; Windows and macOS blocks still
need one, and a Linux-only declaration cannot set a minimum.

Brought over from #129246. The Blender 0.2.0 catalog pin stays out: older
clients reject a list location, so it follows a release that ships this.

* fix(platform): bad app declarations and vanished registry entries no longer break app lookup (review)

A location with a non-string kind raises DeclarationError instead of TypeError.
A location's file and key value are checked after stripping, so a leading / or \ or a drive cannot escape the install folder.
An uninstall registry entry that cannot be opened or read is skipped instead of ending the scan of its root.
2026-10-07 00:58:22 +05:30

57 lines
1.9 KiB
Python

"""Directories where tools install outside PATH, grouped by the ecosystem that owns them.
Every table in Hermes lives here. A directory literal outside this module fails the ratchet in
`tests/test_managed_runtime_resolution.py`. Each table is empty on an OS where the ecosystem
does not install there, so callers compose tables without OS branches.
"""
from __future__ import annotations
import sys
_POSIX = sys.platform != "win32"
_WIN = sys.platform == "win32"
def homebrew_dirs() -> tuple[str, ...]:
return ("/opt/homebrew/bin", "/usr/local/bin") if sys.platform == "darwin" else ()
def user_local_bin() -> tuple[str, ...]:
return ("~/.local/bin",) if _POSIX else ("%USERPROFILE%/.local/bin",)
def rust_tool_dirs() -> tuple[str, ...]:
return ("~/.cargo/bin",) if _POSIX else ("%USERPROFILE%/.cargo/bin",)
def node_tool_dirs() -> tuple[str, ...]:
return ("~/.npm-global/bin", "~/.bun/bin", "~/.volta/bin") if _POSIX else ("%APPDATA%/npm", "%USERPROFILE%/.bun/bin", "%LOCALAPPDATA%/Volta/bin")
def hermes_vendored_dirs() -> tuple[str, ...]:
return ("~/.hermes/bin",) if _POSIX else ("%USERPROFILE%/.hermes/bin",)
def windows_user_program_dirs() -> tuple[str, ...]:
if not _WIN:
return ()
return (
"%LOCALAPPDATA%/Programs",
"%USERPROFILE%/scoop/shims",
"%LOCALAPPDATA%/Microsoft/WinGet/Links",
)
def mac_application_dirs() -> tuple[str, ...]:
return ("/Applications", "~/Applications") if sys.platform == "darwin" else ()
def flatpak_export_dirs() -> tuple[str, ...]:
"""Where flatpak links each installed app's launcher, named by its app id (system, then per-user)."""
return ("/var/lib/flatpak/exports/bin", "~/.local/share/flatpak/exports/bin") if sys.platform.startswith("linux") else ()
def snap_bin_dirs() -> tuple[str, ...]:
return ("/snap/bin",) if sys.platform.startswith("linux") else ()