Merge pull request #51 from deankerr/fix/cli-env-path-resolution
fix: consistent HERMES_HOME and .env path resolution across all entry points
This commit is contained in:
commit
9a148bb9a3
5 changed files with 54 additions and 41 deletions
29
cli.py
29
cli.py
|
|
@ -49,16 +49,26 @@ import threading
|
||||||
import queue
|
import queue
|
||||||
|
|
||||||
|
|
||||||
# Load environment variables first
|
# Load .env from ~/.hermes/.env first, then project root as dev fallback
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from hermes_constants import OPENROUTER_BASE_URL
|
from hermes_constants import OPENROUTER_BASE_URL
|
||||||
|
|
||||||
env_path = Path(__file__).parent / '.env'
|
_hermes_home = Path(os.getenv("HERMES_HOME", Path.home() / ".hermes"))
|
||||||
if env_path.exists():
|
_user_env = _hermes_home / ".env"
|
||||||
|
_project_env = Path(__file__).parent / '.env'
|
||||||
|
if _user_env.exists():
|
||||||
try:
|
try:
|
||||||
load_dotenv(dotenv_path=env_path, encoding="utf-8")
|
load_dotenv(dotenv_path=_user_env, encoding="utf-8")
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
load_dotenv(dotenv_path=env_path, encoding="latin-1")
|
load_dotenv(dotenv_path=_user_env, encoding="latin-1")
|
||||||
|
elif _project_env.exists():
|
||||||
|
try:
|
||||||
|
load_dotenv(dotenv_path=_project_env, encoding="utf-8")
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
load_dotenv(dotenv_path=_project_env, encoding="latin-1")
|
||||||
|
|
||||||
|
# Point mini-swe-agent at ~/.hermes/ so it shares our config
|
||||||
|
os.environ.setdefault("MSWEA_GLOBAL_CONFIG_DIR", str(_hermes_home))
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Configuration Loading
|
# Configuration Loading
|
||||||
|
|
@ -132,15 +142,6 @@ def load_cli_config() -> Dict[str, Any]:
|
||||||
else:
|
else:
|
||||||
config_path = project_config_path
|
config_path = project_config_path
|
||||||
|
|
||||||
# Also load .env from ~/.hermes/.env if it exists
|
|
||||||
user_env_path = Path.home() / '.hermes' / '.env'
|
|
||||||
if user_env_path.exists():
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
try:
|
|
||||||
load_dotenv(dotenv_path=user_env_path, override=True, encoding="utf-8")
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
load_dotenv(dotenv_path=user_env_path, override=True, encoding="latin-1")
|
|
||||||
|
|
||||||
# Default configuration
|
# Default configuration
|
||||||
defaults = {
|
defaults = {
|
||||||
"model": {
|
"model": {
|
||||||
|
|
|
||||||
|
|
@ -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 {}
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ def run_doctor(args):
|
||||||
print()
|
print()
|
||||||
print(color("◆ Directory Structure", Colors.CYAN, Colors.BOLD))
|
print(color("◆ Directory Structure", Colors.CYAN, Colors.BOLD))
|
||||||
|
|
||||||
hermes_home = Path.home() / ".hermes"
|
hermes_home = HERMES_HOME
|
||||||
if hermes_home.exists():
|
if hermes_home.exists():
|
||||||
check_ok("~/.hermes directory exists")
|
check_ok("~/.hermes directory exists")
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
28
rl_cli.py
28
rl_cli.py
|
|
@ -27,19 +27,25 @@ from pathlib import Path
|
||||||
import fire
|
import fire
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
# Load environment variables from .env file
|
# Load .env from ~/.hermes/.env first, then project root as dev fallback
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
# Load from ~/.hermes/.env first, then local .env
|
_hermes_home = Path(os.getenv("HERMES_HOME", Path.home() / ".hermes"))
|
||||||
hermes_env_path = Path.home() / '.hermes' / '.env'
|
_user_env = _hermes_home / ".env"
|
||||||
local_env_path = Path(__file__).parent / '.env'
|
_project_env = Path(__file__).parent / '.env'
|
||||||
|
|
||||||
if hermes_env_path.exists():
|
if _user_env.exists():
|
||||||
load_dotenv(dotenv_path=hermes_env_path)
|
try:
|
||||||
print(f"✅ Loaded environment variables from {hermes_env_path}")
|
load_dotenv(dotenv_path=_user_env, encoding="utf-8")
|
||||||
elif local_env_path.exists():
|
except UnicodeDecodeError:
|
||||||
load_dotenv(dotenv_path=local_env_path)
|
load_dotenv(dotenv_path=_user_env, encoding="latin-1")
|
||||||
print(f"✅ Loaded environment variables from {local_env_path}")
|
print(f"✅ Loaded environment variables from {_user_env}")
|
||||||
|
elif _project_env.exists():
|
||||||
|
try:
|
||||||
|
load_dotenv(dotenv_path=_project_env, encoding="utf-8")
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
load_dotenv(dotenv_path=_project_env, encoding="latin-1")
|
||||||
|
print(f"✅ Loaded environment variables from {_project_env}")
|
||||||
|
|
||||||
# Set terminal working directory to tinker-atropos submodule
|
# Set terminal working directory to tinker-atropos submodule
|
||||||
# This ensures terminal commands run in the right context for RL work
|
# This ensures terminal commands run in the right context for RL work
|
||||||
|
|
@ -77,7 +83,7 @@ def load_hermes_config() -> dict:
|
||||||
Returns:
|
Returns:
|
||||||
dict: Configuration with model, base_url, etc.
|
dict: Configuration with model, base_url, etc.
|
||||||
"""
|
"""
|
||||||
config_path = Path.home() / '.hermes' / 'config.yaml'
|
config_path = _hermes_home / 'config.yaml'
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
"model": DEFAULT_MODEL,
|
"model": DEFAULT_MODEL,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue