Skip to main content

walrus_daemon/config/
mod.rs

1//! Daemon configuration loaded from TOML.
2
3pub use crate::hook::{
4    mcp::McpServerConfig,
5    memory::MemoryConfig,
6    os::{PermissionConfig, ToolPermission},
7    task::TasksConfig,
8};
9pub use ::model::{ModelConfig, ProviderConfig, ProviderManager};
10use anyhow::Result;
11pub use channel::ChannelConfig;
12pub use loader::{load_agents_dir, scaffold_config_dir};
13use serde::{Deserialize, Serialize};
14use std::collections::BTreeMap;
15pub use wcore::{
16    AgentConfig, HeartbeatConfig,
17    paths::{AGENTS_DIR, CONFIG_DIR, DATA_DIR, MEMORY_DB, SKILLS_DIR, SOCKET_PATH},
18};
19
20mod loader;
21
22/// Top-level daemon configuration.
23#[derive(Debug, Clone, Serialize, Deserialize, Default)]
24pub struct DaemonConfig {
25    /// The walrus daemon's own agent config (model, heartbeat).
26    #[serde(default)]
27    pub walrus: AgentConfig,
28    /// Model provider configurations (remote API endpoints).
29    #[serde(default)]
30    pub model: ModelConfig,
31    /// Channel configuration (Telegram bot).
32    #[serde(default)]
33    pub channel: ChannelConfig,
34    /// MCP server configurations.
35    #[serde(default)]
36    pub mcps: BTreeMap<String, McpServerConfig>,
37    /// Memory configuration.
38    #[serde(default)]
39    pub memory: MemoryConfig,
40    /// Task executor pool configuration.
41    #[serde(default)]
42    pub tasks: TasksConfig,
43    /// Per-agent configurations (name → config).
44    #[serde(default)]
45    pub agents: BTreeMap<String, AgentConfig>,
46    /// Permission configuration: global defaults + per-agent overrides.
47    #[serde(default)]
48    pub permissions: PermissionConfig,
49}
50
51impl DaemonConfig {
52    /// Parse a TOML string into a `DaemonConfig`.
53    pub fn from_toml(toml_str: &str) -> Result<Self> {
54        let mut config: Self = toml::from_str(toml_str)?;
55        config
56            .model
57            .providers
58            .iter_mut()
59            .for_each(|(key, provider)| {
60                if provider.model.is_empty() {
61                    provider.model = key.clone();
62                }
63            });
64        config.mcps.iter_mut().for_each(|(name, server)| {
65            if server.name.is_empty() {
66                server.name = name.clone().into();
67            }
68        });
69        if config.walrus.model.is_none() {
70            config.walrus.model = Some(::model::default_model().into());
71        }
72        Ok(config)
73    }
74
75    /// Load configuration from a file path.
76    pub fn load(path: &std::path::Path) -> Result<Self> {
77        let content = std::fs::read_to_string(path)?;
78        Self::from_toml(&content)
79    }
80}