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 on the taskbar with its AI thread's name.
← Commit history
v1.2.0: Tier-1 prewarm for AD v1.9.47 — bridge.json prewarm:[chrome-for-testing]; gate detect-only (defer fetch to AD-core, non-blocking {installing:true}); browser_readiness read-only; skill points 'is it ready' at bridge_readiness not browser_status
5 files changed
+60−43
SKILL.md+22−13@@ -1,7 +1,7 @@ --- name: pup user-invocable: true-description: "Drive Chrome browser windows on the user's desktop via the adom-desktop CLI (the Puppeteer bridge): open/close windows + tabs, navigate, screenshot, eval JS, record, drive the user's REAL signed-in Chrome/Edge/Brave. Includes COLD-START SELF-HEAL — on a fresh PC the bridge auto-installs Chrome for Testing; poll browser_readiness. 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 Chrome browser windows on the user's desktop via the adom-desktop CLI (the Puppeteer bridge): open/close windows + tabs, navigate, screenshot, eval JS, record, drive the user's REAL signed-in Chrome/Edge/Brave. Cold-start: on a fresh PC Adom Desktop background-fetches Chrome for Testing (Tier-1 prewarm); for 'is it ready' poll AD's bridge_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." --- # Pup — Puppeteer Browser Control (with cold-start self-heal)@@ -25,27 +25,36 @@ When you run `browser_open_window` on a cold PC, one of these happens — handle | Response | What it means | What you do | |---|---|---| | `errorCode: "node_not_found"` | Node.js isn't installed (the bridge can't even start). | Run `adom-desktop desktop_install_node '{}'`, wait for it, then retry the `browser_open_window`. |-| `errorCode: "chrome_for_testing_installing"` (`installing:true`) | **Chrome for Testing is missing; the bridge is auto-downloading it now (~150 MB, one time).** This is EXPECTED on a fresh PC — NOT a failure. | **Poll `browser_readiness` until `ready:true`** (usually 1–3 min), THEN re-issue the exact same `browser_open_window`. You do NOT install anything yourself. Do NOT report failure to the user. |+| `errorCode: "chrome_for_testing_installing"` (`installing:true`) | **Chrome for Testing isn't cached yet; Adom Desktop is fetching it in the BACKGROUND (Tier-1 prewarm, ~150 MB, one time).** The bridge did NOT block. EXPECTED on a fresh PC — NOT a failure. | **Poll AD's `bridge_readiness` until Chrome reports ready** (usually 1–3 min), THEN re-issue the exact same `browser_open_window`. You do NOT install anything yourself. Do NOT report failure. (If it never becomes ready, `browser_prewarm` makes the bridge fetch it itself.) | | `errorCode: "chrome_install_failed"` / `chrome_download_failed` | The Chrome download itself failed. | Read `readiness.lastError`. It's almost always no network / a proxy blocking `storage.googleapis.com`, or low disk — NOT a Node problem. Retry `browser_prewarm`; if it persists, surface `lastError` to the user. Fallback: drive their real browser via native mode (below). | | `ok:true` / a real session | Everything was already warm. | Proceed normally. | +### "What do I have / is it ready?" → ask AD's `bridge_readiness` (NOT `browser_status`)++For "is pup ready / what's installed," call **`adom-desktop bridge_readiness`** — AD-core's readiness probe.+It reports server-up + Chrome-for-Testing readiness, including AD's **Tier-1 prewarm** progress.+`browser_status` only lists open sessions — it is NOT a readiness check.+ ### The clean cold-start recipe ```bash-# 1) (optional but nice) Warm Chrome for Testing WITHOUT opening a window first:-adom-desktop browser_prewarm '{}' # returns fast; kicks off the install--# 2) Poll readiness until ready (the bridge installs CfT itself):-adom-desktop browser_readiness '{}' # → {ready:false, installing:true, installProgressPct:42, ...}-# ...wait a few seconds, poll again until → {ready:true, chromeForTestingInstalled:true}+# 1) Ask AD if pup is ready (poll until Chrome shows ready):+adom-desktop bridge_readiness '{}' -# 3) Now open the window — it's instant:+# 2) Once ready, open the window — it's instant: adom-desktop browser_open_window '{"sessionId":"chip-fetcher","profile":"chip-fetcher","url":"<url>"}' ``` -`browser_prewarm` and `browser_readiness` are the two cold-start verbs. `browser_prewarm '{"wait":true}'`-blocks until the install finishes instead of returning immediately. **Always surface a verb's `_hint`-field verbatim to the user when something isn't ready** — it tells you (and them) exactly what's happening.+On a fresh PC, **Adom Desktop fetches Chrome for Testing in the background** (the bridge declares+`prewarm:["chrome-for-testing"]`); `browser_open_window` returns a non-blocking `{installing:true}` until+it's cached — it never blocks or crashes. So: poll `bridge_readiness` → then open. You never run an+installer yourself.++- **`bridge_readiness`** (AD-core) — THE readiness question: server-up + Chrome cached (+ prewarm progress).+- **`browser_readiness`** (this bridge) — pup's own **READ-ONLY** detail (Chrome build id, fetch phase). Never downloads.+- **`browser_prewarm`** — explicit fallback: have the bridge fetch Chrome itself if AD's prewarm didn't.++**Always surface a verb's `_hint` verbatim** when something isn't ready. > Why this matters: before v1.1.0, a fresh PC with no Chrome for Testing made `browser_open_window` > fail with a generic "Failed to launch Chrome after 3 attempts" and no actionable hint — the AI@@ -97,7 +106,7 @@ adom-desktop browser_alert_window '{"sessionId":"job"}' # non-intrusive "do | `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` | All sessions + chrome readiness | none |+| `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` |
adom-bridge-puppeteer-manifest.json+11−6@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "puppeteer",- "version": "1.1.0",- "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.1.0/adom-bridge-puppeteer-v1.1.0.zip",- "sha256": "525eabb0350ea0eaf52a949e9b3026f5a4a035868afe9546a7478298b9539b8c",- "size": 127135,+ "version": "1.2.0",+ "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.2.0/adom-bridge-puppeteer-v1.2.0.zip",+ "sha256": "57d27ac4bb22b9bea1138c4b8260224e14ff0e680f5a731a8356005b510bf469",+ "size": 127304, "verbPrefixes": [ "browser_", "desktop_recorder_",@@ -12,5 +12,10 @@ ], "healthEndpoint": "/status", "statusVerb": "browser_readiness",- "released_at": "2026-06-29T20:30:00Z"-}+ "prewarm": {+ "assets": [+ "chrome-for-testing"+ ]+ },+ "released_at": "2026-06-30T00:00:00Z"+}
src/BRIDGE_VERSION+1−1@@ -1 +1 @@-1.1.0+1.2.0
src/bridge.json+5−2@@ -2,8 +2,8 @@ "manifest_version": 1, "name": "puppeteer", "displayName": "Puppeteer (Chrome for Testing + Native)",- "version": "1.1.0",- "description": "Cold-start self-heal (v1.1.0): on a fresh PC the bridge DETECTS that Chrome for Testing is missing, AUTO-INSTALLS it (programmatically via the bundled @puppeteer/browsers — no npx), and reports progress via browser_readiness / browser_prewarm, so a first-ever 'open in pup' works hands-off instead of failing. Headful browser automation. Default path drives Chrome for Testing (CfT) with managed pup profiles. v1.0.6+ optional native-drive: pass nativeBrowser+nativeProfile (from desktop_list_browsers) to browser_open_window to drive the user's REAL Chrome/Edge/Brave with the actual signed-in account + org policies + device enrollment intact. Smart flow: if the browser is CLOSED, AD launches it fresh immediately with NO prompt (nothing to disrupt) and reports back ready-to-drive (launchedFresh:true); if it's OPEN without the port, AD returns errorCode:'native_relaunch_required' so the caller asks the user, then re-call with nativeRelaunch:true. AD then closes only that browser's own PIDs cleanly (WM_CLOSE — Sessions saved; CfT bridge sessions + other Chromium left untouched) and relaunches with --restore-last-session so every tab/window comes back. If the debug port is policy-blocked (e.g. Google Workspace's RemoteDebuggingAllowed disabled — affects Chrome browser-wide, all profiles), native drive returns errorCode:'native_cdp_blocked' with a `diagnosis` of the likely cause (local registry policy vs cloud-managed) plus an `alternatives` list of other installed browsers — Edge usually escapes Google management — so the caller can ask the user which to use; AD won't close that browser again for it. The generic CfT path (browser_open_window with no nativeBrowser) always works as a fallback.",+ "version": "1.2.0",+ "description": "Tier-1 cold-start (v1.2.0, for AD v1.9.47+ two-tier model): pup brings its OWN browser (Chrome for Testing), so it needs no detect-block. It declares prewarm:[chrome-for-testing] so Adom Desktop fetches Chrome in the BACKGROUND and reports readiness via bridge_readiness. The bridge server starts fine WITHOUT Chrome; browser_readiness is a READ-ONLY probe (ready = server-up AND Chrome cached; never triggers a download); browser_open returns a non-blocking {installing:true} (never blocks/crashes) until Chrome is cached; browser_prewarm is an explicit bridge-side fetch fallback. Headful browser automation. Default path drives Chrome for Testing (CfT) with managed pup profiles. v1.0.6+ optional native-drive: pass nativeBrowser+nativeProfile (from desktop_list_browsers) to browser_open_window to drive the user's REAL Chrome/Edge/Brave with the actual signed-in account + org policies + device enrollment intact. Smart flow: if the browser is CLOSED, AD launches it fresh immediately with NO prompt (nothing to disrupt) and reports back ready-to-drive (launchedFresh:true); if it's OPEN without the port, AD returns errorCode:'native_relaunch_required' so the caller asks the user, then re-call with nativeRelaunch:true. AD then closes only that browser's own PIDs cleanly (WM_CLOSE — Sessions saved; CfT bridge sessions + other Chromium left untouched) and relaunches with --restore-last-session so every tab/window comes back. If the debug port is policy-blocked (e.g. Google Workspace's RemoteDebuggingAllowed disabled — affects Chrome browser-wide, all profiles), native drive returns errorCode:'native_cdp_blocked' with a `diagnosis` plus an `alternatives` list of other installed browsers — Edge usually escapes Google management. The generic CfT path (browser_open_window with no nativeBrowser) always works as a fallback.", "homepage": "https://github.com/adom-inc/adom-desktop", "author": "Adom Inc.", "license": "MIT",@@ -15,6 +15,9 @@ "stopMethod": "kill", "killImageName": "node.exe" },+ "prewarm": {+ "assets": ["chrome-for-testing"]+ }, "verbPrefixes": [ "browser_", "desktop_recorder_",
src/server.js+21−21@@ -2930,25 +2930,24 @@ const server = http.createServer(async (req, res) => { // `profile` arg. Default (no native args) is byte-for-byte the same // CfT path as v1.0.4 — strictly additive, no regressions. const wantsNative = !!(args.nativeBrowser || args.nativeProfile);- // ── Cold-start self-heal (CfT path only) ─────────────────────────────- // The default (non-native) path drives Chrome for Testing. On a fresh PC- // — HD bundled AD, and the installer pre-installed NEITHER Node NOR CfT —- // this is the #1 failure ("open in pup did nothing"). Detect + auto-install- // CfT (non-blocking) and return a POLLABLE structured status instead of- // blindly spawning a missing binary 3× then throwing a dev-string.+ // ── Tier-1 cold-start gate (CfT path only) — READ-ONLY, never blocks ──+ // pup brings its own browser (Chrome for Testing). AD v1.9.47+ backgrounds+ // the Chrome-for-Testing fetch via this bridge's prewarm:["chrome-for-testing"]+ // declaration, so this gate does NOT trigger a download itself (no racing+ // AD-core) and NEVER blocks: if Chrome isn't cached yet it returns a pollable+ // {installing:true}. The explicit bridge-side fetch fallback is browser_prewarm. // Native drive uses the user's REAL browser (not CfT), so it skips this. if (!wantsNative) {- const r = await chrome.ensureChromeReady({ background: true });- if (!r.ready) {- const rd = chrome.readiness();+ const rd = chrome.readiness(); // detect-only; never downloads+ if (!rd.ready) { if (rd.installPhase === 'failed') { sendJSON(res, { success: false, errorCode: rd.lastErrorCode || 'chrome_install_failed',- error: `Chrome for Testing (the browser pup drives) could not be installed: ${rd.lastError}`,+ error: `Chrome for Testing (the browser pup drives) failed to install: ${rd.lastError}`, chromeForTestingInstalled: false, readiness: rd,- _hint: 'Chrome for Testing failed to download. This is almost always no network / a proxy blocking storage.googleapis.com, or low disk — NOT a Node problem (Node is fine; the bridge is running). Retry with browser_prewarm; if it keeps failing, surface readiness.lastError to the user. As a fallback you can drive the user\'s real browser via browser_open_window with nativeBrowser+nativeProfile (run desktop_list_browsers first).',+ _hint: 'A prior Chrome-for-Testing fetch failed (usually no network / a proxy blocking storage.googleapis.com, or low disk — NOT a Node problem). Retry `browser_prewarm`; if it keeps failing, surface readiness.lastError to the user. Fallback: drive the user\'s real browser via browser_open_window with nativeBrowser+nativeProfile.', }); return; }@@ -2956,12 +2955,11 @@ const server = http.createServer(async (req, res) => { success: false, errorCode: 'chrome_for_testing_installing', installing: true,- progressPct: rd.installProgressPct, chromeForTestingInstalled: false,- statusVerb: 'browser_readiness',+ statusVerb: 'bridge_readiness', readiness: rd,- error: 'Chrome for Testing (the browser pup drives) is not installed on this machine yet — the bridge is downloading it now (~150 MB, one time).',- _hint: 'FIRST-TIME SETUP on this PC: pup needs Chrome for Testing and is installing it for you in the background — you do NOT need to install anything yourself. Poll `browser_readiness` until {ready:true} (typically 1–3 min on a normal connection), THEN re-issue this exact browser_open_window call. Do not report failure; this is expected on a fresh machine.',+ error: 'Chrome for Testing is not cached yet — Adom Desktop is fetching it in the background (Tier-1 prewarm).',+ _hint: 'FIRST-TIME on this PC: AD is downloading Chrome for Testing in the background (the bridge declares prewarm:["chrome-for-testing"]); the bridge did NOT block on it. Poll AD\'s `bridge_readiness` until Chrome reports ready, THEN re-issue this exact browser_open_window. Do NOT report failure — this is expected on a fresh machine. (If it never becomes ready, call `browser_prewarm` to have the bridge fetch Chrome itself.)', }); return; }@@ -4043,20 +4041,22 @@ const server = http.createServer(async (req, res) => { return; } case 'browser_readiness': {- // Cold-start readiness probe: is Chrome for Testing installed / installing?- // The AI polls this on a fresh PC before/after browser_open_window.+ // READ-ONLY readiness probe. NEVER triggers a download. `ready` reflects both+ // server-up (this verb answered) AND Chrome-for-Testing cached. For the general+ // "what do I have / is it ready" question prefer AD's `bridge_readiness` (it also+ // reports AD-core's Tier-1 prewarm progress); this verb is pup's own detailed view. const rd = chrome.readiness(); sendJSON(res, { success: true, output: JSON.stringify(rd), ...rd, _hint: rd.ready- ? 'Ready — Chrome for Testing is installed and pup can open windows. Proceed with browser_open_window.'+ ? 'Ready — Chrome for Testing is cached and the server is up; pup can open windows. Proceed with browser_open_window.' : rd.installing- ? `Chrome for Testing is installing (${rd.installProgressPct}%). Poll browser_readiness until ready:true (usually 1–3 min), then browser_open_window.`+ ? `Chrome for Testing is being fetched (${rd.installProgressPct}%). Poll bridge_readiness (or this verb) until ready:true, then browser_open_window.` : rd.installPhase === 'failed'- ? `Chrome for Testing install FAILED: ${rd.lastError}. Retry browser_prewarm; if it persists, surface this to the user (usually network/proxy blocking the download, or low disk).`- : 'Chrome for Testing is not installed yet. Call browser_prewarm (or just browser_open_window — it self-installs) to fetch it.',+ ? `Chrome for Testing fetch FAILED: ${rd.lastError}. Retry browser_prewarm; if it persists, surface this to the user (usually network/proxy blocking the download, or low disk).`+ : 'Chrome for Testing is not cached yet. Adom Desktop fetches it in the background (Tier-1 prewarm) — poll AD\'s bridge_readiness; or call browser_prewarm to have the bridge fetch it.', }); return; }