← Commit history

Deterministic toolbar icon order via the real control: the extensions.pinned_extensions ARRAY in Preferences (not load order, not pin-call order — both proven not to control it, John's hunch was close: it is an order, just the pinned array). Each Adom extension now carries a fixed manifest key so its ID is stable and known (ADOM_EXT_IDS), and prepareCleanProfile writes pinned_extensions pre-launch in the canonical order Pup, Annotate, Identity so the profile tile lands rightmost next to Chrome's own profile avatar, preserving any foreign pinned ids before our block. The unpin-then-repin dance is removed (it could not control order). Extensions bumped (toolbar 0.2.9, annotate/identity 0.1.1, anon 0.1.2).

John Lauer ·7992942445 ·2mo ago ·parent a301293
8 files changed +76−43
adom-bridge-puppeteer-manifest.json+4−4
@@ -1,10 +1,10 @@ {   "manifest_version": 1,   "name": "puppeteer",-  "version": "2.0.149",-  "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.149/adom-bridge-puppeteer-v2.0.149.zip",-  "sha256": "a9ef1818d5705c72cfcda817c734b8af7e62f4c2e1a5198cb893f4b6456def88",-  "size": 899799,+  "version": "2.0.150",+  "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.150/adom-bridge-puppeteer-v2.0.150.zip",+  "sha256": "d5a91cc751474776e71398622c2c437105a442d4025896f88b6433ab2738a802",+  "size": 901662,   "verbPrefixes": [     "browser_",     "desktop_recorder_",
src/BRIDGE_VERSION+1−1
@@ -1 +1 @@-2.0.149+2.0.150
src/bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "puppeteer",   "displayName": "Puppeteer (Chrome for Testing + Native)",-  "version": "2.0.149",+  "version": "2.0.150",   "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/extension-annotate/manifest.json+18−6
@@ -1,10 +1,22 @@ {   "manifest_version": 3,   "name": "Adom Annotate",-  "version": "0.1.0",+  "version": "0.1.1",   "description": "One-click annotate: draw on this pup page, red circle it, and the capture lands on your clipboard.",-  "action": { "default_popup": "go.html", "default_title": "Annotate this page (Alt+Shift+A)" },-  "permissions": ["tabs"],-  "host_permissions": ["http://127.0.0.1/*"],-  "icons": { "16": "icon16.png", "48": "icon48.png", "128": "icon128.png" }-}+  "action": {+    "default_popup": "go.html",+    "default_title": "Annotate this page (Alt+Shift+A)"+  },+  "permissions": [+    "tabs"+  ],+  "host_permissions": [+    "http://127.0.0.1/*"+  ],+  "icons": {+    "16": "icon16.png",+    "48": "icon48.png",+    "128": "icon128.png"+  },+  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnu2AgCLzhN5MRAPCDTr+6p8oUQAgZYmrWBv+Po0s2t7XeyYd6b68IYQntEzaPtmtAorM1eeizLTWgEVy6XSU5FnMQ0rMVcW32Y8L0Z1zLtG9SPJbdRR4NeMjBiJsGwSXzSV4KJcBInyhGZIwiTn0RmO1JCTCl2MGQO1qp+ZPbfjY/JHSSN47uhHeElTDgiag9qu59z3aeF+GgAbkxdp47KJmaW76K0jon3PX1ZRyUwNysf5EDzUEPJjS3C7KGk6//fA2zJ5sKVhpZ7pHpFvBBVKux11sVXkZX0rcsI8xJHqYgQz+z7/AIgOQPYj+ln0vHJj8eB7Vne9Uf1l8hdrUzwIDAQAB"+}\ No newline at end of file
src/extension-identity-anon/manifest.json+12−4
@@ -1,8 +1,16 @@ {   "manifest_version": 3,   "name": "Adom Identity (anonymous profile)",-  "version": "0.1.1",+  "version": "0.1.2",   "description": "This window runs on an isolated throwaway profile, not signed in as you.",-  "action": { "default_popup": "anon.html", "default_title": "Anonymous profile — this window is not signed in as you" },-  "icons": { "16": "icon16.png", "48": "icon48.png", "128": "icon128.png" }-}+  "action": {+    "default_popup": "anon.html",+    "default_title": "Anonymous profile \u2014 this window is not signed in as you"+  },+  "icons": {+    "16": "icon16.png",+    "48": "icon48.png",+    "128": "icon128.png"+  },+  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqyWTOPKGOtCclSuKTGX6SXc1nQ1YEDIJb/oZu47QXDArkuXVHxfCNvpCDdWXnp4wUTPjoGiv1WhzZY2atSaLkXHnOt3mHxKLhAO9wunAMUwG0RODwZfJhAOgVRavccD2dzvkQohd1uFjtqpxHofMdRs8IlTPKKGLrfhiH58ikhhn4JMzIq/0VCMM032fPlUz84vh0+zNkywA5t+jj45qRtV2Y3P1zvhf/uUVmcbVUikIrlOs1F8QpcNnYJ2lA4F5XHTGM89UCBo42IALQEa34CEVblqf5rdgcrFBexlSAm4n0SlV8jvlnKvcSuPPzD+1zmC5Wn5v5tro9arJKM3IqwIDAQAB"+}\ No newline at end of file
src/extension-identity/manifest.json+3−2
@@ -1,7 +1,7 @@ {   "manifest_version": 3,   "name": "Adom Identity",-  "version": "0.1.0",+  "version": "0.1.1",   "description": "Who this browser is surfing as: the shared adom-you profile. Click for the identity card.",   "action": {     "default_popup": "identity.html",@@ -14,5 +14,6 @@   },   "host_permissions": [     "http://127.0.0.1/*"-  ]+  ],+  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7BL3G9VxBGTYp95hL1go5Atzg2otDxuAZvWBFO6smZpRYLOUB7xkvW6CKkloCkkUmmf036zEbCstdApxTDa9jVyhXq37131R78h4Fh3j1qT95X1wphTGIn4GMxjN8A+KsC+vDfdL9pyLyJ8y4zTU/g0KsSP17hoFIBABrafNXhTSru/NXV27cX2VDiRoPJ2Z6Rwux/gMrxZv8VQClvD9Xt0RkIFiKQh4HwpGSGJcopgGA4jU/eUgXDynTMVo01P/I1oCaYI0TJbCA9M/0O/1YvKp9O1ZIvIdwjd/x6CzC37EPh13Vhh5BloKnX0610c862VWoAf7kXrbXlYey+ml5QIDAQAB" }\ No newline at end of file
src/extension/manifest.json+3−2
@@ -1,7 +1,7 @@ {   "manifest_version": 3,   "name": "Adom Pup",-  "version": "0.2.8",+  "version": "0.2.9",   "description": "Toolbar for Adom Pup browser windows: annotate, snip, view toggle, dashboard, owner info. Thin trigger surface - the pup bridge is the engine.",   "action": {     "default_popup": "popup.html",@@ -39,5 +39,6 @@     "16": "icon16.png",     "48": "icon48.png",     "128": "icon128.png"-  }+  },+  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApt/KZkoLldvJGoP/jbNVtXRui/Zp/xeahObOFMzJnbU4U9/WCQhO0RiNnHYZ12GeiiI3FXy7duAUmQJBFZQhMA7tjrtMPswgR4xiYlxxL4WoVi9zJsWgDoeChnVxvFyYe7gZY6b6cCI3riP6jNnZ4s6jOVZ0YeGkVehJGjg9wJZ5Mp+1QnIH8TnTqeNdtCsW74ORu2D1FeiAM96xmbcXszkJbNvfiL4cdzxnBy2V/fWMwnN/9quc7U+zbblZ2N8nHiQiaK9XKgEB9Ia7I3A7ICCWUMIhhNDSAEY3H7YlEI4EB0RYFdOzXPGm5UWwHF+0AoBQGhNb3OtgiAu3X924MwIDAQAB" }\ No newline at end of file
src/server.js+34−23
@@ -263,6 +263,18 @@ async function ensureDeepHelpers(page) { try { await page.evaluate(PUP_DEEP_HELP // restarts, reboots). One-time migration: pull an existing in-cache profiles/ dir over if present. const ADOM_STATE_DIR = path.join(os.homedir(), '.adom'); const PROFILES_DIR = path.join(ADOM_STATE_DIR, 'pup-profiles');+// v2.0.150: DETERMINISTIC Adom extension IDs. Each manifest carries a fixed `key`, so its ID is+// stable + known (not the fragile path-hash of an unpacked load). This lets us write the toolbar+// pin ORDER directly into Preferences (extensions.pinned_extensions), which is what actually+// controls the left→right icon order. Order the array [pup, annotate, identity] to place the+// profile/identity tile rightmost, next to Chrome's own profile avatar. KEEP IN LOCKSTEP with the+// `key` fields in src/extension*/manifest.json — regenerating a key changes the id here too.+const ADOM_EXT_IDS = {+  pup: 'liomcaladmooagjiebkfgegciadapnpi',+  annotate: 'ghadnmahlldiigemdbekmhcbfcpkmclg',+  identity: 'mngfopllhdfgomjphhejmcdimcohcgnf',+  identityAnon: 'mhoenaodccbppcinfbaifamkgfhbakbe',+}; const SESSIONS_DIR = path.join(ADOM_STATE_DIR, 'pup-sessions'); fs.mkdirSync(PROFILES_DIR, { recursive: true }); fs.mkdirSync(SESSIONS_DIR, { recursive: true });@@ -2811,18 +2823,23 @@ function prepareCleanProfile(userDataDir) {       prefs = {};     }     prefs.profile = prefs.profile || {};-    // v2.0.53: PIN the Adom Pup toolbar icon by default (MV3 action icons hide in the puzzle-    // menu unless pinned). The unpacked-extension id is a hash of its stable staging path, so-    // it is the same on every machine that stages to ~/.adom/pup-extension.+    // v2.0.53: PIN the Adom Pup toolbar icons by default (MV3 action icons hide in the puzzle menu+    // unless pinned). v2.0.150 (John: "be consistent on icon order... profile at the RIGHT end...+    // might just be the order you load them"): the toolbar order IS the `pinned_extensions` ARRAY+    // order in Preferences — NOT the --load-extension order and NOT the sequence of pin API calls+    // (both proven not to control it). So we WRITE that array here, pre-launch (Chrome closed), in+    // the fixed canonical order. Each extension now carries a stable `key` in its manifest, so its+    // ID is deterministic (ADOM_EXT_IDS) regardless of staging path — the old path-hash id was+    // fragile and is gone. Canonical left→right: Pup (dots), Annotate (pencil), Identity (profile)+    // LAST so the profile tile lands rightmost, next to Chrome's own profile avatar. Any foreign+    // pinned id is preserved BEFORE our block so our Adom trio stays the rightmost group.     try {       if (pupSettings().toolbarExtension !== 'off') {         prefs.extensions = prefs.extensions || {};-        const pins = Array.isArray(prefs.extensions.pinned_extensions) ? prefs.extensions.pinned_extensions : [];-        for (const d of [path.join(os.homedir(), '.adom', 'pup-annotate-extension'), path.join(os.homedir(), '.adom', 'pup-extension'), path.join(os.homedir(), '.adom', 'pup-identity-extension'), path.join(os.homedir(), '.adom', 'pup-identity-anon-extension')]) {-          const id2 = unpackedExtensionId(d);-          if (!pins.includes(id2)) pins.push(id2);-        }-        prefs.extensions.pinned_extensions = pins;+        const ours = [ADOM_EXT_IDS.pup, ADOM_EXT_IDS.annotate, ADOM_EXT_IDS.identity, ADOM_EXT_IDS.identityAnon];+        const prev = Array.isArray(prefs.extensions.pinned_extensions) ? prefs.extensions.pinned_extensions : [];+        const foreign = prev.filter(id => !ours.includes(id));+        prefs.extensions.pinned_extensions = [...foreign, ...ours];       }     } catch (e) {}     try { prefs.browser = prefs.browser || {}; prefs.browser.promotional_tabs_enabled = false; } catch (e) {}@@ -2906,26 +2923,20 @@ async function ensureExtensionsEnabled(browser, profileName) {             // — the exact inconsistency John saw. Fix: a FIXED canonical order — Pup (dots), then             // Annotate (pencil), then Identity (profile) LAST so it lands rightmost — pinned strictly             // SEQUENTIALLY (await each before the next) so the layout is deterministic every time.-            const rank = (n) => /^Adom Pup\b/.test(n) ? 0 : /^Adom Annotate\b/.test(n) ? 1 : /^Adom Identity\b/.test(n) ? 3 : 2;-            adom.sort((a, b) => rank(a.name || '') - rank(b.name || '') || String(a.name).localeCompare(String(b.name)));-            // Chrome appends a newly-pinned action to the END of the toolbar row, and re-pinning an-            // already-pinned extension does NOT move it. So to FORCE the canonical left-to-right order-            // on a durable profile where they may already be pinned in some prior (random) order, we-            // UNPIN all three first, then PIN them back in rank order — Pup, Annotate, Identity — so-            // Identity lands last (rightmost, by Chrome's own profile avatar). All strictly sequential.+            // v2.0.150: ORDER is now owned by the pinned_extensions array we write in+            // prepareCleanProfile (pre-launch) — that is the real control. Here we only ENSURE each+            // Adom icon is pinned (a no-op if the pref already pinned it); re-pinning never reorders,+            // so this cannot fight the canonical order. (The old unpin-then-repin dance is gone — it+            // could not control order and risked leaving an icon unpinned mid-sequence.)             let pinned = 0;-            const unpin = (idx, done) => {-              if (idx >= adom.length) return done();-              chrome.developerPrivate.updateExtensionConfiguration({ extensionId: adom[idx].id, pinnedToToolbar: false }, () => unpin(idx + 1, done));-            };             const pinNext = (idx) => {-              if (idx >= adom.length) return resolve({ ok: true, devMode: true, pinned, total: adom.length, order: adom.map(a => a.name) });+              if (idx >= adom.length) return resolve({ ok: true, devMode: true, pinned, total: adom.length });               chrome.developerPrivate.updateExtensionConfiguration({ extensionId: adom[idx].id, pinnedToToolbar: true }, () => {                 if (!chrome.runtime.lastError) pinned++;-                pinNext(idx + 1);   // strictly sequential → deterministic toolbar order+                pinNext(idx + 1);               });             };-            unpin(0, () => pinNext(0));+            pinNext(0);           });         });       } catch (e) { resolve({ ok: false, why: e.message }); }