agtrace_runtime/client/
workspace.rs

1use crate::client::{InsightOps, MonitorBuilder, ProjectOps, SessionOps, WatchService};
2use crate::config::Config;
3use crate::init::{InitConfig, InitProgress, InitResult, InitService};
4use crate::ops::{CheckResult, DoctorService, InspectResult};
5use agtrace_engine::DiagnoseResult;
6use agtrace_index::Database;
7use agtrace_providers::ProviderAdapter;
8use anyhow::Result;
9use std::path::PathBuf;
10use std::sync::{Arc, Mutex};
11
12pub struct AgTrace {
13    db: Arc<Mutex<Database>>,
14    config: Arc<Config>,
15    provider_configs: Arc<Vec<(String, PathBuf)>>,
16}
17
18impl AgTrace {
19    pub fn setup<F>(config: InitConfig, progress_fn: Option<F>) -> Result<InitResult>
20    where
21        F: FnMut(InitProgress),
22    {
23        InitService::run(config, progress_fn)
24    }
25
26    pub fn open(data_dir: PathBuf) -> Result<Self> {
27        let db_path = data_dir.join("agtrace.db");
28        let config_path = data_dir.join("config.toml");
29
30        let db = Database::open(&db_path).map_err(|e| {
31            if !db_path.exists() {
32                anyhow::anyhow!(
33                    "Database not found. Please run 'agtrace init' to initialize the workspace.\n\
34                     Database path: {}",
35                    db_path.display()
36                )
37            } else {
38                e
39            }
40        })?;
41
42        let config = if config_path.exists() {
43            Config::load_from(&config_path)?
44        } else {
45            let detected = Config::detect_providers()?;
46            detected.save_to(&config_path)?;
47            detected
48        };
49
50        let provider_configs: Vec<(String, PathBuf)> = config
51            .enabled_providers()
52            .into_iter()
53            .map(|(name, cfg)| (name.clone(), cfg.log_root.clone()))
54            .collect();
55
56        Ok(Self {
57            db: Arc::new(Mutex::new(db)),
58            config: Arc::new(config),
59            provider_configs: Arc::new(provider_configs),
60        })
61    }
62
63    pub fn diagnose(&self) -> Result<Vec<DiagnoseResult>> {
64        let providers: Vec<(ProviderAdapter, PathBuf)> = self
65            .provider_configs
66            .iter()
67            .filter_map(|(name, path)| {
68                agtrace_providers::create_adapter(name)
69                    .ok()
70                    .map(|p| (p, path.clone()))
71            })
72            .collect();
73        DoctorService::diagnose_all(&providers)
74    }
75
76    pub fn projects(&self) -> ProjectOps {
77        ProjectOps::new(self.db.clone(), self.provider_configs.clone())
78    }
79
80    pub fn sessions(&self) -> SessionOps {
81        SessionOps::new(self.db.clone(), self.provider_configs.clone())
82    }
83
84    pub fn insights(&self) -> InsightOps {
85        InsightOps::new(self.db.clone(), self.provider_configs.clone())
86    }
87
88    pub fn watch_service(&self) -> WatchService {
89        WatchService::new(
90            self.db.clone(),
91            self.config.clone(),
92            self.provider_configs.clone(),
93        )
94    }
95
96    pub fn workspace_monitor(&self) -> Result<MonitorBuilder> {
97        Ok(MonitorBuilder::new(
98            self.db.clone(),
99            self.provider_configs.clone(),
100        ))
101    }
102
103    pub fn database(&self) -> Arc<Mutex<Database>> {
104        self.db.clone()
105    }
106
107    pub fn config(&self) -> &Config {
108        &self.config
109    }
110
111    pub fn check_file(
112        file_path: &str,
113        provider: &ProviderAdapter,
114        provider_name: &str,
115    ) -> Result<CheckResult> {
116        DoctorService::check_file(file_path, provider, provider_name)
117    }
118
119    pub fn inspect_file(file_path: &str, lines: usize, json_format: bool) -> Result<InspectResult> {
120        DoctorService::inspect_file(file_path, lines, json_format)
121    }
122}