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
The owner-in-tab-title experiment is removed outright. John: stop mangling the title of the page. The page title belongs to the page; attach and the 10s sweep now inject only the cleanup build that strips any prefix an elder keeper still applies and kills its interval, the settings row is retired, and the doctrine is written down in the new dev-skills pup-page-integrity skill so it never comes back. Ownership signals stay on pup-owned surfaces only: taskbar tooltip, dashboard, extension hover, identity tile. Also replaces the dashboard header close-all icon, a bare X that read as close-this-dashboard, with a stacked-windows-with-x glyph and a tooltip spelling out that it closes every pup window, not the dashboard
4 files changed
+26−38
adom-bridge-puppeteer-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "puppeteer",- "version": "2.0.101",- "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.101/adom-bridge-puppeteer-v2.0.101.zip",- "sha256": "af822745ebade741d228672d640775d642d66bb1464d95c48b112545e53f73b9",- "size": 853514,+ "version": "2.0.102",+ "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.102/adom-bridge-puppeteer-v2.0.102.zip",+ "sha256": "bf967978972c757fa559bf2638f2dc59b12810df969ee331ddd8025fa7913dc6",+ "size": 853296, "verbPrefixes": [ "browser_", "desktop_recorder_",
src/BRIDGE_VERSION+1−1@@ -1 +1 @@-2.0.101+2.0.102
src/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "puppeteer", "displayName": "Puppeteer (Chrome for Testing + Native)",- "version": "2.0.101",+ "version": "2.0.102", "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+20−32@@ -702,11 +702,11 @@ function attachTab(session, sessionId, page, { makeActive = true, opener = 'user // hotkey on/off), and injecting the OFF build is what tears an elder pencil out of the page. const fj = floatyJs(); try { page.evaluateOnNewDocument(fj).catch(() => {}); page.evaluate(fj).catch(() => {}); } catch (e) {}- const _titleOwner = (session && (session.owner || session._ownerThread)) || null;- if (pupSettings().tbOwnerBadge !== 'off' && _titleOwner) {- const tj = titleJs(_titleOwner);- try { page.evaluateOnNewDocument(tj).catch(() => {}); page.evaluate(tj).catch(() => {}); } catch (e) {}- }+ // v2.0.102: cleanup-only — strip any title prefix an elder bridge's injected keeper left+ // (their evaluateOnNewDocument scripts persist on long-lived pages and run first; this+ // registers after them, so the strip wins).+ const tjClean = titleJs('');+ try { page.evaluateOnNewDocument(tjClean).catch(() => {}); page.evaluate(tjClean).catch(() => {}); } catch (e) {} } try { page.evaluateOnNewDocument(PUP_DEEP_HELPERS).catch(() => {}); } catch (e) {} // v1.9.48: re-evaluate the window CATEGORY when this tab finishes navigating.@@ -5767,12 +5767,11 @@ const FLOATY_JS = `(() => { } }, true); })()`;-// v2.0.95 (John: the owner-initials badge overlay on the toolbar icon is too disruptive — show-// the ai-thread name DIRECTLY visible). Chrome gives an extension exactly one 16px action icon-// plus a ~4-char colored overlay badge; no text labels, no wider buttons, no custom toolbar UI.-// So the badge is retired and the owning thread rides the TAB TITLE instead: full thread name,-// always visible, native text in the browser top bar. Idempotent exact-suffix keeper, re-applied-// when the page rewrites its own title; versioned + owner-keyed guard like the floaty rail.+// v2.0.102: the owner-in-tab-title experiment (2.0.96-2.0.101) is DEAD — John: "stop mangling+// the title of the page. i can't fucking stand when you do that." The page's title belongs to+// the page. See dev-skills/pup-page-integrity. TITLE_JS survives ONLY as the cleanup rail: the+// empty-owner build strips any prefix a prior version left and kills its keeper interval; the+// owner build must never be injected again. const TITLE_JS = `(function () { if (window !== window.top) return; if (window.__pupTitleV === '${BRIDGE_VERSION}:__OWNER__') return;@@ -10230,7 +10229,7 @@ footer{color:#5c6c7a;font-size:11px;padding:8px 18px 22px} <button class="ibtn" data-act="regen-overlays" data-tip="Regenerate ALL overlay icons: re-scan every open window and tab and re-run the from-scratch overlay algorithm (re-resolve each window handle, re-pick the favicon, recompose + repaint the taskbar badge) and refresh these dashboard icons to match. A clean sweep to verify correctness and surface any drift." title="regenerate all overlay icons (clean sweep)" aria-label="regenerate overlays"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M23 4v6h-6"/><path d="M1 20v-6h6"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg></button> <button class="ibtn" data-act="flash-all" title="flash every pup taskbar button (the Windows taskbar flash, accent-colored on your theme)" aria-label="flash all"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0"/></svg></button> <button class="ibtn" data-act="unflash-all" title="clear every taskbar flash" aria-label="clear taskbar flashes"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0"/><line x1="3" y1="3" x2="21" y2="21"/></svg></button>- <button class="ibtn danger" data-act="close-all" data-confirm="Close ALL pup windows?" title="close every pup window" aria-label="close all windows"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>+ <button class="ibtn danger" data-act="close-all" data-confirm="Close ALL pup windows?" data-tip="Close ALL pup windows on this desktop (every window in the list below, not this dashboard page). Asks before doing it." aria-label="close all pup windows"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><rect x="8" y="8" width="13" height="11" rx="1.6"/><path d="M4 15V6a1.5 1.5 0 0 1 1.5-1.5H16"/><path d="M12.2 11.2l4.6 4.6M16.8 11.2l-4.6 4.6"/></svg></button> </div> </header> <div id="bar">@@ -10434,7 +10433,6 @@ const SETTINGS_DEF=[ {k:'jlAnnotate',l:'',d:'The Annotate this page task: draw on the page; Done captures it for the AI thread that owns the window.',dep:'jumpLists',subtgl:1}, {k:'annCaptionBar',l:'Caption bar on captures',d:'Bake a thin context strip under every annotation/snip capture (thread, page, time) so any AI you paste it into sees where it came from.'}, {k:'toolbarExtension',l:'Adom toolbar in Chrome',d:'The Adom Pup extension button in the browser toolbar (annotate, snip, view toggle, dashboard, owner, close). Requires the one-time extension install.'},- {k:'tbOwnerBadge',l:'',d:'Appends the owning AI thread to each tab title so ownership is directly visible. Replaces the old badge overlay on the toolbar icon.',dep:'toolbarExtension',subtgl:1}, {k:'tbSnipHotkey',l:'',d:'Alt+Shift+A starts annotate (red pen) on the current pup window.',dep:'toolbarExtension',subtgl:1}, {k:'tbFloatyPencil',l:'',d:'The tiny pencil in the upper-right corner of each page: one click starts annotate. Off by default: the corner hover zone can fight buttons in app headers, and the toolbar pen button covers discovery.',dep:'toolbarExtension',subtgl:1}, {k:'floatyHitPx',l:'',d:'Pencil corner hit area (px): how big the invisible top-right target is that reveals the pencil. Applies to open pages immediately.',slider:[6,120,2],dep:'tbFloatyPencil'},@@ -10787,11 +10785,7 @@ const server = http.createServer(async (req, res) => { if (key === 'floatyHitPx' || key === 'tbFloatyPencil' || key === 'tbSnipHotkey') { (async () => { try { const fj = floatyJs(); for (const [, s6] of sessions) for (const t6 of (s6.tabs || [])) { try { await Promise.race([t6.page.evaluate(fj), new Promise(r => setTimeout(r, 1200))]); } catch (e) {} } } catch (e) {} })(); }- // v2.0.95: the owner-in-tab-title keeper live-applies both ways (ON stamps every owned- // window now; OFF strips the suffix via the empty-owner build).- if (key === 'tbOwnerBadge') {- (async () => { try { for (const [, s7] of sessions) { const ow = (val === 'on' && (s7.owner || s7._ownerThread)) || ''; const tj = titleJs(ow); for (const t7 of (s7.tabs || [])) { try { await Promise.race([t7.page.evaluate(tj), new Promise(r => setTimeout(r, 1200))]); } catch (e) {} } } } catch (e) {} })();- } // v1.9.320: OFF clears the menu, ON/sub re-commits+ // v1.9.320: OFF clears the menu, ON/sub re-commits if (key === 'ownerChip') applyOwnerChipSetting(val === 'on').catch(() => {}); return done({ ok: true, settings: pupSettings() }); }@@ -15490,21 +15484,15 @@ setInterval(async () => { } } } catch (e) {}- // v2.0.98: owner-in-tab-title re-assert. Recovery adopts tabs BEFORE session files restore- // ownership, so the attach-time injection can miss on re-adopted windows (John saw a bare- // title after a bridge restart). The versioned+owner guard makes this an idempotent no-op- // once stamped, so sweeping it is cheap.+ // v2.0.102: title CLEANUP sweep (the 2.0.98 owner re-assert inverted). Strips the prefix any+ // elder keeper still applies on pages the attach-time cleanup missed. Idempotent via guard. try {- if (pupSettings().tbOwnerBadge !== 'off') {- for (const [, sT] of [...sessions]) {- if (sT._lostBrowser) continue;- const owT = sT.owner || sT._ownerThread;- if (!owT) continue;- const tjT = titleJs(owT);- for (const tT of (sT.tabs || [])) {- if (!tT || !tT.page) continue;- try { await Promise.race([tT.page.evaluate(tjT), new Promise(r => setTimeout(r, 1500))]); } catch (e) {}- }+ const tjClean2 = titleJs('');+ for (const [, sT] of [...sessions]) {+ if (sT._lostBrowser) continue;+ for (const tT of (sT.tabs || [])) {+ if (!tT || !tT.page) continue;+ try { await Promise.race([tT.page.evaluate(tjClean2), new Promise(r => setTimeout(r, 1500))]); } catch (e) {} } } } catch (e) {}