"""Echo Core secrets manager — keyring wrapper.""" import json import keyring SERVICE = "echo-core" # Required secrets that should exist for full functionality REQUIRED_SECRETS = ["discord_token"] def set_secret(name: str, value: str) -> None: """Store a secret in the system keyring.""" keyring.set_password(SERVICE, name, value) # Track secret name in the registry names = _get_registry() if name not in names: names.append(name) _save_registry(names) def get_secret(name: str) -> str | None: """Retrieve a secret from keyring. Returns None if not found.""" return keyring.get_password(SERVICE, name) def get_json_secret(name: str) -> dict | None: """Retrieve and JSON-deserialize a secret.""" raw = get_secret(name) if raw is None: return None return json.loads(raw) def delete_secret(name: str) -> bool: """Delete a secret. Returns True if existed.""" try: keyring.delete_password(SERVICE, name) except keyring.errors.PasswordDeleteError: pass names = _get_registry() if name in names: names.remove(name) _save_registry(names) return True return False def list_secrets() -> list[str]: """List all stored secret names.""" return _get_registry() def check_secrets() -> dict[str, bool]: """Check which required secrets exist. Returns {name: exists}.""" return {name: get_secret(name) is not None for name in REQUIRED_SECRETS} def _get_registry() -> list[str]: """Get list of secret names from keyring registry.""" raw = keyring.get_password(SERVICE, "_registry") if raw is None: return [] return json.loads(raw) def _save_registry(names: list[str]) -> None: """Save secret names registry to keyring.""" keyring.set_password(SERVICE, "_registry", json.dumps(sorted(set(names))))