Skip to main content

crabtalk_core/config/
daemon.rs

1//! Daemon configuration loaded from `config.toml`.
2
3use crate::{ProviderDef, config::system::TasksConfig};
4use anyhow::Result;
5use serde::{Deserialize, Serialize};
6use std::collections::BTreeMap;
7
8/// Top-level daemon configuration (`config.toml`).
9///
10/// Holds immutable per-install settings: providers, task executor pool,
11/// and env vars passed to MCP processes. Mutable runtime records (MCPs,
12/// agents) live in [`crate::storage::Storage`]. Per-agent customization
13/// (hooks, etc.) lives directly on each [`crate::AgentConfig`].
14#[derive(Debug, Clone, Serialize, Deserialize, Default)]
15pub struct DaemonConfig {
16    /// Provider definitions (`[provider.<name>]`).
17    #[serde(default)]
18    pub provider: BTreeMap<String, ProviderDef>,
19    /// Task executor pool configuration (`[tasks]`).
20    #[serde(default)]
21    pub tasks: TasksConfig,
22    /// Environment variables passed to all MCP server processes.
23    #[serde(default)]
24    pub env: BTreeMap<String, String>,
25}
26
27impl DaemonConfig {
28    /// Parse a TOML string into a `DaemonConfig`.
29    pub fn from_toml(toml_str: &str) -> Result<Self> {
30        let config: Self = toml::from_str(toml_str)?;
31        validate_providers(&config.provider)?;
32        Ok(config)
33    }
34
35    /// Load configuration from a file path.
36    pub fn load(path: &std::path::Path) -> Result<Self> {
37        let content = std::fs::read_to_string(path)?;
38        Self::from_toml(&content)
39    }
40}
41
42/// Validate provider definitions and reject duplicate model names.
43pub fn validate_providers(providers: &BTreeMap<String, ProviderDef>) -> Result<()> {
44    let mut seen = std::collections::HashSet::new();
45    for (name, def) in providers {
46        def.validate(name).map_err(|e| anyhow::anyhow!(e))?;
47        for model in &def.models {
48            if !seen.insert(model.clone()) {
49                anyhow::bail!("duplicate model name '{model}' across providers");
50            }
51        }
52    }
53    Ok(())
54}