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.
master
John Lauer
v2.0.634 (insiders): background windows proven: default ext lane gets --no-startup-window + --keep-alive-for-test (was…
3fcc8b8
1mo ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
// pup login capture on the EXTENSION lane (2.0.602).
//
// John, 2026-09-17, after typing his Google password into a pup window and finding pup had not kept it:
// "are you fucking kidding me? i did type in my fucking password ... the whole point of pup is that it
// makes my life easier and i stop having to type in damn passwords".
//
// The capture rail existed ONLY on the CDP lane (Page.addScriptToEvaluateOnNewDocument plus the
// __pupCredCapture binding, installed in lib.rs tab-setup and recovery). The extension lane has no CDP
// page instrumentation, and it is the DEFAULT lane, so every login typed in a normal pup window was
// thrown away. This is the same detection logic as rust/verbs/assets/capture-login.js, delivering through
// the service worker instead of a DevTools binding.
//
// Runs in the ISOLATED world (chrome.runtime is needed to reach the worker); it still reads the page's
// DOM, so input.value is available. The MAIN-world HTMLFormElement.prototype.submit hook from the CDP
// copy cannot be installed from here, so a purely programmatic form.submit() is covered by the blur,
// change, keydown and pagehide nets instead.
(() => {
if (window !== window.top) return;
if (window.__pupExtCapV === '1') return;
window.__pupExtCapV = '1';
const vis = (el) => !!el && !el.disabled && el.offsetParent !== null && el.getClientRects().length > 0;
const isPwd = (el) => el && el.tagName === 'INPUT' && (el.type === 'password' || el.autocomplete === 'current-password' || el.autocomplete === 'new-password' || /(^|[^a-z])password([^a-z]|$)/i.test(el.name || '') || /(^|[^a-z])password([^a-z]|$)/i.test(el.id || ''));
let last = '';
function grab(form) {
try {
const scope = form || document;
const pwd = [...scope.querySelectorAll('input')].filter(vis).filter(isPwd)[0];
if (!pwd || !pwd.value) return;
const all = [...scope.querySelectorAll('input')];
const pi = all.indexOf(pwd);
let user = [...scope.querySelectorAll('input[autocomplete=username], input[type=email]')].filter(vis).filter((e) => !isPwd(e))[0];
if (!user) {
const cands = [...scope.querySelectorAll('input[type=text], input[type=tel], input:not([type])')].filter(vis).filter((e) => !isPwd(e));
user = cands.slice().reverse().filter((c) => all.indexOf(c) < pi)[0] || cands[0] || null;
}
// Google asks for the email on one page and the password on the next, so the password page has no
// username field at all. Take the address Google prints on it (measured on the real sign-in page).
let u = user ? user.value : '';
if (!u && /(^|\.)google\.com$/.test(location.hostname)) {
const m = (document.body.innerText || '').match(/[\w.+-]+@[\w.-]+\.\w+/);
if (m) u = m[0];
}
const rec = { host: location.hostname, u, p: pwd.value, ts: Date.now() };
const key = rec.host + '|' + rec.u + '|' + rec.p;
if (key === last) return; // blur then submit then pagehide all fire for one login
last = key;
chrome.runtime.sendMessage({ t: 'pup-cred', rec }, () => void chrome.runtime.lastError);
} catch (e) {}
}
const formHasPwd = (f) => { try { return !!f && [...f.querySelectorAll('input')].some(isPwd); } catch (e) { return false; } };
addEventListener('submit', (e) => grab(e.target && e.target.tagName === 'FORM' ? e.target : null), true);
addEventListener('click', (e) => {
try {
const t = e.target && e.target.closest && e.target.closest('button, input[type=submit], [role=button], a');
if (!t) return;
const f = t.form || (t.closest && t.closest('form'));
if (formHasPwd(f)) grab(f); else if (!f) grab(null);
} catch (e2) {}
}, true);
addEventListener('change', (e) => { try { if (isPwd(e.target) && e.target.value) grab(e.target.form || null); } catch (e2) {} }, true);
addEventListener('blur', (e) => { try { if (isPwd(e.target) && e.target.value) grab(e.target.form || null); } catch (e2) {} }, true);
addEventListener('keydown', (e) => { try { if (e.key === 'Enter' && isPwd(document.activeElement) && document.activeElement.value) grab(document.activeElement.form || null); } catch (e2) {} }, true);
addEventListener('pagehide', () => { try { grab(null); } catch (e2) {} }, true);
})();