diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index e80342e..403cdd4 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -9,6 +9,7 @@ pub mod audio; pub mod briefs; pub mod calendar; pub mod commands; +pub mod deploy; pub mod diarization; pub mod error; pub mod hardware; @@ -155,6 +156,14 @@ pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_dialog::init()) + // Opt-in launch-at-login (NFR-RES-4). The macOS launcher arg is required + // by the signature but unused on Windows, where enable/disable writes a + // per-user HKCU\...\Run entry (no admin). Off until the user (or an + // enterprise deploy file) turns `auto_start` on. + .plugin(tauri_plugin_autostart::init( + tauri_plugin_autostart::MacosLauncher::LaunchAgent, + None, + )) // In-memory streaming of recordings for the player (FR-REC-5): decrypts // on the fly so no plaintext audio is ever written to disk. .register_uri_scheme_protocol("waaudio", |_ctx, request| { @@ -172,6 +181,53 @@ pub fn run() { .build(app)?; app.manage(TrayHandle(tray)); + // First-run enterprise deploy seeding (deploy.rs): if no settings.json + // exists yet and an admin dropped a wa-defaults.ini, seed settings once + // and optionally fetch the configured model in the background. One-shot + // — guarded by the settings file's absence, so it never re-runs and adds + // nothing to idle cost (NFR-RES-1). + if !crate::paths::settings_path().exists() { + if let Some((seeded, model_to_download)) = deploy::seed_settings_from_defaults() { + match commands::save_settings(&seeded) { + Ok(()) => { + tracing::info!("seeded settings.json from wa-defaults.ini"); + if let Some(id) = model_to_download { + let app_handle = app.handle().clone(); + tauri::async_runtime::spawn(async move { + if let Err(e) = commands::download_model( + app_handle, + commands::DownloadModelArgs { + kind: "whisper".into(), + id, + }, + ) + .await + { + tracing::warn!("deploy auto-download of model failed: {e:?}"); + } + }); + } + } + Err(e) => { + tracing::error!("first-run deploy seeding failed to write settings: {e:?}") + } + } + } + } + + // Reconcile launch-at-login with the persisted preference (NFR-RES-4): + // if the user opted in but the OS entry is missing (e.g. after a + // reinstall or a deploy file that set auto_start), restore it. One-shot. + { + use tauri_plugin_autostart::ManagerExt; + let manager = app.autolaunch(); + if commands::load_settings().auto_start && !manager.is_enabled().unwrap_or(false) { + if let Err(e) = manager.enable() { + tracing::warn!("failed to restore auto-start entry: {e}"); + } + } + } + // Reminders (Phase 8, T8.6, FR-CAL-5) are Windows-scheduled toasts, not // an app-side timer — Windows itself is what's "polling", so this stays // within NFR-RES-1. init() just registers the AppUserModelID. @@ -283,6 +339,7 @@ pub fn run() { commands::list_audio_devices, commands::list_input_devices, commands::set_preferred_backend, + commands::set_auto_start, commands::list_models, commands::list_whisper_languages, commands::download_npu_package,