fix(marmot-interop): robust pubkey extraction from wn output

wn prints either JSON (with --json) or a yaml-ish "key: value" pretty
form depending on the command and build. On the user's macOS run,
create-identity printed yaml ("pubkey: npub1..."), the subsequent
wn --json whoami didn't return the expected JSON shape, and
ensure_identity bailed with "could not determine npub for B".

Add a shared extract_pubkey helper in lib.sh that tries, in order:
  1. JSON object .pubkey / .npub / .public_key
  2. JSON array .[0].pubkey / .[0].npub / .[0].public_key
  3. yaml-ish "pubkey: ..." via sed
  4. yaml-ish "npub: ..." via sed

ensure_identity and prompt_for_a_npub now use it, and also fall back
to the non-JSON whoami output if --json returns nothing. On total
failure, the raw output is echoed to the log for diagnosis.

Also made npub_to_hex best-effort with the same double-try approach.
When hex lookup fails, it returns the npub unchanged — downstream
expect_contains assertions work against either form since wn's own
group listings may use either encoding.

https://claude.ai/code/session_01PacmbE1KKWMSRmDw3bnCNo
This commit is contained in:
Claude
2026-04-18 14:15:56 +00:00
parent 10b229ab9d
commit 82f5405f01
2 changed files with 67 additions and 23 deletions
+35 -9
View File
@@ -212,16 +212,42 @@ wait_for_member() {
return 1
}
# ------- npub <-> hex conversion helpers -------------------------------------
# whitenoise's `wn` accepts both, but we use hex internally for JSON comparisons.
# We rely on `wn debug` (or fall back to a python one-liner) — but to keep zero
# extra deps we do the conversion via wn itself by round-tripping through
# `wn users show <npub>` which prints both forms.
# ------- output parsing ------------------------------------------------------
# `wn` prints either JSON (with --json) or a yaml-ish "key: value" pretty form.
# This helper extracts the pubkey (npub) from whichever form we got.
extract_pubkey() {
local raw="$1" v=""
# JSON: single object
v=$(printf '%s' "$raw" | jq -r '.pubkey // .npub // .public_key // empty' 2>/dev/null || true)
if [[ -n "$v" && "$v" != "null" ]]; then printf '%s' "$v"; return; fi
# JSON: array of accounts (whoami may return a list)
v=$(printf '%s' "$raw" | jq -r '.[0].pubkey // .[0].npub // .[0].public_key // empty' 2>/dev/null || true)
if [[ -n "$v" && "$v" != "null" ]]; then printf '%s' "$v"; return; fi
# yaml-ish "pubkey: npub1..."
v=$(printf '%s\n' "$raw" | sed -n 's/^[[:space:]]*pubkey[[:space:]]*:[[:space:]]*//p' | head -n1)
if [[ -n "$v" ]]; then printf '%s' "$v"; return; fi
# yaml-ish "npub: npub1..." (alternative key)
v=$(printf '%s\n' "$raw" | sed -n 's/^[[:space:]]*npub[[:space:]]*:[[:space:]]*//p' | head -n1)
if [[ -n "$v" ]]; then printf '%s' "$v"; return; fi
}
# Best-effort npub -> hex conversion via `wn users show`. If the lookup fails
# (e.g. --json not supported or user not resolvable), returns the input
# unchanged — downstream comparisons tolerate either form.
npub_to_hex() {
local npub="$1"
wn_b --json users show "$npub" 2>/dev/null \
| jq -r '.pubkey // .public_key // empty' 2>/dev/null \
|| printf '%s' "$npub"
local npub="$1" raw hex
raw=$(wn_b --json users show "$npub" 2>/dev/null || true)
hex=$(printf '%s' "$raw" | jq -r '.pubkey // .public_key // .hex // empty' 2>/dev/null || true)
if [[ -z "$hex" || "$hex" == "null" ]]; then
# fallback to yaml-ish output
raw=$(wn_b users show "$npub" 2>/dev/null || true)
hex=$(printf '%s\n' "$raw" | sed -n 's/^[[:space:]]*\(public_key\|hex\)[[:space:]]*:[[:space:]]*//p' | head -n1)
fi
if [[ -n "$hex" && "$hex" != "null" ]]; then
printf '%s' "$hex"
else
printf '%s' "$npub"
fi
}
# ------- state file ----------------------------------------------------------
+32 -14
View File
@@ -145,24 +145,43 @@ ensure_identity() {
local who="$1" cmd
if [[ "$who" == "B" ]]; then cmd=wn_b; else cmd=wn_c; fi
step "ensuring Identity $who"
local npub
npub=$("$cmd" --json whoami 2>/dev/null | jq -r '.pubkey // .npub // empty' 2>/dev/null || true)
if [[ -z "${npub:-}" || "$npub" == "null" ]]; then
info "creating new identity for $who"
"$cmd" create-identity 2>&1 | tee -a "$LOG_FILE" >&2
npub=$("$cmd" --json whoami 2>/dev/null | jq -r '.pubkey // .npub // empty' || true)
fi
# First try whoami (will be empty if no account yet). Try --json, then the
# yaml-ish pretty form as a fallback.
local out npub=""
out=$("$cmd" --json whoami 2>/dev/null || true)
npub=$(extract_pubkey "$out")
if [[ -z "${npub:-}" ]]; then
fail_msg "could not determine npub for $who"; exit 1
out=$("$cmd" whoami 2>/dev/null || true)
npub=$(extract_pubkey "$out")
fi
if [[ -z "${npub:-}" ]]; then
info "creating new identity for $who"
out=$("$cmd" create-identity 2>&1 | tee -a "$LOG_FILE")
npub=$(extract_pubkey "$out")
# Some builds only print the npub on whoami after create, not on
# create-identity itself — re-query as a last resort.
if [[ -z "${npub:-}" ]]; then
out=$("$cmd" whoami 2>/dev/null || true)
npub=$(extract_pubkey "$out")
fi
fi
if [[ -z "${npub:-}" ]]; then
fail_msg "could not determine npub for $who — raw output below:"
printf '%s\n' "$out" | tee -a "$LOG_FILE" >&2
exit 1
fi
local hex
hex=$("$cmd" --json users show "$npub" 2>/dev/null | jq -r '.pubkey // .public_key // .hex // empty' 2>/dev/null || true)
[[ -z "$hex" ]] && hex="$npub"
hex=$(npub_to_hex "$npub")
if [[ "$who" == "B" ]]; then B_NPUB="$npub"; B_HEX="$hex"
else C_NPUB="$npub"; C_HEX="$hex"; fi
save_state "${who}_NPUB" "$npub"
save_state "${who}_HEX" "$hex"
info "$who npub: $npub"
[[ "$hex" != "$npub" ]] && info "$who hex: $hex"
}
prompt_for_a_npub() {
@@ -174,11 +193,10 @@ prompt_for_a_npub() {
read -r A_NPUB
save_state "A_NPUB" "$A_NPUB"
fi
A_HEX=$(wn_b --json users show "$A_NPUB" 2>/dev/null \
| jq -r '.pubkey // .public_key // .hex // empty' 2>/dev/null || true)
[[ -z "$A_HEX" ]] && A_HEX="$A_NPUB"
A_HEX=$(npub_to_hex "$A_NPUB")
save_state "A_HEX" "$A_HEX"
info "A hex: $A_HEX"
info "A npub: $A_NPUB"
[[ "$A_HEX" != "$A_NPUB" ]] && info "A hex: $A_HEX"
}
# --- relays ------------------------------------------------------------------