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
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "pup-bridge",- "version": "2.0.315",+ "version": "2.0.316", "type": "app", "description": "The pup SKILL PACK \u2014 the AI skills that teach a cloud/container AI how to drive the Adom Desktop Puppeteer (pup) bridge (browser_* verbs): the everyday `pup` skill plus focused sub-skills for windows/sessions/tabs, screenshots+recording, browsers+Chrome install, and driving the Adom wiki. Installs into ~/.claude/skills + ~/.codex/skills. DOCS ONLY \u2014 this is NOT the bridge runtime. The bridge itself (the Node program that drives the browser) runs on the DESKTOP: Adom Desktop bundles it and auto-installs/updates it, and it's published separately on the Releases tab as adom-bridge-puppeteer-v<ver>.zip. The pkg version is kept in lockstep with the Release version so the page's max version row always carries BOTH a pkg tarball (skills) and a runtime zip (bridge) \u2014 a skills-only or runtime-only bump on the top row breaks constraint installs. TL;DR: pkg install = the skills for your AI; the bridge = the Release zip, which Adom Desktop manages for you.", "keywords": [
page.json+1−1@@ -4,7 +4,7 @@ "slug": "pup-bridge", "title": "Pup - Puppeteer Bridge", "brief": "pup is the AI's own browser: a real, full Chrome on the user's desktop that the AI fully controls (a sandbox, not the user's signed-in browser). Rides Bridge; pup_* verbs open windows and tabs, navigate, screenshot, and eval JS.",- "version": "2.0.315",+ "version": "2.0.316", "tags": [ "skills", "pup",
tools/pup-dashboard+60−9@@ -127,6 +127,7 @@ const PROXY = (port) => `http://127.0.0.1:${port}/`; // user can see which one the lights describe. let _target = { at: 0, name: null, all: [] }; async function resolveTarget() {+ if (process.env.ADOM_DESKTOP_TARGET) return { name: process.env.ADOM_DESKTOP_TARGET, all: [process.env.ADOM_DESKTOP_TARGET], at: Date.now() }; if (_target.name && Date.now() - _target.at < 60000) return _target; const d = await new Promise((resolve) => { execFile('adom-bridge', ['targets'], { env: ENV, timeout: 8000 }, (err, stdout) => {@@ -333,19 +334,69 @@ load(); setInterval(load,15000); // ── demo ──────────────────────────────────────────────────────────────────────────────────────── async function runDemo() {+ // The dock-card showcase (John: "shouldn't your pup demo be a bit cooler? show off your+ // annotation feature, your jump lists, how cool your icons are"). The user CLICKED Run the demo,+ // so the finale may come to their screen: the raise is captioned with exactly that reason. const lines = []; const sid = 'pup-dock-demo';- lines.push('Opening a window in the BACKGROUND (pup never steals your screen)…');+ const sleep = (ms) => new Promise(r => setTimeout(r, ms)); const tgt = await resolveTarget();++ lines.push('1. Opening a window in the BACKGROUND. pup births it off-screen and parks it behind');+ lines.push(' your work in under 50ms, so it can never steal your screen or your keystrokes.'); const open = await ab('pup_open_window', { sessionId: sid, url: 'https://wiki.adom.inc/adom/pup-bridge', owner: 'pup dock demo' }, 120000, tgt.name);- lines.push(open._err ? ` could not open: ${open._err}` : ` open: ${open.sessionId || sid}`);- if (!open._err) {- const shot = await ab('pup_screenshot', { sessionId: sid }, 60000, tgt.name);- lines.push(shot._err ? ` screenshot failed: ${shot._err}` : ` screenshot: ${shot.width}x${shot.height}`);- lines.push('Its taskbar button carries this thread\'s name and the page favicon.');- lines.push('Ask your AI for the rest: recording (highFps for full-rate demo video), annotation, and the gliding cursor.');- lines.push(`Close it when you are done: pup_close_window {"sessionId":"${sid}"}`);- }+ if (open._err) { lines.push(' could not open: ' + open._err); return lines; }+ lines.push(' open: ' + (open.sessionId || sid));++ lines.push('');+ lines.push('2. LOOK AT YOUR TASKBAR right now. The new button is not a generic Chrome icon: it is');+ lines.push(' the Adom teal tile wearing this thread\'s NAME ("pup dock demo"), with the live page');+ lines.push(' favicon badged in the corner. Every AI thread gets its own named button, so your');+ lines.push(' taskbar reads as a list of your threads. While the icon lands you will see a small');+ lines.push(' PROGRESS BAR under the button: pup times every operation and fills the bar over how');+ lines.push(' long it usually takes, instead of flashing orange at you.');+ await sleep(3500);++ lines.push('');+ lines.push('3. Screenshot, without touching your screen: the window renders in the background and');+ lines.push(' pup reads the pixels off-screen (watch the button, the progress bar runs again).');+ const shot = await ab('pup_screenshot', { sessionId: sid }, 60000, tgt.name);+ lines.push(shot._err ? ' screenshot failed: ' + shot._err : ' screenshot: ' + shot.width + 'x' + shot.height + ' (the AI can read any page without disturbing you)');++ lines.push('');+ lines.push('4. RIGHT-CLICK that taskbar button when you get a moment: pup installs a jump list on');+ lines.push(' every window: Annotate this page, Switch to logged-in/public view (wiki windows),');+ lines.push(' Open pup dashboard, Move to my workspace, Close ALL Adom Pup windows.');+ await sleep(2500);++ lines.push('');+ lines.push('5. The finale: ANNOTATION, live. Bringing the window up (with the on-screen caption');+ lines.push(' pup always shows before a foreground, naming who and why), then drawing on the page.');+ await ab('pup_raise_os_window', { sessionId: sid, foregroundReason: 'you clicked Run the demo on the pup dashboard: watch the annotation feature draw on the page' }, 30000, tgt.name);+ await sleep(1200);+ const arm = await ab('pup_annotate', { sessionId: sid, tool: 'pen' }, 30000, tgt.name);+ if (!arm._err) {+ await sleep(600);+ const draw = "(async()=>{const r=document.getElementById('__pup_annotate');if(!r)return 'no overlay';const cv=r.querySelector('canvas');if(!cv)return 'no canvas';const mk=(t,x,y)=>new PointerEvent(t,{bubbles:true,cancelable:true,clientX:x,clientY:y,pointerId:1,isPrimary:true,button:0,buttons:t==='pointerup'?0:1});const cx=Math.round(innerWidth*0.35),cy=Math.round(innerHeight*0.4),rad=Math.min(140,Math.round(innerHeight*0.18));cv.dispatchEvent(mk('pointerdown',cx+rad,cy));for(let a=0.25;a<=6.55;a+=0.22){cv.dispatchEvent(mk('pointermove',cx+rad*Math.cos(a),cy+rad*Math.sin(a)));await new Promise(q=>setTimeout(q,16));}cv.dispatchEvent(mk('pointerup',cx+rad,cy));return 'drew';})()";+ const drew = await ab('pup_evaluate', { sessionId: sid, expression: draw }, 30000, tgt.name);+ const drewOk = !drew._err && String(drew.result || '') === 'drew';+ if (drewOk) {+ lines.push(' A red circle was just drawn on the page. On the pen-up, pup captured the marked');+ lines.push(' region, put the PNG ON YOUR CLIPBOARD with a caption strip naming the thread and');+ lines.push(' page (paste it into any AI chat), and showed the confirmation toast.');+ } else lines.push(' annotation draw did not run: ' + (drew._err || drew.result || 'unknown'));+ } else lines.push(' annotate did not arm: ' + arm._err);+ await sleep(3500);+ await ab('pup_lower_os_window', { sessionId: sid }, 20000, tgt.name);+ lines.push('');+ lines.push('6. And back to the background, out of your way. The drawing fades once captured (the');+ lines.push(' capture is what persists, on your clipboard). The window stays open: right-click its');+ lines.push(' taskbar button to explore the jump list, or close it there (or say: close the pup');+ lines.push(' demo window).');+ lines.push('');+ lines.push('Not shown: recording a window to video (full frame rate in the background with highFps),');+ lines.push('the credential vault that signs into sites with no Chrome popups, and the gliding Adom');+ lines.push('cursor that narrates what the AI is doing. Say "show me in pup" to any Adom AI.'); return lines; }