← Commit history

Stop reclaim adopting windows mid-launch: skip profiles with a launch in flight and require two consecutive sweeps before adopting

John Lauer ·b0914203f3 · ·parent 8d3a6a4
4 files changed +29−7
adom-bridge-puppeteer-manifest.json+4−4
@@ -1,10 +1,10 @@ {   "manifest_version": 1,   "name": "puppeteer",-  "version": "1.9.49",-  "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.49/adom-bridge-puppeteer-v1.9.49.zip",-  "sha256": "35e08ce795e6794bb15b8edcf439fe3581fad4425f00a8fa188a4cdc3fb03ce9",-  "size": 297848,+  "version": "1.9.50",+  "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.50/adom-bridge-puppeteer-v1.9.50.zip",+  "sha256": "b487261f476d8d3dd5bc4faac2c11d8ebac17461d5d54cedd21900bf2e876cc6",+  "size": 298485,   "verbPrefixes": [     "browser_",     "desktop_recorder_",
src/BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.9.49+1.9.50
src/bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "puppeteer",   "displayName": "Puppeteer (Chrome for Testing + Native)",-  "version": "1.9.49",+  "version": "1.9.50",   "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.",   "homepage": "https://wiki.adom.inc/adom/adom-desktop",   "author": "Adom Inc.",
src/server.js+23−1
@@ -2550,7 +2550,9 @@ function adPayload(r) { let _uiWatchId = null; let _uiWatchSeq = 0; let _lastDragCheck = 0;-let _lastReclaim = 0;   // v1.9.42 throttle cursor for the drag-out check+let _lastReclaim = 0;+// Windows seen unowned but not yet adopted (see the two-strike rule in reclaimUnmanagedWindows).+const _reclaimStrikes = new Map();   // v1.9.42 throttle cursor for the drag-out check let _uiWatchSupported = null;   // null = unprobed, false = AD too old async function ensureWindowOpenedWatch() {   if (process.platform !== 'win32' || _uiWatchSupported === false || _uiWatchId) return;@@ -2687,6 +2689,13 @@ async function reclaimUnmanagedWindows() {      // Part A: any page in a connected profile that no session owns.     for (const [profileName, be] of [...browsers]) {+      // v1.9.50: NEVER reclaim from a profile with a launch in flight. A launching window exists as+      // a page for a moment BEFORE its session is registered, so a sweep landing in that gap sees a+      // legitimate window as unowned and adopts it — producing TWO sessions for ONE window (caught+      // by the audit as 8 sessions / 7 taskbar buttons, with "r4d" and "r4d-wina4rd" both pointing+      // at the same window). Reclaim is a janitor: when a launch is in progress the answer is simply+      // "not yet", never "adopt it".+      if (_launchInFlight.has(profileName)) continue;       let pages = [];       try { pages = await be.browser.pages(); } catch (e) { continue; }       const owned = new Set();@@ -2710,6 +2719,19 @@ async function reclaimUnmanagedWindows() {         byWindow.get(key).push(p);       }       for (const [key, group] of byWindow) {+        // v1.9.50: two-strike confirmation. Even outside a tracked launch there are brief windows+        // where a page is real but not yet owned (a tab being attached, a session being rebuilt).+        // Adopting on first sight turns any such blink into a duplicate session that can never be+        // reconciled. Require the SAME window to look unowned on two consecutive sweeps; anything+        // transient resolves itself in between and is never touched.+        const strikeKey = `${profileName}:${key}`;+        const strikes = (_reclaimStrikes.get(strikeKey) || 0) + 1;+        if (strikes < 2) {+          _reclaimStrikes.set(strikeKey, strikes);+          console.log(`[reclaim] "${profileName}" window ${key} looks unowned (strike ${strikes}/2) — waiting one more sweep before adopting`);+          continue;+        }+        _reclaimStrikes.delete(strikeKey);         const newId = `${profileName}-win${Math.random().toString(36).slice(2, 6)}`;         const ns = createSession(profileName);         ns._sessionId = newId;