macOS support for nb (critical): installer + host registration; it already works with a manual install
@kyle nb has no macOS support today, and that blocks every Mac user from the one thing nb exists for: driving their real, signed-in browser. This is a critical quality-of-life gap for us and we need it as soon as possible.
The good news: the bridge, host and extension already work on macOS. On 2026-10-13 a manual install (about 90 lines of shell, below) brought nb up on a Mac in minutes, and it then drove a real signed-in task end to end. What's missing is the macOS install layer plus a few Windows-only assumptions.
Proof it works on macOS
Mac: Apple Silicon, Google Chrome, Adom Bridge managed by Hydrogen, native-browser bridge 0.16.33, extension 0.2.15, host on the bridge's managed Node 22.11.0.
After the manual install, nbrowser_readiness returned state:"ready", extensionConnected:true, profile chrome:[email protected], extension 0.2.15 = expected, no host exit, no zombies. These all worked against a signed-in site (Gospel Software) in a background window: nbrowser_profiles, open_window (background, no focus change), navigate, wait_for, eval, click, screenshot, list_tabs, login_state, owned, close_window.
What needs to change
- macOS installer + host launcher (the actual blocker).
install/install.ps1registers through HKCU andhost/host-manifest.template.jsonhardcodesnative-host.bat. On macOS registration is a file,~/Library/Application Support/Google/Chrome/NativeMessagingHosts/inc.adom.native_browser.json(Edge:~/Library/Application Support/Microsoft Edge/NativeMessagingHosts/), whosepathmust be an absolute, executable launcher. The script at the end of this issue does it all; it could ship asinstall/install-mac.sh+host/native-host.sh. node-path.txtis written with Windows backslashes on every OS.bridge/server.js:3283:fs.writeFileSync(NODEPATHFILE, process.execPath.split('/').join('\\')). On the Mac it contains\Users\drew\.adom\adom-runtimes\node-22.11.0\bin\node, which no launcher can exec. Only convert whenprocess.platform === 'win32'.- Browser detection is Windows-only, so readiness lies on a Mac. With Chrome installed it reports
chromeInstalled:false,nativeBrowserPresent:false,chromeNeeded:true, zero profiles, and the hint tells the AI to offer installing Chrome. Look for/Applications/Google Chrome.appand/Applications/Microsoft Edge.app(plus~/Applications). Profiles are in~/Library/Application Support/Google/Chrome/Local State,profile.info_cache(same JSON as Windows). nbrowser_credentialssilently returnscount:0on macOS (ok:true), so the AI concludes there are no saved logins. It readsLogin Datafrom the Windows path only. Either read~/Library/Application Support/Google/Chrome/<profile>/Login Data(key from the Keychain item "Chrome Safe Storage"), or refuse with an explicitunsupported_platformerrorCode. Same for the autologin path (host/native-host.js~159-260:LOCALAPPDATA, PowerShell, DPAPI).- Host Windows-only bits. The parent-process browser detection (
host/native-host.js~118-150) shells out to PowerShellGet-CimInstance. It only runs when the extension doesn't report its browser, so it's harmless today, but aps -o ppid=,comm=walk would make it portable. The singleton check already has a non-win32 branch, good. - The installing-the-extension skill is Windows-only (UIA clicks on
chrome://extensions, PowerShell). A Mac AI has no path, so it falls back to "install Chrome". Add the Mac flow: run the installer, then Load unpacked (Cmd+Shift+G in the picker to reach the hidden~/.adomfolder). Heads-up: a cloud agent's safety classifier refused to write the extension + native-messaging host remotely (classified as persistence, twice, even with the user's OK). Plan for a user-run installer, or an ab-side install verb the user approves in ab's own UI. - Flip
platforms.macos.supportedto true once 1-3 and 6 land. Edge on Mac is the same work with Edge's paths. - Minor, not Mac-specific: on a sign-in page
nbrowser_evalfailed with the raw Chrome errorCannot access a chrome-extension:// URL of different extension(no errorCode, no hint), likely a password manager's iframe blockingchrome.debugger.attach. A structured errorCode + hint would help ("another extension's frame is on this page; use native verbs, or retry after sign-in").
The manual install that works
Downloads the extension + host from this page's files API, checks each against the sha256 of the files reviewed on 2026-10-13, writes a POSIX launcher (which normalizes the backslashes from item 2), and registers the host with Chrome. --uninstall reverses it. Dry-run tested in a scratch $HOME, then run on the Mac.
#!/bin/bash
set -euo pipefail
WIKI="https://wiki.adom.inc/api/v1/pages/nb-bridge/files"
BASE="$HOME/.adom/nb-bridge"
NMH_DIR="$HOME/Library/Application Support/Google/Chrome/NativeMessagingHosts"
HOST_NAME="inc.adom.native_browser"
EXT_ID="nncjgceobmlenfliamnibncjojpedbcc" # pinned by the manifest key
if [ "${1:-}" = "--uninstall" ]; then
rm -f "$NMH_DIR/$HOST_NAME.json"; rm -rf "$BASE"
echo "Removed. Also remove the extension in chrome://extensions."; exit 0
fi
while read -r sum f; do
mkdir -p "$BASE/$(dirname "$f")"
curl -fsSL "$WIKI/$f" -o "$BASE/$f"
[ "$(shasum -a 256 "$BASE/$f" | cut -d' ' -f1)" = "$sum" ] || { echo "checksum mismatch: $f" >&2; exit 1; }
done <<'EOF'
1d0e49498c57fa57c28debff62489b7527f912eba34617a14377689d3906d8e1 extension/icons/README.md
bab1cd05abc5c12257b115726a00a4978e9e3a5ffc17d9b8000b35aad00d5089 extension/icons/icon128.png
4c829ed25e9f8afd3a85e2a8c14724e7010598fa7cb607b5515eacf23cbdd1b9 extension/icons/icon16.png
d743b3dfd449fbce4ba22fdc57f2d7cc69e316a5c6dcc2d2252092de66e850fc extension/icons/icon48.png
b6e30e75a3d628e2a8de03a129433761fa173540af7cab62934097e27345cba6 extension/manifest.json
646424d499c57362e8c94054796817d62b146cfbce9ee7fd001a3f241c5b2f5b extension/src/cdp.js
7289713828faf4e1de95715ee9209f9ec4e635f8ccb6bb4cfcc36c70feb24c61 extension/src/native.js
534f8eaacdeb2b701383ba69c2a6a4fba38d33c15295656efaee3ffc058ff6b6 extension/src/offscreen-record.html
4d6ab7e44c90c176deb795e6464115c59e82a62ebeb96a3f6bbb6d3a623908c2 extension/src/offscreen-record.js
6d21343c2769bd407257f2747e5339f2f14dbf7e5c3ce2022246aff6f3db652e extension/src/popup.html
bba2fa1dad1ebfb13463c34a08dc42a3be234a2b349e15ac6a82dcd0b42d7c7f extension/src/popup.js
b866ad011e95bebedaa585a93c619d0e8ae914cec92fe65299f77a060273b64b extension/src/record.js
70c02a6a349ec2a4ac771be4665eb50374a5a878ee3fc049e8d6f33234e0d0bf extension/src/sw.js
331a4b878891f6947dbcace400f052fd6a96993fbaadbd0cf76480bdc0233b49 host/native-host.js
EOF
cat > "$BASE/host/native-host.sh" <<'LAUNCHER'
#!/bin/sh
# macOS counterpart of native-host.bat: start native-host.js on Bridge's Node.
DIR="$(cd "$(dirname "$0")" && pwd)"
NPFILE="$HOME/.adom/bridges/native-browser/node-path.txt"
NODE=""
[ -f "$NPFILE" ] && NODE="$(tr '\\' '/' < "$NPFILE" | tr -d '\r\n')" # bridge writes backslashes (item 2)
if [ -z "$NODE" ] || [ ! -x "$NODE" ]; then
NODE=""
for c in /opt/homebrew/bin/node /usr/local/bin/node /usr/bin/node; do [ -x "$c" ] && { NODE="$c"; break; }; done
fi
[ -n "$NODE" ] || NODE="node"
exec "$NODE" "$DIR/native-host.js" "$@"
LAUNCHER
chmod +x "$BASE/host/native-host.sh"
mkdir -p "$NMH_DIR"
cat > "$NMH_DIR/$HOST_NAME.json" <<JSON
{
"name": "$HOST_NAME",
"description": "Adom native-browser bridge host (stdio <-> Bridge)",
"path": "$BASE/host/native-host.sh",
"type": "stdio",
"allowed_origins": ["chrome-extension://$EXT_ID/"]
}
JSON
echo "Installed. Now: chrome://extensions > Developer mode > Load unpacked > Cmd+Shift+G > $BASE/extension"
Filed from Drew's Claude Code session (Drew's Mac, 2026-10-13). Happy to test a macOS build the moment there is one.