mod commands; mod db; mod error; mod models; mod services; use std::sync::{Arc, Mutex}; use tauri::Manager; pub struct AppState { pub db: Arc>, pub encryption_key: [u8; 32], pub http_client: reqwest::Client, } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_notification::init()) .setup(|app| { let db_dir = app.path().app_data_dir()?; std::fs::create_dir_all(&db_dir)?; let db_path = db_dir.join("orchai.db"); let conn = db::init(&db_path).expect("Failed to initialize database"); let key_path = db_dir.join("orchai.key"); let encryption_key = load_or_generate_key(&key_path)?; let http_client = reqwest::Client::new(); let db_arc = Arc::new(Mutex::new(conn)); app.manage(AppState { db: db_arc.clone(), encryption_key, http_client: http_client.clone(), }); // Start background poller services::poller::start( db_arc.clone(), encryption_key, http_client, app.handle().clone(), ); // Start agent orchestrator services::orchestrator::start( db_arc, app.handle().clone(), ); Ok(()) }) .invoke_handler(tauri::generate_handler![ commands::project::create_project, commands::project::list_projects, commands::project::get_project, commands::project::update_project, commands::project::delete_project, commands::credential::set_tuleap_credentials, commands::credential::get_tuleap_credentials, commands::credential::delete_tuleap_credentials, commands::credential::test_tuleap_connection, commands::tracker::add_tracker, commands::tracker::list_trackers, commands::tracker::update_tracker, commands::tracker::remove_tracker, commands::tracker::get_tracker_fields, commands::tracker::list_processed_tickets, commands::poller::manual_poll, commands::poller::get_queue_status, commands::notification::list_notifications, commands::notification::mark_notification_read, commands::notification::mark_all_notifications_read, commands::orchestrator::get_ticket_result, commands::orchestrator::retry_ticket, commands::orchestrator::cancel_ticket, commands::worktree::list_worktrees, commands::worktree::get_worktree_diff, commands::worktree::apply_fix_to_branch, commands::worktree::delete_worktree_cmd, commands::worktree::list_local_branches, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } fn load_or_generate_key(path: &std::path::Path) -> Result<[u8; 32], Box> { use rand::RngCore; if path.exists() { let bytes = std::fs::read(path)?; if bytes.len() != 32 { return Err("Invalid key file size".into()); } let mut key = [0u8; 32]; key.copy_from_slice(&bytes); Ok(key) } else { let mut key = [0u8; 32]; rand::rngs::OsRng.fill_bytes(&mut key); std::fs::write(path, key)?; Ok(key) } }