"""Abstract base class for runtime adapters.""" from __future__ import annotations from abc import ABC, abstractmethod class RuntimeError(Exception): """Base error for runtime adapter failures. Attributes: category: Normalized error category for retry/classification logic. """ def __init__(self, message: str, category: str = "unknown") -> None: super().__init__(message) self.category = category class RuntimeAdapter(ABC): """Interface for compute runtime backends (EC2, fake, etc.).""" @abstractmethod def launch_instance( self, slot_id: str, user_data: str, *, nested_virtualization: bool = False ) -> str: """Launch an instance for slot_id. Return instance_id. When nested_virtualization is True, an on-demand instance is launched using the on-demand launch template. When False (default), a spot instance is launched. """ @abstractmethod def describe_instance(self, instance_id: str) -> dict: """Return normalized instance info dict. Keys: state, tailscale_ip (or None), launch_time. """ @abstractmethod def terminate_instance(self, instance_id: str) -> None: """Terminate the instance.""" @abstractmethod def list_managed_instances(self) -> list[dict]: """Return list of instances tagged ManagedBy=nix-builder-autoscaler. Each entry has instance_id, state, slot_id (from AutoscalerSlot tag). """