From 210fba4e084e750e7498d59f70fcc9cdbb2f1be9 Mon Sep 17 00:00:00 2001 From: Mark Henderson Date: Mon, 27 Apr 2026 16:26:55 -0700 Subject: [PATCH] Fix chat panel token fetch: use /api/workspaces not URL slug URL param 'mark-account' != workspace slug 'mark'. Fetch default token from /api/workspaces?include_default_token=true which resolves the real slug server-side. Made-with: Cursor --- app/api/workspaces/route.ts | 25 ++++++++++++++++++++++++- components/vibn-chat/chat-panel.tsx | 9 +++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/app/api/workspaces/route.ts b/app/api/workspaces/route.ts index 5ce09375..12b6a5f7 100644 --- a/app/api/workspaces/route.ts +++ b/app/api/workspaces/route.ts @@ -10,7 +10,7 @@ import { NextResponse } from 'next/server'; import { authSession } from '@/lib/auth/session-server'; import { queryOne } from '@/lib/db-postgres'; import { ensureWorkspaceForUser, listWorkspacesForUser } from '@/lib/workspaces'; -import { requireWorkspacePrincipal } from '@/lib/auth/workspace-auth'; +import { requireWorkspacePrincipal, listWorkspaceApiKeys, mintWorkspaceApiKey, revealWorkspaceApiKey } from '@/lib/auth/workspace-auth'; export async function GET(request: Request) { if (request.headers.get('authorization')?.toLowerCase().startsWith('bearer vibn_sk_')) { @@ -49,6 +49,29 @@ export async function GET(request: Request) { } } + const url = new URL(request.url); + const includeDefaultToken = url.searchParams.get('include_default_token') === 'true'; + + if (includeDefaultToken && list.length > 0) { + const ws = list[0]; + let defaultToken: string | null = null; + try { + const keys = await listWorkspaceApiKeys(ws.id); + let defaultKey = keys.find((k: any) => k.name === 'default' && !k.revoked_at); + if (!defaultKey) { + const minted = await mintWorkspaceApiKey({ workspaceId: ws.id, name: 'default', createdBy: userRow!.id, scopes: ['workspace:*'] }); + defaultToken = minted.token; + } else { + defaultToken = await revealWorkspaceApiKey(ws.id, defaultKey.id); + if (!defaultToken) { + const minted = await mintWorkspaceApiKey({ workspaceId: ws.id, name: 'default', createdBy: userRow!.id, scopes: ['workspace:*'] }); + defaultToken = minted.token; + } + } + } catch { /* non-fatal */ } + return NextResponse.json({ workspaces: list.map(serializeWorkspace), defaultToken }); + } + return NextResponse.json({ workspaces: list.map(serializeWorkspace) }); } diff --git a/components/vibn-chat/chat-panel.tsx b/components/vibn-chat/chat-panel.tsx index 9e187129..734ecb59 100644 --- a/components/vibn-chat/chat-panel.tsx +++ b/components/vibn-chat/chat-panel.tsx @@ -163,13 +163,14 @@ export function ChatPanel() { document.documentElement.style.setProperty("--chat-panel-width", open ? "380px" : "0px"); }, [open]); - // Load MCP token — prefer localStorage cache, fetch from API if missing + // Load MCP token — prefer localStorage cache, fetch from API if missing. + // We use /api/workspaces (not the URL param) because the URL slug + // (e.g. "mark-account") differs from the actual workspace slug ("mark"). useEffect(() => { if (!workspace || status !== "authenticated") return; const cached = localStorage.getItem(`vibn-mcp-token-${workspace}`); if (cached) { setMcpToken(cached); return; } - // Auto-fetch the workspace's default key (created at account setup) - fetch(`/api/workspaces/${workspace}/keys?include_default_token=true`) + fetch("/api/workspaces?include_default_token=true") .then((r) => r.ok ? r.json() : null) .then((d) => { if (d?.defaultToken) { @@ -177,7 +178,7 @@ export function ChatPanel() { setMcpToken(d.defaultToken); } }) - .catch(() => {/* silent — panel works in read-only mode */}); + .catch(() => {}); }, [workspace, status]); // Load threads