# Adom Desktop

> Bridge between Claude (running in an Adom Docker container) and the user's desktop applications. **One install** gives Claude every desktop verb it needs — KiCad, Fusion 360, Chrome, file transfer, screenshots, shell, notifications, screen recording — over a single CLI binary that auto-updates from this wiki.

**v1.8.31 highlights:**

- **Direct API with port resilience** — fallback chain 8770→8779, discovery file at `~/.adom/direct-api-port`, per-verb command timeouts, graceful shutdown on app exit. Docker connects in <100ms even after laptop sleep/wake.
- **Dynamic bridge ports** — every bridge binds to an OS-assigned ephemeral port. No more collisions with Hydrogen Desktop's 8772/8773/8851. Third-party bridges no longer need to pick a number.
- **Bridge ecosystem v1.8.0+** — three official bridges (KiCad, Fusion 360, Puppeteer) + two sample templates (Python, Rust) + a community-curated [bridges registry](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges-registry). Install any bridge with `adom-desktop bridge_install '{"manifestUrl":"…"}'`.
- **Auto-update from the wiki** (v1.3.39+) — polls `version.json` every 4h. Settings → Updates lets users pick prompt / auto / off.
- **Single-instance enforcement** — `tauri-plugin-single-instance` ensures only one GUI runs even if autostart + manual launch overlap.

---

## What it is

Adom Desktop is a small **Tauri 2** (Rust + WebView2/WebKit) desktop application that runs on the user's machine and acts as a WebSocket relay between Docker Claude and local applications. The architecture has three layers:

```
[ Docker container ]                [ User's machine ]
                                          │
  adom-desktop CLI  ◄──── HTTP/WS ────►  Adom Desktop (Tauri GUI)
       (Linux)                            │
                                          ├── KiCad bridge        (port-assigned at spawn)
                                          ├── Fusion 360 bridge   (port-assigned at spawn)
                                          ├── Puppeteer bridge    (port-assigned at spawn)
                                          ├── 3rd-party bridges   (port-assigned at spawn)
                                          ├── Direct API endpoint (8770→8779 fallback)
                                          └── File / shell / notify primitives
```

Every desktop integration is implemented as a **bridge** — an independently-versioned process Adom Desktop spawns on demand, with a `bridge.json` manifest declaring verb prefixes (`kicad_*`, `fusion_*`, `browser_*`, etc.). Adom Desktop allocates an ephemeral port at spawn time, passes it as `--port <N>`, and polls `/status` until ready.

Same install gives Claude:

- **KiCad** (via [`adom-bridge-kicad`](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-kicad-bridge)) — open boards/schematics, install symbols/footprints, DRC, screenshot editor windows, watch projects for changes
- **Fusion 360** (via [`adom-bridge-fusion`](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-fusion-bridge)) — launch Fusion, export STEP/IGES/STL/3MF/USDZ/OBJ/DXF/DWG/Gerbers, search cloud files, run EAGLE library commands
- **Browser** (via [`adom-bridge-puppeteer`](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-puppeteer-bridge)) — Chrome for Testing on the desktop, multi-tab + multi-session, screencast recording, full-desktop screen recording with HUD
- **File transfer** — `push_file` / `pull_file` for moving artifacts between Docker and the desktop
- **Shell** — `shell_execute` runs commands on the user's machine with their identity
- **Notifications** — native OS toasts with optional action buttons
- **Screenshots** — `desktop_list_windows`, `desktop_screenshot_window`, `desktop_screenshot_screen`

Plus a [bridge SDK](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges) so the community can ship bridges for Altium, MATLAB, instrument SDKs, anything else — without coordinating with the Adom Desktop release cycle.

---

## Install (per platform)

### Docker side (Linux CLI)

```bash
# v1.4.x+: get the latest CLI binary from the wiki (single static file)
adom-wiki asset get apps/adom-desktop docker_binary -o /usr/local/bin/adom-desktop
chmod +x /usr/local/bin/adom-desktop
adom-desktop --version   # adom-desktop 1.8.31 (<sha>, built <ts>)
adom-desktop install     # deploys the skill, starts the relay on :8765/:8766
```

The CLI auto-detects the user's desktop via the **direct API** (`~/.adom/direct-api-port`, fallback range 8770-8779) and falls back to the WebSocket relay when not in direct mode.

### Windows

[Download the latest installer](https://github.com/adom-inc/adom-desktop/releases/latest) (`Adom Desktop_<version>_x64-setup.exe`). NSIS installer with POSTINSTALL auto-launch. v1.3.39+ auto-updates from the wiki on every launch + every 4h.

### macOS

Download the `.dmg` from the wiki or [GitHub Releases](https://github.com/adom-inc/adom-desktop/releases/latest). Mount → drag to Applications → strip Gatekeeper quarantine on first launch:

```bash
xattr -cr "/Applications/Adom Desktop.app"
```

### Linux (Ubuntu / Debian)

Either the `.deb` package or the standalone binary from the wiki:

```bash
sudo dpkg -i Adom-Desktop_<version>_amd64.deb
# OR standalone:
chmod +x adom-desktop-ubuntu-<version> && ./adom-desktop-ubuntu-<version>
# Optional runtime deps for full feature parity:
sudo apt install wmctrl xdotool imagemagick scrot libnotify-bin
```

---

## Auto-start the relay on every Docker session

The relay is NOT started automatically when the Docker container boots. Every time this skill is invoked, run this idempotent block at the top of the session **before** any other `adom-desktop` subcommand:

```bash
if ! curl -sf http://127.0.0.1:8766/health >/dev/null 2>&1; then
  nohup adom-desktop serve > /tmp/adom-desktop-relay.log 2>&1 &
  disown
  for _ in 1 2 3 4 5 6; do
    sleep 0.5
    curl -sf http://127.0.0.1:8766/health >/dev/null 2>&1 && break
  done
fi
adom-desktop ping
```

`nohup ... & disown` (NOT plain `&`) is critical. Without `nohup` + `disown` the relay dies as soon as the tool call that started it ends, and the next `adom-desktop` command fails with `Cannot reach relay server (port 8766)`.

If `ping` succeeds → desktop is connected, proceed. If it returns `No desktop client connected` → run `adom-desktop setup_desktop` and ask the user to verify the desktop app is running.

---

## Bridge ecosystem

| Bridge | Status | Verbs | Wiki page |
|---|---|---|---|
| **KiCad** | Official, bundled | `kicad_*` (board/schematic open + DRC + symbol/footprint install + watch + screenshot) | [`adom-desktop-kicad-bridge`](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-kicad-bridge) |
| **Fusion 360** | Official, bundled | `fusion_*` (open + export STEP/IGES/STL/3MF/USDZ/OBJ/DXF/DWG/Gerbers + cloud search + EAGLE) | [`adom-desktop-fusion-bridge`](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-fusion-bridge) |
| **Puppeteer** | Official, bundled | `browser_*` + `desktop_*` (browser automation + screen recording + multi-tab) | [`adom-desktop-puppeteer-bridge`](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-puppeteer-bridge) |
| **hello-python** | Sample | `hellopy_*` (ping + echo, ~80 lines stdlib) | [`adom-desktop-hello-python-bridge`](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-hello-python-bridge) |
| **hello-rust** | Sample | `hellors_*` (ping + echo, ~140 lines, single static binary) | [`adom-desktop-hello-rust-bridge`](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-hello-rust-bridge) |

Browse the full **[bridges registry](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges-registry)** for everything installable + the submission flow for community bridges. Read the **[bridge SDK guide](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges)** for the `bridge.json` schema, packaging, and lifecycle.

```bash
# Install any bridge from a manifest URL
adom-desktop bridge_install '{"manifestUrl":"https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/hello-python-bridge-manifest.json"}'

# Lifecycle controls
adom-desktop bridge_list
adom-desktop bridge_pause '{"name":"hello-python"}'
adom-desktop bridge_resume '{"name":"hello-python"}'
adom-desktop bridge_uninstall '{"name":"hello-python"}'

# Refresh any bridges with newer wiki manifests
adom-desktop refresh_bridges
```

---

## Direct API (v1.8.31+)

When the Docker container and the user's machine can talk directly (cloud workspace + local laptop), the CLI prefers the **direct API** over the WebSocket relay:

- **Discovery file** at `~/.adom/direct-api-port` (`host:port` format) — written by Adom Desktop on every successful bind
- **Fallback port range** 8770→8779 — if 8770 is held by a zombie socket from a previous session, Adom Desktop walks the range with `SO_REUSEADDR` until it finds a free port
- **Per-verb command timeouts** — search / walk verbs get 620s; exports get 320s; everything else 120s
- **Graceful shutdown** — Tauri's `RunEvent::Exit` triggers `direct_api::shutdown()` which cleans the discovery file
- **Live-owner probe** before walking — 1s timeout (HD's Python bridges' slow `/health` works inside that window)

Override the bind address via `ADOM_DIRECT_LISTEN_ADDR` (default `127.0.0.1`).

---

## Screen recording (v1.3.30+)

Two recording surfaces:

- **Full desktop** via Chrome `getDisplayMedia` + `MediaRecorder` in a small always-on-top HUD window. Reason is required and shown in the HUD. WebM output, audio optional. Single recorder window stays open across multiple clips in a session.
- **Per-tab** via CDP `Page.startScreencast` — JPEG frames + concat manifest, muxed to WebM via ffmpeg. Concurrent recordings across multiple pup tabs are fine; no HUD.

```bash
# Desktop recording (HUD-visible)
adom-desktop desktop_record_start '{"reason":"Demo of pin-1 sign-off","monitor":"primary","fps":30}'
# ... drive activity ...
adom-desktop desktop_record_stop
adom-desktop desktop_recorder_close

# Per-tab recording (headless, concurrent OK)
adom-desktop browser_record_start '{"sessionId":"align","tabId":"tab-1"}'
adom-desktop browser_record_stop  '{"sessionId":"align","recordingId":"…"}'
```

---

## Architecture notes for contributors

- **Two binaries**: the **CLI** (~5 MB, `cli/target/release/adom-desktop.exe`, has `serve` command) vs. the **Tauri GUI** (~16 MB, `src-tauri/target/release/adom-desktop.exe`, connects to relay as client). Never copy one over the other.
- **Build matrix** in `CLAUDE.md` documents which changes need which rebuilds. Plugin-only edits don't require a Tauri rebuild — sync to `src-tauri/target/release/plugins/` + kill `python.exe` / `node.exe` so the next CLI call respawns the bridge from the new code.
- **Version bumps** via `python scripts/bump-version.py patch` — propagates to `VERSION` + `cli/Cargo.toml` + `src-tauri/tauri.conf.json` together.
- **Releases** are two artifacts on one tag: Windows installer built locally + Linux CLI built on Docker (`scripts/release-publish.sh` handles both).
- **Single source of truth** for Docker Claude's knowledge of the CLI is `cli/src/commands.rs`'s help map. Every new verb / arg / behavior change must update it before shipping.

Full contributor docs: [`CLAUDE.md`](https://github.com/adom-inc/adom-desktop/blob/main/CLAUDE.md) in the repo.

---

## Repo + license

- **Source**: [github.com/adom-inc/adom-desktop](https://github.com/adom-inc/adom-desktop)
- **License**: MIT
- **Releases**: [github.com/adom-inc/adom-desktop/releases](https://github.com/adom-inc/adom-desktop/releases)
- **Issues / discussion**: GitHub Issues on the same repo

The three official bridges live in their own repos:

- [`adom-inc/adom-bridge-kicad`](https://github.com/adom-inc/adom-bridge-kicad)
- [`adom-inc/adom-bridge-fusion`](https://github.com/adom-inc/adom-bridge-fusion)
- [`adom-inc/adom-bridge-puppeteer`](https://github.com/adom-inc/adom-bridge-puppeteer)

(Private as of this version, going public after a review pass. Source is mirrored on each bridge's wiki page as a tarball + git bundle — no GitHub account required to fork.)
