diff --git a/clients/chrome-extension/background/worker.ts b/clients/chrome-extension/background/worker.ts index d48469791b7..a89774ac32d 100644 --- a/clients/chrome-extension/background/worker.ts +++ b/clients/chrome-extension/background/worker.ts @@ -256,7 +256,7 @@ function missingTokenMessage(kind: RelayModeKind): string { if (kind === 'cloud') { return 'Sign in with Vellum (cloud) before connecting'; } - return 'Pair the Vellum daemon (self-hosted) before connecting'; + return 'Pair the Vellum assistant (self-hosted) before connecting'; } async function connect(): Promise { diff --git a/clients/chrome-extension/tsconfig.json b/clients/chrome-extension/tsconfig.json index 7476c828e31..300f3cb23e7 100644 --- a/clients/chrome-extension/tsconfig.json +++ b/clients/chrome-extension/tsconfig.json @@ -15,12 +15,9 @@ "types": [] }, "include": [ - "background/cdp-proxy.ts", - "background/cloud-auth.ts", - "background/host-browser-dispatcher.ts", - "background/relay-connection.ts", - "background/self-hosted-auth.ts", - "background/__tests__/**/*.ts", + "background/**/*.ts", + "popup/**/*.ts", "types/**/*.d.ts" - ] + ], + "exclude": ["dist/**", "node_modules/**"] } diff --git a/clients/chrome-extension/types/chrome-globals.d.ts b/clients/chrome-extension/types/chrome-globals.d.ts index 426b4edfdaa..20e4cdeed0b 100644 --- a/clients/chrome-extension/types/chrome-globals.d.ts +++ b/clients/chrome-extension/types/chrome-globals.d.ts @@ -4,58 +4,244 @@ * Minimal ambient declarations for the subset of the Chrome Extension API * surface used by the Vellum browser-relay extension's typed modules. * - * This is intentionally narrow — it covers what's needed by - * background/cloud-auth.ts, background/self-hosted-auth.ts, and their tests. - * The full @types/chrome package is an option for the future if we type-check - * more of the package. + * This is intentionally narrow — it covers what's needed by the + * typechecked files under `background/` and `popup/`, not the full + * Chrome API surface. The full `@types/chrome` package is an option for + * the future if we type-check more of the package or need additional + * API surface that this file doesn't cover. + * + * Note: `debugger` is a reserved word in TypeScript so we cannot declare + * a `namespace chrome.debugger`. Instead, `chrome` is declared as a + * top-level `const` whose type is an interface — that shape can include + * a `debugger` property because object literal property names may use + * reserved words. */ -declare namespace chrome { - namespace storage { - interface StorageArea { - get(keys?: string | string[] | Record | null): Promise>; - set(items: Record): Promise; - remove(keys: string | string[]): Promise; - clear(): Promise; - } - const local: StorageArea; - const sync: StorageArea; - const session: StorageArea; - } - - namespace identity { - interface WebAuthFlowDetails { - url: string; - interactive?: boolean; - } - function getRedirectURL(path?: string): string; - function launchWebAuthFlow(details: WebAuthFlowDetails): Promise; - } - - namespace runtime { - interface LastError { - message?: string; - } - const lastError: LastError | undefined; - - interface PortMessageEvent { - addListener(listener: (message: unknown) => void): void; - removeListener(listener: (message: unknown) => void): void; - } - - interface PortDisconnectEvent { - addListener(listener: (port: Port) => void): void; - removeListener(listener: (port: Port) => void): void; - } - - interface Port { - name: string; - onMessage: PortMessageEvent; - onDisconnect: PortDisconnectEvent; - postMessage(message: unknown): void; - disconnect(): void; - } - - function connectNative(application: string): Port; - } +interface ChromeStorageArea { + get(keys?: string | string[] | Record | null): Promise>; + set(items: Record): Promise; + remove(keys: string | string[]): Promise; + clear(): Promise; +} + +interface ChromeStorageChange { + newValue?: unknown; + oldValue?: unknown; +} + +type ChromeStorageAreaName = 'local' | 'sync' | 'managed' | 'session'; + +interface ChromeStorageChangedEvent { + addListener( + listener: ( + changes: Record, + areaName: ChromeStorageAreaName, + ) => void, + ): void; + removeListener( + listener: ( + changes: Record, + areaName: ChromeStorageAreaName, + ) => void, + ): void; +} + +interface ChromeStorageNamespace { + local: ChromeStorageArea; + sync: ChromeStorageArea; + session: ChromeStorageArea; + onChanged: ChromeStorageChangedEvent; +} + +interface ChromeIdentityWebAuthFlowDetails { + url: string; + interactive?: boolean; +} + +interface ChromeIdentityNamespace { + getRedirectURL(path?: string): string; + launchWebAuthFlow(details: ChromeIdentityWebAuthFlowDetails): Promise; +} + +interface ChromeRuntimeLastError { + message?: string; +} + +interface ChromeRuntimePortMessageEvent { + addListener(listener: (message: unknown) => void): void; + removeListener(listener: (message: unknown) => void): void; +} + +interface ChromeRuntimePortDisconnectEvent { + addListener(listener: (port: ChromeRuntimePort) => void): void; + removeListener(listener: (port: ChromeRuntimePort) => void): void; +} + +interface ChromeRuntimePort { + name: string; + onMessage: ChromeRuntimePortMessageEvent; + onDisconnect: ChromeRuntimePortDisconnectEvent; + postMessage(message: unknown): void; + disconnect(): void; +} + +interface ChromeRuntimeMessageSender { + tab?: ChromeTab; + frameId?: number; + id?: string; + url?: string; + tlsChannelId?: string; + origin?: string; +} + +type ChromeRuntimeMessageListener = ( + message: Record & { type?: string }, + sender: ChromeRuntimeMessageSender, + sendResponse: (response?: unknown) => void, +) => boolean | void; + +interface ChromeRuntimeOnMessageEvent { + addListener(listener: ChromeRuntimeMessageListener): void; + removeListener(listener: ChromeRuntimeMessageListener): void; +} + +interface ChromeRuntimeManifest { + version: string; + [key: string]: unknown; +} + +interface ChromeRuntimeNamespace { + readonly lastError: ChromeRuntimeLastError | undefined; + connectNative(application: string): ChromeRuntimePort; + onMessage: ChromeRuntimeOnMessageEvent; + // Generic over the response type so callers can narrow the callback + // argument without casting. Matches the de-facto shape used by the + // official @types/chrome package. + sendMessage( + message: unknown, + responseCallback?: (response: TResponse) => void, + ): void; + getManifest(): ChromeRuntimeManifest; } + +interface ChromeTab { + id?: number; + windowId?: number; + url?: string; + active?: boolean; + title?: string; + index?: number; +} + +interface ChromeTabsQueryInfo { + active?: boolean; + lastFocusedWindow?: boolean; + url?: string | string[]; + windowId?: number; + currentWindow?: boolean; + [key: string]: unknown; +} + +interface ChromeTabsCreateProperties { + url?: string; + active?: boolean; + windowId?: number; + index?: number; +} + +interface ChromeTabsUpdateProperties { + url?: string; + active?: boolean; + [key: string]: unknown; +} + +interface ChromeTabsCaptureVisibleTabOptions { + format?: 'jpeg' | 'png'; + quality?: number; +} + +interface ChromeTabsNamespace { + query(queryInfo: ChromeTabsQueryInfo): Promise; + get(tabId: number): Promise; + create(createProperties: ChromeTabsCreateProperties): Promise; + update(tabId: number, updateProperties: ChromeTabsUpdateProperties): Promise; + captureVisibleTab( + windowId: number, + options?: ChromeTabsCaptureVisibleTabOptions, + ): Promise; +} + +interface ChromeWindowsNamespace { + readonly WINDOW_ID_CURRENT: number; + readonly WINDOW_ID_NONE: number; +} + +interface ChromeCookie { + name: string; + value: string; + domain: string; + hostOnly?: boolean; + path: string; + secure: boolean; + httpOnly: boolean; + sameSite?: 'no_restriction' | 'lax' | 'strict' | 'unspecified'; + session?: boolean; + expirationDate?: number; + storeId?: string; +} + +interface ChromeCookiesGetAllDetails { + domain?: string; + name?: string; + path?: string; + secure?: boolean; + session?: boolean; + storeId?: string; + url?: string; +} + +interface ChromeCookiesSetDetails { + url: string; + name?: string; + value?: string; + domain?: string; + path?: string; + secure?: boolean; + httpOnly?: boolean; + sameSite?: 'no_restriction' | 'lax' | 'strict' | 'unspecified'; + expirationDate?: number; + storeId?: string; +} + +interface ChromeCookiesNamespace { + getAll(details: ChromeCookiesGetAllDetails): Promise; + set(details: ChromeCookiesSetDetails): Promise; +} + +interface ChromeDebuggerDebuggee { + tabId?: number; + extensionId?: string; + targetId?: string; +} + +interface ChromeDebuggerNamespace { + attach(target: ChromeDebuggerDebuggee, requiredVersion: string): Promise; + detach(target: ChromeDebuggerDebuggee): Promise; + sendCommand( + target: ChromeDebuggerDebuggee, + method: string, + commandParams?: Record, + ): Promise; +} + +interface ChromeGlobal { + storage: ChromeStorageNamespace; + identity: ChromeIdentityNamespace; + runtime: ChromeRuntimeNamespace; + tabs: ChromeTabsNamespace; + windows: ChromeWindowsNamespace; + cookies: ChromeCookiesNamespace; + debugger: ChromeDebuggerNamespace; +} + +declare const chrome: ChromeGlobal;