Adom Desktop - Puppeteer Bridge
Public Made by Adomby adom
The Adom Desktop Puppeteer (pup) bridge — drive a real headful browser on the user's desktop from the cloud: open/screenshot/eval/record. Cold-start drives the browser ALREADY on the machine (installed Chrome, else Microsoft Edge — every Windows PC ships with Edge) with a fresh isolated profile, so the common case needs NO download; Chrome for Testing is fetched only as a last resort. Two artifacts: the Releases zip is the bridge runtime (also bundled in Adom Desktop); the pkg installs the container-side Claude skills.
Handoff to Adom Desktop: expose ALL AD verbs to bridges via the direct-API
From: pup (adom-desktop-puppeteer-bridge)
TL;DR: A bridge should be able to call any AD verb the CLI can, through the
loopback direct-API (127.0.0.1:47200). Today it can only reach a subset
(desktop_* + other bridges' verbs). That's why pup can't fire notify_user to walk a
user through a Chrome UAC — but the fix should be general, not a one-off for notify.
The two directions (so we're precise)
- Inbound (AD → bridge): AD routes verbs to a bridge by its declared prefixes
(
browser_*for pup). Works fine; not the issue. - Outbound (bridge → AD): a bridge's only callback channel is the direct-API. AD's own
GET /commandsdefines "reachable verbs" for a bridge asdesktop_*+app:"dynamic"bridge verbs only. Top-level/lifecycle verbs are absent. So a bridge has a partial view of AD, not the full CLI surface.
Evidence (AD 1.9.79, ADOMBASELINE)
POST 127.0.0.1:47200/command:
| body | result |
|---|---|
{app:"desktop", command:"notify_user"} |
"Unknown desktop command: notify_user" |
{app:"core"|"adom"|"cli", command:"notify_user"} |
"Unknown app" |
{app:"dynamic", command:"notify_user"} |
"no installed bridge claims this prefix" |
{command:"notify_user"} |
HTTP 400 |
GET /commands returns { desktop:[…desktop_* verbs…], bridges:[…], cliRequired:[…] } —
no notify_user, notify_response, ping, targets, server_*, bridge_*, hd_*,
updater_*. Any bridge that needs one of those hits the same wall pup did.
The ask — make the full verb set reachable by bridges
1. Route every dispatchable verb through the direct-API, not just desktop_*. A bridge
calling POST /command {command:"<any verb>", args:{…}} should hit the same dispatcher the
CLI/WS path uses and get the same JSON back (incl. _hint/errorCode/statusVerb). List
the full set in GET /commands so bridges can capability-probe (pup already does this and
will light up automatically).
2. The cliRequired verbs (shell_execute, run_script, pull_file, send_files,
hd_send_files) need the CLI's streaming/approval flow — so "all verbs" doesn't mean
"bypass approval." Keep their gates: make them reachable but still run the approval /
streaming path (or return a precise "call the streaming variant" error), rather than a bare
404. Don't silently drop them from a bridge's reach.
3. Preserve the security posture, deliberately. The direct-API is loopback-only, but
exposing the full surface (incl. shell_execute) to any local process is a real widening.
Recommended: AD hands each bridge a spawn-time capability token (env var) that the
direct-API requires for the sensitive/cliRequired verbs, so AD can attribute and authorize
bridge calls — and the per-verb approval prompts still fire. Bridges are AD-spawned, so AD
can mint the token cleanly.
4. Cross-AD addressing (the attended-device problem). A bridge reaches only its local
AD, but the user may be at a different connected AD (pup runs on a hypervm; the user is on
their laptop). Honor a target arg on direct-API calls — "attended" | "<clientName>" | "all" — mirroring the CLI's global --target, so a bridge can route a verb (a
notify_user, a screenshot, anything) to the right box. "attended" = the AD the user is
present at (most-recently-focused / input-active). This makes the whole fleet addressable
from a bridge, and specifically lets pup ping the user wherever they actually are while the
UAC stays on the box that needs it.
Why general, not per-verb
pup's immediate need is notify_user + notify_response (walk the user through a Chrome
UAC without an AI turn). But the kicad/fusion/native-browser bridges — and every future one
— will each want some verb outside the desktop_* slice (notify_user, targets,
updater_*, bridge_list…). Exposing the whole surface once removes the wall for all of
them. If you want to stage it: (1) all non-cliRequired verbs + target unblocks pup
today; (2) cliRequired behind the capability token follows.
pup is already ready
pup probes GET /commands for notify_user and caches the result
(readiness.chromeStableNotifyChannel). The moment the verb appears, pup's notify-first
UAC flow activates with zero pup release for the local case. Cross-AD target is a
tiny pup follow-up once the arg is finalized. Full context:
adom/adom-desktop-puppeteer-bridge repo.
# Handoff to Adom Desktop: expose ALL AD verbs to bridges via the direct-API
**From:** pup (adom-desktop-puppeteer-bridge)
**TL;DR:** A bridge should be able to call **any** AD verb the CLI can, through the
loopback direct-API (`127.0.0.1:47200`). Today it can only reach a **subset**
(`desktop_*` + other bridges' verbs). That's why pup can't fire `notify_user` to walk a
user through a Chrome UAC — but the fix should be general, not a one-off for notify.
## The two directions (so we're precise)
- **Inbound (AD → bridge):** AD routes verbs to a bridge by its declared prefixes
(`browser_*` for pup). Works fine; not the issue.
- **Outbound (bridge → AD):** a bridge's only callback channel is the direct-API. AD's own
`GET /commands` defines "reachable verbs" for a bridge as **`desktop_*` + `app:"dynamic"`
bridge verbs only.** Top-level/lifecycle verbs are absent. So a bridge has a *partial*
view of AD, not the full CLI surface.
## Evidence (AD 1.9.79, ADOMBASELINE)
`POST 127.0.0.1:47200/command`:
| body | result |
|---|---|
| `{app:"desktop", command:"notify_user"}` | `"Unknown desktop command: notify_user"` |
| `{app:"core"\|"adom"\|"cli", command:"notify_user"}` | `"Unknown app"` |
| `{app:"dynamic", command:"notify_user"}` | `"no installed bridge claims this prefix"` |
| `{command:"notify_user"}` | HTTP 400 |
`GET /commands` returns `{ desktop:[…desktop_* verbs…], bridges:[…], cliRequired:[…] }` —
no `notify_user`, `notify_response`, `ping`, `targets`, `server_*`, `bridge_*`, `hd_*`,
`updater_*`. Any bridge that needs one of those hits the same wall pup did.
## The ask — make the full verb set reachable by bridges
**1. Route every dispatchable verb through the direct-API**, not just `desktop_*`. A bridge
calling `POST /command {command:"<any verb>", args:{…}}` should hit the same dispatcher the
CLI/WS path uses and get the same JSON back (incl. `_hint`/`errorCode`/`statusVerb`). List
the full set in `GET /commands` so bridges can capability-probe (pup already does this and
will light up automatically).
**2. The `cliRequired` verbs** (`shell_execute`, `run_script`, `pull_file`, `send_files`,
`hd_send_files`) need the CLI's streaming/approval flow — so "all verbs" doesn't mean
"bypass approval." Keep their gates: make them **reachable** but still run the approval /
streaming path (or return a precise "call the streaming variant" error), rather than a bare
404. Don't silently drop them from a bridge's reach.
**3. Preserve the security posture, deliberately.** The direct-API is loopback-only, but
exposing the full surface (incl. `shell_execute`) to any local process is a real widening.
Recommended: AD hands each bridge a **spawn-time capability token** (env var) that the
direct-API requires for the sensitive/`cliRequired` verbs, so AD can attribute and authorize
bridge calls — and the per-verb approval prompts still fire. Bridges are AD-spawned, so AD
can mint the token cleanly.
**4. Cross-AD addressing (the attended-device problem).** A bridge reaches only its *local*
AD, but the user may be at a *different* connected AD (pup runs on a hypervm; the user is on
their laptop). Honor a **`target`** arg on direct-API calls — `"attended" | "<clientName>" |
"all"` — mirroring the CLI's global `--target`, so a bridge can route a verb (a
`notify_user`, a screenshot, anything) to the right box. `"attended"` = the AD the user is
present at (most-recently-focused / input-active). This makes the whole fleet addressable
from a bridge, and specifically lets pup ping the user wherever they actually are while the
UAC stays on the box that needs it.
## Why general, not per-verb
pup's immediate need is `notify_user` + `notify_response` (walk the user through a Chrome
UAC without an AI turn). But the kicad/fusion/native-browser bridges — and every future one
— will each want *some* verb outside the `desktop_*` slice (`notify_user`, `targets`,
`updater_*`, `bridge_list`…). Exposing the whole surface once removes the wall for all of
them. If you want to stage it: **(1) all non-`cliRequired` verbs + `target`** unblocks pup
today; **(2) `cliRequired` behind the capability token** follows.
## pup is already ready
pup probes `GET /commands` for `notify_user` and caches the result
(`readiness.chromeStableNotifyChannel`). The moment the verb appears, pup's notify-first
UAC flow activates with **zero pup release** for the local case. Cross-AD `target` is a
tiny pup follow-up once the arg is finalized. Full context:
`adom/adom-desktop-puppeteer-bridge` repo.