atlas-iac/services/hermes/scripts/migrate_herdr_state.py

62 lines
2.0 KiB
Python

#!/usr/bin/env python3
"""Archive legacy HERDR metadata before the direct CLI lane takes over."""
from __future__ import annotations
import json
import os
import shutil
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
DATA_ROOT = Path("/opt/data")
def _load_json(path: Path) -> dict[str, Any]:
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
return {}
return value if isinstance(value, dict) else {}
def archive_legacy_state(data_root: Path = DATA_ROOT) -> Path:
"""Persist non-secret worker references and retire the exact HERDR binary."""
source = data_root / "home/.config/herdr/session.json"
archive = data_root / "cli-lanes/migration/herdr-retirement.json"
archive.parent.mkdir(parents=True, exist_ok=True)
previous = _load_json(archive)
legacy = _load_json(source)
value = {
"schema_version": 1,
"migrated_at": previous.get("migrated_at")
or datetime.now(timezone.utc).isoformat(),
"source": str(source),
"legacy_session": legacy,
"provider_state_preserved": [
str(data_root / "home/.claude"),
str(data_root / "home/.codex"),
],
}
temporary = archive.with_name(f".{archive.name}.{os.getpid()}.tmp")
temporary.write_text(json.dumps(value, indent=2, sort_keys=True) + "\n", encoding="utf-8")
temporary.chmod(0o600)
os.replace(temporary, archive)
# Provider transcripts remain in .claude and .codex. HERDR itself is
# reproducibly reinstallable from Git history, so only its pinned binary is
# retired after the migration record is durable.
binary = data_root / "tools/bin/herdr"
if binary.is_file():
binary.unlink()
for legacy_dir in (data_root / "home/.config/herdr", data_root / "herdr"):
if legacy_dir.is_dir():
shutil.rmtree(legacy_dir)
return archive
if __name__ == "__main__":
print(archive_legacy_state())