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
v2.0.545 (insiders): Proven on AdomLapper: 20 of 20 vendor sites as tabs in ONE extension-lane window on adom-you, cold, no duplicates; 19 of 20 load, analog.com now among them (the launch flag set was the tell: with only the firewall guards kept and everything else stock, analog serves its real page with the Akamai sensor running; avnet still serves an interactive Akamai challenge); the window survives a pup restart by re-adoption (same hwnd, 20 tabs); the lane presents the installed Chrome 152 on the wire and in JS; the identity button wears the user's avatar; ab-smoke ALL PASS on the default lane; parity 55/56 (pre-existing clearMethod drift in untouched code).
3 files changed
+57−3
src/extension/identity-shim.jsadded+1@@ -0,0 +1 @@+// generated at staging by pup (browser/src/lib.rs stage_extensions_lane): consumer identity values baked in
src/extension/manifest.json+14−2@@ -17,7 +17,9 @@ "scripting", "system.display", "downloads",- "cookies"+ "cookies",+ "declarativeNetRequest",+ "browsingData" ], "host_permissions": [ "<all_urls>",@@ -44,5 +46,15 @@ "48": "icon48.png", "128": "icon128.png" },- "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApt/KZkoLldvJGoP/jbNVtXRui/Zp/xeahObOFMzJnbU4U9/WCQhO0RiNnHYZ12GeiiI3FXy7duAUmQJBFZQhMA7tjrtMPswgR4xiYlxxL4WoVi9zJsWgDoeChnVxvFyYe7gZY6b6cCI3riP6jNnZ4s6jOVZ0YeGkVehJGjg9wJZ5Mp+1QnIH8TnTqeNdtCsW74ORu2D1FeiAM96xmbcXszkJbNvfiL4cdzxnBy2V/fWMwnN/9quc7U+zbblZ2N8nHiQiaK9XKgEB9Ia7I3A7ICCWUMIhhNDSAEY3H7YlEI4EB0RYFdOzXPGm5UWwHF+0AoBQGhNb3OtgiAu3X924MwIDAQAB"+ "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApt/KZkoLldvJGoP/jbNVtXRui/Zp/xeahObOFMzJnbU4U9/WCQhO0RiNnHYZ12GeiiI3FXy7duAUmQJBFZQhMA7tjrtMPswgR4xiYlxxL4WoVi9zJsWgDoeChnVxvFyYe7gZY6b6cCI3riP6jNnZ4s6jOVZ0YeGkVehJGjg9wJZ5Mp+1QnIH8TnTqeNdtCsW74ORu2D1FeiAM96xmbcXszkJbNvfiL4cdzxnBy2V/fWMwnN/9quc7U+zbblZ2N8nHiQiaK9XKgEB9Ia7I3A7ICCWUMIhhNDSAEY3H7YlEI4EB0RYFdOzXPGm5UWwHF+0AoBQGhNb3OtgiAu3X924MwIDAQAB",+ "web_accessible_resources": [+ {+ "resources": [+ "identity-shim.js"+ ],+ "matches": [+ "<all_urls>"+ ]+ }+ ] }
src/extension/worker.js+42−1@@ -161,7 +161,7 @@ async function pupExtPoll() { try { fetch(`http://127.0.0.1:${found.port}/exthello`, { method: 'POST', headers: { 'Content-Type': 'application/json' },- body: JSON.stringify({ lane: _pkgLane, pkg: _pkgPort, read: _pkgRead, worker: chrome.runtime.getURL('worker.js'), tries: pupExtDiag.tries }),+ body: JSON.stringify({ lane: _pkgLane, pkg: _pkgPort, read: _pkgRead, worker: (self.location && self.location.pathname) || 'worker.js', tries: pupExtDiag.tries }), signal: AbortSignal.timeout(1500), }).catch(() => {}); } catch (e) {}@@ -239,6 +239,47 @@ async function pupExtPoll() { const wins = await chrome.windows.getAll({}); for (const w of wins) { try { await chrome.windows.remove(w.id); } catch (e) {} } out = { id: cmd.id, ok: true, closed: wins.length };+ } else if (cmd.action === 'setBrands') {+ // Present the user's INSTALLED Chrome, on the wire and in JS. pup supplies the exact strings (it learned the+ // installed version and the GREASE brand real Chrome sends). Measured 2026-09-13: the user's Chrome is 152+ // and sends "Chromium";v="152", "Not?A_Brand";v="24", "Google Chrome";v="152"; Chrome for Testing 148 sent+ // "Not/A)Brand";v="99", "Chromium";v="148" and analog.com's edge denied it while real Chrome passed.+ const on = cmd.on !== false;+ const ua = cmd.ua || navigator.userAgent, chua = cmd.chua || '', chuaFull = cmd.chuaFull || '';+ const rules = !on ? [] : [{+ id: 7101, priority: 1,+ action: { type: 'modifyHeaders', requestHeaders: [+ { header: 'user-agent', operation: 'set', value: ua },+ ...(chua ? [{ header: 'sec-ch-ua', operation: 'set', value: chua }] : []),+ ...(chuaFull ? [{ header: 'sec-ch-ua-full-version-list', operation: 'set', value: chuaFull }] : []),+ ] },+ condition: { urlFilter: '*', resourceTypes: ['main_frame', 'sub_frame', 'xmlhttprequest', 'script', 'stylesheet', 'image', 'font', 'media', 'websocket', 'other'] },+ }];+ let applyErr = null;+ try { await chrome.declarativeNetRequest.updateDynamicRules({ removeRuleIds: [7101], addRules: rules }); } catch (e) { applyErr = String(e && e.message || e); }+ // the JS side: same values, MAIN world, before any page script runs+ try { await chrome.scripting.unregisterContentScripts({ ids: ['pup-consumer-identity'] }); } catch (e) {}+ if (on && cmd.brands) {+ const src = `(() => { const UA = ${JSON.stringify(ua)}; const B = ${JSON.stringify(cmd.brands)}; const F = ${JSON.stringify(cmd.fullVersionList || [])}; const FULL = ${JSON.stringify(cmd.fullVersion || '')};+ try { Object.defineProperty(navigator, 'userAgent', { get: () => UA, configurable: true }); Object.defineProperty(navigator, 'appVersion', { get: () => UA.replace(/^Mozilla\//, ''), configurable: true }); } catch (e) {}+ try { const uad = navigator.userAgentData; if (uad) { const hev = uad.getHighEntropyValues.bind(uad);+ Object.defineProperty(uad, 'brands', { get: () => B.map(x => ({ ...x })), configurable: true });+ uad.getHighEntropyValues = (hints) => hev(hints).then(v => Object.assign(v, { brands: B.map(x => ({ ...x })), fullVersionList: F.map(x => ({ ...x })), uaFullVersion: FULL || v.uaFullVersion }));+ uad.toJSON = () => ({ brands: B.map(x => ({ ...x })), mobile: uad.mobile, platform: uad.platform }); } } catch (e) {} })();`;+ // registerContentScripts needs a file; write the source as a blob-backed file is not possible, so use a+ // tiny loader file that reads the values from storage set here.+ await chrome.storage.local.set({ pupConsumerIdentity: { ua, brands: cmd.brands, fullVersionList: cmd.fullVersionList || [], fullVersion: cmd.fullVersion || '' } });+ try { await chrome.scripting.registerContentScripts([{ id: 'pup-consumer-identity', js: ['identity-shim.js'], matches: ['<all_urls>'], runAt: 'document_start', world: 'MAIN', allFrames: true }]); } catch (e) { applyErr = applyErr || String(e && e.message || e); }+ }+ const live = await chrome.declarativeNetRequest.getDynamicRules();+ out = { id: cmd.id, ok: !applyErr, error: applyErr || undefined, ua: on ? ua : null, brands: on ? chua : null, rulesLive: live.length };+ } else if (cmd.action === 'clearSiteData') {+ // Everything the jar keeps for an origin that could reach a server on the next request or shape the page:+ // cache, cookies, local/session storage, indexedDB, service workers, cache storage. (HSTS and TLS session+ // state are not per-origin clearable through this API.)+ const origins = (cmd.origins || []).filter(Boolean);+ await chrome.browsingData.remove({ origins, since: 0 }, { cache: true, cacheStorage: true, cookies: true, indexedDB: true, localStorage: true, serviceWorkers: true, webSQL: true, fileSystems: true });+ out = { id: cmd.id, ok: true, origins }; } else if (cmd.action === 'clearCookies') { // Remove every cookie whose domain ends with cmd.domain (HttpOnly included). Used to drop a bot // manager's poisoned verdict cookie (Akamai _abck at ~-1~) so a site gets a clean look at this browser.