← Commit history

pup_floaty_click, the test seam John asked for: a first-class verb that simulates a user click on the floaty pencil by firing the same host click handler a real mouse does, so the flag, sweep harvest, and annotate start are all exercised as the production chain. Built specifically so the floaty can be ralph-tested weekly forever without CDP input-simulation flakiness, with honest errors when the floaty is absent

John Lauer ·8a0b75af11 ·2mo ago ·parent 80ecd05
4 files changed +31−8
adom-bridge-puppeteer-manifest.json+4−4
@@ -1,10 +1,10 @@ {   "manifest_version": 1,   "name": "puppeteer",-  "version": "2.0.68",-  "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.68/adom-bridge-puppeteer-v2.0.68.zip",-  "sha256": "149641d0ac7e7f941e63f5ff4062b4e3eed8b36224bee7fab49912febb4ad09f",-  "size": 832549,+  "version": "2.0.69",+  "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.69/adom-bridge-puppeteer-v2.0.69.zip",+  "sha256": "a221158aa8d28eaba711f1c9dcfb8dcbacb8b526a06f24f028b13d398d571829",+  "size": 833198,   "verbPrefixes": [     "browser_",     "desktop_recorder_",
src/BRIDGE_VERSION+1−1
@@ -1 +1 @@-2.0.68+2.0.69
src/bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "puppeteer",   "displayName": "Puppeteer (Chrome for Testing + Native)",-  "version": "2.0.68",+  "version": "2.0.69",   "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/server.js+25−2
@@ -4221,7 +4221,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([-  'pup_annotate', 'pup_list_windows', 'pup_list_tabs', 'pup_readiness', 'pup_status', 'pup_maximize', 'pup_hover', 'pup_deps', 'pup_dashboard',+  'pup_annotate', 'pup_floaty_click', 'pup_list_windows', 'pup_list_tabs', 'pup_readiness', 'pup_status', 'pup_maximize', 'pup_hover', 'pup_deps', 'pup_dashboard',   'pup_describe', 'pup_configure', 'pup_prewarm', 'pup_rescan', 'pup_wait', 'pup_import_scan', 'pup_set_identity', ]); @@ -7564,7 +7564,7 @@ async function enumAllPupChromeHwnds() { // one session. With sessionId omitted these resolve to the CALLER THREAD's own window (the same // ai-<thread-slug> keying pup_open_window uses) — never to activeSessionId, which is just whichever // window was touched last, usually another thread's.-const SESSION_TARGETING_VERBS = new Set(['pup_navigate', 'pup_reload', 'pup_back', 'pup_forward', 'pup_open_tab', 'pup_switch_tab', 'pup_close_tab', 'pup_close_window', 'pup_eval', 'pup_evaluate', 'pup_click', 'pup_type', 'pup_press_key', 'pup_input_dispatch', 'pup_screenshot', 'pup_screenshot_full_res', 'pup_errors', 'pup_list_tabs', 'pup_wait', 'pup_alert_window', 'pup_focus_window', 'pup_raise_os_window', 'pup_lower_os_window', 'pup_window_info', 'pup_record_start', 'pup_record_stop', 'pup_wiki_set_view', 'pup_switch_window', 'pup_annotate']);+const SESSION_TARGETING_VERBS = new Set(['pup_navigate', 'pup_reload', 'pup_back', 'pup_forward', 'pup_open_tab', 'pup_switch_tab', 'pup_close_tab', 'pup_close_window', 'pup_eval', 'pup_evaluate', 'pup_click', 'pup_type', 'pup_press_key', 'pup_input_dispatch', 'pup_screenshot', 'pup_screenshot_full_res', 'pup_errors', 'pup_list_tabs', 'pup_wait', 'pup_alert_window', 'pup_focus_window', 'pup_raise_os_window', 'pup_lower_os_window', 'pup_window_info', 'pup_record_start', 'pup_record_stop', 'pup_wiki_set_view', 'pup_switch_window', 'pup_annotate', 'pup_floaty_click']); function threadDefaultSessionId(callerThread) {   return 'ai-' + String(callerThread).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 40); }@@ -11078,6 +11078,7 @@ const server = http.createServer(async (req, res) => {             V('pup_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. pup_assisted_click already does all of this — use it. And ALWAYS prefer CDP input (pup_click/pup_type/pup_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('pup_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 pup_status / pup_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('pup_install_toolbar', 'Status/trigger for the toolbar extension, which AUTO-INSTALLS invisibly at every profile launch (transient pipe-launch + CDP Extensions.loadUnpacked; persists in the profile). Running profiles pick it up on their next launch; cold profiles install immediately.', { profile: 'optional, default adom-you' }, 30, {}),+            V('pup_floaty_click', 'TEST SEAM: simulate a user click on the floaty pencil (fires the same host click handler a mouse does; the sweep then starts annotate within ~4s). Built for the recurring floaty ralph — assert the annotate overlay appears after calling this.', { sessionId: 'required window', tabId: 'optional tab' }, 10, {}),             V('pup_annotate', 'Start the HUMAN annotation overlay on a window\'s active tab (pen/arrow/box/text toolbar injected in-page; also on every window\'s taskbar jump list as "Annotate this page"). Done captures a PNG AND leaves the drawing in the DOM, so the owning thread\'s next pup_screenshot shows exactly what the user marked — check session.lastAnnotation via pup_list_windows. action:"cancel" removes the overlay.', { sessionId: 'required window', action: 'optional "cancel"' }, 15, {}),             V('pup_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('pup_deps', 'Report pup dependency health (wiki issue #15): depsPresent, selfInstalledThisBoot, lastError. Use this to answer "is pup broken?" without reading spawn logs.', { }, 10, {}),@@ -14122,6 +14123,28 @@ const server = http.createServer(async (req, res) => {           }) });           return;         }+        case 'pup_floaty_click': {+          // v2.0.69 (John: "tool the clicking so we can ralph this weekly forever"): first-class+          // simulated click on the floaty pencil. Fires the SAME host click handler a mouse does+          // (host.click() -> onclick -> flag -> sweep harvest -> annotate), so the ralph exercises+          // the full production chain without CDP input-simulation flakiness. Honest errors: floaty+          // absent means the setting is off or the page never got the injection.+          const sessionId = args.sessionId;+          if (!sessionId || !sessions.has(sessionId)) { sendJSON(res, { success: false, error: `Session "${sessionId}" not found` }); return; }+          const session = sessions.get(sessionId);+          const tab = (args.tabId && (session.tabs || []).find(t => t.tabId === args.tabId)) || (session.activeTabId && (session.tabs || []).find(t => t.tabId === session.activeTabId)) || (session.tabs || [])[0];+          if (!tab || !tab.page) { sendJSON(res, { success: false, error: 'no tab' }); return; }+          try {+            const r = await tab.page.evaluate(() => {+              const h = document.getElementById('__pup_floaty');+              if (!h) return { clicked: false, reason: 'floaty not present (tbFloatyPencil off, or page not injected)' };+              h.click();+              return { clicked: true, flagSet: !!window.__pupFloatyClick };+            });+            sendJSON(res, { success: !!r.clicked, output: JSON.stringify(r), ...(r.clicked ? {} : { error: r.reason }) });+          } catch (e) { sendJSON(res, { success: false, error: e.message }); }+          return;+        }         case 'pup_annotate': {           // start/cancel the human annotation overlay on a window's active tab. Jump-list clicks           // arrive as user-taskbar-menu (ownership-gate exempt); threads annotate their OWN windows.