app
Pup - the AI's own browser
Public Made by Adomby adom
pup gives your AI a real browser on your desktop that it drives completely, in the background, signed in as you: windows that never jump in front of your work, one shared profile that learns your logins, and every window labeled with its AI thread's name in your taskbar or dock.
← Commit history
Publish 1.8.24
9 files changed
+437−295
README.md+7−5@@ -54,11 +54,13 @@ kicad/fusion bridges). Adom Desktop ships a bundled seed and auto-pulls newer ve `skills/pup-bridge-dev/SKILL.md` + `skills/pup-bridge-publish/SKILL.md` (`user-invocable: false`, clearly marked DEVELOPER-only so they never trigger for everyday users). Skill map: -| Skill | What it's for |-|---|---|-| `pup` (root SKILL.md) | Everyday browser driving |-| `pup-bridge-dev` | Bridge internals + dev learnings |-| `pup-bridge-publish` | Publish/release recipe + gotchas |+| Skill | Ships in pkg? | What it's for |+|---|---|---|+| `pup` (root SKILL.md) | ✅ user | Start here — mental model (CDP-launched fresh process of the installed browser, rendered-but-backgrounded), when-vs-extension, quick start |+| `pup-windows-sessions-tabs` | ✅ user | sessionIds, window ownership (don't steal another thread's window), background-vs-foreground, many tabs in one window |+| `pup-screenshots-recording` | ✅ user | Screenshots (full-page/full-res) + recording a window vs the whole desktop |+| `pup-browsers-and-chrome` | ✅ user | Browser detection/launch, `browser_use`, installing Chrome/CfT (incl. UAC-notify), readiness, cold-start errors |+| `pup-bridge-dev` / `pup-bridge-publish` | ❌ source-only | MAINTAINERS: bridge internals + the publish/release recipe (in `dev-skills/`/`publish-skills/`, not shipped) | - Ownership boundary: `CLAUDE.md`. Publish recipe: `PUBLISHING.md`. History: `CHANGELOG.md`. ## Related
SKILL.md+76−273@@ -1,293 +1,96 @@ --- name: pup user-invocable: true-description: "Drive a generic browser window on the user's desktop via the adom-desktop CLI (the Puppeteer 'pup' bridge): open/close windows + tabs, navigate, screenshot, eval JS, record. pup opens a FRESH isolated Chrome/Edge process (never the user's real signed-in profile — that's the Adom browser extension's nbrowser_* verbs). Cold-start: pup drives the browser already on the machine (installed Chrome, else Edge — every Windows PC has Edge) with a fresh isolated profile, so usually NO download; Chrome for Testing only as a last resort. For 'is it ready' use browser_readiness (not browser_status). Trigger words: pup, puppeteer, browser window, open in pup, browser screenshot, browser reload, browser eval, visual debug, browser_open_window, browser_screenshot, browser_readiness, browser_prewarm, chrome for testing, open in browser."+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 — Puppeteer Browser Control (with cold-start self-heal)+# Pup — browser control on the user's desktop (start here) -## Skill map (this pack)--| Skill | What it's for |-|---|---|-| `adom-desktop-puppeteer-bridge` (pup, this file) | Everyday use: drive generic pup browser windows — open/navigate/screenshot/eval/record |-| `pup-bridge-dev` | DEVELOPERS: architecture, cold-start self-heal internals, field-tested learnings, repo layout |-| `pup-bridge-publish` | DEVELOPERS: the exact publish/release recipe + every gotcha (artifacts, manifests, versioning) |--Drive Chrome windows on the user's desktop from the cloud via the `adom-desktop` CLI. All verbs are-Bash commands (NOT MCP tools):--```bash-adom-desktop <command> '<json_args>'-```--## ⭐ FIRST-TIME on a fresh PC — usually a non-event now--**pup drives the browser that's ALREADY on the machine** — installed **Chrome**, else **Microsoft Edge**-(every Windows PC ships with Edge) — launched with a **fresh isolated profile**. So on a normal box there's-**no download and no wait**: `browser_open_window` just works, and it never touches the user's real-tabs/logins. Chrome for Testing is fetched **only as a last resort** if the machine somehow has no-Chromium browser at all.--When you run `browser_open_window`, one of these happens — handle each:--| Response | What it means | What you do |-|---|---|---|-| `ok:true` / a real session | A browser (Chrome or Edge) was found and launched. | Proceed — the common case. |-| `errorCode: "node_not_found"` / `could not run npm` / runtime not ready | The bridge's Node runtime isn't ready yet. On **AD ≥ 1.9.63 you do nothing** — AD provisions Node automatically (portable, no UAC) and prewarms it in the background right after first launch. | Check `adom-desktop runtimes '{}'`; if `state:"installing"`, wait a few seconds and **retry** the same `browser_*` call — it's ready once `state:"ready"`. No user action, no prompt. (Only on AD older than 1.9.63: `desktop_install_node` — see "The Node step".) **KNOWN AD BUG (some 1.9.x):** if `runtimes` shows `node.state:"ready"` AND `browser_readiness` works but other `browser_*` verbs STILL return `node_not_found`, it's an AD verb-gate false-negative (node IS there) — NOT a real missing Node. Do NOT loop `desktop_install_node`; tell the user it's an Adom Desktop bug and to update AD. |-| `errorCode: "bridge_restarting"` (`stillRunning:true`) | AD ≥ 1.9.77: your verb landed in the brief gap while the bridge respawns after an install/update. NOT fatal, NOT "not installed". | Poll the `statusVerb` (`browser_readiness`) until it answers, then retry the same verb. Do NOT sleep-and-guess or report failure. |-| `errorCode: "chrome_for_testing_installing"` (`installing:true`) | **RARE** — the machine has NO Chrome AND no Edge, so pup is fetching Chrome for Testing as a fallback (~150 MB, in the background; it did NOT block). | Poll **`browser_readiness`** until `{ready:true}`, then re-issue the same `browser_open_window`. **Faster fix:** ask the user to install Chrome or Edge (Edge ships with Windows) — pup then uses it instantly, no download. Don't report failure. |-| `errorCode: "chrome_install_no_disk"` | No Chrome/Edge and too little free disk to install Chrome for Testing (`readiness.diskFreeMb` shown). | Tell the user how much is free and that ~800 MB is needed; ask them to free space OR install Edge/Chrome (Edge needs ~nothing). Do NOT retry the download — it fails until disk is freed. |-| `errorCode: "chrome_install_failed"` / `chrome_download_network` / `chrome_install_permission` | No Chrome/Edge AND the CfT fallback download failed (network/proxy, permission, or other). | Read `readiness.lastError`. Ask the user to install Chrome or Edge (Edge ships with Windows). |--**CfT reported as `skipped` is NOT a failure.** On AD ≥ 1.9.76 the Chrome-for-Testing prewarm is-CONDITIONAL (`prewarm_cft: auto`): boxes with a system Chrome/Edge skip the ~150 MB download, and-`bridge_readiness` reports the asset as `{present:false, skipped:true, reason}`. That's the intended-installed-browser-first behavior — pup drives the installed browser; do NOT retry, prewarm, or report-an error because of a `skipped` CfT row.--**Want real Google Chrome on an Edge-only box?** If the user asks for Chrome specifically, run `browser_use '{"browser":"chrome","install":true}'` — pup downloads Google's official installer and installs Chrome **user-scoped (no admin prompt in most cases)**, then pins it. Poll `browser_readiness` (`installingChrome` / `chromeStablePhase`) until `browserKind:"chrome"`. If it stalls, a UAC prompt may be waiting on the user's desktop — ask them to accept it.--### The Node step — AD provisions Node for you (AD ≥ 1.9.63); do NOTHING--pup is a Node process, but **you no longer install Node.** As of **AD 1.9.63**, AD provisions the runtime-itself — a system Node if one is on PATH, else a **portable, no-UAC copy** it downloads to-`~/.adom/adom-runtimes/node-22.11.0/` and prewarms in the background right after first launch — then spawns-the bridge **by absolute path** (no PATH reliance, no reopen). Node 22.11.0 is pinned; source-only zips get-`npm install` run for them with the managed runtime's own npm.--- **Do NOT** run `desktop_install_node`, `shell_execute`/`send_files` Node installers, or ask the user to- install Node. Users never see a UAC or firewall prompt for the runtime now.-- On a truly fresh box the runtime may still be **installing** for a few seconds after first launch. If a- `browser_*` call reports the runtime isn't ready, check the new AD **`runtimes`** verb- (`adom-desktop runtimes '{}'` → `{state: absent|installing|ready|failed, source: system|cache, version}`)- and simply retry once it's `ready`. No user action required.-- Legacy fallback (only on AD **older than 1.9.63**, which lacks managed runtimes): `desktop_install_node`- — that path pops a **Windows UAC**; warn the user first (chat + `notify_user`) and have them reopen AD- afterward. On current AD this is never needed.--## Permissions on the user's desktop — what needs the user to approve--**Core AD verbs and this bridge's `browser_*` / `desktop_record_*` verbs run WITHOUT any desktop prompt.**-But **anything open-ended — `shell_execute`, `run_script`, arbitrary PowerShell/scripts — pops an AD-"Shell command needs approval" dialog** on the user's desktop (Approve 1 hr / 24 hr / Bypass Permissions).-Until the user clicks it, **your call just hangs and times out** (that's not a failure of your code — it's-waiting for approval).--- **Prefer typed verbs — they never prompt.** AD's `system`/`files` verbs exist specifically to replace- `shell_execute`: `write_file`, `read_file`, `list_dir`, `delete_file`, `registry_get`/`registry_set`,- `launch_process` / `launch`, `find_exe`, `send_files`, `process_list`/`process_kill`. Compose these- (plus `browser_*`) instead of shelling out — that's how the whole Node bootstrap runs with zero popups.- Reach for `shell_execute` / `run_script` only when genuinely no typed verb covers it.-- **Whenever you DO issue `shell_execute` / `run_script`, warn the user first** — in chat AND with a- desktop heads-up, so they approve instead of ignoring it:- ```bash- adom-desktop notify_user '{"title":"Approve the command in Adom Desktop","body":"I need to run a setup command on your PC. Please click Approve (24 hr) on the Adom Desktop popup so it can run."}'- ```-- **It's a per-box setting.** Some machines have shell auto-approve / Bypass on and show NO popup; others- prompt every time. If a `shell_execute` times out with no result, assume an unapproved popup is waiting —- tell the user to approve it (or enable Bypass/24 hr), then retry.-- **AD ≥ 1.9.86: the prompt auto-surfaces, and you can re-summon it.** Every gate-hit now pops the- in-window "Shell Command Approval" dialog on the TARGET machine (reliable over RDP) + a toast + a- taskbar flash — the 1.9.84 regression where a relay caller got `human_only` with NO visible prompt is- fixed. When a verb returns `errorCode: needs_approval` / `human_only`:- 1. `adom-desktop --target <box> request_shell_approval` — re-surfaces the dialog AND foregrounds AD- (use it when the user says "I see nothing to click"; it only surfaces, never grants; if a grant is- already active it tells you so — just run your command).- 2. Ask the user to click **Approve 1hr / 24hr / Bypass** in the AD window on that box.- 3. **RETRY your original verb.** Do NOT call `shell_auto_approve` — it is human-only and refuses;- granting happens only in AD's UI (Settings → Shell Commands, the footer +1hr/+24hr/Bypass bar,- the dialog, or the toast).-- **This is NOT a UAC.** The shell-approval dialog is AD's own permission gate (safe to approve for your- setup commands); a Windows UAC is the separate admin-elevation prompt (e.g. the Node MSI).--### "What do I have / is it ready?"--- **`browser_readiness`** (this bridge, **READ-ONLY**) → `{ready, browserKind, browserSource, browserExecutablePath, candidates, defaultBrowser, …}` — tells you which browser pup will drive (`chrome` / `edge` / `chrome-for-testing`), the **full candidate list** it will fall through, the current **default**, and whether it's ready. Never triggers a download.-- **`adom-desktop bridge_readiness`** (AD-core) is the platform-level "is this bridge ready?" probe — use it for the general readiness question. **`browser_status` is NOT a readiness check** (it only lists open sessions).--**Always surface a verb's `_hint` verbatim** when something isn't ready.--### Picking the browser — `browser_use`--pup auto-picks the first browser that actually launches (Chrome → Edge → cached CfT) and **spawn-verifies** it — if a browser is present but won't start (corrupt install), it falls through to the next, so an open never hard-fails on a broken browser. The one that launches is **cached as the default** so it isn't re-probed each time. To override:--```bash-adom-desktop browser_use '{"browser":"chrome"}' # pin installed Chrome-adom-desktop browser_use '{"browser":"edge"}' # pin Edge-adom-desktop browser_use '{"browser":"cft"}' # install Chrome for Testing (~150 MB once) + pin it-adom-desktop browser_use '{"browser":"auto"}' # back to auto-pick-```--Every `browser_open_window` response includes `browser` (what was driven), `availableBrowsers`, `defaultBrowser`, and an `_hint` spelling out how to switch — surface it if the user asks which browser pup is using.--## How to Run Commands--```bash-adom-desktop browser_open_window '{"sessionId":"myapp","profile":"myapp","url":"http://localhost:3000"}'-adom-desktop browser_navigate '{"sessionId":"myapp","url":"https://example.com"}'-adom-desktop browser_screenshot '{"sessionId":"myapp"}'-adom-desktop browser_eval '{"sessionId":"myapp","expr":"document.title"}'-adom-desktop browser_reload '{"sessionId":"myapp"}'-```--**These are Bash commands, not MCP tools.** Always pass `sessionId` — `'{}'` targets the *active*-session, which may not be the one you mean.--## Window ownership — do NOT steal another AI thread's window--**Pup sessions are shared across every AI thread on this desktop.** If you `browser_open_window` with a-sessionId that already exists, pup NAVIGATES that existing window — and if another thread was using it,-you just destroyed their work. Rules:--1. **Task-prefix your sessionId** — `"<your-task>-web"`, never generic ids like `wiki` / `test` / `main`- (those collide across threads).-2. **Always pass `owner:"<your-task-name>"`** on `browser_open_window`. Pup then REFUSES other threads'- attempts to navigate/close your window (`errorCode: session_owned_by_another_thread`) and tells them- to open their own.-3. **If YOU get `session_owned_by_another_thread`** — that window is not yours. Open your own with a new- task-prefixed sessionId. Pass `takeover:true` ONLY if the user explicitly told you to repurpose that- window.-4. **Unsure what exists?** `browser_status` lists every window with its `owner`, current `url`, and- `ageMinutes` — check before reusing any id.-5. **Reusing your OWN window is good** (same sessionId + same owner = smooth refresh — e.g. republishing- a wiki page into the same window). Ownership only blocks *cross-thread* grabs.+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-# right: owned, task-prefixed-adom-desktop browser_open_window '{"sessionId":"wikifix-preview","owner":"wikifix","url":"<url>"}'-# wrong: generic id, anonymous — may hijack another thread's window (pup will reprimand you in _hint)-adom-desktop browser_open_window '{"sessionId":"wiki","url":"<url>"}'+adom-desktop browser_open_window '{"sessionId":"mytask-web","owner":"mytask","url":"https://example.com"}'+adom-desktop browser_screenshot '{"sessionId":"mytask-web"}' ``` -## Background by default — pup NEVER pops onto the user's screen unless asked--**Pup is the AI's workspace, not the user's.** `browser_open_window` ALWAYS opens **in the background**-(behind the user's windows, no focus stolen) — you do NOT need to lower it yourself; it's automatic and-guaranteed. The window is **fully drivable while hidden**: `browser_screenshot`, `browser_eval`,-`browser_navigate` all work on a background window. So for the overwhelmingly common case — the AI opens a-page to screenshot/scrape/debug — **just open it and drive it; the user never sees a thing.**--- **`foreground:true` is the ONLY thing that shows a window.** Nothing else foregrounds — not sizing, not- navigating, not screenshotting. Pass it ONLY when the user explicitly wants to watch.-- **Sizing/positioning does NOT foreground** (v1.8.17+). `browser_open_window {width, height, x, y}` places- the window for screenshot framing but keeps it in the background — so you can frame a window at an exact- size for a clean screenshot **without ever disturbing the user's main work**. (Earlier versions- foregrounded sized windows; that's fixed.)--- **Only bring a window to the user's screen when they explicitly want to WATCH** — a demo, a recording, a- "show me this" / "open it so I can see." Then (and only then) pass **`foreground:true`** on open, or call- **`browser_raise_os_window`**. Opening something the user didn't ask to see, in the foreground, is exactly- the disruption to avoid.-- Use **`browser_alert_window`** (flashes the taskbar) for a non-intrusive "it's ready / done" nudge instead- of raising the window.-- Re-hide a window you foregrounded with **`browser_lower_os_window`**.--```bash-# Default: opens in the BACKGROUND, drive it invisibly — the user is never interrupted.-adom-desktop browser_open_window '{"sessionId":"job","url":"<url>"}'-adom-desktop browser_screenshot '{"sessionId":"job"}' # works while the window is in the background-# Only if the user asked to SEE it:-adom-desktop browser_open_window '{"sessionId":"demo","url":"<url>","foreground":true}'-```--## Core verbs--| Command | Description | Key args |-|---|---|---|-| `browser_readiness` | Which browser pup will drive + candidate list + default (READ-ONLY) | none |-| `browser_prewarm` | Install Chrome for Testing WITHOUT opening a window, and pin it | `wait` (optional bool) |-| `browser_use` | Pin the browser pup drives: `chrome`/`edge`/`cft`/`auto` | `browser`, `executablePath?` |-| `browser_open_window` | Spawn a new generic pup session/window (fresh isolated Chrome/Edge process; `profile` defaults to `sessionId`) | `sessionId`, `url`, `profile?` |-| `browser_open_tab` | Add a tab to an existing session | `sessionId`, `url` |-| `browser_navigate` | Drive an existing tab to a URL (reuses — no new window) | `sessionId`, `url`, `tabId?` |-| `browser_screenshot` | Capture the page (lossless PNG) | `sessionId`, `fullPage?` |-| `browser_eval` | Run JS in the page, return the result | `sessionId`, `expr` |-| `browser_errors` | Console/page errors | `sessionId` |-| `browser_status` | List open pup sessions (NOT a readiness check — use AD's `bridge_readiness`) | none |-| `browser_reload` | Reload + clear error log | `sessionId` |-| `browser_open_tab` / `browser_switch_tab` / `browser_close_tab` / `browser_list_tabs` | Tabs within one session/window | `sessionId`, `tabId?` |-| `browser_record_start` / `browser_record_stop` | Record a video of a window/tab | `sessionId` |-| `browser_lower_os_window` | Send the window to the back (keep it off the user's screen) | `sessionId` |-| `browser_raise_os_window` / `browser_focus_window` | Bring to foreground (use sparingly — only to show the user) | `sessionId` |-| `browser_close_window` | Close one session + its window (destructive) | `sessionId` |-| `browser_alert_window` | Flash the taskbar (non-intrusive attention) | `sessionId` |-| `browser_close` | Close ALL sessions | none |--Run `adom-desktop browser_describe '{}'` for the full machine-readable verb catalog.--## Tabs management--One session = one Chrome window that can hold many tabs (one taskbar icon). Use tabs (not extra-windows) for multi-URL work — see "One window, many tabs" below. `browser_open_tab` returns a `tabId`;-pass it to `browser_switch_tab` / `browser_close_tab` / `browser_screenshot` / `browser_eval`-(`tabId` optional — omit to target the active tab). Closing the last tab closes the window.--## Recording windows--Two recorders — pick by what you're capturing:--- **`browser_record_start` / `browser_record_stop`** — records ONE pup tab/window (the page content).- Best for "record my app" / a feature walkthrough. Returns the saved video path on stop.-- **`desktop_record_start` / `desktop_record_stop`** — records the WHOLE desktop (multiple apps,- KiCad/Fusion, OS dialogs). Use when the capture spans more than the pup window.--```bash-adom-desktop browser_raise_os_window '{"sessionId":"demo"}' # for a recording, DO bring it forward-adom-desktop browser_record_start '{"sessionId":"demo"}'-# ...drive the scene...-adom-desktop browser_record_stop '{"sessionId":"demo"}' # → { path: "...webm" }-adom-desktop browser_lower_os_window '{"sessionId":"demo"}' # tuck it away again when done-```+## 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 | -## Don't double-open windows+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. -- New session entirely → `browser_open_window`.-- Reuse an existing session's tab → `browser_navigate` (with `tabId` if multiple tabs).-- A second view in the same session → `browser_open_tab` (NOT a second `browser_open_window`).+## Skill map — this is a pack -**Run `browser_status '{}'` before `browser_open_window`** at the start of a conversation (especially-after a context compaction) — if a session already shows the URL you're about to open, reuse it. Opening-the same content under a new `sessionId` leaves the user staring at two identical windows.+| 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 | -## One window, many tabs (multi-URL tasks)+*(Maintaining the bridge itself? The `pup-bridge-dev` + `pup-bridge-publish` skills are source-only in the+repo — not shipped here.)* -For a vendor fetch / multi-doc review, put the URLs in **tabs of one window**, not separate windows:+## Quick start — the 90% path ```bash-adom-desktop browser_open_window '{"sessionId":"task","profile":"task","url":"https://first.example.com"}'-adom-desktop browser_open_tab '{"sessionId":"task","url":"https://second.example.com"}'-adom-desktop browser_switch_tab '{"sessionId":"task","tabId":"tab-2"}'-adom-desktop browser_screenshot '{"sessionId":"task"}'+# 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 ``` -Pick a stable `profile` name across runs so the cookie jar persists (the user logs in once, reuses for-months).--## Verify what you opened — `ok:true` is not enough--`browser_open_window` returns `ok:true` as soon as navigation *starts*, not when content rendered.-Sanity-check: `browser_eval '{"sessionId":"...","expr":"document.title"}'` (fastest — 404s, login walls,-and anti-bot blocks all change the title), or a `browser_screenshot` when the title isn't discriminating.--## Need the user's REAL signed-in browser? That's the Adom extension, not pup.--pup is **generic only** — every session is a fresh isolated Chrome/Edge process, never the user's real-profile. If a task needs the user's **actual signed-in browser** (their cookies, org SSO, saved logins),-that's the **Adom browser extension** (`nbrowser_*` verbs) — a separate bridge that drives the real-running Chrome/Edge without closing it. Reach for the `adom-browser-extension` skill for that; pup does-not do native-browser drive.--## Key rules--- **Always pass `sessionId`.** Never `'{}'` for session-scoped verbs.-- **It's `adom-desktop browser_<cmd> <json>`** — there is no top-level `pup` subcommand.+- **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`.++## 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.-- **Sessions persist** (cookies/localStorage/IndexedDB) keyed by `profile` name — don't close+reopen- reflexively; `browser_reload` or `browser_navigate` instead.-- **Surface `_hint` verbatim** when a verb reports it isn't ready or failed.-- **Never broadly kill Chrome** — use `browser_close_window` for a specific session.-- **Pup launches with FULL browser permissions by default** (notifications, geolocation, camera, mic,- midi, sensors, clipboard, wake-lock, …) so no permission prompt ever blocks a debug flow — you don't- need to grant anything. Pass `strictPermissions:true` to `browser_open_window` only for the rare- untrusted-site audit where you want Chromium's default prompts back.+- **Never broadly kill Chrome** — `browser_close_window` for a specific session.+- **Surface a verb's `_hint` verbatim** whenever it reports not-ready or failure.+- Full machine-readable catalog: `adom-desktop browser_describe '{}'`.
install.sh+12−9@@ -1,20 +1,23 @@ #!/usr/bin/env bash-# Install the `pup` USER skill onto the container — the ONLY skill the pkg carries.-# Per the Bridge SDK: a pkg ships ONLY user skills. The DEV + PUBLISH skills are-# source-only — they live in the repo under dev-skills/ + publish-skills/, are NOT in-# package.json files[], and NEVER land on a user's container (open-vs-closed source-# doesn't change this). The bridge RUNTIME isn't here either — it ships in Adom Desktop-# + as the Releases zip. The AD CLI + core skills arrive via this pkg's-# dependencies:{ "adom/adom-desktop": "^1.9.x" } at `pkg install` time.+# Install the pup USER skillpack: the main `pup` skill + its focused user sub-skills.+# Per the Bridge SDK a pkg ships ONLY user skills; all skills listed here are user-facing.+# The DEV + PUBLISH maintainer skills are source-only (repo dev-skills/ + publish-skills/,+# NOT in package.json files[], never installed here). The bridge RUNTIME isn't here either+# — it ships in Adom Desktop + as the Releases zip. The AD CLI + core skills arrive via+# this pkg's dependencies:{ "adom/adom-desktop": "^1.9.x" } at `pkg install` time. set -e PKG_DIR="$(cd "$(dirname "$0")" && pwd)" deploy_into() { local BASE="$1" install_skill() { mkdir -p "$BASE/$1"; cp "$PKG_DIR/$2" "$BASE/$1/SKILL.md"; }- # the user skill (REQUIRED at repo root by the registry)+ # main user skill (REQUIRED at repo root by the registry) install_skill adom-desktop-puppeteer-bridge SKILL.md- echo " -> $BASE (pup)"+ # focused user sub-skills+ install_skill pup-windows-sessions-tabs skills/pup-windows-sessions-tabs/SKILL.md+ install_skill pup-screenshots-recording skills/pup-screenshots-recording/SKILL.md+ install_skill pup-browsers-and-chrome skills/pup-browsers-and-chrome/SKILL.md+ echo " -> $BASE (pup + 3 sub-skills)" } echo "Installing adom-desktop-puppeteer-bridge skills:"
package.json+4−1@@ -1,6 +1,6 @@ { "slug": "adom-desktop-puppeteer-bridge",- "version": "1.8.23",+ "version": "1.8.24", "type": "app", "description": "CONTAINER-SIDE install \u2014 the pup skill pack in the wiki skillpack layout: the everyday `pup` user skill PLUS two bundled developer sub-skills (pup-bridge-dev, pup-bridge-publish; user-invocable:false, DEVELOPER-only descriptions so they never trigger for everyday users), all dropped into ~/.claude/skills/ AND ~/.codex/skills/ by an explicit install.sh list. It does NOT contain the bridge runtime: the bridge (Node + browser driving) lives on the DESKTOP \u2014 bundled inside Adom Desktop and published on the Releases tab as adom-bridge-puppeteer-v<ver>.zip, which Adom Desktop streams into its bridges-cache. TL;DR: pkg install = the skill pack; the bridge = the Release zip + bundled in Adom Desktop.", "keywords": [@@ -21,6 +21,9 @@ ], "files": [ "SKILL.md",+ "skills/pup-windows-sessions-tabs/SKILL.md",+ "skills/pup-screenshots-recording/SKILL.md",+ "skills/pup-browsers-and-chrome/SKILL.md", "install.sh", "uninstall.sh", "README.md",
page.json+40−6@@ -3,9 +3,16 @@ "type": "app", "slug": "adom-desktop-puppeteer-bridge", "title": "Adom Desktop - Puppeteer Bridge",- "brief": "The Adom Desktop Puppeteer (pup) bridge \u2014 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 \u2014 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.",- "version": "1.1.0",- "tags": [],+ "brief": "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.",+ "version": "1.8.24",+ "tags": [+ "skills",+ "pup",+ "puppeteer",+ "browser",+ "adom-desktop",+ "bridge"+ ], "license": "MIT", "discovery_triggers": [ "pup",@@ -46,7 +53,7 @@ "fork the pup bridge", "puppeteer bridge source" ],- "discovery_pitch": "Use pup to drive Chrome from the cloud: open/close browser windows + tabs, navigate, screenshot, eval JS, and record windows. It drives the browser ALREADY on the machine \u2014 installed Chrome, else Microsoft Edge (every Windows PC has Edge) \u2014 with a fresh isolated profile, so usually NO download and it doesn't disrupt the user; Chrome for Testing is fetched only as a last resort. For 'is it ready' use browser_readiness. Or pass nativeBrowser+nativeProfile to drive the user's REAL signed-in Chrome/Edge/Brave. Call the adom-desktop browser_* verbs.",+ "discovery_pitch": "Use pup to drive Chrome from the cloud: open/close browser windows + tabs, navigate, screenshot, eval JS, and record windows. It drives the browser ALREADY on the machine — installed Chrome, else Microsoft Edge (every Windows PC has Edge) — with a fresh isolated profile, so usually NO download and it doesn't disrupt the user; Chrome for Testing is fetched only as a last resort. For 'is it ready' use browser_readiness. Or pass nativeBrowser+nativeProfile to drive the user's REAL signed-in Chrome/Edge/Brave. Call the adom-desktop browser_* verbs.", "sample_prompts": [ { "label": "Open in pup",@@ -79,7 +86,7 @@ "install_hint": "", "version_cmd": "" },- "readme": "# Adom Desktop \u2014 Puppeteer (pup) Bridge\n\nDrive a real headful browser from the cloud: open/close browser windows + tabs, navigate, screenshot,\neval JS, and record windows. Cold-start drives the browser **already on the machine** \u2014 installed\n**Chrome**, else **Microsoft Edge** (every Windows PC ships with Edge) \u2014 launched with a **fresh isolated\nprofile**, so the common case has **no download** and never touches the user's real tabs/logins. **Chrome\nfor Testing** is fetched only as a **last resort** if the machine has no Chromium browser at all. You can\nalso drive the user's **real Chrome/Edge/Brave** (signed-in profile) via `nativeBrowser`+`nativeProfile`.\n\nCanonical page: **https://wiki.adom.inc/adom/adom-desktop-puppeteer-bridge**\n\n## Three artifacts \u2014 know which is which\n\n| Artifact | Where | What it is |\n|---|---|---|\n| **pkg** (Packages tab) | `adom-wiki pkg install adom/adom-desktop-puppeteer-bridge` | the **container-side `pup` skill** (drops into `~/.claude/skills` + `~/.codex/skills`). No runtime. |\n| **release zip** (Releases tab) | `https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/<ver>/adom-bridge-puppeteer-v<ver>.zip` | the **bridge runtime** (Node + Chrome-for-Testing self-heal) that Adom Desktop streams into its bridges-cache |\n| **bundled seed** | inside the Adom Desktop installer | the same runtime, shipped for first-run/offline; superseded by a newer cache copy via `updateManifestUrl` |\n\n**TL;DR:** `pkg install` = the pup skill for the container; the bridge itself = the Release zip + bundled in Adom Desktop.\n\n## Use it\n\n```bash\n# everyday: open + screenshot (works while lowered so it doesn't disrupt the user)\nadom-desktop browser_open_window '{\"sessionId\":\"app\",\"profile\":\"app\",\"url\":\"http://localhost:3000\"}'\nadom-desktop browser_lower_os_window '{\"sessionId\":\"app\"}'\nadom-desktop browser_screenshot '{\"sessionId\":\"app\"}'\n\n# cold-start (fresh PC, no Chrome for Testing): the bridge installs it; poll readiness then open\nadom-desktop browser_prewarm '{}'\nadom-desktop browser_readiness '{}' # \u2192 {ready:true} when Chrome for Testing is installed\n```\n\nFull everyday usage (tabs, recording, native-Chrome mode, cold-start playbook) is in the `pup` skill\n(`SKILL.md`).\n\n## Source & development\n\nThis bridge is cloud-owned here (extracted from `adom-desktop/plugins/puppeteer`, mirroring the\nkicad/fusion bridges). Adom Desktop ships a bundled seed and auto-pulls newer versions from this page.\n\n- Clone the source: `adom-wiki repo clone adom/adom-desktop-puppeteer-bridge`\n- Runtime lives in `src/` (`server.js`, `chrome.js`, `bridge.json`, \u2026); the release zip is `src/` at zip-root.\n- **Developer/publishing references** live in `dev-skills/` (`pup-bridge-dev.md`, `pup-bridge-publish.md`) \u2014\n source-only, not in the pkg or on the Skills tab. See `dev-skills/README.md`.\n- Ownership boundary: `CLAUDE.md`. Publish recipe: `PUBLISHING.md`. History: `CHANGELOG.md`.\n\n## Related\n\n- [Bridge SDK guide](https://wiki.adom.inc/adom/adom-desktop-bridges) \u2014 `bridge.json` schema, packaging, lifecycle\n- [KiCad bridge](https://wiki.adom.inc/adom/adom-desktop-kicad-bridge) \u00b7 [Fusion 360 bridge](https://wiki.adom.inc/adom/adom-desktop-fusion-bridge)\n- [Adom Desktop (parent app)](https://wiki.adom.inc/adom/adom-desktop)\n\nMIT licensed.\n",+ "readme": "# Adom Desktop — Puppeteer (pup) Bridge\n\nDrive a real headful browser from the cloud: open/close browser windows + tabs, navigate, screenshot,\neval JS, and record windows. Cold-start drives the browser **already on the machine** — installed\n**Chrome**, else **Microsoft Edge** (every Windows PC ships with Edge) — launched with a **fresh isolated\nprofile**, so the common case has **no download** and never touches the user's real tabs/logins. **Chrome\nfor Testing** is fetched only as a **last resort** if the machine has no Chromium browser at all. You can\nalso drive the user's **real Chrome/Edge/Brave** (signed-in profile) via `nativeBrowser`+`nativeProfile`.\n\nCanonical page: **https://wiki.adom.inc/adom/adom-desktop-puppeteer-bridge**\n\n## Three artifacts — know which is which\n\n| Artifact | Where | What it is |\n|---|---|---|\n| **pkg** (Packages tab) | `adom-wiki pkg install adom/adom-desktop-puppeteer-bridge` | the **container-side `pup` skill** (drops into `~/.claude/skills` + `~/.codex/skills`). No runtime. |\n| **release zip** (Releases tab) | `https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/<ver>/adom-bridge-puppeteer-v<ver>.zip` | the **bridge runtime** (Node + Chrome-for-Testing self-heal) that Adom Desktop streams into its bridges-cache |\n| **bundled seed** | inside the Adom Desktop installer | the same runtime, shipped for first-run/offline; superseded by a newer cache copy via `updateManifestUrl` |\n\n**TL;DR:** `pkg install` = the pup skill for the container; the bridge itself = the Release zip + bundled in Adom Desktop.\n\n## Use it\n\n```bash\n# everyday: open + screenshot (works while lowered so it doesn't disrupt the user)\nadom-desktop browser_open_window '{\"sessionId\":\"app\",\"profile\":\"app\",\"url\":\"http://localhost:3000\"}'\nadom-desktop browser_lower_os_window '{\"sessionId\":\"app\"}'\nadom-desktop browser_screenshot '{\"sessionId\":\"app\"}'\n\n# cold-start (fresh PC, no Chrome for Testing): the bridge installs it; poll readiness then open\nadom-desktop browser_prewarm '{}'\nadom-desktop browser_readiness '{}' # → {ready:true} when Chrome for Testing is installed\n```\n\nFull everyday usage (tabs, recording, native-Chrome mode, cold-start playbook) is in the `pup` skill\n(`SKILL.md`).\n\n## Source & development\n\nThis bridge is cloud-owned here (extracted from `adom-desktop/plugins/puppeteer`, mirroring the\nkicad/fusion bridges). Adom Desktop ships a bundled seed and auto-pulls newer versions from this page.\n\n- Clone the source: `adom-wiki repo clone adom/adom-desktop-puppeteer-bridge`\n- Runtime lives in `src/` (`server.js`, `chrome.js`, `bridge.json`, …); the release zip is `src/` at zip-root.\n- **Developer/publishing references** live in `dev-skills/` (`pup-bridge-dev.md`, `pup-bridge-publish.md`) —\n source-only, not in the pkg or on the Skills tab. See `dev-skills/README.md`.\n- Ownership boundary: `CLAUDE.md`. Publish recipe: `PUBLISHING.md`. History: `CHANGELOG.md`.\n\n## Related\n\n- [Bridge SDK guide](https://wiki.adom.inc/adom/adom-desktop-bridges) — `bridge.json` schema, packaging, lifecycle\n- [KiCad bridge](https://wiki.adom.inc/adom/adom-desktop-kicad-bridge) · [Fusion 360 bridge](https://wiki.adom.inc/adom/adom-desktop-fusion-bridge)\n- [Adom Desktop (parent app)](https://wiki.adom.inc/adom/adom-desktop)\n\nMIT licensed.\n", "author": { "id": "695820315b5f1e4db2fcf602", "name": "Kyle Bergstedt",@@ -96,5 +103,32 @@ "created_at": "2026-05-28T05:28:43.991Z", "updated_at": "2026-06-16T12:43:19.735Z", "skills": [],- "author_name": "John Lauer"+ "author_name": "John Lauer",+ "description": "CONTAINER-SIDE install — the pup skill pack in the wiki skillpack layout: the everyday `pup` user skill PLUS two bundled developer sub-skills (pup-bridge-dev, pup-bridge-publish; user-invocable:false, DEVELOPER-only descriptions so they never trigger for everyday users), all dropped into ~/.claude/skills/ AND ~/.codex/skills/ by an explicit install.sh list. It does NOT contain the bridge runtime: the bridge (Node + browser driving) lives on the DESKTOP — bundled inside Adom Desktop and published on the Releases tab as adom-bridge-puppeteer-v<ver>.zip, which Adom Desktop streams into its bridges-cache. TL;DR: pkg install = the skill pack; the bridge = the Release zip + bundled in Adom Desktop.",+ "keywords": [+ "pup",+ "puppeteer",+ "browser",+ "adom-desktop",+ "bridge",+ "skills"+ ],+ "files": [+ "SKILL.md",+ "skills/pup-windows-sessions-tabs/SKILL.md",+ "skills/pup-screenshots-recording/SKILL.md",+ "skills/pup-browsers-and-chrome/SKILL.md",+ "install.sh",+ "uninstall.sh",+ "README.md",+ "package.json"+ ],+ "scripts": {+ "install": "./install.sh",+ "uninstall": "./uninstall.sh"+ },+ "dependencies": {+ "adom/adom-desktop": "^1.9.63"+ },+ "org": "adom" }
skills/pup-browsers-and-chrome/SKILL.mdadded+116@@ -0,0 +1,116 @@+---+name: pup-browsers-and-chrome+user-invocable: true+description: "How pup (Puppeteer bridge) picks and launches a browser, and every cold-start / readiness case. Covers: the installed-browser-first architecture (launches a FRESH CDP-driven process of installed Chrome → else Edge → else cached Chrome for Testing, spawn-verified with fallthrough, caches the winner as default); browser_use to pin chrome/edge/cft/auto; INSTALLING real Google Chrome on an Edge-only box (browser_use install:true) including the UAC-approval-notify flow on locked-down machines; installing/prewarming Chrome for Testing; browser_readiness (READ-ONLY — the right 'is it ready / which browser' probe, NOT browser_status); the cold-start error table (node_not_found, bridge_restarting, chrome_for_testing_installing, chrome_install_no_disk, CfT 'skipped' is not a failure); AD-managed Node provisioning (no install needed on AD >=1.9.63); and the shell-approval gate. Read on any browser-pick question, a fresh/first-run PC, an install/prewarm, a not-ready error, or a shell-approval prompt. Trigger words: browser_readiness, browser_use, browser_prewarm, chrome for testing, install chrome, chrome_for_testing_installing, node_not_found, bridge_restarting, chrome_install_no_disk, cold start pup, which browser, edge only box, UAC approve chrome, request_shell_approval, shell command approval, adom runtimes, pup not ready."+---++**Parent skill:** adom-desktop-puppeteer-bridge · See also: `pup` (start here), `pup-windows-sessions-tabs`, `pup-screenshots-recording`.++# pup — browsers, Chrome install & cold-start++## How pup picks + launches a browser (the architecture)++pup drives the browser **already on the machine** — it does NOT ship or require its own. On every open it+builds a candidate list and **spawn-verifies** it:++1. **Chrome → Edge → cached Chrome for Testing** (a pinned default, if set, goes first). Every Windows PC+ ships Edge, so there's almost always a candidate with **no download**.+2. It **launches a fresh process** of that browser binary with an **isolated `--user-data-dir` + a CDP+ `--remote-debugging-port`** — a clean empty profile driven over the DevTools Protocol, never the user's+ signed-in profile, and a **real rendered window** (not headless).+3. If the top pick is present but **won't start** (corrupt/partial install), pup **falls through** to the+ next — so an open never hard-fails on one broken browser.+4. The browser that actually launched is **cached as the default** (persisted across bridge restarts) so it+ isn't re-probed each time.+5. **Chrome for Testing is the last resort** — fetched (~150 MB) only if the box has *no* Chrome and *no*+ Edge and *no* cached CfT.++## "Is it ready / which browser?" — `browser_readiness` (READ-ONLY)++```bash+adom-desktop browser_readiness '{}'+# → {ready, browserKind:"chrome"|"edge"|"chrome-for-testing", browserSource, browserExecutablePath,+# candidates:[…the full fallthrough list…], defaultBrowser, diskFreeMb, chromeStablePhase, …}+```++- **`browser_readiness`** is the right probe — it never triggers a download and tells you exactly which+ browser will be driven + the candidate list + the default.+- **`browser_status` is NOT a readiness check** — it only lists open sessions.+- AD-core **`bridge_readiness`** is the platform-level "is this bridge up?" probe.++## Pinning a browser — `browser_use`++pup auto-picks, but you can override (persists across restarts; launch still spawn-verifies + falls back):++```bash+adom-desktop browser_use '{"browser":"chrome"}' # pin installed Chrome+adom-desktop browser_use '{"browser":"edge"}' # pin Edge+adom-desktop browser_use '{"browser":"cft"}' # install Chrome for Testing (~150 MB once) + pin it+adom-desktop browser_use '{"browser":"auto"}' # back to auto-pick+```++Every `browser_open_window` response also carries `browser` (what was driven), `availableBrowsers`, and+`defaultBrowser` — surface it if the user asks which browser pup is using.++## Installing real Google Chrome on an Edge-only box++If the user specifically wants Chrome:++```bash+adom-desktop browser_use '{"browser":"chrome","install":true}'+```++- Downloads Google's official installer and installs Chrome, then pins it. **On a normal PC this is+ silent** (user-scoped, no prompt).+- **On a locked-down box (a VM / managed device) it needs a Windows UAC.** pup escalates automatically and,+ on **AD ≥ 1.9.84**, fires a native "**click YES to finish installing Chrome**" notification **from the+ bridge itself** — on the machine with the prompt **and** pinged to whatever device the user is attending+ (cross-AD). If the prompt expires it re-notifies with a one-tap "Approve now". All autonomous — no AI turn+ needed. On older AD (no bridge-notify) it raises the UAC once and returns a hint for **you** to relay+ ("go to the box and click YES"), then you re-run the verb.+- **Poll `browser_readiness`**: `chromeStablePhase` goes `installing → awaiting_uac` (waiting on the user's+ click) `→ ready`; `browserKind` flips to `"chrome"`. pup keeps driving Edge meanwhile, so nothing is+ blocked. `elevate:"auto"|true|false` on the call tunes the prompt behavior.++## Installing / prewarming Chrome for Testing (last resort)++```bash+adom-desktop browser_prewarm '{}' # fetch CfT WITHOUT opening a window, then pin it (poll readiness)+adom-desktop browser_use '{"browser":"cft"}' # same, via the pin path+```++Only needed on a box with no Chrome and no Edge. **CfT reported as `skipped` is NOT a failure** — on AD+≥ 1.9.76 the prewarm is conditional, so a box with a system browser skips the ~150 MB download and+`bridge_readiness` shows the asset `{present:false, skipped:true}`. That's the intended+installed-browser-first behavior; don't retry or report an error.++## Cold-start error table — what a `browser_open_window` response can mean++| Response | Means | Do |+|---|---|---|+| `ok:true` / a real session | A browser was found + launched | Proceed — the common case |+| `errorCode:"node_not_found"` / "could not run npm" | The bridge's Node runtime isn't ready yet | **AD ≥ 1.9.63 provisions Node for you** — check `adom-desktop runtimes '{}'`; if `installing`, wait a few seconds and **retry**. No user action. **Known AD false-negative:** if `runtimes` shows node `ready` AND `browser_readiness` works but other verbs still say `node_not_found`, it's an AD verb-gate bug — do NOT loop `desktop_install_node`; tell the user to update AD |+| `errorCode:"bridge_restarting"` (`stillRunning:true`) | AD ≥ 1.9.77: verb landed in the brief gap while the bridge respawns after an install/update | Poll `browser_readiness` until it answers, then retry the same verb — not a failure |+| `errorCode:"chrome_for_testing_installing"` (`installing:true`) | RARE — no Chrome AND no Edge, so pup is fetching CfT in the background | Poll `browser_readiness` until `{ready:true}`, then re-issue. Faster: ask the user to install Chrome/Edge (Edge ships with Windows) |+| `errorCode:"chrome_install_no_disk"` | No browser + too little disk for CfT (`readiness.diskFreeMb`) | Tell the user how much is free vs the ~800 MB needed; ask them to free space or install Edge. Do NOT retry the download |+| `errorCode:"chrome_install_failed"` / `chrome_download_network` / `chrome_install_permission` | No browser AND the CfT fallback download failed | Read `readiness.lastError`; ask the user to install Chrome or Edge |++**Always surface a verb's `_hint` verbatim** when it reports not-ready or failure.++## Node provisioning — you do NOTHING (AD ≥ 1.9.63)++pup is a Node process, but **you no longer install Node.** AD provisions the runtime itself — a system Node+if on PATH, else a portable **no-UAC** copy it downloads to `~/.adom/adom-runtimes/` and prewarms, then+spawns the bridge by absolute path. Do NOT run `desktop_install_node` / npm installers / ask the user to+install Node. On a truly fresh box it may be `installing` for a few seconds after first launch — check+`adom-desktop runtimes '{}'` (`{state: absent|installing|ready|failed}`) and retry once `ready`. (Legacy+`desktop_install_node` — which pops a UAC — is only for AD older than 1.9.63.)++## The shell-approval gate (rarely relevant to pup)++Core AD verbs and pup's own `browser_*` / `desktop_record_*` verbs run with **NO desktop prompt**. Only+open-ended `shell_execute` / `run_script` pop AD's "Shell command needs approval" dialog — which you+almost never need (prefer typed verbs). If one returns `needs_approval` / `human_only` on **AD ≥ 1.9.86**:+run `adom-desktop --target <box> request_shell_approval` (re-surfaces the dialog), ask the user to click+Approve/Bypass, then **retry your original verb**. Never call `shell_auto_approve` (human-only, refuses).+This is AD's own gate, NOT a Windows UAC.
skills/pup-screenshots-recording/SKILL.mdadded+61@@ -0,0 +1,61 @@+---+name: pup-screenshots-recording+user-invocable: true+description: "Capturing pup (Puppeteer bridge) windows: SCREENSHOTS (browser_screenshot — lossless PNG, full-page vs viewport, full-resolution via browser_screenshot_full_res, and how to actually READ the image) and RECORDING (browser_record_start/stop records ONE pup tab/window's page content to a video; desktop_record_start/stop records the WHOLE desktop across apps). Covers framing a window at an exact size for a clean shot WITHOUT foregrounding it, and why pup shots are true-to-life (real rendered window, not headless). Read when screenshotting a page, capturing full-page vs above-the-fold, recording a demo/walkthrough of an app, or choosing the window-recorder vs the desktop-recorder. Trigger words: browser_screenshot, browser_screenshot_full_res, full page screenshot, screenshot the page, read the screenshot, browser_record_start, browser_record_stop, desktop_record_start, desktop_record_stop, record my app, record a demo, screen recording, walkthrough video, pup screenshot, pup recording."+---++**Parent skill:** adom-desktop-puppeteer-bridge · See also: `pup` (start here), `pup-windows-sessions-tabs`, `pup-browsers-and-chrome`.++# pup — screenshots & recording++pup windows are **real, GPU-rendered windows** (not headless), so screenshots and recordings are+true-to-life — fonts, canvas, WebGL, video, and CSS all render exactly as a user would see them. And it+all works while the window is in the **background** (see `pup-windows-sessions-tabs`), so you capture+without disturbing the user.++## Screenshots++```bash+adom-desktop browser_screenshot '{"sessionId":"mytask-web"}' # viewport, lossless PNG+adom-desktop browser_screenshot '{"sessionId":"mytask-web","fullPage":true}' # entire scrollable page+adom-desktop browser_screenshot '{"sessionId":"mytask-web","tabId":"tab-2"}' # a specific tab+```++- Returns a **PNG saved on the container** (the machine running the CLI) — the response gives you the path.+ **`Read` that path** to actually see it; a returned path is not the same as having looked. Downscaled to+ stay under the image-read limit by default.+- **`fullPage:true`** stitches the whole scrollable page; omit for just the viewport (above the fold).+- **`browser_screenshot_full_res`** captures at full native resolution (no downscale) when you need to read+ fine detail — larger file; use when the default shrink loses text you need.+- **Framing for a clean shot without showing the user:** size/position the window on open —+ `browser_open_window {width,height,x,y}` — which places it **without foregrounding** (background stays+ background). Great for a deterministic, repeatable capture frame. To capture the OS window *plus* any+ owned popups/dialogs on top, use AD's `desktop_screenshot_window` instead (it returns the window with its+ child popups).+- **Sanity-check, don't assume:** a page that 404'd or hit a login wall still screenshots — pair with+ `browser_eval` on `document.title` when in doubt.++## Recording — two recorders, pick by what you're capturing++| Recorder | Captures | Use for |+|---|---|---|+| **`browser_record_start` / `browser_record_stop`** | ONE pup tab/window — the page content | "record my app", a feature walkthrough, a focused UI flow |+| **`desktop_record_start` / `desktop_record_stop`** | the WHOLE desktop (multiple apps, KiCad/Fusion, OS dialogs) | anything spanning more than the pup window |++```bash+# Recording a pup window the user will watch → DO bring it forward first+adom-desktop browser_raise_os_window '{"sessionId":"demo-web"}'+adom-desktop browser_record_start '{"sessionId":"demo-web"}'+# ...drive the scene: navigate, click, type, scroll...+adom-desktop browser_record_stop '{"sessionId":"demo-web"}' # → { path: "...webm" }+adom-desktop browser_lower_os_window '{"sessionId":"demo-web"}' # tuck it away again when done+```++- `browser_record_stop` returns the saved video **path**. Recording status/list verbs:+ `browser_record_status` / `browser_record_list` (window recorder) and `desktop_record_status` /+ `desktop_record_list` / `desktop_recorder_open` / `desktop_recorder_close` (desktop recorder).+- Closing a tab/window mid-recording auto-stops that recording.+- For a **narrated** walkthrough, drive the scene here and hand the video to the `video-post` / `adom-tts`+ flow for voiceover — pup just produces the raw capture.+- A pup window can record from the **background** too, but for a demo the user will actually watch, raise it+ first (that's an explicit "show me" case where `foreground` is warranted).
skills/pup-windows-sessions-tabs/SKILL.mdadded+118@@ -0,0 +1,118 @@+---+name: pup-windows-sessions-tabs+user-invocable: true+description: "pup (Puppeteer bridge) windows, sessions, and tabs: how sessionIds work and why to task-prefix them, WINDOW OWNERSHIP so you never steal another AI thread's window (owner + session_owned_by_another_thread + takeover), the BACKGROUND-by-default rule (foreground:true is the only way to show a window; sizing/positioning does NOT foreground), running MANY TABS in one window instead of many windows, switching/listing/closing sessions and tabs, and verifying what you actually opened. Read when opening/reusing pup windows, managing tabs, deciding background vs foreground, or seeing errorCode session_owned_by_another_thread. Trigger words: pup sessionId, browser_open_window owner, session_owned_by_another_thread, takeover window, pup background, foreground:true, browser_open_tab, browser_switch_tab, browser_list_tabs, browser_switch_window, browser_list_windows, browser_close_window, pup tabs, many tabs one window, don't steal window, browser_lower_os_window, browser_raise_os_window, browser_alert_window."+---++**Parent skill:** adom-desktop-puppeteer-bridge · See also: `pup` (start here), `pup-screenshots-recording`, `pup-browsers-and-chrome`.++# pup — windows, sessions & tabs++A **session** = one browser **window** (a fresh isolated Chrome/Edge process) that holds **many tabs**.+You address everything by `sessionId`. `profile` (defaults to `sessionId`) names the on-disk cookie jar.++## sessionIds — task-prefix them, and pass `owner`++`browser_open_window {sessionId}` either **creates** a window (new id) or **reuses/navigates** an existing+one (id already exists). Because sessions are **shared across every AI thread on this desktop**, a generic+id collides:++- **Task-prefix the id:** `"<your-task>-web"` (e.g. `wikifix-preview`), never `wiki` / `test` / `main`.+- **Always pass `owner:"<your-task>"`.** It's an advisory label that lets pup protect your window.+- Pick a **stable `profile`** across runs if you want the cookie jar to persist (user logs in once, reuse+ for months): `{"sessionId":"vendor-fetch","profile":"vendor","owner":"vendor-fetch", …}`.++## Window ownership — do NOT steal another AI thread's window++If you `browser_open_window` a sessionId that already exists **with a new url**, pup NAVIGATES that window+— destroying whatever another thread was doing there. pup guards this:++1. **Declared cross-owner reuse is REFUSED** — `errorCode: session_owned_by_another_thread` (also on+ `browser_navigate` / `browser_close` / `browser_close_window`). The `_hint` tells you the current owner+ + url + age.+2. **If YOU get that error** — the window isn't yours. Open your OWN with a unique task-prefixed sessionId.+ Pass **`takeover:true`** ONLY if the user explicitly told you to repurpose that window.+3. **Anonymous grabs are allowed but loudly reprimanded** in the `_hint` (with the previous url so you can+ put it back) — so ALWAYS declare `owner` to be a good citizen and to get protection.+4. **Reusing your OWN window is good** — same sessionId + same owner = a smooth refresh (e.g. re-rendering+ a page you just republished). Ownership only blocks *cross-thread* grabs.+5. **Unsure what exists?** `browser_status '{}'` lists every window with its `owner`, current `url`, and+ `ageMinutes`. Run it at the start of a task (especially after a context compaction) before picking an id.++```bash+# right — owned, task-prefixed+adom-desktop browser_open_window '{"sessionId":"wikifix-preview","owner":"wikifix","url":"<url>"}'+# wrong — generic + anonymous: may hijack another thread's window (pup reprimands you in _hint)+adom-desktop browser_open_window '{"sessionId":"wiki","url":"<url>"}'+```++## Background by default — pup NEVER pops onto the user's screen unless asked++**pup is the AI's workspace, not the user's.** Every window opens **in the background** (behind the user's+windows, no focus stolen) and stays fully drivable + screenshottable there — you do NOT lower it yourself.++- **`foreground:true` is the ONLY thing that shows a window.** Not sizing, not navigating, not+ screenshotting, not recording setup. Pass it ONLY when the user explicitly wants to WATCH (a demo, a+ "show me", a live recording).+- **Sizing/positioning does NOT foreground** — `browser_open_window {width,height,x,y}` places the window+ at an exact size/spot **for clean screenshot framing** while it stays in the background. So you can frame+ a shot without ever disturbing the user's main work.+- **`browser_alert_window`** flashes the taskbar (a non-intrusive "it's ready/done" nudge) — prefer it over+ raising the window.+- `browser_raise_os_window` / `browser_focus_window` bring a window forward (use sparingly, only to show+ the user); `browser_lower_os_window` tucks it back.++```bash+# default: background — drive it invisibly, user never sees a thing+adom-desktop browser_open_window '{"sessionId":"job-web","owner":"job","url":"<url>"}'+adom-desktop browser_screenshot '{"sessionId":"job-web"}'+# only if the user asked to SEE it:+adom-desktop browser_open_window '{"sessionId":"demo-web","owner":"demo","url":"<url>","foreground":true}'+```++## One window, many tabs (multi-URL tasks)++For a vendor fetch / multi-doc review, put the URLs in **tabs of one window** (one taskbar icon), not+separate windows:++```bash+adom-desktop browser_open_window '{"sessionId":"task-web","owner":"task","url":"https://first.example.com"}'+adom-desktop browser_open_tab '{"sessionId":"task-web","url":"https://second.example.com"}' # → { tabId: "tab-2" }+adom-desktop browser_switch_tab '{"sessionId":"task-web","tabId":"tab-2"}'+adom-desktop browser_screenshot '{"sessionId":"task-web","tabId":"tab-2"}' # tabId optional — omit for the active tab+adom-desktop browser_close_tab '{"sessionId":"task-web","tabId":"tab-2"}'+```++- `browser_open_tab` returns a `tabId`; pass it to `switch`/`close`/`screenshot`/`eval`/`navigate` (omit to+ target the active tab).+- **Closing the last tab closes the window.**+- Popup tabs (e.g. a PDF the page opens) auto-attach and appear in `browser_list_tabs`; a closed popup's+ last URL is retained so you can `browser_fetch_url` it if you missed it.++## Don't double-open; reuse correctly++- New session entirely → `browser_open_window`.+- Same session, new page in the same tab → `browser_navigate` (with `tabId` if multiple tabs).+- A second view in the same session → `browser_open_tab` (NOT a second window).+- Switch which window is "active" → `browser_switch_window`; list them → `browser_list_windows`.++Opening the same content under a new `sessionId` leaves the user staring at two identical windows — so+`browser_status` first and reuse when the URL is already open.++## Verify what you opened — `ok:true` is not enough++`browser_open_window` returns `ok:true` as soon as navigation *starts*, not when content rendered. Confirm+with `browser_eval '{"sessionId":"…","expr":"document.title"}'` (fastest — 404s, login walls, anti-bot+blocks all change the title) or a `browser_screenshot` when the title isn't discriminating.++## Verbs at a glance++| Verb | Does |+|---|---|+| `browser_open_window` | Create (or reuse) a session/window. Args: `sessionId`, `owner`, `url?`, `profile?`, `foreground?`, `width/height/x/y?`, `takeover?` |+| `browser_navigate` | Drive an existing tab to a URL (no new window) — `sessionId`, `url`, `tabId?` |+| `browser_open_tab` / `browser_switch_tab` / `browser_close_tab` / `browser_list_tabs` | Tabs within one window |+| `browser_switch_window` / `browser_list_windows` | Multiple sessions |+| `browser_status` | List every session + `owner`/`url`/`ageMinutes` (NOT a readiness check) |+| `browser_lower_os_window` / `browser_raise_os_window` / `browser_focus_window` / `browser_alert_window` | Z-order / focus / taskbar flash |+| `browser_close_window` (one session) / `browser_close` (all) | Close — destructive |
uninstall.sh+3−1@@ -4,7 +4,9 @@ set -e remove_from() { local BASE="$1"- rm -rf "$BASE/adom-desktop-puppeteer-bridge" "$BASE/pup-bridge-dev" "$BASE/pup-bridge-publish"+ rm -rf "$BASE/adom-desktop-puppeteer-bridge" \+ "$BASE/pup-windows-sessions-tabs" "$BASE/pup-screenshots-recording" "$BASE/pup-browsers-and-chrome" \+ "$BASE/pup-bridge-dev" "$BASE/pup-bridge-publish" echo " <- $BASE" } echo "Removing adom-desktop-puppeteer-bridge skills:"