117 lines
3.6 KiB
Python
117 lines
3.6 KiB
Python
"""Configuration loading utilities."""
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
|
|
from nanobot.config.schema import Config
|
|
|
|
|
|
def get_config_path() -> Path:
|
|
"""Get the default configuration file path."""
|
|
return Path.home() / ".nanobot" / "config.json"
|
|
|
|
|
|
def get_data_dir() -> Path:
|
|
"""Get the nanobot data directory."""
|
|
from nanobot.utils.helpers import get_data_path
|
|
return get_data_path()
|
|
|
|
|
|
def _load_env_file(workspace: Path | None = None) -> None:
|
|
"""Load .env file from workspace directory if it exists."""
|
|
if workspace:
|
|
env_file = Path(workspace) / ".env"
|
|
else:
|
|
# Try current directory and workspace
|
|
env_file = Path(".env")
|
|
if not env_file.exists():
|
|
# Try workspace directory
|
|
try:
|
|
from nanobot.utils.helpers import get_workspace_path
|
|
workspace_path = get_workspace_path()
|
|
env_file = workspace_path / ".env"
|
|
except:
|
|
pass
|
|
|
|
if env_file.exists():
|
|
try:
|
|
with open(env_file) as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
# Skip comments and empty lines
|
|
if not line or line.startswith("#"):
|
|
continue
|
|
# Parse KEY=VALUE format
|
|
if "=" in line:
|
|
key, value = line.split("=", 1)
|
|
key = key.strip()
|
|
value = value.strip().strip('"').strip("'")
|
|
# Only set if not already in environment
|
|
if key and key not in os.environ:
|
|
os.environ[key] = value
|
|
except Exception:
|
|
# Silently fail if .env can't be loaded
|
|
pass
|
|
|
|
|
|
def load_config(config_path: Path | None = None) -> Config:
|
|
"""
|
|
Load configuration from file or create default.
|
|
|
|
Args:
|
|
config_path: Optional path to config file. Uses default if not provided.
|
|
|
|
Returns:
|
|
Loaded configuration object.
|
|
"""
|
|
# Load .env file before loading config (so env vars are available to pydantic)
|
|
try:
|
|
from nanobot.utils.helpers import get_workspace_path
|
|
workspace = get_workspace_path()
|
|
_load_env_file(workspace)
|
|
except:
|
|
# Fallback to current directory
|
|
_load_env_file()
|
|
|
|
path = config_path or get_config_path()
|
|
|
|
if path.exists():
|
|
try:
|
|
with open(path) as f:
|
|
data = json.load(f)
|
|
data = _migrate_config(data)
|
|
return Config.model_validate(data)
|
|
except (json.JSONDecodeError, ValueError) as e:
|
|
print(f"Warning: Failed to load config from {path}: {e}")
|
|
print("Using default configuration.")
|
|
|
|
return Config()
|
|
|
|
|
|
def save_config(config: Config, config_path: Path | None = None) -> None:
|
|
"""
|
|
Save configuration to file.
|
|
|
|
Args:
|
|
config: Configuration to save.
|
|
config_path: Optional path to save to. Uses default if not provided.
|
|
"""
|
|
path = config_path or get_config_path()
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
data = config.model_dump(by_alias=True)
|
|
|
|
with open(path, "w") as f:
|
|
json.dump(data, f, indent=2)
|
|
|
|
|
|
def _migrate_config(data: dict) -> dict:
|
|
"""Migrate old config formats to current."""
|
|
# Move tools.exec.restrictToWorkspace → tools.restrictToWorkspace
|
|
tools = data.get("tools", {})
|
|
exec_cfg = tools.get("exec", {})
|
|
if "restrictToWorkspace" in exec_cfg and "restrictToWorkspace" not in tools:
|
|
tools["restrictToWorkspace"] = exec_cfg.pop("restrictToWorkspace")
|
|
return data
|