claude-session-dashboard 0.4.4 → 0.5.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 (48) hide show
  1. package/dist/client/assets/_dashboard-ChSI7O-o.js +1 -0
  2. package/dist/client/assets/_sessionId-DxfkocLt.js +12 -0
  3. package/dist/client/assets/app-BAZLXX_G.css +1 -0
  4. package/dist/client/assets/createServerFn-DJjqV8Yc.js +1 -0
  5. package/dist/client/assets/index-BHwOAB5a.js +1 -0
  6. package/dist/client/assets/main-DMwMzWYr.js +69 -0
  7. package/dist/client/assets/sessions.queries-D7WObk5d.js +1 -0
  8. package/dist/client/assets/settings-BM0TBEkW.js +1 -0
  9. package/dist/client/assets/settings.types-BNSfIfFX.js +1 -0
  10. package/dist/client/assets/stats-wLUGXFpZ.js +4 -0
  11. package/dist/client/assets/useSessionCost-BPqnyONZ.js +37 -0
  12. package/dist/server/assets/{_dashboard-smfIqyQC.js → _dashboard-DZJZ9oYy.js} +13 -12
  13. package/dist/server/assets/{_sessionId-DIUMcrWR.js → _sessionId-znGcd1Dj.js} +53 -24
  14. package/dist/server/assets/_tanstack-start-manifest_v-BNorjuP4.js +4 -0
  15. package/dist/server/assets/{app-info.server-CXcS0a5s.js → app-info.api-CrHplh0q.js} +33 -18
  16. package/dist/server/assets/{createServerRpc-Bd3B-Ah9.js → createServerRpc-D_-6bKnO.js} +1 -1
  17. package/dist/server/assets/{createSsrRpc-CVg2UDl0.js → createSsrRpc-OFLSk08e.js} +2 -3
  18. package/dist/server/assets/{index-hFrIPkke.js → index-BnvjnlZM.js} +31 -14
  19. package/dist/server/assets/{project-analytics.server-Bxk8-NnT.js → project-analytics.api-6b5TIBNn.js} +12 -10
  20. package/dist/server/assets/{router-5hznwWqr.js → router-DAepjaOj.js} +80 -12
  21. package/dist/server/assets/{session-detail.server-BIoOQwSE.js → session-detail.api-BQts3iQn.js} +38 -33
  22. package/dist/server/assets/{session-parser-B0pdBvgT.js → session-parser-DKZZMuh6.js} +123 -31
  23. package/dist/server/assets/{session-scanner-CpgOq5m1.js → session-scanner-CECpfGFh.js} +4 -4
  24. package/dist/server/assets/{sessions.server-Biq8gbAJ.js → sessions.api-CQ-Lrk5S.js} +16 -14
  25. package/dist/server/assets/{sessions.queries-B5ZBiVJy.js → sessions.queries-MfPgj6cK.js} +4 -4
  26. package/dist/server/assets/{settings-D0FgLIR5.js → settings-DsLaw8yg.js} +42 -13
  27. package/dist/server/assets/{settings.server-6B2PvLgf.js → settings.api-Cq5KPUxN.js} +12 -11
  28. package/dist/server/assets/{settings.queries-DSQd324O.js → settings.queries-CMWxUDF-.js} +4 -4
  29. package/dist/server/assets/{settings.types-DntadCHo.js → settings.types-CphWe-HW.js} +10 -1
  30. package/dist/server/assets/{stats-Ae6umrPI.js → stats-DKbhSePW.js} +57 -43
  31. package/dist/server/assets/{stats.server-DhzOihwM.js → stats.api-tIZqhk4B.js} +12 -10
  32. package/dist/server/assets/{useSessionCost-CYs5UOX-.js → useSessionCost-iQEg4FRM.js} +2 -2
  33. package/dist/server/server.js +458 -808
  34. package/package.json +6 -2
  35. package/LICENSE +0 -21
  36. package/README.md +0 -194
  37. package/dist/client/assets/_dashboard-DLFGahko.js +0 -1
  38. package/dist/client/assets/_sessionId-xiPzwrlf.js +0 -12
  39. package/dist/client/assets/app-DREGBD44.css +0 -1
  40. package/dist/client/assets/createServerFn-BLR4iNR3.js +0 -1
  41. package/dist/client/assets/index-D_9sS4oJ.js +0 -1
  42. package/dist/client/assets/main-BcKPK-4E.js +0 -56
  43. package/dist/client/assets/sessions.queries-CHKiZnLm.js +0 -1
  44. package/dist/client/assets/settings-B2tG1vy0.js +0 -1
  45. package/dist/client/assets/settings.types-DHC6rkil.js +0 -1
  46. package/dist/client/assets/stats-BlA0NIHc.js +0 -4
  47. package/dist/client/assets/useSessionCost-BikgEmWy.js +0 -37
  48. package/dist/server/assets/_tanstack-start-manifest_v-Dmhlhehg.js +0 -4
@@ -1,7 +1,7 @@
1
1
  import { createRootRoute, Outlet, HeadContent, Scripts, createFileRoute, lazyRouteComponent, redirect, createRouter } from "@tanstack/react-router";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
4
- import { useState, useRef, useCallback, createContext, useContext } from "react";
4
+ import { useState, useRef, useCallback, createContext, useContext, useEffect, useMemo } from "react";
5
5
  import { z } from "zod";
6
6
  const OS_USERNAME_PATTERN = /^(\/(?:Users|home))\/[^/]+/;
7
7
  function anonymizePath(path, anonymizedProjectName) {
@@ -96,7 +96,57 @@ function usePrivacy() {
96
96
  }
97
97
  return ctx;
98
98
  }
99
- const appCss = "/assets/app-DREGBD44.css";
99
+ const THEME_STORAGE_KEY = "csd-theme";
100
+ const ThemeContext = createContext(null);
101
+ function getInitialTheme() {
102
+ if (typeof window === "undefined") return "dark";
103
+ const stored = window.localStorage.getItem(THEME_STORAGE_KEY);
104
+ if (stored === "light" || stored === "dark") {
105
+ return stored;
106
+ }
107
+ const prefersDark = window.matchMedia?.("(prefers-color-scheme: dark)").matches;
108
+ return prefersDark ? "dark" : "light";
109
+ }
110
+ function ThemeProvider({ children }) {
111
+ const [theme, setTheme] = useState(getInitialTheme);
112
+ useEffect(() => {
113
+ const root = document.documentElement;
114
+ root.setAttribute("data-theme", theme);
115
+ window.localStorage.setItem(THEME_STORAGE_KEY, theme);
116
+ const metaTheme = document.querySelector('meta[name="theme-color"]');
117
+ if (metaTheme) {
118
+ metaTheme.setAttribute("content", theme === "dark" ? "#141413" : "#f5f3ec");
119
+ }
120
+ }, [theme]);
121
+ useEffect(() => {
122
+ const media = window.matchMedia("(prefers-color-scheme: dark)");
123
+ const listener = (event) => {
124
+ const stored = window.localStorage.getItem(THEME_STORAGE_KEY);
125
+ if (stored !== "light" && stored !== "dark") {
126
+ setTheme(event.matches ? "dark" : "light");
127
+ }
128
+ };
129
+ media.addEventListener("change", listener);
130
+ return () => media.removeEventListener("change", listener);
131
+ }, []);
132
+ const value = useMemo(
133
+ () => ({
134
+ theme,
135
+ toggleTheme: () => setTheme((prev) => prev === "dark" ? "light" : "dark"),
136
+ setTheme
137
+ }),
138
+ [theme]
139
+ );
140
+ return /* @__PURE__ */ jsx(ThemeContext.Provider, { value, children });
141
+ }
142
+ function useTheme() {
143
+ const ctx = useContext(ThemeContext);
144
+ if (!ctx) {
145
+ throw new Error("useTheme must be used within a ThemeProvider");
146
+ }
147
+ return ctx;
148
+ }
149
+ const appCss = "/assets/app-BAZLXX_G.css";
100
150
  const queryClient = new QueryClient({
101
151
  defaultOptions: {
102
152
  queries: {
@@ -125,18 +175,35 @@ const Route$6 = createRootRoute({
125
175
  component: RootComponent
126
176
  });
127
177
  function RootComponent() {
128
- return /* @__PURE__ */ jsx(RootDocument, { children: /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx(PrivacyProvider, { children: /* @__PURE__ */ jsx(Outlet, {}) }) }) });
178
+ return /* @__PURE__ */ jsx(RootDocument, { children: /* @__PURE__ */ jsx(ThemeProvider, { children: /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx(PrivacyProvider, { children: /* @__PURE__ */ jsx(Outlet, {}) }) }) }) });
129
179
  }
180
+ const themeInitScript = `
181
+ (() => {
182
+ try {
183
+ const stored = localStorage.getItem('csd-theme');
184
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
185
+ const theme = stored === 'light' || stored === 'dark' ? stored : (prefersDark ? 'dark' : 'light');
186
+ document.documentElement.setAttribute('data-theme', theme);
187
+ const meta = document.querySelector('meta[name="theme-color"]');
188
+ if (meta) meta.setAttribute('content', theme === 'dark' ? '#141413' : '#f5f3ec');
189
+ } catch (e) {
190
+ document.documentElement.setAttribute('data-theme', 'dark');
191
+ }
192
+ })();
193
+ `;
130
194
  function RootDocument({ children }) {
131
195
  return /* @__PURE__ */ jsxs("html", { lang: "en", children: [
132
- /* @__PURE__ */ jsx("head", { children: /* @__PURE__ */ jsx(HeadContent, {}) }),
133
- /* @__PURE__ */ jsxs("body", { className: "bg-gray-950 text-gray-100 antialiased", children: [
196
+ /* @__PURE__ */ jsxs("head", { children: [
197
+ /* @__PURE__ */ jsx(HeadContent, {}),
198
+ /* @__PURE__ */ jsx("script", { dangerouslySetInnerHTML: { __html: themeInitScript } })
199
+ ] }),
200
+ /* @__PURE__ */ jsxs("body", { className: "antialiased", children: [
134
201
  children,
135
202
  /* @__PURE__ */ jsx(Scripts, {})
136
203
  ] })
137
204
  ] });
138
205
  }
139
- const $$splitComponentImporter$4 = () => import("./_dashboard-smfIqyQC.js");
206
+ const $$splitComponentImporter$4 = () => import("./_dashboard-DZJZ9oYy.js");
140
207
  const Route$5 = createFileRoute("/_dashboard")({
141
208
  component: lazyRouteComponent($$splitComponentImporter$4, "component")
142
209
  });
@@ -145,7 +212,7 @@ const Route$4 = createFileRoute("/")({
145
212
  throw redirect({ to: "/sessions" });
146
213
  }
147
214
  });
148
- const $$splitComponentImporter$3 = () => import("./stats-Ae6umrPI.js");
215
+ const $$splitComponentImporter$3 = () => import("./stats-DKbhSePW.js");
149
216
  const statsSearchSchema = z.object({
150
217
  tab: z.enum(["overview", "projects"]).default("overview").catch("overview")
151
218
  });
@@ -153,11 +220,11 @@ const Route$3 = createFileRoute("/_dashboard/stats")({
153
220
  validateSearch: statsSearchSchema,
154
221
  component: lazyRouteComponent($$splitComponentImporter$3, "component")
155
222
  });
156
- const $$splitComponentImporter$2 = () => import("./settings-D0FgLIR5.js");
223
+ const $$splitComponentImporter$2 = () => import("./settings-DsLaw8yg.js");
157
224
  const Route$2 = createFileRoute("/_dashboard/settings")({
158
225
  component: lazyRouteComponent($$splitComponentImporter$2, "component")
159
226
  });
160
- const $$splitComponentImporter$1 = () => import("./index-hFrIPkke.js");
227
+ const $$splitComponentImporter$1 = () => import("./index-BnvjnlZM.js");
161
228
  const sessionsSearchSchema = z.object({
162
229
  page: z.number().int().min(1).default(1).catch(1),
163
230
  pageSize: z.number().int().min(5).max(100).default(5).catch(5),
@@ -169,7 +236,7 @@ const Route$1 = createFileRoute("/_dashboard/sessions/")({
169
236
  validateSearch: sessionsSearchSchema,
170
237
  component: lazyRouteComponent($$splitComponentImporter$1, "component")
171
238
  });
172
- const $$splitComponentImporter = () => import("./_sessionId-DIUMcrWR.js");
239
+ const $$splitComponentImporter = () => import("./_sessionId-znGcd1Dj.js");
173
240
  const searchSchema = z.object({
174
241
  project: z.string().optional()
175
242
  });
@@ -233,8 +300,9 @@ const router = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
233
300
  }, Symbol.toStringTag, { value: "Module" }));
234
301
  export {
235
302
  Route$3 as R,
236
- Route$1 as a,
237
- Route as b,
303
+ useTheme as a,
304
+ Route$1 as b,
305
+ Route as c,
238
306
  router as r,
239
307
  usePrivacy as u
240
308
  };
@@ -1,40 +1,34 @@
1
- import { c as createServerRpc } from "./createServerRpc-Bd3B-Ah9.js";
1
+ import { c as createServerRpc } from "./createServerRpc-D_-6bKnO.js";
2
+ import { a as getDataSources, b as getProjectsDirFor, d as decodeProjectDirName, e as extractProjectName, p as parseDetail } from "./session-parser-DKZZMuh6.js";
2
3
  import * as path from "node:path";
3
4
  import * as fs from "node:fs";
4
- import { e as extractProjectName, p as parseDetail, g as getProjectsDir, d as decodeProjectDirName } from "./session-parser-B0pdBvgT.js";
5
5
  import { c as createServerFn } from "../server.js";
6
6
  import "node:os";
7
+ import "node:fs/promises";
7
8
  import "node:readline";
8
- import "@tanstack/history";
9
- import "@tanstack/router-core/ssr/client";
10
- import "@tanstack/router-core";
11
9
  import "node:async_hooks";
12
- import "@tanstack/router-core/ssr/server";
13
10
  import "h3-v2";
11
+ import "@tanstack/router-core";
14
12
  import "tiny-invariant";
15
13
  import "seroval";
14
+ import "@tanstack/history";
15
+ import "@tanstack/router-core/ssr/client";
16
+ import "@tanstack/router-core/ssr/server";
17
+ import "react";
18
+ import "@tanstack/react-router";
16
19
  import "react/jsx-runtime";
17
20
  import "@tanstack/react-router/ssr/server";
18
- import "@tanstack/react-router";
19
- const getSessionDetail_createServerFn_handler = createServerRpc({
20
- id: "ff8a3161afdfa175e9c519e4146a56ab5bce6e80745e99cfc2191ebbb7a859bb",
21
- name: "getSessionDetail",
22
- filename: "src/features/session-detail/session-detail.server.ts"
23
- }, (opts) => getSessionDetail.__executeServer(opts));
24
- const getSessionDetail = createServerFn({
25
- method: "GET"
26
- }).inputValidator((input) => input).handler(getSessionDetail_createServerFn_handler, async ({
27
- data
28
- }) => {
29
- const filePath = findSessionFile(data.sessionId, data.projectPath);
30
- if (!filePath) {
31
- throw new Error(`Session not found: ${data.sessionId}`);
21
+ async function findSessionFile(sessionId, projectPath) {
22
+ const sources = await getDataSources();
23
+ for (const source of sources) {
24
+ if (!source.available) continue;
25
+ const result = findInSource(sessionId, projectPath, source);
26
+ if (result) return result;
32
27
  }
33
- const projectName = extractProjectName(data.projectPath);
34
- return parseDetail(filePath.path, data.sessionId, data.projectPath, projectName);
35
- });
36
- function findSessionFile(sessionId, projectPath) {
37
- const projectsDir = getProjectsDir();
28
+ return null;
29
+ }
30
+ function findInSource(sessionId, projectPath, source) {
31
+ const projectsDir = getProjectsDirFor(source);
38
32
  let entries;
39
33
  try {
40
34
  entries = fs.readdirSync(projectsDir);
@@ -46,24 +40,35 @@ function findSessionFile(sessionId, projectPath) {
46
40
  if (decoded === projectPath || dirName === projectPath) {
47
41
  const filePath = path.join(projectsDir, dirName, `${sessionId}.jsonl`);
48
42
  if (fs.existsSync(filePath)) {
49
- return {
50
- path: filePath,
51
- dirName
52
- };
43
+ return { path: filePath, dirName };
53
44
  }
54
45
  }
55
46
  }
56
47
  for (const dirName of entries) {
57
48
  const filePath = path.join(projectsDir, dirName, `${sessionId}.jsonl`);
58
49
  if (fs.existsSync(filePath)) {
59
- return {
60
- path: filePath,
61
- dirName
62
- };
50
+ return { path: filePath, dirName };
63
51
  }
64
52
  }
65
53
  return null;
66
54
  }
55
+ const getSessionDetail_createServerFn_handler = createServerRpc({
56
+ id: "71794080473579a94431392ab409ebd02772f6a9f6a08386cadbb8c0d3cf804a",
57
+ name: "getSessionDetail",
58
+ filename: "src/features/session-detail/session-detail.api.ts"
59
+ }, (opts) => getSessionDetail.__executeServer(opts));
60
+ const getSessionDetail = createServerFn({
61
+ method: "GET"
62
+ }).inputValidator((input) => input).handler(getSessionDetail_createServerFn_handler, async ({
63
+ data
64
+ }) => {
65
+ const filePath = await findSessionFile(data.sessionId, data.projectPath);
66
+ if (!filePath) {
67
+ throw new Error(`Session not found: ${data.sessionId}`);
68
+ }
69
+ const projectName = extractProjectName(data.projectPath);
70
+ return parseDetail(filePath.path, data.sessionId, data.projectPath, projectName);
71
+ });
67
72
  export {
68
73
  getSessionDetail_createServerFn_handler
69
74
  };
@@ -1,6 +1,7 @@
1
1
  import * as path from "node:path";
2
2
  import * as os from "node:os";
3
- import * as fs from "node:fs";
3
+ import * as fs from "node:fs/promises";
4
+ import * as fs$1 from "node:fs";
4
5
  import * as readline from "node:readline";
5
6
  function resolveClaudeDir() {
6
7
  if (process.env.CLAUDE_HOME) {
@@ -9,14 +10,25 @@ function resolveClaudeDir() {
9
10
  return path.join(os.homedir(), ".claude");
10
11
  }
11
12
  const CLAUDE_DIR = resolveClaudeDir();
13
+ function getClaudeDir() {
14
+ return CLAUDE_DIR;
15
+ }
12
16
  function getProjectsDir() {
13
17
  return path.join(CLAUDE_DIR, "projects");
14
18
  }
15
19
  function getStatsPath() {
16
20
  return path.join(CLAUDE_DIR, "stats-cache.json");
17
21
  }
22
+ function getProjectsDirFor(source) {
23
+ return path.join(source.claudeDir, "projects");
24
+ }
18
25
  function decodeProjectDirName(dirName) {
19
- return dirName.replace(/^-/, "/").replace(/-/g, "/");
26
+ const decoded = dirName.replace(/^-/, "/").replace(/-/g, "/");
27
+ const windowsDrive = decoded.match(/^\/([A-Z])\/(.*)$/);
28
+ if (windowsDrive) {
29
+ return `${windowsDrive[1]}:/${windowsDrive[2]}`;
30
+ }
31
+ return decoded;
20
32
  }
21
33
  function extractProjectName(decodedPath) {
22
34
  return path.basename(decodedPath);
@@ -24,6 +36,75 @@ function extractProjectName(decodedPath) {
24
36
  function extractSessionId(filename) {
25
37
  return filename.replace(/\.jsonl$/, "");
26
38
  }
39
+ async function detectCurrentPlatform() {
40
+ if (process.platform === "win32") return "windows";
41
+ if (process.platform === "darwin") return "macos";
42
+ try {
43
+ const procVersion = await fs.readFile("/proc/version", "utf8");
44
+ if (procVersion.toLowerCase().includes("microsoft") || procVersion.toLowerCase().includes("wsl")) {
45
+ return "wsl";
46
+ }
47
+ } catch {
48
+ }
49
+ return "linux";
50
+ }
51
+ async function detectWslDistros() {
52
+ if (process.platform !== "win32") return [];
53
+ const wslRoot = "\\\\wsl$";
54
+ let distros;
55
+ try {
56
+ distros = await fs.readdir(wslRoot);
57
+ } catch {
58
+ return [];
59
+ }
60
+ const sources = [];
61
+ for (const distro of distros) {
62
+ const homeDir = `${wslRoot}\\${distro}\\home`;
63
+ let users;
64
+ try {
65
+ users = await fs.readdir(homeDir);
66
+ } catch {
67
+ continue;
68
+ }
69
+ for (const user of users) {
70
+ const claudeDir = `${wslRoot}\\${distro}\\home\\${user}\\.claude`;
71
+ try {
72
+ await fs.access(claudeDir);
73
+ sources.push({
74
+ id: `wsl-${distro.toLowerCase()}-${user}`,
75
+ label: `WSL - ${distro}`,
76
+ claudeDir,
77
+ platform: "wsl",
78
+ available: true
79
+ });
80
+ } catch {
81
+ }
82
+ }
83
+ }
84
+ return sources;
85
+ }
86
+ async function getDataSources() {
87
+ const claudeDir = getClaudeDir();
88
+ const platform = await detectCurrentPlatform();
89
+ let available = false;
90
+ try {
91
+ await fs.access(claudeDir);
92
+ available = true;
93
+ } catch {
94
+ available = false;
95
+ }
96
+ const primarySource = {
97
+ id: "primary",
98
+ label: platform === "windows" ? "Windows" : platform === "macos" ? "macOS" : platform === "wsl" ? "WSL" : "Linux",
99
+ claudeDir,
100
+ platform,
101
+ available
102
+ };
103
+ const sources = [primarySource];
104
+ const wslSources = await detectWslDistros();
105
+ sources.push(...wslSources);
106
+ return sources;
107
+ }
27
108
  const AGENT_FILE_PATTERN = /^agent-(.+)\.jsonl$/;
28
109
  async function discoverSubagentFiles(sessionDir) {
29
110
  const result = /* @__PURE__ */ new Map();
@@ -33,7 +114,7 @@ async function discoverSubagentFiles(sessionDir) {
33
114
  ];
34
115
  for (const dir of candidateDirs) {
35
116
  try {
36
- const entries = await fs.promises.readdir(dir);
117
+ const entries = await fs$1.promises.readdir(dir);
37
118
  for (const entry of entries) {
38
119
  const match = AGENT_FILE_PATTERN.exec(entry);
39
120
  if (match) {
@@ -131,7 +212,8 @@ async function parseDetail(filePath, sessionId, projectPath, projectName) {
131
212
  const taskById = /* @__PURE__ */ new Map();
132
213
  const contextSnapshots = [];
133
214
  let assistantTurnIndex = 0;
134
- const stream = fs.createReadStream(filePath, { encoding: "utf-8" });
215
+ const seenRequestIds = /* @__PURE__ */ new Set();
216
+ const stream = fs$1.createReadStream(filePath, { encoding: "utf-8" });
135
217
  const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
136
218
  for await (const line of rl) {
137
219
  const msg = safeParse(line);
@@ -147,8 +229,11 @@ async function parseDetail(filePath, sessionId, projectPath, projectName) {
147
229
  if (progressModel && parentId) {
148
230
  agentProgressModel.set(parentId, progressModel);
149
231
  }
232
+ const progressRequestId = msg.requestId;
233
+ const isNewProgressRequest = !progressRequestId || !seenRequestIds.has(progressRequestId);
234
+ if (progressRequestId) seenRequestIds.add(progressRequestId);
150
235
  const usage = msg.data?.message?.message?.usage;
151
- if (usage) {
236
+ if (usage && isNewProgressRequest) {
152
237
  const existing = agentProgressTokens.get(parentId) ?? {
153
238
  inputTokens: 0,
154
239
  outputTokens: 0,
@@ -266,23 +351,28 @@ async function parseDetail(filePath, sessionId, projectPath, projectName) {
266
351
  cacheReadInputTokens: u.cache_read_input_tokens ?? 0,
267
352
  cacheCreationInputTokens: u.cache_creation_input_tokens ?? 0
268
353
  };
269
- totalTokens.inputTokens += tokens.inputTokens;
270
- totalTokens.outputTokens += tokens.outputTokens;
271
- totalTokens.cacheReadInputTokens += tokens.cacheReadInputTokens;
272
- totalTokens.cacheCreationInputTokens += tokens.cacheCreationInputTokens;
273
- if (msg.message.model) {
274
- const modelId = msg.message.model;
275
- const existing = tokensByModel[modelId] ?? {
276
- inputTokens: 0,
277
- outputTokens: 0,
278
- cacheReadInputTokens: 0,
279
- cacheCreationInputTokens: 0
280
- };
281
- existing.inputTokens += tokens.inputTokens;
282
- existing.outputTokens += tokens.outputTokens;
283
- existing.cacheReadInputTokens += tokens.cacheReadInputTokens;
284
- existing.cacheCreationInputTokens += tokens.cacheCreationInputTokens;
285
- tokensByModel[modelId] = existing;
354
+ const requestId = msg.requestId;
355
+ const isNewRequest = !requestId || !seenRequestIds.has(requestId);
356
+ if (requestId) seenRequestIds.add(requestId);
357
+ if (isNewRequest) {
358
+ totalTokens.inputTokens += tokens.inputTokens;
359
+ totalTokens.outputTokens += tokens.outputTokens;
360
+ totalTokens.cacheReadInputTokens += tokens.cacheReadInputTokens;
361
+ totalTokens.cacheCreationInputTokens += tokens.cacheCreationInputTokens;
362
+ if (msg.message.model) {
363
+ const modelId = msg.message.model;
364
+ const existing = tokensByModel[modelId] ?? {
365
+ inputTokens: 0,
366
+ outputTokens: 0,
367
+ cacheReadInputTokens: 0,
368
+ cacheCreationInputTokens: 0
369
+ };
370
+ existing.inputTokens += tokens.inputTokens;
371
+ existing.outputTokens += tokens.outputTokens;
372
+ existing.cacheReadInputTokens += tokens.cacheReadInputTokens;
373
+ existing.cacheCreationInputTokens += tokens.cacheCreationInputTokens;
374
+ tokensByModel[modelId] = existing;
375
+ }
286
376
  }
287
377
  const contextSize = tokens.inputTokens + tokens.cacheReadInputTokens + tokens.cacheCreationInputTokens;
288
378
  const lastSnapshot = contextSnapshots[contextSnapshots.length - 1];
@@ -301,7 +391,7 @@ async function parseDetail(filePath, sessionId, projectPath, projectName) {
301
391
  timestamp: msg.timestamp ?? "",
302
392
  model: msg.message.model,
303
393
  toolCalls,
304
- tokens,
394
+ tokens: isNewRequest ? tokens : void 0,
305
395
  stopReason: msg.message.stop_reason
306
396
  });
307
397
  continue;
@@ -473,7 +563,7 @@ async function parseSubagentDetail(subagentFilePath) {
473
563
  let model;
474
564
  let totalToolUseCount = 0;
475
565
  const seenRequestIds = /* @__PURE__ */ new Set();
476
- const stream = fs.createReadStream(subagentFilePath, { encoding: "utf-8" });
566
+ const stream = fs$1.createReadStream(subagentFilePath, { encoding: "utf-8" });
477
567
  const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
478
568
  let lineCount = 0;
479
569
  const MAX_LINES_FOR_INJECTED = 20;
@@ -616,7 +706,7 @@ function mergeSubagentData(agent, detail, progressTokens, totalTokens, tokensByM
616
706
  }
617
707
  async function readHeadLines(filePath, count) {
618
708
  const lines = [];
619
- const stream = fs.createReadStream(filePath, { encoding: "utf-8" });
709
+ const stream = fs$1.createReadStream(filePath, { encoding: "utf-8" });
620
710
  const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
621
711
  for await (const line of rl) {
622
712
  lines.push(line);
@@ -627,10 +717,10 @@ async function readHeadLines(filePath, count) {
627
717
  return lines;
628
718
  }
629
719
  async function readTailLines(filePath, count) {
630
- const stat = await fs.promises.stat(filePath);
720
+ const stat = await fs$1.promises.stat(filePath);
631
721
  const readSize = Math.min(stat.size, 65536);
632
722
  const buffer = Buffer.alloc(readSize);
633
- const fd = await fs.promises.open(filePath, "r");
723
+ const fd = await fs$1.promises.open(filePath, "r");
634
724
  try {
635
725
  await fd.read(buffer, 0, readSize, Math.max(0, stat.size - readSize));
636
726
  } finally {
@@ -664,11 +754,13 @@ function extractTextContent(msg) {
664
754
  return texts.length > 0 ? texts.join("\n").slice(0, 500) : void 0;
665
755
  }
666
756
  export {
667
- getStatsPath as a,
668
- extractSessionId as b,
669
- parseSummary as c,
757
+ getDataSources as a,
758
+ getProjectsDirFor as b,
759
+ getProjectsDir as c,
670
760
  decodeProjectDirName as d,
671
761
  extractProjectName as e,
672
- getProjectsDir as g,
762
+ extractSessionId as f,
763
+ getStatsPath as g,
764
+ parseSummary as h,
673
765
  parseDetail as p
674
766
  };
@@ -1,6 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import { g as getProjectsDir, d as decodeProjectDirName, e as extractProjectName, b as extractSessionId, c as parseSummary } from "./session-parser-B0pdBvgT.js";
3
+ import { c as getProjectsDir, d as decodeProjectDirName, e as extractProjectName, f as extractSessionId, h as parseSummary } from "./session-parser-DKZZMuh6.js";
4
4
  async function scanProjects() {
5
5
  const projectsDir = getProjectsDir();
6
6
  let entries;
@@ -28,7 +28,7 @@ async function scanProjects() {
28
28
  return projects;
29
29
  }
30
30
  const ACTIVE_THRESHOLD_MS = 12e4;
31
- async function isSessionActive(projectDirName, sessionId) {
31
+ async function isSessionActive(projectDirName, sessionId, projectsDirOverride) {
32
32
  const projectsDir = getProjectsDir();
33
33
  const jsonlPath = path.join(projectsDir, projectDirName, `${sessionId}.jsonl`);
34
34
  const lockDirPath = path.join(projectsDir, projectDirName, sessionId);
@@ -94,7 +94,7 @@ async function getActiveSessions() {
94
94
  return all.filter((s) => s.isActive);
95
95
  }
96
96
  export {
97
- scanAllSessions as a,
97
+ scanAllSessionsWithPaths as a,
98
98
  getActiveSessions as g,
99
- scanAllSessionsWithPaths as s
99
+ scanAllSessions as s
100
100
  };
@@ -1,27 +1,29 @@
1
- import { c as createServerRpc } from "./createServerRpc-Bd3B-Ah9.js";
1
+ import { c as createServerRpc } from "./createServerRpc-D_-6bKnO.js";
2
2
  import { z } from "zod";
3
- import { a as scanAllSessions, g as getActiveSessions } from "./session-scanner-CpgOq5m1.js";
3
+ import { s as scanAllSessions, g as getActiveSessions } from "./session-scanner-CECpfGFh.js";
4
4
  import { c as createServerFn } from "../server.js";
5
5
  import "node:fs";
6
6
  import "node:path";
7
- import "./session-parser-B0pdBvgT.js";
7
+ import "./session-parser-DKZZMuh6.js";
8
8
  import "node:os";
9
+ import "node:fs/promises";
9
10
  import "node:readline";
10
- import "@tanstack/history";
11
- import "@tanstack/router-core/ssr/client";
12
- import "@tanstack/router-core";
13
11
  import "node:async_hooks";
14
- import "@tanstack/router-core/ssr/server";
15
12
  import "h3-v2";
13
+ import "@tanstack/router-core";
16
14
  import "tiny-invariant";
17
15
  import "seroval";
16
+ import "@tanstack/history";
17
+ import "@tanstack/router-core/ssr/client";
18
+ import "@tanstack/router-core/ssr/server";
19
+ import "react";
20
+ import "@tanstack/react-router";
18
21
  import "react/jsx-runtime";
19
22
  import "@tanstack/react-router/ssr/server";
20
- import "@tanstack/react-router";
21
23
  const getSessionList_createServerFn_handler = createServerRpc({
22
- id: "bf8e4a7901f1843bdc9c46be1ad5ad59c615b8bbe611b73eb3ff28f20e43ee0d",
24
+ id: "8fd6c4e5b4d5590acf1ec73da75f249978e8aced6dd2be23de06ade8431033be",
23
25
  name: "getSessionList",
24
- filename: "src/features/sessions/sessions.server.ts"
26
+ filename: "src/features/sessions/sessions.api.ts"
25
27
  }, (opts) => getSessionList.__executeServer(opts));
26
28
  const getSessionList = createServerFn({
27
29
  method: "GET"
@@ -29,9 +31,9 @@ const getSessionList = createServerFn({
29
31
  return scanAllSessions();
30
32
  });
31
33
  const getActiveSessionList_createServerFn_handler = createServerRpc({
32
- id: "839d29fe93dfa2a6d506af7b48ca25197190a5ff4c796e970ddfdc6e8c98827f",
34
+ id: "946cc550946f64ee7985dc35913a690eb13183d7ba83cffe398e424e697b4265",
33
35
  name: "getActiveSessionList",
34
- filename: "src/features/sessions/sessions.server.ts"
36
+ filename: "src/features/sessions/sessions.api.ts"
35
37
  }, (opts) => getActiveSessionList.__executeServer(opts));
36
38
  const getActiveSessionList = createServerFn({
37
39
  method: "GET"
@@ -83,9 +85,9 @@ async function paginateAndFilterSessions(allSessions, input) {
83
85
  };
84
86
  }
85
87
  const getPaginatedSessions_createServerFn_handler = createServerRpc({
86
- id: "a3f42f9012fd83586787da8f7cb90649da739dd947d867eb67572f68735ff495",
88
+ id: "e574977967ea9b3387e72d70704b6ca87230e72becaf69f0b98cbc91c9cd1339",
87
89
  name: "getPaginatedSessions",
88
- filename: "src/features/sessions/sessions.server.ts"
90
+ filename: "src/features/sessions/sessions.api.ts"
89
91
  }, (opts) => getPaginatedSessions.__executeServer(opts));
90
92
  const getPaginatedSessions = createServerFn({
91
93
  method: "GET"
@@ -1,13 +1,13 @@
1
1
  import { queryOptions, keepPreviousData } from "@tanstack/react-query";
2
- import { c as createSsrRpc } from "./createSsrRpc-CVg2UDl0.js";
2
+ import { c as createSsrRpc } from "./createSsrRpc-OFLSk08e.js";
3
3
  import { z } from "zod";
4
4
  import { c as createServerFn } from "../server.js";
5
5
  const getSessionList = createServerFn({
6
6
  method: "GET"
7
- }).handler(createSsrRpc("bf8e4a7901f1843bdc9c46be1ad5ad59c615b8bbe611b73eb3ff28f20e43ee0d"));
7
+ }).handler(createSsrRpc("8fd6c4e5b4d5590acf1ec73da75f249978e8aced6dd2be23de06ade8431033be"));
8
8
  const getActiveSessionList = createServerFn({
9
9
  method: "GET"
10
- }).handler(createSsrRpc("839d29fe93dfa2a6d506af7b48ca25197190a5ff4c796e970ddfdc6e8c98827f"));
10
+ }).handler(createSsrRpc("946cc550946f64ee7985dc35913a690eb13183d7ba83cffe398e424e697b4265"));
11
11
  const paginatedSessionsInputSchema = z.object({
12
12
  page: z.number().int().min(1),
13
13
  pageSize: z.number().int().min(5).max(100),
@@ -17,7 +17,7 @@ const paginatedSessionsInputSchema = z.object({
17
17
  });
18
18
  const getPaginatedSessions = createServerFn({
19
19
  method: "GET"
20
- }).inputValidator((input) => paginatedSessionsInputSchema.parse(input)).handler(createSsrRpc("a3f42f9012fd83586787da8f7cb90649da739dd947d867eb67572f68735ff495"));
20
+ }).inputValidator((input) => paginatedSessionsInputSchema.parse(input)).handler(createSsrRpc("e574977967ea9b3387e72d70704b6ca87230e72becaf69f0b98cbc91c9cd1339"));
21
21
  queryOptions({
22
22
  queryKey: ["sessions", "list"],
23
23
  queryFn: () => getSessionList(),