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.8.27: remove redundant pup-side localhost probe (AD core already reprimands localhost on failure); keep the real background-by-default fix + foreground-gate + finite flash
4 files changed
+13−85
adom-bridge-puppeteer-manifest.json+5−5@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "puppeteer",- "version": "1.8.26",- "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.8.26/adom-bridge-puppeteer-v1.8.26.zip",- "sha256": "e5f071c0e73444ca9b1ae28cbfefbaaf699cbc4cf5d8a12e64a1f395bb020142",- "size": 156777,+ "version": "1.8.27",+ "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.8.27/adom-bridge-puppeteer-v1.8.27.zip",+ "sha256": "4fb299c0a4bd85e4382d69ed1ea1ce2565f644b701122a750cf13b5655319cbd",+ "size": 155251, "verbPrefixes": [ "browser_", "desktop_recorder_",@@ -12,5 +12,5 @@ ], "healthEndpoint": "/status", "statusVerb": "browser_readiness",- "released_at": "2026-07-14T18:41:59Z"+ "released_at": "2026-07-14T18:54:10Z" }
src/BRIDGE_VERSION+1−1@@ -1 +1 @@-1.8.26+1.8.27
src/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "puppeteer", "displayName": "Puppeteer (Chrome for Testing + Native)",- "version": "1.8.26",+ "version": "1.8.27", "description": "Drives a browser ALREADY on the machine — installed Chrome, else Microsoft Edge (every Windows PC ships with Edge) — 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 → Edge → 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, …). 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. pup is a GENERIC, non-profile browser only — every session is a fresh isolated Chrome/Edge process, never the user's real signed-in browser; profile defaults to sessionId. To drive the user's REAL Chrome/Edge (their profile, cookies, SSO), use the Adom browser EXTENSION's nbrowser_* verbs — a separate bridge, not pup's job.", "homepage": "https://wiki.adom.inc/adom/adom-desktop", "author": "Adom Inc.",
src/server.js+6−78@@ -1491,26 +1491,6 @@ function osSendWindowToBack(profileName) { } catch (e) { console.error(`[bg] send-to-back failed for "${profileName}": ${e.message}`); return false; } } -// v1.8.24: capture the user's CURRENT foreground window BEFORE we launch a pup-// browser (which grabs foreground). We hand focus back to it right after, so-// opening a pup window NEVER steals focus. Returns a decimal HWND string or null.-function osGetForegroundWindow() {- if (process.platform !== 'win32') return null;- try {- const scriptPath = path.join(require('os').tmpdir(), `_adom_fg_${process.pid}_${Date.now()}.ps1`);- const ps = [- 'Add-Type @"',- 'using System; using System.Runtime.InteropServices;',- 'public class WinFgGet { [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow(); }',- '"@',- 'Write-Output ([int64][WinFgGet]::GetForegroundWindow())',- ].join('\r\n');- fs.writeFileSync(scriptPath, ps);- const out = execSync(`powershell.exe -NoProfile -ExecutionPolicy Bypass -File "${scriptPath}"`, { encoding: 'utf8', timeout: 4000 }).trim();- return out && out !== '0' ? out : null;- } catch (e) { return null; }-}- // v1.8.26: THE real "background by default" enforcement, done RIGHT. A freshly-launched // --start-maximized browser grabs foreground; osSendWindowToBack only lowers z-order and // SetForegroundWindow(userWindow) is BLOCKED by Windows' foreground lock — so the earlier@@ -1572,49 +1552,11 @@ function osBackgroundWindowByPid(pid, cdpPort) { } catch (e) { console.error(`[bg] pid ${pid} port ${cdpPort}: ${e.message}`); return 'error'; } } -// v1.8.26: is anything actually LISTENING on host:port (from the desktop, where pup-// runs)? A quick TCP connect. Used to sanity-check localhost URLs — see localhostReachability.-function isPortListening(host, port, timeoutMs = 1200) {- return new Promise((resolve) => {- const net = require('net');- const sock = new net.Socket();- let done = false;- const finish = (ok) => { if (done) return; done = true; try { sock.destroy(); } catch (e) {} resolve(ok); };- sock.setTimeout(timeoutMs);- sock.once('connect', () => finish(true));- sock.once('timeout', () => finish(false));- sock.once('error', () => finish(false));- try { sock.connect(port, host); } catch (e) { finish(false); }- });-}--// v1.8.26: pup opens URLs on the USER'S DESKTOP, so a `localhost` URL points at the-// DESKTOP's localhost — NOT the caller's cloud container. A cloud-container AI that-// serves something on its OWN localhost and asks pup to open it hits a dead port on the-// user's machine. Since pup runs ON the desktop, it can just check whether anything is-// listening there. Returns { isLocalhost, host, port, reachable }. reachable is null when-// the URL isn't localhost (nothing to warn about — e.g. a normal https site or a public-// container URL). Hydrogen Desktop + genuine desktop servers ARE reachable, so they never-// trip the warning; this only flags the container-localhost mistake.-async function localhostReachability(urlStr) {- let host, port;- try {- const u = new URL(urlStr);- host = u.hostname.replace(/^\[|\]$/g, '');- const LH = new Set(['localhost', '127.0.0.1', '0.0.0.0', '::1']);- const isLocalhost = LH.has(host.toLowerCase()) || /\.localhost$/i.test(host);- port = u.port ? Number(u.port) : (u.protocol === 'https:' ? 443 : 80);- if (!isLocalhost) return { isLocalhost: false, host, port, reachable: null };- } catch (e) { return { isLocalhost: false, reachable: null }; }- const probeHost = (host === '0.0.0.0' || host === '::1') ? '127.0.0.1' : host;- const reachable = await isPortListening(probeHost, port);- return { isLocalhost: true, host, port, reachable };-}--// The reprimand text attached when a localhost URL is NOT reachable from the desktop.-function localhostReprimandHint(urlStr, host, port) {- return `⚠ LOCALHOST ON THE WRONG MACHINE: you opened ${urlStr}, but pup opens the browser on the USER'S DESKTOP — so "localhost" is THEIR machine, and nothing is listening on ${host}:${port} there. If you're a cloud container, localhost:${port} is YOUR container (the desktop can't reach it), so this will just show a connection error. To actually surface this to the user: (1) if it's a FILE you generated, send it to the desktop (adom-desktop file transfer) and open it as file:///C:/path/... ; or (2) expose your server at your container's PUBLIC url and open THAT. If you genuinely started a server ON the desktop via AD and it's still booting, retry shortly. (Reachable localhost servers — e.g. Hydrogen Desktop — get NO warning; you're seeing this because the port is dead here.)`;-}+// NOTE: localhost-URL reprimand (container-vs-desktop) is handled by AD CORE, which+// appends a container-topology hint to a failed response whose URL is localhost. pup+// doesn't duplicate it — and because AD keys on the connection FAILURE, Hydrogen+// Desktop's working localhost URLs load fine and never trip it. (Enriching AD's hint+// with actionable alternatives is an adom/adom-desktop request.) // v1.8.23: REAL taskbar flash (FlashWindowEx) for a session's window — the orange // nudge that tells the user "the agent touched this window" WITHOUT stealing focus.@@ -3075,24 +3017,10 @@ const server = http.createServer(async (req, res) => { } else if (reuse && reuse.reused) { ownHint = ' Reused the existing window (same/no url — benign refresh).'; }- // v1.8.26: localhost sanity — pup opens on the DESKTOP, so a localhost URL the- // desktop can't reach is almost always the CALLER'S container localhost. Warn- // (don't block — it might be a desktop server that's still booting).- let lhHint = '', lhInfo = {};- try {- if (args.url) {- const lh = await localhostReachability(args.url);- if (lh.isLocalhost && lh.reachable === false) {- lhHint = ' ' + localhostReprimandHint(args.url, lh.host, lh.port);- lhInfo = { localhostUnreachable: true, probedHost: lh.host, probedPort: lh.port };- }- }- } catch (e) {} sendJSON(res, { success: true, output: JSON.stringify({ ok: true,- ...lhInfo, defaultPermissionsApplied: !args.strictPermissions, defaultPermissions: args.strictPermissions ? [] : PERMISSIVE_DEFAULT_PERMISSIONS.map(p => `${p.name}=${p.setting}`), downloadsEnabled: !args.strictPermissions,@@ -3108,7 +3036,7 @@ const server = http.createServer(async (req, res) => { // v1.8.16: reuse/ownership verdict (null = fresh window). reused: reuse ? { navigatedAway: reuse.navigatedAway, previousUrl: reuse.previousUrl, previousOwner: reuse.previousOwner, takeover: reuse.takeover } : null, owner: session.owner || null,- _hint: `${bgHint}${ownHint} ${brep._hint}${lhHint}`,+ _hint: `${bgHint}${ownHint} ${brep._hint}`, ...info, }), });