← Commit history

Publish 0.8.0

John Lauer ·0cb4b304fd ·2mo ago ·parent 01afdb9
3 files changed +138−23
bin/nb-dashboard+73−16
@@ -18,7 +18,7 @@ const http = require('http'); const { execFile, spawn } = require('child_process'); -const VERSION = '0.7.2';           // keep in lockstep with package.json (pkg version)+const VERSION = '0.8.0';           // keep in lockstep with package.json (pkg version) const PORT = 8954; const URL_SELF = `http://127.0.0.1:${PORT}/`; @@ -58,26 +58,45 @@ async function refresh() {     facets.push({ label: 'Adom Bridge', state: 'ok', detail: `${targets.length} desktop(s): ${targets.map((t) => t.name).join(', ')}` });   }   // Facets 2+3: layer 1 / layer 2, judged across every connected desktop (any-ok wins).-  let l1ok = false; let l2ready = false; let l2installable = false;+  let l1ok = false; let l2ready = false; let anyMissing = false; let anyDisabled = false;+  const matrix = [];   // one row per machine x browser-profile, in every state incl. deliberately-disabled   for (const t of targets.slice(0, 4)) {     const r = await cli(['--target', t.name, '--ai-thread', 'nb-dashboard', 'nbrowser_readiness', '{}']);     targetRows.push({ name: t.name, state: r.state || null, profiles: r.profiles || [], extensionVersions: r.extensionVersions || {}, chromeInstalled: r.chromeInstalled });     if (r.state) l1ok = true;                       // the nb bridge answered = layer 1 verbs route     if (r.ready === true) l2ready = true;-    if (r.state === 'needs-extension' || r.state === 'resolving') l2installable = true;+    const pr = await cli(['--target', t.name, '--ai-thread', 'nb-dashboard', 'nbrowser_profiles', '{}']);+    for (const p of (pr.profiles || [])) {+      if (/pending-\d+/.test(String(p.profile || '')) || p.unresolved) continue; // mid-identification, not a real row+      // Four states. 'disabled' is a USER CHOICE (they installed it, then switched it off - the+      // browser-native way of saying "nb may not drive this browser"). It is respected: grey, never+      // counted against health, never upsold. John does exactly this on Edge, on purpose.+      const st = p.live ? 'connected'+        : (p.extensionInstalled && p.extensionEnabled === false) ? 'disabled'+        : p.extensionInstalled ? 'asleep'+        : 'missing';+      if (st === 'missing') anyMissing = true;+      if (st === 'disabled') anyDisabled = true;+      matrix.push({ machine: t.name, browser: p.browser, profile: p.profile, email: p.email || '', state: st,+        version: (r.extensionVersions || {})[p.profile] || null });+    }   }+  const l2installable = anyMissing;   facets.push(l1ok     ? { label: 'Layer 1 - Native Browser', state: 'ok', detail: 'extension-free opens into the user\'s real browser work now', _hint: 'nbrowser_open_os_window / nbrowser_profiles are live.' }     : { label: 'Layer 1 - Native Browser', state: targets.length ? 'warn' : 'fail', detail: targets.length ? 'nb bridge not answering on the connected desktop(s)' : 'needs a connected desktop', _hint: 'adom-bridge refresh_bridges {"name":"native-browser"} on the desktop, then re-check nbrowser_readiness.' });   facets.push(l2ready-    ? { label: 'Layer 2 - nbe (extension)', state: 'ok', detail: 'nbe connected - full driving is live', _hint: 'Drive with nbrowser_open_window; background screenshots, trusted clicks, logins all work.' }-    : l2installable-      ? { label: 'Layer 2 - nbe (extension)', state: 'warn', detail: 'nbe is NOT installed/connected - the user is missing the best part', _hint: 'OFFER the install (installing-the-extension skill runs it FOR the user in about a minute). This is the upsell the dashboard exists for.' }-      : { label: 'Layer 2 - nbe (extension)', state: 'fail', detail: 'no desktop to check', _hint: 'Connect Adom Bridge first.' });+    ? { label: 'Layer 2 - nbe (extension)', state: 'ok', detail: anyMissing ? 'nbe live (some browsers still without it)' : 'nbe connected - full driving is live', _hint: 'Drive with nbrowser_open_window. Rows marked DISABLED are the user\'s explicit choice - treat like nbrowser_profile_block: never nag, never try to enable, never drive.' }+    : anyMissing+      ? { label: 'Layer 2 - nbe (extension)', state: 'warn', detail: 'nbe is NOT installed - the user is missing the best part', _hint: 'OFFER the install (installing-the-extension skill runs it FOR the user in about a minute). But a browser where the extension is INSTALLED-AND-DISABLED is the user saying no - respect it like nbrowser_profile_block.' }+      : anyDisabled+        ? { label: 'Layer 2 - nbe (extension)', state: 'ok', detail: 'disabled everywhere by the user - their choice, respected', _hint: 'The user deliberately disabled nbe. Do NOT upsell, enable, or work around it - offer layer 1 only.' }+        : { label: 'Layer 2 - nbe (extension)', state: 'fail', detail: 'no desktop to check', _hint: 'Connect Adom Bridge first.' });   // Rollup is OURS: the dashboard is healthy when layer 1 works; a missing nbe is a warn (that is   // the upsell state, not an outage).   cache.facets = facets;   cache.targets = targetRows;+  cache.matrix = matrix;   cache.state = l1ok ? (l2ready ? 'ok' : 'warn') : (targets.length ? 'warn' : 'fail');   cache.at = Date.now(); }@@ -117,6 +136,10 @@ function page() {   .prompt button { background:var(--teal); color:#04211f; border:0; border-radius:8px; padding:0 14px; font-weight:700; font-size:13px; cursor:pointer; }   .prompt button:active { transform:translateY(1px); }   .hintline { font-size:12px; color:var(--dim); margin-top:6px; }+  .mini { background:transparent; border:1px solid var(--teal); color:var(--teal); border-radius:6px; padding:2px 10px; font-size:12px; cursor:pointer; }+  .mini:hover { background:rgba(0,206,194,.12); }+  tr.off td { color:var(--dim); opacity:.75; }+  .explain { margin-top:12px; border:1px solid rgba(0,206,194,.35); border-radius:10px; padding:12px 14px; background:rgba(0,206,194,.05); }   table { width:100%; border-collapse:collapse; margin-top:10px; font-size:13px; }   th { text-align:left; color:var(--dim); font-weight:500; font-size:11.5px; text-transform:uppercase; letter-spacing:.08em; padding:4px 8px; border-bottom:1px solid var(--edge); }   td { padding:6px 8px; border-bottom:1px solid rgba(30,42,54,.5); }@@ -156,10 +179,26 @@ const UPSELL = '<p><strong>Not installed yet</strong> - and it is the single bes   + '<div class="gain">fill forms, download behind logins, and clean up after itself</div>'   + '<div class="gain">screenshot pages in the background while you keep working</div>'   + '<div class="gain">show a gliding cursor that narrates what it is doing</div>'-  + '<p>The install takes about a minute and <strong>your AI runs it for you</strong>:</p>'+  + '<p><strong>Installing takes your AI, not a download.</strong> There is no store listing yet, so the nbe installs in developer mode - and your AI drives that whole flow: loads the extension, wires the secure native host, verifies the connection. About a minute; you approve one folder picker.</p>'   + '<div class="prompt"><code id="pr">install the adom native browser extension in my chrome</code><button onclick="copyPrompt()">Copy</button></div>'-  + '<p class="hintline">Paste that to your AI. It drives the whole install; you approve the one folder picker.</p>';+  + '<p class="hintline">Paste that into any Adom AI thread to start.</p>'; function copyPrompt(){ try { navigator.clipboard.writeText(document.getElementById('pr').textContent); } catch(e){} }+function installPrompt(i){+  const r = (window.__matrix||[])[i]; if (!r) return;+  const p = 'install the adom native browser extension in my ' + r.browser + ' on ' + r.machine;+  const box = document.getElementById('install-explain');+  box.innerHTML = '<div class="explain">'+    + '<p><strong>Installing the nbe takes your AI, not a download.</strong> There is no store listing yet '+    + '(the Chrome/Edge review queue takes a while for an extension with these permissions), so it installs '+    + 'in developer mode - and your AI drives that whole flow for you: it loads the extension into <strong>'+    + r.browser + ' on ' + r.machine + '</strong>, wires the secure native-messaging host, and verifies the '+    + 'connection. Takes about a minute; you approve one folder picker.</p>'+    + '<p>Paste this to any Adom AI thread:</p>'+    + '<div class="prompt"><code id="pr2">' + p + '</code>'+    + '<button onclick="try{navigator.clipboard.writeText(document.getElementById(\'pr2\').textContent);this.textContent=\'Copied!\';}catch(e){}">Copy</button></div>'+    + '</div>';+  box.scrollIntoView({ behavior: 'smooth', block: 'nearest' });+} function led(el, s){ el.className = 'led ' + (s||'unknown'); } async function tick(){   try {@@ -177,13 +216,31 @@ async function tick(){     if (preview) {       led(document.getElementById('led-l2'), 'warn');       body.innerHTML = '<p style="color:var(--amber);font-size:11px;text-transform:uppercase;letter-spacing:.1em">Preview mode - not the live state</p>' + UPSELL;-    } else if (l2.state === 'ok') {+    } else {+      const m = d.matrix || [];       let rows = '';-      for (const t of (d.targets||[])) for (const [p,v] of Object.entries(t.extensionVersions||{}))-        rows += '<tr><td>'+t.name+'</td><td>'+p+'</td><td>v'+v+'</td><td><span class="led ok"></span></td></tr>';-      body.innerHTML = '<p><strong>Connected.</strong> Full driving is live: trusted clicks, logins, background capture, downloads behind auth.</p>'-        + (rows ? '<table><tr><th>Desktop</th><th>Profile</th><th>nbe</th><th></th></tr>'+rows+'</table>' : '');-    } else { body.innerHTML = UPSELL; }+      for (let i = 0; i < m.length; i++) {+        const r = m[i];+        const who = r.email || r.profile;+        if (r.state === 'connected')+          rows += '<tr><td>'+r.machine+'</td><td>'+r.browser+'</td><td>'+who+'</td><td><span class="led ok"></span> live'+(r.version?' v'+r.version:'')+'</td></tr>';+        else if (r.state === 'asleep')+          rows += '<tr><td>'+r.machine+'</td><td>'+r.browser+'</td><td>'+who+'</td><td><span class="led ok" style="opacity:.45"></span> installed, wakes on demand</td></tr>';+        else if (r.state === 'disabled')+          rows += '<tr class="off"><td>'+r.machine+'</td><td>'+r.browser+'</td><td>'+who+'</td><td><span class="led unknown"></span> disabled by you - nb will not drive this browser</td></tr>';+        else+          rows += '<tr><td>'+r.machine+'</td><td>'+r.browser+'</td><td>'+who+'</td><td><button class="mini" onclick="installPrompt('+i+')">Install&hellip;</button></td></tr>';+      }+      const anyLive = m.some(r => r.state === 'connected');+      const anyMissing = m.some(r => r.state === 'missing');+      body.innerHTML =+        (anyLive ? '<p><strong>Connected.</strong> Full driving is live: trusted clicks, logins, background capture, downloads behind auth.</p>'+                 : anyMissing ? UPSELL : '<p>Per-browser status:</p>')+        + (rows ? '<table><tr><th>Machine</th><th>Browser</th><th>Profile</th><th>nbe</th></tr>'+rows+'</table>' : '')+        + (anyLive && anyMissing ? '<p class="hintline">Browsers without nbe have an Install button - it explains the AI-driven install and hands you the prompt.</p>' : '')+        + '<div id="install-explain"></div>';+      window.__matrix = m;+    }     document.getElementById('facets').innerHTML = d.facets.map(x =>       '<div class="facet"><span class="led '+(x.state||'unknown')+'"></span>'+x.label+'<span class="d">'+(x.detail||'')+'</span></div>').join('');   } catch(e) { led(document.getElementById('topled'), 'unknown'); }@@ -199,7 +256,7 @@ function startServer() {     const u = String(req.url || '/');     if (u.startsWith('/api/status')) {       res.writeHead(200, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' });-      res.end(JSON.stringify({ state: cache.state, version: VERSION, facets: cache.facets, targets: cache.targets, cacheAgeMs: Date.now() - cache.at }));+      res.end(JSON.stringify({ state: cache.state, version: VERSION, facets: cache.facets, targets: cache.targets, matrix: cache.matrix || [], cacheAgeMs: Date.now() - cache.at }));       return;     }     if (u.startsWith('/api/shutdown') && req.method === 'POST') {
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "nb-bridge",-  "version": "0.7.2",+  "version": "0.8.0",   "type": "app",   "title": "NB - Native Browser Bridge",   "description": "This package installs the SKILLS into your container so your AI knows how to drive your real signed-in Chrome and Edge; Bridge loads the bridge runtime itself from the release zip.",
page.json+64−6
@@ -16,10 +16,10 @@     "chrome",     "edge",     "nbrowser",-    "ai-agent",-    "developer-tools",-    "vibe-coding",-    "native-messaging"+    "pup",+    "native-messaging",+    "credentials",+    "chip-fetcher"   ],   "status": "live",   "discovery_triggers": [@@ -70,5 +70,63 @@   },   "visibility": "public",   "brief": "Your real browser, driven by AI.",-  "discovery_pitch": "nbe (the Adom Native Browser Extension, layer 2 of nb) drives the user's REAL signed-in Chrome/Edge via nbrowser_* verbs: use it for LOGGING IN, filling forms, and downloading behind auth with their OWN saved passwords - pup cannot (it is a fresh Chrome-for-Testing with no logins). Next moves: run 'adom-bridge bridge_list' to check it is installed; if not, OFFER to install it (see the installing-the-extension skill) - never make the user do it. If this container lacks the skills, 'adom-wiki pkg install adom/nb-bridge'. Check readiness with 'adom-bridge nbrowser_readiness' (state:needs-extension means offer the install)."-}+  "discovery_pitch": "nbe (the Adom Native Browser Extension, layer 2 of nb) drives the user's REAL signed-in Chrome/Edge via nbrowser_* verbs: use it for LOGGING IN, filling forms, and downloading behind auth with their OWN saved passwords - pup cannot (it is a fresh Chrome-for-Testing with no logins). Next moves: run 'adom-bridge bridge_list' to check it is installed; if not, OFFER to install it (see the installing-the-extension skill) - never make the user do it. If this container lacks the skills, 'adom-wiki pkg install adom/nb-bridge'. Check readiness with 'adom-bridge nbrowser_readiness' (state:needs-extension means offer the install).",+  "version": "0.8.0",+  "type": "app",+  "description": "This package installs the SKILLS into your container so your AI knows how to drive your real signed-in Chrome and Edge; Bridge loads the bridge runtime itself from the release zip.",+  "install": {+    "binary_name": "nb-bridge"+  },+  "files": [+    "screenshots/hero.png",+    "skills/adom-native-browser/SKILL.md",+    "skills/driving-sites/SKILL.md",+    "skills/driving-the-desktop/SKILL.md",+    "skills/driving-the-extension/SKILL.md",+    "skills/driving-the-extension-demo/SKILL.md",+    "skills/installing-the-extension/SKILL.md",+    "skills/native-browser-recording/SKILL.md",+    "skills/native-browser-safety/SKILL.md",+    "skills/reading-google-messages/SKILL.md",+    "skills/uninstalling-the-extension/SKILL.md",+    "bin/nb-dashboard",+    "dockbar.json",+    "install.sh",+    "uninstall.sh",+    "package.json"+  ],+  "scripts": {+    "install": "./install.sh",+    "uninstall": "./uninstall.sh"+  },+  "dependencies": {+    "adom/adom-bridge": "^2.0.3"+  },+  "sample_prompts": [+    {+      "label": "Open in my browser",+      "prompt": "open the wiki in my real chrome work profile"+    },+    {+      "label": "Log in for me",+      "prompt": "log into digikey for me using my saved password"+    },+    {+      "label": "Form behind a login",+      "prompt": "fill out this form on the site I'm already signed into"+    },+    {+      "label": "Download behind auth",+      "prompt": "download that datasheet, it's behind a login"+    },+    {+      "label": "Install the extension",+      "prompt": "install the adom native browser extension in my chrome"+    }+  ],+  "org": "adom",+  "author": {+    "name": "John Lauer",+    "email": "[email protected]"+  }+}\ No newline at end of file