← Commit history

Publish 2.0.318

John Lauer ·7f59fbe1d0 ·1mo ago ·parent 968d96a
3 files changed +54−37
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "pup-bridge",-  "version": "2.0.317",+  "version": "2.0.318",   "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.317",+  "version": "2.0.318",   "tags": [     "skills",     "pup",
tools/pup-dashboard+52−35
@@ -333,64 +333,81 @@ load(); setInterval(load,15000); </script>`;  // ── demo ────────────────────────────────────────────────────────────────────────────────────────+async function demoTarget() {+  // v2.0.318 (John ran the demo and saw NOTHING: with two desktops connected the demo drove the+  // FIRST target, which was the other machine). The user is at the desktop they just clicked+  // "Run the demo" on, and that machine's input-idle is seconds while the other's is minutes:+  // pick the target with the LOWEST desktop_user_idle_ms.+  const t = await resolveTarget();+  if (!t.all || t.all.length <= 1) return t;+  const idles = await Promise.all(t.all.map(async (name) => {+    const r = await ab('desktop_user_idle_ms', {}, 6000, name);+    const ms = Number(r.idleMs != null ? r.idleMs : (r.ms != null ? r.ms : NaN));+    return { name, idle: Number.isFinite(ms) ? ms : Infinity };+  }));+  idles.sort((a, b) => a.idle - b.idle);+  return { name: idles[0].name, all: t.all, at: Date.now(), idles };+}+ async function runDemo() {-  // The dock-card showcase. v2.0.317 (John: "you lost me in that demo. i had no idea what was going-  // on... show non-stop ab captions step by step, take over the cursor so i see you moving"): the-  // narration lives ON THE DESKTOP now. Every act announces itself with an on-screen caption BEFORE-  // it happens (duration scaled to reading speed), the foreground act glides the visible Adom cursor-  // with intent labels, and the drawing is slow enough to watch. The console lines mirror the-  // captions for the record; the show itself is on screen.+  // v2.0.318 (John: "be more in-your-face: bring the pup window to foreground, show more ab+  // captions, maybe even do some text to speech"). The demo now plays on the desktop the user is+  // actually AT (lowest input idle), narrates every act with an on-screen caption AND speaks it+  // (Windows SAPI through ab), and brings the window up early since the user asked for a show.   const lines = [];   const sid = 'pup-dock-demo';   const sleep = (ms) => new Promise(r => setTimeout(r, ms));-  const tgt = await resolveTarget();-  const cap = async (text) => {+  const tgt = await demoTarget();+  lines.push('Playing on desktop: ' + tgt.name + (tgt.idles ? ' (you are at it: idle ' + Math.round(tgt.idles[0].idle / 1000) + 's; ' + tgt.idles.slice(1).map(i => i.name + ' idle ' + Math.round(i.idle / 1000) + 's').join(', ') + ')' : ''));+  let ttsOk = true;+  const speak = (text) => {+    if (!ttsOk) return Promise.resolve();+    const safe = text.replace(/[`$"\\]/g, ' ').replace(/'/g, "''");+    return ab('desktop_run_script', { language: 'powershell', body: "Add-Type -AssemblyName System.Speech; $v = New-Object System.Speech.Synthesis.SpeechSynthesizer; $v.Rate = 1; $v.Speak('" + safe + "')", timeoutSeconds: 30 }, 35000, tgt.name).then(r => { if (r._err) ttsOk = false; });+  };+  const cap = (text) => {     const ms = Math.min(12000, Math.max(2500, 900 + text.trim().split(/\s+/).length * 350));-    await ab('desktop_caption', { text, duration: ms, id: 'pup-demo', position: 'bottom', size: 'medium' }, 8000, tgt.name);     lines.push('   [caption] ' + text);-    return ms;+    return ab('desktop_caption', { text, duration: ms, id: 'pup-demo', position: 'bottom', size: 'medium' }, 8000, tgt.name).then(() => ms);   };-  const capWait = async (text, extra) => { const ms = await cap(text); await sleep(ms + (extra || 0)); };+  // caption AND voice together; the spoken line paces the step (speech is slower than reading)+  const say = async (text, extra) => { const [ms] = await Promise.all([cap(text), speak(text)]); await sleep(extra || 400); return ms; }; -  lines.push('The demo narrates ON YOUR SCREEN with captions, step by step. Watch the desktop.');-  await capWait('pup demo, step 1 of 6: I am opening a browser window IN THE BACKGROUND. Watch your TASKBAR, not your screen. Nothing will pop over your work.', 500);+  await say('Hi, this is the pup demo. I am an AI browser that lives on your desktop. Watch, I will narrate everything I do.');+  await say('First: I am opening a browser window in the background. Nothing pops over your work, ever. Look at your taskbar: a new teal button just appeared wearing my thread name, pup dock demo, with the page icon badged on its corner.', 200);   const open = await ab('pup_open_window', { sessionId: sid, url: 'https://wiki.adom.inc/adom/pup-bridge', owner: 'pup dock demo' }, 120000, tgt.name);-  if (open._err) { await cap('The demo could not open a window: ' + open._err); lines.push('   could not open: ' + open._err); return lines; }+  if (open._err) { await say('I could not open a window: ' + open._err); return lines; }   lines.push('   open: ' + (open.sessionId || sid));+  await say('The little bar filling under that button is my progress signal. I time every operation and fill the bar over how long it usually takes. No orange flashing at you.'); -  await capWait('Step 2: look at the NEW TASKBAR BUTTON at the bottom of your screen. It is a teal tile wearing this thread\'s name, "pup dock demo", with the page favicon badged on its corner. Every AI thread gets its own named button.', 1000);-  await capWait('The small bar FILLING UNDER that button is pup\'s progress signal: it times every operation and fills over how long it usually takes. No orange flashing.', 800);+  await say('You started this demo, so let me bring the window up where you can see it. I always show a caption first saying who is foregrounding and why, like the one coming now.');+  await ab('pup_raise_os_window', { sessionId: sid, foregroundReason: 'the pup demo you started: watch me drive this window in front of you' }, 30000, tgt.name);+  await sleep(700);+  await ab('pup_hover', { sessionId: sid, x: 700, y: 300, intent: 'this is my cursor: it glides and narrates my intent', steps: 16 }, 20000, tgt.name);+  await say('That teal cursor is me. It glides to wherever I am about to act and says why, so you can always tell what your AI is doing.'); -  await capWait('Step 3: I am taking a SCREENSHOT of that background window right now. Watch the progress bar run under its button. Your screen is not touched: the page renders off-screen.', 300);+  await say('Taking a screenshot now. Watch the taskbar button: the progress bar runs while I capture. I can do this even when the window is buried in the background.');   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); -  await capWait('Step 4: that button also carries a RIGHT-CLICK menu. Try it later: Annotate this page, switch wiki views, open the pup dashboard, move the page to your workspace, close all pup windows.', 500);--  await capWait('Step 5, the finale: I am about to bring the window UP ON YOUR SCREEN to demonstrate annotation. pup always captions a foreground with who is doing it and why, like this.', 500);-  await ab('pup_raise_os_window', { sessionId: sid, foregroundReason: 'the pup demo you started: watch the annotation feature draw on this page' }, 30000, tgt.name);-  await sleep(800);-  // glide the visible Adom cursor so the eye lands where the action will be-  await ab('pup_hover', { sessionId: sid, x: 700, y: 380, intent: 'the demo is about to draw HERE', steps: 14 }, 20000, tgt.name);-  await sleep(600);+  await say('Now the annotation pen. I will draw on this live page, slowly, in red. Watch the circle.');   const arm = await ab('pup_annotate', { sessionId: sid, tool: 'pen' }, 30000, tgt.name);   if (!arm._err) {-    await cap('Drawing on the live page with the annotation pen now. Watch the red ink.');-    await sleep(400);+    await ab('pup_hover', { sessionId: sid, x: 620, y: 380, intent: 'drawing here', steps: 10 }, 20000, tgt.name).catch(() => {});     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(150,Math.round(innerHeight*0.2));cv.dispatchEvent(mk('pointerdown',cx+rad,cy));for(let a=0.12;a<=6.55;a+=0.12){cv.dispatchEvent(mk('pointermove',cx+rad*Math.cos(a),cy+rad*Math.sin(a)));await new Promise(q=>setTimeout(q,55));}cv.dispatchEvent(mk('pointerup',cx+rad,cy));return 'drew';})()";     const drew = await ab('pup_evaluate', { sessionId: sid, expression: draw }, 40000, tgt.name);     const drewOk = !drew._err && String(drew.result || '') === 'drew';     if (drewOk) {       lines.push('   drew the red circle live');-      await capWait('The pen-up CAPTURED the circled region: it is on your CLIPBOARD right now, with a caption strip naming the thread and the page. Paste it into any AI chat. The ink fades; the capture persists.', 1200);-    } else { lines.push('   annotation draw did not run: ' + (drew._err || drew.result || 'unknown')); await cap('The drawing step did not run on this page; the rest of the demo continues.'); }-  } else { lines.push('   annotate did not arm: ' + arm._err); await cap('The annotation overlay did not arm on this page; the rest of the demo continues.'); }-  await capWait('Step 6: and back to the BACKGROUND, out of your way. The window stays open. That is pup: real browser windows an AI drives without ever stealing your screen.', 300);+      await say('Done. The moment the pen lifted, I captured the circled region and put it on your clipboard, with a caption strip naming this thread and page. Paste it anywhere. The ink fades; the capture is yours.');+    } else { lines.push('   draw did not run: ' + (drew._err || drew.result || 'unknown')); await say('The drawing step did not run on this page, so I will move on.'); }+  } else { lines.push('   annotate did not arm: ' + arm._err); await say('The annotation overlay did not arm on this page, so I will move on.'); }++  await say('One more thing: right-click my taskbar button any time for the menu: annotate this page, switch wiki views, open my dashboard, or close every pup window at once.');+  await say('And now I get out of your way: back to the background. That is pup. Say, show me in pup, to any Adom AI.');   await ab('pup_lower_os_window', { sessionId: sid }, 20000, tgt.name);-  await cap('pup demo done. Right-click the "pup dock demo" taskbar button to explore, or say: close the pup demo window.');-  lines.push('');-  lines.push('Not shown: window recording (full frame rate in the background with highFps), the');-  lines.push('credential vault that signs into sites with no Chrome popups. Say "show me in pup".');+  await cap('pup demo done. The window stays open in the background; close it from its right-click menu, or say: close the pup demo window.');+  if (!ttsOk) lines.push('   (voice narration unavailable on this machine; captions carried the demo)');   return lines; }