""" Room - Runtime environment configuration. A Room defines connection details for a managed environment (hosts, ports, domains, credentials). Used by Pulse (Vein + Room + Depot) and Desk (Cabinet + Room + Depots). Room instances are stored in data/rooms.json. """ from enum import Enum from typing import Optional from pydantic import BaseModel, Field class RoomStatus(str, Enum): PENDING = "pending" PLANNED = "planned" BUILDING = "building" DEV = "dev" LIVE = "live" READY = "ready" class RoomConfig(BaseModel): """Environment-specific configuration for a room.""" # Network host: Optional[str] = Field(None, description="Primary host/domain") port: Optional[int] = Field(None, description="Primary port") # Paths config_path: Optional[str] = Field(None, description="Path to room config folder") deploy_path: Optional[str] = Field(None, description="Deployment target path") # Docker network_name: Optional[str] = Field(None, description="Docker network name") deployment_name: Optional[str] = Field(None, description="Container name prefix") # Database (when room has DB access) db_host: Optional[str] = None db_port: Optional[int] = Field(None, ge=1, le=65535) db_name: Optional[str] = None db_user: Optional[str] = None # Note: db_password should come from env vars, not stored in config class Room(BaseModel): """Runtime environment configuration.""" name: str = Field(..., description="Unique identifier") slug: str = Field(..., description="URL-friendly identifier") title: str = Field(..., description="Display title for UI") status: RoomStatus = Field(RoomStatus.PENDING, description="Current status") # Optional extended config config: Optional[RoomConfig] = Field(None, description="Environment configuration") # Legacy field for backwards compatibility config_path: Optional[str] = Field(None, description="Path to room config folder") class Config: use_enum_values = True def load_rooms(data_path: str = "data/rooms.json") -> list[Room]: """Load rooms from data file.""" import json from pathlib import Path path = Path(data_path) if not path.exists(): return [] with open(path) as f: data = json.load(f) return [Room(**item) for item in data.get("items", [])]