← Commit history

Pre-emptively refuse guaranteed-broken file:// URLs (John: a thread opened file:///home/adom/project/.../helium-lab.html and pup said NOTHING, just rendered Chrome's ERR_FILE_NOT_FOUND). urlLint now handles file:// — a CONTAINER-shaped path (/home,/root,/workspace,/tmp,/usr,/var,/opt,/mnt) can never exist on the user's desktop, so it is refused with a reprimand telling the caller to serve it via the container proxy URL or push_file it to the desktop first; any other file:// path gets an EXISTENCE PRE-FLIGHT (the bridge runs on the desktop, so it just checks) and is refused with the real reason if missing. browser_open_window and browser_navigate both refuse BEFORE spending a window, instead of opening one that renders an error page the calling AI has to guess at.

John Lauer ·edd776ddfe ·3mo ago ·parent 3111689
4 files changed +72−6
adom-bridge-puppeteer-manifest.json+4−4
@@ -1,10 +1,10 @@ {   "manifest_version": 1,   "name": "puppeteer",-  "version": "1.9.194",-  "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.194/adom-bridge-puppeteer-v1.9.194.zip",-  "sha256": "6b613e8bb0de075165201dcfd3d89de3244fb47b3a8ac0cd9c7b843f87b87df4",-  "size": 424269,+  "version": "1.9.195",+  "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.195/adom-bridge-puppeteer-v1.9.195.zip",+  "sha256": "ea3353cc79b1c6207405fbe1e62930a5c9b5f0fb3b782b66fba835f967dd9a9b",+  "size": 425840,   "verbPrefixes": [     "browser_",     "desktop_recorder_",
src/BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.9.194+1.9.195
src/bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "puppeteer",   "displayName": "Puppeteer (Chrome for Testing + Native)",-  "version": "1.9.194",+  "version": "1.9.195",   "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+66
@@ -6686,6 +6686,49 @@ const PUP_ISSUES_REF = 'adom/adom-desktop-puppeteer-bridge'; function urlLint(u, callerContainer) {   try {     if (!u || typeof u !== 'string') return null;++    // ── file:// — v1.9.195 (John, 2026-07-29: a thread opened+    // file:///home/adom/project/hd-launcher-mockup/publish/orbital-lab-helium/helium-lab.html and pup+    // said NOTHING; it just rendered Chrome's ERR_FILE_NOT_FOUND). pup drives Chrome on the USER'S+    // DESKTOP, so a file:// URL resolves against THEIR filesystem, never the caller's container. A+    // container-shaped path (/home/, /root/, /workspace/, /tmp/) can NEVER exist there — that is a+    // guaranteed-broken open and must be reprimanded before it wastes a window. Any other file://+    // path gets an EXISTENCE PRE-FLIGHT below (the bridge runs on the desktop, so it can just look).+    if (/^file:/i.test(u)) {+      let fpath = '';+      try { fpath = decodeURIComponent(new URL(u).pathname || ''); } catch (e) { fpath = ''; }+      const winPath = fpath.replace(/^\/([A-Za-z]:)/, '$1').replace(/\//g, path.sep);+      const containerish = /^\/(home|root|workspace|tmp|usr|var|opt|mnt)\//i.test(fpath);+      if (containerish) {+        return {+          code: 'url_file_container_path',+          message: `CONTAINER PATH HANDED TO PUP. You passed "${u}", but pup drives Chrome on the USER'S DESKTOP `+            + `(Windows/macOS) — a file:// URL resolves against THEIR filesystem, not your container's. "${fpath}" is a `+            + `container path, so it can never exist there and this open is guaranteed to render Chrome's `+            + `ERR_FILE_NOT_FOUND page. Do ONE of these instead: (1) SERVE it and open the public proxy URL — run a `+            + `static server in your container (e.g. \`python3 -m http.server <port>\` in that directory) and open `+            + `https://<your-container>.adom.cloud/proxy/<port>/<file> ; or (2) PUSH it to the user's machine first `+            + `with adom-desktop push_file, then open the file:// path it actually landed at on THEIR disk. Never `+            + `assume your container's paths exist on the user's computer — they are different machines.`,+        };+      }+      // Not container-shaped: it MIGHT be a real path on this desktop. The bridge runs on the desktop,+      // so pre-flight it rather than letting Chrome render an error page the caller may misread.+      try {+        if (winPath && !fs.existsSync(winPath)) {+          return {+            code: 'url_file_not_found',+            message: `FILE DOES NOT EXIST ON THE USER'S MACHINE. pup pre-flighted "${winPath}" on the desktop before `+              + `opening it and there is no such file, so this would have rendered Chrome's ERR_FILE_NOT_FOUND page. `+              + `Check the path, or push the file to the desktop first with adom-desktop push_file and open the path it `+              + `reports back. (Remember: pup's Chrome runs on the USER'S computer, so file:// is THEIR filesystem, `+              + `not your container's.)`,+          };+        }+      } catch (e) {}+      return null;+    }+     const url = new URL(u);     const host = url.hostname.toLowerCase(); @@ -7282,6 +7325,19 @@ const server = http.createServer(async (req, res) => {         }         // --- Session management commands ---         case 'browser_open_window': {+          // v1.9.195 (John: "you're supposed to pre-emptively catch stupid urls the ai tells you to+          // open"). REFUSE a URL that CANNOT work, before spending a window on it. A container path+          // handed to pup (file:///home/...) or a file:// path that does not exist on the desktop is+          // a guaranteed ERR_FILE_NOT_FOUND — previously pup opened a window, rendered Chrome's error+          // page, and said NOTHING, so the calling AI had to guess (and often misdiagnosed it).+          {+            const _pre = urlLint(args.url, res && res._callerContainer);+            if (_pre && (_pre.code === 'url_file_container_path' || _pre.code === 'url_file_not_found')) {+              console.log(`[urlLint] REFUSED open of "${args.url}" — ${_pre.code}`);+              sendJSON(res, { success: false, errorCode: _pre.code, error: _pre.message, _urlLint: _pre.message, urlLintCode: _pre.code });+              return;+            }+          }           // v1.9.147: sessionId is OPTIONAL when the caller identifies its thread — the thread gets           // its own keyed window (see the thread-default block below). Only the fully anonymous           // no-id no-thread case is refused, down there, with a teaching error.@@ -9728,6 +9784,16 @@ const server = http.createServer(async (req, res) => {           return;         }         case 'browser_navigate': {+          {+            // v1.9.195: same pre-flight as open — never navigate a window to a guaranteed-broken+            // file:// URL (container path, or missing on the desktop) and leave the caller guessing.+            const _pre = urlLint(args.url, res && res._callerContainer);+            if (_pre && (_pre.code === 'url_file_container_path' || _pre.code === 'url_file_not_found')) {+              console.log(`[urlLint] REFUSED navigate to "${args.url}" — ${_pre.code}`);+              sendJSON(res, { success: false, errorCode: _pre.code, error: _pre.message, _urlLint: _pre.message, urlLintCode: _pre.code });+              return;+            }+          }           let session, resolvedId;           // v1.6.1+: auto-recover (reconnect or relaunch) before surfacing           // any error. The Docker container should never see "Session