diff --git a/AGENTS.md b/AGENTS.md index 7821501..3942bfb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -142,6 +142,8 @@ session with **no other memory of this project**. So: Owner Settings shows the upstream balance (`SasquatchAiStatusCard`) via the owner-only `POST /api/ai/status` (NIP-98 + owner pubkey → `handleStatus`), with a "low balance" warning below 500 sats — the top-up nudge (no webhook needed). + The same balance shows as a chip in the Sasquatch panel header (owner-only, + fetched only while the panel is open so it never prompts at startup). **AI answers are Pro-only**: free users get the static FAQ/glossary + local relay lookups only (`if (isPro)` gates the AI block in `SasquatchGuide`). Pro access (`useSasquatchPro`) is granted by a provider-authored zap receipt, diff --git a/src/components/SasquatchGuide.tsx b/src/components/SasquatchGuide.tsx index dd8f1fc..e0bff17 100644 --- a/src/components/SasquatchGuide.tsx +++ b/src/components/SasquatchGuide.tsx @@ -17,6 +17,8 @@ import { useCurrentUser } from '@/hooks/useCurrentUser'; import { useCVMClient } from '@/hooks/useCVMClient'; import { usePersonalContext } from '@/hooks/usePersonalContext'; import { useSasquatchPro } from '@/hooks/useSasquatchPro'; +import { useSasquatchAiStatus } from '@/hooks/useSasquatchAiStatus'; +import { PLATFORM_OWNER_PUBKEYS } from '@/hooks/useListingRestrictions'; import { SasquatchProDialog } from '@/components/SasquatchProDialog'; import { useSasquatchActions, type SasquatchDraft } from '@/hooks/useSasquatchActions'; import { SasquatchActionCard } from '@/components/SasquatchActionCard'; @@ -636,11 +638,16 @@ export function SasquatchGuide({ message, onDismiss }: SasquatchGuideProps) { const cvm = useCVMClient(); const personal = usePersonalContext(); const { isPro, receiptEvent } = useSasquatchPro(); + // Owner-only: show the AI (Routstr) balance on the panel — fetched only while + // the panel is open, so it never prompts at startup. + const isOwner = !!user?.pubkey && PLATFORM_OWNER_PUBKEYS.includes(user.pubkey); const { publishDraft } = useSasquatchActions(); const [proDialogOpen, setProDialogOpen] = useState(false); const [isMinimized, setIsMinimized] = useLocalStorage('sasquatch-minimized', false); const [isVisible, setIsVisible] = useState(false); + // Fetched only while the panel is open (owner-only). + const aiStatus = useSasquatchAiStatus({ enabled: isOwner && isVisible && !isMinimized }); const [chatMessages, setChatMessages] = useState([]); const [userInput, setUserInput] = useState(''); const [showChat, setShowChat] = useState(false); @@ -1547,6 +1554,17 @@ Instructions: ) : null} + {isOwner && aiStatus.data?.configured && aiStatus.data.balanceSats != null && ( + + ⚡ {aiStatus.data.balanceSats.toLocaleString()} sats + + )}
{activeMessage.title} diff --git a/src/hooks/useSasquatchAiStatus.ts b/src/hooks/useSasquatchAiStatus.ts index 9a9cf9e..1271665 100644 --- a/src/hooks/useSasquatchAiStatus.ts +++ b/src/hooks/useSasquatchAiStatus.ts @@ -21,13 +21,13 @@ const AI_STATUS_URL = Capacitor.isNativePlatform() * nudged to top up before AI silently degrades to the keyless fallback. Polled * every few minutes. Returns `undefined`/skips for non-owners. */ -export function useSasquatchAiStatus() { +export function useSasquatchAiStatus(options: { enabled?: boolean } = {}) { const { user } = useCurrentUser(); const isOwner = !!user?.pubkey && PLATFORM_OWNER_PUBKEYS.includes(user.pubkey); return useQuery({ queryKey: ['sasquatch-ai-status', user?.pubkey], - enabled: isOwner, + enabled: (options.enabled ?? true) && isOwner, refetchInterval: 5 * 60 * 1000, staleTime: 60 * 1000, queryFn: async () => {