← Commit history

Show the user's Adom identity in the floating owner chip: since Chrome's own profile chip cannot take a custom local image (Google sign-in only), the owner chip now leads with the user's Adom avatar + name. New verb browser_set_identity {avatarB64,name,enabled,clear} stores the identity in pup-settings and repaints live windows. Owner-chip injection (attachTab) and the live-apply path both render the round avatar; falls back to the hexagon glyph when no identity is set.

John Lauer ·770fbf9ae8 ·2mo ago ·parent 2ed43c1
4 files changed +57−15
adom-bridge-puppeteer-manifest.json+4−4
@@ -1,10 +1,10 @@ {   "manifest_version": 1,   "name": "puppeteer",-  "version": "1.9.342",-  "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.342/adom-bridge-puppeteer-v1.9.342.zip",-  "sha256": "7f8b46100f9063605b37e31361926d8fa2c52eb939a71c786431bdeb5fe0144f",-  "size": 633534,+  "version": "1.9.343",+  "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.343/adom-bridge-puppeteer-v1.9.343.zip",+  "sha256": "ae8cb82b984c702cb86b7db1283b485fd0fe7a5ec0b1f62ec12ee77f5d5083a9",+  "size": 635255,   "verbPrefixes": [     "browser_",     "desktop_recorder_",
src/BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.9.342+1.9.343
src/bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "puppeteer",   "displayName": "Puppeteer (Chrome for Testing + Native)",-  "version": "1.9.342",+  "version": "1.9.343",   "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. browser_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. browser_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, browser_alert_window posts a native toast whose click focuses the window, browser_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-desktop",   "author": "Adom Inc.",
src/server.js+51−9
@@ -633,7 +633,14 @@ function attachTab(session, sessionId, page, { makeActive = true, opener = 'user     if (pupSettings().ownerChip === 'on') {       const _threadDisp = String(sessionId).replace(/^ai-/, '');       const _prof = (session && session.profileName) || 'adom-you';-      page.evaluateOnNewDocument((thread, profile) => {+      // v1.9.343 (John: "put my Adom profile picture in there — show my adom identity"). Chrome's own+      // profile chip cannot take a custom local image (only a real Google sign-in can — tested), so we+      // show the user's Adom identity HERE instead: the owner chip leads with the user's Adom avatar+      // (set via browser_set_identity → fetched, circle-cropped, embedded as a data URI in settings).+      // Falls back to the ⬢ glyph when no identity is configured.+      const _avatar = pupSettings().identityAvatarB64 || '';+      const _ident = pupSettings().identityName || '';+      page.evaluateOnNewDocument((thread, profile, avatar, ident) => {         try {           const add = () => {             try {@@ -643,8 +650,11 @@ function attachTab(session, sessionId, page, { makeActive = true, opener = 'user               host.setAttribute('data-pup-owner', '1');               host.style.cssText = 'position:fixed;left:8px;bottom:8px;z-index:2147483647;pointer-events:none;margin:0;padding:0;';               const sh = host.attachShadow ? host.attachShadow({ mode: 'closed' }) : null;-              const html = '<style>.c{font:11px/1.35 "JetBrains Mono",ui-monospace,SFMono-Regular,monospace;color:#7fe9e2;background:rgba(13,17,23,.72);border:1px solid rgba(0,184,177,.45);border-radius:6px;padding:2px 8px;white-space:nowrap;opacity:.9;transition:opacity .5s ease;user-select:none}.c b{color:#e6edf3;font-weight:600}.dim{opacity:.28}</style>'-                + '<div class="c" id="pupc">⬢ <b>' + String(thread) + '</b> · ' + String(profile) + '</div>';+              const lead = avatar+                ? '<img class="av" alt="" src="data:image/png;base64,' + avatar + '">'+                : '<span class="hx">⬢</span>';+              const html = '<style>.c{display:flex;align-items:center;gap:6px;font:11px/1.35 "JetBrains Mono",ui-monospace,SFMono-Regular,monospace;color:#7fe9e2;background:rgba(13,17,23,.72);border:1px solid rgba(0,184,177,.45);border-radius:13px;padding:2px 9px 2px 3px;white-space:nowrap;opacity:.92;transition:opacity .5s ease;user-select:none}.c b{color:#e6edf3;font-weight:600}.av{width:20px;height:20px;border-radius:50%;object-fit:cover;flex:0 0 auto;border:1px solid rgba(0,184,177,.55)}.hx{padding-left:5px}.dim{opacity:.3}</style>'+                + '<div class="c" id="pupc" title="' + (ident ? String(ident) + ' — ' : '') + 'Adom pup">' + lead + '<span><b>' + String(thread) + '</b> · ' + String(profile) + '</span></div>';               if (sh) { sh.innerHTML = html; } else { host.innerHTML = html; }               root.appendChild(host);               const el = sh ? sh.getElementById('pupc') : host.querySelector('#pupc');@@ -654,7 +664,7 @@ function attachTab(session, sessionId, page, { makeActive = true, opener = 'user           if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', add, { once: true });           else add();         } catch (e) {}-      }, _threadDisp, _prof).catch(() => {});+      }, _threadDisp, _prof, _avatar, _ident).catch(() => {});     }   } catch (e) {}   // v1.8.21: install the shadow-DOM + scroll helpers on every document so browser_eval@@ -3950,7 +3960,7 @@ function clearSessionBusy(sessionId) { // the bar for them would make every window look permanently busy (the audit loops alone poll these). const PASSIVE_VERBS = new Set([   'browser_list_windows', 'browser_list_tabs', 'browser_readiness', 'browser_status', 'browser_maximize', 'browser_hover', 'browser_deps', 'browser_dashboard',-  'browser_describe', 'browser_configure', 'browser_prewarm', 'browser_rescan', 'browser_wait', 'browser_import_scan',+  'browser_describe', 'browser_configure', 'browser_prewarm', 'browser_rescan', 'browser_wait', 'browser_import_scan', 'browser_set_identity', ]);  // v1.9.58 — THUMBNAIL TOOLTIP (AD >= 1.9.152: desktop_taskbar { thumbnailTooltip }).@@ -5574,18 +5584,21 @@ async function applyOwnerChipSetting(on) {         } else {           const thread = String(sid).replace(/^ai-/, '');           const prof = s.profileName || 'adom-you';-          await t.page.evaluate((thread2, profile) => {+          const avatar = pupSettings().identityAvatarB64 || '';+          const ident = pupSettings().identityName || '';+          await t.page.evaluate((thread2, profile, avatar2, ident2) => {             try {               const root = document.documentElement; if (!root || root.querySelector('div[data-pup-owner]')) return;               const host = document.createElement('div'); host.setAttribute('data-pup-owner', '1');               host.style.cssText = 'position:fixed;left:8px;bottom:8px;z-index:2147483647;pointer-events:none;margin:0;padding:0;';               const sh = host.attachShadow ? host.attachShadow({ mode: 'closed' }) : null;-              const html = '<style>.c{font:11px/1.35 "JetBrains Mono",ui-monospace,monospace;color:#7fe9e2;background:rgba(13,17,23,.72);border:1px solid rgba(0,184,177,.45);border-radius:6px;padding:2px 8px;white-space:nowrap;opacity:.9;user-select:none}.c b{color:#e6edf3;font-weight:600}</style>'-                + '<div class="c">⬢ <b>' + thread2 + '</b> · ' + profile + '</div>';+              const lead = avatar2 ? '<img class="av" alt="" src="data:image/png;base64,' + avatar2 + '">' : '<span class="hx">⬢</span>';+              const html = '<style>.c{display:flex;align-items:center;gap:6px;font:11px/1.35 "JetBrains Mono",ui-monospace,monospace;color:#7fe9e2;background:rgba(13,17,23,.72);border:1px solid rgba(0,184,177,.45);border-radius:13px;padding:2px 9px 2px 3px;white-space:nowrap;opacity:.92;user-select:none}.c b{color:#e6edf3;font-weight:600}.av{width:20px;height:20px;border-radius:50%;object-fit:cover;flex:0 0 auto;border:1px solid rgba(0,184,177,.55)}.hx{padding-left:5px}</style>'+                + '<div class="c" title="' + (ident2 ? ident2 + ' — ' : '') + 'Adom pup">' + lead + '<span><b>' + thread2 + '</b> · ' + profile + '</span></div>';               if (sh) sh.innerHTML = html; else host.innerHTML = html;               root.appendChild(host);             } catch (e) {}-          }, thread, prof);+          }, thread, prof, avatar, ident);         }       } catch (e) {}     }@@ -10349,6 +10362,7 @@ const server = http.createServer(async (req, res) => {             V('browser_deps', 'Report pup dependency health (wiki issue #15): depsPresent, selfInstalledThisBoot, lastError. Use this to answer "is pup broken?" without reading spawn logs.', { }, 10, {}),             V('browser_hover', 'Move the pointer over a target WITHOUT clicking (reveals tooltips / hover menus). Glides the visible Adom cursor there too.', { sessionId: 'required str', selector: 'optional str', x: 'optional int', y: 'optional int', intent: 'optional str — narrated on the cursor label', steps: 'optional int (default 8)' }, 15, { sessionId: 'work', selector: '#menu' }),             V('browser_maximize', 'Maximize (or restore/fullscreen/minimize) a pup window via CDP — no AD desktop verb required. Does NOT foreground it.', { sessionId: 'required str', state: 'optional "maximized"|"normal"|"minimized"|"fullscreen" (default maximized)' }, 15, { sessionId: 'work' }),+            V('browser_set_identity', 'Set the Adom identity shown in the floating owner chip on every pup window (avatar + name). Chrome\'s own profile chip cannot take a custom local image (Google sign-in only), so the user\'s Adom avatar lives here instead. Pass a pre-circle-cropped PNG as base64. Applies to live windows immediately + persists.', { avatarB64: 'optional str — base64 PNG (ideally ~48px, circle-cropped); the leading data: prefix is stripped if present', name: 'optional str — display name shown in the chip tooltip', enabled: 'optional bool — turn the whole owner chip on/off', clear: 'optional bool — remove the stored avatar + name' }, 15, { name: 'John Lauer', avatarB64: '<base64 png>' }),             V('browser_screenshot', 'Screenshot the page (PNG; fullPage optional). NOTE: fullPage only covers DOCUMENT-level scroll — on SPAs that scroll inside a nested container it returns just the viewport + a nestedScroll hint; use browser_screenshot_element or browser_scroll for those.', { sessionId: 'required str', fullPage: 'optional bool' }, 30, { sessionId: 'work' }),             V('browser_screenshot_element', 'Screenshot ONE element, resolved by CSS selector OR visible text (pierces shadow DOM), scrolled into view first. The clean way to grab a single card/widget on a component-heavy page. Text matches the TIGHTEST text node — to capture the enclosing CARD, add closest (e.g. {text:"Download…", closest:".download-card"}); with closest, an ambiguous phrase still lands on the real widget (skips prose that has no such ancestor). Returns base64 + filePath like browser_screenshot.', { sessionId: 'required str', selector: 'CSS selector (or use text)', text: 'visible text to match (or use selector)', closest: 'optional selector — climb to the nearest ancestor matching it (the card container)', padding: 'optional int px around the element', deep: 'optional bool — pierce shadow DOM (default true)' }, 30, { sessionId: 'work', selector: '.download-card' }),             V('browser_scroll', 'Scroll the ACTUAL scrolling element (nearest ancestor whose scrollHeight>clientHeight, NOT just window — window.scrollTo no-ops on nested-scroll SPAs). Scroll by delta {dy,dx} OR to an element {toSelector|toText} (pierces shadow DOM). Returns {scrollElement, scrollTop, scrollHeight, clientHeight} so you can confirm it landed.', { sessionId: 'required str', dy: 'optional int px (down+/up-)', dx: 'optional int px', toSelector: 'optional CSS selector to scroll to', toText: 'optional visible text to scroll to', block: 'optional start|center|end (for scroll-to; default center)', behavior: 'optional auto|smooth' }, 30, { sessionId: 'work', toText: 'Download' }),@@ -12225,6 +12239,34 @@ const server = http.createServer(async (req, res) => {           }           return;         }+        case 'browser_set_identity': {+          // v1.9.343 (John: "put my Adom profile picture in there — show my adom identity"). Chrome's+          // own profile chip refuses a custom local image (only a real Google sign-in gets one), so we+          // show the user's Adom identity in the floating owner chip instead. This verb stores the+          // avatar (a pre-circle-cropped PNG as base64, so the bridge stays image-lib-free here) + a+          // display name into pup-settings, and repaints every live window's chip so it shows at once.+          try {+            const patch = {};+            if (args.clear) { patch.identityAvatarB64 = ''; patch.identityName = ''; }+            if (typeof args.avatarB64 === 'string') patch.identityAvatarB64 = args.avatarB64.replace(/^data:image\/\w+;base64,/, '');+            if (typeof args.name === 'string') patch.identityName = args.name;+            if (args.enabled === true || args.enabled === false) patch.ownerChip = args.enabled ? 'on' : 'off';+            savePupSettings(patch);+            // Repaint live windows: remove the current chip then re-add with the new identity.+            try { await applyOwnerChipSetting(false); } catch (e) {}+            if (pupSettings().ownerChip === 'on') { try { await applyOwnerChipSetting(true); } catch (e) {} }+            const s = pupSettings();+            sendJSON(res, { success: true, output: JSON.stringify({ ok: true,+              identityName: s.identityName || null,+              hasAvatar: !!s.identityAvatarB64,+              avatarBytes: s.identityAvatarB64 ? Buffer.from(s.identityAvatarB64, 'base64').length : 0,+              ownerChip: s.ownerChip,+              _hint: 'Adom identity set. It shows as the avatar leading the floating owner chip (bottom-left) on every pup window, applied live. Chrome\'s own profile chip cannot take a custom local image (Google sign-in only), so this is where your Adom identity lives. Turn the chip off with ownerChip:off or {enabled:false}; clear the identity with {clear:true}.' }) });+          } catch (e) {+            sendJSON(res, { success: false, errorCode: 'set_identity_failed', error: `browser_set_identity failed: ${e.message}` });+          }+          return;+        }         case 'browser_maximize': {           // v1.9.197 GAP 6 (Bills thread): honouring "pup windows must be maximized" required           // browser_eval + window.resizeTo(...) because desktop_set_window_state is not in every