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
Follow-on: auto-fill a vault-matched login form the instant the page renders, in the background. New autoLoginOnLoad runs after every render verb (open_window/open_tab/navigate/reload). Conservative by design: fires only when the vault holds a credential for that exact origin (so it is a per-host opt-in the user set deliberately), only on real http(s) pages, FILL ONLY never auto-submit (auto-submitting on load risks MFA prompts, lockouts, and wrong-form logins), never throws so it can't block or fail a navigation, is reported to the caller as _autoLogin (added to the output merge list so it survives AD unwrapping on success), and is silenceable with browser_configure {autoLogin:'off'}. This is the automatic counterpart to the explicit browser_login verb from 1.9.124: sign in once, store the credential, and thereafter pup pre-fills the login form on load with no Chrome dropdown and no foregrounding. Patch applied across two indentation variants of the tab.page probe chokepoint (anchored on newlines to avoid substring overlap).
4 files changed
+44−7
adom-bridge-puppeteer-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "puppeteer",- "version": "1.9.124",- "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.124/adom-bridge-puppeteer-v1.9.124.zip",- "sha256": "2c08904d9ff5185c29c3dcee8f1838e69e6714449ac1c9659dcb2fd7edbdfed9",- "size": 391726,+ "version": "1.9.125",+ "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.125/adom-bridge-puppeteer-v1.9.125.zip",+ "sha256": "45125f30759fe5c30985e76413008eddbffa8f1acedcb35838cf46f86824423a",+ "size": 392491, "verbPrefixes": [ "browser_", "desktop_recorder_",
src/BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.124+1.9.125
src/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "puppeteer", "displayName": "Puppeteer (Chrome for Testing + Native)",- "version": "1.9.124",+ "version": "1.9.125", "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. browser_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. browser_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, browser_alert_window posts a native toast whose click focuses the window, browser_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-desktop", "author": "Adom Inc.",
src/server.js+38−1@@ -5991,6 +5991,30 @@ function wikiUrlLint(u) { const r = urlLint(u, null); return (r && r.code === 'url_wiki_missing_owner') ? r.message : null; }+// v1.9.125 (follow-on) — AUTO-FILL a vault-matched login form the instant the page renders.+// Runs after every render verb (open/tab/navigate/reload). Conservative by design:+// - only when the vault has a credential for this exact origin (deliberate opt-in per host),+// - only on real http(s) pages,+// - FILL ONLY, never auto-submit (auto-submitting on load risks MFA/lockouts/wrong-form logins),+// - never throws — autofill must never block or fail a navigation,+// - reported to the caller as _autoLogin, and silenceable with browser_configure {autoLogin:"off"}.+async function autoLoginOnLoad(res, page) {+ try {+ if (!res || !page) return;+ let mode = 'on';+ try { mode = (pupSettings().autoLogin === 'off') ? 'off' : 'on'; } catch (e) {}+ if (mode === 'off') return;+ let url = '';+ try { url = page.url(); } catch (e) { return; }+ if (!url || !/^https?:/i.test(url)) return;+ const creds = await credentialVault.getCredentialForUrl(url);+ if (!creds) return;+ const r = await credentialVault.fillLoginForm(page, { username: creds.username, password: creds.password, submit: false });+ if (r && r.ok && r.filledPassword) {+ res._autoLoginNote = `pup AUTO-FILLED this login form for "${creds.host}" from your saved credential (username ${creds.username}) — in the BACKGROUND, NOT submitted, no Chrome dropdown, no foregrounding. To sign in: browser_login {sessionId,submit:true} or click the submit button yourself. Wrong form? browser_configure {autoLogin:"off"} turns this off.`;+ }+ } catch (e) { /* autofill is best-effort; never disturb the navigation */ }+} async function probeRender(page, httpStatus) { const out = { httpStatus: (httpStatus === undefined ? null : httpStatus), ok: true, signals: [] }; try {@@ -6171,6 +6195,8 @@ function sendJSON(res, obj, status = 200) { if (_v && RENDER_VERBS.has(_v) && _ok && inner._verifyRender === undefined) { const _rc2 = (res && res._renderCheck) || null; if (_rc2 && inner.renderCheck === undefined) inner.renderCheck = _rc2;+ // v1.9.125: surface any background auto-login note.+ if (res && res._autoLoginNote && inner._autoLogin === undefined) { inner._autoLogin = res._autoLoginNote; touched = true; } // v1.9.118: CALL-TIME reprimand — fires on the URL itself, pass or fail. try { if (inner._urlLint === undefined) {@@ -6193,7 +6219,7 @@ function sendJSON(res, obj, status = 200) { touched = true; } } catch (e) {}- for (const k of ['_reportIssues', '_cleanupReport', '_hint', '_next', 'related', 'pitfalls']) {+ for (const k of ['_reportIssues', '_cleanupReport', '_autoLogin', '_hint', '_next', 'related', 'pitfalls']) { if (obj[k] !== undefined && inner[k] === undefined) { inner[k] = obj[k]; touched = true; } } if (touched) obj.output = JSON.stringify(inner);@@ -6719,6 +6745,7 @@ const server = http.createServer(async (req, res) => { let _hs = null; try { _hs = session.page && session.page.__pupHttpStatus; } catch (e) {} res._renderCheck = await probeRender(session.page, _hs);+ try { await autoLoginOnLoad(res, session.page); } catch (e) {} const _rc = res._renderCheck; if (_rc && _rc.ok === false) console.log(`[render] "${sessionId}" LOOKS BROKEN: ${_rc.signals.join(', ')}`); } catch (e) {}@@ -6935,7 +6962,14 @@ const server = http.createServer(async (req, res) => { sendJSON(res, { success: false, error: `reportIssues must be one of on|off` }); return; }+ // v1.9.125: autoLogin on|off — pup auto-filling a vault-matched login form on page load.+ const al = args.autoLogin;+ if (al !== undefined && !['on', 'off'].includes(al)) {+ sendJSON(res, { success: false, error: `autoLogin must be one of on|off` });+ return;+ } if (ri) { savePupSettings({ reportIssues: ri }); console.log(`[configure] reportIssues -> ${ri}`); }+ if (al) { savePupSettings({ autoLogin: al }); console.log(`[configure] autoLogin -> ${al}`); } if (aa) { savePupSettings({ agentActivity: aa }); console.log(`[configure] agentActivity -> ${aa}`);@@ -7414,6 +7448,7 @@ const server = http.createServer(async (req, res) => { // verdict — otherwise "open it in a tab" silently skips the check that "open it in a window" // performs. When a behavior belongs to a declared SET, every member gets it. try { res._renderCheck = await probeRender(tab.page, tab.page.__pupHttpStatus); } catch (e) {}+ try { await autoLoginOnLoad(res, tab.page); } catch (e) {} try { const _rc = res._renderCheck; if (_rc && _rc.ok === false) console.log(`[render] "${sessionId}" tab ${tabId} BROKEN: ${_rc.signals.join(', ')}`);@@ -8684,6 +8719,7 @@ const server = http.createServer(async (req, res) => { // success on a page nobody had checked. The verb used to confirm a fix is exactly the verb // that must verify it. try { res._renderCheck = await probeRender(tab.page, tab.page.__pupHttpStatus); } catch (e) {}+ try { await autoLoginOnLoad(res, tab.page); } catch (e) {} try { const _rc = res._renderCheck; if (_rc && _rc.ok === false) console.log(`[render] "${resolvedId}" STILL BROKEN after reload: ${_rc.signals.join(', ')}`);@@ -8771,6 +8807,7 @@ const server = http.createServer(async (req, res) => { if (tab._crashed) { tab._crashed = false; delete tab._crashedAt; delete tab._crashReason; console.log(`[crash] session "${resolvedId}" tab ${tabId} recovered via navigate`); } // v1.9.109: probe on navigate as well — same reasoning as reload. try { res._renderCheck = await probeRender(tab.page, tab.page.__pupHttpStatus); } catch (e) {}+ try { await autoLoginOnLoad(res, tab.page); } catch (e) {} try { const _rc = res._renderCheck; if (_rc && _rc.ok === false) console.log(`[render] "${resolvedId}" BROKEN after navigate to ${args.url}: ${_rc.signals.join(', ')}`);