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
Assisted-foreground-click protocol — John's respect requirements after I disrupted his work by foregrounding while he was mid-task and not ready. New browser_assisted_click verb for the rare surface pup's CDP click can't drive (native dialog, cross-origin FedCM chooser): (1) captions the AI's reasoning via desktop_caption BEFORE touching the screen, (2) toasts, (3) foregrounds, (4) THE RESPECT LOOP — waitForUserReady polls osGetForegroundWindow vs the session's HWND and BLOCKS until the user's foreground actually IS this window and stays stable ~1.2s, captioning 'Adom is waiting — click back when ready, nothing happens until you do' if they alt-tabbed away; times out gracefully with user_not_ready and clicks NOTHING, (5) only then a TIGHT real-cursor hover->click (fresh capture, hover, 900ms for genuine motion, click — the exact sequence FedCM accepts and that failed when ops were interleaved), (6) captions each step through completion. Also generalized pupCaption + sessionHwnd helpers. This is the protocol so pup never again foregrounds and acts while the user isn't ready.
4 files changed
+79−6
adom-bridge-puppeteer-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "puppeteer",- "version": "1.9.166",- "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.166/adom-bridge-puppeteer-v1.9.166.zip",- "sha256": "dbf05b6cb32039229d7859d0e7ac128e56320d11f1058da6ee6a25761944937a",- "size": 404995,+ "version": "1.9.167",+ "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.167/adom-bridge-puppeteer-v1.9.167.zip",+ "sha256": "9d6d6ef74af97f73c70c1f0feedca7f12e4b4ecdbb77ffeccdac1437ab8a4ae4",+ "size": 406697, "verbPrefixes": [ "browser_", "desktop_recorder_",
src/BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.166+1.9.167
src/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "puppeteer", "displayName": "Puppeteer (Chrome for Testing + Native)",- "version": "1.9.166",+ "version": "1.9.167", "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. pup is a GENERIC, non-profile browser only \u2014 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 \u2014 a separate bridge, not pup's job. macOS (v1.9.74+): full parity with mac-native mechanics \u2014 background-by-default is the app-level hide (windows stay drivable/screenshottable while hidden), raise/lower map to unhide+activate/hide, browser_alert_window posts a native toast whose click focuses the window, browser_use {browser:'chrome',install:true} installs Chrome from the official dmg, and taskbar identity/badging remains Windows-only.", "homepage": "https://wiki.adom.inc/adom/adom-desktop", "author": "Adom Inc.",
src/server.js+73@@ -1579,6 +1579,37 @@ async function showPupCursor(page, x, y, { click = true } = {}) { }, Math.round(x), Math.round(y), !!click); } catch (e) {} }+// v1.9.167 (John's assisted-foreground protocol) — a general OS caption (what/why narration).+async function pupCaption(text, expiresInMs = 8000, id = 'pup-assist') {+ try { await chrome.adCommand('desktop_caption', { text, expiresInMs, id, position: 'bottom', size: 'medium' }, { timeoutMs: 4000 }); } catch (e) {}+}+async function sessionHwnd(sessionId) {+ try {+ const fw = adPayload(await chrome.adCommand('desktop_find_window', { titleContains: `(session: ${sessionId}` }, { timeoutMs: 5000, caller: sessionCaller(sessions.get(sessionId)) }));+ const b = fw && (fw.best || (fw.data && fw.data.best));+ return b && (b.hwnd != null ? Number(b.hwnd) : null);+ } catch (e) { return null; }+}+// The RESPECT LOOP (John: 'detect whether they alt+tabbed away ... wait until i bring the window+// back'). Blocks until the user's foreground IS this pup window and stays so briefly, captioning+// that we are waiting so we never act while they look elsewhere. true when ready, false on timeout.+async function waitForUserReady(sessionId, hwnd, { maxMs = 120000 } = {}) {+ if (process.platform !== 'win32' || !hwnd) return true;+ const start = Date.now();+ let stableSince = 0, lastCap = 0;+ while (Date.now() - start < maxMs) {+ const fg = osGetForegroundWindow();+ if (fg === hwnd) {+ if (!stableSince) stableSince = Date.now();+ if (Date.now() - stableSince >= 1200) return true;+ } else {+ stableSince = 0;+ if (Date.now() - lastCap > 3500) { lastCap = Date.now(); pupCaption('Adom is waiting — click back into THIS window when you are ready, and I will continue. (Nothing happens until you do.)', 6000).catch(() => {}); }+ }+ await new Promise(r => setTimeout(r, 400));+ }+ return false;+} function setupErrorCollection(pg, errorsArr) { // v1.9.163 (crash fix): bounded buffer + NO requestfailed listener. The requestfailed listener // enabled the CDP Network domain, whose per-request event stream is a firehose on request-heavy@@ -8495,6 +8526,48 @@ const server = http.createServer(async (req, res) => { } return; }+ // v1.9.167 — ASSISTED FOREGROUND CLICK (John's full protocol) for surfaces pup's CDP click+ // can't drive (native dialog, cross-origin FedCM chooser). Caption the reasoning, toast,+ // foreground, WAIT until the user is actually on the window (alt-tab aware), then a TIGHT+ // real-cursor hover->click, captioning each step. x,y = WINDOW-image pixels from a+ // desktop_screenshot_window you took to find the target. reason=why, label=what.+ case 'browser_assisted_click': {+ let session, resolvedId;+ try { ({ session, resolvedId } = await recoverOrRelaunchSession(args)); }+ catch (e) { sendSessionResolveError(res, e); return; }+ if (!(Number.isFinite(Number(args.x)) && Number.isFinite(Number(args.y)))) {+ sendJSON(res, { success: false, error: 'browser_assisted_click needs window-image x+y (from a desktop_screenshot_window of this session) plus reason+label.' }); return;+ }+ if (process.platform !== 'win32') { sendJSON(res, { success: false, error: 'assisted click is Windows-only (uses the OS cursor + foreground detection).' }); return; }+ const _reason = String(args.reason || 'Adom needs to click something on your screen.').slice(0, 180);+ const _label = String(args.label || 'the highlighted control').slice(0, 80);+ try {+ await pupCaption(`Adom: ${_reason} I will bring this window up and WAIT until you are ready before clicking ${_label}.`, 9000);+ try { await chrome.adCommand('notify_user', { title: 'Adom needs one click', body: `${_label} — ${_reason} I will wait until you are on the window.`, focus: false }, { timeoutMs: 4000 }); } catch (e) {}+ session._foreground = true; session._lastForeground = { ts: Date.now(), verb: command, reason: _reason };+ try { await osRaiseSessionWindow(session, resolvedId); } catch (e) {}+ const hwnd = await sessionHwnd(resolvedId);+ const ready = await waitForUserReady(resolvedId, hwnd, { maxMs: Number(args.maxWaitMs) || 120000 });+ if (!ready) {+ await pupCaption('Adom stopped waiting — you did not return to the window. Ask me to try again when ready.', 7000);+ sendJSON(res, { success: false, errorCode: 'user_not_ready', error: 'Timed out waiting for you to focus the window. Nothing was clicked.', _hint: 'The user alt-tabbed away and did not return within the wait window. Re-issue browser_assisted_click when they are ready.' });+ return;+ }+ await pupCaption(`Adom is clicking ${_label} now.`, 5000);+ const shot = adPayload(await chrome.adCommand('desktop_screenshot_window', { titleContains: `(session: ${resolvedId}` }, { timeoutMs: 8000, caller: sessionCaller(session) }));+ const shotId = shot && (shot.shotId || (shot.screenshots && shot.screenshots[0] && shot.screenshots[0].shotId));+ if (!shotId) { sendJSON(res, { success: false, error: 'could not capture the window to aim the click' }); return; }+ const X = Math.round(Number(args.x)), Y = Math.round(Number(args.y));+ try { await chrome.adCommand('desktop_hover', { space: 'image', shotId, x: X, y: Y }, { timeoutMs: 6000, caller: sessionCaller(session) }); } catch (e) {}+ await new Promise(r => setTimeout(r, 900));+ try { await chrome.adCommand('desktop_click', { space: 'image', shotId, x: X, y: Y }, { timeoutMs: 6000, caller: sessionCaller(session) }); } catch (e) {}+ await pupCaption(`Adom clicked ${_label}. Continuing.`, 5000);+ sendJSON(res, { success: true, output: JSON.stringify({ ok: true, clicked: _label, x: X, y: Y, sessionId: resolvedId }), _hint: `Assisted click done after the user was confirmed on the window. Verify the effect with a screenshot before assuming it advanced.` });+ } catch (e) {+ sendJSON(res, { success: false, error: `assisted click failed: ${e.message}` });+ }+ return;+ } case 'browser_type': { if (typeof args.text !== 'string') { sendJSON(res, { success: false, error: 'browser_type needs "text" (string). Optional "selector" focuses the element first. Usage: { "sessionId": "...", "selector": "#q", "text": "hello" }' });