hermes-hub/src/antigravity_provider/paths.py

163 lines
5 KiB
Python

"""Single Source of Truth for Hermes Hub Filesystem Paths.
Ensures zero hardcoded absolute developer directories or usernames.
Fully respects HERMES_HOME for complete hermetic test isolation.
"""
from __future__ import annotations
import os
import sys
from pathlib import Path
from typing import Optional
def get_hermes_home() -> Path:
"""Return base hermes home directory, respecting HERMES_HOME environment variable."""
env_home = os.environ.get("HERMES_HOME")
if env_home:
p = Path(env_home).resolve()
p.mkdir(parents=True, exist_ok=True)
return p
local_app = os.environ.get("LOCALAPPDATA", "")
if local_app:
p = Path(local_app) / "hermes"
else:
p = Path.home() / ".hermes"
p.mkdir(parents=True, exist_ok=True)
return p
def get_repo_root() -> Path:
"""Find repository / installation root containing assets and launcher."""
cur = Path(__file__).resolve()
for parent in [cur.parents[3], cur.parents[2], cur.parents[1]]:
if (parent / "assets").exists() or (parent / "pyproject.toml").exists():
return parent
# Fallback to plugin directory in hermes home
plugin_dir = get_hermes_home() / "plugins" / "antigravity-provider"
if plugin_dir.exists():
return plugin_dir
return cur.parents[2]
def get_assets_dir() -> Path:
return get_repo_root() / "assets"
def get_branding_dir() -> Path:
return get_assets_dir() / "branding"
def get_providers_assets_dir() -> Path:
return get_assets_dir() / "providers"
def get_logs_dir() -> Path:
d = get_hermes_home() / "logs"
d.mkdir(parents=True, exist_ok=True)
return d
def get_log_file() -> Path:
return get_logs_dir() / "hermes-hub.log"
def get_startup_log_file() -> Path:
return get_logs_dir() / "startup.log"
def get_config_dir() -> Path:
d = get_hermes_home()
d.mkdir(parents=True, exist_ok=True)
return d
def get_router_profiles_path() -> Path:
return get_config_dir() / "router_profiles.yaml"
def get_router_state_path() -> Path:
return get_config_dir() / "router_state.json"
def get_router_active_profile_path() -> Path:
return get_config_dir() / "router_active_profile.json"
def get_workflow_state_path() -> Path:
"""Return the persisted agent/workflow state sidecar.
Logical roles and their execution routes remain canonical in
``router_profiles.yaml``. This file stores only the extra agent metadata,
graph layout and execution checkpoints which do not belong to routing.
"""
return get_config_dir() / "workflow_state.json"
def get_workflow_run_state_path() -> Path:
"""Return the persisted active workflow run state file."""
return get_config_dir() / "workflow_run_state.json"
def get_agent_files_dir() -> Path:
"""Return the user-editable directory containing real Agent Files."""
directory = get_hermes_home() / "agents"
directory.mkdir(parents=True, exist_ok=True)
return directory
def get_compatibility_path() -> Path:
return get_config_dir() / "compatibility.json"
def get_profile_dir(profile_id: str, provider: Optional[str] = None) -> Path:
"""Return isolated storage directory for a profile.
Accepts both (profile_id) and (provider, profile_id) or (profile_id, provider) gracefully.
"""
# If first argument looks like a provider or swapped, resolve cleanly
p_id = profile_id
prov = provider
if prov is None:
p_lower = p_id.lower()
if p_lower.startswith("ag-") or "antigravity" in p_lower:
folder_prefix = "agy_profiles"
elif p_lower.startswith("codex-") or "codex" in p_lower:
folder_prefix = "codex_profiles"
elif p_lower.startswith("opengo-") or "opencode" in p_lower:
folder_prefix = "opengo_profiles"
elif p_lower.startswith("claude-") or "claude" in p_lower or "anthropic" in p_lower:
folder_prefix = "claude_profiles"
elif p_lower.startswith("grok-") or "grok" in p_lower or "xai" in p_lower:
folder_prefix = "grok_profiles"
else:
folder_prefix = f"{p_lower}_profiles"
else:
# Provider explicitly passed
prov_lower = prov.lower()
if "antigravity" in prov_lower or "agy" in prov_lower:
folder_prefix = "agy_profiles"
elif "codex" in prov_lower or "openai" in prov_lower:
folder_prefix = "codex_profiles"
elif "opencode" in prov_lower or "opengo" in prov_lower:
folder_prefix = "opengo_profiles"
elif "claude" in prov_lower or "anthropic" in prov_lower:
folder_prefix = "claude_profiles"
elif "grok" in prov_lower or "xai" in prov_lower:
folder_prefix = "grok_profiles"
else:
folder_prefix = f"{prov_lower}_profiles"
d = get_hermes_home() / folder_prefix / p_id
d.mkdir(parents=True, exist_ok=True)
return d
def get_hermes_agent_dir() -> Path:
return get_hermes_home() / "hermes-agent"
def get_hermes_agent_venv() -> Path:
return get_hermes_agent_dir() / "venv"