name: adom-desktop-bridge-author
description: Use this skill when the user says they want to write/author/build a third-party bridge for adom-desktop, integrate a new desktop app (Altium, MATLAB, SolidWorks, an oscilloscope, etc.) with cloud Claude, fork the hello sample bridge, ship a bridge to the wiki, or asks how custom CLI verbs get routed into adom-desktop. Walks them through scaffolding a fresh bridge from the canonical template, wiring bridge.json, writing the HTTP server, packaging the zip, hosting it, and verifying install via adom-desktop bridge_install.
Authoring an Adom Desktop bridge
A bridge is a small HTTP server (Python, Node, or any executable) that adom-desktop spawns on demand and proxies cloud CLI requests to. Once installed, every adom-desktop <verb> '<json>' whose verb starts with your bridge's prefix routes to your server. Adom Desktop v1.8.0+ supports dynamic bridges installed at runtime from a wiki manifest URL — no fork of adom-desktop required.
When to use this skill
The user wants to write a bridge if they say any of:
- "I want to integrate Altium / MATLAB / Eagle / SolidWorks / OrCAD / my oscilloscope / our internal CAD tool with adom-desktop"
- "Help me build a third-party adom-desktop bridge"
- "How do I add a new CLI verb to adom-desktop?" (the answer is almost always "ship it as a bridge, not by forking adom-desktop")
- "Fork the hello sample bridge"
- "Make my bridge installable via the install modal"
If they want to USE existing bridges (kicad, fusion, puppeteer), that's the adom-desktop skill, not this one.
Prerequisites
The user needs adom-desktop installed + running on their laptop and reachable from this container:
adom-desktop ping
If that fails, walk them through the install flow at https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop first.
Workflow
Step 1 — pick a bridge name + verb prefix
Ask the user:
- What's the host app this bridge integrates? (e.g. "Altium Designer")
- What lowercase one-word name should the bridge use? (e.g.
altium) — this is the cache directory name + the manifest key - What verb prefix should they own? Conventionally
<name>_*, e.g.altium_open_project,altium_export_step. Prefixes are first-come-first-served across the registry; check existing bridges withadom-desktop bridge_listso you don't collide.
Reserved prefixes that are already taken:
kicad_*(kicad bridge)browser_*,desktop_recorder_*,desktop_record_*(puppeteer bridge)fusion_*(fusion360 bridge)hello_*(sample bridge)
Reserved bridge ports: 8770–8779 (Adom-owned), 8851 (puppeteer). Pick something in 8800–9000 for new bridges.
Step 2 — scaffold from the hello sample
The hello sample bridge is the minimum viable example — a ~80-line stdlib-Python HTTP server. Have the user download + extract it:
# On the user's laptop (run via adom-desktop shell_execute or have them run it themselves):
curl -L https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/hello-bridge-v1.0.0.zip \
-o /tmp/hello-bridge.zip
mkdir -p ~/my-bridge && cd ~/my-bridge
python3 -c "import zipfile; zipfile.ZipFile('/tmp/hello-bridge.zip').extractall()"
ls -la
They now have bridge.json, BRIDGE_VERSION, server.py, README.md. Rename the directory + edit each file to match the new bridge name/verbs.
For Node-based bridges, look at the puppeteer bridge zip instead:
https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/puppeteer-bridge-v1.0.0.zip
For app-with-plugin-API bridges (Altium / SolidWorks / etc.), the fusion360 bridge is the right reference — it has both a bridge process AND an in-host add-in:
https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/fusion360-bridge-v1.0.0.zip
Step 3 — edit bridge.json
The schema is documented at https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-bridges#bridgejson-schema. Minimum required fields:
{
"manifest_version": 1,
"name": "<lowercase-name>",
"displayName": "<human-readable>",
"version": "1.0.0",
"description": "One-line description that shows up in bridge_list.",
"spawn": {
"kind": "python",
"entrypoint": "server.py",
"port": 8XXX,
"healthEndpoint": "http://127.0.0.1:8XXX/status",
"stopMethod": "kill",
"killImageName": "python.exe"
},
"verbPrefixes": ["<name>_"],
"verbs": ["<name>_verb1", "<name>_verb2"]
}
Keep verbs accurate — bridge_list advertises this to discovery. Don't list verbs you haven't implemented.
If your bridge supports project-watching (file watcher → auto-resync to Docker), add a watcher block:
"watcher": {
"supports": true,
"displayName": "<Watcher name>",
"defaultGlobs": ["*.your-ext"],
"configKey": "project_watch"
}
The GUI renders an eye-pip on the chip when watching is active.
Recommended: docs URL + platforms map (v1.8.14+)
These two optional fields make your bridge a first-class citizen of the GUI:
"docs": "https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/your-bridge-name",
"platforms": {
"windows": { "supported": true },
"macos": { "supported": false, "reason": "Mac port pending — Win32 deps in handlers/window_automation.py need a Quartz equivalent." },
"linux": { "supported": false, "reason": "Same as macOS." }
}
docs: surfaces in the chip's right-click context menu as "View on wiki" (top item). Use it. If you don't have a wiki page yet, publish one first (seeadom-wiki page publishvia theadom-wikiskill).platforms: an honest declaration of which OSes the bridge actually works on. Keys arewindows/macos/linux. The GUI shows the support status in the tooltip + warns (non-blocking) in the install modal if the user is on asupported: falseOS. Provide areasonstring when something doesn't work — the user sees it verbatim, so explain why.
Tell the user don't lie in platforms. If they haven't tested macOS, leave it as {"supported": false, "reason": "Untested on macOS so far"} rather than claiming support. The whole point of the field is honesty — better to set expectations than burn a user who installs a bridge that silently fails.
Step 4 — implement the server
The HTTP contract is two endpoints:
GET /status→{"ok": true, "version": "..."}— used by adom-desktop to confirm the bridge is alivePOST /commandbody{"verb": "<name>_<verb>", "args": {...}}→{"success": true, "output": "..."}or{"success": false, "error": "..."}
The hello sample's server.py is the canonical shape — fork it and replace the verb handlers with yours.
When implementing handlers:
- Validate
argsstrictly. Return{"success": false, "error": "..."}for invalid input; don't 500. - Keep responses small. If returning large payloads (file contents, binary blobs), write to disk first and return the path; Docker will use
pull_fileto fetch. - Long-running operations (>5s) should return immediately with a job handle, then expose a
<name>_statusverb for polling.
Step 5 — test locally via a localhost manifest
Before publishing to the wiki, test with a local HTTP server:
# Build the zip
cd ~/my-bridge
python3 -c "
import zipfile, os, fnmatch
EXCLUDE = ['*/__pycache__/*', '*.pyc', '.history/*']
with zipfile.ZipFile('../my-bridge-v1.0.0.zip', 'w', zipfile.ZIP_DEFLATED) as zf:
for root, dirs, files in os.walk('.'):
for f in files:
rel = os.path.relpath(os.path.join(root, f), '.')
if any(fnmatch.fnmatch(rel.replace(os.sep, '/'), g) for g in EXCLUDE):
continue
zf.write(os.path.join(root, f), rel)
"
# Compute sha256 + size
SHA=$(sha256sum ../my-bridge-v1.0.0.zip | awk '{print $1}')
SIZE=$(stat -c%s ../my-bridge-v1.0.0.zip)
# Write the manifest
cat > ../my-bridge-manifest.json <<EOF
{
"manifest_version": 1,
"version": "1.0.0",
"url": "http://127.0.0.1:9999/my-bridge-v1.0.0.zip",
"sha256": "$SHA",
"size": $SIZE,
"released_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
}
EOF
# Serve them on a local HTTP port
cd .. && python3 -m http.server 9999 &
LOCAL_SERVE_PID=$!
# Install through the localhost URL
adom-desktop bridge_install '{"manifestUrl":"http://127.0.0.1:9999/my-bridge-manifest.json"}'
# Try a verb
adom-desktop <name>_<verb> '{...}'
# Stop the local server when done
kill $LOCAL_SERVE_PID
This roundtrip exercises the entire install path (download + sha256 verify + extract + registry rescan + spawn-on-first-call) without uploading anything yet.
Step 6 — ship to the wiki (recommended) or any HTTPS host
Two options:
Option A — Adom Wiki (recommended for bridges they want the community to find):
adom-wiki asset upload apps/adom-desktop \
--asset-type file \
--file ../my-bridge-v1.0.0.zip \
--caption "my-bridge v1.0.0"
# Then upload a manifest pointing at that zip's wiki URL
# (Replace the local-server URL with the wiki static URL the upload command prints.)
adom-wiki asset upload apps/adom-desktop \
--asset-type file \
--file ../my-bridge-manifest.json \
--caption "my-bridge manifest v1.0.0"
Suggest the user also write a dedicated apps/<my-bridge>-bridge wiki page documenting their bridge — same shape as the reference bridges. Use the adom-wiki skill for the publish flow.
Option B — any HTTPS host (GitHub Releases, your own CDN, S3):
# Upload both files anywhere reachable by HTTPS, then:
adom-desktop bridge_install '{"manifestUrl":"https://your-host/my-bridge-manifest.json"}'
Cloud users can install from any reachable URL — they're not locked to the wiki.
Step 7 — verify end-to-end + iterate
# See it in the registry
adom-desktop bridge_list
# Trigger your verbs
adom-desktop <name>_<verb> '{...}'
# Inspect the install dir (right-click the chip in the GUI → Open install folder)
# OR programmatically:
adom-desktop shell_execute '{"command":"powershell -NoProfile -Command \"Get-ChildItem $env:LOCALAPPDATA\\\\\\\"Adom Desktop\\\\\\\"\\\\bridges-cache\\\\<name>\"","timeout_secs":5}'
# After edits, bump bridge.json's version + BRIDGE_VERSION, rebuild the zip, re-upload, then:
adom-desktop refresh_bridges
To ship a breaking change without leaving users on the old version: bump the manifest's version field. On next launch + refresh, adom-desktop fetches the new manifest, compares versions, downloads + verifies the new zip, atomically swaps the cache dir.
Lifecycle controls available to your bridge users
These verbs are built into adom-desktop — you don't implement them, just know they exist so you can mention them when users ask:
| Verb | What it does |
|---|---|
bridge_list | List all installed bridges + their metadata |
bridge_install | Install from a manifest URL |
bridge_uninstall | Remove from cache (returns to the bundled version if there was one) |
bridge_pause | Stop routing verbs (process stays running, verbs return "paused") |
bridge_resume | Resume routing |
refresh_bridges | Re-poll all known manifest URLs for updates |
From the Adom Desktop GUI: right-click any chip in the bridge bar for Pause / Resume / Open install folder / Uninstall.
Reference pages on the wiki
These pages contain full architecture writeups for the three bundled bridges. Use them when the user is stuck on a pattern you've already seen one of these solve:
- Bridge SDK guide — schema, packaging, lifecycle, trust model
- KiCad bridge reference — multi-instance, reverse-bridge into the host app
- Puppeteer bridge reference — single-instance Node + session-profile isolation + recording
- Fusion 360 bridge reference — passthrough-to-add-in pattern (use for Altium, SolidWorks, Eagle)
Trust + security gotchas to warn users about
- The trust model is SHA256-verification of the zip against the manifest. Users install with their own privileges — bridges can read/write any file the user can. Tell them to install only from sources they trust.
- If you publish to the wiki, anyone in the org can see it. For internal-only bridges, host the manifest behind auth (HTTPS basic, signed URLs, etc.) — adom-desktop's
bridge_installhonors HTTP redirects + auth headers passed via--auth-header. - Don't put secrets in the bridge zip. The unpacked cache dir is readable by anyone on the same machine. Read secrets at runtime from env vars / OS keychain / a
.envnext to the bridge cache that the user populates manually.
When something goes wrong
bridge_installsays SHA mismatch — the zip you uploaded doesn't match the manifest'ssha256. Recompute and re-upload the manifest.- Verb returns "bridge not found" — the prefix you declared doesn't match the verb. Check
adom-desktop bridge_listto see what prefixes you actually registered. - Bridge process won't start — adom-desktop reports the spawn failure. Common causes: wrong
entrypoint,spawn.kindmismatch (Python file asnode), missing dependencies. Check the GUI's activity log. - Bridge runs but verbs return "process exited" — your handler probably crashed. Add
try/exceptaround your dispatcher and log to stderr; adom-desktop forwards stderr lines to the GUI log. - Open install folder shows a "Location is not available" dialog — the cache dir doesn't exist on the user's real filesystem (probably installed from a sandboxed context). Uninstall + reinstall from a clean session. See https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-bridges for the full troubleshooting.