fix: respect HERMES_HOME env var in gateway and cron scheduler

Both entry points hardcoded Path.home() / ".hermes" for .env, config.yaml,
logs, and lock files. Now uses _hermes_home which reads HERMES_HOME env var
with ~/.hermes as default, matching cli.py and run_agent.py.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dean Kerr 2026-02-26 18:51:46 +11:00
parent cf3236ed27
commit f2891b70d0
2 changed files with 21 additions and 15 deletions

View file

@ -34,8 +34,11 @@ sys.path.insert(0, str(Path(__file__).parent.parent))
from cron.jobs import get_due_jobs, mark_job_run, save_job_output from cron.jobs import get_due_jobs, mark_job_run, save_job_output
# Resolve Hermes home directory (respects HERMES_HOME override)
_hermes_home = Path(os.getenv("HERMES_HOME", Path.home() / ".hermes"))
# File-based lock prevents concurrent ticks from gateway + daemon + systemd timer # File-based lock prevents concurrent ticks from gateway + daemon + systemd timer
_LOCK_DIR = Path.home() / ".hermes" / "cron" _LOCK_DIR = _hermes_home / "cron"
_LOCK_FILE = _LOCK_DIR / ".tick.lock" _LOCK_FILE = _LOCK_DIR / ".tick.lock"
@ -165,9 +168,9 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
# changes take effect without a gateway restart. # changes take effect without a gateway restart.
from dotenv import load_dotenv from dotenv import load_dotenv
try: try:
load_dotenv(os.path.expanduser("~/.hermes/.env"), override=True, encoding="utf-8") load_dotenv(str(_hermes_home / ".env"), override=True, encoding="utf-8")
except UnicodeDecodeError: except UnicodeDecodeError:
load_dotenv(os.path.expanduser("~/.hermes/.env"), override=True, encoding="latin-1") load_dotenv(str(_hermes_home / ".env"), override=True, encoding="latin-1")
model = os.getenv("HERMES_MODEL", "anthropic/claude-opus-4.6") model = os.getenv("HERMES_MODEL", "anthropic/claude-opus-4.6")
# Custom endpoint (OPENAI_*) takes precedence, matching CLI behavior # Custom endpoint (OPENAI_*) takes precedence, matching CLI behavior
@ -176,7 +179,7 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
try: try:
import yaml import yaml
_cfg_path = os.path.expanduser("~/.hermes/config.yaml") _cfg_path = str(_hermes_home / "config.yaml")
if os.path.exists(_cfg_path): if os.path.exists(_cfg_path):
with open(_cfg_path) as _f: with open(_cfg_path) as _f:
_cfg = yaml.safe_load(_f) or {} _cfg = yaml.safe_load(_f) or {}

View file

@ -28,9 +28,12 @@ from typing import Dict, Optional, Any, List
# Add parent directory to path # Add parent directory to path
sys.path.insert(0, str(Path(__file__).parent.parent)) sys.path.insert(0, str(Path(__file__).parent.parent))
# Resolve Hermes home directory (respects HERMES_HOME override)
_hermes_home = Path(os.getenv("HERMES_HOME", Path.home() / ".hermes"))
# Load environment variables from ~/.hermes/.env first # Load environment variables from ~/.hermes/.env first
from dotenv import load_dotenv from dotenv import load_dotenv
_env_path = Path.home() / '.hermes' / '.env' _env_path = _hermes_home / '.env'
if _env_path.exists(): if _env_path.exists():
try: try:
load_dotenv(_env_path, encoding="utf-8") load_dotenv(_env_path, encoding="utf-8")
@ -41,7 +44,7 @@ load_dotenv()
# Bridge config.yaml values into the environment so os.getenv() picks them up. # Bridge config.yaml values into the environment so os.getenv() picks them up.
# Values already set in the environment (from .env or shell) take precedence. # Values already set in the environment (from .env or shell) take precedence.
_config_path = Path.home() / '.hermes' / 'config.yaml' _config_path = _hermes_home / 'config.yaml'
if _config_path.exists(): if _config_path.exists():
try: try:
import yaml as _yaml import yaml as _yaml
@ -141,7 +144,7 @@ class GatewayRunner:
if not file_path: if not file_path:
try: try:
import yaml as _y import yaml as _y
cfg_path = Path.home() / ".hermes" / "config.yaml" cfg_path = _hermes_home / "config.yaml"
if cfg_path.exists(): if cfg_path.exists():
with open(cfg_path) as _f: with open(cfg_path) as _f:
cfg = _y.safe_load(_f) or {} cfg = _y.safe_load(_f) or {}
@ -152,7 +155,7 @@ class GatewayRunner:
return [] return []
path = Path(file_path).expanduser() path = Path(file_path).expanduser()
if not path.is_absolute(): if not path.is_absolute():
path = Path.home() / ".hermes" / path path = _hermes_home / path
if not path.exists(): if not path.exists():
logger.warning("Prefill messages file not found: %s", path) logger.warning("Prefill messages file not found: %s", path)
return [] return []
@ -179,7 +182,7 @@ class GatewayRunner:
return prompt return prompt
try: try:
import yaml as _y import yaml as _y
cfg_path = Path.home() / ".hermes" / "config.yaml" cfg_path = _hermes_home / "config.yaml"
if cfg_path.exists(): if cfg_path.exists():
with open(cfg_path) as _f: with open(cfg_path) as _f:
cfg = _y.safe_load(_f) or {} cfg = _y.safe_load(_f) or {}
@ -200,7 +203,7 @@ class GatewayRunner:
if not effort: if not effort:
try: try:
import yaml as _y import yaml as _y
cfg_path = Path.home() / ".hermes" / "config.yaml" cfg_path = _hermes_home / "config.yaml"
if cfg_path.exists(): if cfg_path.exists():
with open(cfg_path) as _f: with open(cfg_path) as _f:
cfg = _y.safe_load(_f) or {} cfg = _y.safe_load(_f) or {}
@ -884,7 +887,7 @@ class GatewayRunner:
try: try:
import yaml import yaml
config_path = Path.home() / '.hermes' / 'config.yaml' config_path = _hermes_home / 'config.yaml'
if config_path.exists(): if config_path.exists():
with open(config_path, 'r') as f: with open(config_path, 'r') as f:
config = yaml.safe_load(f) or {} config = yaml.safe_load(f) or {}
@ -981,7 +984,7 @@ class GatewayRunner:
# Save to config.yaml # Save to config.yaml
try: try:
import yaml import yaml
config_path = Path.home() / '.hermes' / 'config.yaml' config_path = _hermes_home / 'config.yaml'
user_config = {} user_config = {}
if config_path.exists(): if config_path.exists():
with open(config_path) as f: with open(config_path) as f:
@ -1243,7 +1246,7 @@ class GatewayRunner:
# Try to load platform_toolsets from config # Try to load platform_toolsets from config
platform_toolsets_config = {} platform_toolsets_config = {}
try: try:
config_path = Path.home() / '.hermes' / 'config.yaml' config_path = _hermes_home / 'config.yaml'
if config_path.exists(): if config_path.exists():
import yaml import yaml
with open(config_path, 'r') as f: with open(config_path, 'r') as f:
@ -1405,7 +1408,7 @@ class GatewayRunner:
try: try:
import yaml as _y import yaml as _y
_cfg_path = Path.home() / ".hermes" / "config.yaml" _cfg_path = _hermes_home / "config.yaml"
if _cfg_path.exists(): if _cfg_path.exists():
with open(_cfg_path) as _f: with open(_cfg_path) as _f:
_cfg = _y.safe_load(_f) or {} _cfg = _y.safe_load(_f) or {}
@ -1697,7 +1700,7 @@ async def start_gateway(config: Optional[GatewayConfig] = None) -> bool:
A False return causes a non-zero exit code so systemd can auto-restart. A False return causes a non-zero exit code so systemd can auto-restart.
""" """
# Configure rotating file log so gateway output is persisted for debugging # Configure rotating file log so gateway output is persisted for debugging
log_dir = Path.home() / '.hermes' / 'logs' log_dir = _hermes_home / 'logs'
log_dir.mkdir(parents=True, exist_ok=True) log_dir.mkdir(parents=True, exist_ok=True)
file_handler = RotatingFileHandler( file_handler = RotatingFileHandler(
log_dir / 'gateway.log', log_dir / 'gateway.log',