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
Flip pup's default browser to NATIVE-FIRST. launchCandidates() now orders the user's installed Chrome, then Edge (ships on every Windows PC), FIRST, driven with a fresh isolated profile PLUS the noBrowserIdentityFlags that suppress Chrome's sign-in / set-up-a-work-profile merge dialog. Chrome-for-Testing is now the genuine LAST RESORT, fetched only when the box has no installed Chromium at all; warmup() no longer force-downloads CfT when a native browser is present. This makes native as clean as CfT (the identity-dialog reason for CfT-first was already invalidated: CfT shows the same dialog, the flags are the real fix and apply to any Chromium), with no 150MB download, always the user's current battle-tested build, and all taskbar identity overrides work identically. browser_use {browser:cft} still pins CfT for a managed/enterprise box whose policies interfere. Cold-start gate + hints updated; decision doc flipped.
5 files changed
+52−33
adom-bridge-puppeteer-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "puppeteer",- "version": "1.9.89",- "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.89/adom-bridge-puppeteer-v1.9.89.zip",- "sha256": "799b042ea9f5fefdbd4f644bc293f77b372f5ef69f0a4eab3b1ea9cca9ba5810",- "size": 362354,+ "version": "1.9.90",+ "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.90/adom-bridge-puppeteer-v1.9.90.zip",+ "sha256": "08053d442ee892424e9becffc40aedb25ab47602b8098c488c9457d959f67259",+ "size": 363073, "verbPrefixes": [ "browser_", "desktop_recorder_",
src/BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.89+1.9.90
src/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "puppeteer", "displayName": "Puppeteer (Chrome for Testing + Native)",- "version": "1.9.89",+ "version": "1.9.90", "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/chrome.js+27−12@@ -179,17 +179,22 @@ function markVerified(cand) { // Each entry is a fully-launchable {kind, executablePath, source}. The launch path // tries them top-to-bottom and spawn-verifies each, so a broken top pick falls through. function launchCandidates() {- // v1.8.71 (wiki issue #202 + the Aditya/Google-auth incident): cached CfT comes FIRST.- // CfT is the automation-first binary with NO profile/sync/sign-in machinery — the branded- // browsers' identity features bleed into pup's "anonymous" windows (sign-in → "create a- // profile?" → duplicate profiles → consolidation prompts → lost user). System browsers are- // the zero-download fallback for a fresh box; a fallback launch triggers a background CfT- // prewarm (server.js) so every box converges to CfT by its second session without ever- // stalling a first open. browser_use still pins either direction explicitly.+ // v1.9.90 (John's call, verified against this code): NATIVE-FIRST. The installed browser comes+ // FIRST — installed Chrome, else Edge (ships on every Windows PC) — driven with a FRESH+ // --user-data-dir AND the noBrowserIdentityFlags (server.js) that kill the sign-in / "set up a+ // work profile" merge dialog at launch. Those flags + the isolated profile make a native browser+ // EXACTLY as clean as CfT: the old v1.8.71 "CfT-first because the branded browsers' identity+ // bleeds in" reason is DEAD — I proved (and my own v1.8.95 comment admits) CfT shows the SAME+ // dialog, and the flags that suppress it apply to ANY Chromium. Native is the user's battle-tested,+ // auto-updated build with NO 150MB download and NO fleet dep-bump to move it, and every taskbar+ // icon/identity override works on it identically (OS-window level, browser-agnostic).+ // Chrome-for-Testing is the GENUINE LAST RESORT now — a box with no installed Chromium at all+ // (e.g. a Mac with no Chrome). browser_use {browser:"cft"} still pins CfT explicitly for the one+ // case it earns its weight: a managed/enterprise box whose policies interfere with automation. const all = [];+ all.push(...listSystemBrowsers()); // installed Chrome, then Edge — FIRST (zero download, already isolated) const cft = detectChrome();- if (cft.installed) all.push({ kind: 'chrome-for-testing', executablePath: cft.executablePath, source: 'cache', buildId: cft.buildId });- all.push(...listSystemBrowsers());+ if (cft.installed) all.push({ kind: 'chrome-for-testing', executablePath: cft.executablePath, source: 'cache', buildId: cft.buildId }); // LAST resort const def = readDefault(); if (def) {@@ -941,12 +946,22 @@ function warmup() { const free = diskFreeMb(); const cft = detectChrome(); console.log(`[warmup] browsers: ${cands.map(c => `${c.kind}(${c.source})`).join(', ') || 'NONE'} | cft cached: ${cft.installed} | disk free: ${free == null ? '?' : free + 'MB'}`);- if (cft.installed) return; // pup's browser is ready — nothing to do+ if (cft.installed) return; // already cached — nothing to do+ // v1.9.90 NATIVE-FIRST: only fetch CfT when the box has NO installed Chromium at all. If a native+ // Chrome or Edge is present (Edge ships on every Windows PC), pup drives THAT — no 150MB download,+ // ever. CfT is downloaded only as the genuine last resort for a Chromium-less box (e.g. a Mac with+ // no Chrome). This replaces the old "CfT on every box" doctrine that pulled 150MB even on machines+ // that already had a perfectly good, battle-tested browser.+ const hasNative = cands.some(c => c.kind === 'chrome' || c.kind === 'edge');+ if (hasNative) {+ console.log('[warmup] native Chrome/Edge present — native-first: NOT fetching Chrome for Testing (no download needed)');+ return;+ } if (free != null && free < CFT_MIN_FREE_MB) {- console.log(`[warmup] CfT missing + low disk (${free}MB) — NOT auto-fetching; readiness reports lowDisk and opens will surface the disk hint`);+ console.log(`[warmup] no native browser + CfT missing + low disk (${free}MB) — NOT auto-fetching; readiness reports lowDisk and opens will surface the disk hint`); return; }- console.log('[warmup] Chrome for Testing (pup\'s browser) not cached — background-fetching now so the first open never waits');+ console.log('[warmup] no installed Chromium on this box — background-fetching Chrome for Testing as the last-resort browser'); ensureChromeReady({ background: true }); } catch (e) { console.error(`[warmup] ${e && e.message}`); } }
src/server.js+19−15@@ -5823,15 +5823,16 @@ const server = http.createServer(async (req, res) => { sendJSON(res, { success: false, error: 'sessionId is required. Example: browser_open_window { "sessionId": "dart2", "profile": "dart2", "url": "https://..." }' }); return; }- // ── Cold-start gate (v1.8.72: THE DEMARCATION GATE) — READ-ONLY detect; never blocks ──- // pup's browser is Chrome for Testing, PERIOD (the Aditya incident: a silent branded-- // Chrome fallback let a Google sign-in trigger Chrome's profile-creation/consolidation- // machinery and strand the user). An unpinned NEW-window open therefore REQUIRES cached- // CfT: if it isn't there yet, kick the ~150 MB install in the BACKGROUND and return the- // pollable {installing:true} — the caller polls browser_readiness and retries. This wait- // is rare (AD prewarns CfT at install; flip of its skip-when-branded-exists default is- // filed) and only ever hits a box's FIRST pup use. The user's installed Chrome/Edge is- // available ONLY by explicit pin — browser_use {browser:"chrome"|"edge"} — eyes open.+ // ── Cold-start gate (v1.9.90: NATIVE-FIRST) — READ-ONLY detect; never blocks when drivable ──+ // pup drives the user's INSTALLED browser by default — Chrome, else Edge — in a FRESH isolated+ // profile with the noBrowserIdentityFlags that kill Chrome's sign-in / "set up a work profile"+ // merge dialog at launch. That combination is exactly as clean as CfT (the Aditya incident was+ // fixed by those flags, not by the binary — CfT shows the SAME dialog without them). So this+ // gate only blocks when there is NOTHING drivable at all (no Chrome, no Edge, no cached CfT):+ // then it fetches Chrome-for-Testing in the BACKGROUND as the last resort and returns the+ // pollable {installing:true}. That case is rare (Edge ships on every Windows PC). Chrome for+ // Testing is still available by explicit pin — browser_use {browser:"cft"} — for a+ // managed/enterprise box whose policies interfere with automation. // Existing/live sessions are exempt (their browser is already running). { const rd = chrome.readiness(); // detect-only; never downloads@@ -5848,6 +5849,9 @@ const server = http.createServer(async (req, res) => { // CfT is still PREFERRED (launchCandidates orders it first, and prewarm still fetches it in the // background) — preference is fine, blocking is not. Only gate when there is genuinely nothing // drivable on the machine.+ // v1.9.90 NATIVE-FIRST: launchCandidates orders the installed browser FIRST, so this gate+ // only fires when there is genuinely NOTHING drivable (no Chrome, no Edge, no cached CfT).+ // When a system browser exists, pup drives it directly (fresh profile + identity flags). const _hasSystemBrowser = (rd.candidates || []).some(c => c && c.source === 'system'); const _needsCft = !_pinnedBranded && !_cftCached && !_sessionAlive && !_hasSystemBrowser; if (!rd.ready || _needsCft) {@@ -5857,7 +5861,7 @@ const server = http.createServer(async (req, res) => { errorCode: rd.lastErrorCode || 'chrome_install_failed', error: `No Chrome/Edge installed and the Chrome-for-Testing fallback failed to install: ${rd.lastError}`, readiness: rd,- _hint: 'Chrome for Testing (pup\'s dedicated browser) failed to install — usually no network / a proxy blocking storage.googleapis.com, or low disk. Retry with browser_prewarm once the cause is fixed. If the user explicitly accepts driving their installed Chrome/Edge instead (identity-bleed risk — see the demarcation doctrine): browser_use {browser:"chrome"|"edge"}, then retry.',+ _hint: 'Chrome for Testing (the last-resort browser for a box with no Chrome/Edge) failed to install — usually no network / a proxy blocking storage.googleapis.com, or low disk. Retry with browser_prewarm once the cause is fixed. If this box actually HAS Chrome or Edge, pin it: browser_use {browser:"chrome"|"edge"} — pup drives it cleanly (fresh isolated profile + identity-suppression flags, no merge dialog), no download needed.', }); return; }@@ -5873,9 +5877,9 @@ const server = http.createServer(async (req, res) => { }); return; }- // Fetch CfT in the background and tell the caller to poll+retry. This is- // pup's dedicated browser being provisioned — NOT an error, and NOT a reason- // to fall back to the user's branded browser.+ // No native Chrome/Edge on this box, so fetch CfT in the background as the LAST-RESORT+ // browser and tell the caller to poll+retry. (When a native browser exists we never reach+ // here — native-first drives it directly with a fresh profile + identity-suppression flags.) if (!rd.installing) chrome.ensureChromeReady({ background: true }); sendJSON(res, { success: false,@@ -5883,8 +5887,8 @@ const server = http.createServer(async (req, res) => { installing: true, statusVerb: 'browser_readiness', readiness: rd,- error: 'Chrome for Testing (pup\'s dedicated sandboxed browser) is not cached yet — downloading it now (~150 MB, one time).',- _hint: 'FIRST-USE PROVISIONING (not an error): pup deliberately drives Chrome for Testing, its OWN sandboxed browser — it does NOT silently fall back to the user\'s installed Chrome/Edge, whose profile/sign-in machinery bleeds into pup\'s anonymous windows (sign-in → "create a profile?" → duplicate profiles → lost user). The download runs in the BACKGROUND: tell the user pup is provisioning its browser (~a minute or two, one time ever), poll `browser_readiness` until {ready:true}, then re-issue this browser_open_window. Only if the user EXPLICITLY wants their installed browser driven (rare; identity-bleed risk): browser_use {browser:"chrome"|"edge"} pins it, then retry.',+ error: 'This box has no installed Chrome or Edge, so pup is fetching Chrome for Testing as the last-resort browser (~150 MB, one time).',+ _hint: 'LAST-RESORT PROVISIONING (not an error): pup drives your INSTALLED Chrome or Edge by default, cleanly — a fresh isolated profile PLUS launch flags that suppress Chrome\'s sign-in / "set up a work profile" merge dialog, so there is no identity bleed. THIS box has neither Chrome nor Edge, so pup is fetching Chrome for Testing in the background as the only drivable option (~a minute or two, one time). Poll `browser_readiness` until {ready:true}, then re-issue browser_open_window. If the box actually DOES have Chrome/Edge, pin it with browser_use {browser:"chrome"|"edge"} to skip the download.', }); return; }