Skip to content

Commit 553a71e

Browse files
committed
feat(cli): add buzz users me for external-agent identity (#2663)
OpenClaw/external bridges hold BUZZ_PRIVATE_KEY but had no first-class self-identity command short of embedding secp256k1. Print hex pubkey + npub locally with no relay I/O (alias: whoami). Signed-off-by: Bartok9 <danielrpike9@gmail.com>
1 parent 3a4bf51 commit 553a71e

2 files changed

Lines changed: 54 additions & 0 deletions

File tree

crates/buzz-cli/src/commands/users.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,34 @@
11
use crate::client::{normalize_write_response, BuzzClient};
22
use crate::error::CliError;
33
use crate::validate::validate_hex64;
4+
use nostr::ToBech32;
45

56
// TODO(phase-4): Replace raw nostr::EventBuilder usage in cmd_set_presence with buzz-sdk builder
67

8+
/// Build identity JSON for `buzz users me` (unit-testable without a live client).
9+
pub(crate) fn me_identity_json(pubkey_hex: &str, npub_bech32: &str) -> serde_json::Value {
10+
serde_json::json!({
11+
"pubkey": pubkey_hex,
12+
"npub": npub_bech32,
13+
})
14+
}
15+
16+
/// Print the active CLI identity (hex pubkey + npub) from the loaded private key.
17+
///
18+
/// No relay I/O — agents can resolve "who am I?" before connecting. Refs #2663.
19+
pub async fn cmd_me(
20+
client: &BuzzClient,
21+
_format: &crate::OutputFormat,
22+
) -> Result<(), CliError> {
23+
let pk = client.keys().public_key();
24+
let hex = pk.to_hex();
25+
let npub = pk
26+
.to_bech32()
27+
.map_err(|e| CliError::Other(format!("npub encode failed: {e}")))?;
28+
println!("{}", me_identity_json(&hex, &npub));
29+
Ok(())
30+
}
31+
732
/// Get user profiles (kind:0 metadata events).
833
///
934
/// - 0 pubkeys, no name → query our own profile
@@ -311,6 +336,7 @@ pub async fn dispatch(
311336
) -> Result<(), CliError> {
312337
use crate::UsersCmd;
313338
match cmd {
339+
UsersCmd::Me => cmd_me(client, format).await,
314340
UsersCmd::Get { pubkeys, name } => {
315341
cmd_get_users(client, &pubkeys, name.as_deref(), format).await
316342
}
@@ -356,4 +382,26 @@ mod tests {
356382
let event = json!({"pubkey": "user", "tags": [["p"]]});
357383
assert_eq!(presence_subject(&event), "user");
358384
}
385+
386+
#[test]
387+
fn me_identity_json_includes_pubkey_and_npub() {
388+
let hex = "a".repeat(64);
389+
let v = super::me_identity_json(
390+
&hex,
391+
"npub1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq5s0xov",
392+
);
393+
assert_eq!(v["pubkey"], hex);
394+
assert!(v["npub"].as_str().unwrap().starts_with("npub1"));
395+
}
396+
397+
#[test]
398+
fn me_identity_from_generated_keys_roundtrips_bech32() {
399+
use nostr::{Keys, ToBech32};
400+
let keys = Keys::generate();
401+
let hex = keys.public_key().to_hex();
402+
let npub = keys.public_key().to_bech32().expect("npub");
403+
let v = super::me_identity_json(&hex, &npub);
404+
assert_eq!(v["pubkey"].as_str().unwrap().len(), 64);
405+
assert_eq!(v["npub"].as_str().unwrap(), npub);
406+
}
359407
}

crates/buzz-cli/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,12 @@ pub enum DmsCmd {
800800

801801
#[derive(Subcommand)]
802802
pub enum UsersCmd {
803+
/// Print the CLI identity derived from BUZZ_PRIVATE_KEY (no relay round-trip)
804+
///
805+
/// External agents (OpenClaw, custom bridges) hold a private key but often
806+
/// need their own pubkey without embedding secp256k1. Refs #2663 gap #1.
807+
#[command(name = "me", alias = "whoami")]
808+
Me,
803809
/// Look up user profiles by pubkey or name
804810
Get {
805811
/// User pubkey(s) to look up (64-char hex). Omit for your own profile

0 commit comments

Comments
 (0)