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.