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
Annotate captures go flicker-free: CDP captureScreenshot compositor-swaps the surface and visibly blips on a foreground hardware-accelerated window, which pup never noticed because its usual screenshots target background windows; annotate is the first feature shooting the window the user is watching. Captures now grab a single screencast frame on the recorder's rail, which has no visual artifact, DPR-scale-cropped server-side with sharp, with captureScreenshot as the logged fallback
4 files changed
+43−10
adom-bridge-puppeteer-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "puppeteer",- "version": "2.0.82",- "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.82/adom-bridge-puppeteer-v2.0.82.zip",- "sha256": "f45c8a3b45bde62132c7bb1f47c3e3619cf9763f6a993eb4febd4b7ec2827a6f",- "size": 834715,+ "version": "2.0.83",+ "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.83/adom-bridge-puppeteer-v2.0.83.zip",+ "sha256": "acfc2fc8fba8509bdd5f81540fbe173b240418dd2338f1cd2e65d833a855dc34",+ "size": 835279, "verbPrefixes": [ "browser_", "desktop_recorder_",
src/BRIDGE_VERSION+1−1@@ -1 +1 @@-2.0.82+2.0.83
src/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "puppeteer", "displayName": "Puppeteer (Chrome for Testing + Native)",- "version": "2.0.82",+ "version": "2.0.83", "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. pup_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. pup_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, pup_alert_window posts a native toast whose click focuses the window, pup_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-bridge", "author": "Adom Inc.",
src/server.js+37−4@@ -5773,16 +5773,49 @@ async function startAnnotation(session, sessionId, callerThread, opts) { // v2.0.72 CAPTURE PIPELINE, reordered for perceived speed (John: the blip, the slow toast): // screenshot first, then IMMEDIATELY the client-side camera flash + toast with the preview, and // only afterwards the slow legs (caption bar, iTXt, clipboard, disk) run in the background.+// v2.0.83: FLICKER-FREE capture. CDP captureScreenshot compositor-swaps the surface, which+// visibly blips on a FOREGROUND hardware-accelerated window (pup's usual background shots never+// showed it; annotate shoots the window the user is watching). A single screencast frame (the+// recorder's rail) has no visual artifact; crop server-side with sharp, DPR-scaled.+async function flickerFreeShot(pg, clipRect) {+ const cdp = await pg.target().createCDPSession();+ try {+ const frameP = new Promise((resolve, reject) => {+ const to = setTimeout(() => reject(new Error('screencast frame timeout (6s)')), 6000);+ cdp.once('Page.screencastFrame', ev => { clearTimeout(to); resolve(ev); });+ });+ await cdp.send('Page.startScreencast', { format: 'png', everyNthFrame: 1 });+ const ev = await frameP;+ cdp.send('Page.screencastFrameAck', { sessionId: ev.sessionId }).catch(() => {});+ await cdp.send('Page.stopScreencast').catch(() => {});+ let buf = Buffer.from(ev.data, 'base64');+ if (clipRect) {+ const sharp = require('sharp');+ const meta = await sharp(buf).metadata();+ const cssW = (ev.metadata && ev.metadata.deviceWidth) || null;+ const scale = cssW ? meta.width / cssW : 1;+ const L = Math.max(0, Math.round(clipRect.x * scale)), T = Math.max(0, Math.round(clipRect.y * scale));+ const W = Math.min(meta.width - L, Math.round(clipRect.w * scale)), H = Math.min(meta.height - T, Math.round(clipRect.h * scale));+ if (W > 8 && H > 8) buf = await sharp(buf).extract({ left: L, top: T, width: W, height: H }).png().toBuffer();+ }+ return buf;+ } finally { try { await cdp.detach(); } catch (e) {} }+} async function processAnnotateCapture(session, sessionId, pg, res) { console.log(`[annotate] "${sessionId}" capture: screenshotting`); const ts = new Date().toISOString().replace(/[:.]/g, '-'); const isSnip = res.snip && res.snip.w > 8 && res.snip.h > 8; const clipRect = isSnip ? res.snip : res.bbox; const fp = path.join(SHOTS_DIR, `annotate-${isSnip ? 'snip-' : ''}${sessionId}-${ts}.png`);- let buf = await Promise.race([- pg.screenshot(clipRect ? { clip: { x: clipRect.x, y: clipRect.y, width: clipRect.w, height: clipRect.h }, captureBeyondViewport: false } : { captureBeyondViewport: false }),- new Promise((_, rej) => setTimeout(() => rej(new Error('screenshot timed out (10s)')), 10000)),- ]);+ let buf;+ try { buf = await flickerFreeShot(pg, clipRect); }+ catch (e) {+ console.log(`[annotate] "${sessionId}" screencast shot failed (${e.message}) — falling back to captureScreenshot`);+ buf = await Promise.race([+ pg.screenshot(clipRect ? { clip: { x: clipRect.x, y: clipRect.y, width: clipRect.w, height: clipRect.h }, captureBeyondViewport: false } : { captureBeyondViewport: false }),+ new Promise((_, rej) => setTimeout(() => rej(new Error('screenshot timed out (10s)')), 10000)),+ ]);+ } console.log(`[annotate] "${sessionId}" capture: screenshot ok (${buf.length}B)`); // instant feedback: flash + toast (preview resize is fast, everything else waits) let preview = null;