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
Two fixes from the full-menu ralph and John's approval. One: annotate and snip injected their overlay into pup's bookkeeping of the active tab while the user was looking at a different tab, an invisible overlay and the true reason the snippet menu never worked for John; the resolved clicking tab now wins, then Chrome's actually-visible tab, then the old chain. Two: URL-based category prediction at open, wiki being John's 60 percent case: while pages still load, the intended URL classifies the window and an unchecked wiki page predicts signed-in, so the very first AUMID stamp is already the categorized id and the visible double-stamp collapses to one for the common case, with the rare wrong guess re-stamping once
4 files changed
+21−10
adom-bridge-puppeteer-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "puppeteer",- "version": "2.0.60",- "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.60/adom-bridge-puppeteer-v2.0.60.zip",- "sha256": "04491f0b3543e2fda75cd3b57585158fe5e7c01127455b7ade39c6fc558dff76",- "size": 828381,+ "version": "2.0.61",+ "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.61/adom-bridge-puppeteer-v2.0.61.zip",+ "sha256": "8b7e5cc437ced85774317d3ecc25134626481f7f8d4db1a53e5c8def5e2f1398",+ "size": 828890, "verbPrefixes": [ "browser_", "desktop_recorder_",
src/BRIDGE_VERSION+1−1@@ -1 +1 @@-2.0.60+2.0.61
src/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "puppeteer", "displayName": "Puppeteer (Chrome for Testing + Native)",- "version": "2.0.60",+ "version": "2.0.61", "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+15−4@@ -3237,10 +3237,15 @@ function pupCategory(sessionId) { try { k = classifyUrl(t.page && t.page.url()); } catch (e) {} if (k) kinds.add(k); }+ // v2.0.61 (John: predict from the URL at open — wiki is the 60% case): while pages are+ // still loading (about:blank), classify the INTENDED url so the FIRST stamp is already the+ // categorized id; and an unchecked wiki page predicts signed-in (auto-login lands there in+ // the common case). A wrong guess re-stamps once when the check resolves — the rare case.+ if (!kinds.size) { const kp = classifyUrl(s._intendedUrl); if (kp) kinds.add(kp); } if (!kinds.size) return null; if (kinds.size > 1) return 'mixed'; const only = [...kinds][0];- if (only === 'wiki' && s._wikiAuthed) return 'wiki-in';+ if (only === 'wiki' && (s._wikiAuthed || s._wikiAuthed === undefined)) return 'wiki-in'; return only; } catch (e) { return null; } }@@ -5643,7 +5648,12 @@ function unpackedExtensionId(dir) { return [...h].map(c => String.fromCharCode('a'.charCodeAt(0) + parseInt(c, 16))).join(''); } async function startAnnotation(session, sessionId, callerThread, opts) {- const tab = (session.activeTabId && (session.tabs || []).find(t => t.tabId === session.activeTabId)) || (session.tabs || [])[0];+ // v2.0.61 (menu ralph: annotate/snip injected into pup's belief of the active tab while the+ // user was LOOKING at another — invisible overlay, "snip never worked"): the resolved tab (the+ // one the user clicked from) wins; then Chrome's actually-visible tab; then the old chain.+ let tab = (opts && opts.tabId) ? (session.tabs || []).find(t => t.tabId === opts.tabId) : null;+ if (!tab) { for (const t5 of (session.tabs || [])) { try { const vis = await Promise.race([t5.page.evaluate(() => document.visibilityState), new Promise(r => setTimeout(() => r(''), 900))]); if (vis === 'visible') { tab = t5; break; } } catch (e) {} } }+ if (!tab) tab = (session.activeTabId && (session.tabs || []).find(t => t.tabId === session.activeTabId)) || (session.tabs || [])[0]; const pg = tab && tab.page; if (!pg) throw new Error('no page to annotate'); const tool = (opts && opts.tool) || 'pen';@@ -10853,7 +10863,7 @@ const server = http.createServer(async (req, res) => { outer: for (const [k3, s3] of sessions) { for (const t3 of (s3.tabs || [])) { let u3 = null; try { u3 = t3.page.url(); } catch (e) {}- if (u3 && u3.split('#')[0] === tu) { args.sessionId = k3; break outer; }+ if (u3 && u3.split('#')[0] === tu) { args.sessionId = k3; args._resolvedTabId = t3.tabId; break outer; } } } }@@ -11381,6 +11391,7 @@ const server = http.createServer(async (req, res) => { session._lastContainer = _callerContainer; if (_callerReason) session._openReason = String(_callerReason).slice(0, 300); try { if (_callerContainer) session._callerContainer = _callerContainer; } catch (e) {}+ try { if (args && args.url && !session._intendedUrl) session._intendedUrl = String(args.url); } catch (e) {} console.log(`[caller] "${sessionId}" owned by AI thread "${_callerThread}"${_callerContainer ? ` (${_callerContainer})` : ''}${_callerReason ? ` — reason: ${String(_callerReason).slice(0, 90)}` : ''}`); // v1.9.103: put the thread FIRST in the window title, which is the text Windows draws above // the taskbar hover-preview. Kept SHORT (18 chars) on purpose: that header truncates around@@ -14085,7 +14096,7 @@ const server = http.createServer(async (req, res) => { sendJSON(res, { success: true, output: JSON.stringify({ ok: true, cancelled: true }) }); return; }- await startAnnotation(session, sessionId, _callerThread, { tool: args.tool === 'snip' ? 'snip' : 'pen' });+ await startAnnotation(session, sessionId, _callerThread, { tool: args.tool === 'snip' ? 'snip' : 'pen', tabId: args._resolvedTabId || null }); // the human is about to draw — the window must be in front of them. A jump-list click IS // the user asking, so this raise is user-sanctioned by construction. session._foreground = true; session._userActiveAt = Date.now();