mirror of
https://github.com/hanzoai/extension.git
synced 2026-10-31 21:57:06 +00:00
The extension reached the router through a native-messaging host the browser had to exec. Snap and Flatpak browsers refuse to exec anything outside their sandbox, so it never connected there, and the host needed a manifest per browser plus a daemon someone had to start. The router is now embedded in hanzo-mcp and the other Hanzo processes, and one of them serves a WebSocket door on 127.0.0.1:20000+uid%10000. shared/socket.ts is the one transport: each binary message is one ZAP router envelope, and ensureZap() is the one reconciler on one clock (a 20 s heartbeat while registered, which also keeps the MV3 worker alive; 250 ms doubling to 30 s after a drop, so a router moving to another process is followed in under a second). Pairing is explicit. `hanzo-mcp pair` prints ws://127.0.0.1:<port>/#<token>; the popup takes it once, and shared/zap.ts keeps it in storage.local. The router proves the token first (HMAC over both nonces) and the extension says nothing to a door that cannot, so a process squatting on the port is never obeyed; the token never crosses the wire. The node is browser/<host>/<engine>-<id>, its name minted once per install. The nativeMessaging permission, native-zap.ts and the Safari Swift bridge are gone: WebKit opens ws://127.0.0.1 from an extension's secure context, so Safari runs the same code. Browser extension 1.9.58. Claude-Session: https://claude.ai/code/session_01Dc8DXc3fEQCRHUa6XWK4Ku
26 lines
1.0 KiB
TypeScript
26 lines
1.0 KiB
TypeScript
import { existsSync, readFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
/**
|
|
* Safari joins the ZAP router the way Chrome and Firefox do: a WebSocket to the
|
|
* router's loopback door (shared/socket.ts). WebKit opens ws://127.0.0.1 from a
|
|
* secure context, which an extension's pages are, so Safari needs no native
|
|
* handler of its own and ships the converter's stock one untouched.
|
|
*/
|
|
const root = join(__dirname, '..', 'src');
|
|
const build = readFileSync(join(root, 'build.js'), 'utf8');
|
|
|
|
describe('Safari', () => {
|
|
it('carries no native bridge of its own', () => {
|
|
expect(existsSync(join(root, 'safari'))).toBe(false);
|
|
expect(build).not.toMatch(/SafariWebExtensionHandler\.swift/);
|
|
});
|
|
|
|
it('separates a missing converter from a failed conversion', () => {
|
|
// One `catch {}` around both reported "Xcode not available" for every
|
|
// failure, and the project read as generated when nothing was written.
|
|
expect(build).toMatch(/haveConverter/);
|
|
});
|
|
});
|