app
DEPRECATED: Bridge SDK (moved into Adom Bridge)
Public Made by Adomby adom
Bridge SDK guide: bridge.json schema, kind:python/node/exe, hello-python + hello-rust reference templates, packaging + lifecycle commands.
← Commit history
page.json+2−2@@ -56,7 +56,7 @@ "install_hint": "", "version_cmd": "" },- "readme": "# Writing a third-party Adom Desktop bridge\n\n> **Public version of this guide:** [`apps/adom-desktop-bridges` on the Adom Wiki](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-bridges). The wiki page is the canonical user-facing copy and is what the GUI's \"+ Bridge\" install modal links to (the adom-inc GitHub repo is private). When you update this file with breaking schema changes or new lifecycle commands, also republish the wiki page via `adom-wiki page edit apps/adom-desktop-bridges --field content --body-md <this file>`.\n\nv1.8.0+ supports dynamic bridges: any zip with a `bridge.json` at its root can be installed via `adom-desktop bridge_install --manifestUrl <wiki-or-github-url>`. This lets community authors ship bridges for Altium, MATLAB, Rohde & Schwarz oscilloscopes, Keysight, OrCAD, anything — without coordinating with the Adom Desktop release cycle.\n\n## Try it now — the `hello-python` and `hello-rust` sample bridges\n\nTwo parallel reference bridges are hosted on the Adom Wiki, one per language. Both expose the same two verbs (`*_ping` / `*_echo`) under different prefixes (`hellopy_` / `hellors_`), so they can be installed simultaneously for a side-by-side demo. Both use `port: 0` (dynamic OS-assigned ports — v1.8.31+) so no collision is possible.\n\n| Flavor | Manifest URL | Verb prefix | Port | When to fork |\n|---|---|---|---|---|\n| **Python** (stdlib only, ~80 lines) | `…/hello-python-bridge-manifest.json` | `hellopy_` | 8890 | Vendor has Python bindings, fast dev iter, I/O-bound work |\n| **Rust** (`tiny_http` + serde, single static binary, ~120 lines) | `…/hello-rust-bridge-manifest.json` | `hellors_` | 8891 | No Python runtime on user's machine, CPU-bound, type safety |\n\nBoth manifest URLs are under `https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/`.\n\n### Install from the GUI\n\n1. Click **+ Bridge** in Adom Desktop's bridge bar\n2. Paste one (or both) of the manifest URLs into the dialog, click **Install**\n3. New chips appear ~2 seconds later with a small ✦ marker (third-party indicator)\n\n### Install both from the CLI\n\n```bash\n# Install the Python flavor\nadom-desktop bridge_install '{\"manifestUrl\":\"https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/hello-python-bridge-manifest.json\"}'\nadom-desktop hellopy_ping\n# → {\"success\":true,\"output\":\"Hello from the Python sample bridge v1.1.0!\",\"language\":\"python\",...}\n\n# Install the Rust flavor (can coexist with hello-python)\nadom-desktop bridge_install '{\"manifestUrl\":\"https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/hello-rust-bridge-manifest.json\"}'\nadom-desktop hellors_ping\n# → {\"success\":true,\"output\":\"Hello from the Rust sample bridge v1.1.0!\",\"language\":\"rust\",...}\n\n# Echo through both\nadom-desktop hellopy_echo '{\"message\":\"world\"}'\n# → {\"success\":true,\"output\":\"echo (python): world\",...}\nadom-desktop hellors_echo '{\"message\":\"world\"}'\n# → {\"success\":true,\"output\":\"echo (rust): world\",...}\n\n# Both appear in bridge_list\nadom-desktop bridge_list\n# → bundled three + \"hello-python\" + \"hello-rust\"\n```\n\n### Uninstall\n\n```bash\nadom-desktop bridge_uninstall '{\"name\":\"hello-python\"}'\nadom-desktop bridge_uninstall '{\"name\":\"hello-rust\"}'\n```\n\nEach bridge's cache dir + registry entry are removed; the chips disappear from the bar.\n\nThe sources live at:\n- [`scripts/sample-bridges/hello-python/`](https://github.com/adom-inc/adom-desktop/tree/main/scripts/sample-bridges/hello-python) — `bridge.json` + `server.py` (~120 lines of `http.server.BaseHTTPRequestHandler`)\n- [`scripts/sample-bridges/hello-rust/`](https://github.com/adom-inc/adom-desktop/tree/main/scripts/sample-bridges/hello-rust) — `bridge.json` + `Cargo.toml` + `src/main.rs` (~140 lines of `tiny_http`) + pre-built `hello-rust.exe` (~300 KB)\n\nFork whichever flavor matches your target's natural ecosystem.\n\n## Directory layout\n\n```\nmy-bridge/\n├── bridge.json ← REQUIRED — schema below\n├── BRIDGE_VERSION ← optional, semver line (kept in sync with bridge.json's version field)\n├── server.py ← or server.js, or my-bridge.exe — your bridge's entrypoint\n├── handlers/ ← your code\n│ ├── ...\n└── README.md ← optional, recommended\n```\n\n## `bridge.json` schema\n\n```json\n{\n \"manifest_version\": 1,\n \"name\": \"altium\",\n \"displayName\": \"Altium Designer\",\n \"version\": \"1.0.0\",\n \"description\": \"One-line description shown in bridge_list output.\",\n \"homepage\": \"https://github.com/your-org/adom-bridge-altium\",\n \"author\": \"Your Org\",\n \"license\": \"MIT\",\n \"spawn\": {\n \"kind\": \"python\",\n \"entrypoint\": \"server.py\",\n \"port\": 0,\n \"healthEndpoint\": \"/status\",\n \"stopMethod\": \"kill\",\n \"killImageName\": \"python.exe\"\n },\n \"verbPrefixes\": [\"altium_\"],\n \"verbs\": [\n \"altium_open_project\",\n \"altium_export_step\",\n \"altium_run_drc\"\n ],\n \"dependencies\": {\n \"python\": \">=3.11\",\n \"altium-designer\": \">=24\"\n },\n \"category\": \"EDA\",\n \"tags\": [\"pcb\", \"altium\", \"commercial\"]\n}\n```\n\n### Field reference\n\n- **`name`** (required, lowercase, no spaces). The directory name + cache key. Must be unique across the registry.\n- **`displayName`** — human-friendly shown in `bridge_list` + future GUI bridge panel.\n- **`version`** (required, semver). Each ship bumps this.\n- **`spawn.kind`** — `python` | `node` | `exe`. Determines how Adom Desktop launches the bridge.\n - `python` → Adom Desktop finds Python in PATH and runs `python <entrypoint> --port <port>`. Good when the vendor's SDK has Python bindings.\n - `node` → Adom Desktop finds Node in PATH and runs `node <entrypoint> --port <port>`. Good for ecosystem reuse (Puppeteer, Playwright, etc.).\n - `exe` → Adom Desktop runs `<entrypoint> --port <port>` directly. The entrypoint must be a pre-built executable in the zip. Good for Rust, Go, .NET single-file builds, etc. — no runtime install required on the user's machine.\n- **`spawn.entrypoint`** — relative path inside the bridge dir (e.g. `server.py`, `server.js`, `my-bridge.exe`).\n- **`spawn.port`** — **set to `0`** (v1.8.31+ recommended). Adom Desktop picks an OS-assigned ephemeral port at spawn time and passes it to your `--port` arg. No collision possible between bridges. Hardcoded ports still work for back-compat but are deprecated — anything you pick will eventually collide with someone (Hydrogen Desktop's bridges already used 8772/8773/8851; static-port collisions are the #1 source of bridge \"won't start\" bugs). Bridge ports are internal plumbing — callers always route through `adom-desktop <prefix>_<verb>`, never reach a bridge directly.\n- **`spawn.healthEndpoint`** — path Adom Desktop polls to confirm the bridge is alive (e.g. `/status`, `/health`). v1.8.31+ accepts a path-only fragment (recommended) and constructs the URL using the runtime port. Legacy full URLs (`http://127.0.0.1:8881/status`) still parse — the host+port get replaced at runtime.\n- **`spawn.stopMethod`** — `kill` (process-handle Child::kill, NOT `taskkill /im` — that would nuke ALL Pythons on the box), `sigterm` (graceful), or `graceful_endpoint` (POST to an endpoint).\n- **`spawn.killImageName`** — legacy field, only used if Adom Desktop falls back to image-name kill. With v1.8.31's dynamic ports + process-handle kill this is rarely needed. Set to your process name for safety (`python.exe` / `node.exe` / your binary name).\n- **`verbPrefixes`** — list of CLI verb prefixes this bridge owns (e.g. `[\"altium_\"]`). All `altium_*` verbs route here.\n- **`verbs`** — explicit verb list. Used in `bridge_list` to advertise capabilities; doesn't affect routing.\n\n## Packaging + shipping\n\nUse the same `scripts/release-bridge.sh` flow Adom Desktop uses for its own bridges. Adapted for third parties:\n\n```bash\n# In your bridge repo:\npython3 -c \"\nimport zipfile, os, fnmatch\nEXCLUDE = ['*/__pycache__/*', '*.pyc', '.history/*', '*/node_modules/*']\nwith zipfile.ZipFile('my-bridge-v1.0.0.zip', 'w', zipfile.ZIP_DEFLATED) as zf:\n for root, dirs, files in os.walk('.'):\n for f in files:\n rel = os.path.relpath(os.path.join(root, f), '.')\n if any(fnmatch.fnmatch(rel.replace(os.sep, '/'), g) for g in EXCLUDE):\n continue\n zf.write(os.path.join(root, f), rel)\n\"\n\n# Compute SHA256 of the zip\nsha256sum my-bridge-v1.0.0.zip\n\n# Write the manifest the GUI polls\ncat > my-bridge-manifest.json <<EOF\n{\n \"manifest_version\": 1,\n \"version\": \"1.0.0\",\n \"url\": \"https://your-host/my-bridge-v1.0.0.zip\",\n \"sha256\": \"<sha256 from above>\",\n \"size\": <bytes>,\n \"released_at\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"\n}\nEOF\n```\n\nHost both files anywhere reachable by HTTPS — GitHub Pages, your own CDN, the Adom Wiki if you contribute to the official registry.\n\n## Installing\n\nUsers install your bridge with one command:\n\n```bash\nadom-desktop bridge_install '{\"manifestUrl\":\"https://your-host/my-bridge-manifest.json\"}'\n```\n\nThe GUI:\n1. Fetches `my-bridge-manifest.json`\n2. Downloads the referenced zip\n3. SHA256-verifies against the manifest\n4. Extracts to `%LOCALAPPDATA%\\Adom Desktop\\bridges-cache\\<name>\\` (the `name` comes from your `bridge.json`)\n5. Rescans the runtime registry — your bridge is now discoverable via `bridge_list`\n\nThe next CLI call to one of your `verbPrefixes` auto-spawns the bridge process.\n\n## Updating\n\nBump `bridge.json`'s `version` + `BRIDGE_VERSION`, rebuild the zip, re-upload, update the manifest. Users:\n\n```bash\nadom-desktop refresh_bridges # picks up new versions from manifest URLs they previously installed from\n```\n\n(Currently `refresh_bridges` only polls the three core wiki manifests. The follow-up work — tracking third-party manifest URLs in user state so they auto-refresh — lands in v1.8.x.)\n\n## Lifecycle controls (from cloud Docker, or local GUI)\n\n```bash\nadom-desktop bridge_list # see everything\nadom-desktop bridge_pause '{\"name\":\"altium\"}' # stop routing verbs\nadom-desktop bridge_resume '{\"name\":\"altium\"}'\nadom-desktop bridge_uninstall '{\"name\":\"altium\"}' # remove from cache\n```\n\n## Trust + security\n\nFor v1.8.0 the trust model is SHA256-verification of the wiki-hosted zip against the manifest. Bridges run with the user's privileges; install from sources you trust.\n\nFuture work (v1.9.0+):\n- Optional Ed25519 signing of bridge zips with the Adom-Inc public key for \"verified\" status\n- Per-bridge sandboxing (limited filesystem / network access declared in manifest `permissions` field)\n- Community submission flow on the wiki so contributors don't need a GitHub account\n\n## Reference implementations\n\nEvery bridge below has its own dedicated wiki page with hero, install/uninstall recipes, and a \"fork this\" walkthrough. The **[bridges registry](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges-registry)** is the canonical directory — both humans browse it and the GUI's Browse tab parses it.\n\n**Hello-world starter templates** — fork these for a 5-minute \"does this thing work?\" loop:\n- **[hello-python](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-hello-python-bridge)**: `scripts/sample-bridges/hello-python/` — ~80 lines, stdlib only, `kind:python`\n- **[hello-rust](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-hello-rust-bridge)**: `scripts/sample-bridges/hello-rust/` — ~140 lines, `tiny_http` + serde, `kind:exe`, single 300-KB static binary\n\n**Adom-shipped bridges** — canonical full-complexity examples; fork their layout for a real vendor integration:\n- **[kicad](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-kicad-bridge)**: `plugins/kicad/` — Python, complex, multi-instance (one bridge per KiCad GUI exe)\n- **[puppeteer](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-puppeteer-bridge)**: `plugins/puppeteer/` — Node, single-instance, recording-capable\n- **[fusion360](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-fusion-bridge)**: `plugins/fusion360/` — Python, simpler, command-passthrough to Fusion add-in\n\n## Get listed\n\nOnce your bridge is stable, add it to the **[bridges registry](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges-registry)** — that's the canonical machine-readable directory the GUI's Browse tab parses to discover what's installable. Two paths:\n\n1. **PR-based** (preferred): open a PR against [`adom-inc/adom-desktop`](https://github.com/adom-inc/adom-desktop) adding your row to the \"Community bridges\" table in this file (`plugins/BRIDGES.md`). A maintainer mirrors it to the wiki on the next sync. See the registry page's [submission section](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges-registry#how-to-submit-a-bridge) for the row format + review criteria.\n2. **Direct wiki edit**: if you have an Adom account, `adom-wiki page edit apps/adom-desktop-bridges-registry --field content --body-md <patch>`. Maintainers review and either keep or revert.\n",+ "readme": "# Adom Desktop - Bridge SDK\n\n## Build your own bridge\n\nA **bridge** is any small local program that exposes a few verbs over a tiny HTTP server. Adom Desktop (AD) discovers it from a `bridge.json`, spawns it, and relays your verbs to it from anywhere — including a cloud Claude session. If you can write a 50-line HTTP server in any language, you can ship a bridge.\n\nThe fastest path is to **let Claude build it for you**. Open a cloud Claude (or Claude Code) session that has the `adom-desktop` CLI installed, and paste a prompt like the one below.\n\n### Starting prompt (paste into a cloud Claude session)\n\n```\nI want to build an Adom Desktop bridge for <YOUR APP / DEVICE / SERVICE>.\n\nA bridge is a small local HTTP server described by a bridge.json; AD spawns it on a\nstable port and relays verbs to it. Before writing anything, study the reference\nbridges for design ideas and copy the conventions you see:\n\n - START SIMPLE — read the two \"hello\" reference bridges first (one Rust, one\n Python). They are the minimal, correct shape: a /command POST handler, a /status\n health endpoint that self-reports its LED, and a bridge.json. Mirror them.\n - THEN study an advanced bridge for the patterns you need: a long-running native\n app (KiCad / Fusion 360), a process you supervise, or an external client that\n dials back in (native-browser). Steal their bridge.json fields and verb shapes.\n\nMy bridge should expose these verbs: <list them, e.g. foo_status, foo_do_thing>.\n\nFollow the Adom Desktop Bridge SDK conventions on this page:\n - Self-report {led, summary, tooltip} from the health endpoint (AD is a pure\n renderer — it never invents your status).\n - Return rich, structured JSON with an _hint on every response so the calling AI\n knows what to do next; on a slow operation, return a statusVerb to poll.\n - Declare statusVerb in bridge.json if any verb can run longer than ~60s (AD will\n relay it so a timeout reads as \"still running, poll this\", not \"failed\").\n\nWhen it works locally, package it with release-bridge.sh and tell me the\nbridge_install command so I can install it on my Adom Desktop from here.\n```\n\nClaude will read the reference bridges, scaffold your `bridge.json` + server, run it locally, and hand you a one-line install command. You stay in the cloud session the whole time.\n\n### Reference bridges — copy these\n\nStart with a **hello** bridge (minimal + correct), then borrow from an **advanced** one for the hard parts.\n\n| Bridge | Language | Why read it |\n|---|---|---|\n| **hello-python** | Python | The minimal bridge: `/command` + `/status` self-report + `bridge.json`. The shape every bridge follows. |\n| **hello-rust** | Rust | Same minimal shape in Rust — single-binary, no runtime deps. |\n| **native-browser** | — | Advanced: an **external client** (a Chrome extension) dials back into the bridge; shows the stable-port + reconnect pattern. |\n| **kicad** / **fusion360** | Python | Advanced: supervising a **long-running native app**, multi-screenshot arrays, `statusVerb` for slow cloud operations. |\n\nThe hello bridges live in the AD repo under `scripts/sample-bridges/`; each advanced bridge has its own wiki page (open it from AD's Bridges panel, or the apps index).\n\n### The whole dev loop is remote — install & uninstall from anywhere\n\nThis is the part that makes bridge development easy: **you never touch the Windows box by hand.** From any cloud Claude session with the `adom-desktop` CLI:\n\n```bash\n# Install (or update) a bridge from its published wiki manifest:\nadom-desktop bridge_install '{\"manifestUrl\":\"https://wiki.adom.inc/api/v1/pages/<your-bridge-page>/files/<your>-bridge-manifest.json\"}'\n\n# It appears in AD's Bridges panel immediately and starts on first verb call.\nadom-desktop bridge_list # see it registered + its LED\nadom-desktop <your_verb> '{...}' # call it — AD spawns + relays\n\n# Iterate: re-publish a higher version, then re-install — AD reaps the old\n# instance and streams the new one onto the SAME stable port (no port churn).\nadom-desktop bridge_install '{\"manifestUrl\":\"...\",\"force\":true}'\n\n# Remove it cleanly when done:\nadom-desktop bridge_uninstall '{\"name\":\"<your-bridge>\"}'\n```\n\nIn the AD GUI, the same flows are one click: **\"+ Bridge\"** (top of the Bridges panel) installs from a manifest URL, and each bridge card has **Uninstall** in its pop-out. Publishing the manifest + zip is a single `release-bridge.sh <name>` run (see the SDK reference below).\n\nThat's the full toolchain — author in a cloud prompt, publish once, `bridge_install` from anywhere, iterate by re-installing. No RDP, no copying files to the desktop.\n\n---\n\n\n\n\n\n\n\n\n> **Public version of this guide:** [`apps/adom-desktop-bridges` on the Adom Wiki](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-bridges). The wiki page is the canonical user-facing copy and is what the GUI's \"+ Bridge\" install modal links to (the adom-inc GitHub repo is private). When you update this file with breaking schema changes or new lifecycle commands, also republish the wiki page via `adom-wiki page edit apps/adom-desktop-bridges --field content --body-md <this file>`.\n\nv1.8.0+ supports dynamic bridges: any zip with a `bridge.json` at its root can be installed via `adom-desktop bridge_install --manifestUrl <wiki-or-github-url>`. This lets community authors ship bridges for Altium, MATLAB, Rohde & Schwarz oscilloscopes, Keysight, OrCAD, anything — without coordinating with the Adom Desktop release cycle.\n\n## Try it now — the `hello-python` and `hello-rust` sample bridges\n\nTwo parallel reference bridges are hosted on the Adom Wiki, one per language. Both expose the same two verbs (`*_ping` / `*_echo`) under different prefixes (`hellopy_` / `hellors_`), so they can be installed simultaneously for a side-by-side demo. Both use `port: 0` (dynamic OS-assigned ports — v1.8.31+) so no collision is possible.\n\n| Flavor | Manifest URL | Verb prefix | Port | When to fork |\n|---|---|---|---|---|\n| **Python** (stdlib only, ~80 lines) | `…/hello-python-bridge-manifest.json` | `hellopy_` | 8890 | Vendor has Python bindings, fast dev iter, I/O-bound work |\n| **Rust** (`tiny_http` + serde, single static binary, ~120 lines) | `…/hello-rust-bridge-manifest.json` | `hellors_` | 8891 | No Python runtime on user's machine, CPU-bound, type safety |\n\nBoth manifest URLs are under `https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/`.\n\n### Install from the GUI\n\n1. Click **+ Bridge** in Adom Desktop's bridge bar\n2. Paste one (or both) of the manifest URLs into the dialog, click **Install**\n3. New chips appear ~2 seconds later with a small ✦ marker (third-party indicator)\n\n### Install both from the CLI\n\n```bash\n# Install the Python flavor\nadom-desktop bridge_install '{\"manifestUrl\":\"https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/hello-python-bridge-manifest.json\"}'\nadom-desktop hellopy_ping\n# → {\"success\":true,\"output\":\"Hello from the Python sample bridge v1.1.0!\",\"language\":\"python\",...}\n\n# Install the Rust flavor (can coexist with hello-python)\nadom-desktop bridge_install '{\"manifestUrl\":\"https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/hello-rust-bridge-manifest.json\"}'\nadom-desktop hellors_ping\n# → {\"success\":true,\"output\":\"Hello from the Rust sample bridge v1.1.0!\",\"language\":\"rust\",...}\n\n# Echo through both\nadom-desktop hellopy_echo '{\"message\":\"world\"}'\n# → {\"success\":true,\"output\":\"echo (python): world\",...}\nadom-desktop hellors_echo '{\"message\":\"world\"}'\n# → {\"success\":true,\"output\":\"echo (rust): world\",...}\n\n# Both appear in bridge_list\nadom-desktop bridge_list\n# → bundled three + \"hello-python\" + \"hello-rust\"\n```\n\n### Uninstall\n\n```bash\nadom-desktop bridge_uninstall '{\"name\":\"hello-python\"}'\nadom-desktop bridge_uninstall '{\"name\":\"hello-rust\"}'\n```\n\nEach bridge's cache dir + registry entry are removed; the chips disappear from the bar.\n\nThe sources live at:\n- [`scripts/sample-bridges/hello-python/`](https://github.com/adom-inc/adom-desktop/tree/main/scripts/sample-bridges/hello-python) — `bridge.json` + `server.py` (~120 lines of `http.server.BaseHTTPRequestHandler`)\n- [`scripts/sample-bridges/hello-rust/`](https://github.com/adom-inc/adom-desktop/tree/main/scripts/sample-bridges/hello-rust) — `bridge.json` + `Cargo.toml` + `src/main.rs` (~140 lines of `tiny_http`) + pre-built `hello-rust.exe` (~300 KB)\n\nFork whichever flavor matches your target's natural ecosystem.\n\n## Directory layout\n\n```\nmy-bridge/\n├── bridge.json ← REQUIRED — schema below\n├── BRIDGE_VERSION ← optional, semver line (kept in sync with bridge.json's version field)\n├── server.py ← or server.js, or my-bridge.exe — your bridge's entrypoint\n├── handlers/ ← your code\n│ ├── ...\n└── README.md ← optional, recommended\n```\n\n## `bridge.json` schema\n\n```json\n{\n \"manifest_version\": 1,\n \"name\": \"altium\",\n \"displayName\": \"Altium Designer\",\n \"version\": \"1.0.0\",\n \"description\": \"One-line description shown in bridge_list output.\",\n \"homepage\": \"https://github.com/your-org/adom-bridge-altium\",\n \"author\": \"Your Org\",\n \"license\": \"MIT\",\n \"spawn\": {\n \"kind\": \"python\",\n \"entrypoint\": \"server.py\",\n \"port\": 0,\n \"healthEndpoint\": \"/status\",\n \"stopMethod\": \"kill\",\n \"killImageName\": \"python.exe\"\n },\n \"verbPrefixes\": [\"altium_\"],\n \"verbs\": [\n \"altium_open_project\",\n \"altium_export_step\",\n \"altium_run_drc\"\n ],\n \"dependencies\": {\n \"python\": \">=3.11\",\n \"altium-designer\": \">=24\"\n },\n \"category\": \"EDA\",\n \"tags\": [\"pcb\", \"altium\", \"commercial\"]\n}\n```\n\n### Field reference\n\n- **`name`** (required, lowercase, no spaces). The directory name + cache key. Must be unique across the registry.\n- **`displayName`** — human-friendly shown in `bridge_list` + future GUI bridge panel.\n- **`version`** (required, semver). Each ship bumps this.\n- **`spawn.kind`** — `python` | `node` | `exe`. Determines how Adom Desktop launches the bridge.\n - `python` → Adom Desktop finds Python in PATH and runs `python <entrypoint> --port <port>`. Good when the vendor's SDK has Python bindings.\n - `node` → Adom Desktop finds Node in PATH and runs `node <entrypoint> --port <port>`. Good for ecosystem reuse (Puppeteer, Playwright, etc.).\n - `exe` → Adom Desktop runs `<entrypoint> --port <port>` directly. The entrypoint must be a pre-built executable in the zip. Good for Rust, Go, .NET single-file builds, etc. — no runtime install required on the user's machine.\n- **`spawn.entrypoint`** — relative path inside the bridge dir (e.g. `server.py`, `server.js`, `my-bridge.exe`).\n- **`spawn.port`** — **set to `0`** (v1.8.31+ recommended). Adom Desktop picks an OS-assigned ephemeral port at spawn time and passes it to your `--port` arg. No collision possible between bridges. Hardcoded ports still work for back-compat but are deprecated — anything you pick will eventually collide with someone (Hydrogen Desktop's bridges already used 8772/8773/8851; static-port collisions are the #1 source of bridge \"won't start\" bugs). Bridge ports are internal plumbing — callers always route through `adom-desktop <prefix>_<verb>`, never reach a bridge directly.\n- **`spawn.healthEndpoint`** — path Adom Desktop polls to confirm the bridge is alive (e.g. `/status`, `/health`). v1.8.31+ accepts a path-only fragment (recommended) and constructs the URL using the runtime port. Legacy full URLs (`http://127.0.0.1:8881/status`) still parse — the host+port get replaced at runtime.\n- **`spawn.stopMethod`** — `kill` (process-handle Child::kill, NOT `taskkill /im` — that would nuke ALL Pythons on the box), `sigterm` (graceful), or `graceful_endpoint` (POST to an endpoint).\n- **`spawn.killImageName`** — legacy field, only used if Adom Desktop falls back to image-name kill. With v1.8.31's dynamic ports + process-handle kill this is rarely needed. Set to your process name for safety (`python.exe` / `node.exe` / your binary name).\n- **`verbPrefixes`** — list of CLI verb prefixes this bridge owns (e.g. `[\"altium_\"]`). All `altium_*` verbs route here.\n- **`verbs`** — explicit verb list. Used in `bridge_list` to advertise capabilities; doesn't affect routing.\n\n## Packaging + shipping\n\nUse the same `scripts/release-bridge.sh` flow Adom Desktop uses for its own bridges. Adapted for third parties:\n\n```bash\n# In your bridge repo:\npython3 -c \"\nimport zipfile, os, fnmatch\nEXCLUDE = ['*/__pycache__/*', '*.pyc', '.history/*', '*/node_modules/*']\nwith zipfile.ZipFile('my-bridge-v1.0.0.zip', 'w', zipfile.ZIP_DEFLATED) as zf:\n for root, dirs, files in os.walk('.'):\n for f in files:\n rel = os.path.relpath(os.path.join(root, f), '.')\n if any(fnmatch.fnmatch(rel.replace(os.sep, '/'), g) for g in EXCLUDE):\n continue\n zf.write(os.path.join(root, f), rel)\n\"\n\n# Compute SHA256 of the zip\nsha256sum my-bridge-v1.0.0.zip\n\n# Write the manifest the GUI polls\ncat > my-bridge-manifest.json <<EOF\n{\n \"manifest_version\": 1,\n \"version\": \"1.0.0\",\n \"url\": \"https://your-host/my-bridge-v1.0.0.zip\",\n \"sha256\": \"<sha256 from above>\",\n \"size\": <bytes>,\n \"released_at\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"\n}\nEOF\n```\n\nHost both files anywhere reachable by HTTPS — GitHub Pages, your own CDN, the Adom Wiki if you contribute to the official registry.\n\n## Installing\n\nUsers install your bridge with one command:\n\n```bash\nadom-desktop bridge_install '{\"manifestUrl\":\"https://your-host/my-bridge-manifest.json\"}'\n```\n\nThe GUI:\n1. Fetches `my-bridge-manifest.json`\n2. Downloads the referenced zip\n3. SHA256-verifies against the manifest\n4. Extracts to `%LOCALAPPDATA%\\Adom Desktop\\bridges-cache\\<name>\\` (the `name` comes from your `bridge.json`)\n5. Rescans the runtime registry — your bridge is now discoverable via `bridge_list`\n\nThe next CLI call to one of your `verbPrefixes` auto-spawns the bridge process.\n\n## Updating\n\nBump `bridge.json`'s `version` + `BRIDGE_VERSION`, rebuild the zip, re-upload, update the manifest. Users:\n\n```bash\nadom-desktop refresh_bridges # picks up new versions from manifest URLs they previously installed from\n```\n\n(Currently `refresh_bridges` only polls the three core wiki manifests. The follow-up work — tracking third-party manifest URLs in user state so they auto-refresh — lands in v1.8.x.)\n\n## Lifecycle controls (from cloud Docker, or local GUI)\n\n```bash\nadom-desktop bridge_list # see everything\nadom-desktop bridge_pause '{\"name\":\"altium\"}' # stop routing verbs\nadom-desktop bridge_resume '{\"name\":\"altium\"}'\nadom-desktop bridge_uninstall '{\"name\":\"altium\"}' # remove from cache\n```\n\n## Trust + security\n\nFor v1.8.0 the trust model is SHA256-verification of the wiki-hosted zip against the manifest. Bridges run with the user's privileges; install from sources you trust.\n\nFuture work (v1.9.0+):\n- Optional Ed25519 signing of bridge zips with the Adom-Inc public key for \"verified\" status\n- Per-bridge sandboxing (limited filesystem / network access declared in manifest `permissions` field)\n- Community submission flow on the wiki so contributors don't need a GitHub account\n\n## Reference implementations\n\nEvery bridge below has its own dedicated wiki page with hero, install/uninstall recipes, and a \"fork this\" walkthrough. The **[bridges registry](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges-registry)** is the canonical directory — both humans browse it and the GUI's Browse tab parses it.\n\n**Hello-world starter templates** — fork these for a 5-minute \"does this thing work?\" loop:\n- **[hello-python](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-hello-python-bridge)**: `scripts/sample-bridges/hello-python/` — ~80 lines, stdlib only, `kind:python`\n- **[hello-rust](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-hello-rust-bridge)**: `scripts/sample-bridges/hello-rust/` — ~140 lines, `tiny_http` + serde, `kind:exe`, single 300-KB static binary\n\n**Adom-shipped bridges** — canonical full-complexity examples; fork their layout for a real vendor integration:\n- **[kicad](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-kicad-bridge)**: `plugins/kicad/` — Python, complex, multi-instance (one bridge per KiCad GUI exe)\n- **[puppeteer](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-puppeteer-bridge)**: `plugins/puppeteer/` — Node, single-instance, recording-capable\n- **[fusion360](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-fusion-bridge)**: `plugins/fusion360/` — Python, simpler, command-passthrough to Fusion add-in\n\n## Get listed\n\nOnce your bridge is stable, add it to the **[bridges registry](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges-registry)** — that's the canonical machine-readable directory the GUI's Browse tab parses to discover what's installable. Two paths:\n\n1. **PR-based** (preferred): open a PR against [`adom-inc/adom-desktop`](https://github.com/adom-inc/adom-desktop) adding your row to the \"Community bridges\" table in this file (`plugins/BRIDGES.md`). A maintainer mirrors it to the wiki on the next sync. See the registry page's [submission section](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges-registry#how-to-submit-a-bridge) for the row format + review criteria.\n2. **Direct wiki edit**: if you have an Adom account, `adom-wiki page edit apps/adom-desktop-bridges-registry --field content --body-md <patch>`. Maintainers review and either keep or revert.", "author": { "id": "695820315b5f1e4db2fcf602", "name": "Kyle Bergstedt",@@ -71,7 +71,7 @@ }, "metadata": {}, "created_at": "2026-05-28T05:28:40.558Z",- "updated_at": "2026-06-26T16:09:03.570Z",+ "updated_at": "2026-06-26T16:10:28.725Z", "skills": [], "author_name": "John Lauer", "changelog": "Set descriptive title to the Adom Desktop - <name> naming convention for app-index consistency"