1use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Serialize, Deserialize, Default)]
12pub struct DaemonConfig {
13 #[serde(default)]
14 pub daemon: DaemonSettings,
15 #[serde(default)]
16 pub server: ServerSettings,
17 #[serde(default)]
18 pub identity: IdentitySettings,
19 #[serde(default)]
20 pub privacy: PrivacySettings,
21 #[serde(default)]
22 pub watchers: WatcherSettings,
23 #[serde(default)]
24 pub git_storage: GitStorageSettings,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct DaemonSettings {
29 #[serde(default = "default_false")]
30 pub auto_publish: bool,
31 #[serde(default = "default_debounce")]
32 pub debounce_secs: u64,
33 #[serde(default = "default_publish_on")]
34 pub publish_on: PublishMode,
35 #[serde(default = "default_max_retries")]
36 pub max_retries: u32,
37 #[serde(default = "default_health_check_interval")]
38 pub health_check_interval_secs: u64,
39 #[serde(default = "default_realtime_debounce_ms")]
40 pub realtime_debounce_ms: u64,
41 #[serde(default = "default_detail_realtime_preview_enabled")]
43 pub detail_realtime_preview_enabled: bool,
44 #[serde(default)]
46 pub stream_write: Vec<String>,
47 #[serde(default = "default_summary_enabled")]
49 pub summary_enabled: bool,
50 #[serde(default)]
53 pub summary_provider: Option<String>,
54 #[serde(default)]
56 pub summary_model: Option<String>,
57 #[serde(default = "default_summary_content_mode")]
59 pub summary_content_mode: String,
60 #[serde(default = "default_summary_disk_cache_enabled")]
62 pub summary_disk_cache_enabled: bool,
63 #[serde(default)]
65 pub summary_openai_compat_endpoint: Option<String>,
66 #[serde(default)]
68 pub summary_openai_compat_base: Option<String>,
69 #[serde(default)]
71 pub summary_openai_compat_path: Option<String>,
72 #[serde(default)]
74 pub summary_openai_compat_style: Option<String>,
75 #[serde(default)]
77 pub summary_openai_compat_key: Option<String>,
78 #[serde(default)]
80 pub summary_openai_compat_key_header: Option<String>,
81 #[serde(default = "default_summary_event_window")]
83 pub summary_event_window: u32,
84 #[serde(default = "default_false")]
86 pub summary_window_migrated_v2: bool,
87 #[serde(default = "default_summary_debounce_ms")]
89 pub summary_debounce_ms: u64,
90 #[serde(default = "default_summary_max_inflight")]
92 pub summary_max_inflight: u32,
93}
94
95impl Default for DaemonSettings {
96 fn default() -> Self {
97 Self {
98 auto_publish: false,
99 debounce_secs: 5,
100 publish_on: PublishMode::Manual,
101 max_retries: 3,
102 health_check_interval_secs: 300,
103 realtime_debounce_ms: 500,
104 detail_realtime_preview_enabled: false,
105 stream_write: Vec::new(),
106 summary_enabled: true,
107 summary_provider: None,
108 summary_model: None,
109 summary_content_mode: "normal".to_string(),
110 summary_disk_cache_enabled: true,
111 summary_openai_compat_endpoint: None,
112 summary_openai_compat_base: None,
113 summary_openai_compat_path: None,
114 summary_openai_compat_style: None,
115 summary_openai_compat_key: None,
116 summary_openai_compat_key_header: None,
117 summary_event_window: 0,
118 summary_window_migrated_v2: false,
119 summary_debounce_ms: 1200,
120 summary_max_inflight: 1,
121 }
122 }
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
126#[serde(rename_all = "snake_case")]
127pub enum PublishMode {
128 SessionEnd,
129 Realtime,
130 Manual,
131}
132
133impl PublishMode {
134 pub fn cycle(&self) -> Self {
135 match self {
136 Self::SessionEnd => Self::Realtime,
137 Self::Realtime => Self::Manual,
138 Self::Manual => Self::SessionEnd,
139 }
140 }
141
142 pub fn display(&self) -> &'static str {
143 match self {
144 Self::SessionEnd => "Session End",
145 Self::Realtime => "Realtime",
146 Self::Manual => "Manual",
147 }
148 }
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct ServerSettings {
153 #[serde(default = "default_server_url")]
154 pub url: String,
155 #[serde(default)]
156 pub api_key: String,
157}
158
159impl Default for ServerSettings {
160 fn default() -> Self {
161 Self {
162 url: default_server_url(),
163 api_key: String::new(),
164 }
165 }
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct IdentitySettings {
170 #[serde(default = "default_nickname")]
171 pub nickname: String,
172 #[serde(default)]
174 pub team_id: String,
175}
176
177impl Default for IdentitySettings {
178 fn default() -> Self {
179 Self {
180 nickname: default_nickname(),
181 team_id: String::new(),
182 }
183 }
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize)]
187pub struct PrivacySettings {
188 #[serde(default = "default_true")]
189 pub strip_paths: bool,
190 #[serde(default = "default_true")]
191 pub strip_env_vars: bool,
192 #[serde(default = "default_exclude_patterns")]
193 pub exclude_patterns: Vec<String>,
194 #[serde(default)]
195 pub exclude_tools: Vec<String>,
196}
197
198impl Default for PrivacySettings {
199 fn default() -> Self {
200 Self {
201 strip_paths: true,
202 strip_env_vars: true,
203 exclude_patterns: default_exclude_patterns(),
204 exclude_tools: Vec::new(),
205 }
206 }
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize)]
210pub struct WatcherSettings {
211 #[serde(default = "default_true")]
212 pub claude_code: bool,
213 #[serde(default = "default_true")]
214 pub opencode: bool,
215 #[serde(default)]
216 pub cursor: bool,
217 #[serde(default)]
218 pub custom_paths: Vec<String>,
219}
220
221impl Default for WatcherSettings {
222 fn default() -> Self {
223 Self {
224 claude_code: true,
225 opencode: true,
226 cursor: false,
227 custom_paths: Vec::new(),
228 }
229 }
230}
231
232#[derive(Debug, Clone, Serialize, Deserialize)]
233pub struct GitStorageSettings {
234 #[serde(default)]
235 pub method: GitStorageMethod,
236 #[serde(default)]
237 pub token: String,
238}
239
240impl Default for GitStorageSettings {
241 fn default() -> Self {
242 Self {
243 method: GitStorageMethod::None,
244 token: String::new(),
245 }
246 }
247}
248
249#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
250#[serde(rename_all = "snake_case")]
251pub enum GitStorageMethod {
252 PlatformApi,
253 Native,
255 #[default]
256 #[serde(other)]
257 None,
258}
259
260fn default_true() -> bool {
263 true
264}
265fn default_false() -> bool {
266 false
267}
268fn default_debounce() -> u64 {
269 5
270}
271fn default_max_retries() -> u32 {
272 3
273}
274fn default_health_check_interval() -> u64 {
275 300
276}
277fn default_realtime_debounce_ms() -> u64 {
278 500
279}
280fn default_detail_realtime_preview_enabled() -> bool {
281 false
282}
283fn default_summary_enabled() -> bool {
284 true
285}
286fn default_summary_content_mode() -> String {
287 "normal".to_string()
288}
289fn default_summary_disk_cache_enabled() -> bool {
290 true
291}
292fn default_summary_event_window() -> u32 {
293 0
294}
295fn default_summary_debounce_ms() -> u64 {
296 1200
297}
298fn default_summary_max_inflight() -> u32 {
299 1
300}
301fn default_publish_on() -> PublishMode {
302 PublishMode::Manual
303}
304fn default_server_url() -> String {
305 "https://opensession.io".to_string()
306}
307fn default_nickname() -> String {
308 "user".to_string()
309}
310fn default_exclude_patterns() -> Vec<String> {
311 vec![
312 "*.env".to_string(),
313 "*secret*".to_string(),
314 "*credential*".to_string(),
315 ]
316}