Files
vdm/extension
samiandClaude Sonnet 5 25c171f742 ext: pairing-restart test + AMO permission justification
tests/transport/storage.test.ts covers the storage half of "the pairing
token survives a browser restart" (round-trip, unpair-clears, corrupted
override falls back to auto). websocket.test.ts adds the transport half:
a fresh WebSocketTransport instance over the same backing store reuses
the persisted token with no re-pairing, plus pairWithCode/unpair
coverage. "Wrong token rejected and rate-limited" was already covered
(websocket.test.ts's NotPaired/RateLimited cases).

docs/amo-permissions.md is the submission-ready permission justification
for AMO's Notes to Reviewer field, covering every permission in
manifest.json plus what was deliberately not requested and how cookie/
header data is handled.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ed8KEmAW48v4YHdxLtqsMB
2026-09-11 12:25:42 +04:00
..

Owner: lane EXT. See ../docs/agents/AGENT-EXT.md and ../docs/05-extension-spec.md. Firefox MV3, TypeScript. Zero download logic.

Layout

src/background/transport/   Transport interface + WebSocket and native-messaging impls,
                            runtime picker. See docs/adr/0003 for why there are two.
src/shared/protocol/        GENERATED from ../contracts — never hand-edit.
tests/                      vitest; webextension-polyfill is mocked in tests/setup.ts.

Develop

npm ci
npm run typecheck        # tsc --noEmit, strict
npm test                 # vitest run
npm run lint             # web-ext lint (AMO rules) — needs manifest.json

Build against tools/mockd over the WebSocket transport; veloxd is not required.

Transport quick start

import { createTransport } from './src/background/transport/index.js';

const t = await createTransport();            // reads the Options override; default 'auto'
t.onStateChange((s) => renderDot(s));
const rules = await t.call('capture.getRules', {});

createTransport resolves with a live, self-reconnecting transport. When veloxd is not up yet it still resolves (WebSocket, status disconnected, retrying) — callers render the red dot. It rejects only when the user explicitly forced native messaging and that failed. Capture code treats every call() rejection as fail-open.