stack/apps/internal-tool/src/hooks/useSpacetimeDB.ts
aadesh18 5341371782
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests (Local Emulator) / E2E Tests (Local Emulator, Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
Mirror main branch to main-mirror-for-wdb / lint_and_build (push) Has been cancelled
Publish npm packages / publish (push) Has been cancelled
Publish Swift SDK to prerelease repo / publish (push) Has been cancelled
Sync Main to Dev / sync-commits (push) Has been cancelled
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
DB migration compat / No migration changes (skipped) (push) Has been cancelled
LLM MCP Flow (#1321)
<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Automated AI QA review pipeline and human-verified knowledge base
consulted first
* Internal MCP review tool: call log viewer, conversation replay,
add/edit/publish Q&A, knowledge editor, and analytics
  * Docs search now preserves follow-up conversation context

* **Documentation**
  * Added “Ask DeepWiki” badge to README

* **Chores**
* Added local SpacetimeDB background service and internal-tool app
scaffolding
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: mantrakp04 <mantrakp@gmail.com>
Co-authored-by: Mantra <87142457+mantrakp04@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com>
2026-04-15 17:57:08 +00:00

127 lines
4.4 KiB
TypeScript

import { useEffect, useState, useRef } from "react";
import { DbConnection, type EventContext, type SubscriptionEventContext } from "../module_bindings";
import type { McpCallLogRow } from "../types";
const IS_DEV = process.env.NODE_ENV === "development";
const PLACEHOLDER = "REPLACE_ME";
const rawHost = process.env.NEXT_PUBLIC_SPACETIMEDB_HOST;
const rawDbName = process.env.NEXT_PUBLIC_SPACETIMEDB_DB_NAME;
function resolveEnv(raw: string | undefined, devDefault: string, name: string): string {
if (raw && raw !== PLACEHOLDER) return raw;
if (IS_DEV) return devDefault;
throw new Error(`${name} is not configured. Set it in .env.local or hosting platform env.`);
}
const HOST = resolveEnv(rawHost, "ws://localhost:8139", "NEXT_PUBLIC_SPACETIMEDB_HOST");
const DB_NAME = resolveEnv(rawDbName, "stack-auth-llm", "NEXT_PUBLIC_SPACETIMEDB_DB_NAME");
const TOKEN_KEY = `spacetimedb_${HOST}/${DB_NAME}/auth_token`;
const MAX_RETRIES = 5;
const RETRY_DELAY_MS = 2000;
type ConnectionState = "connecting" | "connected" | "disconnected" | "error";
export function useMcpCallLogs() {
const [rows, setRows] = useState<McpCallLogRow[]>([]);
const [connectionState, setConnectionState] = useState<ConnectionState>("connecting");
const connRef = useRef<DbConnection | null>(null);
useEffect(() => {
let cancelled = false;
let retryCount = 0;
let retryTimer: ReturnType<typeof setTimeout> | null = null;
console.log("[SpacetimeDB] Connecting to", HOST, "db:", DB_NAME);
function retry() {
if (cancelled) return;
retryCount++;
if (retryCount > MAX_RETRIES) {
console.error("[SpacetimeDB] Max retries reached");
setConnectionState("error");
return;
}
console.log(`[SpacetimeDB] Retrying in ${RETRY_DELAY_MS}ms (attempt ${retryCount}/${MAX_RETRIES})...`);
retryTimer = setTimeout(() => {
retryTimer = null;
if (!cancelled) {
connect();
}
}, RETRY_DELAY_MS);
}
function connect() {
const conn = DbConnection.builder()
.withUri(HOST)
.withDatabaseName(DB_NAME)
.withToken(localStorage.getItem(TOKEN_KEY) || undefined)
.onConnect((connInstance: DbConnection, _identity: unknown, token: string) => {
if (cancelled) return;
console.log("[SpacetimeDB] Connected successfully");
retryCount = 0;
localStorage.setItem(TOKEN_KEY, token);
connRef.current = connInstance;
connInstance.subscriptionBuilder()
.onApplied((ctx: SubscriptionEventContext) => {
if (cancelled) return;
const initialRows: McpCallLogRow[] = [];
for (const row of ctx.db.mcpCallLog.iter()) {
initialRows.push(row);
}
initialRows.sort((a, b) => Number(b.id - a.id));
console.log("[SpacetimeDB] Loaded", initialRows.length, "rows");
setRows(initialRows);
setConnectionState("connected");
})
.subscribe(`SELECT * FROM mcp_call_log`);
connInstance.db.mcpCallLog.onInsert((_ctx: EventContext, row: McpCallLogRow) => {
if (cancelled) return;
setRows(prev => {
const existing = prev.findIndex(r => r.id === row.id);
if (existing >= 0) {
const updated = [...prev];
updated[existing] = row;
return updated;
}
return [row, ...prev];
});
});
connInstance.db.mcpCallLog.onDelete((_ctx: EventContext, row: McpCallLogRow) => {
if (cancelled) return;
setRows(prev => prev.filter(r => r.id !== row.id));
});
})
.onConnectError((_ctx: unknown, err: unknown) => {
console.error("[SpacetimeDB] Connection error:", err);
const storedToken = localStorage.getItem(TOKEN_KEY);
if (storedToken) {
console.log("[SpacetimeDB] Clearing stale token");
localStorage.removeItem(TOKEN_KEY);
}
retry();
})
.build();
connRef.current = conn;
}
connect();
return () => {
cancelled = true;
if (retryTimer !== null) {
clearTimeout(retryTimer);
retryTimer = null;
}
if (connRef.current) {
connRef.current.disconnect();
connRef.current = null;
}
};
}, []);
return { rows, connectionState };
}