entry.ts
1import { setupMessageHandler, setRestoreReady } from "./messages";2import { setupAutoLock } from "./alarms";3import { setupPasskeyProxy } from "./passkey";4import { setupCommands } from "./commands";5import { setupClipboardClearAlarm } from "./clipboard";6import { restoreFromSession, isUnlocked, lock } from "./vault";7import { ensureSettingsLoaded } from "./vault";8import { isRecoveryConfirmPending } from "./recovery-pending";9import { denyAllPendingPasskeyRequests } from "./passkey";10import { hydratePersistedSyncState, setSyncRunner } from "./sync-trigger";11import { productionSyncRunner } from "./chirp-sync-runner";1213// Keep alive for MV3 service worker14chrome.runtime.onInstalled.addListener(() => {});1516// The chrome.idle.onStateChanged listener lives in ./alarms (the architectural17// home for auto-lock timing). Keeping it in only one place avoids drift.1819// Lock-on-close via port disconnect. The popup opens a "popup" named port;20// when that port disconnects (popup closed), lock if lockOnClose is enabled.21// Previously declared in index.ts which was never bundled — moved here.22chrome.runtime.onConnect.addListener((port) => {23 if (port.name !== "popup") return;24 port.onDisconnect.addListener(async () => {25 await denyAllPendingPasskeyRequests();26 const s = await ensureSettingsLoaded();27 if (!isUnlocked() || s.lockOnClose === false) return;28 if (await isRecoveryConfirmPending()) return;29 lock();30 });31});3233export function start(): void {34 // MV3: the chrome.runtime.onMessage listener MUST be registered during the35 // worker's initial SYNCHRONOUS evaluation. If registration is deferred behind36 // an await, a popup/content message that wakes a cold worker is dispatched37 // before the listener exists → "receiving end does not exist", and the popup38 // misreads the failed HAS_VAULT as "no vault" and shows onboarding.39 //40 // So: kick off session-restore, hand its promise to the message layer as a41 // gate, and register the handler synchronously. Unlock-dependent handlers42 // await the gate (see handleMessage), which preserves the SEV-3-B fix — no43 // spurious lock screen — without delaying listener registration.44 const restored = restoreFromSession();45 setRestoreReady(restored);46 setupMessageHandler();47 void setupAutoLock();48 setupCommands();49 // HV-2: clipboard auto-clear backstop must survive worker eviction; the50 // listener has to be registered during initial synchronous evaluation so51 // a cold worker woken BY the alarm still handles it.52 setupClipboardClearAlarm();53 void setupPasskeyProxy();54 // Wire the chirp-sync production runner into the dispatcher. Without this55 // `triggerSyncIfReachable` is a no-op (the runner slot starts null) — the56 // pairing/transport/coordinator stack ships dormant until this assignment.57 setSyncRunner(productionSyncRunner);58 void restored.then(() => hydratePersistedSyncState()).catch(() => undefined);59}6061start();