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
v1.9.9: fix jump-list click no-op (Windows argv strips unescaped quotes so JSON args arrived mangled) + desktop_caption feedback on switch
5 files changed
+62−10
CHANGELOG.md+18@@ -1,5 +1,23 @@ # Changelog β Adom Desktop Puppeteer Bridge +## 1.9.9 β 2026-07-19 β FIX: clicking the jump-list task did nothing (Windows argv quoting)++THE bug that made the whole feature look broken. The jump-list task line is parsed by the CLI's+argv parser, which STRIPS unescaped double quotes β so the raw JSON args arrived as+{sessionId:wiki-demo,view:authed} and the CLI died instantly with "Invalid JSON args: key must be a+string at line 1 column 2" (exit 1). The user saw a 50ms cmd flash and nothing else. Reproduced and+fixed on the real command line: the JSON is now wrapped in quotes with inner quotes+backslash-escaped so argv delivers it intact as one argument.++Also added an on-screen AD caption (desktop_caption) so a click gives INSTANT feedback during the+few-second relaunch: "π Switching to the LOGGED-IN view of the Adom wikiβ¦" on click, replaced on+completion by "π Adom wiki: now the LOGGED-IN view (<user>)" / "π β¦now the PUBLIC (logged-out)+view", or a prompt to do the one-time sign-in.++TESTING LESSON (baked into pup-bridge-debug): I had only tested the VERB directly via the CLI,+which bypasses the exact path a click takes. The bug lived entirely in that untested segment. Test+the ACTUAL entry point the user hits, not a convenient proxy for it.+ ## 1.9.8 β 2026-07-19 β Wiki toggle: snappy click, non-blocking raise, self-correcting glyph Follow-up to 1.9.7's foreground raise, from ralph-testing it live. Three fixes: (1) the OS raise now
adom-bridge-puppeteer-manifest.json+5−5@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "puppeteer",- "version": "1.9.8",- "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.8/adom-bridge-puppeteer-v1.9.8.zip",- "sha256": "48567e579859f697fa90e03c8864a7b54a94d9beb69788af4fb9e09cccf048cb",- "size": 215427,+ "version": "1.9.9",+ "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.9/adom-bridge-puppeteer-v1.9.9.zip",+ "sha256": "7fc65759fecf66d9cdb1c4e4b6e1d8957f442b4331f0b00a449fd53b6db49f55",+ "size": 216435, "verbPrefixes": [ "browser_", "desktop_recorder_",@@ -12,5 +12,5 @@ ], "healthEndpoint": "/status", "statusVerb": "browser_readiness",- "released_at": "2026-07-19T21:25:00Z"+ "released_at": "2026-07-19T22:00:00Z" }
src/BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.8+1.9.9
src/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "puppeteer", "displayName": "Puppeteer (Chrome for Testing + Native)",- "version": "1.9.8",+ "version": "1.9.9", "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+37−3@@ -1788,11 +1788,19 @@ async function updateWikiJumplist(session, sessionId) { // v1.9.6: give the task the Adom Pup ICON (iconPath) β without it Windows shows a generic // blank-document glyph (John: "why is your icon generic for that menu item?"). const _taskIcon = pupIconPath();+ // v1.9.9: WINDOWS ARGV QUOTING β THE reason clicking the task did NOTHING. The jump-list task+ // line is parsed by the CLI's argv parser, which STRIPS unescaped double quotes. Raw JSON+ // ({"sessionId":"x","view":"authed"}) therefore arrived as {sessionId:x,view:authed} and the CLI+ // died instantly with `Invalid JSON args: key must be a string at line 1 column 2` (exit 1) β+ // a 50ms cmd flash and no switch, exactly what John reported. Verified both ways on his box.+ // Fix: wrap the JSON in quotes with the inner quotes BACKSLASH-escaped, so argv delivers it+ // intact as ONE argument. Any future jump-list task passing JSON MUST use this escaping.+ const taskArgs = (v) => `browser_wiki_set_view "${JSON.stringify({ sessionId, view: v }).replace(/"/g, '\\"')}"`; let tasks = []; if (view === 'authed') {- tasks = [{ title: '\u{1F4D6} Adom wiki: switch to public view', description: 'Reload this Adom wiki page logged OUT β exactly what the public/world sees', iconPath: _taskIcon, args: `browser_wiki_set_view {"sessionId":"${sessionId}","view":"public"}` }];+ tasks = [{ title: '\u{1F4D6} Adom wiki: switch to public view', description: 'Reload this Adom wiki page logged OUT β exactly what the public/world sees', iconPath: _taskIcon, args: taskArgs('public') }]; } else if (view === 'public') {- tasks = [{ title: '\u{1F513} Adom wiki: switch to logged-in view', description: 'Reload this Adom wiki page SIGNED IN β private source, drafts, owner-only cards', iconPath: _taskIcon, args: `browser_wiki_set_view {"sessionId":"${sessionId}","view":"authed"}` }];+ tasks = [{ title: '\u{1F513} Adom wiki: switch to logged-in view', description: 'Reload this Adom wiki page SIGNED IN β private source, drafts, owner-only cards', iconPath: _taskIcon, args: taskArgs('authed') }]; } // The shell ignores a jump list on an unregistered AUMID β ensure it's registered first // (idempotent; only registers when there IS a menu to show).@@ -2055,6 +2063,18 @@ function appIconReprimand(url, hadFavicon) { return ' π¨ APP ICON MISSING: this looks like an Adom app, but the page serves NO favicon, so its pup window cannot wear the app\'s icon (and its webview tab shows a blank icon too). The adom-ui-design standard requires every app to serve a favicon (<link rel="icon">, monochrome WHITE mark, clean 24x24 SVG β see the adom-ui-design + app-creator skills). Add one and reopen, or pass appIconB64/appIconUrl on browser_open_window meanwhile.'; } +// v1.9.9: on-screen CAPTION for the taskbar wiki toggle (John's ask: "why don't you show an AD+// screen caption saying 'switching to logged in view of wiki for [email protected]'"). The switch+// relaunches the browser under the other cookie jar and takes several seconds, so WITHOUT this a+// click felt like nothing happened at all. Same `id` on every call so the start caption is+// REPLACED by the result caption instead of stacking. Best-effort; never throws.+async function wikiCaption(text, duration) {+ try {+ await chrome.adCommand('desktop_caption', {+ text, duration, id: 'pup-wiki-view', position: 'bottom', size: 'medium',+ }, { timeoutMs: 4000 });+ } catch (e) {}+} // v1.9.7: reusable OS FOREGROUND raise, extracted verbatim from the proven browser_raise_os_window // path, so the wiki view-toggle callback can actually POP the relaunched window to the user's // screen. A jump-list click is a genuine USER action, so a real SetForegroundWindow raise is@@ -4744,6 +4764,11 @@ const server = http.createServer(async (req, res) => { return; } console.log(`[wiki] view toggle "${sessionId}": ${curView} β ${view} (${url})`);+ // v1.9.9: tell the user IMMEDIATELY that their click registered β the relaunch takes a+ // few seconds and silence reads as "it did nothing". Replaced by the result caption below.+ wikiCaption(view === 'authed'+ ? '\u{1F513} Switching to the LOGGED-IN view of the Adom wikiβ¦'+ : '\u{1F4D6} Switching to the PUBLIC (logged-out) view of the Adom wikiβ¦', 30000).catch(() => {}); // Close the current window (mirror browser_close_window) so we can relaunch under the // other cookie jar. const oldProfile = session.profileName;@@ -4795,7 +4820,16 @@ const server = http.createServer(async (req, res) => { } if (view === 'authed') { await new Promise(r => setTimeout(r, 1800));- try { const a = await checkWikiAuth(s2.page); await setWikiModeGlyph(s2.page, (a && a.authed) ? '\u{1F513} Logged in' : '\u{1F4D6} Public'); } catch (e) {}+ let a = null;+ try { a = await checkWikiAuth(s2.page); } catch (e) {}+ try { await setWikiModeGlyph(s2.page, (a && a.authed) ? '\u{1F513} Logged in' : '\u{1F4D6} Public'); } catch (e) {}+ // v1.9.9: result caption β names WHO you're signed in as (John's ask), or says the+ // one-time sign-in is still needed. Replaces the "Switchingβ¦" caption (same id).+ wikiCaption(a && a.authed+ ? `\u{1F513} Adom wiki: now the LOGGED-IN view${a.user ? ` (${a.user})` : ''}`+ : '\u{1F513} Adom wiki: logged-in view needs the one-time sign-in β sign in in this window', 4000).catch(() => {});+ } else {+ wikiCaption('\u{1F4D6} Adom wiki: now the PUBLIC (logged-out) view', 4000).catch(() => {}); } })().catch(() => {}); return;