diff --git a/cron/scheduler.py b/cron/scheduler.py index 64badee9c3..23cf5cd615 100644 --- a/cron/scheduler.py +++ b/cron/scheduler.py @@ -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 +# 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 -_LOCK_DIR = Path.home() / ".hermes" / "cron" +_LOCK_DIR = _hermes_home / "cron" _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. from dotenv import load_dotenv 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: - 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") # Custom endpoint (OPENAI_*) takes precedence, matching CLI behavior @@ -176,7 +179,7 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]: try: import yaml - _cfg_path = os.path.expanduser("~/.hermes/config.yaml") + _cfg_path = str(_hermes_home / "config.yaml") if os.path.exists(_cfg_path): with open(_cfg_path) as _f: _cfg = yaml.safe_load(_f) or {} diff --git a/gateway/run.py b/gateway/run.py index e332991deb..030c109875 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -28,9 +28,12 @@ from typing import Dict, Optional, Any, List # Add parent directory to path 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 from dotenv import load_dotenv -_env_path = Path.home() / '.hermes' / '.env' +_env_path = _hermes_home / '.env' if _env_path.exists(): try: 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. # 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(): try: import yaml as _yaml @@ -141,7 +144,7 @@ class GatewayRunner: if not file_path: try: import yaml as _y - cfg_path = Path.home() / ".hermes" / "config.yaml" + cfg_path = _hermes_home / "config.yaml" if cfg_path.exists(): with open(cfg_path) as _f: cfg = _y.safe_load(_f) or {} @@ -152,7 +155,7 @@ class GatewayRunner: return [] path = Path(file_path).expanduser() if not path.is_absolute(): - path = Path.home() / ".hermes" / path + path = _hermes_home / path if not path.exists(): logger.warning("Prefill messages file not found: %s", path) return [] @@ -179,7 +182,7 @@ class GatewayRunner: return prompt try: import yaml as _y - cfg_path = Path.home() / ".hermes" / "config.yaml" + cfg_path = _hermes_home / "config.yaml" if cfg_path.exists(): with open(cfg_path) as _f: cfg = _y.safe_load(_f) or {} @@ -200,7 +203,7 @@ class GatewayRunner: if not effort: try: import yaml as _y - cfg_path = Path.home() / ".hermes" / "config.yaml" + cfg_path = _hermes_home / "config.yaml" if cfg_path.exists(): with open(cfg_path) as _f: cfg = _y.safe_load(_f) or {} @@ -884,7 +887,7 @@ class GatewayRunner: try: import yaml - config_path = Path.home() / '.hermes' / 'config.yaml' + config_path = _hermes_home / 'config.yaml' if config_path.exists(): with open(config_path, 'r') as f: config = yaml.safe_load(f) or {} @@ -981,7 +984,7 @@ class GatewayRunner: # Save to config.yaml try: import yaml - config_path = Path.home() / '.hermes' / 'config.yaml' + config_path = _hermes_home / 'config.yaml' user_config = {} if config_path.exists(): with open(config_path) as f: @@ -1243,7 +1246,7 @@ class GatewayRunner: # Try to load platform_toolsets from config platform_toolsets_config = {} try: - config_path = Path.home() / '.hermes' / 'config.yaml' + config_path = _hermes_home / 'config.yaml' if config_path.exists(): import yaml with open(config_path, 'r') as f: @@ -1405,7 +1408,7 @@ class GatewayRunner: try: import yaml as _y - _cfg_path = Path.home() / ".hermes" / "config.yaml" + _cfg_path = _hermes_home / "config.yaml" if _cfg_path.exists(): with open(_cfg_path) as _f: _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. """ # 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) file_handler = RotatingFileHandler( log_dir / 'gateway.log',