@tokentop/ttop 0.2.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/README.md +2 -5
  2. package/package.json +8 -5
  3. package/src/agents/aggregator.ts +39 -19
  4. package/src/agents/costing.ts +11 -9
  5. package/src/agents/index.ts +7 -9
  6. package/src/agents/types.ts +2 -2
  7. package/src/cli.ts +28 -23
  8. package/src/config/schema.test.ts +42 -40
  9. package/src/config/schema.ts +26 -26
  10. package/src/demo/simulator.ts +161 -122
  11. package/src/plugins/agents/index.ts +2 -2
  12. package/src/plugins/auth-sources.ts +42 -38
  13. package/src/plugins/lifecycle.ts +42 -38
  14. package/src/plugins/loader.test.ts +49 -49
  15. package/src/plugins/loader.ts +67 -63
  16. package/src/plugins/notification-bus.ts +21 -16
  17. package/src/plugins/notifications/index.ts +2 -2
  18. package/src/plugins/notifications/terminal-bell.ts +36 -23
  19. package/src/plugins/notifications/visual-flash.ts +31 -19
  20. package/src/plugins/npm-installer.ts +23 -25
  21. package/src/plugins/plugin-context-factory.ts +10 -10
  22. package/src/plugins/plugin-host.ts +12 -5
  23. package/src/plugins/providers/anthropic.ts +64 -57
  24. package/src/plugins/providers/antigravity.ts +81 -69
  25. package/src/plugins/providers/codex.ts +42 -42
  26. package/src/plugins/providers/gemini.ts +106 -87
  27. package/src/plugins/providers/github-copilot.ts +49 -45
  28. package/src/plugins/providers/index.ts +10 -12
  29. package/src/plugins/providers/minimax.ts +50 -44
  30. package/src/plugins/providers/openai-api.ts +82 -62
  31. package/src/plugins/providers/opencode-zen.ts +46 -39
  32. package/src/plugins/providers/perplexity.ts +55 -46
  33. package/src/plugins/providers/zai.ts +68 -57
  34. package/src/plugins/registry.ts +80 -71
  35. package/src/plugins/sandbox-guard.test.ts +45 -48
  36. package/src/plugins/sandbox-guard.ts +20 -25
  37. package/src/plugins/sandbox.ts +26 -48
  38. package/src/plugins/themes/catppuccin-latte.ts +29 -29
  39. package/src/plugins/themes/catppuccin-mocha.ts +29 -29
  40. package/src/plugins/themes/claude-code.ts +29 -29
  41. package/src/plugins/themes/dracula.ts +29 -29
  42. package/src/plugins/themes/github-light.ts +29 -29
  43. package/src/plugins/themes/gruvbox-dark.ts +29 -29
  44. package/src/plugins/themes/gruvbox-light.ts +29 -29
  45. package/src/plugins/themes/index.ts +15 -15
  46. package/src/plugins/themes/kanagawa.ts +29 -29
  47. package/src/plugins/themes/nord.ts +29 -29
  48. package/src/plugins/themes/one-dark.ts +29 -29
  49. package/src/plugins/themes/opencode.ts +31 -31
  50. package/src/plugins/themes/rose-pine-dawn.ts +29 -29
  51. package/src/plugins/themes/rose-pine.ts +29 -29
  52. package/src/plugins/themes/solarized-light.ts +29 -29
  53. package/src/plugins/themes/tokyo-night.ts +29 -29
  54. package/src/plugins/types/agent.ts +5 -5
  55. package/src/plugins/types/base.ts +35 -24
  56. package/src/plugins/types/index.ts +9 -9
  57. package/src/plugins/types/notification.ts +12 -12
  58. package/src/plugins/types/provider.ts +12 -8
  59. package/src/plugins/types/theme.ts +31 -23
  60. package/src/plugins/update-checker.ts +6 -6
  61. package/src/pricing/estimator.test.ts +39 -44
  62. package/src/pricing/estimator.ts +7 -13
  63. package/src/pricing/fallback.test.ts +30 -34
  64. package/src/pricing/fallback.ts +54 -27
  65. package/src/pricing/index.ts +18 -12
  66. package/src/pricing/models-dev.ts +6 -6
  67. package/src/storage/database.ts +56 -46
  68. package/src/storage/db.ts +12 -12
  69. package/src/storage/index.ts +44 -45
  70. package/src/storage/migrations/index.ts +2 -2
  71. package/src/storage/paths.ts +13 -13
  72. package/src/storage/persistence-service.ts +206 -0
  73. package/src/storage/repos/agentSessions.ts +33 -23
  74. package/src/storage/repos/providerSnapshots.ts +5 -5
  75. package/src/storage/repos/usageEvents.ts +40 -28
  76. package/src/storage/retention.ts +146 -0
  77. package/src/storage/types.ts +5 -5
  78. package/src/tui/App.tsx +180 -137
  79. package/src/tui/components/CommandPalette.tsx +46 -34
  80. package/src/tui/components/DebugConsole.tsx +39 -33
  81. package/src/tui/components/DebugPanel.tsx +314 -183
  82. package/src/tui/components/FooterHints.tsx +2 -2
  83. package/src/tui/components/GhostProviderCard.tsx +37 -35
  84. package/src/tui/components/Header.tsx +68 -35
  85. package/src/tui/components/HelpOverlay.tsx +99 -29
  86. package/src/tui/components/InlineGauge.tsx +10 -7
  87. package/src/tui/components/InlineSparkline.tsx +17 -11
  88. package/src/tui/components/KpiStrip.tsx +128 -98
  89. package/src/tui/components/LimitGauge.tsx +96 -59
  90. package/src/tui/components/ModalBackdrop.tsx +4 -4
  91. package/src/tui/components/ProviderAggregateStrip.tsx +69 -38
  92. package/src/tui/components/ProviderCard.tsx +232 -226
  93. package/src/tui/components/ProviderDetailPanel.tsx +133 -58
  94. package/src/tui/components/ProviderLimitsPanel.tsx +126 -89
  95. package/src/tui/components/ProvidersList.tsx +215 -126
  96. package/src/tui/components/SessionDetailsDrawer.tsx +191 -94
  97. package/src/tui/components/SessionsTable.tsx +320 -169
  98. package/src/tui/components/SettingsModal.tsx +593 -269
  99. package/src/tui/components/SidebarBreakdown.tsx +30 -16
  100. package/src/tui/components/Skeleton.tsx +24 -17
  101. package/src/tui/components/SmartSidebar.tsx +360 -258
  102. package/src/tui/components/Sparkline.tsx +64 -52
  103. package/src/tui/components/Spinner.tsx +3 -3
  104. package/src/tui/components/StatusBar.tsx +27 -27
  105. package/src/tui/components/ThemePicker.tsx +67 -66
  106. package/src/tui/components/Toast.tsx +9 -6
  107. package/src/tui/components/UsageGauge.tsx +10 -13
  108. package/src/tui/components/index.ts +5 -5
  109. package/src/tui/contexts/AgentSessionContext.tsx +164 -151
  110. package/src/tui/contexts/ConfigContext.tsx +15 -13
  111. package/src/tui/contexts/DashboardRuntimeContext.tsx +26 -26
  112. package/src/tui/contexts/DemoModeContext.tsx +22 -15
  113. package/src/tui/contexts/DrawerContext.tsx +11 -9
  114. package/src/tui/contexts/InputContext.tsx +2 -2
  115. package/src/tui/contexts/LogContext.tsx +62 -47
  116. package/src/tui/contexts/PluginContext.tsx +238 -183
  117. package/src/tui/contexts/RealTimeActivityContext.tsx +70 -36
  118. package/src/tui/contexts/StorageContext.tsx +95 -149
  119. package/src/tui/contexts/ThemeContext.tsx +21 -16
  120. package/src/tui/contexts/TimeWindowContext.tsx +72 -61
  121. package/src/tui/contexts/ToastContext.tsx +10 -7
  122. package/src/tui/contexts/index.ts +9 -5
  123. package/src/tui/createApp.tsx +8 -12
  124. package/src/tui/debug/captureFrame.ts +25 -28
  125. package/src/tui/debug/snapshot.tsx +179 -162
  126. package/src/tui/driver/assertions.ts +30 -22
  127. package/src/tui/driver/cli.ts +231 -221
  128. package/src/tui/driver/coverage.ts +27 -44
  129. package/src/tui/driver/demo-snapshot.ts +33 -33
  130. package/src/tui/driver/diff.ts +26 -32
  131. package/src/tui/driver/driver.ts +43 -48
  132. package/src/tui/driver/index.ts +36 -40
  133. package/src/tui/driver/recorder.ts +37 -48
  134. package/src/tui/driver/test-workflow.ts +51 -51
  135. package/src/tui/driver/test.ts +25 -25
  136. package/src/tui/hooks/index.ts +24 -20
  137. package/src/tui/hooks/useAnimatedValue.ts +13 -18
  138. package/src/tui/hooks/useDashboardKeyboard.ts +118 -98
  139. package/src/tui/hooks/useDashboardState.ts +26 -24
  140. package/src/tui/hooks/useEmaActivity.ts +82 -74
  141. package/src/tui/hooks/useEntranceAnimation.ts +11 -11
  142. package/src/tui/hooks/useExitAnimation.ts +7 -3
  143. package/src/tui/hooks/usePulse.ts +4 -4
  144. package/src/tui/hooks/useSafeRenderer.ts +5 -5
  145. package/src/tui/hooks/useSessionEnrichedProviders.ts +150 -0
  146. package/src/tui/hooks/useValueFlash.ts +10 -14
  147. package/src/tui/index.tsx +29 -6
  148. package/src/tui/shutdown.ts +19 -0
  149. package/src/tui/utils/providerColor.ts +1 -1
  150. package/src/tui/views/Dashboard.tsx +164 -102
  151. package/src/tui/views/HistoricalTrendsView.tsx +579 -250
  152. package/src/tui/views/ProjectsView.tsx +495 -243
  153. package/src/tui/views/RealTimeDashboard.tsx +176 -116
  154. package/src/tui/views/SettingsView.tsx +225 -189
  155. package/src/tui/views/index.ts +2 -2
  156. package/src/types/opentui-test-utils.d.ts +7 -7
  157. package/src/utils/clipboard.ts +29 -29
  158. package/src/utils/currency.test.ts +53 -53
  159. package/src/utils/currency.ts +35 -32
  160. package/src/version.ts +1 -1
@@ -1,8 +1,12 @@
1
- import { createContext, useContext, useState, useEffect, useRef, type ReactNode } from 'react';
2
- import { useAgentSessions } from './AgentSessionContext.tsx';
3
- import { useTimeWindow } from './TimeWindowContext.tsx';
4
- import { useRealTimeActivity } from './RealTimeActivityContext.tsx';
5
- import { useEmaActivity, type ActivityState, type ActivityDebugData } from '../hooks/useEmaActivity.ts';
1
+ import { createContext, type ReactNode, useContext, useEffect, useRef, useState } from "react";
2
+ import {
3
+ type ActivityDebugData,
4
+ type ActivityState,
5
+ useEmaActivity,
6
+ } from "../hooks/useEmaActivity.ts";
7
+ import { useAgentSessions } from "./AgentSessionContext.tsx";
8
+ import { useRealTimeActivity } from "./RealTimeActivityContext.tsx";
9
+ import { useTimeWindow } from "./TimeWindowContext.tsx";
6
10
 
7
11
  interface Deltas {
8
12
  cost: number;
@@ -13,10 +17,10 @@ interface Deltas {
13
17
  interface DashboardRuntimeContextValue {
14
18
  activity: ActivityState;
15
19
  sparkData: number[];
16
-
20
+
17
21
  deltas: Deltas;
18
- history: Array<{time: number, cost: number, tokens: number}>;
19
-
22
+ history: Array<{ time: number; cost: number; tokens: number }>;
23
+
20
24
  debugDataRef: React.MutableRefObject<ActivityDebugData>;
21
25
  }
22
26
 
@@ -26,48 +30,46 @@ export function DashboardRuntimeProvider({ children }: { children: ReactNode })
26
30
  const { sessions: agentSessions, isLoading } = useAgentSessions();
27
31
  const { windowMs } = useTimeWindow();
28
32
  const { subscribe } = useRealTimeActivity();
29
-
33
+
30
34
  const totalTokens = agentSessions.reduce((sum, s) => sum + s.totals.input + s.totals.output, 0);
31
35
  const { activity, sparkData, debugDataRef, injectDelta } = useEmaActivity(totalTokens);
32
-
36
+
33
37
  useEffect(() => {
34
38
  return subscribe((delta, _timestamp) => {
35
39
  injectDelta(delta);
36
40
  });
37
41
  }, [subscribe, injectDelta]);
38
42
 
39
- const historyRef = useRef<{time: number, cost: number, tokens: number}[]>([]);
43
+ const historyRef = useRef<{ time: number; cost: number; tokens: number }[]>([]);
40
44
  const [deltas, setDeltas] = useState<Deltas>({ cost: 0, tokens: 0, windowSec: 0 });
41
45
 
42
46
  useEffect(() => {
43
47
  if (isLoading || agentSessions.length === 0) return;
44
-
48
+
45
49
  const totalCost = agentSessions.reduce((sum, s) => sum + (s.totalCostUsd ?? 0), 0);
46
50
  const currentTime = Date.now();
47
-
51
+
48
52
  const lastEntry = historyRef.current[historyRef.current.length - 1];
49
- const isDiscontinuity = lastEntry && (
50
- totalTokens < lastEntry.tokens ||
51
- totalTokens > lastEntry.tokens * 2
52
- );
53
+ const isDiscontinuity =
54
+ lastEntry && (totalTokens < lastEntry.tokens || totalTokens > lastEntry.tokens * 2);
53
55
  if (isDiscontinuity) {
54
56
  historyRef.current = [];
55
57
  }
56
-
58
+
57
59
  historyRef.current.push({ time: currentTime, cost: totalCost, tokens: totalTokens });
58
-
60
+
59
61
  if (historyRef.current.length > 300) historyRef.current.shift();
60
62
 
61
63
  const targetTime = windowMs !== null ? currentTime - windowMs : 0;
62
64
  let baseline = historyRef.current[0];
63
-
65
+
64
66
  for (let i = historyRef.current.length - 1; i >= 0; i--) {
65
67
  if (historyRef.current[i]!.time <= targetTime) {
66
68
  baseline = historyRef.current[i];
67
69
  break;
68
70
  }
69
71
  }
70
-
72
+
71
73
  if (baseline) {
72
74
  const windowSec = (currentTime - baseline.time) / 1000;
73
75
  setDeltas({
@@ -83,20 +85,18 @@ export function DashboardRuntimeProvider({ children }: { children: ReactNode })
83
85
  sparkData,
84
86
  deltas,
85
87
  history: historyRef.current,
86
- debugDataRef
88
+ debugDataRef,
87
89
  };
88
90
 
89
91
  return (
90
- <DashboardRuntimeContext.Provider value={value}>
91
- {children}
92
- </DashboardRuntimeContext.Provider>
92
+ <DashboardRuntimeContext.Provider value={value}>{children}</DashboardRuntimeContext.Provider>
93
93
  );
94
94
  }
95
95
 
96
96
  export function useDashboardRuntime() {
97
97
  const context = useContext(DashboardRuntimeContext);
98
98
  if (!context) {
99
- throw new Error('useDashboardRuntime must be used within DashboardRuntimeProvider');
99
+ throw new Error("useDashboardRuntime must be used within DashboardRuntimeProvider");
100
100
  }
101
101
  return context;
102
102
  }
@@ -1,5 +1,5 @@
1
- import { createContext, useContext, useMemo, type ReactNode } from 'react';
2
- import { DemoSimulator, type DemoPreset, type DemoSimulatorOptions } from '@/demo/simulator.ts';
1
+ import { createContext, type ReactNode, useContext, useMemo } from "react";
2
+ import { type DemoPreset, DemoSimulator, type DemoSimulatorOptions } from "@/demo/simulator.ts";
3
3
 
4
4
  interface DemoModeContextValue {
5
5
  demoMode: boolean;
@@ -22,7 +22,10 @@ interface DemoModeProviderProps {
22
22
  children: ReactNode;
23
23
  }
24
24
 
25
- function buildSimulatorOptions(seed: number | undefined, preset: DemoPreset | undefined): DemoSimulatorOptions {
25
+ function buildSimulatorOptions(
26
+ seed: number | undefined,
27
+ preset: DemoPreset | undefined,
28
+ ): DemoSimulatorOptions {
26
29
  if (seed !== undefined && preset !== undefined) {
27
30
  return { seed, preset };
28
31
  }
@@ -35,24 +38,28 @@ function buildSimulatorOptions(seed: number | undefined, preset: DemoPreset | un
35
38
  return {};
36
39
  }
37
40
 
38
- export function DemoModeProvider({ demoMode, demoSeed, demoPreset, children }: DemoModeProviderProps) {
41
+ export function DemoModeProvider({
42
+ demoMode,
43
+ demoSeed,
44
+ demoPreset,
45
+ children,
46
+ }: DemoModeProviderProps) {
39
47
  const simulator = useMemo(() => {
40
48
  if (!demoMode) return null;
41
49
  return new DemoSimulator(buildSimulatorOptions(demoSeed, demoPreset));
42
50
  }, [demoMode, demoSeed, demoPreset]);
43
51
 
44
- const value = useMemo<DemoModeContextValue>(() => ({
45
- demoMode,
46
- simulator,
47
- seed: simulator?.getSeed() ?? null,
48
- preset: simulator?.getPreset() ?? null,
49
- }), [demoMode, simulator]);
50
-
51
- return (
52
- <DemoModeContext.Provider value={value}>
53
- {children}
54
- </DemoModeContext.Provider>
52
+ const value = useMemo<DemoModeContextValue>(
53
+ () => ({
54
+ demoMode,
55
+ simulator,
56
+ seed: simulator?.getSeed() ?? null,
57
+ preset: simulator?.getPreset() ?? null,
58
+ }),
59
+ [demoMode, simulator],
55
60
  );
61
+
62
+ return <DemoModeContext.Provider value={value}>{children}</DemoModeContext.Provider>;
56
63
  }
57
64
 
58
65
  export function useDemoMode(): DemoModeContextValue {
@@ -1,5 +1,5 @@
1
- import { createContext, useContext, useState, useCallback, type ReactNode } from 'react';
2
- import type { AgentSessionAggregate } from '@/agents/types.ts';
1
+ import { createContext, type ReactNode, useCallback, useContext, useState } from "react";
2
+ import type { AgentSessionAggregate } from "@/agents/types.ts";
3
3
 
4
4
  interface DrawerContextValue {
5
5
  selectedSession: AgentSessionAggregate | null;
@@ -22,12 +22,14 @@ export function DrawerProvider({ children }: { children: ReactNode }) {
22
22
  }, []);
23
23
 
24
24
  return (
25
- <DrawerContext.Provider value={{
26
- selectedSession,
27
- showDrawer,
28
- hideDrawer,
29
- isOpen: selectedSession !== null,
30
- }}>
25
+ <DrawerContext.Provider
26
+ value={{
27
+ selectedSession,
28
+ showDrawer,
29
+ hideDrawer,
30
+ isOpen: selectedSession !== null,
31
+ }}
32
+ >
31
33
  {children}
32
34
  </DrawerContext.Provider>
33
35
  );
@@ -36,7 +38,7 @@ export function DrawerProvider({ children }: { children: ReactNode }) {
36
38
  export function useDrawer(): DrawerContextValue {
37
39
  const context = useContext(DrawerContext);
38
40
  if (!context) {
39
- throw new Error('useDrawer must be used within DrawerProvider');
41
+ throw new Error("useDrawer must be used within DrawerProvider");
40
42
  }
41
43
  return context;
42
44
  }
@@ -1,4 +1,4 @@
1
- import { createContext, useContext, useState, useCallback, type ReactNode } from 'react';
1
+ import { createContext, type ReactNode, useCallback, useContext, useState } from "react";
2
2
 
3
3
  interface InputContextType {
4
4
  isInputFocused: boolean;
@@ -24,7 +24,7 @@ export function InputProvider({ children }: { children: ReactNode }) {
24
24
  export function useInputFocus() {
25
25
  const context = useContext(InputContext);
26
26
  if (context === undefined) {
27
- throw new Error('useInputFocus must be used within an InputProvider');
27
+ throw new Error("useInputFocus must be used within an InputProvider");
28
28
  }
29
29
  return context;
30
30
  }
@@ -1,6 +1,6 @@
1
- import { createContext, useContext, useState, useCallback, type ReactNode } from 'react';
1
+ import { createContext, type ReactNode, useCallback, useContext, useState } from "react";
2
2
 
3
- export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
3
+ export type LogLevel = "debug" | "info" | "warn" | "error";
4
4
 
5
5
  export interface LogEntry {
6
6
  id: number;
@@ -40,46 +40,64 @@ export function LogProvider({ children, debugEnabled = true }: LogProviderProps)
40
40
  const [logs, setLogs] = useState<LogEntry[]>([]);
41
41
  const [isConsoleOpen, setIsConsoleOpen] = useState(false);
42
42
 
43
- const addLog = useCallback((level: LogLevel, message: string, data?: Record<string, unknown>, source?: string) => {
44
- if (level === 'debug' && !debugEnabled) return;
45
-
46
- const entry: LogEntry = {
47
- id: ++logIdCounter,
48
- level,
49
- message,
50
- timestamp: Date.now(),
51
- ...(data !== undefined ? { data } : {}),
52
- ...(source !== undefined ? { source } : {}),
53
- };
54
-
55
- setLogs((prev) => {
56
- const next = [...prev, entry];
57
- if (next.length > MAX_LOGS) {
58
- return next.slice(-MAX_LOGS);
59
- }
60
- return next;
61
- });
62
- }, [debugEnabled]);
43
+ const addLog = useCallback(
44
+ (level: LogLevel, message: string, data?: Record<string, unknown>, source?: string) => {
45
+ if (level === "debug" && !debugEnabled) return;
46
+
47
+ const entry: LogEntry = {
48
+ id: ++logIdCounter,
49
+ level,
50
+ message,
51
+ timestamp: Date.now(),
52
+ ...(data !== undefined ? { data } : {}),
53
+ ...(source !== undefined ? { source } : {}),
54
+ };
55
+
56
+ setLogs((prev) => {
57
+ const next = [...prev, entry];
58
+ if (next.length > MAX_LOGS) {
59
+ return next.slice(-MAX_LOGS);
60
+ }
61
+ return next;
62
+ });
63
+ },
64
+ [debugEnabled],
65
+ );
63
66
 
64
- const log = useCallback((level: LogLevel, message: string, data?: Record<string, unknown>, source?: string) => {
65
- addLog(level, message, data, source);
66
- }, [addLog]);
67
+ const log = useCallback(
68
+ (level: LogLevel, message: string, data?: Record<string, unknown>, source?: string) => {
69
+ addLog(level, message, data, source);
70
+ },
71
+ [addLog],
72
+ );
67
73
 
68
- const debug = useCallback((message: string, data?: Record<string, unknown>, source?: string) => {
69
- addLog('debug', message, data, source);
70
- }, [addLog]);
74
+ const debug = useCallback(
75
+ (message: string, data?: Record<string, unknown>, source?: string) => {
76
+ addLog("debug", message, data, source);
77
+ },
78
+ [addLog],
79
+ );
71
80
 
72
- const info = useCallback((message: string, data?: Record<string, unknown>, source?: string) => {
73
- addLog('info', message, data, source);
74
- }, [addLog]);
81
+ const info = useCallback(
82
+ (message: string, data?: Record<string, unknown>, source?: string) => {
83
+ addLog("info", message, data, source);
84
+ },
85
+ [addLog],
86
+ );
75
87
 
76
- const warn = useCallback((message: string, data?: Record<string, unknown>, source?: string) => {
77
- addLog('warn', message, data, source);
78
- }, [addLog]);
88
+ const warn = useCallback(
89
+ (message: string, data?: Record<string, unknown>, source?: string) => {
90
+ addLog("warn", message, data, source);
91
+ },
92
+ [addLog],
93
+ );
79
94
 
80
- const error = useCallback((message: string, data?: Record<string, unknown>, source?: string) => {
81
- addLog('error', message, data, source);
82
- }, [addLog]);
95
+ const error = useCallback(
96
+ (message: string, data?: Record<string, unknown>, source?: string) => {
97
+ addLog("error", message, data, source);
98
+ },
99
+ [addLog],
100
+ );
83
101
 
84
102
  const clearLogs = useCallback(() => {
85
103
  setLogs([]);
@@ -100,11 +118,11 @@ export function LogProvider({ children, debugEnabled = true }: LogProviderProps)
100
118
  const exportLogs = useCallback(() => {
101
119
  const lines = logs.map((entry) => {
102
120
  const time = new Date(entry.timestamp).toISOString();
103
- const src = entry.source ? `[${entry.source}]` : '';
104
- const dataStr = entry.data ? ` ${JSON.stringify(entry.data)}` : '';
121
+ const src = entry.source ? `[${entry.source}]` : "";
122
+ const dataStr = entry.data ? ` ${JSON.stringify(entry.data)}` : "";
105
123
  return `${time} ${entry.level.toUpperCase().padEnd(5)} ${src} ${entry.message}${dataStr}`;
106
124
  });
107
- return lines.join('\n');
125
+ return lines.join("\n");
108
126
  }, [logs]);
109
127
 
110
128
  const value: LogContextValue = {
@@ -122,17 +140,13 @@ export function LogProvider({ children, debugEnabled = true }: LogProviderProps)
122
140
  exportLogs,
123
141
  };
124
142
 
125
- return (
126
- <LogContext.Provider value={value}>
127
- {children}
128
- </LogContext.Provider>
129
- );
143
+ return <LogContext.Provider value={value}>{children}</LogContext.Provider>;
130
144
  }
131
145
 
132
146
  export function useLogs(): LogContextValue {
133
147
  const context = useContext(LogContext);
134
148
  if (!context) {
135
- throw new Error('useLogs must be used within LogProvider');
149
+ throw new Error("useLogs must be used within LogProvider");
136
150
  }
137
151
  return context;
138
152
  }
@@ -141,7 +155,8 @@ export function useLogger(source: string) {
141
155
  const { log, debug, info, warn, error } = useLogs();
142
156
 
143
157
  return {
144
- log: (level: LogLevel, message: string, data?: Record<string, unknown>) => log(level, message, data, source),
158
+ log: (level: LogLevel, message: string, data?: Record<string, unknown>) =>
159
+ log(level, message, data, source),
145
160
  debug: (message: string, data?: Record<string, unknown>) => debug(message, data, source),
146
161
  info: (message: string, data?: Record<string, unknown>) => info(message, data, source),
147
162
  warn: (message: string, data?: Record<string, unknown>) => warn(message, data, source),