[GEMINI-HARDEN-01] Replace hard-coded fleet inventory with repo-native config
Some checks failed
Smoke Test / smoke (pull_request) Failing after 23s
Architecture Lint / Linter Tests (pull_request) Successful in 26s
Validate Config / YAML Lint (pull_request) Failing after 15s
Validate Config / JSON Validate (pull_request) Successful in 19s
Validate Config / Python Syntax & Import Check (pull_request) Failing after 1m1s
Validate Config / Python Test Suite (pull_request) Has been skipped
Validate Config / Shell Script Lint (pull_request) Failing after 1m4s
Validate Config / Cron Syntax Check (pull_request) Successful in 13s
Validate Config / Deploy Script Dry Run (pull_request) Successful in 13s
Validate Config / Playbook Schema Validation (pull_request) Successful in 25s
Architecture Lint / Lint Repository (pull_request) Failing after 22s
PR Checklist / pr-checklist (pull_request) Successful in 5m0s

Add fleet.inventory and fleet.path_contracts to config.yaml:
- Central source of truth for IPs, ports, roles, remote paths
- Introduce get_config_path(), load_fleet_inventory(), get_path_contract()
- Updated fleet_llama.py, self_healing.py, telemetry.py, agent_dispatch.py,
  skill_installer.py to read from config instead of hard-coded dicts/paths
- Documented inventory contract and override mechanism in scripts/README.md

Scripts retain forward-compatible fallback defaults for backwards compatibility.

Closes #433
This commit is contained in:
Alexander Payne
2026-04-26 22:47:59 -04:00
parent 34a1e68e67
commit ab9d1c0fa4
7 changed files with 267 additions and 29 deletions

View File

@@ -12,16 +12,59 @@ import argparse
import subprocess
from pathlib import Path
#!/usr/bin/env python3
"""
[OPS] Sovereign Skill Installer
Part of the Gemini Sovereign Infrastructure Suite.
Packages and installs Hermes skills onto remote wizard nodes.
"""
import os
import sys
import argparse
import subprocess
from pathlib import Path
import yaml
# --- CONFIGURATION ---
# Assumes hermes-agent is a sibling directory to timmy-config
HERMES_ROOT = "../hermes-agent"
# Load fleet inventory and path contracts from central timmy-config
def get_config_path():
return os.environ.get('TIMMY_CONFIG') or os.path.join(
os.path.dirname(os.path.abspath(__file__)), '..', 'config.yaml'
)
def load_fleet_inventory():
"""Return dict of {{host: {{ip, port, role, remote_root, capabilities}}}}"""
try:
with open(get_config_path(), 'r') as f:
cfg = yaml.safe_load(f)
inv = cfg.get('fleet', {}).get('inventory', {})
return inv if inv else {}
except Exception:
return {}
def get_path_contract(key, default):
"""Return path contract value from config."""
try:
with open(get_config_path(), 'r') as f:
cfg = yaml.safe_load(f)
return cfg.get('fleet', {}).get('path_contracts', {}).get(key, default)
except Exception:
return default
FLEET = load_fleet_inventory()
HERMES_ROOT = get_path_contract('hermes_agent_local', '../hermes-agent')
REMOTE_ROOT = get_path_contract('hermes_remote', '/opt/hermes')
SKILLS_DIR = "skills"
class SkillInstaller:
def __init__(self, host: str, ip: str):
self.host = host
self.ip = ip
self.hermes_path = Path(HERMES_ROOT).resolve()
self.hermes_path = Path(HERMES_ROOT).expanduser().resolve()
def log(self, message: str):
print(f"[*] {message}")
@@ -44,13 +87,13 @@ class SkillInstaller:
# 2. Upload to remote
self.log("Uploading to remote...")
remote_path = f"/opt/hermes/skills/{skill_name}"
subprocess.run(["ssh", f"root@{self.ip}", f"mkdir -p /opt/hermes/skills"])
remote_path = f"{REMOTE_ROOT}/skills/{skill_name}"
subprocess.run(["ssh", f"root@{self.ip}", f"mkdir -p {REMOTE_ROOT}/skills"])
subprocess.run(["scp", tar_file, f"root@{self.ip}:/tmp/"])
# 3. Extract and register
self.log("Extracting and registering...")
extract_cmd = f"tar -xzf /tmp/{tar_file} -C /opt/hermes/skills/ && rm /tmp/{tar_file}"
extract_cmd = f"tar -xzf /tmp/{tar_file} -C {REMOTE_ROOT}/skills/ && rm /tmp/{tar_file}"
subprocess.run(["ssh", f"root@{self.ip}", extract_cmd])
# Registration logic (simplified)