"""Org/workspace credits for metered sandbox consumption.""" from __future__ import annotations import json import os from pathlib import Path def _default_credits_path() -> Path: base = Path(os.environ.get("XDG_DATA_HOME", Path.home() / ".local" / "share")) return base / "sandboxer" / "credits.json" class CreditsStore: def __init__(self, path: Path | None = None) -> None: self.path = path or _default_credits_path() self.path.parent.mkdir(parents=True, exist_ok=True) def _read(self) -> dict: if not self.path.exists(): default = float(os.environ.get("SANDBOXER_DEFAULT_CREDITS", "10.0")) return {"balance_usd": default, "currency": "USD"} return json.loads(self.path.read_text()) def _write(self, data: dict) -> None: self.path.write_text(json.dumps(data, indent=2)) def balance(self) -> float: return float(self._read().get("balance_usd", 0.0)) def can_afford(self, amount_usd: float) -> bool: return self.balance() >= amount_usd def add(self, amount_usd: float) -> float: data = self._read() data["balance_usd"] = round(float(data.get("balance_usd", 0.0)) + amount_usd, 4) self._write(data) return data["balance_usd"] def debit(self, amount_usd: float) -> float: data = self._read() new_balance = round(float(data.get("balance_usd", 0.0)) - amount_usd, 4) if new_balance < 0: raise ValueError(f"Insufficient credits: need {amount_usd:.4f} USD") data["balance_usd"] = new_balance self._write(data) return new_balance