12345678910111213import {14 pairWithCode,15 type CodePairingDeps,16 type CodePairingCallbacks,17} from "./code-pairing";18import { openPairingSocket } from "../shared/pairing-dial";19import { upsertPairedDevice, setLastKnownIp } from "./paired-devices";20import { buildLocalIdentity } from "./device-identity";21import { computeVaultId } from "../shared/sync-identity";22import { isUnlocked, getRootKeyHex } from "./vault";23import { probeSubnetForPort, wsPortOpen } from "./desktop-pairing-probe";24import { discoverPeachSync } from "./desktop-pairing-mdns";25import { getLocalSubnet } from "../shared/local-network";26import type { LocalIdentity } from "../shared/pairing-handshake";2728function hexToBytesLocal(hex: string): Uint8Array {29 const out = new Uint8Array(hex.length / 2);30 for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);31 return out;32}333435function push(type: string, payload: unknown): void {36 try {37 const maybe = chrome.runtime.sendMessage({ type, payload }) as unknown;38 if (maybe && typeof (maybe as Promise<unknown>).catch === "function") {39 (maybe as Promise<unknown>).catch(() => undefined);40 }41 } catch {42 43 }44}454647export function productionCodePairingDeps(): CodePairingDeps {48 return {49 async findHost(port) {50 51 52 53 if (await wsPortOpen("127.0.0.1", port, 800)) return "127.0.0.1";54 55 const discovered = await discoverPeachSync();56 if (discovered && (await wsPortOpen(discovered.ip, port, 800))) return discovered.ip;57 58 const subnet = await getLocalSubnet();59 if (!subnet) return null;60 return probeSubnetForPort(subnet, port);61 },62 openSocket: (ip, port) => openPairingSocket(ip, port),63 async getLocalIdentity(): Promise<LocalIdentity> {64 if (!isUnlocked()) throw new Error("vault_locked");65 const masterKeyHex = getRootKeyHex();66 if (!masterKeyHex) throw new Error("vault_locked");67 68 69 const masterKey = hexToBytesLocal(masterKeyHex);70 try {71 const base = await buildLocalIdentity(masterKey);72 const vaultId = await computeVaultId(masterKey);73 return { ...base, vaultId };74 } finally {75 masterKey.fill(0);76 }77 },78 persistDevice: (d) => upsertPairedDevice(d),79 cacheLastKnownIp: (pubkeyHex, ip) => setLastKnownIp(pubkeyHex, ip),80 };81}8283848586let pendingConfirm: ((confirmed: boolean) => void) | null = null;87let inFlight = false;8889function clearPairingState(): void {90 pendingConfirm = null;91 inFlight = false;92}9394export function isCodePairingInFlight(): boolean {95 return inFlight;96}979899100101102103export function startCodePairing(104 code: string,105 deps: CodePairingDeps = productionCodePairingDeps(),106): void {107 inFlight = true;108 const cb: CodePairingCallbacks = {109 onSasReady: (sas) => push("CODE_PAIR_SAS", { sas }),110 onComplete: (device) => {111 push("CODE_PAIR_RESULT", { status: "complete", device });112 clearPairingState();113 },114 onError: (error) => {115 push("CODE_PAIR_RESULT", { status: "error", error });116 clearPairingState();117 },118 };119 120 const confirm = (_sas: string): Promise<boolean> =>121 new Promise<boolean>((resolve) => { pendingConfirm = resolve; });122123 void pairWithCode(code, cb, deps, confirm).finally(() => {124 125 126 if (inFlight) clearPairingState();127 });128}129130131export function resolveCodePairingConfirm(confirmed: boolean): void {132 const resolve = pendingConfirm;133 pendingConfirm = null;134 if (resolve) resolve(confirmed);135}