fix: generated identity uses extension signer instead of local key

buildNip98Token checked window.nostr before secretKeyHex, so
"Generate New Identity" would sign with the browser extension's key
after saving. Now explicit keys always take priority over extensions.

Also made setup flow mode-aware: webhook users get BOTFIGHTS-WEBHOOK.md,
polling users get BOTFIGHTS-POLLING.md with matching copy prompts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-12 16:49:22 +00:00
co-authored by Claude Opus 4.6
parent bc4a52bc12
commit af50580aca
2 changed files with 23 additions and 12 deletions
+8 -6
View File
@@ -68,20 +68,22 @@ export async function buildNip98Token(
let signedEvent: Record<string, unknown> let signedEvent: Record<string, unknown>
const signer = (window as { nostr?: NostrSigner }).nostr if (secretKeyHex) {
if (signer) { // Sign locally with provided secret key — always prefer explicit key over extension
// Sign via NIP-07 extension or Amber
signedEvent = await signer.signEvent(unsignedEvent)
} else if (secretKeyHex) {
// Sign locally with secret key
const { hexToBytes } = await import('nostr-tools/utils') const { hexToBytes } = await import('nostr-tools/utils')
const { finalizeEvent } = await import('nostr-tools') const { finalizeEvent } = await import('nostr-tools')
const sk = hexToBytes(secretKeyHex) const sk = hexToBytes(secretKeyHex)
const event = finalizeEvent(unsignedEvent, sk) const event = finalizeEvent(unsignedEvent, sk)
signedEvent = event as unknown as Record<string, unknown> signedEvent = event as unknown as Record<string, unknown>
} else {
const signer = (window as { nostr?: NostrSigner }).nostr
if (signer) {
// Sign via NIP-07 extension or Amber
signedEvent = await signer.signEvent(unsignedEvent)
} else { } else {
throw new Error('No Nostr signer available. Install a NIP-07 extension or use a saved key.') throw new Error('No Nostr signer available. Install a NIP-07 extension or use a saved key.')
} }
}
return btoa(JSON.stringify(signedEvent)) return btoa(JSON.stringify(signedEvent))
} }
+14 -5
View File
@@ -523,12 +523,21 @@ async function confirmPolling() {
} }
} }
function setupDocPath() {
return connectionMode.value === 'polling' ? '/docs/BOTFIGHTS-POLLING.md' : '/docs/BOTFIGHTS-WEBHOOK.md'
}
function setupDocName() {
return connectionMode.value === 'polling' ? 'BOTFIGHTS-POLLING.md' : 'BOTFIGHTS-WEBHOOK.md'
}
function downloadSetupGuide() { function downloadSetupGuide() {
window.open('/docs/BOTFIGHTS.md', '_blank') window.open(setupDocPath(), '_blank')
} }
function copyFullPrompt() { function copyFullPrompt() {
const text = `Read BOTFIGHTS.md and follow the setup instructions. Here are my credentials:\nBOT_ID=${botId.value}\nBOT_SECRET=${botSecret.value}` const docName = setupDocName()
const text = `Read ${docName} and follow the setup instructions. Here are my credentials:\nBOT_ID=${botId.value}\nBOT_SECRET=${botSecret.value}`
navigator.clipboard.writeText(text) navigator.clipboard.writeText(text)
setupGuideCopied.value = true setupGuideCopied.value = true
setTimeout(() => { setupGuideCopied.value = false }, 2000) setTimeout(() => { setupGuideCopied.value = false }, 2000)
@@ -1336,13 +1345,13 @@ function handleSignOut() {
<div class="flex items-start gap-2.5 mb-3"> <div class="flex items-start gap-2.5 mb-3">
<span class="font-display font-black text-neon-cyan text-xs mt-0.5 shrink-0">1</span> <span class="font-display font-black text-neon-cyan text-xs mt-0.5 shrink-0">1</span>
<div class="flex-1"> <div class="flex-1">
<p class="font-mono text-[10px] text-text-primary mb-1.5">Download BOTFIGHTS.md into your workspace</p> <p class="font-mono text-[10px] text-text-primary mb-1.5">Download {{ setupDocName() }} into your workspace</p>
<button <button
class="w-full py-1.5 border border-border text-[9px] font-display font-bold tracking-wider class="w-full py-1.5 border border-border text-[9px] font-display font-bold tracking-wider
text-text-muted hover:border-neon-cyan/40 hover:text-neon-cyan transition-all" text-text-muted hover:border-neon-cyan/40 hover:text-neon-cyan transition-all"
@click="downloadSetupGuide" @click="downloadSetupGuide"
> >
DOWNLOAD BOTFIGHTS.md DOWNLOAD {{ setupDocName() }}
</button> </button>
</div> </div>
</div> </div>
@@ -1353,7 +1362,7 @@ function handleSignOut() {
<div class="flex-1"> <div class="flex-1">
<p class="font-mono text-[10px] text-text-primary mb-1.5">Tell your AI this:</p> <p class="font-mono text-[10px] text-text-primary mb-1.5">Tell your AI this:</p>
<div class="bg-bg border border-border p-2.5 font-mono text-[10px] text-text-muted leading-relaxed select-all"> <div class="bg-bg border border-border p-2.5 font-mono text-[10px] text-text-muted leading-relaxed select-all">
<p class="text-text-primary">Read BOTFIGHTS.md and follow the setup instructions. Here are my credentials:</p> <p class="text-text-primary">Read {{ setupDocName() }} and follow the setup instructions. Here are my credentials:</p>
<div class="mt-1"> <div class="mt-1">
<div>BOT_ID=<span class="text-neon-cyan">{{ botId }}</span></div> <div>BOT_ID=<span class="text-neon-cyan">{{ botId }}</span></div>
<div>BOT_SECRET=<span class="text-neon-cyan">{{ botSecret }}</span></div> <div>BOT_SECRET=<span class="text-neon-cyan">{{ botSecret }}</span></div>