1use serde::{Deserialize, Serialize};
2
3#[cfg(feature = "schema")]
4use schemars::JsonSchema;
5
6use crate::openrouter_oauth::OpenRouterOAuthConfig;
7
8#[derive(Debug, Clone, Default, Serialize, Deserialize)]
9#[cfg_attr(feature = "schema", derive(JsonSchema))]
10pub struct AuthConfig {
11 #[serde(default)]
12 pub openrouter: OpenRouterOAuthConfig,
13 #[serde(default)]
14 pub openai: OpenAIAuthConfig,
15 #[serde(default)]
16 pub copilot: CopilotAuthConfig,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
20#[cfg_attr(feature = "schema", derive(JsonSchema))]
21#[serde(default)]
22pub struct CopilotAuthConfig {
23 pub command: Option<String>,
24 pub host: Option<String>,
25 #[serde(default = "default_copilot_available_tools")]
26 pub available_tools: Vec<String>,
27 #[serde(default)]
28 pub excluded_tools: Vec<String>,
29 #[serde(default = "default_vtcode_tool_allowlist")]
30 pub vtcode_tool_allowlist: Vec<String>,
31 pub startup_timeout_secs: u64,
32 pub auth_timeout_secs: u64,
33}
34
35impl Default for CopilotAuthConfig {
36 fn default() -> Self {
37 Self {
38 command: None,
39 host: None,
40 available_tools: default_copilot_available_tools(),
41 excluded_tools: Vec::new(),
42 vtcode_tool_allowlist: default_vtcode_tool_allowlist(),
43 startup_timeout_secs: 20,
44 auth_timeout_secs: 300,
45 }
46 }
47}
48
49fn default_copilot_available_tools() -> Vec<String> {
50 ["view", "glob", "grep"]
51 .into_iter()
52 .map(str::to_string)
53 .collect()
54}
55
56fn default_vtcode_tool_allowlist() -> Vec<String> {
57 ["exec_command", "write_stdin", "apply_patch", "code_search"]
58 .into_iter()
59 .map(str::to_string)
60 .collect()
61}
62
63#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
64#[cfg_attr(feature = "schema", derive(JsonSchema))]
65#[serde(rename_all = "snake_case")]
66pub enum OpenAIPreferredMethod {
67 #[default]
68 Auto,
69 ApiKey,
70 Chatgpt,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
74#[cfg_attr(feature = "schema", derive(JsonSchema))]
75#[serde(default)]
76pub struct OpenAIAuthConfig {
77 pub preferred_method: OpenAIPreferredMethod,
78 pub callback_port: u16,
79 pub auto_refresh: bool,
80 pub flow_timeout_secs: u64,
81}
82
83impl Default for OpenAIAuthConfig {
84 fn default() -> Self {
85 Self {
86 preferred_method: OpenAIPreferredMethod::Auto,
87 callback_port: 1455,
88 auto_refresh: true,
89 flow_timeout_secs: 300,
90 }
91 }
92}
93
94impl OpenAIPreferredMethod {
95 #[must_use]
96 pub fn as_str(self) -> &'static str {
97 match self {
98 Self::Auto => "auto",
99 Self::ApiKey => "api_key",
100 Self::Chatgpt => "chatgpt",
101 }
102 }
103}
104
105impl OpenAIAuthConfig {
106 #[must_use]
107 pub fn callback_url(&self) -> String {
108 format!("http://localhost:{}/callback", self.callback_port)
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn openai_defaults_match_expected_values() {
118 let config = OpenAIAuthConfig::default();
119 assert_eq!(config.preferred_method, OpenAIPreferredMethod::Auto);
120 assert_eq!(config.callback_port, 1455);
121 assert!(config.auto_refresh);
122 assert_eq!(config.flow_timeout_secs, 300);
123 }
124
125 #[test]
126 fn copilot_defaults_match_expected_values() {
127 let config = CopilotAuthConfig::default();
128 assert!(config.command.is_none());
129 assert!(config.host.is_none());
130 assert_eq!(config.available_tools, vec!["view", "glob", "grep"]);
131 assert!(config.excluded_tools.is_empty());
132 assert_eq!(
133 config.vtcode_tool_allowlist,
134 vec!["exec_command", "write_stdin", "apply_patch", "code_search"]
135 );
136 assert_eq!(config.startup_timeout_secs, 20);
137 assert_eq!(config.auth_timeout_secs, 300);
138 }
139}