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.
name: pup user-invocable: true description: "Drive a browser on the user's desktop from the cloud — the Puppeteer 'pup' bridge (browser_* verbs via the adom-desktop CLI). pup launches a FRESH, isolated process of the browser ALREADY on the machine (installed Chrome, else Microsoft Edge — every Windows PC has Edge), driven over CDP, in the BACKGROUND so it never disturbs the user; NOT the user's real signed-in profile (that's the Adom extension's nbrowser_* verbs), NOT headless (a real rendered window, so screenshots/recording are true-to-life). Chrome for Testing only as a last resort. Open/close windows + tabs, navigate, screenshot, eval JS, record. This is the START-HERE skill: the mental model + when-to-use + the skill map; deeper topics route to the sub-skills. For 'is it ready' use browser_readiness (not browser_status). Trigger words: pup, puppeteer, browser window, open in pup, open my app in pup, browser screenshot, browser eval, browser reload, visual debug, headful chrome, browser_open_window, browser_screenshot, browser_readiness, browser_prewarm, chrome for testing, open in browser."
Pup — browser control on the user's desktop (start here)
Drive a browser on the user's desktop from a cloud container via the adom-desktop CLI. Every verb is a
Bash command (NOT an MCP tool):
adom-desktop browser_open_window '{"sessionId":"mytask-web","owner":"mytask","url":"https://example.com"}'
adom-desktop browser_screenshot '{"sessionId":"mytask-web"}'
The mental model — what pup actually is (read this once)
pup is a small Node bridge that Adom Desktop spawns on the user's machine. When you open a window it does NOT attach to the user's running browser and does NOT run headless. It:
- Launches a brand-new process of the browser ALREADY installed on the machine — installed Chrome first, else Microsoft Edge (every Windows PC ships Edge). So there's usually no download. It uses their browser binary, never their profile.
- …with its own isolated
--user-data-dir+ a CDP debugging port (--remote-debugging-port). That's a fresh, empty profile — no cookies, no logins, no extensions, its own cookie jar keyed by yourprofilename — and pup drives it over the Chrome DevTools Protocol. It is never the user's signed-in Chrome/Edge. (Need their real profile/cookies/SSO? That's the Adom extensionnbrowser_*— see below.) - Renders a REAL window, but keeps it in the BACKGROUND. It is not headless — a real GPU-composited
window, so
browser_screenshot/ recording are true-to-life and canvas/WebGL/media render correctly. But pup immediately z-orders it to the bottom (Win32SetWindowPoswithSWP_NOACTIVATE), so it renders without stealing focus and without covering the user's active window. You drive and screenshot it fully while it stays hidden.foreground:trueis the ONLY thing that shows it. - Spawn-verifies + falls through. The pick order is Chrome → Edge → cached Chrome for Testing; pup actually launches each and, if one won't start (corrupt install), falls to the next — so an open never hard-fails on a broken browser. The one that launches is cached as the default. Only a box with no Chromium at all fetches Chrome for Testing (~150 MB, last resort).
- Is driven by
sessionId, and one session = one window that holds many tabs.
Keep this model in mind and every verb makes sense: it's a disposable, isolated, backgrounded copy of the user's own browser, driven by CDP.
When pup — vs the Adom extension (nbrowser_*)
| The task | Tool |
|---|---|
| Screenshot / scrape / test / demo a page; drive an app you're building; anything that does NOT need the user's logins | pup (this) |
| Log into a site, use saved passwords, fill a form on a signed-in page, download behind a login, sail past captchas | Adom browser extension (nbrowser_*, the adom-browser-extension skill) — drives their REAL signed-in Chrome/Edge |
pup is generic only — a fresh empty profile every time. It cannot use the user's cookies/SSO. That's by design; the extension is its counterpart for the real profile.
Skill map — this is a pack
| Skill | Read it for |
|---|---|
pup (this file) |
The mental model, when-to-use, quick start, the essentials |
pup-windows-sessions-tabs |
sessionIds + naming, window ownership (don't steal another AI thread's window), background vs foreground rules, many tabs in one window, list/switch/close, verifying what you opened |
pup-screenshots-recording |
Screenshots (full-page, full-res), reading the shot, recording a window vs the whole desktop, framing a window for a clean shot without foregrounding |
pup-browsers-and-chrome |
The browser detection/CDP-launch details, browser_use to pin a browser, installing Chrome / Chrome-for-Testing (incl. the UAC-notify flow), readiness, the cold-start error table, Node provisioning, the shell-approval gate |
pup-adom-wiki |
Driving + verifying wiki.adom.inc pages in pup (show/screenshot/verify a published page; the login situation — pup views the wiki logged out for now) |
(Maintaining the bridge itself? The pup-bridge-dev + pup-bridge-publish skills are source-only in the
repo — not shipped here.)
Quick start — the 90% path
# Open in the BACKGROUND (automatic — never disturbs the user), then drive it invisibly.
adom-desktop browser_open_window '{"sessionId":"mytask-web","owner":"mytask","url":"http://localhost:3000"}'
adom-desktop browser_eval '{"sessionId":"mytask-web","expr":"document.title"}' # verify it really loaded
adom-desktop browser_screenshot '{"sessionId":"mytask-web"}' # true-to-life PNG
- Task-prefix your
sessionIdand passowner— sessions are shared across every AI thread on the desktop; this stops another thread stealing your window (and you stealing theirs). Seepup-windows-sessions-tabs. ok:truemeans navigation started, not that content rendered — sanity-check withbrowser_evalondocument.title(404s / login walls change it) or a screenshot.- On a fresh PC it's usually a non-event — pup drives the installed Chrome/Edge with no download. The
rare "no browser at all" / Node-provisioning / disk cases are all in
pup-browsers-and-chrome.
Taskbar preference — the USER can ask for "combined mode"
pup windows each get their own taskbar button by default ("split": one button per window, badged
with its tab count). If the user says anything like "combine my pup windows", "group the pup
icons", "one taskbar button for pup", "my taskbar is cluttered with pup windows" — that's the
GROUPED preference: browser_configure {"taskbarGrouping":"grouped"} stacks every pup window
under one "Adom Pup" button whose badge shows windows + total tabs. "Split them back" / "separate
icons" → {"taskbarGrouping":"split"}. The setting persists across updates. Change it ONLY when
the user asks — it's their taskbar. Details: pup-windows-sessions-tabs.
Window-type taskbar icons (split mode) — what the user sees per window
In split mode every pup window's taskbar icon states its CONTENT KIND. All five share the teal Adom tile with a solid browser-window drawing; the glyph inside the window is the category:
| Glyph inside the window | Category |
|---|---|
| open book | Adom wiki, PUBLIC view (logged out) |
| book + person | Adom wiki, SIGNED IN (the user's logged-in view) |
| the Adom mark | an Adom APP (localhost / cloud-slug proxy URL) |
| globe | the web (any other site) |
| two panes | MIXED (tabs span more than one category) |
The category derives from ALL tabs and updates live (about a second). The corner overlay stays the ACTIVE TAB'S FAVICON (or window/tab counters in grouped mode) — never repurpose it. Adom wiki tab titles also lead with a text glyph: solid dot = signed in, hollow dot = public. You never manage any of this; pup does it automatically. It is worth KNOWING because the user can see login state and window kind at a glance — do not open a second window to "check" what a taskbar icon already answers, and when the user says "the window with the book/globe icon" you know which one they mean.
A WIKI window's taskbar right-click jump list also carries a view toggle ("Adom wiki: switch to
logged-in view" / "...to public view") — a USER affordance that relaunches that window under the
other cookie jar with on-screen captions. Its callback verb browser_wiki_set_view is not for AI
threads; pass wikiView:"authed"|"public" on browser_open_window instead. Details: pup-adom-wiki.
Key rules (the essentials — details in the sub-skills)
- Always pass
sessionIdon session-scoped verbs —'{}'targets the active session, which may not be the one you mean. There is no top-levelpupsubcommand; it'sadom-desktop browser_<cmd> <json>. - Background is guaranteed;
foreground:trueis the only way to show a window — not sizing, not navigating, not screenshotting. - Own your window (
owner:"<task>", task-prefixedsessionId); never navigate/close a window you don't own. - One window, many tabs — use
browser_open_tab, not a secondbrowser_open_window, for multi-URL work. browser_readiness(notbrowser_status) answers "is it ready / which browser?".- No semicolons in
browser_eval— use the comma operator or an IIFE. - Complex page fighting you? (a
querySelectorAllcomes back empty, or a screenshot overshoots the target) — it's usually shadow DOM or a nested scroll container. Verify by DOM, scroll the element not the window, count before you eyeball. Seepup-screenshots-recording→ "Driving & verifying tricky pages." - Never broadly kill Chrome —
browser_close_windowfor a specific session. - Surface a verb's
_hintverbatim whenever it reports not-ready or failure. - If pup's verbs TIME OUT (or the AD LED is dim /
browser.bridgeRunning:false) the bridge PROCESS is down — usually right after arestart_bridgeor a crash. To wake it, fire onebrowser_open_window(a status probe likebrowser_readinessdoes NOT respawn a down bridge — only a window/tab verb does), then pollbrowser_list_windowswith backoff until it answers. Do NOT spam opens while AD saysbridge_starting— one is enough; it's already starting. (bridge_liststatus:"running"is not liveness; the LED +browser.bridgeRunning+ an actual verb reply are.) - Full machine-readable catalog:
adom-desktop browser_describe '{}'.
---
name: pup
user-invocable: true
description: "Drive a browser on the user's desktop from the cloud — the Puppeteer 'pup' bridge (browser_* verbs via the adom-desktop CLI). pup launches a FRESH, isolated process of the browser ALREADY on the machine (installed Chrome, else Microsoft Edge — every Windows PC has Edge), driven over CDP, in the BACKGROUND so it never disturbs the user; NOT the user's real signed-in profile (that's the Adom extension's nbrowser_* verbs), NOT headless (a real rendered window, so screenshots/recording are true-to-life). Chrome for Testing only as a last resort. Open/close windows + tabs, navigate, screenshot, eval JS, record. This is the START-HERE skill: the mental model + when-to-use + the skill map; deeper topics route to the sub-skills. For 'is it ready' use browser_readiness (not browser_status). Trigger words: pup, puppeteer, browser window, open in pup, open my app in pup, browser screenshot, browser eval, browser reload, visual debug, headful chrome, browser_open_window, browser_screenshot, browser_readiness, browser_prewarm, chrome for testing, open in browser."
---
# Pup — browser control on the user's desktop (start here)
Drive a browser on the user's desktop from a cloud container via the `adom-desktop` CLI. Every verb is a
**Bash command** (NOT an MCP tool):
```bash
adom-desktop browser_open_window '{"sessionId":"mytask-web","owner":"mytask","url":"https://example.com"}'
adom-desktop browser_screenshot '{"sessionId":"mytask-web"}'
```
## The mental model — what pup actually is (read this once)
pup is a small **Node bridge** that Adom Desktop spawns on the user's machine. When you open a window it
does NOT attach to the user's running browser and does NOT run headless. It:
1. **Launches a brand-new process of the browser ALREADY installed** on the machine — **installed Chrome
first, else Microsoft Edge** (every Windows PC ships Edge). So there's usually **no download**. It uses
their *browser binary*, never their *profile*.
2. **…with its own isolated `--user-data-dir` + a CDP debugging port** (`--remote-debugging-port`). That's
a **fresh, empty profile** — no cookies, no logins, no extensions, its own cookie jar keyed by your
`profile` name — and pup drives it over the Chrome DevTools Protocol. It is never the user's signed-in
Chrome/Edge. (Need their real profile/cookies/SSO? That's the **Adom extension** `nbrowser_*` — see
below.)
3. **Renders a REAL window, but keeps it in the BACKGROUND.** It is *not* headless — a real GPU-composited
window, so `browser_screenshot` / recording are true-to-life and canvas/WebGL/media render correctly.
But pup immediately z-orders it to the **bottom** (Win32 `SetWindowPos` with `SWP_NOACTIVATE`), so it
renders **without stealing focus and without covering the user's active window**. You drive and
screenshot it fully while it stays hidden. `foreground:true` is the ONLY thing that shows it.
4. **Spawn-verifies + falls through.** The pick order is **Chrome → Edge → cached Chrome for Testing**; pup
actually launches each and, if one won't start (corrupt install), falls to the next — so an open never
hard-fails on a broken browser. The one that launches is **cached as the default**. Only a box with *no*
Chromium at all fetches **Chrome for Testing** (~150 MB, last resort).
5. **Is driven by `sessionId`**, and **one session = one window that holds many tabs**.
Keep this model in mind and every verb makes sense: it's a disposable, isolated, backgrounded copy of the
user's own browser, driven by CDP.
## When pup — vs the Adom extension (`nbrowser_*`)
| The task | Tool |
|---|---|
| Screenshot / scrape / test / demo a page; drive an app you're building; anything that does NOT need the user's logins | **pup (this)** |
| Log into a site, use saved passwords, fill a form on a signed-in page, download behind a login, sail past captchas | **Adom browser extension** (`nbrowser_*`, the `adom-browser-extension` skill) — drives their REAL signed-in Chrome/Edge |
pup is **generic only** — a fresh empty profile every time. It cannot use the user's cookies/SSO. That's by
design; the extension is its counterpart for the real profile.
## Skill map — this is a pack
| Skill | Read it for |
|---|---|
| **`pup`** (this file) | The mental model, when-to-use, quick start, the essentials |
| **`pup-windows-sessions-tabs`** | sessionIds + naming, **window ownership** (don't steal another AI thread's window), **background vs foreground** rules, **many tabs in one window**, list/switch/close, verifying what you opened |
| **`pup-screenshots-recording`** | Screenshots (full-page, full-res), reading the shot, **recording** a window vs the whole desktop, framing a window for a clean shot without foregrounding |
| **`pup-browsers-and-chrome`** | The browser detection/CDP-launch details, `browser_use` to pin a browser, **installing Chrome / Chrome-for-Testing** (incl. the UAC-notify flow), **readiness**, the cold-start error table, Node provisioning, the shell-approval gate |
| **`pup-adom-wiki`** | Driving + verifying **`wiki.adom.inc`** pages in pup (show/screenshot/verify a published page; the login situation — pup views the wiki logged out for now) |
*(Maintaining the bridge itself? The `pup-bridge-dev` + `pup-bridge-publish` skills are source-only in the
repo — not shipped here.)*
## Quick start — the 90% path
```bash
# Open in the BACKGROUND (automatic — never disturbs the user), then drive it invisibly.
adom-desktop browser_open_window '{"sessionId":"mytask-web","owner":"mytask","url":"http://localhost:3000"}'
adom-desktop browser_eval '{"sessionId":"mytask-web","expr":"document.title"}' # verify it really loaded
adom-desktop browser_screenshot '{"sessionId":"mytask-web"}' # true-to-life PNG
```
- **Task-prefix your `sessionId` and pass `owner`** — sessions are shared across every AI thread on the
desktop; this stops another thread stealing your window (and you stealing theirs). See
`pup-windows-sessions-tabs`.
- **`ok:true` means navigation *started*, not that content rendered** — sanity-check with `browser_eval`
on `document.title` (404s / login walls change it) or a screenshot.
- **On a fresh PC it's usually a non-event** — pup drives the installed Chrome/Edge with no download. The
rare "no browser at all" / Node-provisioning / disk cases are all in `pup-browsers-and-chrome`.
## Taskbar preference — the USER can ask for "combined mode"
pup windows each get their own taskbar button by default ("split": one button per window, badged
with its tab count). If the user says anything like "combine my pup windows", "group the pup
icons", "one taskbar button for pup", "my taskbar is cluttered with pup windows" — that's the
GROUPED preference: `browser_configure {"taskbarGrouping":"grouped"}` stacks every pup window
under one "Adom Pup" button whose badge shows windows + total tabs. "Split them back" / "separate
icons" → `{"taskbarGrouping":"split"}`. The setting persists across updates. Change it ONLY when
the user asks — it's their taskbar. Details: `pup-windows-sessions-tabs`.
## Window-type taskbar icons (split mode) — what the user sees per window
In split mode every pup window's taskbar icon states its CONTENT KIND. All five share the teal
Adom tile with a solid browser-window drawing; the glyph inside the window is the category:
| Glyph inside the window | Category |
|---|---|
| open book | Adom wiki, PUBLIC view (logged out) |
| book + person | Adom wiki, SIGNED IN (the user's logged-in view) |
| the Adom mark | an Adom APP (localhost / cloud-slug proxy URL) |
| globe | the web (any other site) |
| two panes | MIXED (tabs span more than one category) |
The category derives from ALL tabs and updates live (about a second). The corner overlay stays the
ACTIVE TAB'S FAVICON (or window/tab counters in grouped mode) — never repurpose it. Adom wiki tab
titles also lead with a text glyph: solid dot = signed in, hollow dot = public. You never manage
any of this; pup does it automatically. It is worth KNOWING because the user can see login state
and window kind at a glance — do not open a second window to "check" what a taskbar icon already
answers, and when the user says "the window with the book/globe icon" you know which one they mean.
A WIKI window's taskbar right-click jump list also carries a view toggle ("Adom wiki: switch to
logged-in view" / "...to public view") — a USER affordance that relaunches that window under the
other cookie jar with on-screen captions. Its callback verb `browser_wiki_set_view` is not for AI
threads; pass `wikiView:"authed"|"public"` on browser_open_window instead. Details: `pup-adom-wiki`.
## Key rules (the essentials — details in the sub-skills)
- **Always pass `sessionId`** on session-scoped verbs — `'{}'` targets the *active* session, which may not
be the one you mean. There is no top-level `pup` subcommand; it's `adom-desktop browser_<cmd> <json>`.
- **Background is guaranteed; `foreground:true` is the only way to show a window** — not sizing, not
navigating, not screenshotting.
- **Own your window** (`owner:"<task>"`, task-prefixed `sessionId`); never navigate/close a window you
don't own.
- **One window, many tabs** — use `browser_open_tab`, not a second `browser_open_window`, for multi-URL work.
- **`browser_readiness`** (not `browser_status`) answers "is it ready / which browser?".
- **No semicolons in `browser_eval`** — use the comma operator or an IIFE.
- **Complex page fighting you?** (a `querySelectorAll` comes back empty, or a screenshot overshoots the
target) — it's usually shadow DOM or a nested scroll container. Verify by DOM, scroll the *element* not
the window, count before you eyeball. See `pup-screenshots-recording` → "Driving & verifying tricky pages."
- **Never broadly kill Chrome** — `browser_close_window` for a specific session.
- **Surface a verb's `_hint` verbatim** whenever it reports not-ready or failure.
- **If pup's verbs TIME OUT (or the AD LED is dim / `browser.bridgeRunning:false`) the bridge PROCESS is
down** — usually right after a `restart_bridge` or a crash. To wake it, fire **one**
`browser_open_window` (a status probe like `browser_readiness` does NOT respawn a down bridge — only a
window/tab verb does), then poll `browser_list_windows` with backoff until it answers. **Do NOT spam
opens** while AD says `bridge_starting` — one is enough; it's already starting. (`bridge_list`
`status:"running"` is not liveness; the LED + `browser.bridgeRunning` + an actual verb reply are.)
- Full machine-readable catalog: `adom-desktop browser_describe '{}'`.