← Commit history

Publish 2.0.319

John Lauer ·3aef75adf0 ·1mo ago ·parent b7b3b22
3 files changed +56−22
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "pup-bridge",-  "version": "2.0.318",+  "version": "2.0.319",   "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.318",+  "version": "2.0.319",   "tags": [     "skills",     "pup",
tools/pup-dashboard+54−20
@@ -350,50 +350,83 @@ async function demoTarget() { }  async function runDemo() {-  // 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.+  // v2.0.319 demo v4, all from John watching v3: (1) voice is adom-tts (neural, Adom+  // pronunciations), never the SAPI robot; (2) no caption-over-caption: the raise step lets the+  // foreground caption BE the narration; (3) a dedicated act drives the taskbar progress bar 0+  // to 100 and says it runs for the length of ANY verb or long process; (4) the three toolbar+  // extension tiles get introduced; (5) the Adom cursor is turned OFF at the end.   const lines = [];   const sid = 'pup-dock-demo';   const sleep = (ms) => new Promise(r => setTimeout(r, ms));   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();++  // Voice: adom-tts (edge neural voice + Adom pronunciation overrides + cache) through the shared+  // player; SAPI only if adom-tts is missing; captions always carry the words regardless.+  let voice = 'adom-tts';+  const speak = async (text) => {+    if (voice === 'off') return;+    if (voice === 'adom-tts') {+      const r = await new Promise((resolve) => {+        execFile('adom-tts', ['say', text, '--out', path.join(os.tmpdir(), 'pup-demo-line.mp3'), '--thread', 'pup demo', '--desc', 'pup demo narration: ' + text.slice(0, 60), '--play'],+          { env: ENV, timeout: 45000 }, (err) => resolve(!err));+      });+      if (r) { await sleep(Math.max(1500, text.split(/\s+/).length * 330)); return; }   // let the clip play out+      voice = 'sapi'; lines.push('   (adom-tts unavailable; falling back to the system voice)');+    }     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 r2 = await 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);+    if (r2._err) { voice = 'off'; lines.push('   (no voice on this machine; captions carry the demo)'); }   };   const cap = (text) => {     const ms = Math.min(12000, Math.max(2500, 900 + text.trim().split(/\s+/).length * 350));     lines.push('   [caption] ' + text);     return ab('desktop_caption', { text, duration: ms, id: 'pup-demo', position: 'bottom', size: 'medium' }, 8000, tgt.name).then(() => ms);   };-  // 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; };+  const clearCap = () => ab('desktop_caption', { text: ' ', duration: 300, id: 'pup-demo', position: 'bottom', size: 'medium' }, 8000, tgt.name).catch(() => {});+  const say = async (text, extra) => { await Promise.all([cap(text), speak(text)]); await sleep(extra || 400); }; -  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('Hi, this is the pup demo. I am an A I 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 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 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);+  // THE PROGRESS BAR, driven visibly (John: "show how you drive the progress bar... explain you do+  // that for the length of any verb or long running process").+  const wi = await ab('pup_window_info', { sessionId: sid }, 20000, tgt.name);+  const hwnd = wi && wi.hwnd;+  if (hwnd) {+    await say('Now watch that button closely. I am driving its PROGRESS BAR by hand so you can see it: pup paints one of these under the button for the length of ANY operation, a screenshot, an icon apply, a page load, any long-running verb, sized to how long that operation usually takes.', 0);+    for (const v of [12, 30, 48, 66, 82, 95, 100]) { await ab('desktop_taskbar', { hwnd, progress: { state: 'normal', value: v } }, 8000, tgt.name); await sleep(700); }+    await sleep(400);+    await ab('desktop_taskbar', { hwnd, progress: { state: 'none' } }, 8000, tgt.name);+    await say('And done: the bar fills, completes, and clears. That is how you always know when I am working on a window and roughly how far along I am. No orange flashing at you.');+  } else lines.push('   (no hwnd resolved yet; skipped the manual progress sweep)');++  // The raise: ONE caption only (the foreground caption IS the narration; no demo caption under it).+  await clearCap();+  const raiseWhy = 'the pup demo you started: bringing the window up so you can watch me drive it';+  await Promise.all([+    speak('You started this demo, so let me bring the window up where you can see it. pup always shows a caption first, saying who is foregrounding a window and why. Here it comes.'),+    (async () => { lines.push('   [caption via raise] ' + raiseWhy); })(),+  ]);+  await ab('pup_raise_os_window', { sessionId: sid, foregroundReason: raiseWhy }, 30000, tgt.name);+  await sleep(900);   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 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 A I is doing.');++  // The three toolbar tiles (John: "you should have shown the 3 browser extensions").+  await ab('pup_hover', { sessionId: sid, x: 1180, y: 8, intent: 'look UP at the toolbar, right of the address bar', steps: 14 }, 20000, tgt.name).catch(() => {});+  await say('Now look at the TOP RIGHT of this window, next to the address bar: three teal tiles. The pup dots are my menu: annotate, snip, switch wiki views, open my dashboard. The pencil starts the annotation pen with one click. And the third tile is your adom you identity: your avatar means this window is signed in as you; windows that are NOT you wear an anonymous mark instead.', 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.');+  await say('Taking a screenshot now. Watch the taskbar button: the same progress bar runs on its own 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 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 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';@@ -404,10 +437,11 @@ async function runDemo() {   } 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.');+  // cursor OFF before leaving (John: "you never turned the cursor off")+  await ab('pup_evaluate', { sessionId: sid, expression: "['__adom_cursor__','__pupCursor','__adom_cursor_ring__','__adom_cursor_label__'].forEach(id=>{const e=document.getElementById(id);if(e)e.remove();});'cursor off'" }, 15000, tgt.name).catch(() => {});+  await say('And now I get out of your way: cursor off, back to the background. That is pup. Say, show me in pup, to any Adom A I.');   await ab('pup_lower_os_window', { sessionId: sid }, 20000, tgt.name);   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; }