ext: S1 native-messaging spike (ADR 0003) + WebSocket transport
Spike S1 — run on the target machine through real snap confinement
(apparmor snap.firefox.firefox enforced; web-ext's direct-exec of the inner
binary bypasses it, so runs were forced through `snap run firefox`):
- manifest in ~/.mozilla/native-messaging-hosts/ -> WORKS; host launched
unconfined with real $HOME and real $XDG_RUNTIME_DIR, bound a socket in
the real /run/user/<uid>. Corroborated by the machine's 1Password host.
- ~/snap/firefox/common/.mozilla/native-messaging-hosts/ -> not read
- /usr/lib/mozilla/native-messaging-hosts/ -> not read
- flatpak path -> N/A (snap Firefox)
Decision: WebSocket stays the default; native messaging is an opportunistic
upgrade taken only when its handshake succeeds. docs/05 §4 corrected in this
commit to point the snap manifest at ~/.mozilla and mark /usr/lib as
deb/tarball-only. ADR carries a self-contained reproduction; the scratch
harness has been removed.
transport/ (build order item 1):
- types.ts VeloxTransport interface + error taxonomy
- rpc.ts JSON-RPC id correlation, per-call deadline, AbortSignal
- backoff.ts exponential backoff with jitter
- discovery.ts 52000-52016 scan ordering (last-good port first)
- websocket.ts scan -> session.hello -> auto-pair (token in
storage.local) -> reconnect; -32001 fatal, refused/
rate-limited pairing latches needsPairing (no retry storm);
a mid-handshake drop aborts hello immediately
- native.ts connectNative(); distinguishes "not installed" (fatal,
lets the picker fall through) from a crash (reconnect)
- index.ts createTransport() runtime picker + persisted Options override
Toolchain: package.json / tsconfig (strict) / vitest; webextension-polyfill
mocked. 38 tests, incl. the WS suite against a real loopback ws server.
No manifest.json yet, so CI's extension-lint guard stays a no-op.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_012Y9RU58hD1BuwP82DySUHk
This commit is contained in:
@@ -1,2 +1,37 @@
|
||||
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
|
||||
|
||||
```ts
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user