/** * VerificationCatcherService — the ambient "code just arrived" loop. * * Subscribes DIRECTLY to `email:new` (not through the hooks batch — its 5 s * delay defeats the point of an OTP catcher) and, for each fresh message: * cheap subject/sender pre-filter → one-shot dedup gate → single body fetch * → code/link extraction → concealed clipboard copy + desktop notification. * * Requires the IMAP IDLE watcher to be running to receive events at all; * app wiring logs when verification is enabled but the watcher is not. * * The extracted value is never written to logs — only its provenance. */ import type { EmailAddress, VerificationConfig } from '../types/index.js'; import type ClipboardService from './clipboard.service.js'; import type DialogService from './dialog.service.js'; import type ImapService from './imap.service.js'; import type NotifierService from './notifier.service.js'; export interface VerificationHit { kind: 'code' | 'link'; /** Canonical value for clipboard/paste. */ value: string; /** Human-facing form (code as printed in the mail, or the URL). */ display: string; from: EmailAddress; subject: string; /** Short sender label for notifications ("GitHub", "notion.so"). */ service: string; account: string; mailbox: string; date: string; confidence?: 'high' | 'medium' | 'low'; evidence?: string; } export default class VerificationCatcherService { private readonly config; private readonly imapService; private readonly notifier; private readonly clipboard; private readonly dialog; /** Serializes event processing; also the drain point for settled(). */ private queue; private started; /** * Named handler so stop() detaches exactly this subscription — the channel * is shared with the sync engine and hooks. */ private readonly onNewEmail; constructor(config: VerificationConfig, imapService: ImapService, notifier: NotifierService, clipboard: ClipboardService, dialog: DialogService); start(): void; stop(): void; /** Resolves when every event received so far has been fully processed. */ settled(): Promise; private processEvent; private processMessage; private act; private actOnCode; private actOnLink; private copyAndNotify; /** Notification-only outcome: the hit is surfaced but nothing was touched. */ private notifyFound; } /** * Scan recent messages for a code/link, newest first. Read-only and * idempotent: does NOT touch the one-shot dedup state. IMAP SINCE is * day-granular, so results are re-filtered client-side against `lookbackMs`. */ export declare function scanRecent(imapService: ImapService, accounts: string[], mailbox: string, lookbackMs: number, opts?: { copyLinks?: boolean; maxBodies?: number; }): Promise; /** * Scan now, then wait up to `waitSeconds` for a code/link to arrive. * * Event-augmented polling: an `email:new` event (watcher running) wakes the * loop within ~1 s; without the watcher the 5 s poll is the correctness * backstop, so the tool works either way. Re-scans accept only messages * dated after the call started (minus one minute of clock slack) — the full * `lookbackMs` window applies to the initial scan only. The transient * listener is removed on every path. */ export declare function waitForVerification(imapService: ImapService, accounts: string[], mailbox: string, lookbackMs: number, waitSeconds: number, opts?: { copyLinks?: boolean; }): Promise; //# sourceMappingURL=verification-catcher.service.d.ts.map