diff --git a/src/components/monitor/SettingsPanel.tsx b/src/components/monitor/SettingsPanel.tsx index c4630a9..1ca30f1 100644 --- a/src/components/monitor/SettingsPanel.tsx +++ b/src/components/monitor/SettingsPanel.tsx @@ -1,13 +1,18 @@ import { useState } from 'react' import { motion, AnimatePresence } from 'framer-motion' -import { Settings, X, History, Wifi, WifiOff, RefreshCw, Terminal } from 'lucide-react' +import { Settings, X, History, Wifi, WifiOff, RefreshCw, Terminal, Download, Trash2, Database } from 'lucide-react' interface SettingsPanelProps { connected: boolean historicalMode: boolean debugMode: boolean + logCollection: boolean + logCount: number onHistoricalModeChange: (enabled: boolean) => void onDebugModeChange: (enabled: boolean) => void + onLogCollectionChange: (enabled: boolean) => void + onDownloadLogs: () => void + onClearLogs: () => void onConnect: () => void onDisconnect: () => void onRefresh: () => void @@ -17,8 +22,13 @@ export function SettingsPanel({ connected, historicalMode, debugMode, + logCollection, + logCount, onHistoricalModeChange, onDebugModeChange, + onLogCollectionChange, + onDownloadLogs, + onClearLogs, onConnect, onDisconnect, onRefresh, @@ -161,6 +171,56 @@ export function SettingsPanel({ + {/* Log collection */} +
+
+ + + Log Collection + +
+ +

+ > collect raw events for export +

+ + {logCount > 0 && ( +
+ > {logCount} events collected +
+ )} + + + +
+ + +
+
+ {/* Info panel */}

diff --git a/src/integrations/trpc/router.ts b/src/integrations/trpc/router.ts index f08d9b4..7b6378a 100644 --- a/src/integrations/trpc/router.ts +++ b/src/integrations/trpc/router.ts @@ -13,6 +13,10 @@ import { // Server-side debug mode state let debugMode = false +// Server-side log collection +let collectLogs = false +const collectedEvents: Array<{ timestamp: number; event: unknown }> = [] + const t = initTRPC.create({ transformer: superjson, }) @@ -66,6 +70,38 @@ const clawdbotRouter = router({ return { debugMode } }), + // Log collection + setLogCollection: publicProcedure + .input(z.object({ enabled: z.boolean() })) + .mutation(({ input }) => { + collectLogs = input.enabled + if (input.enabled) { + console.log(`[clawdbot] log collection started`) + } else { + console.log(`[clawdbot] log collection stopped, ${collectedEvents.length} events collected`) + } + return { collectLogs, eventCount: collectedEvents.length } + }), + + getLogCollection: publicProcedure.query(() => { + return { collectLogs, eventCount: collectedEvents.length } + }), + + downloadLogs: publicProcedure.query(() => { + return { + events: collectedEvents, + count: collectedEvents.length, + collectedAt: new Date().toISOString(), + } + }), + + clearLogs: publicProcedure.mutation(() => { + const count = collectedEvents.length + collectedEvents.length = 0 + console.log(`[clawdbot] cleared ${count} collected events`) + return { cleared: count } + }), + sessions: publicProcedure .input( z @@ -103,6 +139,14 @@ const clawdbotRouter = router({ const client = getClawdbotClient() const unsubscribe = client.onEvent((event) => { + // Collect raw event when log collection is enabled + if (collectLogs) { + collectedEvents.push({ + timestamp: Date.now(), + event, + }) + } + // Log raw event when debug mode is enabled if (debugMode) { console.log('\n[DEBUG] Raw event:', JSON.stringify(event, null, 2)) diff --git a/src/routes/monitor/index.tsx b/src/routes/monitor/index.tsx index 7f0d2ad..c4411ba 100644 --- a/src/routes/monitor/index.tsx +++ b/src/routes/monitor/index.tsx @@ -63,6 +63,8 @@ function MonitorPage() { const [error, setError] = useState(null) const [historicalMode, setHistoricalMode] = useState(false) const [debugMode, setDebugMode] = useState(false) + const [logCollection, setLogCollection] = useState(false) + const [logCount, setLogCount] = useState(0) const [selectedSession, setSelectedSession] = useState(null) // Sidebar collapse state - default to collapsed on mobile @@ -168,6 +170,56 @@ function MonitorPage() { } } + const handleLogCollectionChange = async (enabled: boolean) => { + setLogCollection(enabled) + try { + const result = await trpc.clawdbot.setLogCollection.mutate({ enabled }) + setLogCount(result.eventCount) + } catch (e) { + console.error('Failed to set log collection:', e) + } + } + + const handleDownloadLogs = async () => { + try { + const result = await trpc.clawdbot.downloadLogs.query() + const blob = new Blob([JSON.stringify(result, null, 2)], { type: 'application/json' }) + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = `clawdbot-events-${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.json` + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + URL.revokeObjectURL(url) + } catch (e) { + console.error('Failed to download logs:', e) + } + } + + const handleClearLogs = async () => { + try { + await trpc.clawdbot.clearLogs.mutate() + setLogCount(0) + } catch (e) { + console.error('Failed to clear logs:', e) + } + } + + // Poll log count while collecting + useEffect(() => { + if (!logCollection) return + const interval = setInterval(async () => { + try { + const result = await trpc.clawdbot.getLogCollection.query() + setLogCount(result.eventCount) + } catch { + // ignore + } + }, 2000) + return () => clearInterval(interval) + }, [logCollection]) + const handleToggleSidebar = useCallback(() => { setSidebarCollapsed((prev) => !prev) }, []) @@ -275,8 +327,13 @@ function MonitorPage() { connected={connected} historicalMode={historicalMode} debugMode={debugMode} + logCollection={logCollection} + logCount={logCount} onHistoricalModeChange={handleHistoricalModeChange} onDebugModeChange={handleDebugModeChange} + onLogCollectionChange={handleLogCollectionChange} + onDownloadLogs={handleDownloadLogs} + onClearLogs={handleClearLogs} onConnect={handleConnect} onDisconnect={handleDisconnect} onRefresh={handleRefresh}