"""Simple file-backed idempotency for dual-run commands.""" from __future__ import annotations import hashlib import json import os from pathlib import Path from typing import Any def _store_path() -> Path: raw = os.environ.get("RM_IDEMPOTENCY_PATH", "~/.repo-manager/idempotency.json") return Path(raw).expanduser() def _load() -> dict[str, Any]: path = _store_path() if not path.is_file(): return {} try: return json.loads(path.read_text(encoding="utf-8")) except (OSError, json.JSONDecodeError): return {} def _save(data: dict[str, Any]) -> None: path = _store_path() path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8") def payload_hash(payload: dict[str, Any]) -> str: blob = json.dumps(payload, sort_keys=True, default=str) return hashlib.sha256(blob.encode()).hexdigest() def get(key: str) -> dict[str, Any] | None: return _load().get(key) def put(key: str, payload_hash_value: str, result: dict[str, Any]) -> None: data = _load() data[key] = {"payload_hash": payload_hash_value, "result": result} _save(data)