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
The Settings examples become five fixed canonical samples per John's spec - a wiki page, the dashboard app, ti.com, mouser and digikey - rendered identically under all six states from bundled ground-truth art: each vendor's own favicon including TI's real red logo, the real category tiles, and the real Chrome-for-Testing icon, always present regardless of which windows are open.
8 files changed
+86−33
adom-bridge-puppeteer-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "puppeteer",- "version": "2.0.203",- "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.203/adom-bridge-puppeteer-v2.0.203.zip",- "sha256": "6677a5929f07004cb9389fc121ac459b8fc671cd90ac8f4913a144976424f464",- "size": 927180,+ "version": "2.0.204",+ "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.204/adom-bridge-puppeteer-v2.0.204.zip",+ "sha256": "26fc26082ca35d4cbb4ff909d478bc42b50583f27e8e616f07476d67a966d9e5",+ "size": 933957, "verbPrefixes": [ "browser_", "desktop_recorder_",
src/BRIDGE_VERSION+1−1@@ -1 +1 @@-2.0.203+2.0.204
src/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "puppeteer", "displayName": "Puppeteer (Chrome for Testing + Native)",- "version": "2.0.203",+ "version": "2.0.204", "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. pup_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. pup_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, pup_alert_window posts a native toast whose click focuses the window, pup_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-bridge", "author": "Adom Inc.",
src/icons/settings-samples/digikey.svgadded+18@@ -0,0 +1,18 @@+<?xml version="1.0" encoding="UTF-8"?>+<svg id="Art" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">+ <defs>+ <style>+ .cls-1 {+ fill: #fff;+ }++ .cls-2 {+ fill: #ff2100;+ }+ </style>+ </defs>+ <g id="App_Icon" data-name="App Icon">+ <rect class="cls-1" width="32" height="32"/>+ <path id="D" class="cls-2" d="m13.5368374,25.3684211h4.9263209V6.6315789h-4.9263209v18.7368421ZM6.3789469,2h12.4631605c4.0842071,0,6.7789457,2.4842122,6.7789457,6.3157895v14.9473684c0,3.284214-1.8105297,6.7368421-6.7789457,6.7368421H6.3789469V2Z"/>+ </g>+</svg>
src/icons/settings-samples/mouser.icoadded⋯ 1 unchanged line ⋯
src/icons/settings-samples/ti.icoadded⋯ 1 unchanged line ⋯
src/icons/settings-samples/wiki.svgadded+1@@ -0,0 +1 @@+<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 244 244"><defs><style>.cls-1{fill:none;}.cls-1,.cls-2,.cls-3,.cls-4{stroke-width:0px;}.cls-2{fill:#8C6BF7;}.cls-3{fill:#fff;}.cls-4{fill:#00204F;}.cls-5{clip-path:url(#clippath);}</style><clipPath id="clippath"><rect class="cls-1" x="2.33" y="2.33" width="239.33" height="239.33"/></clipPath></defs><path class="cls-3" d="M184.3,59.7c-16.95-16.95-41.26-20.14-54.29-7.11-5.29,5.29-7.9,12.43-8.01,20.18h0s0,0,0,0c0,.03,0,.05,0,.08v48.09c0,.58.47,1.05,1.05,1.05h47.57-.01c7.99,0,15.37-2.6,20.79-8.02,13.03-13.03,9.84-37.33-7.11-54.29Z"/><path class="cls-3" d="M59.7,184.3c16.95,16.95,41.26,20.14,54.29,7.11,5.29-5.29,7.9-12.43,8.01-20.18h0s0,0,0,0c0-.03,0-.05,0-.08v-48.09c0-.58-.47-1.05-1.05-1.05h-47.57.01c-7.99,0-15.37,2.6-20.79,8.02-13.03,13.03-9.84,37.33,7.11,54.29Z"/><g class="cls-5"><path class="cls-2" d="M206.67,241.67H37.33c-19.33,0-34.99-15.67-34.99-34.99V37.33C2.33,18,18,2.33,37.33,2.33h169.34c19.33,0,34.99,15.67,34.99,34.99v169.34c0,19.33-15.67,34.99-34.99,34.99"/><path class="cls-4" d="M173.76,70.24c-14.09-14.09-34.28-16.73-45.1-5.91-4.39,4.39-6.56,10.33-6.66,16.77h0v40.03c0,.48.39.88.88.88h39.52,0c6.64,0,12.77-2.16,17.28-6.66,10.82-10.82,8.18-31.02-5.91-45.1"/><path class="cls-4" d="M70.24,173.76c14.09,14.09,34.28,16.73,45.1,5.91,4.39-4.39,6.56-10.33,6.66-16.77h0v-40.03c0-.48-.39-.88-.88-.88h-39.52,0c-6.64,0-12.77,2.16-17.28,6.66-10.82,10.82-8.18,31.02,5.91,45.1"/></g></svg>
src/server.js+61−27@@ -11389,6 +11389,8 @@ select,input{background:#111b23;color:#e6edf3;border:1px solid #23303c;border-ra .strow6.mcur{border-color:#00b8b1;box-shadow:0 0 0 1px #00b8b1 inset} .stlbl{flex:0 0 175px;font:600 12px 'Familjen Grotesk',sans-serif;color:#c9d1d9} .sticons{display:flex;gap:10px;flex-wrap:wrap;align-items:center}+.mbcard{position:absolute;top:-7px;right:-7px;width:20px;height:20px;border-radius:5px;background:#202124;display:flex;align-items:center;justify-content:center;overflow:hidden}+.mbcard img{width:16px;height:16px;object-fit:contain} .srow{display:flex;align-items:flex-start;gap:12px;padding:11px 0;border-bottom:1px solid #21262d} .srow:last-child{border-bottom:none} .srow .stxt{flex:1 1 auto;min-width:0}@@ -11801,25 +11803,26 @@ function renderDlg(){ // so the strip always shows a range of name lengths. function paintThreadExamples(){ const strip=_q('#texstrip'); if(!strip) return;- // v2.0.203 (John: "show the EXACT SAME sample windows' taskbar icons for each of the 6 states").- // The preview is now SIX MINI-TASKBAR ROWS over ONE fixed sample set (the live windows), so every- // state is an apples-to-apples rendering of the same windows:- // tiles rows -> one name tile per THREAD (real tile renderer) + that thread's real badge- // aumid rows -> one icon per WINDOW (the actual registered icon via stampicon) + real badge- // plain rows -> ONE stock Chrome button (actual exe icon) + one real badge when overlay is on- // Rows highlight the CURRENT state, carry the default chip, and click-to-switch.+ // v2.0.204 (John, the final spec): the examples are FIVE FIXED CANONICAL SAMPLES — wiki page,+ // dashboard app, ti.com, mouser, digikey — rendered identically under every one of the six+ // states, from BUNDLED ground-truth art (each vendor's own favicon, the real category tiles, the+ // real Chrome-for-Testing icon). Always present, never dependent on which windows are open: the+ // rows exist to teach what the modes MEAN. const st2=(SNAP&&SNAP.bridge&&SNAP.bridge.settings)||{}; const mode=st2.aumidIcons!=='on'?'plain':(st2.threadTiles==='on'?'tiles':'aumid'); const ovOn=st2.overlayBadges!=='off';- const wins=((SNAP&&SNAP.windows)||[]).filter(function(w){return (w.sessionId||w.id);});- const V='&v='+(typeof PAGE_VERSION!=='undefined'?PAGE_VERSION:'0');- const threads=[]; const sidByOwner={};- wins.forEach(function(w){const t=w.owner||w.ownerThread||'window';const sid=w.sessionId||w.id;- if(threads.indexOf(t)<0)threads.push(t); if(!sidByOwner[t])sidByOwner[t]=sid;});- function badge(sid){ return '<img class="mbadge" src="/dashboard/badge?session='+encodeURIComponent(sid)+V+'" data-fbb="1">'; }- function tileIc(name,withBadge){ return '<span class="mic"><canvas width="128" height="128" data-tname="'+esc(name)+'"></canvas>'+(withBadge&&sidByOwner[name]?badge(sidByOwner[name]):'')+'</span>'; }- function aumidIc(sid,withBadge){ return '<span class="mic"><img class="mbase" src="/dashboard/stampicon?session='+encodeURIComponent(sid)+V+'" data-fb="1">'+(withBadge?badge(sid):'')+'</span>'; }- function plainIc(withBadge){ return '<span class="mic"><img class="mbase" src="/dashboard/baseicon?profile=adom-you'+V+'">'+(withBadge?'<img class="mbadge" src="/dashboard/bestbadge?'+V.slice(1)+'" data-fbb="1">':'')+'</span>'; }+ const V='v='+(typeof PAGE_VERSION!=='undefined'?PAGE_VERSION:'0');+ const SAMPLES=[+ {tile:'wiki', fav:'/dashboard/sample-favicon?n=wiki&'+V, cat:'wiki', dark:false},+ {tile:'dash', fav:'/dashboard/icon.svg?'+V, cat:'app', dark:false},+ {tile:'ti.com', fav:'/dashboard/sample-favicon?n=ti&'+V, cat:'web', dark:false},+ {tile:'mouser', fav:'/dashboard/sample-favicon?n=mouser&'+V, cat:'web', dark:false},+ {tile:'digikey', fav:'/dashboard/sample-favicon?n=digikey&'+V, cat:'web', dark:false},+ ];+ function fb(sm){ return '<span class="mbcard"><img src="'+sm.fav+'" alt=""></span>'; }+ function tileIc(sm,ov){ return '<span class="mic"><canvas width="128" height="128" data-tname="'+esc(sm.tile)+'"></canvas>'+(ov?fb(sm):'')+'</span>'; }+ function aumidIc(sm,ov){ return '<span class="mic"><img class="mbase" src="/dashboard/sample-cat?k='+sm.cat+'&'+V+'" data-catfb="'+sm.cat+'">'+(ov?fb(sm):'')+'</span>'; }+ function plainIc(ov){ return '<span class="mic"><img class="mbase" src="/dashboard/baseicon?profile=adom-you&'+V+'">'+(ov?fb(SAMPLES[0]):'')+'</span>'; } const states=[ ['tiles',true, '1. tiles + overlay'], ['tiles',false,'2. tiles'],@@ -11833,23 +11836,26 @@ function paintThreadExamples(){ const cur=(m===mode&&ov===ovOn); const isDefault=(m==='tiles'&&ov); let icons='';- if(!wins.length){ icons='<span class="texn">(no live windows to sample — open a pup window and reopen Settings)</span>'; }- else if(m==='tiles'){ icons=threads.map(function(t){return tileIc(t,ov);}).join(''); }- else if(m==='aumid'){ icons=wins.map(function(w){return aumidIc(w.sessionId||w.id,ov);}).join(''); }- else { icons=plainIc(ov); }+ if(m==='tiles'){ icons=SAMPLES.map(function(sm){return tileIc(sm,ov);}).join(''); }+ else if(m==='aumid'){ icons=SAMPLES.map(function(sm){return aumidIc(sm,ov);}).join(''); }+ else { icons=plainIc(ov)+'<span class="texn" style="align-self:center">all 5 sample windows stack here</span>'; } return '<div class="strow6'+(cur?' mcur':'')+'" data-si="'+i+'" title="click to switch to this state">' +'<span class="stlbl">'+lbl+(cur?' (current)':'')+(isDefault?' <span class="mdef">default</span>':'')+'</span>' +'<div class="sticons">'+icons+'</div></div>'; }).join('')+'</div>'; const cap=strip.parentElement?strip.parentElement.querySelector('.texcap'):null;- if(cap)cap.textContent='The SAME sample windows rendered under every state: tiles rows show one name tile per thread (drawn by the real tile renderer) with that thread\u2019s real composed badge; aumid rows show each window\u2019s actual registered icon; plain rows show the actual Chrome-for-Testing icon. Badges are the exact PNGs pup handed Windows. Click a row to switch. Sampling '+wins.length+' live window(s) across '+threads.length+' thread(s).';+ if(cap)cap.textContent='Five canonical sample windows — a wiki page, the pup dashboard app, ti.com, mouser and digikey — rendered under every state so you can compare like for like. Tiles are drawn by the real tile renderer; category icons and the Chrome icon are the real registered art; badges are each site\u2019s own favicon. Click a row to switch to it.'; strip.querySelectorAll('canvas[data-tname]').forEach(function(cv){ drawNameTile(cv.getContext('2d'),128,cv.getAttribute('data-tname')); });- strip.querySelectorAll('img.mbadge[data-fbb]').forEach(function(im){- im.onload=function(){ if(im.naturalWidth<=2){ im.remove(); } };- im.onerror=function(){ im.remove(); };- });- strip.querySelectorAll('img.mbase[data-fb]').forEach(function(im){- im.onerror=function(){ im.style.display='none'; };+ strip.querySelectorAll('img[data-catfb]').forEach(function(im){+ im.onerror=function(){+ const cv=document.createElement('canvas'); cv.width=128; cv.height=128; cv.className='mbase';+ const ctx=cv.getContext('2d'),box=128;+ rr(ctx,0,0,box,box,box*0.14,'#00b8b1');+ rr(ctx,box*0.18,box*0.18,box*0.64,box*0.64,box*0.1,'#053b38');+ ctx.strokeStyle='#7fe7e2';ctx.lineWidth=box*0.045;+ ctx.beginPath();ctx.arc(box/2,box/2,box*0.19,0,7);ctx.stroke();+ im.replaceWith(cv);+ }; }); strip.querySelectorAll('.strow6').forEach(function(row,i){ row.onclick=function(){@@ -12407,6 +12413,34 @@ const server = http.createServer(async (req, res) => { res.end(buf); return; } catch (e) { res.writeHead(404); res.end(); return; } }+ if (pathname === '/dashboard/sample-favicon') {+ // v2.0.204 (John: the Settings examples are CANONICAL — wiki page, dashboard app, ti.com,+ // mouser, digikey — so users learn what the modes mean regardless of what is open): serve the+ // bundled ground-truth favicons fetched from each vendor's own site (ti's is the red Texas+ // logo pup's live pipeline cannot fetch yet, #55 — the example is still correct art).+ try {+ const n = (url.searchParams.get('n') || '').replace(/[^a-z]/g, '');+ const map = { wiki: ['wiki.svg', 'image/svg+xml'], ti: ['ti.ico', 'image/x-icon'], mouser: ['mouser.ico', 'image/x-icon'], digikey: ['digikey.svg', 'image/svg+xml'] };+ if (map[n]) {+ const buf = fs.readFileSync(path.join(__dirname, 'icons', 'settings-samples', map[n][0]));+ res.writeHead(200, { 'Content-Type': map[n][1], 'Cache-Control': 'public, max-age=3600' });+ res.end(buf); return;+ }+ } catch (e) {}+ res.writeHead(404); res.end(); return;+ }+ if (pathname === '/dashboard/sample-cat') {+ // The real registered category tiles (wiki signed-in / app / web) as PNG, for the aumid rows.+ try {+ const k = (url.searchParams.get('k') || '').replace(/[^a-z]/g, '');+ const name = { wiki: 'pup-cat-wikiin9.ico', app: 'pup-cat-app9.ico', web: 'pup-cat-web9.ico' }[k];+ if (name) {+ const inner = icoLargestPng(fs.readFileSync(path.join(_NOTCH_CACHE, name)));+ if (inner) { res.writeHead(200, { 'Content-Type': 'image/png', 'Cache-Control': 'public, max-age=3600' }); res.end(inner); return; }+ }+ } catch (e) {}+ res.writeHead(404); res.end(); return;+ } if (pathname === '/dashboard/bestbadge') { // v2.0.201 (John: the matrix overlay cells showed blank circles): serve the composed badge of // a session whose favicon is REAL (kind page-favicon), so the Settings preview always has an