From 6b06b4e3ad3ebe63b12968bce335498f3798fdf3 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Wed, 1 Oct 2025 13:22:46 +0200 Subject: [PATCH] fix: TS code after napi changes --- apps/desktop/desktop_native/napi/index.d.ts | 382 +++++++++--------- apps/desktop/desktop_native/napi/package.json | 2 +- apps/desktop/desktop_native/napi/src/lib.rs | 138 ++++--- .../autofill/main/main-ssh-agent.service.ts | 2 +- .../desktop/src/main/native-messaging.main.ts | 59 +-- .../main/autofill/native-autofill.main.ts | 16 +- 6 files changed, 306 insertions(+), 293 deletions(-) diff --git a/apps/desktop/desktop_native/napi/index.d.ts b/apps/desktop/desktop_native/napi/index.d.ts index 2212c03f4f8..f409cf814fb 100644 --- a/apps/desktop/desktop_native/napi/index.d.ts +++ b/apps/desktop/desktop_native/napi/index.d.ts @@ -1,142 +1,46 @@ -/* tslint:disable */ -/* eslint-disable */ - /* auto-generated by NAPI-RS */ - -export declare namespace passwords { - /** The error message returned when a password is not found during retrieval or deletion. */ - export const PASSWORD_NOT_FOUND: string - /** - * Fetch the stored password from the keychain. - * Throws {@link Error} with message {@link PASSWORD_NOT_FOUND} if the password does not exist. - */ - export function getPassword(service: string, account: string): Promise - /** Save the password to the keychain. Adds an entry if none exists otherwise updates the existing entry. */ - export function setPassword(service: string, account: string, password: string): Promise - /** - * Delete the stored password from the keychain. - * Throws {@link Error} with message {@link PASSWORD_NOT_FOUND} if the password does not exist. - */ - export function deletePassword(service: string, account: string): Promise - /** Checks if the os secure storage is available */ - export function isAvailable(): Promise -} -export declare namespace biometrics { - export function prompt(hwnd: Buffer, message: string): Promise - export function available(): Promise - export function setBiometricSecret(service: string, account: string, secret: string, keyMaterial: KeyMaterial | undefined | null, ivB64: string): Promise - /** - * Retrieves the biometric secret for the given service and account. - * Throws Error with message [`passwords::PASSWORD_NOT_FOUND`] if the secret does not exist. - */ - export function getBiometricSecret(service: string, account: string, keyMaterial?: KeyMaterial | undefined | null): Promise - /** - * Derives key material from biometric data. Returns a string encoded with a - * base64 encoded key and the base64 encoded challenge used to create it - * separated by a `|` character. - * - * If the iv is provided, it will be used as the challenge. Otherwise a random challenge will be generated. - * - * `format!("|")` - */ - export function deriveKeyMaterial(iv?: string | undefined | null): Promise - export interface KeyMaterial { - osKeyPartB64: string - clientKeyPartB64?: string - } - export interface OsDerivedKey { - keyB64: string - ivB64: string - } -} -export declare namespace clipboards { - export function read(): Promise - export function write(text: string, password: boolean): Promise -} -export declare namespace sshagent { - export interface PrivateKey { - privateKey: string - name: string - cipherId: string - } - export interface SshKey { - privateKey: string - publicKey: string - keyFingerprint: string - } - export interface SshUiRequest { - cipherId?: string - isList: boolean - processName: string - isForwarding: boolean - namespace?: string - } - export function serve(callback: (err: Error | null, arg: SshUiRequest) => any): Promise - export function stop(agentState: SshAgentState): void - export function isRunning(agentState: SshAgentState): boolean - export function setKeys(agentState: SshAgentState, newKeys: Array): void - export function lock(agentState: SshAgentState): void - export function clearKeys(agentState: SshAgentState): void - export class SshAgentState { } -} -export declare namespace processisolations { - export function disableCoredumps(): Promise - export function isCoreDumpingDisabled(): Promise - export function isolateProcess(): Promise -} -export declare namespace powermonitors { - export function onLock(callback: (err: Error | null, ) => any): Promise - export function isLockMonitorAvailable(): Promise -} -export declare namespace windows_registry { - export function createKey(key: string, subkey: string, value: string): Promise - export function deleteKey(key: string, subkey: string): Promise -} -export declare namespace ipc { - export interface IpcMessage { - clientId: number - kind: IpcMessageType - message?: string - } - export const enum IpcMessageType { - Connected = 0, - Disconnected = 1, - Message = 2 - } - export class IpcServer { +/* eslint-disable */ +export declare namespace autofill { + export class AutofillIpcServer { /** * Create and start the IPC server without blocking. * * @param name The endpoint name to listen on. This name uniquely identifies the IPC connection and must be the same for both the server and client. * @param callback This function will be called whenever a message is received from a client. */ - static listen(name: string, callback: (error: null | Error, message: IpcMessage) => void): Promise + static listen(name: string, registrationCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyRegistrationRequest) => void, assertionCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionRequest) => void, assertionWithoutUserInterfaceCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionWithoutUserInterfaceRequest) => void): Promise /** Return the path to the IPC server. */ getPath(): string /** Stop the IPC server. */ stop(): void - /** - * Send a message over the IPC server to all the connected clients - * - * @return The number of clients that the message was sent to. Note that the number of messages - * actually received may be less, as some clients could disconnect before receiving the message. - */ - send(message: string): number + completeRegistration(clientId: number, sequenceNumber: number, response: PasskeyRegistrationResponse): number + completeAssertion(clientId: number, sequenceNumber: number, response: PasskeyAssertionResponse): number + completeError(clientId: number, sequenceNumber: number, error: string): number } -} -export declare namespace autostart { - export function setAutostart(autostart: boolean, params: Array): Promise -} -export declare namespace autofill { - export function runCommand(value: string): Promise - export const enum UserVerification { - Preferred = 'preferred', - Required = 'required', - Discouraged = 'discouraged' + export interface PasskeyAssertionRequest { + rpId: string + clientDataHash: Array + userVerification: UserVerification + allowedCredentials: Array> + windowXy: Position } - export interface Position { - x: number - y: number + export interface PasskeyAssertionResponse { + rpId: string + userHandle: Array + signature: Array + clientDataHash: Array + authenticatorData: Array + credentialId: Array + } + export interface PasskeyAssertionWithoutUserInterfaceRequest { + rpId: string + credentialId: Array + userName: string + userHandle: Array + recordIdentifier?: string + clientDataHash: Array + userVerification: UserVerification + windowXy: Position } export interface PasskeyRegistrationRequest { rpId: string @@ -153,66 +57,60 @@ export declare namespace autofill { credentialId: Array attestationObject: Array } - export interface PasskeyAssertionRequest { - rpId: string - clientDataHash: Array - userVerification: UserVerification - allowedCredentials: Array> - windowXy: Position + export interface Position { + x: number + y: number } - export interface PasskeyAssertionWithoutUserInterfaceRequest { - rpId: string - credentialId: Array - userName: string - userHandle: Array - recordIdentifier?: string - clientDataHash: Array - userVerification: UserVerification - windowXy: Position - } - export interface PasskeyAssertionResponse { - rpId: string - userHandle: Array - signature: Array - clientDataHash: Array - authenticatorData: Array - credentialId: Array - } - export class IpcServer { - /** - * Create and start the IPC server without blocking. - * - * @param name The endpoint name to listen on. This name uniquely identifies the IPC connection and must be the same for both the server and client. - * @param callback This function will be called whenever a message is received from a client. - */ - static listen(name: string, registrationCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyRegistrationRequest) => void, assertionCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionRequest) => void, assertionWithoutUserInterfaceCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionWithoutUserInterfaceRequest) => void): Promise - /** Return the path to the IPC server. */ - getPath(): string - /** Stop the IPC server. */ - stop(): void - completeRegistration(clientId: number, sequenceNumber: number, response: PasskeyRegistrationResponse): number - completeAssertion(clientId: number, sequenceNumber: number, response: PasskeyAssertionResponse): number - completeError(clientId: number, sequenceNumber: number, error: string): number + export function runCommand(value: string): Promise + export const enum UserVerification { + Preferred = 'preferred', + Required = 'required', + Discouraged = 'discouraged' } } -export declare namespace passkey_authenticator { - export function register(): void + +export declare namespace autostart { + export function setAutostart(autostart: boolean, params: Array): Promise } -export declare namespace logging { - export const enum LogLevel { - Trace = 0, - Debug = 1, - Info = 2, - Warn = 3, - Error = 4 + +export declare namespace autotype { + export function getForegroundWindowTitle(): string + export function typeInput(input: Array): void +} + +export declare namespace biometrics { + export function available(): Promise + /** + * Derives key material from biometric data. Returns a string encoded with a + * base64 encoded key and the base64 encoded challenge used to create it + * separated by a `|` character. + * + * If the iv is provided, it will be used as the challenge. Otherwise a random challenge will be generated. + * + * `format!("|")` + */ + export function deriveKeyMaterial(iv?: string | undefined | null): Promise + /** + * Retrieves the biometric secret for the given service and account. + * Throws Error with message [`passwords::PASSWORD_NOT_FOUND`] if the secret does not exist. + */ + export function getBiometricSecret(service: string, account: string, keyMaterial?: KeyMaterial | undefined | null): Promise + export interface KeyMaterial { + osKeyPartB64: string + clientKeyPartB64?: string } - export function initNapiLog(jsLogFn: (err: Error | null, arg0: LogLevel, arg1: string) => any): void + export interface OsDerivedKey { + keyB64: string + ivB64: string + } + export function prompt(hwnd: Buffer, message: string): Promise + export function setBiometricSecret(service: string, account: string, secret: string, keyMaterial: KeyMaterial | undefined | null, ivB64: string): Promise } + export declare namespace chromium_importer { - export interface ProfileInfo { - id: string - name: string - } + export function getAvailableProfiles(browser: string): Array + export function getInstalledBrowsers(): Array + export function importLogins(browser: string, profileId: string): Promise> export interface Login { url: string username: string @@ -228,11 +126,125 @@ export declare namespace chromium_importer { login?: Login failure?: LoginImportFailure } - export function getInstalledBrowsers(): Array - export function getAvailableProfiles(browser: string): Array - export function importLogins(browser: string, profileId: string): Promise> + export interface ProfileInfo { + id: string + name: string + } } -export declare namespace autotype { - export function getForegroundWindowTitle(): string - export function typeInput(input: Array): void + +export declare namespace clipboards { + export function read(): Promise + export function write(text: string, password: boolean): Promise +} + +export declare namespace ipc { + export class NativeIpcServer { + /** + * Create and start the IPC server without blocking. + * + * @param name The endpoint name to listen on. This name uniquely identifies the IPC connection and must be the same for both the server and client. + * @param callback This function will be called whenever a message is received from a client. + */ + static listen(name: string, callback: (error: null | Error, message: IpcMessage) => void): Promise + /** Return the path to the IPC server. */ + getPath(): string + /** Stop the IPC server. */ + stop(): void + /** + * Send a message over the IPC server to all the connected clients + * + * @return The number of clients that the message was sent to. Note that the number of messages + * actually received may be less, as some clients could disconnect before receiving the message. + */ + send(message: string): number + } + export interface IpcMessage { + clientId: number + kind: IpcMessageType + message?: string + } + export const enum IpcMessageType { + Connected = 0, + Disconnected = 1, + Message = 2 + } +} + +export declare namespace logging { + export function initNapiLog(jsLogFn: ((err: Error | null, arg0: LogLevel, arg1: string) => any)): void + export const enum LogLevel { + Trace = 0, + Debug = 1, + Info = 2, + Warn = 3, + Error = 4 + } +} + +export declare namespace passkey_authenticator { + export function register(): void +} + +export declare namespace passwords { + /** + * Delete the stored password from the keychain. + * Throws {@link Error} with message {@link PASSWORD_NOT_FOUND} if the password does not exist. + */ + export function deletePassword(service: string, account: string): Promise + /** + * Fetch the stored password from the keychain. + * Throws {@link Error} with message {@link PASSWORD_NOT_FOUND} if the password does not exist. + */ + export function getPassword(service: string, account: string): Promise + /** Checks if the os secure storage is available */ + export function isAvailable(): Promise + /** The error message returned when a password is not found during retrieval or deletion. */ + export const PASSWORD_NOT_FOUND: string + /** Save the password to the keychain. Adds an entry if none exists otherwise updates the existing entry. */ + export function setPassword(service: string, account: string, password: string): Promise +} + +export declare namespace powermonitors { + export function isLockMonitorAvailable(): Promise + export function onLock(callback: ((err: Error | null, ) => any)): Promise +} + +export declare namespace processisolations { + export function disableCoredumps(): Promise + export function isCoreDumpingDisabled(): Promise + export function isolateProcess(): Promise +} + +export declare namespace sshagent { + export class SshAgentState { + + } + export function clearKeys(agentState: SshAgentState): void + export function isRunning(agentState: SshAgentState): boolean + export function lock(agentState: SshAgentState): void + export interface PrivateKey { + privateKey: string + name: string + cipherId: string + } + export function serve(callback: ((err: Error | null, arg: SshUiRequest) => Promise)): Promise + export function setKeys(agentState: SshAgentState, newKeys: Array): void + export interface SshKey { + privateKey: string + publicKey: string + keyFingerprint: string + } + export interface SshUiRequest { + cipherId?: string + isList: boolean + processName: string + isForwarding: boolean + namespace?: string + } + export function stop(agentState: SshAgentState): void +} + +export declare namespace windows_registry { + export function createKey(key: string, subkey: string, value: string): Promise + export function deleteKey(key: string, subkey: string): Promise } diff --git a/apps/desktop/desktop_native/napi/package.json b/apps/desktop/desktop_native/napi/package.json index 04d31d8d2a3..acaf8c77371 100644 --- a/apps/desktop/desktop_native/napi/package.json +++ b/apps/desktop/desktop_native/napi/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "description": "", "scripts": { - "build": "napi build --platform --js false", + "build": "napi build --platform --no-js", "test": "cargo test" }, "author": "", diff --git a/apps/desktop/desktop_native/napi/src/lib.rs b/apps/desktop/desktop_native/napi/src/lib.rs index 4731166852b..4b35fbb5565 100644 --- a/apps/desktop/desktop_native/napi/src/lib.rs +++ b/apps/desktop/desktop_native/napi/src/lib.rs @@ -170,10 +170,8 @@ pub mod sshagent { use std::sync::Arc; use desktop_core::ssh_agent::BitwardenSshKey; - use napi::{ - bindgen_prelude::Promise, - threadsafe_function::{ErrorStrategy::CalleeHandled, ThreadsafeFunction}, - }; + use napi::bindgen_prelude::Promise; + use napi::threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}; use tokio::{self, sync::Mutex}; use tracing::error; @@ -208,13 +206,15 @@ pub mod sshagent { #[allow(clippy::unused_async)] // FIXME: Remove unused async! #[napi] pub async fn serve( - callback: ThreadsafeFunction, + callback: ThreadsafeFunction>, ) -> napi::Result { let (auth_request_tx, mut auth_request_rx) = tokio::sync::mpsc::channel::(32); let (auth_response_tx, auth_response_rx) = tokio::sync::broadcast::channel::<(u32, bool)>(32); let auth_response_tx_arc = Arc::new(Mutex::new(auth_response_tx)); + // Wrap callback in Arc so it can be shared across spawned tasks + let callback = Arc::new(callback); tokio::spawn(async move { let _ = auth_response_rx; @@ -224,42 +224,49 @@ pub mod sshagent { tokio::spawn(async move { let auth_response_tx_arc = cloned_response_tx_arc; let callback = cloned_callback; - let promise_result: Result, napi::Error> = callback - .call_async(Ok(SshUIRequest { + // In NAPI v3, obtain the JS callback return as a Promise and await it in Rust + let (tx, rx) = std::sync::mpsc::channel::>(); + let status = callback.call_with_return_value( + Ok(SshUIRequest { cipher_id: request.cipher_id, is_list: request.is_list, process_name: request.process_name, is_forwarding: request.is_forwarding, namespace: request.namespace, - })) - .await; - match promise_result { - Ok(promise_result) => match promise_result.await { - Ok(result) => { - let _ = auth_response_tx_arc - .lock() - .await - .send((request.request_id, result)) - .expect("should be able to send auth response to agent"); - } - Err(e) => { - error!(error = %e, "Calling UI callback promise was rejected"); - let _ = auth_response_tx_arc - .lock() - .await - .send((request.request_id, false)) - .expect("should be able to send auth response to agent"); + }), + ThreadsafeFunctionCallMode::Blocking, + move |ret: Result, napi::Error>, _env| { + if let Ok(p) = ret { + let _ = tx.send(p); } + Ok(()) }, - Err(e) => { - error!(error = %e, "Calling UI callback could not create promise"); - let _ = auth_response_tx_arc - .lock() - .await - .send((request.request_id, false)) - .expect("should be able to send auth response to agent"); + ); + + let result = if status == napi::Status::Ok { + match rx.recv() { + Ok(promise) => match promise.await { + Ok(v) => v, + Err(e) => { + error!(error = %e, "UI callback promise rejected"); + false + } + }, + Err(e) => { + error!(error = %e, "Failed to receive UI callback promise"); + false + } } - } + } else { + error!(error = ?status, "Calling UI callback failed"); + false + }; + + let _ = auth_response_tx_arc + .lock() + .await + .send((request.request_id, result)) + .expect("should be able to send auth response to agent"); }); } }); @@ -347,14 +354,12 @@ pub mod processisolations { #[napi] pub mod powermonitors { use napi::{ - threadsafe_function::{ - ErrorStrategy::CalleeHandled, ThreadsafeFunction, ThreadsafeFunctionCallMode, - }, + threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}, tokio, }; #[napi] - pub async fn on_lock(callback: ThreadsafeFunction<(), CalleeHandled>) -> napi::Result<()> { + pub async fn on_lock(callback: ThreadsafeFunction<()>) -> napi::Result<()> { let (tx, mut rx) = tokio::sync::mpsc::channel::<()>(32); desktop_core::powermonitor::on_lock(tx) .await @@ -393,9 +398,7 @@ pub mod windows_registry { #[napi] pub mod ipc { use desktop_core::ipc::server::{Message, MessageType}; - use napi::threadsafe_function::{ - ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode, - }; + use napi::threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}; #[napi(object)] pub struct IpcMessage { @@ -432,12 +435,12 @@ pub mod ipc { } #[napi] - pub struct IpcServer { + pub struct NativeIpcServer { server: desktop_core::ipc::server::Server, } #[napi] - impl IpcServer { + impl NativeIpcServer { /// Create and start the IPC server without blocking. /// /// @param name The endpoint name to listen on. This name uniquely identifies the IPC connection and must be the same for both the server and client. @@ -447,7 +450,7 @@ pub mod ipc { pub async fn listen( name: String, #[napi(ts_arg_type = "(error: null | Error, message: IpcMessage) => void")] - callback: ThreadsafeFunction, + callback: ThreadsafeFunction, ) -> napi::Result { let (send, mut recv) = tokio::sync::mpsc::channel::(32); tokio::spawn(async move { @@ -464,7 +467,7 @@ pub mod ipc { )) })?; - Ok(IpcServer { server }) + Ok(NativeIpcServer { server }) } /// Return the path to the IPC server. @@ -510,9 +513,7 @@ pub mod autostart { #[napi] pub mod autofill { use desktop_core::ipc::server::{Message, MessageType}; - use napi::threadsafe_function::{ - ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode, - }; + use napi::threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use tracing::error; @@ -617,14 +618,14 @@ pub mod autofill { } #[napi] - pub struct IpcServer { + pub struct AutofillIpcServer { server: desktop_core::ipc::server::Server, } // FIXME: Remove unwraps! They panic and terminate the whole application. #[allow(clippy::unwrap_used)] #[napi] - impl IpcServer { + impl AutofillIpcServer { /// Create and start the IPC server without blocking. /// /// @param name The endpoint name to listen on. This name uniquely identifies the IPC connection and must be the same for both the server and client. @@ -638,25 +639,24 @@ pub mod autofill { #[napi( ts_arg_type = "(error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyRegistrationRequest) => void" )] - registration_callback: ThreadsafeFunction< - (u32, u32, PasskeyRegistrationRequest), - ErrorStrategy::CalleeHandled, - >, + registration_callback: ThreadsafeFunction<( + u32, + u32, + PasskeyRegistrationRequest, + )>, #[napi( ts_arg_type = "(error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionRequest) => void" )] - assertion_callback: ThreadsafeFunction< - (u32, u32, PasskeyAssertionRequest), - ErrorStrategy::CalleeHandled, - >, + assertion_callback: ThreadsafeFunction<(u32, u32, PasskeyAssertionRequest)>, #[napi( ts_arg_type = "(error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionWithoutUserInterfaceRequest) => void" )] - assertion_without_user_interface_callback: ThreadsafeFunction< - (u32, u32, PasskeyAssertionWithoutUserInterfaceRequest), - ErrorStrategy::CalleeHandled, - >, - ) -> napi::Result { + assertion_without_user_interface_callback: ThreadsafeFunction<( + u32, + u32, + PasskeyAssertionWithoutUserInterfaceRequest, + )>, + ) -> napi::Result { let (send, mut recv) = tokio::sync::mpsc::channel::(32); tokio::spawn(async move { while let Some(Message { @@ -742,7 +742,7 @@ pub mod autofill { )) })?; - Ok(IpcServer { server }) + Ok(AutofillIpcServer { server }) } /// Return the path to the IPC server. @@ -835,9 +835,7 @@ pub mod logging { use std::fmt::Write; use std::sync::OnceLock; - use napi::threadsafe_function::{ - ErrorStrategy::CalleeHandled, ThreadsafeFunction, ThreadsafeFunctionCallMode, - }; + use napi::threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}; use tracing::Level; use tracing_subscriber::fmt::format::{DefaultVisitor, Writer}; use tracing_subscriber::{ @@ -847,7 +845,7 @@ pub mod logging { Layer, }; - struct JsLogger(OnceLock>); + struct JsLogger(OnceLock>); static JS_LOGGER: JsLogger = JsLogger(OnceLock::new()); #[napi] @@ -925,7 +923,7 @@ pub mod logging { } #[napi] - pub fn init_napi_log(js_log_fn: ThreadsafeFunction<(LogLevel, String), CalleeHandled>) { + pub fn init_napi_log(js_log_fn: ThreadsafeFunction<(LogLevel, String)>) { let _ = JS_LOGGER.0.set(js_log_fn); let filter = EnvFilter::builder() @@ -1035,7 +1033,7 @@ pub mod chromium_importer { #[napi] pub mod autotype { #[napi] - pub fn get_foreground_window_title() -> napi::Result { + pub fn get_foreground_window_title() -> napi::Result { autotype::get_foreground_window_title().map_err(|_| { napi::Error::from_reason( "Autotype Error: failed to get foreground window title".to_string(), @@ -1044,7 +1042,7 @@ pub mod autotype { } #[napi] - pub fn type_input(input: Vec) -> napi::Result<(), napi::Status> { + pub fn type_input(input: Vec) -> napi::Result<()> { autotype::type_input(input).map_err(|_| { napi::Error::from_reason("Autotype Error: failed to type input".to_string()) }) diff --git a/apps/desktop/src/autofill/main/main-ssh-agent.service.ts b/apps/desktop/src/autofill/main/main-ssh-agent.service.ts index 595ef778bcf..31196e4cf98 100644 --- a/apps/desktop/src/autofill/main/main-ssh-agent.service.ts +++ b/apps/desktop/src/autofill/main/main-ssh-agent.service.ts @@ -37,7 +37,7 @@ export class MainSshAgentService { init() { // handle sign request passing to UI sshagent - .serve(async (err: Error, sshUiRequest: sshagent.SshUiRequest) => { + .serve(async (err: Error | null, sshUiRequest: sshagent.SshUiRequest): Promise => { // clear all old (> SIGN_TIMEOUT) requests this.requestResponses = this.requestResponses.filter( (response) => response.timestamp > new Date(Date.now() - this.SIGN_TIMEOUT), diff --git a/apps/desktop/src/main/native-messaging.main.ts b/apps/desktop/src/main/native-messaging.main.ts index 93525164ff5..2847cae75a9 100644 --- a/apps/desktop/src/main/native-messaging.main.ts +++ b/apps/desktop/src/main/native-messaging.main.ts @@ -14,7 +14,7 @@ import { isDev } from "../utils"; import { WindowMain } from "./window.main"; export class NativeMessagingMain { - private ipcServer: ipc.IpcServer | null; + private ipcServer: ipc.NativeIpcServer | null; private connected: number[] = []; constructor( @@ -78,37 +78,40 @@ export class NativeMessagingMain { this.ipcServer.stop(); } - this.ipcServer = await ipc.IpcServer.listen("bitwarden", (error, msg) => { - switch (msg.kind) { - case ipc.IpcMessageType.Connected: { - this.connected.push(msg.clientId); - this.logService.info("Native messaging client " + msg.clientId + " has connected"); - break; - } - case ipc.IpcMessageType.Disconnected: { - const index = this.connected.indexOf(msg.clientId); - if (index > -1) { - this.connected.splice(index, 1); + this.ipcServer = await ipc.NativeIpcServer.listen( + "bitwarden", + (error: Error | null, msg: ipc.IpcMessage) => { + switch (msg.kind) { + case ipc.IpcMessageType.Connected: { + this.connected.push(msg.clientId); + this.logService.info("Native messaging client " + msg.clientId + " has connected"); + break; } + case ipc.IpcMessageType.Disconnected: { + const index = this.connected.indexOf(msg.clientId); + if (index > -1) { + this.connected.splice(index, 1); + } - this.logService.info("Native messaging client " + msg.clientId + " has disconnected"); - break; - } - case ipc.IpcMessageType.Message: - try { - const msgJson = JSON.parse(msg.message); - this.logService.debug("Native messaging message:", msgJson); - this.windowMain.win?.webContents.send("nativeMessaging", msgJson); - } catch (e) { - this.logService.warning("Error processing message:", e, msg.message); + this.logService.info("Native messaging client " + msg.clientId + " has disconnected"); + break; } - break; + case ipc.IpcMessageType.Message: + try { + const msgJson = JSON.parse(msg.message); + this.logService.debug("Native messaging message:", msgJson); + this.windowMain.win?.webContents.send("nativeMessaging", msgJson); + } catch (e) { + this.logService.warning("Error processing message:", e, msg.message); + } + break; - default: - this.logService.warning("Unknown message type:", msg.kind, msg.message); - break; - } - }); + default: + this.logService.warning("Unknown message type:", msg.kind, msg.message); + break; + } + }, + ); this.logService.info("Native messaging server started at:", this.ipcServer.getPath()); diff --git a/apps/desktop/src/platform/main/autofill/native-autofill.main.ts b/apps/desktop/src/platform/main/autofill/native-autofill.main.ts index f66eea180cf..4b3b5f28806 100644 --- a/apps/desktop/src/platform/main/autofill/native-autofill.main.ts +++ b/apps/desktop/src/platform/main/autofill/native-autofill.main.ts @@ -16,7 +16,7 @@ export type RunCommandParams = { export type RunCommandResult = C["output"]; export class NativeAutofillMain { - private ipcServer: autofill.IpcServer | null; + private ipcServer?: autofill.AutofillIpcServer; constructor( private logService: LogService, @@ -34,13 +34,13 @@ export class NativeAutofillMain { }, ); - this.ipcServer = await autofill.IpcServer.listen( + this.ipcServer = await autofill.AutofillIpcServer.listen( "autofill", // RegistrationCallback (error, clientId, sequenceNumber, request) => { if (error) { this.logService.error("autofill.IpcServer.registration", error); - this.ipcServer.completeError(clientId, sequenceNumber, String(error)); + this.ipcServer?.completeError(clientId, sequenceNumber, String(error)); return; } this.windowMain.win.webContents.send("autofill.passkeyRegistration", { @@ -53,7 +53,7 @@ export class NativeAutofillMain { (error, clientId, sequenceNumber, request) => { if (error) { this.logService.error("autofill.IpcServer.assertion", error); - this.ipcServer.completeError(clientId, sequenceNumber, String(error)); + this.ipcServer?.completeError(clientId, sequenceNumber, String(error)); return; } this.windowMain.win.webContents.send("autofill.passkeyAssertion", { @@ -66,7 +66,7 @@ export class NativeAutofillMain { (error, clientId, sequenceNumber, request) => { if (error) { this.logService.error("autofill.IpcServer.assertion", error); - this.ipcServer.completeError(clientId, sequenceNumber, String(error)); + this.ipcServer?.completeError(clientId, sequenceNumber, String(error)); return; } this.windowMain.win.webContents.send("autofill.passkeyAssertionWithoutUserInterface", { @@ -80,19 +80,19 @@ export class NativeAutofillMain { ipcMain.on("autofill.completePasskeyRegistration", (event, data) => { this.logService.warning("autofill.completePasskeyRegistration", data); const { clientId, sequenceNumber, response } = data; - this.ipcServer.completeRegistration(clientId, sequenceNumber, response); + this.ipcServer?.completeRegistration(clientId, sequenceNumber, response); }); ipcMain.on("autofill.completePasskeyAssertion", (event, data) => { this.logService.warning("autofill.completePasskeyAssertion", data); const { clientId, sequenceNumber, response } = data; - this.ipcServer.completeAssertion(clientId, sequenceNumber, response); + this.ipcServer?.completeAssertion(clientId, sequenceNumber, response); }); ipcMain.on("autofill.completeError", (event, data) => { this.logService.warning("autofill.completeError", data); const { clientId, sequenceNumber, error } = data; - this.ipcServer.completeError(clientId, sequenceNumber, String(error)); + this.ipcServer?.completeError(clientId, sequenceNumber, String(error)); }); }