← Commit history
bin/nb-dashboard+26−9@@ -20,7 +20,7 @@ const { execFile, spawn } = require('child_process'); const fs = require('fs'); const crypto = require('crypto'); -const VERSION = '0.12.0'; // keep in lockstep with package.json (pkg version)+const VERSION = '0.12.1'; // keep in lockstep with package.json (pkg version) // SEND TO AI IS UN-PARKED (John: "yeah, we are re-enabling the send to ai. it seems to be good now"). // The ah build thread certified the crash family dead on the gate-fixed build and the delivery leg is // proven end to end. The park const is DELETED rather than flipped, so no dead branch can resurrect it.@@ -72,12 +72,12 @@ function controlUrl() { try { const u = fs.readFileSync(path.join(require('os').homedir(), '.adom', 'hydrogen-control-url'), 'utf8').trim(); if (u) return u.replace(/\/$/, ''); } catch {} return 'http://127.0.0.1:47084'; }-function httpJson(method, url, body) {+function httpJson(method, url, body, timeoutMs) { return new Promise((resolve) => { try { const u = new URL(url); const data = body ? JSON.stringify(body) : null;- const req = http.request({ host: u.hostname, port: u.port, path: u.pathname + u.search, method, timeout: 5000,+ const req = http.request({ host: u.hostname, port: u.port, path: u.pathname + u.search, method, timeout: timeoutMs || 5000, headers: data ? { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) } : {} }, (res) => { let b = ''; res.on('data', (c) => (b += c)); res.on('end', () => { let j = null; try { j = JSON.parse(b); } catch {} resolve({ status: res.statusCode, json: j, text: b.slice(0, 300) }); }); });@@ -87,16 +87,33 @@ function httpJson(method, url, body) { }); } async function saySend(phrase) {+ // ONE CALL composite. NOT the two-step new-then-send-by-sid flow: /ai-threads/new has NEVER+ // returned an id (a thread has no sid until its first submitted prompt), so a client that reads+ // sid||id from /new always aborts and ships a Send button that opens a tab and delivers nothing.+ // I shipped exactly that in 0.12.0 and it was caught in review before John pressed it.+ // `new:true` inside run-prompt creates the thread; name and tags stage on the SAME request. const base = controlUrl(); const gate = await shellVersionOk(); if (!gate.ok) return { ok: false, why: `your Hydrogen is ${gate.version} and Send to AI needs 1.0.142 or newer (1.0.141 carries a crash this would trigger) - update Hydrogen to enable it; the phrase is on your clipboard` }; try {- const mk = await httpJson('POST', base + '/ai-threads/new', { name: 'Native Browser - say it', tags: ['dock:nb-bridge'] });- const sid = mk.json && (mk.json.sid || mk.json.id);- if (!(mk.status >= 200 && mk.status < 300) || !sid) return { ok: false, why: mk.status ? ('could not create a thread (status ' + mk.status + ')') : ('AI panel unreachable at ' + base) };- const run = await httpJson('POST', base + '/ai-threads/run-prompt', { sid, prompt: phrase, submit: true });- if (run.status >= 200 && run.status < 300) return { ok: true, sid };- return { ok: false, why: 'thread created but the prompt did not send (status ' + run.status + '); the staged thread expires on its own', sid };+ // 120s: the tab CREATE alone can take 60s on a loaded workbench, and a short client timeout+ // turns a working send into a spurious "could not reach the AI panel".+ const r = await httpJson('POST', base + '/ai-threads/run-prompt', {+ new: true, prompt: phrase, submit: true,+ name: 'NB - say it', tags: ['dock:nb-bridge'],+ }, 120000);+ if (!(r.status >= 200 && r.status < 300)) {+ const hint = r.json && (r.json._hints ? (typeof r.json._hints === 'string' ? r.json._hints : JSON.stringify(r.json._hints)) : r.json.error);+ return { ok: false, why: hint || (r.status ? `the AI panel refused the prompt (status ${r.status})` : `AI panel unreachable at ${base}`) };+ }+ // Delivery is proven by delivered_to, NEVER by the thread's name: a perfectly named tab with a+ // null delivered_to means nothing was typed, and an unnamed tab with a truthy one succeeded.+ const delivered = r.json && r.json.delivered_to;+ if (!delivered) {+ const hint = r.json && r.json._hints ? (typeof r.json._hints === 'string' ? r.json._hints : JSON.stringify(r.json._hints)) : null;+ return { ok: false, why: hint || 'the AI panel opened a thread but did not confirm delivery - do not resend, the tab may still be settling' };+ }+ return { ok: true, delivered_to: delivered }; } catch (e) { return { ok: false, why: String(e && e.message || e) }; } } // ---- the guided demo (dash-demo contract) ----------------------------------
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "nb-bridge",- "version": "0.12.0",+ "version": "0.12.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+75−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,74 @@ }, "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.12.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",+ "docs/img/dock-nb.svg",+ "docs/img/settings/overlay-on.png",+ "docs/img/settings/overlay-off.png",+ "docs/img/settings/activity-progress.png",+ "docs/img/settings/activity-flash.png",+ "docs/img/settings/activity-off.png",+ "demo/audio/intro.mp3",+ "demo/audio/open.mp3",+ "demo/audio/shot.mp3",+ "demo/audio/click.mp3",+ "demo/audio/wrap.mp3",+ "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]"+ }+}