Skip to main content

seher/config/
mod.rs

1//! Minimal provider config types used as the bridge into [`crate::agent::Agent`].
2//!
3//! The resolver synthesizes an [`AgentConfig`] on the fly so the existing
4//! cookie-based limit checkers in [`crate::agent::Agent::check_limit`] keep
5//! working unchanged. This module therefore exposes only the minimal shape
6//! required by that dispatch.
7
8use std::collections::HashMap;
9
10/// Provider field state:
11/// - `None` (`Option::None`) → infer provider from the command name
12/// - `Some(Explicit(name))` → use that provider name
13/// - `Some(Null)` → explicitly no provider (cookie-less fallback)
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum ProviderConfig {
16    Explicit(String),
17    /// Maps to the YAML `provider: null` case — no provider, no cookie check.
18    Null,
19}
20
21#[derive(Debug, Clone)]
22pub struct AgentConfig {
23    pub command: String,
24    pub env: Option<HashMap<String, String>>,
25    pub provider: Option<ProviderConfig>,
26    pub openrouter_management_key: Option<String>,
27    pub glm_api_key: Option<String>,
28}
29
30fn command_to_provider(command: &str) -> Option<&str> {
31    match command {
32        "claude" => Some("claude"),
33        "codex" => Some("codex"),
34        "copilot" => Some("copilot"),
35        "glm" => Some("glm"),
36        "zai" => Some("zai"),
37        "kimi-k2" => Some("kimi-k2"),
38        "warp" => Some("warp"),
39        "kiro" => Some("kiro"),
40        _ => None,
41    }
42}
43
44fn resolve_provider<'a>(command: &'a str, provider: Option<&'a ProviderConfig>) -> Option<&'a str> {
45    match provider {
46        Some(ProviderConfig::Explicit(name)) => Some(name.as_str()),
47        Some(ProviderConfig::Null) => None,
48        None => command_to_provider(command),
49    }
50}
51
52impl AgentConfig {
53    #[must_use]
54    pub fn resolve_provider(&self) -> Option<&str> {
55        resolve_provider(&self.command, self.provider.as_ref())
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    #[test]
64    fn explicit_provider_passes_through() {
65        let cfg = AgentConfig {
66            command: "myai".to_string(),
67            env: None,
68            provider: Some(ProviderConfig::Explicit("copilot".to_string())),
69            openrouter_management_key: None,
70            glm_api_key: None,
71        };
72        assert_eq!(cfg.resolve_provider(), Some("copilot"));
73    }
74
75    #[test]
76    fn null_provider_returns_none() {
77        let cfg = AgentConfig {
78            command: "claude".to_string(),
79            env: None,
80            provider: Some(ProviderConfig::Null),
81            openrouter_management_key: None,
82            glm_api_key: None,
83        };
84        assert_eq!(cfg.resolve_provider(), None);
85    }
86
87    #[test]
88    fn inferred_provider_falls_back_to_command() {
89        let cfg = AgentConfig {
90            command: "claude".to_string(),
91            env: None,
92            provider: None,
93            openrouter_management_key: None,
94            glm_api_key: None,
95        };
96        assert_eq!(cfg.resolve_provider(), Some("claude"));
97    }
98}