feat: implement M2 Chrome browser extension

Build the CookieBridge Chrome extension (Manifest V3) with:

- Background service worker: cookie monitoring via chrome.cookies.onChanged,
  WebSocket connection to relay server with auto-reconnect, HTTP polling
  fallback, device registration and pairing flow
- Browser-compatible crypto: libsodium-wrappers-sumo for XChaCha20-Poly1305
  encryption, Ed25519 signing, X25519 key exchange (mirrors server's
  sodium-native API)
- Popup UI: device registration, connection status indicator (gray/blue/
  green/red), cookie/device/sync stats, one-click current site sync,
  whitelist quick-add, device pairing with 6-digit code
- Options page: server URL config, connection mode (auto/WS/polling),
  poll interval slider, auto-sync toggle, domain whitelist/blacklist
  management, paired device list, key export/import, data clearing
- Sync engine: LWW conflict resolution with Lamport clocks (same as
  server), bidirectional cookie sync with all paired peers, echo
  suppression to prevent sync loops
- Badge management: icon color reflects state (gray=not logged in,
  blue=connected, green=syncing with count, red=error)
- Build system: esbuild bundling for Chrome 120+, TypeScript with
  strict mode, clean type checking

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
徐枫
2026-03-17 16:30:18 +08:00
parent 1bd7a34de8
commit dc3be4d73f
36 changed files with 3549 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
declare module "libsodium-wrappers-sumo" {
interface KeyPair {
publicKey: Uint8Array;
privateKey: Uint8Array;
keyType: string;
}
interface Sodium {
ready: Promise<void>;
crypto_sign_keypair(): KeyPair;
crypto_box_keypair(): KeyPair;
crypto_scalarmult(privateKey: Uint8Array, publicKey: Uint8Array): Uint8Array;
crypto_generichash(hashLength: number, message: Uint8Array): Uint8Array;
crypto_aead_xchacha20poly1305_ietf_encrypt(
message: Uint8Array,
additionalData: Uint8Array | null,
nsec: Uint8Array | null,
nonce: Uint8Array,
key: Uint8Array,
): Uint8Array;
crypto_aead_xchacha20poly1305_ietf_decrypt(
nsec: Uint8Array | null,
ciphertext: Uint8Array,
additionalData: Uint8Array | null,
nonce: Uint8Array,
key: Uint8Array,
): Uint8Array;
crypto_aead_xchacha20poly1305_ietf_NPUBBYTES: number;
crypto_sign_detached(message: Uint8Array, privateKey: Uint8Array): Uint8Array;
crypto_sign_verify_detached(
signature: Uint8Array,
message: Uint8Array,
publicKey: Uint8Array,
): boolean;
randombytes_buf(length: number): Uint8Array;
}
const sodium: Sodium;
export default sodium;
}