shared/evaluable.ts is the single cross-browser rule for caller-supplied
JS: pickEvaluable accepts every code-param alias (expression/code/script/
function/js); wrapEvaluable passes bare expressions through and wraps
statement bodies in an async IIFE with the trailing value auto-returned.
Both dispatch paths consume it — browser-dispatch.ts (chrome.debugger
Runtime.evaluate, which also gains the plain 'evaluate' method alias
Firefox already accepted) and background-firefox.ts, whose previous
`return (${code})` wrap was a SyntaxError on any multi-statement body
and silently returned undefined for `expr;`. The Firefox path now always
settles promises in-page and routes rejections over the __hanzo_error
channel so callers see the real page-side error.
Tests: behavioral suite for the wrapper (evaluates wrapped output, not
string-matching); revive the three suites dead since the node bridge was
deleted — parity contracts repointed at browser-dispatch.ts, the IAM
contracts updated to the HIP-0111 canonical /v1/iam/oauth/token shape,
hub-wiring trimmed to the live background.ts surface, and the node-bridge
routing suite removed with its subject. 243 passing (was 186 + 3 dead
files).
2.9 KiB
Hanzo Extension
Browser extension (packages/browser) that joins the shared local zapd fabric.
One native primitive — no WebSocket, no localhost port, no mDNS, no CDP bridge.
Ships Chrome / Firefox / Safari. Build: esbuild (src/build.js, pnpm build);
tests: vitest.
Architecture (native ZAP)
extension ─connectNative("ai.hanzo.zap")─► native host ─UDS─► zapd ◄─ hanzo-mcp
shared/native-zap.ts— the ONE transport:connectNativeZap()opens the native port (singleton — one port; the router does evict-and-replace), registers as providerbrowser:<engine>/<host>/default, dispatches inbound ROUTE commands. Cross-browser (browser ∥ chrome).background.ts(Chrome) — dispatch viachrome.debugger(CDP→tab; shows the "debugging this browser" banner; WebKit-incompatible). To be removed — see WXT plan.background-firefox.ts— nativebrowser.*dispatch (no banner). The correct model.browser-dispatch.ts—chrome.debuggeractuation (Chrome-only).shared/evaluable.ts— the ONE evaluate rule:pickEvaluable(code-param aliases) +wrapEvaluable(bare expression passes through; statement bodies → async IIFE with the trailing value auto-returned). Both dispatch paths consume it, so Chrome and Firefox accept identical caller JS.
Wire to zapd: the binary ZAP router envelope (zap-proto/zapd/src/frame.rs); the
browser↔host hop is native-messaging JSON, base64'd, quarantined in the host. Host +
per-browser manifests come from zapd install-host (zap-proto/zapd).
Versioning
Patch only (X.Y.Z+1), never major. package.json is the source of truth; build.js
stamps it into every manifest (chrome/firefox/safari).
Cross-platform — WXT migration (canonical plan, not yet executed)
One WebExtension codebase, every engine. WXT = build/SDK layer (build matrix,
manifest gen, targets, MV3, Vite/TS); webextension-polyfill / @wxt-dev/browser =
browser.* normalization. Custom (keep): shared/native-zap.ts, and the Safari
SafariWebExtensionHandler.swift → ~/.zap/run/zapd.sock bridge. WXT builds Safari
but does NOT give WebKit chrome.debugger — so dispatch must become portable
browser.* (the Firefox model is already correct).
- Adopt WXT build matrix (chrome/firefox/safari) — replaces
build.js. - Collapse forks → one
background.ts; deletebackground-firefox.ts; runtimebrowseradapter. - Collapse dispatch → delete
chrome.debugger/browser-dispatch.ts; actuate overtabs/scripting/webNavigation/browser.*(portable, no banner, Safari-capable). - Transport stays per-platform: Chrome/Firefox
connectNative→ host →zapd.sock; SafariSafariWebExtensionHandler.swift→zapd.sock. - CI guards (last; go red until 1–4 land): fail if
chrome.debugger, thedebuggerpermission, orbackground-firefox.tsappears.
Large, multi-phase — do it as a focused pass, never half-merged.