diff --git a/Cargo.lock b/Cargo.lock index 873b1c9..626fef1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1369,6 +1369,7 @@ dependencies = [ "gpui_platform", "gpui_windows", "log", + "nostr-sdk", "person", "reqwest_client", "settings", diff --git a/crates/state/src/lib.rs b/crates/state/src/lib.rs index 9186a89..210356a 100644 --- a/crates/state/src/lib.rs +++ b/crates/state/src/lib.rs @@ -24,7 +24,7 @@ pub use nip4e::*; pub use nip05::*; pub use signer::{CoopAuthUrlHandler, UniversalSigner}; -pub fn init(window: &mut Window, cx: &mut App) { +pub fn init(window: &mut Window, cx: &mut App, cli_key: Option) { // rustls uses the `aws_lc_rs` provider by default // This only errors if the default provider has already // been installed. We can ignore this `Result`. @@ -37,7 +37,7 @@ pub fn init(window: &mut Window, cx: &mut App) { #[cfg(not(target_arch = "wasm32"))] gpui_tokio::init(cx); - NostrRegistry::set_global(cx.new(|cx| NostrRegistry::new(window, cx)), cx); + NostrRegistry::set_global(cx.new(|cx| NostrRegistry::new(window, cx, cli_key)), cx); } struct GlobalNostrRegistry(Entity); @@ -58,16 +58,16 @@ pub enum StateEvent { } impl StateEvent { + pub fn signer_changed(&self) -> bool { + matches!(self, StateEvent::SignerChanged) + } + pub fn error(error: T) -> Self where T: Into, { Self::Error(error.into()) } - - pub fn signer_changed(&self) -> bool { - matches!(self, StateEvent::SignerChanged) - } } /// Nostr Registry @@ -100,7 +100,7 @@ impl NostrRegistry { } /// Create a new nostr instance - fn new(window: &mut Window, cx: &mut Context) -> Self { + fn new(window: &mut Window, cx: &mut Context, cli_key: Option) -> Self { let signer = UniversalSigner::new(Keys::generate()); let authenticator = SignerAuthenticator::new(signer.clone()); @@ -133,6 +133,10 @@ impl NostrRegistry { if cfg!(target_arch = "wasm32") { cx.emit(StateEvent::NoSigner); + } else if let Some(secret) = cli_key { + // Use CLI-provided key -- same path as get_user_credential + let keys = Keys::new(secret); + this.set_signer(keys, cx); } else { this.get_user_credential(cx); } diff --git a/desktop/Cargo.toml b/desktop/Cargo.toml index 76dfb1f..a1ff45a 100644 --- a/desktop/Cargo.toml +++ b/desktop/Cargo.toml @@ -48,3 +48,4 @@ reqwest_client.workspace = true log.workspace = true tracing-subscriber.workspace = true +nostr-sdk.workspace = true diff --git a/desktop/src/main.rs b/desktop/src/main.rs index 91db6a7..2c20e2d 100644 --- a/desktop/src/main.rs +++ b/desktop/src/main.rs @@ -7,6 +7,7 @@ use gpui::{ actions, point, px, size, }; use gpui_platform::application; +use nostr_sdk::prelude::SecretKey; use state::{APP_ID, CLIENT_NAME}; use ui::Root; @@ -16,6 +17,14 @@ fn main() { // Initialize logging tracing_subscriber::fmt::init(); + // Parse CLI arguments for --sec + let cli_key = parse_cli_key(); + if let Err(ref e) = cli_key { + eprintln!("Failed to parse --sec argument: {e}"); + std::process::exit(1); + } + let cli_key = cli_key.unwrap(); + // Run application application() .with_assets(Assets) @@ -75,7 +84,7 @@ fn main() { settings::init(window, cx); // Initialize the nostr client - state::init(window, cx); + state::init(window, cx, cli_key); // Initialize person registry person::init(window, cx); @@ -125,6 +134,25 @@ fn load_embedded_fonts(cx: &App) { .unwrap(); } +fn parse_cli_key() -> Result, String> { + let args: Vec = std::env::args().collect(); + let mut i = 0; + while i < args.len() { + if args[i] == "--sec" { + if i + 1 < args.len() { + let nsec = &args[i + 1]; + return SecretKey::parse(nsec) + .map(Some) + .map_err(|e| format!("Invalid nsec key '{nsec}': {e}")); + } else { + return Err("--sec requires a value (nsec1...)".to_string()); + } + } + i += 1; + } + Ok(None) +} + fn quit(_ev: &Quit, cx: &mut App) { log::info!("Gracefully quitting the application . . ."); cx.quit(); diff --git a/web/src/lib.rs b/web/src/lib.rs index d2a02a4..df97a5d 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -59,7 +59,7 @@ pub fn run() -> Result<(), JsValue> { settings::init(window, cx); // Initialize the nostr client - state::init(window, cx); + state::init(window, cx, None); // Initialize person registry person::init(window, cx);