1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4use serde_json::{Map, Value};
5
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
7#[serde(rename_all = "lowercase")]
8pub enum BackendType {
9 Pty,
10 Tmux,
11 Zellij,
12}
13
14impl Default for BackendType {
15 fn default() -> Self {
16 Self::Tmux
17 }
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
21pub struct DaemonConfig {
22 #[serde(default)]
23 pub backend_type: BackendType,
24 #[serde(default)]
25 pub quiet_restart: bool,
26 #[serde(default = "default_working_dirs")]
27 pub working_dirs: Vec<String>,
28}
29
30fn default_working_dirs() -> Vec<String> {
31 vec!["~".to_string()]
32}
33
34impl Default for DaemonConfig {
35 fn default() -> Self {
36 Self {
37 backend_type: BackendType::default(),
38 quiet_restart: false,
39 working_dirs: default_working_dirs(),
40 }
41 }
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
45pub struct WebConfig {
46 #[serde(default = "default_web_host")]
47 pub host: String,
48 #[serde(default = "default_proxy_base_port")]
49 pub proxy_base_port: u16,
50}
51
52fn default_web_host() -> String {
53 "0.0.0.0".to_string()
54}
55
56fn default_proxy_base_port() -> u16 {
57 8800
58}
59
60impl Default for WebConfig {
61 fn default() -> Self {
62 Self {
63 host: default_web_host(),
64 proxy_base_port: default_proxy_base_port(),
65 }
66 }
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
70pub struct Config {
71 #[serde(default)]
72 pub daemon: DaemonConfig,
73 #[serde(default)]
74 pub web: WebConfig,
75 #[serde(default)]
76 pub lark: LarkConfig,
77 #[serde(default, rename = "screenAnalyzer")]
78 pub screen_analyzer: ScreenAnalyzerConfig,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
82pub struct BotConfig {
83 #[serde(default)]
84 pub name: Option<String>,
85 #[serde(rename = "larkAppId")]
86 pub lark_app_id: String,
87 #[serde(rename = "larkAppSecret")]
88 pub lark_app_secret: String,
89 #[serde(rename = "cliId")]
90 pub cli_id: String,
91 #[serde(rename = "cliBin", default)]
92 pub cli_bin: Option<String>,
93 #[serde(default)]
94 pub model: Option<String>,
95 #[serde(rename = "workingDir", default)]
96 pub working_dir: Option<String>,
97 #[serde(rename = "backendType", default)]
98 pub backend_type: Option<BackendType>,
99 #[serde(rename = "larkEncryptKey", default)]
100 pub lark_encrypt_key: Option<String>,
101 #[serde(rename = "larkVerificationToken", default)]
102 pub lark_verification_token: Option<String>,
103 #[serde(rename = "allowedUsers", default)]
104 pub allowed_users: Vec<String>,
105 #[serde(rename = "privateCard", default)]
106 pub private_card: bool,
107 #[serde(rename = "allowedChatGroups", default)]
108 pub allowed_chat_groups: Vec<String>,
109 #[serde(rename = "chatGrants", default)]
110 pub chat_grants: std::collections::HashMap<String, Vec<String>>,
111 #[serde(rename = "globalGrants", default)]
112 pub global_grants: Vec<String>,
113 #[serde(rename = "oncallChats", default)]
114 pub oncall_chats: Vec<OncallChatBinding>,
115 #[serde(rename = "restrictGrantCommands", default)]
116 pub restrict_grant_commands: bool,
117 #[serde(rename = "messageQuota", default)]
118 pub message_quota: Option<MessageQuotaConfig>,
119 #[serde(rename = "quotaState", default)]
120 pub quota_state: std::collections::HashMap<String, QuotaEntry>,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
124pub struct OncallChatBinding {
125 #[serde(rename = "chatId")]
126 pub chat_id: String,
127 #[serde(rename = "workingDir", default)]
128 pub working_dir: Option<String>,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
132pub struct MessageQuotaConfig {
133 #[serde(rename = "defaultLimit", default)]
134 pub default_limit: Option<u32>,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
138pub struct QuotaEntry {
139 pub limit: u32,
140 pub used: u32,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
144pub struct LarkConfig {
145 #[serde(default = "default_lark_event_mode")]
146 pub event_mode: String,
147 #[serde(default)]
148 pub verification_token: Option<String>,
149 #[serde(default)]
150 pub encrypt_key: Option<String>,
151}
152
153#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
154pub struct ScreenAnalyzerConfig {
155 #[serde(default)]
156 pub enabled: bool,
157 #[serde(default)]
158 pub base_url: String,
159 #[serde(default)]
160 pub api_key: String,
161 #[serde(default)]
162 pub model: String,
163 #[serde(default = "default_screen_analyzer_interval_ms")]
164 pub interval_ms: u64,
165 #[serde(default = "default_screen_analyzer_stable_count")]
166 pub stable_count: u32,
167 #[serde(default = "default_screen_analyzer_snapshot_max_chars")]
168 pub snapshot_max_chars: usize,
169 #[serde(default)]
170 pub extra_headers: HashMap<String, String>,
171 #[serde(default)]
172 pub extra_body: Map<String, Value>,
173}
174
175fn default_screen_analyzer_interval_ms() -> u64 {
176 2_000
177}
178
179fn default_screen_analyzer_stable_count() -> u32 {
180 6
181}
182
183fn default_screen_analyzer_snapshot_max_chars() -> usize {
184 8_000
185}
186
187impl Default for ScreenAnalyzerConfig {
188 fn default() -> Self {
189 Self {
190 enabled: false,
191 base_url: String::new(),
192 api_key: String::new(),
193 model: String::new(),
194 interval_ms: default_screen_analyzer_interval_ms(),
195 stable_count: default_screen_analyzer_stable_count(),
196 snapshot_max_chars: default_screen_analyzer_snapshot_max_chars(),
197 extra_headers: HashMap::new(),
198 extra_body: Map::new(),
199 }
200 }
201}
202
203fn default_lark_event_mode() -> String {
204 "http".to_string()
205}
206
207impl Default for LarkConfig {
208 fn default() -> Self {
209 Self {
210 event_mode: default_lark_event_mode(),
211 verification_token: None,
212 encrypt_key: None,
213 }
214 }
215}