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 type { LocalIdentity } from "../shared/pairing-handshake";24import { getRelaySettings } from "./relay-settings";2526function hexToBytesLocal(hex: string): Uint8Array {27 const out = new Uint8Array(hex.length / 2);28 for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);29 return out;30}313233function push(type: string, payload: unknown): void {34 try {35 const maybe = chrome.runtime.sendMessage({ type, payload }) as unknown;36 if (maybe && typeof (maybe as Promise<unknown>).catch === "function") {37 (maybe as Promise<unknown>).catch(() => undefined);38 }39 } catch {40 41 }42}434445export function productionCodePairingDeps(): CodePairingDeps {46 return {47 openSocket: (ip, port) => openPairingSocket(ip, port),48 async getRelayUrl() {49 return (await getRelaySettings()).relayUrl;50 },51 async getLocalIdentity(): Promise<LocalIdentity> {52 if (!isUnlocked()) throw new Error("vault_locked");53 const masterKeyHex = getRootKeyHex();54 if (!masterKeyHex) throw new Error("vault_locked");55 56 57 const masterKey = hexToBytesLocal(masterKeyHex);58 try {59 const base = await buildLocalIdentity(masterKey);60 const vaultId = await computeVaultId(masterKey);61 return { ...base, vaultId };62 } finally {63 masterKey.fill(0);64 }65 },66 persistDevice: (d) => upsertPairedDevice(d),67 cacheLastKnownIp: (pubkeyHex, ip) => setLastKnownIp(pubkeyHex, ip),68 };69}7071727374let pendingConfirm: ((confirmed: boolean) => void) | null = null;75let inFlight = false;7677function clearPairingState(): void {78 pendingConfirm = null;79 inFlight = false;80}8182export function isCodePairingInFlight(): boolean {83 return inFlight;84}85868788899091export function startCodePairing(92 code: string,93 deps: CodePairingDeps = productionCodePairingDeps(),94): void {95 inFlight = true;96 const cb: CodePairingCallbacks = {97 onSasReady: (sas) => push("CODE_PAIR_SAS", { sas }),98 onComplete: (device) => {99 push("CODE_PAIR_RESULT", { status: "complete", device });100 clearPairingState();101 },102 onError: (error) => {103 push("CODE_PAIR_RESULT", { status: "error", error });104 clearPairingState();105 },106 };107 108 const confirm = (_sas: string): Promise<boolean> =>109 new Promise<boolean>((resolve) => { pendingConfirm = resolve; });110111 void pairWithCode(code, cb, deps, confirm).finally(() => {112 113 114 if (inFlight) clearPairingState();115 });116}117118119export function resolveCodePairingConfirm(confirmed: boolean): void {120 const resolve = pendingConfirm;121 pendingConfirm = null;122 if (resolve) resolve(confirmed);123}