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.6.3: honor ADOM_BIND_HOST (AD 1.9.63); SKILL drops self Node bootstrap — AD now provisions the runtime (portable no-UAC, absolute-path spawn); document the runtimes verb
6 files changed
+33−51
SKILL.md+18−37@@ -26,49 +26,30 @@ 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"` (or `could not run npm`) | Node.js isn't on AD's PATH (the bridge can't even start). | **Warn the user about the UAC prompt FIRST (see "The Node step" below), then** run `adom-desktop desktop_install_node '{}'`, wait ~1–2 min, retry. **If it STILL says node_not_found after a successful install, AD's PATH is stale** — Node is installed but the running Adom Desktop process can't see it. Ask the user to quit + reopen Adom Desktop, then retry. Do NOT loop desktop_install_node — reinstalling won't fix a stale PATH. |+| `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".) | | `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/Edge, or drive their real browser via native mode (below). | **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 — get Node on the box with ZERO prompts (preferred)--pup is a Node process, so on a fresh box AD can't spawn it until Node exists (`node_not_found` /-`could not run npm`). **Do NOT use `desktop_install_node` (a UAC/admin MSI) or `shell_execute` (an approval-popup).** Install portable Node with **AD's typed verbs, which are ungated — no UAC, no shell approval, no-firewall.** Proven on a fresh Edge-only box. Get `<user>` from `bridge_list` (pup's `installPath`) or-`list_dir C:\Users`; use `--target <name>` when >1 desktop is connected.--```bash-# 1. In the container: download the portable Node zip (~34 MB)-curl -sL https://nodejs.org/dist/v22.11.0/node-v22.11.0-win-x64.zip -o /tmp/node.zip--# 2. Push it to the box — send_files is a typed 'files' verb (NO prompt)-adom-desktop send_files '{"filePaths":["/tmp/node.zip"],"dest":"C:\\Users\\<user>\\AppData\\Local\\adom-node"}'--# 3. Unzip with Windows' built-in tar via launch_process (typed, NO prompt)-adom-desktop launch_process '{"path":"C:\\Windows\\System32\\tar.exe","args":["-xf","C:\\Users\\<user>\\AppData\\Local\\adom-node\\node.zip","-C","C:\\Users\\<user>\\AppData\\Local\\adom-node"],"window":"hidden"}'--# 4. Add node to the USER PATH — read first, APPEND, don't clobber (registry_* are typed, NO prompt)-adom-desktop registry_get '{"hive":"HKCU","key":"Environment","name":"Path"}' # note the current value-adom-desktop registry_set '{"hive":"HKCU","key":"Environment","name":"Path","type":"REG_SZ","value":"<existing>;C:\\Users\\<user>\\AppData\\Local\\adom-node\\node-v22.11.0-win-x64"}'-```--5. **Ask the user to quit + reopen Adom Desktop** so AD re-reads PATH — an app restart, **not** a prompt,- **not** admin. Then `browser_readiness` → ready, and `browser_open_window` drives Edge. (This one reopen- is the only manual step; it disappears once AD ships absolute-path runtime spawn — see the shared-runtime- proposal.)--**Zero approval popups, zero UAC, zero firewall.** Verify with `list_dir` after step 3 (`node.exe` +-`npm.cmd` present) and `registry_get` after step 4.--**Fallbacks (only if the above is blocked):**-- `desktop_install_node` — installs Node via an admin MSI → **a Windows UAC**. If you must use it, warn the- user first (chat + `notify_user`) that a UAC for "Node.js — OpenJS Foundation" will appear and they must- click **Yes**, else the cold-start dead-ends. Then reopen AD (stale PATH) as in step 5.-- `shell_execute` — works but pops an AD shell-approval; the typed verbs above avoid it entirely.+### 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
adom-bridge-puppeteer-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "puppeteer",- "version": "1.6.1",- "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.6.1/adom-bridge-puppeteer-v1.6.1.zip",- "sha256": "908f7f647ba7c7e05ae0ad796539faba1cee17cc216fed59cd3592171c1e44bf",- "size": 138965,+ "version": "1.6.3",+ "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.6.3/adom-bridge-puppeteer-v1.6.3.zip",+ "sha256": "70327d8abcfd589b7bcd56aaf601a8ca31d5b0c24dfa699b9b8ddd2c98f05f3a",+ "size": 138996, "verbPrefixes": [ "browser_", "desktop_recorder_",
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "adom-desktop-puppeteer-bridge",- "version": "1.6.2",+ "version": "1.6.3", "type": "app", "description": "CONTAINER-SIDE install \u2014 the everyday `pup` user skill only. This pkg drops the `pup` skill (drive the browser bridge: open/close/tabs/screenshot/record, background-by-default) into ~/.claude/skills/ AND ~/.codex/skills/. It does NOT install the developer/publishing skills (pup-bridge-dev, pup-bridge-publish) \u2014 those are source-only; clone the repo (adom-wiki repo clone adom/adom-desktop-puppeteer-bridge) to get them from dev-skills/. It also does NOT contain the bridge runtime: the bridge (Node + Chrome for Testing) lives on the DESKTOP \u2014 bundled inside Adom Desktop and published here on the Releases tab as adom-bridge-puppeteer-v<ver>.zip, which Adom Desktop streams into its bridges-cache. TL;DR: `pkg install` = the pup user skill; dev skills = clone the source; the bridge = the Release zip + bundled in Adom Desktop.", "keywords": [
src/BRIDGE_VERSION+1−1@@ -1 +1 @@-1.6.1+1.6.3
src/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "puppeteer", "displayName": "Puppeteer (Chrome for Testing + Native)",- "version": "1.6.1",+ "version": "1.6.3", "description": "Drives a browser ALREADY on the machine \u2014 installed Chrome, else Microsoft Edge (every Windows PC ships with Edge) \u2014 launched with a FRESH isolated profile, so the common case has NO ~150 MB Chrome-for-Testing download and it never touches the user's real tabs/logins. Binds its local server to LOOPBACK (127.0.0.1) only, so no Windows Firewall 'allow Node through the firewall' prompt ever pops. Chrome for Testing is fetched only as a LAST RESORT if the machine has no Chromium browser at all. SPAWN-VERIFIED fallback: pup tries each detected browser (Chrome \u2192 Edge \u2192 CfT) and actually launches it, falling through to the next if one won't start (and handling browsers like Edge that relaunch onto another PID), so a corrupt/partial install never hard-fails an open. The browser that launches is cached as the default (persisted across restarts) so it isn't re-probed each time. On spawn the bridge SELF-WARMS: detects browsers + disk, and only if the box has no Chrome/Edge and no cached CfT does it background-fetch CfT so the first open is instant. browser_use pins a specific browser (chrome | edge | cft | auto); {browser:'chrome',install:true} downloads & installs REAL Google Chrome (user-scoped, no admin) on an Edge-only box. browser_readiness (READ-ONLY) reports which browser will be used + the full candidate list + the default + free disk. Every failure returns a specific errorCode + _hint (chrome_install_no_disk, chrome_download_network, chrome_install_permission, chrome_stable_not_detected, \u2026). Launches with FULL browser permissions by default for full debug (strictPermissions:true opts out). Headful automation: open/close windows+tabs, navigate, screenshot, eval JS, record. v1.0.6+ optional native-drive: pass nativeBrowser+nativeProfile (from desktop_list_browsers) to browser_open_window to drive the user's REAL signed-in Chrome/Edge/Brave (their actual profile, org policies, device enrollment) \u2014 with the smart relaunch/consent flow (native_relaunch_required / native_cdp_blocked). The generic path (no nativeBrowser) always works.", "homepage": "https://wiki.adom.inc/adom/adom-desktop", "author": "Adom Inc.",
src/server.js+8−7@@ -4725,13 +4725,14 @@ const server = http.createServer(async (req, res) => { server.timeout = 45_000; server.keepAliveTimeout = 30_000; -// Bind to LOOPBACK only. AD always talks to the bridge over 127.0.0.1, so binding to-// 127.0.0.1 (not 0.0.0.0/all interfaces) keeps Windows Firewall silent — a socket that-// only listens on loopback is firewall-exempt, so the scary "Allow Node.js through the-// firewall on public/private networks?" dialog never pops on the user's desktop. (It-// used to, because a hostless listen() binds every interface.)-server.listen(PORT, '127.0.0.1', async () => {- console.log(`Puppeteer Bridge running on http://127.0.0.1:${PORT} (loopback-only; no firewall prompt)`);+// Bind to the host AD tells us to (AD 1.9.63+ passes ADOM_BIND_HOST, always 127.0.0.1),+// falling back to loopback. AD's hard guarantee is NO firewall prompts by default —+// nothing it spawns may bind a public interface (0.0.0.0) unasked. A hostless listen()+// binds every interface and pops the Windows Firewall "allow access?" dialog; honoring+// ADOM_BIND_HOST keeps a loopback-only socket, which is firewall-exempt.+const BIND_HOST = process.env.ADOM_BIND_HOST || '127.0.0.1';+server.listen(PORT, BIND_HOST, async () => {+ console.log(`Puppeteer Bridge running on http://${BIND_HOST}:${PORT} (bind=${BIND_HOST}; no firewall prompt)`); console.log('Multi-session support: sessions are tabs sharing Chrome profiles via userDataDir'); console.log('Endpoints: /health, /command, /launch, /navigate, /reload, /screenshot, /screenshot.png, /errors, /eval, /wait, /status, /close'); // Self-warm: detect browsers + disk the moment the bridge spawns (right after AD