← Commit history

Delete skills/adom-bridge-installer

Kyle Bergstedt ·842d348938 ·2mo ago ·parent 9aaded0
1 file changed −452
skills/adom-bridge-installer/SKILL.mddeleted−452
@@ -1,452 +0,0 @@-----name: adom-bridge-installer-description: How to invoke the Adom Bridge NSIS installer from a parent installer (especially Adom Hydrogen's setup steps) — silent install, /NOLAUNCH switch for Hydrogen-managed bundling, live progress via the structured log file at %TEMP%\adom-bridge-install.log. Use when bundling Adom Bridge inside another Windows installer, writing an install supervisor / setup-step UI, detecting Adom Bridge's installed version, or upgrading an existing Adom Bridge install programmatically.------# Adom Bridge installer integration--Adom Bridge (Adom Bridge) ships as a single NSIS installer:--```-Adom Bridge_<version>_x64-setup.exe   (~4.8 MB on v1.8.45)-```--It installs to `%LOCALAPPDATA%\Adom Bridge\` — no UAC prompt, no admin needed. Tauri-2 bundled NSIS with our customisations in `src-tauri/installer-hooks.nsh`.--## Command-line surface--| Flag | Meaning |-|---|---|-| (none) | Installs silently, auto-launches `adom-bridge.exe` after install |-| `/S` | Standard NSIS silent flag — same behavior since the top-level `SilentInstall silent` directive already suppresses UI |-| `/NOLAUNCH` | **v1.8.45+** — install silently, do NOT auto-launch after install. Caller is responsible for spawning Adom Bridge with the desired flags |-| `/D=<path>` | Install destination override. Default is `$LOCALAPPDATA\Adom Bridge`. Rarely needed |-| `<uninstall.exe> /S` | Silent uninstall. The uninstaller lives at `%LOCALAPPDATA%\Adom Bridge\uninstall.exe` |--**UI behavior**: zero UI in all cases. `SilentInstall silent` is set at the top of the NSIS script, so even `setup.exe` invoked from Explorer runs without a window. No Welcome page, no progress bar, no Finish page.--**Auto-launch**: by default the installer calls `ExecShell "" "$INSTDIR\adom-bridge.exe"` at the end of POSTINSTALL. Pass `/NOLAUNCH` to suppress this. Used by Hydrogen when bundling Adom Bridge because Hydrogen wants to spawn Adom Bridge with `--embedded --start-hidden --relay-url ... --session-token ...` itself, not let Adom Bridge launch standalone first and get respawned via single-instance.--## Live progress log--The installer writes structured progress to `%TEMP%\adom-bridge-install.log` (Windows expands `%TEMP%` to the user's temp dir, typically `C:\Users\<name>\AppData\Local\Temp`). Each line is `[STAGE] message\r\n`:--```-[START] adom-bridge installer 1.8.45 starting-[PREINSTALL] Cleaning legacy desktop shortcuts-[PREINSTALL] Done. Tauri will now copy bundle files.-[POSTINSTALL] Bundle files copied. Cleaning Tauri's auto-created desktop shortcut.-[POSTINSTALL] /NOLAUNCH passed — skipping auto-launch (caller will spawn)-[DONE] adom-bridge installer complete (no-launch mode)-```--**`[START]`** opens with a truncated write (fresh log per install). **`[DONE]`** is always the final line — readers tailing the file can use that as the completion sentinel without needing to wait for the process exit.--Stage tags currently emitted:-- `[START]` — once, at the top-- `[PREINSTALL]` — Tauri's beforeBuildCommand hook ran, files about to be copied-- `[POSTINSTALL]` — files copied; Tauri's standard shortcuts created and our hook is cleaning the unwanted ones-- `[DONE]` — final line, installer about to exit--The uninstaller writes the same log with `[PREUNINSTALL]` / `[POSTUNINSTALL]` stages.--## Recipe: Hydrogen's setup-step UI tails the log--Spawn the installer with `/NOLAUNCH`, poll the log file on a 200 ms timer, surface each new line to Hydrogen's UI. When `[DONE]` shows up OR the process exits, the install is complete.--### Rust (Hydrogen's installer driver)--```rust-use std::{env, fs, path::PathBuf, process::Command, time::Duration};-use std::os::windows::process::CommandExt;-use tokio::time::sleep;--const CREATE_NO_WINDOW: u32 = 0x08000000;--async fn install_adom_bridge<F: FnMut(String)>(-    installer_path: &str,-    mut on_line: F,-) -> Result<(), String> {-    let log_path: PathBuf = [&env::var("TEMP").map_err(|e| e.to_string())?, "adom-bridge-install.log"]-        .iter()-        .collect();-    // Pre-clear so we don't read stale lines from a previous install.-    let _ = fs::remove_file(&log_path);--    let mut child = Command::new(installer_path)-        .args(["/S", "/NOLAUNCH"])-        .creation_flags(CREATE_NO_WINDOW)-        .spawn()-        .map_err(|e| format!("spawn installer: {e}"))?;--    let mut last_pos = 0usize;-    loop {-        // Tail any new content-        if let Ok(content) = fs::read_to_string(&log_path) {-            if content.len() > last_pos {-                let slice = &content[last_pos..];-                if let Some(last_nl) = slice.rfind('\n') {-                    let consumed = &slice[..=last_nl];-                    for line in consumed.lines() {-                        on_line(line.to_string());-                        if line.starts_with("[DONE]") {-                            // Don't return yet — wait for process exit-                            // so we surface the actual exit code, but-                            // we know progress streaming is finished.-                        }-                    }-                    last_pos += consumed.len();-                }-            }-        }-        // Check if installer has exited-        if let Some(status) = child.try_wait().map_err(|e| e.to_string())? {-            return if status.success() {-                Ok(())-            } else {-                Err(format!("installer exited with {}", status))-            };-        }-        sleep(Duration::from_millis(200)).await;-    }-}--// Caller:-// install_adom_bridge(r"C:\path\to\Adom Bridge_1.8.45_x64-setup.exe", |line| {-//     hydrogen_setup_step.append_live_output(&line);-// }).await?;-```--### Bash (debugging / scripting)--```bash-# Pre-clear stale log-rm -f "$TEMP/adom-bridge-install.log" 2>/dev/null--# Spawn installer in background, tail the log-"./Adom Bridge_1.8.45_x64-setup.exe" /S /NOLAUNCH &-INSTALLER_PID=$!--# Tail until process exits OR [DONE] appears-while kill -0 $INSTALLER_PID 2>/dev/null; do-  [ -f "$TEMP/adom-bridge-install.log" ] && tail -F "$TEMP/adom-bridge-install.log" &-  sleep 0.2-done-wait-echo "Installer exited"-```--## Recipe: detect if Adom Bridge is already installed (and at what version)--The signed NSIS installer (`installMode: "currentUser"`) writes Adom Bridge's uninstall info to `HKCU` — no admin needed. **⚠ The key name is the product name `Adom Bridge` (WITH a space), NOT `AdomBridge`** (verified live 2026-07-03). Two history notes so you don't chase the wrong key:-- The NSIS uninstall key = the product name → `…\Uninstall\Adom Bridge` (space).-- Adom Bridge's OWN launch-time code used to ALSO write an `AdomBridge` (no-space) key to **HKLM** — that write was **DELETED in v1.9.68** (the no-UAC fix). So `AdomBridge` no longer exists anywhere; read `Adom Bridge`.--```-HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall\Adom Bridge-  DisplayName       = "Adom Bridge"-  DisplayVersion    = "1.9.74"-  InstallLocation   = "C:\Users\<name>\AppData\Local\Adom Bridge"-  UninstallString   = "C:\Users\<name>\AppData\Local\Adom Bridge\uninstall.exe"-  Publisher         = "Adom Industries, Inc."-```--```rust-use winreg::enums::HKEY_CURRENT_USER;-use winreg::RegKey;--fn adom_bridge_installed_version() -> Option<String> {-    let hkcu = RegKey::predef(HKEY_CURRENT_USER);-    // NOTE the SPACE — the NSIS uninstall key is the product name "Adom Bridge".-    let key = hkcu-        .open_subkey(r"Software\Microsoft\Windows\CurrentVersion\Uninstall\Adom Bridge")-        .ok()?;-    key.get_value("DisplayVersion").ok()-}-```--Compare with `semver::Version::parse` to decide whether to skip / upgrade / leave-alone.--## Recipe: Hydrogen's setup-step ladder for embedding Adom Bridge--The full pattern Hydrogen's setup-step UI runs when embedding Adom Bridge:--```-1. Check installed version          → registry read above-2. Decide:-     no install         → install bundled Adom Bridge-     older than bundled → install bundled Adom Bridge (upgrade)-     same or newer      → skip install entirely-3. Run installer (silent + /NOLAUNCH)   → tail log, surface to UI-4. Write embedded marker file       → %LOCALAPPDATA%\Adom Bridge\embedded.json-5. Delete Adom Bridge's user-facing shortcuts → Start Menu, Desktop, Startup folder-                                       (so user can't double-launch standalone)-6. Spawn Adom Bridge with embedded flags     → adom-bridge.exe --embedded-                                          --start-hidden-                                          --relay-url ws://127.0.0.1:8765-                                          --relay-name adom-hydrogen-                                          --session-token <hd's token>-7. Poll Adom Bridge's direct API for liveness → GET http://127.0.0.1:47200/health-```--Steps 4 and 5 are also covered by Adom Bridge's installer if you pass `--embedded` at Adom Bridge's first launch (the embedded-mode detection writes the marker and Adom Bridge's autostart-upgrade routine handles its own shortcuts). Doing them in Hydrogen's installer is belt-and-suspenders.--## Bundle-resource pattern--Hydrogen's `tauri.conf.json` should include the Adom Bridge installer as a bundle resource so it ships inside Hydrogen's NSIS installer:--```json-"bundle": {-  "resources": {-    "resources/Adom Bridge_1.8.45_x64-setup.exe": "resources/"-  }-}-```--At runtime Hydrogen finds it under its install dir at `$INSTDIR\resources\Adom Bridge_<ver>_x64-setup.exe` — pass that path to the install function above.--## Versioning + upgrade strategy--| Scenario | What Hydrogen's installer should do |-|---|---|-| No Adom Bridge installed | Install bundled version |-| Adom Bridge older than bundled | Install bundled version (upgrade) |-| Adom Bridge same as bundled | Skip install step; still spawn with embedded flags |-| Adom Bridge newer than bundled | Skip install step; spawn with embedded flags. Don't downgrade — embedded mode works for any Adom Bridge ≥ v1.8.42 |--The bundled Adom Bridge installer is the minimum supported version for the Hydrogen release. Hydrogen can spawn newer ADs without modification.--## Exit codes--NSIS returns:-- `0` — install succeeded-- `2` — user cancelled (won't happen with `SilentInstall silent` since there's no UI to cancel)-- Other — install error (rare with our hooks; typical cause is disk full or `%LOCALAPPDATA%` not writable)--Treat anything non-zero as a hard failure and surface the log file's last 20 lines to the user.--## Where the installer lives--**v1.8.115+** — the end-user binary home is the **V2 wiki blob store** (the-GitHub repo is private; GH release URLs 404 for anonymous downloads). Two-discovery surfaces, both anonymous:--**Preferred for parent installers (Hydrogen setup): the packages API.** Resolve-the latest registered version and its assets in one or two GETs — note-`?org=adom` is REQUIRED (the slug exists in multiple orgs; bare URLs 400):--```-https://wiki.adom.inc/api/v1/packages/adom-bridge/dist-tags?org=adom-  → {"dist_tags":{"latest":"X.Y.Z"}}-https://wiki.adom.inc/api/v1/packages/adom-bridge/manifest?org=adom-  → { version, ..., assets: { "windows-x64": { filename, url, sha256, size } } }-```--The `assets` map is server-derived from the page's blobs (sha256 computed-from the actual bytes — integrity-checkable). `assets[...].url` is-server-relative (`/blob/app/adom-bridge/<file>`); prefix the git-wiki host.--**Fallback / updater path: version.json**, V2-first with V1 fallback:--```-https://wiki.adom.inc/api/v1/pages/adom-bridge/files/version.json-https://wiki.adom.inc/api/v1/pages/adom-bridge/files/version.json-```--The V2 manifest's `windows.installer_url` / `cli.<plat>.binary_url` point at-the V2 files endpoint; V1's point at V1 static copies. **Follow the-manifest's URLs — do NOT construct binary URLs by hand.**--Network quirk: HEAD on V2 blob/file URLs may return 404 while GET works-(git-wiki #40 — fix in flight). Probe with a Range GET (`Range: bytes=0-0`,-expect 206), never bare HEAD. Asset filenames are dot-form everywhere-(`Adom.Bridge_<ver>_windows_x64-setup.exe`); V1 dot-normalized since v1.8.72.--The wiki page's `metadata.install.windows_installer` field points at the-canonical filename for the current published version. Hydrogen's release build-should fetch the latest via the V2-first pattern:--```bash-# Try V2 first, fall back to V1.-fetch_manifest() {-  curl -fL --max-time 15 \-    "https://wiki.adom.inc/api/v1/pages/adom-bridge/files/version.json" \-    2>/dev/null \-  || curl -fL --max-time 15 \-    "https://wiki.adom.inc/api/v1/pages/adom-bridge/files/version.json"-}--MANIFEST=$(fetch_manifest)-INSTALLER_URL=$(echo "$MANIFEST" | jq -r .windows.installer_url)-INSTALLER_SHA=$(echo "$MANIFEST" | jq -r .windows.installer_sha256)-# Each manifest carries a self-consistent installer_url that points at the-# SAME backend the manifest was served from — V2 manifest → V2 installer URL,-# V1 manifest → V1 installer URL. No URL mixing across backends.--curl -fL "$INSTALLER_URL" -o resources/ad-setup.exe--# Verify SHA256 before bundling — defends against partial fetches + tampering-DOWNLOAD_SHA=$(sha256sum resources/ad-setup.exe | awk '{print $1}')-[ "$DOWNLOAD_SHA" = "$INSTALLER_SHA" ] || { echo "SHA mismatch"; exit 1; }-```--The desktop Adom Bridge's built-in auto-updater (`src-tauri/src/updater.rs`) walks an-ordered `MANIFEST_URLS` list — V2 first, V1 fallback — and uses whichever-responds first. Once V2 has been the source of truth for a release cycle-without incident, V1 will be retired from the pipeline.--`version.json` also carries the SHA256 hash of the installer so Hydrogen's build-pipeline can verify the download before bundling.--## Asset naming convention (v1.8.113+)--The Adom Bridge wiki page hosts TWO distinct types of artifacts per release:--1. **Full Adom Bridge GUI installer** — the actual desktop app (Tauri window, bundled-   bridges, relauncher). One per OS / architecture.-2. **CLI companion binary** — a separate executable that talks to Adom Bridge over-   the relay. Used by Hydrogen's WSL2 container, cloud builds, sibling-app-   installers, cron jobs. Does NOT contain Adom Bridge itself.--Both ship together at the same version (single `python scripts/bump-version.py-patch` bumps both, single `cargo tauri build` + `cargo build --release` in-`cli/` produces both, single `scripts/release-publish.sh` uploads both).--### Filename pattern--| Artifact | Wiki asset name pattern | Example (v1.8.113) |-|---|---|---|-| Full Adom Bridge GUI — Windows | `Adom.Bridge_<ver>_windows_x64-setup.exe` | `Adom.Bridge_1.8.113_windows_x64-setup.exe` |-| Full Adom Bridge GUI — macOS | `Adom.Bridge_<ver>_macos_<arch>.dmg` | `Adom.Bridge_1.8.113_macos_aarch64.dmg` (future) |-| Full Adom Bridge GUI — Linux | `Adom.Bridge_<ver>_linux_<arch>.AppImage` | `Adom.Bridge_1.8.113_linux_x86_64.AppImage` (future) |-| CLI companion — Linux | `adom-bridge-cli_<ver>_linux_<arch>` | `adom-bridge-cli_1.8.113_linux_x86_64` |-| CLI companion — macOS | `adom-bridge-cli_<ver>_macos_<arch>` | `adom-bridge-cli_1.8.113_macos_aarch64` (future) |-| CLI companion — Windows | `adom-bridge-cli_<ver>_windows_<arch>.exe` | `adom-bridge-cli_1.8.113_windows_x64.exe` (future) |--The `_cli_` token in the middle is the only signal you need to know "this is-the companion, not the app." `linux_x86_64` always means Linux x86_64,-regardless of whether the file is a GUI installer or a CLI binary.--### After install, the binary name is `adom-bridge`--The wiki asset name disambiguates at FETCH time. At INSTALL time the binary-is renamed to `adom-bridge` so the command users type doesn't change:--```bash-# Linux example — install the CLI companion-curl -fL "https://wiki.adom.inc/api/v1/pages/adom-bridge/files/adom-bridge-cli_1.8.113_linux_x86_64" \-  -o /tmp/ad-sudo install -m 755 /tmp/ad /usr/local/bin/adom-bridge-adom-bridge --version-# → adom-bridge 1.8.113 (<sha>, built <ts>)-```--### Version.json `.cli` slot--The auto-update manifest (v1.8.113+) carries a typed `cli` sibling section-for the CLI binaries:--```json-{-  "manifest_version": 1,-  "version": "1.8.113",-  "windows": { "installer_url": ".../Adom.Bridge_1.8.113_windows_x64-setup.exe", ... },-  "cli": {-    "linux_x86_64": {-      "binary_url":      ".../adom-bridge-cli_1.8.113_linux_x86_64",-      "binary_filename": "adom-bridge-cli_1.8.113_linux_x86_64",-      "binary_sha256":   "...",-      "binary_size":     8012408-    }-  },-  "changelog_url": "...",-  "minimum_required": "1.3.30"-}-```--Pre-v1.8.113 readers ignore `.cli` entirely (additive schema; no-`manifest_version` bump required). The desktop Adom Bridge's own auto-updater only-reads `.windows / .macos / .linux` for its GUI installer URL — it doesn't-touch `.cli`.--For Hydrogen / cloud / Docker-side fetchers, prefer the typed lookup:--```bash-# Best: read the URL out of the manifest (so the filename can evolve)-MANIFEST=$(fetch_manifest)-LINUX_CLI_URL=$(echo "$MANIFEST" | jq -r '.cli.linux_x86_64.binary_url')-LINUX_CLI_SHA=$(echo "$MANIFEST" | jq -r '.cli.linux_x86_64.binary_sha256')-curl -fL "$LINUX_CLI_URL" -o /tmp/ad-[ "$(sha256sum /tmp/ad | awk '{print $1}')" = "$LINUX_CLI_SHA" ] || exit 1-sudo install -m 755 /tmp/ad /usr/local/bin/adom-bridge-```--### Windows CLI is shipped with the NSIS (v1.8.114+)--Installing Adom Bridge on Windows now drops the CLI companion next to the GUI exe-and adds the install dir to the user's PATH so PowerShell / Claude Desktop /-cmd find it without configuration:--```-%LOCALAPPDATA%\Adom Bridge\adom-bridge.exe        ← GUI (Windows subsystem)-%LOCALAPPDATA%\Adom Bridge\adom-bridge-cli.exe    ← CLI companion (Console subsystem)-%LOCALAPPDATA%\Adom Bridge\ad-relauncher.exe       ← visible self-restart helper-```--NSIS's POSTINSTALL hook prepends `%LOCALAPPDATA%\Adom Bridge` to-`HKCU\Environment\Path` (no UAC — user-level only) and broadcasts-`WM_SETTINGCHANGE` so freshly-opened shells pick up the change. Already--open shells need a re-launch (OS limitation).--After install, in a fresh PowerShell:--```powershell-adom-bridge-cli --version-# adom-bridge 1.8.114 (<sha>, built <ts>)-adom-bridge-cli status-adom-bridge-cli screenshot_window '{"title-contains":"VS Code"}'-```--The CLI auto-discovers the local Adom Bridge via direct-API probe at-`127.0.0.1:47200` — no relay needed when Adom Bridge is on the same box. ~5ms RTT-for compatible verbs; verbs in Adom Bridge's `cliRequired` list still flow through-a relay if one's running (Hydrogen's WSL2 distro typically owns the relay).--**Relay self-detect (v1.8.114+):** `adom-bridge-cli serve` checks for-another adom-bridge relay on `127.0.0.1:8766/health` before binding. If-one already answers, it prints a friendly note and exits cleanly without-starting a second relay (which would just sit idle — Adom Bridge's WS client only-attaches to the first one it finds). Pass `--force-bind` to override.--PREUNINSTALL strips the PATH entry back out and rebroadcasts so an-uninstall of Adom Bridge also gets PATH cleanup.--### Legacy alias migration window--Releases prior to v1.8.113 published the Linux CLI as `adom-bridge-linux`-(no version, no architecture). Hydrogen's WSL2 setup and a few cron-based-installers have that literal filename baked in.--To keep them working through the rename, v1.8.113 publishes BOTH names:--| Name | Status | Wiki URL (V2 / V1) |-|---|---|---|-| `adom-bridge-cli_<ver>_linux_x86_64` | **canonical, v1.8.113+** | V2 / V1 |-| `adom-bridge-linux` | **deprecated alias** — byte-identical copy | V2 / V1 |--Both files are byte-identical with the same SHA256. The legacy alias stays-through **v1.8.116**, then disappears. Migrate by then. Recommended pattern:--```bash-# Migration period (v1.8.113 → v1.8.115): try new name, fall back to alias-LINUX_CLI_URL_NEW=$(echo "$MANIFEST" | jq -r '.cli.linux_x86_64.binary_url // empty')-if [ -n "$LINUX_CLI_URL_NEW" ]; then-  curl -fL "$LINUX_CLI_URL_NEW" -o /tmp/ad-else-  # Falling back — likely talking to a pre-v1.8.113 manifest-  curl -fL "https://wiki.adom.inc/api/v1/pages/adom-bridge/files/adom-bridge-linux" \-    -o /tmp/ad-fi-```--Post-v1.8.116, the fallback branch dies; only the canonical name will exist.