wraith/src-tauri/src/lib.rs
Vantz Stockwell e28d0f65cd
Some checks failed
Build & Sign Wraith / Build Windows + Sign (push) Failing after 9s
feat: integrate Gemini AI XO copilot + backend cleanup + connection timeouts
Backend cleanup (Gemini):
- Strip verbose doc comments across SSH, RDP, and command modules
- Add 10s timeout on SSH connect/auth, 15s on RDP connection
- Fix macOS data directory to ~/Library/Application Support/Wraith
- Add generic disconnect_session command
- Simplify SFTP setup and error handling
- Inline AppState field construction

Gemini AI XO integration:
- Add GeminiService (src-tauri/src/ai/) with API Key, Service Account,
  and Google Account (OAuth2) authentication methods
- Add ai_commands (set_gemini_auth, gemini_chat, is_gemini_authenticated)
- Add GeminiPanel.vue — collapsible chat sidebar with multi-auth UI
- Wire Ctrl+Shift+G toggle and status bar AI button in MainLayout
- Add reqwest + anyhow dependencies

Bugfix:
- Fix dropped modulo operator in Ctrl+Tab/Ctrl+Shift+Tab handlers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:38:52 -04:00

109 lines
4.9 KiB
Rust

pub mod db;
pub mod vault;
pub mod settings;
pub mod connections;
pub mod credentials;
pub mod ssh;
pub mod sftp;
pub mod rdp;
pub mod theme;
pub mod workspace;
pub mod ai;
pub mod commands;
use std::path::PathBuf;
use std::sync::Mutex;
use db::Database;
use vault::VaultService;
use credentials::CredentialService;
use settings::SettingsService;
use connections::ConnectionService;
use sftp::SftpService;
use ssh::session::SshService;
use rdp::RdpService;
use theme::ThemeService;
use workspace::WorkspaceService;
pub struct AppState {
pub db: Database,
pub vault: Mutex<Option<VaultService>>,
pub settings: SettingsService,
pub connections: ConnectionService,
pub credentials: Mutex<Option<CredentialService>>,
pub ssh: SshService,
pub sftp: SftpService,
pub rdp: RdpService,
pub theme: ThemeService,
pub workspace: WorkspaceService,
pub gemini: Mutex<Option<ai::GeminiClient>>,
}
impl AppState {
pub fn new(data_dir: PathBuf) -> Result<Self, Box<dyn std::error::Error>> {
std::fs::create_dir_all(&data_dir)?;
let database = Database::open(&data_dir.join("wraith.db"))?;
database.migrate()?;
Ok(Self {
db: database.clone(),
vault: Mutex::new(None),
settings: SettingsService::new(database.clone()),
connections: ConnectionService::new(database.clone()),
credentials: Mutex::new(None),
ssh: SshService::new(database.clone()),
sftp: SftpService::new(),
rdp: RdpService::new(),
theme: ThemeService::new(database.clone()),
workspace: WorkspaceService::new(SettingsService::new(database.clone())),
gemini: Mutex::new(None),
})
}
pub fn is_first_run(&self) -> bool {
self.settings.get("vault_salt").unwrap_or_default().is_empty()
}
pub fn is_unlocked(&self) -> bool {
self.vault.lock().unwrap().is_some()
}
}
pub fn data_directory() -> PathBuf {
if let Ok(appdata) = std::env::var("APPDATA") { return PathBuf::from(appdata).join("Wraith"); }
if let Ok(home) = std::env::var("HOME") {
if cfg!(target_os = "macos") { return PathBuf::from(home).join("Library").join("Application Support").join("Wraith"); }
if let Ok(xdg) = std::env::var("XDG_DATA_HOME") { return PathBuf::from(xdg).join("wraith"); }
return PathBuf::from(home).join(".local").join("share").join("wraith");
}
PathBuf::from(".")
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
let app_state = AppState::new(data_directory()).expect("Failed to init AppState");
app_state.theme.seed_builtins();
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.manage(app_state)
.setup(|app| {
use tauri::Manager;
if let Some(window) = app.get_webview_window("main") { let _ = window.open_devtools(); }
Ok(())
})
.invoke_handler(tauri::generate_handler![
commands::vault::is_first_run, commands::vault::create_vault, commands::vault::unlock, commands::vault::is_unlocked,
commands::settings::get_setting, commands::settings::set_setting,
commands::connections::list_connections, commands::connections::create_connection, commands::connections::get_connection, commands::connections::update_connection, commands::connections::delete_connection,
commands::connections::list_groups, commands::connections::create_group, commands::connections::delete_group, commands::connections::rename_group, commands::connections::search_connections,
commands::credentials::list_credentials, commands::credentials::create_password, commands::credentials::create_ssh_key, commands::credentials::delete_credential, commands::credentials::decrypt_password, commands::credentials::decrypt_ssh_key,
commands::ssh_commands::connect_ssh, commands::ssh_commands::connect_ssh_with_key, commands::ssh_commands::ssh_write, commands::ssh_commands::ssh_resize, commands::ssh_commands::disconnect_ssh, commands::ssh_commands::disconnect_session, commands::ssh_commands::list_ssh_sessions,
commands::sftp_commands::sftp_list, commands::sftp_commands::sftp_read_file, commands::sftp_commands::sftp_write_file, commands::sftp_commands::sftp_mkdir, commands::sftp_commands::sftp_delete, commands::sftp_commands::sftp_rename,
commands::rdp_commands::connect_rdp, commands::rdp_commands::rdp_get_frame, commands::rdp_commands::rdp_send_mouse, commands::rdp_commands::rdp_send_key, commands::rdp_commands::disconnect_rdp, commands::rdp_commands::list_rdp_sessions,
commands::theme_commands::list_themes, commands::theme_commands::get_theme,
commands::ai_commands::set_gemini_auth, commands::ai_commands::gemini_chat, commands::ai_commands::is_gemini_authenticated,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}