"""Admin config source loading and source precedence.""" import os from io import StringIO from pathlib import Path from typing import Literal from dotenv import dotenv_values from free_claude_code.config.env_files import ( explicit_env_path as configured_explicit_env_path, ) from free_claude_code.config.env_files import ( repo_env_path as configured_repo_env_path, ) from free_claude_code.config.env_files import ( settings_env_files, ) from free_claude_code.config.env_template import load_env_template_or_empty from .manifest import FIELDS SourceType = Literal[ "default", "template", "repo_env", "managed_env", "explicit_env_file", "process", ] def repo_env_path() -> Path: """Return the repo-local env path.""" return configured_repo_env_path() def explicit_env_path() -> Path | None: """Return the explicit FCC_ENV_FILE path, when configured.""" return configured_explicit_env_path(os.environ) def configured_env_files() -> tuple[tuple[SourceType, Path], ...]: """Return dotenv files in low-to-high precedence order.""" source_names: tuple[SourceType, ...] = ( "repo_env", "managed_env", "explicit_env_file", ) return tuple(zip(source_names, settings_env_files(), strict=False)) def dotenv_values_from_text(text: str) -> dict[str, str]: """Parse dotenv text into string values.""" values = dotenv_values(stream=StringIO(text)) return {key: "" if value is None else value for key, value in values.items()} def template_values() -> dict[str, str]: """Return .env.example values plus manifest defaults for newer fields.""" values = dotenv_values_from_text(load_env_template_or_empty()) for field in FIELDS: values.setdefault(field.key, field.default) return values def dotenv_values_from_file(path: Path) -> dict[str, str]: """Return dotenv values from a file, or an empty mapping when absent.""" if not path.is_file(): return {} values = dotenv_values(path) return {key: "" if value is None else value for key, value in values.items()} def is_locked_source(source: SourceType) -> bool: """Return whether an admin value source must not be overwritten.""" return source in {"process", "explicit_env_file"}