Adom Bridge (macOS)
Public Made by Adomby adom
Adom Bridge for macOS: the menu-bar daemon that connects AI tools to your Mac.
name: adom-desktop-runtimes description: > Managed Node and Python runtimes for Adom Bridge bridges on macOS. Use this skill when a node or python bridge fails to spawn, when you need to check runtime status with the runtimes verb, when a bridge needs Node.js or Python and the box may not have one, when npm dependencies for a source-only node bridge are missing, or when you want to understand the no-elevation portable node/python install, the prewarm on first launch, the Runtimes panel in the GUI, or the loopback-only bind rule for bridge processes.
Managed runtimes: how Adom Bridge provisions Node and Python for bridges
Adom Bridge owns a global, portable Node and Python that every bridge shares.
A bridge never installs its own interpreter and never needs Homebrew, sudo, or
an installer. Core logic lives in src-tauri/src/runtime_cache.rs.
What the managed runtimes are
Resolution order for ensure_runtime(app, Node|Python), race-safe via a
per-kind install lock (two bridges asking at once never double-download):
- Already
Readyin the in-memory state map: return the exe. - A system runtime on PATH is used as-is (
source: system), no download. On a Mac this is typically a Homebrew or nvm/pyenv install; a found exe is accepted only if it reports a parseable version. - A prior portable copy under
~/.adom/adom-runtimes/<kind>-<ver>/(source: cache). - Download a portable copy: stream-download, extract, atomic rename into
the runtime dir, emitting
runtime-statusprogress events.
Pinned portable versions (one place to bump, in runtime_cache.rs):
- Node 22.11.0 (official nodejs.org tarball;
darwin-arm64on Apple Silicon,darwin-x64otherwise) - Python 3.12.13 (astral-sh python-build-standalone
install_onlytar.gz foraarch64-apple-darwin/x86_64-apple-darwin; full CPython withvenvandpip)
The two hard guarantees:
- Never an elevation or installer prompt. The portable runtimes are plain
archive extracts into the user-writable
~/.adom/adom-runtimes/. No.pkg, no sudo, no admin rights, no Homebrew dependency. - Never a firewall prompt by default. Adom Bridge passes
ADOM_BIND_HOST=127.0.0.1to every bridge process, and bridges must bind it. A bareserver.listen(PORT)binds0.0.0.0and can trigger the macOS "accept incoming network connections" dialog; loopback binds never do.
Bridges are spawned with the interpreter's ABSOLUTE path (never a bare
node/python PATH lookup), so a shell whose PATH lacks the runtime cannot
break a spawn.
The runtimes verb (read-only)
adom-desktop runtimes
Available over the relay and the direct API. Returns a per-runtime snapshot:
state:absent | installing | ready | failedsource:system | cacheversionandexewhen ready, pluspct/phasewhile a download is in flight
This is the machine-readable "what is installed and at which version" for
cloud AIs. The human-facing mirror is the Runtimes panel in the GUI's
Bridges sidebar: a state dot, version, and source per runtime, with live
updates from the runtime-status event.
Prewarm on first launch
runtime_cache::prewarm_all fires shortly after boot on every launch
(standalone and embedded), gated by config prewarm_runtimes (default true).
It kicks off both ensure_runtime calls fire-and-forget: a system or cached
runtime resolves in milliseconds; only a runtime-less box actually downloads.
Turning prewarm_runtimes off means runtimes fetch lazily on the first
bridge spawn instead.
Because prewarm is async, a bridge can need Node or Python before it
finishes. The bridge auto-start path calls a non-blocking runtime_gate:
Ready-> spawn now (the common case).Warming-> the verb returns a non-fatalruntime_warmingresult withstillRunning: trueandstatusVerb: "runtimes". Poll and retry; this is not a failure.Failed-> aruntime_failedresult; a retry re-attempts the install.
# A bridge verb returned runtime_warming? Poll until ready, then retry:
adom-desktop runtimes
# ... wait for state=ready on the needed runtime ...
adom-desktop browser_open_window '{"url": "https://example.com"}'
npm installs for source-only node bridges (ensure_node_modules)
A node bridge's wiki zip rightly ships source-only, so its cache dir has no
node_modules. At spawn, ensure_node_modules resolves dependencies in
this order:
- The cache dir already has
node_modules: use it, nothing to do. - A bundled seed of the same bridge name has
node_modulesAND the seed'sbridge.jsonversion is>=the cache version: reuse the seed's modules viaNODE_PATHfor the spawn. Instant, no copy, and the seed's native bindings are already built for this platform. The version gate is the guardrail: a cache strictly newer than the seed may have different deps, so stale bundled modules are never reused. - Otherwise run
npm install --include=optionalin the cache dir.
npm runs through the managed runtime's own npm
(node <node_modules/npm/bin/npm-cli.js>), because a portable Node is not
on PATH so a bare npm would fail. Bare npm is used only for a system Node.
When a node or python bridge fails to spawn: what to check
- Runtime state first.
adom-desktop runtimesstate: installing: the portable download is in flight. Poll and retry the bridge verb; do not treatruntime_warmingas an error.state: failed: retry the bridge verb (a retry re-attempts the install), or check network reachability to nodejs.org / the python-build-standalone release host on GitHub.state: absentwithprewarm_runtimesoff: the first bridge spawn will fetch lazily; expect a one-time delay.
- MODULE_NOT_FOUND from a node bridge means its dependencies did not
resolve: verify
ensure_node_modulesran (cachenode_modulespresent, or a same-or-newer bundled seed exists), or that the npm install in the cache dir succeeded. - A macOS firewall dialog appeared: the bridge bound
0.0.0.0instead of honoringADOM_BIND_HOST. That is a bridge bug; bridges must bindprocess.env.ADOM_BIND_HOST || '127.0.0.1'(Node) oros.environ.get('ADOM_BIND_HOST', '127.0.0.1')(Python). - A strange or unparseable system runtime:
detect_systemaccepts a PATH exe only if it reports a parseable version; anything else falls through to the portable download, so a broken shim never gets pinned.
Bridge author contract (summary)
- Bind
ADOM_BIND_HOST(loopback), never0.0.0.0. - Do not bootstrap your own Node or Python. Adom Bridge provisions the interpreter and spawns your entrypoint by absolute path, with no elevation. Shipping a source-only node bridge is fine; the app runs npm for you at spawn.
Dev internals and the do-not-regress rules: skills/MANAGED_RUNTIMES.md.
Launch-order timeline of every background prewarm task: skills/PREWARM.md.
---
name: adom-desktop-runtimes
description: >
Managed Node and Python runtimes for Adom Bridge bridges on macOS. Use this skill when a
node or python bridge fails to spawn, when you need to check runtime status with the
runtimes verb, when a bridge needs Node.js or Python and the box may not have one,
when npm dependencies for a source-only node bridge are missing, or when you want to
understand the no-elevation portable node/python install, the prewarm on first launch,
the Runtimes panel in the GUI, or the loopback-only bind rule for bridge processes.
---
# Managed runtimes: how Adom Bridge provisions Node and Python for bridges
Adom Bridge owns a global, portable Node and Python that every bridge shares.
A bridge never installs its own interpreter and never needs Homebrew, sudo, or
an installer. Core logic lives in `src-tauri/src/runtime_cache.rs`.
## What the managed runtimes are
Resolution order for `ensure_runtime(app, Node|Python)`, race-safe via a
per-kind install lock (two bridges asking at once never double-download):
1. Already `Ready` in the in-memory state map: return the exe.
2. A **system runtime on PATH** is used as-is (`source: system`), no download.
On a Mac this is typically a Homebrew or nvm/pyenv install; a found exe is
accepted only if it reports a parseable version.
3. A prior **portable copy** under `~/.adom/adom-runtimes/<kind>-<ver>/`
(`source: cache`).
4. **Download a portable copy**: stream-download, extract, atomic rename into
the runtime dir, emitting `runtime-status` progress events.
Pinned portable versions (one place to bump, in `runtime_cache.rs`):
- Node 22.11.0 (official nodejs.org tarball; `darwin-arm64` on Apple Silicon,
`darwin-x64` otherwise)
- Python 3.12.13 (astral-sh python-build-standalone `install_only` tar.gz for
`aarch64-apple-darwin` / `x86_64-apple-darwin`; full CPython with `venv`
and `pip`)
The two hard guarantees:
1. **Never an elevation or installer prompt.** The portable runtimes are plain
archive extracts into the user-writable `~/.adom/adom-runtimes/`. No `.pkg`,
no sudo, no admin rights, no Homebrew dependency.
2. **Never a firewall prompt by default.** Adom Bridge passes
`ADOM_BIND_HOST=127.0.0.1` to every bridge process, and bridges must bind
it. A bare `server.listen(PORT)` binds `0.0.0.0` and can trigger the macOS
"accept incoming network connections" dialog; loopback binds never do.
Bridges are spawned with the interpreter's ABSOLUTE path (never a bare
`node`/`python` PATH lookup), so a shell whose PATH lacks the runtime cannot
break a spawn.
## The `runtimes` verb (read-only)
```bash
adom-desktop runtimes
```
Available over the relay and the direct API. Returns a per-runtime snapshot:
- `state`: `absent | installing | ready | failed`
- `source`: `system | cache`
- `version` and `exe` when ready, plus `pct`/`phase` while a download is in
flight
This is the machine-readable "what is installed and at which version" for
cloud AIs. The human-facing mirror is the **Runtimes panel** in the GUI's
Bridges sidebar: a state dot, version, and source per runtime, with live
updates from the `runtime-status` event.
## Prewarm on first launch
`runtime_cache::prewarm_all` fires shortly after boot on **every** launch
(standalone and embedded), gated by config `prewarm_runtimes` (default true).
It kicks off both `ensure_runtime` calls fire-and-forget: a system or cached
runtime resolves in milliseconds; only a runtime-less box actually downloads.
Turning `prewarm_runtimes` off means runtimes fetch lazily on the first
bridge spawn instead.
Because prewarm is async, a bridge can need Node or Python before it
finishes. The bridge auto-start path calls a non-blocking `runtime_gate`:
- `Ready` -> spawn now (the common case).
- `Warming` -> the verb returns a non-fatal `runtime_warming` result with
`stillRunning: true` and `statusVerb: "runtimes"`. Poll and retry; this is
not a failure.
- `Failed` -> a `runtime_failed` result; a retry re-attempts the install.
```bash
# A bridge verb returned runtime_warming? Poll until ready, then retry:
adom-desktop runtimes
# ... wait for state=ready on the needed runtime ...
adom-desktop browser_open_window '{"url": "https://example.com"}'
```
## npm installs for source-only node bridges (`ensure_node_modules`)
A node bridge's wiki zip rightly ships source-only, so its cache dir has no
`node_modules`. At spawn, `ensure_node_modules` resolves dependencies in
this order:
1. The cache dir already has `node_modules`: use it, nothing to do.
2. A **bundled seed** of the same bridge name has `node_modules` AND the
seed's `bridge.json` version is `>=` the cache version: reuse the seed's
modules via `NODE_PATH` for the spawn. Instant, no copy, and the seed's
native bindings are already built for this platform. The version gate is
the guardrail: a cache strictly newer than the seed may have different
deps, so stale bundled modules are never reused.
3. Otherwise run `npm install --include=optional` in the cache dir.
npm runs through the **managed runtime's own npm**
(`node <node_modules/npm/bin/npm-cli.js>`), because a portable Node is not
on PATH so a bare `npm` would fail. Bare npm is used only for a system Node.
## When a node or python bridge fails to spawn: what to check
1. **Runtime state first.**
```bash
adom-desktop runtimes
```
- `state: installing`: the portable download is in flight. Poll and retry
the bridge verb; do not treat `runtime_warming` as an error.
- `state: failed`: retry the bridge verb (a retry re-attempts the
install), or check network reachability to nodejs.org / the
python-build-standalone release host on GitHub.
- `state: absent` with `prewarm_runtimes` off: the first bridge spawn
will fetch lazily; expect a one-time delay.
2. **MODULE_NOT_FOUND from a node bridge** means its dependencies did not
resolve: verify `ensure_node_modules` ran (cache `node_modules` present,
or a same-or-newer bundled seed exists), or that the npm install in the
cache dir succeeded.
3. **A macOS firewall dialog appeared**: the bridge bound `0.0.0.0`
instead of honoring `ADOM_BIND_HOST`. That is a bridge bug; bridges must
bind `process.env.ADOM_BIND_HOST || '127.0.0.1'` (Node) or
`os.environ.get('ADOM_BIND_HOST', '127.0.0.1')` (Python).
4. **A strange or unparseable system runtime**: `detect_system` accepts a
PATH exe only if it reports a parseable version; anything else falls
through to the portable download, so a broken shim never gets pinned.
## Bridge author contract (summary)
1. Bind `ADOM_BIND_HOST` (loopback), never `0.0.0.0`.
2. Do not bootstrap your own Node or Python. Adom Bridge provisions the
interpreter and spawns your entrypoint by absolute path, with no
elevation. Shipping a source-only node bridge is fine; the app runs npm
for you at spawn.
Dev internals and the do-not-regress rules: `skills/MANAGED_RUNTIMES.md`.
Launch-order timeline of every background prewarm task: `skills/PREWARM.md`.