← Commit history
0.2.1: describe parity, the same 12 verbs were also undescribed; CI now gates all three verb lists
6 files changed
+32−8
bridge/BRIDGE_VERSION+1−1@@ -1 +1 @@-0.2.0+0.2.1
bridge/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "native-browser", "displayName": "Adom Native Browser",- "version": "0.2.0",+ "version": "0.2.1", "expectedExtensionVersion": "0.2.0", "description": "Drives the user's REAL signed-in Chrome/Edge, in two layers. Layer 1 (nb) needs NOTHING installed and works on any machine: nbrowser_open_os_window (open a URL in the user's real profile, background+flashing, the OAuth/consent path), nbrowser_os_windows, nbrowser_profiles, nbrowser_wake_profile, nbrowser_force_close. Layer 2 (nbe, the Adom Native Browser Extension) unlocks the rest: CDP driving (navigate/click/read/screenshot) inside their logged-in profile. The bridge binds an ephemeral loopback port and the native-messaging host pumps stdio <-> bridge.", "homepage": "https://wiki.adom.inc/adom/adom-native-browser",
bridge/package.json+1−1@@ -1,6 +1,6 @@ { "name": "adom-native-browser-bridge",- "version": "0.2.0",+ "version": "0.2.1", "private": true, "description": "Native-browser bridge for Bridge (ab): layer 1 (nb) plus layer 2 via nbe, the Adom Native Browser Extension (Chrome + Edge).", "main": "server.js",
bridge/server.js+15@@ -1540,6 +1540,21 @@ function dispatch(verb, args) { d('nbrowser_flash_stop', 'Stop the taskbar flash.', { sessionId: 'required str' }, { ok: 'bool' }, 15, { example: { sessionId: 'work' } }), d('nbrowser_flash_mode', 'Set auto-flash verbosity.', { mode: 'required "quiet"|"chatty"|"off"' }, { ok: 'bool', mode: 'str' }, 15, { example: { mode: 'quiet' } }), d('nbrowser_taskbar', 'Paint progress/overlay/flash on the window taskbar button.', { sessionId: 'required str', progress: 'optional {state,value?}', overlay: 'optional {badge}', flash: 'optional "until_focused"|"stop"' }, { ok: 'bool', applied: '[str]' }, 15, { example: { sessionId: 'work', progress: { state: 'indeterminate' } } }),+ // These 12 were implemented but never described (nor declared in bridge.json verbs[]), so they+ // were invisible to every caller for several releases. Keep this list in step with VERB_META;+ // tools/check-guard-coverage.mjs now fails the build if it drifts again.+ d('nbrowser_events', 'Drain buffered lifecycle events since your last call: window_closed (with byUser), tab_closed, spawned_window (a page you drove opened a compose/popup), focus_violation, foreground_raised, dialog. THE channel by which you learn the user closed your window or a page spawned one behind your back.', { limit: 'optional int' }, { ok: 'bool', events: '[{event,windowId?,tabId?,byUser?,url?,agoMs}]', count: 'int' }, 20, { example: {} }),+ d('nbrowser_hover', 'Hover an element (by selector or visible text) to reveal menus/tooltips. Real isTrusted input.', { sessionId: S, selector: 'optional css str', text: 'optional visible-text str', x: 'optional int', y: 'optional int' }, { ok: 'bool', matchedText: 'str?' }, 30, { example: { sessionId: 's1', selector: '.info-icon' } }),+ d('nbrowser_double_click', 'Double-click an element (isTrusted).', { sessionId: S, selector: 'optional css str', text: 'optional visible-text str', x: 'optional int', y: 'optional int' }, { ok: 'bool', matchedText: 'str?' }, 30, { example: { sessionId: 's1', selector: '#row-3' } }),+ d('nbrowser_right_click', 'Right-click an element to open a context menu (isTrusted).', { sessionId: S, selector: 'optional css str', text: 'optional visible-text str', x: 'optional int', y: 'optional int' }, { ok: 'bool', matchedText: 'str?' }, 30, { example: { sessionId: 's1', selector: '.ctx' } }),+ d('nbrowser_focus', 'Select a tab within its window and politely flash its taskbar button. Does NOT raise the window unless you justify a foreground.', { sessionId: S, tabId: 'optional str', userRequestedForeground: 'optional bool', foregroundReason: 'REQUIRED with userRequestedForeground' }, { ok: 'bool', tabId: 'str', foregrounded: 'bool' }, 15, { example: { sessionId: 's1' } }),+ d('nbrowser_login_form', 'Detect a login form\'s state (already autofilled? SSO? 2FA?) and recommend the smartest path. ALWAYS call this before driving a login.', { sessionId: S }, { ok: 'bool', hasForm: 'bool', autofilled: 'bool', sso: 'bool', needs2FA: 'bool', recommendation: 'str' }, 30, { statusVerb: null, example: { sessionId: 's1' } }),+ d('nbrowser_autologin', 'Fill and submit a saved-credential login. The bridge injects the laptop-decrypted secret directly; the agent NEVER sees it. Gated on confirm:true + host.', { sessionId: S, host: 'required str (e.g. "gusto.com")', confirm: 'required true', username: 'optional str' }, { ok: 'bool', loggedIn: 'bool', needs2FA: 'bool' }, 60, { example: { sessionId: 's1', host: 'gusto.com', confirm: true } }),+ d('nbrowser_download', 'Download a URL to the user\'s real Downloads folder via chrome.downloads (lands on the LAPTOP, not the container).', { sessionId: S, url: 'required str', filename: 'optional str', saveAs: 'optional bool' }, { ok: 'bool', downloadId: 'int', filename: 'str?' }, 60, { statusVerb: 'nbrowser_download_wait', example: { sessionId: 's1', url: 'https://example.com/datasheet.pdf' } }),+ d('nbrowser_clear_cookies', 'Background logout: drop one registrable domain\'s cookies (incl. httpOnly), leaving other sites (e.g. Google SSO) intact.', { host: 'required str (e.g. "gusto.com"; url/domain also accepted)' }, { ok: 'bool', removed: 'int', hosts: '[str]' }, 30, { example: { host: 'gusto.com' } }),+ d('nbrowser_caption', 'Paint an on-page caption (subtitle, or red alert banner) for demos and narration.', { sessionId: S, text: 'required str (or action:"hide")', style: 'optional "subtitle"(default)|"alert"', position: 'optional str', fontSize: 'optional int', pulse: 'optional bool', durationMs: 'optional int (default 5000)', action: 'optional "hide"' }, { ok: 'bool', tabId: 'str', style: 'str' }, 20, { example: { sessionId: 's1', text: 'Signing in to DigiKey' } }),+ d('nbrowser_cursor', 'Show or move the gliding Adom cursor with an intent label (demo narration). Visual only, it does not click.', { sessionId: S, action: 'optional "move"(default)|"hide"', x: 'optional int', y: 'optional int', label: 'optional str (or intent)' }, { ok: 'bool', tabId: 'str', action: 'str', label: 'str' }, 20, { example: { sessionId: 's1', x: 400, y: 300, label: 'Clicking Sign in' } }),+ d('nbrowser_dev_reload', 'MAINTAINER-only: reload the extension to pick up on-disk code changes (2-pass, reconnects in ~10s). Throttled to 1 per 90s.', { profile: P }, { ok: 'bool', reloaded: 'bool', throttled: 'bool?' }, 30, { example: { profile: 'chrome:[email protected]' } }), d('nbrowser_describe', 'This verb: returns per-verb schemas for the ab Verbs tab.', {}, { success: 'bool', verbs: '[{name,summary,input,output,timeoutSeconds,statusVerb,longRunning,example}]' }, 15, { example: {} }), ]; return resolve({ success: true, verbs });
native-browser-bridge-manifest.json+5−5@@ -1,8 +1,8 @@ { "manifest_version": 1,- "version": "0.2.0",- "url": "https://wiki.adom.inc/download/adom/adom-native-browser/0.2.0/native-browser-bridge-v0.2.0.zip",- "sha256": "2fe7d7dda61b5f84cde92208664e20562d85797479cc7991009899b0724ecb3f",- "size": 99359,- "released_at": "2026-08-08T16:33:56Z"+ "version": "0.2.1",+ "url": "https://wiki.adom.inc/download/adom/adom-native-browser/0.2.1/native-browser-bridge-v0.2.1.zip",+ "sha256": "3d36956883e1aa1ac8d03f3a0ab3a9e07f2b89f73fcef3501353aa3fbf28c7ae",+ "size": 100724,+ "released_at": "2026-08-08T16:39:21Z" }
tools/check-guard-coverage.mjs+9@@ -56,5 +56,14 @@ if (manifest.version !== readFileSync(join(root, 'bridge/BRIDGE_VERSION'), 'utf8 errors.push(`VERSION SKEW: bridge.json version ${manifest.version} != BRIDGE_VERSION — bump them in lockstep`); } +// DESCRIBE PARITY. nbrowser_describe is the THIRD copy of the verb list (the d(...) calls) and it+// rotted with the other two: the same 12 verbs were implemented, undeclared AND undescribed, so even+// a caller who guessed the name got no schema. An undescribed verb is one no AI will ever discover.+const described = [...new Set([...server.matchAll(/\bd\(\s*'(nbrowser_[a-z0-9_]+)'/g)].map((m) => m[1]))];+const undescribed = metaKeys.filter((v) => !described.includes(v));+const ghostDescribed = described.filter((v) => !metaKeys.includes(v));+if (undescribed.length) errors.push(`UNDESCRIBED in nbrowser_describe (no AI can discover them): ${undescribed.join(', ')}`);+if (ghostDescribed.length) errors.push(`DESCRIBED but not implemented (no VERB_META): ${ghostDescribed.join(', ')}`);+ if (errors.length) { console.error('guard-coverage FAILED:\n ' + errors.join('\n ')); process.exit(1); } console.log(`guard-coverage OK: ${dispatched.length} verbs classified, ${Object.values(policy).filter((c) => c === 'mutating').length} mutating verbs gated, ${dispatched.filter((v) => policy[v] !== 'alias').length} verbs with non-empty hint + pitfalls, ${manifest.verbs.length} verbs declared to ab in bridge.json`);