llm-deep-trace 0.1.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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +159 -0
  3. package/bin/llm-deep-trace.js +24 -0
  4. package/next.config.ts +8 -0
  5. package/package.json +56 -0
  6. package/postcss.config.mjs +5 -0
  7. package/public/banner-v2.png +0 -0
  8. package/public/file.svg +1 -0
  9. package/public/globe.svg +1 -0
  10. package/public/logo.png +0 -0
  11. package/public/next.svg +1 -0
  12. package/public/vercel.svg +1 -0
  13. package/public/window.svg +1 -0
  14. package/src/app/api/agent-config/route.ts +31 -0
  15. package/src/app/api/all-sessions/route.ts +9 -0
  16. package/src/app/api/analytics/route.ts +379 -0
  17. package/src/app/api/detect-agents/route.ts +170 -0
  18. package/src/app/api/image/route.ts +73 -0
  19. package/src/app/api/search/route.ts +28 -0
  20. package/src/app/api/session-by-key/route.ts +21 -0
  21. package/src/app/api/sessions/[sessionId]/messages/route.ts +46 -0
  22. package/src/app/api/sse/route.ts +86 -0
  23. package/src/app/favicon.ico +0 -0
  24. package/src/app/globals.css +3518 -0
  25. package/src/app/icon.svg +4 -0
  26. package/src/app/layout.tsx +20 -0
  27. package/src/app/page.tsx +5 -0
  28. package/src/components/AnalyticsDashboard.tsx +393 -0
  29. package/src/components/App.tsx +243 -0
  30. package/src/components/CopyButton.tsx +42 -0
  31. package/src/components/Logo.tsx +20 -0
  32. package/src/components/MainPanel.tsx +1128 -0
  33. package/src/components/MessageRenderer.tsx +983 -0
  34. package/src/components/SessionTree.tsx +505 -0
  35. package/src/components/SettingsPanel.tsx +160 -0
  36. package/src/components/SetupView.tsx +206 -0
  37. package/src/components/Sidebar.tsx +714 -0
  38. package/src/components/ThemeToggle.tsx +54 -0
  39. package/src/lib/client-utils.ts +360 -0
  40. package/src/lib/normalizers.ts +371 -0
  41. package/src/lib/sessions.ts +1223 -0
  42. package/src/lib/store.ts +518 -0
  43. package/src/lib/types.ts +112 -0
  44. package/src/lib/useSSE.ts +81 -0
  45. package/tsconfig.json +34 -0
@@ -0,0 +1,206 @@
1
+ "use client";
2
+
3
+ import { useState, useEffect } from "react";
4
+
5
+ interface AgentResult {
6
+ id: string;
7
+ name: string;
8
+ color: string;
9
+ binary: { found: boolean; path: string | null; isCustom: boolean };
10
+ sessions: { found: boolean; dir: string; defaultDir: string; count: number; dirExists: boolean; isCustom: boolean };
11
+ }
12
+
13
+ interface AgentOverride {
14
+ binaryPath?: string;
15
+ sessionsDir?: string;
16
+ }
17
+
18
+ interface SetupViewProps {
19
+ onDone: () => void;
20
+ }
21
+
22
+ export default function SetupView({ onDone }: SetupViewProps) {
23
+ const [agents, setAgents] = useState<AgentResult[]>([]);
24
+ const [scanning, setScanning] = useState(true);
25
+ const [overrides, setOverrides] = useState<Record<string, AgentOverride>>({});
26
+ const [editingBinary, setEditingBinary] = useState<Record<string, string>>({});
27
+ const [editingSessions, setEditingSessions] = useState<Record<string, string>>({});
28
+ const [saving, setSaving] = useState(false);
29
+
30
+ useEffect(() => {
31
+ fetch("/api/detect-agents")
32
+ .then((r) => r.json())
33
+ .then((data) => {
34
+ setAgents(data.agents || []);
35
+ // Pre-populate override inputs with current custom values
36
+ const binaryEdits: Record<string, string> = {};
37
+ const sessionsEdits: Record<string, string> = {};
38
+ for (const a of data.agents || []) {
39
+ binaryEdits[a.id] = a.binary.path || "";
40
+ sessionsEdits[a.id] = a.sessions.dir;
41
+ }
42
+ setEditingBinary(binaryEdits);
43
+ setEditingSessions(sessionsEdits);
44
+ setScanning(false);
45
+ })
46
+ .catch(() => setScanning(false));
47
+ }, []);
48
+
49
+ const setCustomBinary = (id: string, val: string) => {
50
+ setOverrides((prev) => ({ ...prev, [id]: { ...prev[id], binaryPath: val || undefined } }));
51
+ setEditingBinary((prev) => ({ ...prev, [id]: val }));
52
+ };
53
+
54
+ const setCustomSessions = (id: string, val: string) => {
55
+ setOverrides((prev) => ({ ...prev, [id]: { ...prev[id], sessionsDir: val || undefined } }));
56
+ setEditingSessions((prev) => ({ ...prev, [id]: val }));
57
+ };
58
+
59
+ const clearCustomBinary = (id: string) => {
60
+ setOverrides((prev) => {
61
+ const next = { ...prev };
62
+ if (next[id]) delete next[id].binaryPath;
63
+ return next;
64
+ });
65
+ const agent = agents.find((a) => a.id === id);
66
+ setEditingBinary((prev) => ({ ...prev, [id]: agent?.binary.path || "" }));
67
+ };
68
+
69
+ const clearCustomSessions = (id: string) => {
70
+ setOverrides((prev) => {
71
+ const next = { ...prev };
72
+ if (next[id]) delete next[id].sessionsDir;
73
+ return next;
74
+ });
75
+ const agent = agents.find((a) => a.id === id);
76
+ setEditingSessions((prev) => ({ ...prev, [id]: agent?.sessions.defaultDir || "" }));
77
+ };
78
+
79
+ const handleStart = async () => {
80
+ setSaving(true);
81
+ try {
82
+ if (Object.keys(overrides).length > 0) {
83
+ await fetch("/api/agent-config", {
84
+ method: "POST",
85
+ headers: { "Content-Type": "application/json" },
86
+ body: JSON.stringify(overrides),
87
+ });
88
+ }
89
+ localStorage.setItem("llm-deep-trace-setup-done", "1");
90
+ onDone();
91
+ } catch {
92
+ onDone(); // proceed anyway
93
+ }
94
+ };
95
+
96
+ const foundCount = agents.filter((a) => a.sessions.count > 0).length;
97
+ const totalSessions = agents.reduce((s, a) => s + a.sessions.count, 0);
98
+
99
+ return (
100
+ <div className="setup-view">
101
+ <div className="setup-header">
102
+ {/* Gene-sequence logo inline */}
103
+ <svg width="120" height="120" viewBox="0 0 1024 1024" fill="none" className="setup-logo">
104
+ <path d="M896 384c0 35.2-28.8 64-64 64s-64-28.8-64-64 28.8-64 64-64 64 28.8 64 64z M240 352c-62.4 0-112-49.6-112-112s49.6-112 112-112 112 49.6 112 112-49.6 112-112 112z M544 688c-80 0-144-64-144-144s64-144 144-144 144 64 144 144-64 144-144 144z M320 832c0-35.2 28.8-64 64-64s64 28.8 64 64-28.8 64-64 64-64-28.8-64-64z M176 656c0-17.6 14.4-32 32-32s32 14.4 32 32-14.4 32-32 32-32-14.4-32-32z M624 216c0-22.4 17.6-40 40-40s40 17.6 40 40-17.6 40-40 40-40-17.6-40-40z M736 560c0-27.2 20.8-48 48-48s48 20.8 48 48-20.8 48-48 48-48-20.8-48-48z" fill="#9B72EF"/>
105
+ </svg>
106
+ <h1 className="setup-title">llm-deep-trace</h1>
107
+ <p className="setup-subtitle">
108
+ {scanning
109
+ ? "scanning for agent sessions\u2026"
110
+ : foundCount > 0
111
+ ? `found ${totalSessions.toLocaleString()} sessions across ${foundCount} agent${foundCount !== 1 ? "s" : ""}`
112
+ : "no sessions found — configure paths below"}
113
+ </p>
114
+ {scanning && <div className="setup-scan-bar"><div className="setup-scan-fill" /></div>}
115
+ </div>
116
+
117
+ {!scanning && (
118
+ <>
119
+ <div className="setup-agent-list">
120
+ {agents.map((agent) => {
121
+ const hasCustomBinary = !!(overrides[agent.id]?.binaryPath);
122
+ const hasCustomSessions = !!(overrides[agent.id]?.sessionsDir);
123
+ const binaryVal = editingBinary[agent.id] ?? "";
124
+ const sessionsVal = editingSessions[agent.id] ?? "";
125
+
126
+ return (
127
+ <div key={agent.id} className={`setup-agent-row ${agent.sessions.count > 0 ? "has-sessions" : ""}`}>
128
+ <div className="setup-agent-dot" style={{ background: agent.color }} />
129
+ <div className="setup-agent-info">
130
+ <span className="setup-agent-name">{agent.name}</span>
131
+ <span className={`setup-agent-count ${agent.sessions.count > 0 ? "found" : "empty"}`}>
132
+ {agent.sessions.count > 0
133
+ ? `${agent.sessions.count} session${agent.sessions.count !== 1 ? "s" : ""}`
134
+ : agent.sessions.dirExists ? "0 sessions" : "directory not found"}
135
+ </span>
136
+ </div>
137
+
138
+ <div className="setup-agent-paths">
139
+ {/* Binary */}
140
+ <div className="setup-path-row">
141
+ <span className="setup-path-label">binary</span>
142
+ {hasCustomBinary ? (
143
+ <div className="setup-path-input-wrap">
144
+ <input
145
+ className="setup-path-input"
146
+ value={binaryVal}
147
+ onChange={(e) => setCustomBinary(agent.id, e.target.value)}
148
+ placeholder="path to binary"
149
+ />
150
+ <button className="setup-path-clear" onClick={() => clearCustomBinary(agent.id)} title="Reset to auto">×</button>
151
+ </div>
152
+ ) : (
153
+ <div className="setup-path-auto">
154
+ {agent.binary.found ? (
155
+ <span className="setup-path-found" title={agent.binary.path || ""}>{agent.binary.path}</span>
156
+ ) : (
157
+ <span className="setup-path-missing">not found</span>
158
+ )}
159
+ <button className="setup-path-custom-btn" onClick={() => setCustomBinary(agent.id, binaryVal)}>custom</button>
160
+ </div>
161
+ )}
162
+ </div>
163
+
164
+ {/* Sessions dir */}
165
+ <div className="setup-path-row">
166
+ <span className="setup-path-label">sessions</span>
167
+ {hasCustomSessions ? (
168
+ <div className="setup-path-input-wrap">
169
+ <input
170
+ className="setup-path-input"
171
+ value={sessionsVal}
172
+ onChange={(e) => setCustomSessions(agent.id, e.target.value)}
173
+ placeholder="path to sessions directory"
174
+ />
175
+ <button className="setup-path-clear" onClick={() => clearCustomSessions(agent.id)} title="Reset to auto">×</button>
176
+ </div>
177
+ ) : (
178
+ <div className="setup-path-auto">
179
+ <span className={agent.sessions.dirExists ? "setup-path-found" : "setup-path-missing"}
180
+ title={sessionsVal}>{sessionsVal}</span>
181
+ <button className="setup-path-custom-btn" onClick={() => setCustomSessions(agent.id, sessionsVal)}>custom</button>
182
+ </div>
183
+ )}
184
+ </div>
185
+ </div>
186
+ </div>
187
+ );
188
+ })}
189
+ </div>
190
+
191
+ <div className="setup-footer">
192
+ <button className="setup-start-btn" onClick={handleStart} disabled={saving}>
193
+ {saving ? "saving\u2026" : "start browsing"}
194
+ {!saving && (
195
+ <svg width="14" height="14" viewBox="0 0 16 16" fill="none" style={{ marginLeft: 6 }}>
196
+ <path d="M3 8h10M9 4l4 4-4 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
197
+ </svg>
198
+ )}
199
+ </button>
200
+ <button className="setup-skip-btn" onClick={onDone}>skip</button>
201
+ </div>
202
+ </>
203
+ )}
204
+ </div>
205
+ );
206
+ }