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
This commit is contained in:
@@ -62,6 +62,11 @@ export interface VeloxTransport {
|
||||
|
||||
/** Fires on every state change. Returns an unsubscribe. */
|
||||
onStateChange(cb: (status: TransportStatus) => void): () => void;
|
||||
|
||||
/** WebSocket transport only (pairing has no meaning over native messaging's uds
|
||||
* socket, which has no token). Options renders these controls only when present. */
|
||||
pairWithCode?(code: string): Promise<void>;
|
||||
unpair?(): Promise<void>;
|
||||
}
|
||||
|
||||
// --- errors ---------------------------------------------------------------------------
|
||||
|
||||
@@ -97,6 +97,9 @@ export class WebSocketTransport implements VeloxTransport {
|
||||
private stopped = false;
|
||||
private connectPromise: Promise<void> | null = null;
|
||||
private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
/** Set only for the duration of pairWithCode(); consumed by pair(). docs/05 §4: "the
|
||||
* user clicks Allow (or types the code in the extension options)." */
|
||||
private pendingPairCode: string | null = null;
|
||||
|
||||
private readonly listeners = new Map<string, Set<EventListener>>();
|
||||
private readonly stateListeners = new Set<(status: TransportStatus) => void>();
|
||||
@@ -139,6 +142,29 @@ export class WebSocketTransport implements VeloxTransport {
|
||||
return this.connectPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options → "Pair" with a code typed from the daemon's dialog, for when the GUI isn't
|
||||
* running to click Allow (docs/05 §4). Drops any stored token first so the handshake
|
||||
* takes the pairing branch, then reconnects with the code attached.
|
||||
*/
|
||||
async pairWithCode(code: string): Promise<void> {
|
||||
this.disconnect();
|
||||
await this.deps.setToken(null); // drop any stale token so the handshake takes the pairing branch
|
||||
this.pendingPairCode = code;
|
||||
try {
|
||||
await this.connect();
|
||||
} finally {
|
||||
this.pendingPairCode = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Options → "Unpair": revoke the local token. The daemon's own record of it is
|
||||
* cleaned up on its side; this only ever forgets our copy. */
|
||||
async unpair(): Promise<void> {
|
||||
await this.deps.setToken(null);
|
||||
this.disconnect();
|
||||
}
|
||||
|
||||
disconnect(): void {
|
||||
this.stopped = true;
|
||||
if (this.reconnectTimer) {
|
||||
@@ -322,6 +348,7 @@ export class WebSocketTransport implements VeloxTransport {
|
||||
const params: SessionPairParams = {
|
||||
clientName: this.clientName,
|
||||
extensionId: this.deps.extensionId,
|
||||
code: this.pendingPairCode,
|
||||
};
|
||||
try {
|
||||
const res = (await rpc.request(
|
||||
|
||||
Reference in New Issue
Block a user