Agent self-healing: auto-migrate old config key names on startup

Instead of crash-looping with KeyError when config has old key names,
load_config() now detects and migrates automatically:
- server_url -> jarvis_url
- api_key -> registration_key
- adds hostname from gethostname() if missing
- adds ssl_verify (false if URL is a bare IP)
- falls back to /opt/jarvis-agent/config.json if /etc path missing

Migrated config is saved in place so the fix is permanent.
Agents self-update within 24h via the existing update_url mechanism.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 01:57:40 +00:00
parent 5d12ed6f62
commit c6549ee27e
+35 -1
View File
@@ -28,11 +28,45 @@ AGENT_VERSION = "3.1"
# ── Config helpers ──────────────────────────────────────────────────────────── # ── Config helpers ────────────────────────────────────────────────────────────
def load_config() -> dict: def load_config() -> dict:
legacy_path = "/opt/jarvis-agent/config.json"
if not os.path.exists(CONFIG_PATH): if not os.path.exists(CONFIG_PATH):
if os.path.exists(legacy_path):
print(f"[JARVIS] Config found at legacy path {legacy_path} - migrating...", flush=True)
Path(CONFIG_PATH).parent.mkdir(parents=True, exist_ok=True)
with open(legacy_path) as f:
cfg = json.load(f)
else:
print(f"[ERROR] Config not found at {CONFIG_PATH}. Run the installer first.", flush=True) print(f"[ERROR] Config not found at {CONFIG_PATH}. Run the installer first.", flush=True)
sys.exit(1) sys.exit(1)
else:
with open(CONFIG_PATH) as f: with open(CONFIG_PATH) as f:
return json.load(f) cfg = json.load(f)
# Migrate old key names so the agent self-heals instead of crash-looping
import re as _re
changed = False
if "server_url" in cfg and "jarvis_url" not in cfg:
cfg["jarvis_url"] = cfg.pop("server_url")
print("[JARVIS] Config migrated: server_url -> jarvis_url", flush=True)
changed = True
if "api_key" in cfg and "registration_key" not in cfg:
cfg["registration_key"] = cfg.pop("api_key")
print("[JARVIS] Config migrated: api_key -> registration_key", flush=True)
changed = True
if "hostname" not in cfg:
cfg["hostname"] = socket.gethostname()
changed = True
if "ssl_verify" not in cfg:
cfg["ssl_verify"] = not bool(_re.match(r"https?://\d+\.\d+\.\d+\.\d+", cfg.get("jarvis_url", "")))
changed = True
if changed:
with open(CONFIG_PATH, "w") as f:
json.dump(cfg, f, indent=2)
print("[JARVIS] Config saved after migration.", flush=True)
return cfg
def load_state() -> dict: def load_state() -> dict:
if os.path.exists(STATE_PATH): if os.path.exists(STATE_PATH):