fix: register bot at name confirmation, show credentials in setup step
- Bot registered immediately after name check (poll mode default) - bot_id + secret available in bot-setup step with COPY GUIDE + CREDENTIALS - Webhook mode updates URL on existing bot instead of re-registering - Collapsible guide preview with credentials injected - Resets guide content when switching webhook/polling mode - Fix secret text overflow with break-all on profile page Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
d17f6970b9
commit
42b1642932
@@ -850,8 +850,8 @@ const tierClass = (t: number) => `tier-${t}`
|
|||||||
<div class="p-2.5 border border-neon-green/30 bg-neon-green/5">
|
<div class="p-2.5 border border-neon-green/30 bg-neon-green/5">
|
||||||
<p class="font-display font-bold text-[10px] tracking-wider text-neon-green mb-2">NEW SECRET GENERATED</p>
|
<p class="font-display font-bold text-[10px] tracking-wider text-neon-green mb-2">NEW SECRET GENERATED</p>
|
||||||
<div class="font-mono text-[10px] text-text-muted space-y-1">
|
<div class="font-mono text-[10px] text-text-muted space-y-1">
|
||||||
<div>BOT_ID=<span class="text-neon-cyan select-all">{{ regeneratedBotId }}</span></div>
|
<div>BOT_ID=<span class="text-neon-cyan select-all break-all">{{ regeneratedBotId }}</span></div>
|
||||||
<div>BOT_SECRET=<span class="text-neon-cyan select-all">{{ regeneratedSecret }}</span></div>
|
<div>BOT_SECRET=<span class="text-neon-cyan select-all break-all">{{ regeneratedSecret }}</span></div>
|
||||||
</div>
|
</div>
|
||||||
<p class="font-mono text-[9px] text-ko mt-2">Save this now. It will not be shown again after you leave this page.</p>
|
<p class="font-mono text-[9px] text-ko mt-2">Save this now. It will not be shown again after you leave this page.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onUnmounted } from 'vue'
|
import { ref, watch, onMounted, onUnmounted } from 'vue'
|
||||||
import { useRouter, onBeforeRouteLeave } from 'vue-router'
|
import { useRouter, onBeforeRouteLeave } from 'vue-router'
|
||||||
import { useNostr } from '../composables/useNostr'
|
import { useNostr } from '../composables/useNostr'
|
||||||
import { useWallet } from '../composables/useWallet'
|
import { useWallet } from '../composables/useWallet'
|
||||||
@@ -10,7 +10,7 @@ import WalletConnect from '../components/WalletConnect.vue'
|
|||||||
import { authFetch } from '../lib/nostr-auth'
|
import { authFetch } from '../lib/nostr-auth'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { pubkey, bot, profilePicUrl, isLoggedIn, hasExtension, hasStoredKey, isLoading, login, waitForSigner, generateLogin, loginWithNsec, registerBot, registerHuman, getStoredNsec, persistKey, logout, initiateNip55Login, processNip55Return, hasAndroidSigner } = useNostr()
|
const { pubkey, bot, profilePicUrl, isLoggedIn, hasExtension, hasStoredKey, isLoading, login, waitForSigner, generateLogin, loginWithNsec, registerBot, registerHuman, updateWebhook, getStoredNsec, persistKey, logout, initiateNip55Login, processNip55Return, hasAndroidSigner } = useNostr()
|
||||||
const showNsecBackup = ref(false)
|
const showNsecBackup = ref(false)
|
||||||
const generatedNsec = ref('')
|
const generatedNsec = ref('')
|
||||||
const generatedNsecHex = ref('')
|
const generatedNsecHex = ref('')
|
||||||
@@ -388,20 +388,16 @@ async function confirmName() {
|
|||||||
error.value = ''
|
error.value = ''
|
||||||
isCheckingName.value = true
|
isCheckingName.value = true
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/auth/check-name/${encodeURIComponent(name)}`)
|
// Register the bot immediately (poll mode default — webhook updated later)
|
||||||
if (res.ok) {
|
const result = await registerBot(name, '', selectedArchetype.value)
|
||||||
const data = await res.json()
|
botId.value = result.bot.id
|
||||||
if (!data.available) {
|
botSecret.value = result.secret
|
||||||
error.value = 'That name is taken. Pick another.'
|
step.value = 'bot-setup'
|
||||||
return
|
} catch (e) {
|
||||||
}
|
handleError(e, 'Registration failed.')
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// If check fails, proceed anyway — server will catch it at registration
|
|
||||||
} finally {
|
} finally {
|
||||||
isCheckingName.value = false
|
isCheckingName.value = false
|
||||||
}
|
}
|
||||||
step.value = 'bot-setup'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const isChoosingMode = ref(false)
|
const isChoosingMode = ref(false)
|
||||||
@@ -504,9 +500,7 @@ async function confirmWebhook() {
|
|||||||
error.value = ''
|
error.value = ''
|
||||||
isVerifyingWebhook.value = true
|
isVerifyingWebhook.value = true
|
||||||
try {
|
try {
|
||||||
const result = await registerBot(botName.value.trim(), url, selectedArchetype.value)
|
await updateWebhook(url)
|
||||||
botId.value = result.bot.id
|
|
||||||
botSecret.value = result.secret
|
|
||||||
step.value = 'ready'
|
step.value = 'ready'
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(e, 'Webhook verification failed. Check your URL and try again.')
|
handleError(e, 'Webhook verification failed. Check your URL and try again.')
|
||||||
@@ -515,22 +509,15 @@ async function confirmWebhook() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function confirmPolling() {
|
function confirmPolling() {
|
||||||
error.value = ''
|
step.value = 'ready'
|
||||||
try {
|
|
||||||
const result = await registerBot(botName.value.trim(), '', selectedArchetype.value)
|
|
||||||
botId.value = result.bot.id
|
|
||||||
botSecret.value = result.secret
|
|
||||||
step.value = 'ready'
|
|
||||||
} catch (e) {
|
|
||||||
handleError(e, 'Registration failed.')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const showSetupContent = ref(false)
|
const showSetupContent = ref(false)
|
||||||
const setupContent = ref('')
|
const setupContent = ref('')
|
||||||
const setupContentLoading = ref(false)
|
const setupContentLoading = ref(false)
|
||||||
const setupContentCopied = ref(false)
|
const setupContentCopied = ref(false)
|
||||||
|
watch(connectionMode, () => { setupContent.value = ''; showSetupContent.value = false })
|
||||||
|
|
||||||
function setupDocPath() {
|
function setupDocPath() {
|
||||||
return connectionMode.value === 'polling' ? '/docs/BOTFIGHTS-POLLING.md' : '/docs/BOTFIGHTS-WEBHOOK.md'
|
return connectionMode.value === 'polling' ? '/docs/BOTFIGHTS-POLLING.md' : '/docs/BOTFIGHTS-WEBHOOK.md'
|
||||||
@@ -1009,35 +996,58 @@ function handleSignOut() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Safety callout -->
|
<!-- Credentials + setup guide -->
|
||||||
<div class="p-2.5 border border-neon-cyan/20 bg-neon-cyan/5 mb-4">
|
<div v-if="botSecret" class="mb-4 p-3 border border-neon-cyan/20 bg-neon-cyan/5">
|
||||||
<p class="font-display font-bold text-[10px] tracking-wider text-neon-cyan mb-1">
|
<p class="font-display font-bold text-[10px] tracking-wider text-neon-cyan mb-2">
|
||||||
SAFE & SIMPLE
|
COPY THIS TO YOUR AI
|
||||||
</p>
|
</p>
|
||||||
<ul class="font-mono text-[10px] text-text-muted space-y-0.5 leading-relaxed">
|
|
||||||
<li v-if="connectionMode === 'webhook'">
|
|
||||||
We only send <span class="text-text-secondary">POST</span> requests with fight questions
|
|
||||||
</li>
|
|
||||||
<li v-if="connectionMode === 'polling'">
|
|
||||||
<span class="text-text-secondary">No incoming connections</span> — outbound only
|
|
||||||
</li>
|
|
||||||
<li>Your <span class="text-text-secondary">API keys stay on your machine</span></li>
|
|
||||||
<li>Private IPs <span class="text-text-secondary">blocked</span>, payloads <span class="text-text-secondary"><2KB</span></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- What's happening -->
|
<div class="bg-bg border border-border p-2.5 font-mono text-[10px] text-text-muted leading-relaxed select-all mb-2">
|
||||||
<details class="mb-4 group">
|
<div>BOT_ID=<span class="text-neon-cyan break-all">{{ botId }}</span></div>
|
||||||
<summary class="font-display font-bold text-[10px] tracking-wider text-text-secondary cursor-pointer
|
<div>BOT_SECRET=<span class="text-neon-cyan break-all">{{ botSecret }}</span></div>
|
||||||
hover:text-neon-purple transition-colors select-none">
|
</div>
|
||||||
WHAT'S ACTUALLY HAPPENING?
|
|
||||||
</summary>
|
<button
|
||||||
<p class="mt-2 font-mono text-[10px] text-text-muted leading-relaxed">
|
class="w-full py-2 border border-border text-[10px] font-display font-bold tracking-wider
|
||||||
Your AI runs a small server that receives fight challenges from BOTFIGHTS.
|
hover:border-neon-cyan/40 hover:text-neon-cyan transition-all"
|
||||||
When a challenge comes in, it uses Claude to figure out the answer and fires it back.
|
:class="setupGuideCopied ? 'text-neon-green border-neon-green/40' : 'text-text-muted'"
|
||||||
5-10 rounds per fight, scored on correctness and speed.
|
@click="copyFullPrompt"
|
||||||
|
>
|
||||||
|
{{ setupGuideCopied ? 'COPIED' : 'COPY GUIDE + CREDENTIALS' }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<p class="font-mono text-[9px] text-text-muted/60 leading-relaxed mt-2">
|
||||||
|
Paste into your AI — includes full setup guide with your credentials.
|
||||||
</p>
|
</p>
|
||||||
</details>
|
|
||||||
|
<!-- Collapsible guide preview -->
|
||||||
|
<button
|
||||||
|
class="w-full mt-2 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"
|
||||||
|
@click="toggleSetupContent"
|
||||||
|
>
|
||||||
|
{{ showSetupContent ? 'HIDE' : 'VIEW' }} SETUP GUIDE
|
||||||
|
</button>
|
||||||
|
<div v-if="showSetupContent" class="mt-2 border border-border bg-black/40 overflow-hidden">
|
||||||
|
<div class="flex items-center justify-between px-3 py-2 border-b border-border/50 bg-surface-raised/30">
|
||||||
|
<span class="font-display font-bold text-[9px] tracking-wider text-text-muted">{{ setupDocName() }}</span>
|
||||||
|
<button
|
||||||
|
class="font-display font-bold text-[9px] tracking-wider px-2 py-0.5 border transition-all"
|
||||||
|
:class="setupContentCopied
|
||||||
|
? 'border-neon-green/40 text-neon-green'
|
||||||
|
: 'border-border text-text-muted hover:border-neon-cyan/40 hover:text-neon-cyan'"
|
||||||
|
@click="copySetupContent"
|
||||||
|
>
|
||||||
|
{{ setupContentCopied ? 'COPIED' : 'COPY ALL' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div v-if="setupContentLoading" class="p-4 text-center">
|
||||||
|
<p class="font-mono text-[10px] text-text-muted animate-pulse">Loading...</p>
|
||||||
|
</div>
|
||||||
|
<pre v-else class="p-3 font-mono text-[10px] text-text-secondary leading-relaxed
|
||||||
|
overflow-x-auto max-h-80 overflow-y-auto whitespace-pre-wrap break-words select-all">{{ setupContent }}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<button
|
<button
|
||||||
|
|||||||
Reference in New Issue
Block a user