- Add ~/.webber/config.toml for persistent settings - Support api.url, api.key, cli.mode, cli.stream, history.file - Environment variables override config file values - Add 'config' command to show settings and init config file - Update all commands to use config defaults Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
153 lines
3.5 KiB
Python
153 lines
3.5 KiB
Python
"""
|
|
Configuration management for Webber CLI.
|
|
|
|
Loads settings from ~/.webber/config.toml with environment variable overrides.
|
|
"""
|
|
import os
|
|
import tomllib
|
|
from dataclasses import dataclass, field
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
# Config directory and file paths
|
|
CONFIG_DIR = Path.home() / ".webber"
|
|
CONFIG_FILE = CONFIG_DIR / "config.toml"
|
|
|
|
# Default configuration template
|
|
DEFAULT_CONFIG = """\
|
|
# Webber CLI Configuration
|
|
# https://github.com/jpmschweitzer/webber
|
|
|
|
[api]
|
|
# API server URL (dev: 8095, prod: 8086)
|
|
url = "http://localhost:8095"
|
|
|
|
# API key for authentication (optional for dev mode)
|
|
# key = "your-api-key"
|
|
|
|
[cli]
|
|
# Default permission mode: default, plan, auto_accept
|
|
mode = "default"
|
|
|
|
# Enable streaming by default
|
|
stream = true
|
|
|
|
[history]
|
|
# Command history file location
|
|
file = "~/.webber_history"
|
|
"""
|
|
|
|
|
|
@dataclass
|
|
class ApiConfig:
|
|
"""API connection settings."""
|
|
url: str = "http://localhost:8095"
|
|
key: str | None = None
|
|
|
|
|
|
@dataclass
|
|
class CliConfig:
|
|
"""CLI behavior settings."""
|
|
mode: str = "default"
|
|
stream: bool = True
|
|
|
|
|
|
@dataclass
|
|
class HistoryConfig:
|
|
"""History settings."""
|
|
file: str = "~/.webber_history"
|
|
|
|
|
|
@dataclass
|
|
class Config:
|
|
"""Complete configuration."""
|
|
api: ApiConfig = field(default_factory=ApiConfig)
|
|
cli: CliConfig = field(default_factory=CliConfig)
|
|
history: HistoryConfig = field(default_factory=HistoryConfig)
|
|
|
|
|
|
def load_config() -> Config:
|
|
"""
|
|
Load configuration from file with environment variable overrides.
|
|
|
|
Priority (highest to lowest):
|
|
1. Environment variables (WEBBER_API_URL, WEBBER_API_KEY, WEBBER_MODE)
|
|
2. Config file (~/.webber/config.toml)
|
|
3. Built-in defaults
|
|
|
|
Returns:
|
|
Config object with merged settings
|
|
"""
|
|
config = Config()
|
|
|
|
# Load from file if exists
|
|
if CONFIG_FILE.exists():
|
|
try:
|
|
with open(CONFIG_FILE, "rb") as f:
|
|
data = tomllib.load(f)
|
|
config = _parse_config(data)
|
|
except Exception:
|
|
# If config file is invalid, use defaults
|
|
pass
|
|
|
|
# Apply environment variable overrides
|
|
if url := os.environ.get("WEBBER_API_URL"):
|
|
config.api.url = url
|
|
if key := os.environ.get("WEBBER_API_KEY"):
|
|
config.api.key = key
|
|
if mode := os.environ.get("WEBBER_MODE"):
|
|
config.cli.mode = mode
|
|
|
|
return config
|
|
|
|
|
|
def _parse_config(data: dict[str, Any]) -> Config:
|
|
"""Parse config dict into Config object."""
|
|
config = Config()
|
|
|
|
if api := data.get("api"):
|
|
config.api.url = api.get("url", config.api.url)
|
|
config.api.key = api.get("key", config.api.key)
|
|
|
|
if cli := data.get("cli"):
|
|
config.cli.mode = cli.get("mode", config.cli.mode)
|
|
config.cli.stream = cli.get("stream", config.cli.stream)
|
|
|
|
if history := data.get("history"):
|
|
config.history.file = history.get("file", config.history.file)
|
|
|
|
return config
|
|
|
|
|
|
def init_config() -> Path:
|
|
"""
|
|
Initialize config directory and file with defaults.
|
|
|
|
Returns:
|
|
Path to the created config file
|
|
"""
|
|
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
if not CONFIG_FILE.exists():
|
|
CONFIG_FILE.write_text(DEFAULT_CONFIG)
|
|
|
|
return CONFIG_FILE
|
|
|
|
|
|
def get_config_path() -> Path | None:
|
|
"""Get path to config file if it exists."""
|
|
return CONFIG_FILE if CONFIG_FILE.exists() else None
|
|
|
|
|
|
# Module-level cached config
|
|
_config: Config | None = None
|
|
|
|
|
|
def get_config() -> Config:
|
|
"""Get cached config, loading if necessary."""
|
|
global _config
|
|
if _config is None:
|
|
_config = load_config()
|
|
return _config
|