fix: route stray HERMES_HOME hardcodes through get_hermes_home() (profile + native-Windows safety)

This commit is contained in:
teknium1 2026-07-29 08:57:48 -07:00 committed by Teknium
parent b8ceba97ed
commit 19055492aa
12 changed files with 50 additions and 58 deletions

View File

@ -124,10 +124,9 @@ def _is_windows() -> bool:
def hermes_lsp_bin_dir() -> Path:
"""Return the Hermes-owned bin staging dir for LSP servers."""
home = os.environ.get("HERMES_HOME")
if home is None:
home = os.path.join(os.path.expanduser("~"), ".hermes")
p = Path(home) / "lsp" / "bin"
from hermes_constants import get_hermes_home
p = get_hermes_home() / "lsp" / "bin"
p.mkdir(parents=True, exist_ok=True)
return p

View File

@ -710,9 +710,9 @@ def _find_pses_bundle(ctx: ServerContext) -> Optional[str]:
env_path = os.environ.get("PSES_BUNDLE_PATH")
if env_path:
candidates.append(env_path)
home = os.environ.get("HERMES_HOME") or os.path.join(
os.path.expanduser("~"), ".hermes"
)
from hermes_constants import get_hermes_home
home = str(get_hermes_home())
candidates.append(os.path.join(home, "lsp", "PowerShellEditorServices"))
for cand in candidates:
@ -796,9 +796,9 @@ def _spawn_powershell_es(root: str, ctx: ServerContext) -> Optional[SpawnSpec]:
def hermes_lsp_session_dir() -> str:
"""Return (and create) the dir for PSES session/log scratch files."""
home = os.environ.get("HERMES_HOME") or os.path.join(
os.path.expanduser("~"), ".hermes"
)
from hermes_constants import get_hermes_home
home = str(get_hermes_home())
d = os.path.join(home, "lsp", "pses")
os.makedirs(d, exist_ok=True)
return d

View File

@ -82,7 +82,9 @@ def resolve_cache_home(home_path: Optional[Path] = None) -> Path:
(and tests that don't thread a home through) working.
"""
if home_path is None:
home_path = Path(os.getenv("HERMES_HOME", Path.home() / ".hermes"))
from hermes_constants import get_hermes_home
home_path = get_hermes_home()
return home_path

View File

@ -8046,7 +8046,7 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
except (RuntimeError, ValueError, OSError):
try:
_fh_log_dir = getattr(self.config, "log_dir", None) or os.path.join(
os.environ.get("HERMES_HOME", str(Path.home() / ".hermes")),
str(get_hermes_home()),
"logs",
)
os.makedirs(_fh_log_dir, exist_ok=True)
@ -8065,7 +8065,7 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
if _sigusr2 is not None and hasattr(faulthandler, "register"):
try:
_log_dir = getattr(self.config, "log_dir", None) or os.path.join(
os.environ.get("HERMES_HOME", str(Path.home() / ".hermes")),
str(get_hermes_home()),
"logs",
)
_faulthandler_path = os.path.join(_log_dir, "gateway_faulthandler.log")

View File

@ -6221,7 +6221,9 @@ def warn_deprecated_cwd_env_vars(config: Optional[Dict[str, Any]] = None) -> Non
f"this is deprecated."
)
if lines:
hint_path = os.environ.get("HERMES_HOME", "~/.hermes")
from hermes_constants import display_hermes_home
hint_path = display_hermes_home()
lines.insert(0, "\033[33m⚠ Deprecated .env settings detected:\033[0m")
lines.append(
" \033[2mMove to config.yaml instead: "

View File

@ -57,14 +57,15 @@ class AuditEvent(enum.Enum):
def _resolve_log_path() -> Path:
"""``$HERMES_HOME/logs/dashboard-auth.log`` with the standard fallback.
"""``$HERMES_HOME/logs/dashboard-auth.log``.
Mirrors ``hermes_constants.get_hermes_home`` semantics: env var wins,
else ``~/.hermes``. A local copy avoids an import cycle with the
middleware which lives below ``hermes_cli``.
Uses ``hermes_constants.get_hermes_home()`` (a leaf module no import
cycle) so profile overrides and the native-Windows ``%LOCALAPPDATA%``
fallback are honored.
"""
home = os.environ.get("HERMES_HOME") or str(Path.home() / ".hermes")
return Path(home) / "logs" / "dashboard-auth.log"
from hermes_constants import get_hermes_home
return get_hermes_home() / "logs" / "dashboard-auth.log"
def audit_log(event: AuditEvent, **fields: Any) -> None:

View File

@ -1818,7 +1818,9 @@ def _ensure_tui_node() -> None:
if not helper.is_file():
return
hermes_home = os.environ.get("HERMES_HOME") or str(Path.home() / ".hermes")
from hermes_constants import get_hermes_home
hermes_home = str(get_hermes_home())
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

View File

@ -168,9 +168,12 @@ def _path_is_mounted(path: Path) -> bool:
def _container_no_volume_mount(hermes_home: Optional[Path]) -> Optional[str]:
if not _in_container():
return None
home = hermes_home or Path(
os.environ.get("HERMES_HOME", os.path.expanduser("~/.hermes"))
)
if hermes_home is not None:
home = hermes_home
else:
from hermes_constants import get_hermes_home
home = get_hermes_home()
try:
if _path_is_mounted(home):
return None

View File

@ -256,12 +256,9 @@ def slack_manifest_command(args) -> int:
if write_target is not None:
if isinstance(write_target, bool) and write_target:
# --write with no value → default location
try:
from hermes_constants import get_hermes_home
from hermes_constants import get_hermes_home
target = Path(get_hermes_home()) / "slack-manifest.json"
except Exception:
target = Path(os.environ.get("HERMES_HOME") or str(Path.home() / ".hermes")) / "slack-manifest.json"
target = Path(get_hermes_home()) / "slack-manifest.json"
else:
target = Path(write_target).expanduser()
target.parent.mkdir(parents=True, exist_ok=True)

View File

@ -62,11 +62,9 @@ except ImportError:
def _get_sessions_dir() -> Path:
"""Return the sessions directory using HERMES_HOME."""
try:
from hermes_constants import get_hermes_home
return get_hermes_home() / "sessions"
except ImportError:
return Path(os.environ.get("HERMES_HOME", Path.home() / ".hermes")) / "sessions"
from hermes_constants import get_hermes_home
return get_hermes_home() / "sessions"
def _get_session_db():
@ -194,13 +192,9 @@ def _load_sessions_index_from_json() -> dict:
def _load_channel_directory() -> dict:
"""Load the cached channel directory for available targets."""
try:
from hermes_constants import get_hermes_home
directory_file = get_hermes_home() / "channel_directory.json"
except ImportError:
directory_file = Path(
os.environ.get("HERMES_HOME", Path.home() / ".hermes")
) / "channel_directory.json"
from hermes_constants import get_hermes_home
directory_file = get_hermes_home() / "channel_directory.json"
if not directory_file.exists():
return {}
@ -450,11 +444,9 @@ class EventBridge:
eliminating the old dual-file (sessions.json + state.db) race that
could drop brand-new conversations (#8925).
"""
try:
from hermes_constants import get_hermes_home
db_file = get_hermes_home() / "state.db"
except ImportError:
db_file = Path(os.environ.get("HERMES_HOME", Path.home() / ".hermes")) / "state.db"
from hermes_constants import get_hermes_home
db_file = get_hermes_home() / "state.db"
try:
db_mtime = db_file.stat().st_mtime if db_file.exists() else 0.0

View File

@ -117,13 +117,9 @@ def _resolve_spill_dir(directory_override: Optional[str], session_id: Optional[s
if directory_override:
base = Path(os.path.expanduser(directory_override))
else:
try:
from hermes_constants import get_hermes_home
base = Path(get_hermes_home()) / "hook_outputs"
except Exception:
# Last-resort fallback: HERMES_HOME env var, then ~/.hermes
home = os.environ.get("HERMES_HOME") or os.path.expanduser("~/.hermes")
base = Path(home) / "hook_outputs"
from hermes_constants import get_hermes_home
base = Path(get_hermes_home()) / "hook_outputs"
# Group by session so spills are contained per conversation.
session_segment = session_id or "no-session"

View File

@ -137,11 +137,9 @@ def _get_token_dir(hermes_home: str | Path | None = None) -> Path:
Uses HERMES_HOME so each profile gets its own OAuth tokens.
Layout: ``HERMES_HOME/mcp-tokens/``
"""
try:
from hermes_constants import get_hermes_home
base = Path(hermes_home) if hermes_home is not None else Path(get_hermes_home())
except ImportError:
base = Path(os.environ.get("HERMES_HOME", str(Path.home() / ".hermes")))
from hermes_constants import get_hermes_home
base = Path(hermes_home) if hermes_home is not None else Path(get_hermes_home())
return base / "mcp-tokens"