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
Dashboard gets its own icon and its own window (John). ICON: the dashboard is an Adom app, so per adom-ui-design it serves its OWN icon at /dashboard/icon.svg rather than inheriting Chrome's, a clean 32x32 Adom-teal pup mark that reads at 16px in a taskbar overlay. Served as a real asset rather than a data URI so pup's favicon pipeline fetches it exactly like any other app icon, which means the dashboard window's taskbar badge is produced by the very code path the dashboard exists to display. OWN WINDOW: new browser_dashboard verb always opens the dashboard in its own window on its own durable profile, so it carries its own taskbar identity and can be found and closed independently; a fixed sessionId means repeat calls refresh the single window instead of spawning duplicates. Backgrounded by default like any pup window, because a monitor must not steal the screen.
4 files changed
+48−8
adom-bridge-puppeteer-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "puppeteer",- "version": "1.9.230",- "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.230/adom-bridge-puppeteer-v1.9.230.zip",- "sha256": "6db6d55ac23aec003ccf3cfa7f78370d25993a291c07127eb38b13f4f783a459",- "size": 455125,+ "version": "1.9.231",+ "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.231/adom-bridge-puppeteer-v1.9.231.zip",+ "sha256": "517511bbfd1cb0e0734e1aeba8dc5ec8dae06a1d5de00f32bdb3f53d51fcc549",+ "size": 456153, "verbPrefixes": [ "browser_", "desktop_recorder_",
src/BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.230+1.9.231
src/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "puppeteer", "displayName": "Puppeteer (Chrome for Testing + Native)",- "version": "1.9.230",+ "version": "1.9.231", "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+42−2@@ -3412,7 +3412,7 @@ function clearSessionBusy(sessionId) { // Verbs that are pure status/polling: they do NOT mean an agent is driving the window, and lighting // the bar for them would make every window look permanently busy (the audit loops alone poll these). const PASSIVE_VERBS = new Set([- 'browser_list_windows', 'browser_list_tabs', 'browser_readiness', 'browser_status', 'browser_maximize', 'browser_hover', 'browser_deps',+ 'browser_list_windows', 'browser_list_tabs', 'browser_readiness', 'browser_status', 'browser_maximize', 'browser_hover', 'browser_deps', 'browser_dashboard', 'browser_describe', 'browser_configure', 'browser_prewarm', 'browser_rescan', 'browser_wait', ]); @@ -7844,7 +7844,7 @@ async function dashboardSnapshot() { const DASHBOARD_HTML = `<!doctype html><html lang="en"><head> <meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"> <title>pup — live windows</title>-<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='7' fill='%2312e0c4'/><circle cx='12' cy='13' r='4' fill='%2306282a'/><circle cx='21' cy='20' r='4' fill='%2306282a'/></svg>">+<link rel="icon" type="image/svg+xml" href="/dashboard/icon.svg"> <style> *{box-sizing:border-box} html,body{margin:0;background:#0b1116;color:#e6edf3;font:14px/1.5 "Segoe UI",system-ui,sans-serif} a{color:#12e0c4}@@ -8118,6 +8118,22 @@ const server = http.createServer(async (req, res) => { return done({ ok: false, error: 'unknown action' }); } catch (e) { return done({ ok: false, error: e.message }); } }+ if (pathname === '/dashboard/icon.svg') {+ // v1.9.231 (John): the dashboard is an Adom APP, so per adom-ui-design it serves its OWN icon+ // rather than inheriting Chrome's. Clean 32x32 SVG, Adom teal, the two-leaf pup mark on a+ // rounded tile so it reads at 16px in a taskbar overlay and in a browser tab. Served as a real+ // asset (not a data: URI) so pup's favicon pipeline fetches it exactly like any other app icon,+ // which also means the dashboard window's own taskbar badge is produced by the same code path+ // it is built to display.+ res.writeHead(200, { 'Content-Type': 'image/svg+xml', 'Cache-Control': 'public, max-age=3600' });+ res.end('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">'+ + '<rect width="32" height="32" rx="7" fill="#12e0c4"/>'+ + '<circle cx="12" cy="12.5" r="4.1" fill="#06282a"/>'+ + '<circle cx="20.5" cy="20" r="4.1" fill="#06282a"/>'+ + '<path d="M14.6 15.1 L18 17.6" stroke="#06282a" stroke-width="2.1" stroke-linecap="round"/>'+ + '</svg>');+ return;+ } if (pathname === '/dashboard/favicon') { // v1.9.225 (John): every TAB shows its own favicon in the dashboard as a visual cue. Cached per // tab URL so repeated SSE renders never re-fetch, and fetched through the page itself (cookies)@@ -8281,6 +8297,7 @@ const server = http.createServer(async (req, res) => { V('browser_press_key', 'Press a key, optionally with modifier chords held (Ctrl+A, Ctrl+V, Shift+Tab).', { sessionId: 'required str', key: 'required str', modifiers: 'optional array — any of ["Control","Shift","Alt","Meta"] (aliases ctrl/cmd/command/option accepted); held down around the key press' }, 15, { sessionId: 'work', key: 'a', modifiers: ['Control'] }), V('browser_NATIVE_INPUT_WARNING', '⛔ READ BEFORE USING AD\'s desktop_click / desktop_type / desktop_press_key ON A PUP WINDOW. Native OS input does NOT target a window — it goes to WHATEVER WINDOW HAS OS FOCUS at that instant. This user runs many AI threads and alt-tabs away from a foregrounded window almost immediately. If you fire native input without warning them, your keystrokes land in THEIR document (this happened: a username was typed into the user\'s own window). REQUIRED: notify_user toast + desktop_caption FIRST, tell them explicitly "don\'t type or click until I say done", wait until the window is genuinely foreground, re-verify immediately before EACH event, and caption when done. browser_assisted_click already does all of this — use it. And ALWAYS prefer CDP input (browser_click/browser_type/browser_hover): it needs no foreground and disturbs nobody. A native verb returning ok:true means the event was dispatched to the OS, NOT that it reached your window.', {}, 0, {}), V('browser_set_viewport', 'Pin the page viewport (CDP device-metrics) to an exact size for deterministic screenshots — OR clear that pin. ⚠ A pin PERSISTS: it survives navigation and hard reload, and while set the window stops tracking OS resizing (a human resizing it sees no reflow, which looks exactly like an app layout bug). ALWAYS clear it when done. Reported as emulatedViewport in browser_status / browser_list_windows.', { sessionId: 'required str', width: 'int — required unless reset', height: 'int — required unless reset', deviceScaleFactor: 'optional number (default 1)', reset: 'optional bool — CLEAR the override and track the OS window again (width/height not needed)' }, 15, { sessionId: 'work', reset: true }),+ V('browser_dashboard', 'Open the LIVE pup management dashboard in its OWN window (its own profile, so its own taskbar icon). Shows every window pup is driving as taskbar button -> window(s) -> tabs with each tab favicon, owning AI thread, foreground/background + the reason given to foreground it, highFps lease, uptime, and WHY each overlay icon was chosen. Live over SSE. Reuses the one window on repeat calls; backgrounded by default like any pup window.', {}, 30, {}), V('browser_deps', 'Report pup dependency health (wiki issue #15): depsPresent, selfInstalledThisBoot, lastError. Use this to answer "is pup broken?" without reading spawn logs.', { }, 10, {}), V('browser_hover', 'Move the pointer over a target WITHOUT clicking (reveals tooltips / hover menus). Glides the visible Adom cursor there too.', { sessionId: 'required str', selector: 'optional str', x: 'optional int', y: 'optional int', intent: 'optional str — narrated on the cursor label', steps: 'optional int (default 8)' }, 15, { sessionId: 'work', selector: '#menu' }), V('browser_maximize', 'Maximize (or restore/fullscreen/minimize) a pup window via CDP — no AD desktop verb required. Does NOT foreground it.', { sessionId: 'required str', state: 'optional "maximized"|"normal"|"minimized"|"fullscreen" (default maximized)' }, 15, { sessionId: 'work' }),@@ -11076,6 +11093,29 @@ const server = http.createServer(async (req, res) => { sendJSON(res, { success: true, output: JSON.stringify({ ok: true, url: args.url, sessionId: resolvedId, tabId, ...(authSteerHint(args.url) ? { _hint: authSteerHint(args.url).trim() } : {}) }) }); return; }+ case 'browser_dashboard': {+ // v1.9.231 (John): the dashboard ALWAYS gets its OWN pup window, never a tab in someone+ // else's, so it carries its own taskbar icon (its served icon.svg) and can be found and+ // closed independently. Fixed sessionId + its own durable profile, so repeat calls REUSE+ // the one window instead of spawning duplicates. Opens in the background like any pup+ // window: it is a monitor, and monitors must not steal the screen.+ const dashUrl = `http://127.0.0.1:${PORT}/dashboard`;+ try {+ const existing = sessions.get('pup-dashboard');+ if (existing) {+ try { await existing.page.goto(dashUrl, { waitUntil: 'domcontentloaded', timeout: 15000 }); } catch (e) {}+ sendJSON(res, { success: true, output: JSON.stringify({ ok: true, sessionId: 'pup-dashboard', url: dashUrl, reused: true,+ _hint: `The pup dashboard is already open in its own window (session "pup-dashboard") and was refreshed. It is BACKGROUNDED like any pup window; raise it with browser_raise_os_window + a foregroundReason if the user asked to see it.` }) });+ return;+ }+ await launchSession('pup-dashboard', dashUrl, { profile: 'pup-dashboard', owner: 'pup-dashboard' });+ sendJSON(res, { success: true, output: JSON.stringify({ ok: true, sessionId: 'pup-dashboard', url: dashUrl, reused: false,+ _hint: `Opened the live pup dashboard in its OWN window (session "pup-dashboard", its own profile so it has its own taskbar icon). It shows every window pup is driving, grouped taskbar button -> window -> tabs, with each tab's favicon, the owning AI thread, foreground/background plus the reason given to foreground, highFps lease, uptime, and how each overlay icon was chosen. Updates live over SSE. Backgrounded by default.` }) });+ } catch (e) {+ sendJSON(res, { success: false, errorCode: 'dashboard_open_failed', error: `could not open the dashboard: ${e.message}` });+ }+ return;+ } case 'browser_deps': { // v1.9.218 (issue #15): report dependency health so "pup is broken" can be answered without // reading the spawn log. selfInstalled:true means the bridge repaired itself this boot.