Adom Desktop - Bridge SDK
Public Made by Adomby adom
Bridge SDK guide: bridge.json schema, kind:python/node/exe, hello-python + hello-rust reference templates, packaging + lifecycle commands.
name: bridge-dev-template user-invocable: false description: "TEMPLATE / reference for building, publishing, and maintaining ANY Adom Desktop bridge (the cloud Claude/Codex thread that owns a bridge copies this into its own dev-skills/ and fills it in). NOT a user-facing skill — general users want the bridge's own user skill. Read this when editing bridge code, cutting a new version, publishing to the wiki, wiring a runtime/heavy-dependency cold-start self-heal, or understanding the pkg-vs-release-vs-bundled artifact model. Trigger words: bridge dev, build adom-desktop bridge, publish bridge, ship bridge, bridge_install, adom-desktop--bridge, cold-start self-heal, bridge readiness internals, bridge release, bridge manifest, updateManifestUrl, pkg vs release vs bundled."
bridge-dev-template — building & shipping an Adom Desktop bridge
The canonical source of truth for the <bridge> bridge is its wiki repo
(wiki.adom.inc/adom/adom-desktop-<bridge>-bridge), cloud-owned (like the kicad/fusion/puppeteer
bridges) so bridge iteration is decoupled from adom-desktop core. Read CLAUDE.md (ownership boundary)
and PUBLISHING.md (step-by-step recipe) in this repo alongside this skill.
The THREE artifacts — know which is which (this is the #1 confusion)
A user installs TWO different things from this one page. They are not the same:
| Artifact | What it is | Who installs it | How |
|---|---|---|---|
| pkg tarball (Packages tab) | the container-side Claude/Codex skills (SKILL.md + skills/*) — docs that teach the cloud AI how to drive <bridge>. No bridge runtime. |
a cloud container | adom-wiki pkg install adom/adom-desktop-<bridge>-bridge, or auto via AD's sync_skills |
| release zip (Releases tab) | the bridge RUNTIME (entrypoint + bridge code + bridge.json + BRIDGE_VERSION; heavy deps reconstructed on spawn) — the code that actually runs on the desktop |
Adom Desktop, streamed into bridges-cache\<bridge> |
bridge_install {manifestUrl} / refresh_bridges (via updateManifestUrl) |
| bundled seed (inside the AD installer) | a copy of the same runtime, shipped in the NSIS for first-run/offline | every AD install | ships with Adom Desktop; superseded by a newer cache copy (cache-over-bundled, numeric version) |
So: the pkg is skills (container). The bridge is the zip (desktop) + the bundled seed. Keep the pkg description saying exactly that, or people think the tarball contains the bridge — it does not.
How <bridge> reaches a container — auto-discovery → auto-install → verbs
The full chain when a user on a fresh container says "":
- Discovery. The wiki's
discoverindex (fed by this page'sdiscovery_triggers— which should lead with your bridge's natural phrases, e.g. its short name + the top user actions) matches the phrase. The container's regeneratedadom-wiki-discoverskill surfaces this page, so the AI proposes it. - Auto-install the pkg. The AI runs
adom-wiki skills install adom/adom-desktop-<bridge>-bridge(a.k.a.pkg install). That extracts the tarball and runsinstall.sh, which dropsSKILL.md+skills/*into both~/.claude/skills/and~/.codex/skills/. The<bridge>user skill is now loaded. - The skill calls the bridge. The user skill's
SKILL.mdtells the AI to runadom-desktop <prefix>_*verbs. AD routes every<prefix>_*verb to the<bridge>bridge. - The bridge runtime. It's already on the desktop: AD ships
<bridge>as a bundled seed (and supersedes it from this page's release zip viaupdateManifestUrl/bridge_install). On a truly fresh PC the cold-start self-heal installs<your runtime / heavy dependency>on first use.
Two install paths, keep BOTH healthy:
- Discovery path (above) — works once
discovery_triggersinclude the user phrases + the discover index is regenerated. This is the "user said<bridge>→ AI installs the skill" path. - AD
sync_skillspath — AD's dailymaybe_background_skill_syncreadsbridge_list→ each bridge'sskillPkg→pkg installs it into the container. For this to work AD must reportskillPkg = adom/adom-desktop-<bridge>-bridgefor your bridge. AD now derives that slug from the bridge'sdocsfield automatically, except for any bridge hardcoded intoAD_CORE_SKILL_BRIDGESinbridge_registry.rs(that list forcesskillPkg = nullfor bridges whose skill historically lived in the AD-core pkg). If your bridge's skill ships from THIS page (not core), make sure your name is NOT in that list — if it is, removing it is an AD-core handoff item.
Repo layout
src/— the runnable bridge (what the release zip contains, at zip-root). Entrypoint + bridge modules,bridge.json,BRIDGE_VERSION, dependency manifest/lockfile, any html/icons the runtime needs.SKILL.md(root) — the user skill (name: <bridge>). Deployed to~/.claude/skills/adom-desktop-<bridge>-bridge/.dev-skills/<bridge>-bridge-dev/— THIS dev skill.dev-skills/<bridge>-bridge-publish/— the publish recipe. Both are source-only (indev-skills/, NOTskills/): NOT in the installable pkg and NOT on the wiki Skills tab — you get them viaadom-wiki repo clone. Everyday users only get the<bridge>user skill.adom-bridge-<bridge>-manifest.json(root) — the streaming manifest AD'supdateManifestUrlpoints at.package.json(root) — the adompkg descriptor (slug/version/type/description) forpkg publish. NOT a language/runtime package descriptor (e.g. not the nodepackage.json) — keep them distinct.install.sh/uninstall.sh— deploySKILL.md+skills/*into the container's~/.claude/skills/.page.json,README.md,*.pngheroes — the wiki page presentation (the heroes are gitignored so the container pkg stays lean — they're already uploaded as page assets).
Cold-start self-heal — the architecture pattern (the reason a cloud-owned repo earns its keep)
On a fresh PC the installer pre-installs neither your bridge's language runtime nor its heavy dependency.
Without a self-heal, the first <prefix>_<verb> fails with a generic launch error + a dev string. The
pattern that fixes it — adapt the names to your runtime:
- A detect/install/ready module —
detectDependency()(validate the ACTUAL artifact, not "a dir exists"),installDependency()(programmatic, pinned to the exact version your code expects — never a floating 'latest'/'stable', or the launch path won't find what it pinned against), an install-state singleton (dedup concurrent installs + report progress),ensureReady()(the gate — kicks off a background install and returns immediately),readiness()(powers the verb + the status chip). - The first action verb gates on readiness: missing dependency → kick off a non-blocking install +
return
{errorCode:"<dependency>_installing", statusVerb:"<prefix>_readiness", _hint:"poll then retry"}. Install fail → a specific<dependency>_install_failed/<dependency>_download_failederror — never silently continue into a doomed launch. - A
<prefix>_readinessverb (poll) + a<prefix>_prewarmverb (install/warm without doing the user-visible action). /status(or yourhealthEndpoint) emitsled/summary/tooltiptruthfully (yellow installing, red failed, green ready). AD paints these verbatim.- The language runtime itself is handled by AD core BEFORE the bridge spawns (e.g. a
node_not_found→desktop_install_nodeflow) — NOT your bridge's job. The bridge can't bootstrap the very runtime it needs to run; AD installs the runtime, then spawns you. - The one thing the bridge can't do: pre-warm before first use (it isn't running until the first call).
AD/HD must call
<prefix>_prewarmon embedded first-run — track that as a handoff item.
Worked example (pup's case): the puppeteer bridge's runtime dependency is Chrome for Testing. Its module is
src/chrome.js(detectChrome/installChromevia the bundled@puppeteer/browserspinned to puppeteer's expected buildId /ensureChromeReady/readiness);browser_open_windowis the gated first verb (CfT path only; its native-drive path skips the gate), returningchrome_for_testing_installing; and it exposesbrowser_readiness+browser_prewarm. Node is the language runtime AD core installs first (node_not_found→desktop_install_node). That's the concrete shape of every bullet above.
Publishing (the wiki has TWO storage layers — don't mix them)
Full recipe in PUBLISHING.md. The load-bearing rules:
- The bridge
.zip→ a RELEASE asset (adom-wiki release create+release upload). NEVERrepo pusha.zip— it's gitignored and silently skipped; the manifest URL would 404. - The manifest JSON + source → the git repo (
adom-wiki repo push). The manifest'surlis the RELEASE download URL; itssha256/sizeMUST match the served asset (releases preserve your sha; re-download to confirm orbridge_installrejects it). - The skills → a PKG (
adom-wiki pkg publish). The pkg description (rootpackage.json) must say "container-side skills."pkg packhonors.gitignorefor exclusion (no separate ignore file) — that's why the heroes are gitignored, to keep the tarball lean. Set per-version notes withadom-wiki pkg notes. - Version display gotcha: a RELEASE shows up in
adom-wiki pkg infoas a row withtype:null(a wiki UI conflation). The real pkg rows havetype:"app". Userelease listfor true releases. bridge.json+BRIDGE_VERSIONlockstep, and keep the version > the AD bundled seed so cache-over-bundled wins.- Declare
updateManifestUrlin yourbridge.json(the full URL of this page's manifest) so a user's AD re-polls THIS page for updates — and the same value lives in the bundled seed AD ships, so a fresh install auto-updates with no user opt-in. A republish that drops it makes AD fall back to the legacy convention and stop seeing your updates.
Testing a new version on a real desktop
adom-desktop targets # multiple desktops → --target <name> is required
adom-desktop --target <name> bridge_install '{"manifestUrl":"https://wiki.adom.inc/api/v1/pages/adom-desktop-<bridge>-bridge/files/adom-bridge-<bridge>-manifest.json","force":true}'
adom-desktop --target <name> <prefix>_readiness '{}' # proves the new code is live
- The language runtime is missing on the box →
<prefix>_*returns the runtime-not-found error from AD core before the bridge spawns (correct). That's a genuine cold-start demo of the FIRST link, but it can't exercise your self-heal module (there's no runtime to run it). - A box WITH the runtime but WITHOUT the heavy dependency is the only place to see the full dependency
self-install live (
<prefix>_<verb>→<dependency>_installing→ poll<prefix>_readiness→ succeeds). Most dev boxes already have the dependency, soreadiness()just returnsready:true. - The streamed zip is source-only; AD reconstructs heavy deps in the cache on first spawn (ship prebuilt native deps where possible). The bundled seed already has its deps materialized. Don't put reconstructable dependency trees (node_modules, venvs, vendored build output) in the zip.
Don't
- Don't edit the bundled seed in the adom-desktop repo (
plugins/<bridge>) as if it's live, and don't runrelease-bridge.sh <bridge>from the adom-desktop repo — that clobbers this cloud-owned page. (The adom-desktop release script hard-refuses the cloud-owned bridges; the rule is "don't try.") - Don't ship the bridge runtime in the pkg tarball or the skills in the release zip. Two artifacts, two homes.
- Don't pin your heavy dependency to a floating channel ('latest'/'stable') — pin to the exact version your code launches against, or the install and the launch disagree.
---
name: bridge-dev-template
user-invocable: false
description: "TEMPLATE / reference for building, publishing, and maintaining ANY Adom Desktop bridge (the cloud Claude/Codex thread that owns a bridge copies this into its own dev-skills/ and fills it in). NOT a user-facing skill — general users want the bridge's own user skill. Read this when editing bridge code, cutting a new version, publishing to the wiki, wiring a runtime/heavy-dependency cold-start self-heal, or understanding the pkg-vs-release-vs-bundled artifact model. Trigger words: bridge dev, build adom-desktop bridge, publish bridge, ship bridge, bridge_install, adom-desktop-<bridge>-bridge, cold-start self-heal, bridge readiness internals, bridge release, bridge manifest, updateManifestUrl, pkg vs release vs bundled."
---
<!-- Template — copy into your bridge's own dev-skills/, replace <bridge>/<prefix>/<Bridge Name>, keep the structure. -->
# bridge-dev-template — building & shipping an Adom Desktop bridge
The canonical source of truth for the `<bridge>` bridge is **its wiki repo**
(`wiki.adom.inc/adom/adom-desktop-<bridge>-bridge`), cloud-owned (like the kicad/fusion/puppeteer
bridges) so bridge iteration is decoupled from adom-desktop core. Read `CLAUDE.md` (ownership boundary)
and `PUBLISHING.md` (step-by-step recipe) in this repo alongside this skill.
## The THREE artifacts — know which is which (this is the #1 confusion)
A user installs TWO different things from this one page. They are not the same:
| Artifact | What it is | Who installs it | How |
|---|---|---|---|
| **pkg tarball** (Packages tab) | the **container-side Claude/Codex skills** (`SKILL.md` + `skills/*`) — docs that teach the cloud AI how to drive `<bridge>`. **No bridge runtime.** | a cloud **container** | `adom-wiki pkg install adom/adom-desktop-<bridge>-bridge`, or auto via AD's `sync_skills` |
| **release zip** (Releases tab) | the **bridge RUNTIME** (entrypoint + bridge code + `bridge.json` + `BRIDGE_VERSION`; heavy deps reconstructed on spawn) — the code that actually runs on the desktop | **Adom Desktop**, streamed into `bridges-cache\<bridge>` | `bridge_install {manifestUrl}` / `refresh_bridges` (via `updateManifestUrl`) |
| **bundled seed** (inside the AD installer) | a copy of the same runtime, shipped in the NSIS for first-run/offline | every AD install | ships with Adom Desktop; superseded by a newer cache copy (cache-over-bundled, numeric version) |
So: **the pkg is skills (container). The bridge is the zip (desktop) + the bundled seed.** Keep the
pkg description saying exactly that, or people think the tarball contains the bridge — it does not.
## How `<bridge>` reaches a container — auto-discovery → auto-install → verbs
The full chain when a user on a fresh container says *"<the phrase that triggers your bridge>"*:
1. **Discovery.** The wiki's `discover` index (fed by this page's `discovery_triggers` — which should lead
with your bridge's natural phrases, e.g. its short name + the top user actions) matches the phrase. The
container's regenerated `adom-wiki-discover` skill surfaces this page, so the AI proposes it.
2. **Auto-install the pkg.** The AI runs `adom-wiki skills install adom/adom-desktop-<bridge>-bridge`
(a.k.a. `pkg install`). That extracts the tarball and runs `install.sh`, which drops `SKILL.md` +
`skills/*` into **both `~/.claude/skills/` and `~/.codex/skills/`**. The `<bridge>` user skill is now loaded.
3. **The skill calls the bridge.** The user skill's `SKILL.md` tells the AI to run `adom-desktop <prefix>_*`
verbs. AD routes every `<prefix>_*` verb to the `<bridge>` bridge.
4. **The bridge runtime.** It's already on the desktop: AD ships `<bridge>` as a **bundled seed** (and
supersedes it from this page's release zip via `updateManifestUrl` / `bridge_install`). On a truly fresh
PC the cold-start self-heal installs `<your runtime / heavy dependency>` on first use.
**Two install paths, keep BOTH healthy:**
- **Discovery path (above)** — works once `discovery_triggers` include the user phrases + the discover
index is regenerated. This is the "user said `<bridge>` → AI installs the skill" path.
- **AD `sync_skills` path** — AD's daily `maybe_background_skill_sync` reads `bridge_list` → each bridge's
`skillPkg` → `pkg install`s it into the container. For this to work AD must report
`skillPkg = adom/adom-desktop-<bridge>-bridge` for your bridge. AD now derives that slug from the bridge's
`docs` field automatically, **except** for any bridge hardcoded into `AD_CORE_SKILL_BRIDGES` in
`bridge_registry.rs` (that list forces `skillPkg = null` for bridges whose skill historically lived in
the AD-core pkg). If your bridge's skill ships from THIS page (not core), make sure your name is NOT in
that list — if it is, removing it is an AD-core handoff item.
## Repo layout
- `src/` — the runnable bridge (what the release zip contains, at zip-root). Entrypoint + bridge modules,
`bridge.json`, `BRIDGE_VERSION`, dependency manifest/lockfile, any html/icons the runtime needs.
- `SKILL.md` (root) — the **user** skill (`name: <bridge>`). Deployed to `~/.claude/skills/adom-desktop-<bridge>-bridge/`.
- `dev-skills/<bridge>-bridge-dev/` — THIS dev skill. `dev-skills/<bridge>-bridge-publish/` — the publish recipe.
Both are **source-only** (in `dev-skills/`, NOT `skills/`): NOT in the installable pkg and NOT on the
wiki Skills tab — you get them via `adom-wiki repo clone`. Everyday users only get the `<bridge>` user skill.
- `adom-bridge-<bridge>-manifest.json` (root) — the streaming manifest AD's `updateManifestUrl` points at.
- `package.json` (root) — the **adompkg descriptor** (slug/version/type/description) for `pkg publish`.
NOT a language/runtime package descriptor (e.g. not the node `package.json`) — keep them distinct.
- `install.sh` / `uninstall.sh` — deploy `SKILL.md` + `skills/*` into the container's `~/.claude/skills/`.
- `page.json`, `README.md`, `*.png` heroes — the wiki page presentation (the heroes are gitignored so the
container pkg stays lean — they're already uploaded as page assets).
## Cold-start self-heal — the architecture pattern (the reason a cloud-owned repo earns its keep)
On a fresh PC the installer pre-installs neither your bridge's language runtime nor its heavy dependency.
Without a self-heal, the first `<prefix>_<verb>` fails with a generic launch error + a dev string. The
pattern that fixes it — adapt the names to your runtime:
- **A detect/install/ready module** — `detectDependency()` (validate the ACTUAL artifact, not "a dir
exists"), `installDependency()` (programmatic, **pinned to the exact version your code expects** — never
a floating 'latest'/'stable', or the launch path won't find what it pinned against), an install-state
singleton (dedup concurrent installs + report progress), `ensureReady()` (the gate — kicks off a
background install and returns immediately), `readiness()` (powers the verb + the status chip).
- **The first action verb gates on readiness:** missing dependency → kick off a **non-blocking** install +
return `{errorCode:"<dependency>_installing", statusVerb:"<prefix>_readiness", _hint:"poll then retry"}`.
Install fail → a specific `<dependency>_install_failed` / `<dependency>_download_failed` error — **never
silently continue into a doomed launch.**
- **A `<prefix>_readiness` verb** (poll) + a **`<prefix>_prewarm` verb** (install/warm without doing the
user-visible action).
- **`/status` (or your `healthEndpoint`)** emits `led`/`summary`/`tooltip` truthfully (yellow installing,
red failed, green ready). AD paints these verbatim.
- **The language runtime itself is handled by AD core BEFORE the bridge spawns** (e.g. a `node_not_found` →
`desktop_install_node` flow) — NOT your bridge's job. The bridge can't bootstrap the very runtime it
needs to run; AD installs the runtime, then spawns you.
- **The one thing the bridge can't do:** pre-warm before first use (it isn't running until the first call).
AD/HD must call `<prefix>_prewarm` on embedded first-run — track that as a handoff item.
> **Worked example (pup's case):** the puppeteer bridge's runtime dependency is Chrome for Testing. Its
> module is `src/chrome.js` (`detectChrome` / `installChrome` via the bundled `@puppeteer/browsers` pinned
> to puppeteer's expected buildId / `ensureChromeReady` / `readiness`); `browser_open_window` is the gated
> first verb (CfT path only; its native-drive path skips the gate), returning
> `chrome_for_testing_installing`; and it exposes `browser_readiness` + `browser_prewarm`. Node is the
> language runtime AD core installs first (`node_not_found` → `desktop_install_node`). That's the concrete
> shape of every bullet above.
## Publishing (the wiki has TWO storage layers — don't mix them)
Full recipe in `PUBLISHING.md`. The load-bearing rules:
1. **The bridge `.zip` → a RELEASE asset** (`adom-wiki release create` + `release upload`). NEVER
`repo push` a `.zip` — it's gitignored and silently skipped; the manifest URL would 404.
2. **The manifest JSON + source → the git repo** (`adom-wiki repo push`). The manifest's `url` is the
RELEASE download URL; its `sha256`/`size` MUST match the served asset (releases preserve your sha;
re-download to confirm or `bridge_install` rejects it).
3. **The skills → a PKG** (`adom-wiki pkg publish`). The pkg description (root `package.json`) must say
"container-side skills." `pkg pack` honors `.gitignore` for exclusion (no separate ignore file) — that's
why the heroes are gitignored, to keep the tarball lean. Set per-version notes with `adom-wiki pkg notes`.
4. **Version display gotcha:** a RELEASE shows up in `adom-wiki pkg info` as a row with `type:null` (a
wiki UI conflation). The real pkg rows have `type:"app"`. Use `release list` for true releases.
5. **`bridge.json` + `BRIDGE_VERSION` lockstep**, and keep the version **>** the AD bundled seed so
cache-over-bundled wins.
6. **Declare `updateManifestUrl` in your `bridge.json`** (the full URL of this page's manifest) so a
user's AD re-polls THIS page for updates — and the same value lives in the bundled seed AD ships, so a
fresh install auto-updates with no user opt-in. A republish that drops it makes AD fall back to the
legacy convention and stop seeing your updates.
## Testing a new version on a real desktop
```bash
adom-desktop targets # multiple desktops → --target <name> is required
adom-desktop --target <name> bridge_install '{"manifestUrl":"https://wiki.adom.inc/api/v1/pages/adom-desktop-<bridge>-bridge/files/adom-bridge-<bridge>-manifest.json","force":true}'
adom-desktop --target <name> <prefix>_readiness '{}' # proves the new code is live
```
- **The language runtime is missing on the box** → `<prefix>_*` returns the runtime-not-found error from
AD core *before* the bridge spawns (correct). That's a genuine cold-start demo of the FIRST link, but it
can't exercise your self-heal module (there's no runtime to run it).
- **A box WITH the runtime but WITHOUT the heavy dependency** is the only place to see the full dependency
self-install live (`<prefix>_<verb>` → `<dependency>_installing` → poll `<prefix>_readiness` → succeeds).
Most dev boxes already have the dependency, so `readiness()` just returns `ready:true`.
- The streamed zip is **source-only**; AD reconstructs heavy deps in the cache on first spawn (ship
prebuilt native deps where possible). The bundled seed already has its deps materialized. **Don't put
reconstructable dependency trees (node_modules, venvs, vendored build output) in the zip.**
## Don't
- Don't edit the bundled seed in the adom-desktop repo (`plugins/<bridge>`) as if it's live, and don't run
`release-bridge.sh <bridge>` from the adom-desktop repo — that clobbers this cloud-owned page. (The
adom-desktop release script hard-refuses the cloud-owned bridges; the rule is "don't try.")
- Don't ship the bridge runtime in the pkg tarball or the skills in the release zip. Two artifacts, two homes.
- Don't pin your heavy dependency to a floating channel ('latest'/'stable') — pin to the exact version your
code launches against, or the install and the launch disagree.