← Commit history
bin/nb-dashboard+20−6@@ -17,8 +17,14 @@ 'use strict'; const http = require('http'); const { execFile, spawn } = require('child_process');+const fs = require('fs');+const crypto = require('crypto'); -const VERSION = '0.8.0'; // keep in lockstep with package.json (pkg version)+const VERSION = '0.8.1'; // keep in lockstep with package.json (pkg version)+// BUILD = hash of this very file. Version-aware reuse compared the VERSION string alone and happily+// kept serving a stale process whose code had changed under the same version (2026-08-23) - the+// dock guide's version-blind trap at one finer grain. Reuse now requires version AND build to match.+const BUILD = (() => { try { return crypto.createHash('sha1').update(fs.readFileSync(__filename)).digest('hex').slice(0, 10); } catch { return 'unknown'; } })(); const PORT = 8954; const URL_SELF = `http://127.0.0.1:${PORT}/`; @@ -183,6 +189,7 @@ const UPSELL = '<p><strong>Not installed yet</strong> - and it is the single bes + '<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 into any Adom AI thread to start.</p>'; function copyPrompt(){ try { navigator.clipboard.writeText(document.getElementById('pr').textContent); } catch(e){} }+function copyPrompt2(btn){ try { navigator.clipboard.writeText(document.getElementById('pr2').textContent); btn.textContent = 'Copied!'; } 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;@@ -195,7 +202,7 @@ function installPrompt(i){ + '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>'+ + '<button onclick="copyPrompt2(this)">Copy</button></div>' + '</div>'; box.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }@@ -256,7 +263,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, matrix: cache.matrix || [], cacheAgeMs: Date.now() - cache.at }));+ res.end(JSON.stringify({ state: cache.state, version: VERSION, build: BUILD, facets: cache.facets, targets: cache.targets, matrix: cache.matrix || [], cacheAgeMs: Date.now() - cache.at })); return; } if (u.startsWith('/api/shutdown') && req.method === 'POST') {@@ -292,7 +299,7 @@ function probeExisting() { async function serve(printUrl) { const existing = await probeExisting();- if (existing && existing.version === VERSION) {+ if (existing && existing.version === VERSION && existing.build === BUILD) { if (printUrl) process.stdout.write(URL_SELF + '\n'); process.exit(0); }@@ -311,7 +318,7 @@ async function serve(printUrl) { for (let i = 0; i < 40; i++) { await new Promise((r) => setTimeout(r, 250)); const up = await probeExisting();- if (up && up.version === VERSION) { if (printUrl) process.stdout.write(URL_SELF + '\n'); process.exit(0); }+ if (up && up.version === VERSION && up.build === BUILD) { if (printUrl) process.stdout.write(URL_SELF + '\n'); process.exit(0); } } process.stderr.write('nb-dashboard: server did not come up\n'); process.exit(1);@@ -324,5 +331,12 @@ else if (cmd === 'status') probeExisting().then((d) => { console.log(JSON.string else if (cmd === 'stop') { const req = http.request({ host: '127.0.0.1', port: PORT, path: '/api/shutdown', method: 'POST' }, () => { console.log('stopped'); process.exit(0); }); req.on('error', () => { console.log('not running'); process.exit(0); }); req.end();-} else if (cmd === '--version' || cmd === 'version') { console.log(VERSION); process.exit(0); }+} else if (cmd === 'selfcheck') {+ // Ship gate: the page's inline script must PARSE (a quoting slip once shipped a blank dashboard).+ const vm = require('vm');+ const m = /<script>([\s\S]*?)<\/script>/.exec(page());+ try { new vm.Script(m ? m[1] : ''); console.log('selfcheck ok: inline script parses; build ' + BUILD); process.exit(0); }+ catch (e) { console.error('selfcheck FAILED: inline script does not parse: ' + e.message); process.exit(1); }+}+else if (cmd === '--version' || cmd === 'version') { console.log(VERSION); process.exit(0); } else { console.log('usage: nb-dashboard serve [--print-url] | status | stop | version'); process.exit(1); }
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "nb-bridge",- "version": "0.8.0",+ "version": "0.8.1", "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.1",+ "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]"+ }+}