Download apps/bootstrap-installer/src-tauri/src/lib.rs from SaylorTwift/hermes-agent: direct link, hf CLI and curl.
- Browser
- Download file 9.1 kB
-
https://huggingface.co/SaylorTwift/hermes-agent/resolve/main/apps/bootstrap-installer/src-tauri/src/lib.rs
- Command line
-
hf download hf://SaylorTwift/hermes-agent/apps/bootstrap-installer/src-tauri/src/lib.rs
-
curl -L -o lib.rs https://huggingface.co/SaylorTwift/hermes-agent/resolve/main/apps/bootstrap-installer/src-tauri/src/lib.rs
9.1 kB
| //! Hermes Setup — Tauri entrypoint. | |
| //! | |
| //! Spawns a single window pointed at the React frontend (apps/bootstrap-installer/src/). | |
| //! All install-time work lives in `bootstrap.rs` and is invoked through the Tauri | |
| //! commands registered at the bottom of `run()`. | |
| //! | |
| //! The Windows-subsystem strip lives on the binary crate (src/main.rs), not | |
| //! here — a crate-level attribute on a lib doesn't propagate to the linker | |
| //! flags of the executable that consumes it. | |
| mod bootstrap; | |
| mod events; | |
| mod install_script; | |
| mod powershell; | |
| mod paths; | |
| mod update; | |
| use std::sync::Arc; | |
| use tokio::sync::Mutex; | |
| /// How the installer was invoked. Resolved once from the process args in | |
| /// `run()` and exposed to the frontend via `get_mode` so it can route to the | |
| /// install flow (first-run onboarding) or the update flow (driven by the | |
| /// desktop app handing off via `Hermes-Setup.exe --update`). | |
| /// | |
| /// Bare launch (double-click, first-run) => Install. | |
| /// `--update` (spawned by the desktop's "Update" button) => Update. | |
| pub enum AppMode { | |
| Install, | |
| Update, | |
| } | |
| impl AppMode { | |
| /// Resolve the mode from an argument iterator. Anything containing the | |
| /// `--update` flag selects Update; otherwise Install. Kept arg-iterator | |
| /// generic (not reading `std::env` directly) so it's unit-testable. | |
| pub fn from_args<I, S>(args: I) -> Self | |
| where | |
| I: IntoIterator<Item = S>, | |
| S: AsRef<str>, | |
| { | |
| for a in args { | |
| if a.as_ref() == "--update" { | |
| return AppMode::Update; | |
| } | |
| } | |
| AppMode::Install | |
| } | |
| } | |
| /// Returns true when the args request a forced installer UI (repair/reinstall) | |
| /// via `--reinstall` or `--repair`, which overrides the macOS launcher | |
| /// fast-path so a broken install can be repaired. Arg-iterator generic so it's | |
| /// unit-testable, mirroring `AppMode::from_args`. Independent of mode selection: | |
| /// these flags never flip Install<->Update. | |
| pub fn force_setup_from_args<I, S>(args: I) -> bool | |
| where | |
| I: IntoIterator<Item = S>, | |
| S: AsRef<str>, | |
| { | |
| args.into_iter() | |
| .any(|a| a.as_ref() == "--reinstall" || a.as_ref() == "--repair") | |
| } | |
| /// Process-wide install state, shared across Tauri commands. | |
| /// | |
| /// The bootstrap is a one-shot, single-tenant process — we only need one | |
| /// of these per window. `Arc<Mutex<...>>` lets command handlers grab it | |
| /// without lifetime gymnastics. | |
| pub struct AppState { | |
| pub bootstrap: Mutex<Option<bootstrap::BootstrapHandle>>, | |
| /// How this process was launched (install vs update). Immutable for the | |
| /// lifetime of the process; read by the `get_mode` command. | |
| pub mode: AppMode, | |
| } | |
| impl AppState { | |
| fn new(mode: AppMode) -> Self { | |
| Self { | |
| bootstrap: Mutex::new(None), | |
| mode, | |
| } | |
| } | |
| } | |
| /// Frontend → Rust: which flow should the UI render? | |
| fn get_mode(state: tauri::State<'_, Arc<AppState>>) -> AppMode { | |
| state.mode | |
| } | |
| pub fn run() { | |
| // Tracing → bootstrap-installer.log under HERMES_HOME/logs/ so install | |
| // failures leave a trail for support. Console output also goes here in | |
| // debug builds. | |
| let _guard = paths::init_logging(); | |
| let mode = AppMode::from_args(std::env::args().skip(1)); | |
| // Escape hatch: `--reinstall`/`--repair` forces the installer UI even when | |
| // Hermes is already installed, so users can re-run setup to repair a broken | |
| // install instead of the launcher fast path silently relaunching the app. | |
| let force_setup = force_setup_from_args(std::env::args().skip(1)); | |
| tracing::info!(?mode, force_setup, "Hermes installer starting"); | |
| tauri::Builder::default() | |
| .plugin(tauri_plugin_dialog::init()) | |
| .plugin(tauri_plugin_opener::init()) | |
| .plugin(tauri_plugin_process::init()) | |
| .plugin(tauri_plugin_shell::init()) | |
| .manage(Arc::new(AppState::new(mode))) | |
| .setup(move |app| { | |
| use tauri::Manager; | |
| // Launcher fast path (macOS only): a bare ("Install") launch when | |
| // Hermes is already installed should NOT show the installer or | |
| // rebuild — it should just open the app, so the /Applications | |
| // "Hermes" doubles as a normal launcher (first run installs, every | |
| // later run launches instantly). The window is kept hidden until | |
| // here via `"visible": false` so this path never flashes a window. | |
| // | |
| // Gated to macOS deliberately: on Windows/Linux the installer keeps | |
| // its existing behavior (Windows users relaunch via the Start | |
| // Menu/Desktop "Hermes" shortcuts that install.ps1 creates, and a | |
| // reliable detached relaunch there needs the DETACHED_PROCESS + | |
| // startup-grace handling used by launch_hermes_desktop — out of | |
| // scope here). So this is a pure no-op on non-macOS. | |
| // | |
| // `--reinstall`/`--repair` opts out so a broken install can be | |
| // repaired by re-running setup instead of launching the bad app. | |
| if cfg!(target_os = "macos") && mode == AppMode::Install && !force_setup { | |
| let install_root = paths::hermes_home().join("hermes-agent"); | |
| if bootstrap::hermes_is_installed(&install_root) { | |
| match bootstrap::spawn_installed_desktop(&install_root) { | |
| Ok(()) => { | |
| // Brief grace so the spawned app is registered | |
| // before we exit (mirrors launch_hermes_desktop). | |
| std::thread::sleep(std::time::Duration::from_millis(200)); | |
| tracing::info!( | |
| "hermes already installed — relaunched desktop; exiting installer" | |
| ); | |
| app.handle().exit(0); | |
| return Ok(()); | |
| } | |
| Err(err) => { | |
| tracing::warn!( | |
| ?err, | |
| "relaunch of installed desktop failed; showing installer UI" | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| // First run / repair install, or Update mode: reveal the UI. | |
| match app.get_webview_window("main") { | |
| Some(win) => { | |
| if let Err(err) = win.show() { | |
| tracing::error!(?err, "failed to show main installer window"); | |
| } | |
| } | |
| None => { | |
| tracing::error!("main installer window not found; installer UI will not appear"); | |
| } | |
| } | |
| Ok(()) | |
| }) | |
| .invoke_handler(tauri::generate_handler![ | |
| // Mode (install vs update) | |
| get_mode, | |
| // Bootstrap lifecycle | |
| bootstrap::start_bootstrap, | |
| bootstrap::cancel_bootstrap, | |
| bootstrap::get_bootstrap_status, | |
| // Update lifecycle | |
| update::start_update, | |
| // Hand-off | |
| bootstrap::launch_hermes_desktop, | |
| // Diagnostics | |
| paths::get_log_path, | |
| paths::get_hermes_home, | |
| paths::open_log_dir, | |
| ]) | |
| .run(tauri::generate_context!()) | |
| .expect("error while running Hermes Setup"); | |
| } | |
| mod tests { | |
| use super::{force_setup_from_args, AppMode}; | |
| fn bare_args_are_install() { | |
| assert_eq!(AppMode::from_args(Vec::<String>::new()), AppMode::Install); | |
| assert_eq!(AppMode::from_args(["--foo", "bar"]), AppMode::Install); | |
| } | |
| fn update_flag_selects_update() { | |
| assert_eq!(AppMode::from_args(["--update"]), AppMode::Update); | |
| assert_eq!( | |
| AppMode::from_args(["--something", "--update", "--else"]), | |
| AppMode::Update | |
| ); | |
| } | |
| fn reinstall_and_repair_flags_force_setup() { | |
| assert!(force_setup_from_args(["--reinstall"])); | |
| assert!(force_setup_from_args(["--repair"])); | |
| assert!(force_setup_from_args(["--foo", "--repair", "--bar"])); | |
| } | |
| fn bare_or_unrelated_args_do_not_force_setup() { | |
| assert!(!force_setup_from_args(Vec::<String>::new())); | |
| assert!(!force_setup_from_args(["--foo", "bar"])); | |
| // --update must not be mistaken for a force-setup flag. | |
| assert!(!force_setup_from_args(["--update"])); | |
| } | |
| fn force_setup_flags_do_not_affect_mode_selection() { | |
| // The repair flags must never flip Install<->Update. | |
| assert_eq!(AppMode::from_args(["--reinstall"]), AppMode::Install); | |
| assert_eq!(AppMode::from_args(["--repair"]), AppMode::Install); | |
| assert_eq!( | |
| AppMode::from_args(["--update", "--reinstall"]), | |
| AppMode::Update | |
| ); | |
| } | |
| } | |