graphtrack 0.1.0 → 0.1.2
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.
- package/README.md +82 -42
- package/dist/graph.d.ts +9 -0
- package/dist/graph.js +35 -30
- package/dist/graph.js.map +1 -1
- package/dist/parser.d.ts +7 -1
- package/dist/parser.js +104 -20
- package/dist/parser.js.map +1 -1
- package/dist/server.js +39 -2
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +26 -1
- package/package.json +7 -3
- package/public/images/icon.svg +1 -0
- package/public/index.html +973 -90
package/README.md
CHANGED
|
@@ -1,70 +1,110 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
1
3
|
# Graphtrack
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/graphtrack)
|
|
6
|
+
[](https://github.com/nicholasgriffintn/graphtrack/blob/main/LICENSE)
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
prompts, tool calls, and their token weight rendered as nodes and edges. Node size
|
|
7
|
-
is token share, color is tool type, and edges trace how your tokens actually flowed.
|
|
8
|
-
Find out at a glance which tool is eating your budget, or trace a single request
|
|
9
|
-
from prompt to result.
|
|
8
|
+
**Interactive token-usage dashboard for Claude Code.**
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
Run `npx graphtrack`, open `localhost:9090`, and see exactly where your tokens go —
|
|
11
|
+
as a graph, a table, or a multi-session overview.
|
|
12
|
+
|
|
13
|
+
Your logs never leave your machine.
|
|
14
|
+
|
|
15
|
+
[Installation](#installation) · [Quick Start](#quick-start) · [Features](#features) · [CLI Options](#cli-options) · [Development](#development) · [License](#license)
|
|
16
|
+
|
|
17
|
+
</div>
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# No install needed — runs directly from npm
|
|
25
|
+
npx graphtrack
|
|
26
|
+
|
|
27
|
+
# Or install globally
|
|
28
|
+
npm install -g graphtrack
|
|
29
|
+
graphtrack
|
|
30
|
+
```
|
|
16
31
|
|
|
17
|
-
|
|
18
|
-
served to your own browser at `localhost`. Nothing is uploaded, stored, or shared.
|
|
19
|
-
Your usage data never leaves your computer.
|
|
32
|
+
Requires [Node.js](https://nodejs.org/) 18+.
|
|
20
33
|
|
|
21
|
-
##
|
|
34
|
+
## Quick Start
|
|
22
35
|
|
|
23
36
|
```bash
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
37
|
+
npx graphtrack
|
|
38
|
+
# → Server starts at http://localhost:9090
|
|
39
|
+
# → Picks up sessions from ~/.claude/projects automatically
|
|
40
|
+
# → Pick a session from the dropdown and explore
|
|
28
41
|
```
|
|
29
42
|
|
|
30
|
-
|
|
31
|
-
`~/.claude/projects`), and explore the graph.
|
|
43
|
+
Point at a custom log directory:
|
|
32
44
|
|
|
33
|
-
|
|
45
|
+
```bash
|
|
46
|
+
npx graphtrack --dir /path/to/.claude/projects
|
|
47
|
+
```
|
|
34
48
|
|
|
35
|
-
|
|
36
|
-
the visualization without any setup:
|
|
49
|
+
Use a different port:
|
|
37
50
|
|
|
38
51
|
```bash
|
|
39
|
-
|
|
40
|
-
node scripts/build-spike.mjs
|
|
41
|
-
# open public/index.html in a browser
|
|
52
|
+
npx graphtrack --port 3000
|
|
42
53
|
```
|
|
43
54
|
|
|
44
|
-
##
|
|
55
|
+
## Features
|
|
56
|
+
|
|
57
|
+
### Four views, one dashboard
|
|
58
|
+
|
|
59
|
+
| View | What it shows |
|
|
60
|
+
| ----------------------- | ------------------------------------------------------------------------------------------- |
|
|
61
|
+
| **Overview** | Aggregate stats across all sessions — total tokens, per-project breakdown, tool usage share |
|
|
62
|
+
| **Aggregate Graph** | Hub-and-spoke network: each tool is a node, size = token share, color = tool type |
|
|
63
|
+
| **Chronological Graph** | Turn-by-turn chain: each prompt links to the tools it triggered, ordered in time |
|
|
64
|
+
| **Table** | Sortable per-tool and per-turn tables with color-coded token bars |
|
|
65
|
+
|
|
66
|
+
### Session picker
|
|
67
|
+
|
|
68
|
+
A searchable dropdown groups sessions by project — type to filter by ID or project name, then click to load.
|
|
45
69
|
|
|
46
|
-
|
|
47
|
-
- **Phase 1** — JSONL parser + typed model + per-tool aggregation
|
|
48
|
-
- **Phase 2** — localhost web app: vis.js graph + table view
|
|
49
|
-
- **Phase 3** — multi-session overview, empty state, error handling
|
|
50
|
-
- **Phase 4** — install polish, docs, CI
|
|
70
|
+
### What gets tracked
|
|
51
71
|
|
|
52
|
-
|
|
72
|
+
- **Input / output / cache tokens** per session, per turn, and per tool call
|
|
73
|
+
- **Real timestamps** — session start time and wall-clock duration from the actual logs
|
|
74
|
+
- **Tool breakdown** — which tools consume the most tokens, how many times each was called
|
|
75
|
+
- **Project grouping** — sessions organized by project in the Overview view
|
|
76
|
+
|
|
77
|
+
### Tool color legend
|
|
78
|
+
|
|
79
|
+
| Tool | Color |
|
|
80
|
+
| ----- | ------------------------------------------- |
|
|
81
|
+
| Edit | <span style="color:#ff6b6b">●</span> Red |
|
|
82
|
+
| Bash | <span style="color:#faff69">●</span> Yellow |
|
|
83
|
+
| Read | <span style="color:#3b82f6">●</span> Blue |
|
|
84
|
+
| Write | <span style="color:#4cff4c">●</span> Green |
|
|
85
|
+
| Glob | <span style="color:#b06bff">●</span> Purple |
|
|
86
|
+
| Grep | <span style="color:#ff8cff">●</span> Pink |
|
|
87
|
+
| Agent | <span style="color:#ffb347">●</span> Orange |
|
|
88
|
+
|
|
89
|
+
## Project Structure
|
|
53
90
|
|
|
54
91
|
```
|
|
55
92
|
src/
|
|
56
|
-
types.ts
|
|
57
|
-
parser.ts
|
|
58
|
-
graph.ts
|
|
59
|
-
|
|
60
|
-
|
|
93
|
+
types.ts Data model (Session, Turn, ToolCall, Usage, Overview)
|
|
94
|
+
parser.ts JSONL → Session + aggregateSessions()
|
|
95
|
+
graph.ts Session → vis.js nodes/edges (aggregate + chronological)
|
|
96
|
+
server.ts Express API + static file serving
|
|
97
|
+
cli.ts CLI entry point (--dir, --port, --help)
|
|
98
|
+
|
|
61
99
|
public/
|
|
62
|
-
index.html
|
|
63
|
-
|
|
100
|
+
index.html Dashboard UI (4 views, vanilla JS)
|
|
101
|
+
images/ Icon and assets
|
|
102
|
+
|
|
64
103
|
test/
|
|
65
|
-
|
|
104
|
+
parser.test.ts Unit tests for parser, aggregation, and graph builders
|
|
105
|
+
fixtures/ Sample session data for tests
|
|
66
106
|
```
|
|
67
107
|
|
|
68
108
|
## License
|
|
69
109
|
|
|
70
|
-
MIT
|
|
110
|
+
MIT
|
package/dist/graph.d.ts
CHANGED
|
@@ -37,6 +37,15 @@ export interface VisEdge {
|
|
|
37
37
|
arrows?: string;
|
|
38
38
|
}
|
|
39
39
|
export declare function toolColor(name: string): string;
|
|
40
|
+
/** Hub-and-tool aggregate graph: one root connected to tool nodes sized by token share. */
|
|
41
|
+
export declare function buildToolHubGraph(tools: {
|
|
42
|
+
name: string;
|
|
43
|
+
calls: number;
|
|
44
|
+
totalTokens: number;
|
|
45
|
+
}[], rootLabel: string, rootTitle?: string): {
|
|
46
|
+
nodes: VisNode[];
|
|
47
|
+
edges: VisEdge[];
|
|
48
|
+
};
|
|
40
49
|
/** Hub-and-tool aggregate graph for one session. */
|
|
41
50
|
export declare function buildAggregateGraph(session: Session): {
|
|
42
51
|
nodes: VisNode[];
|
package/dist/graph.js
CHANGED
|
@@ -14,17 +14,18 @@ export function spreadTokens(session) {
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
-
/** Consistent color per tool name, tuned for a dark canvas. */
|
|
17
|
+
/** Consistent color per tool name, tuned for a dark canvas. Bright & vivid. */
|
|
18
18
|
const TOOL_COLORS = {
|
|
19
19
|
Bash: "#faff69",
|
|
20
|
-
Read: "#
|
|
21
|
-
Edit: "#
|
|
22
|
-
Grep: "#
|
|
23
|
-
Glob: "#
|
|
24
|
-
WebSearch: "#
|
|
20
|
+
Read: "#60a5fa",
|
|
21
|
+
Edit: "#4ade80",
|
|
22
|
+
Grep: "#f87171",
|
|
23
|
+
Glob: "#c084fc",
|
|
24
|
+
WebSearch: "#fbbf24",
|
|
25
25
|
Task: "#22d3ee",
|
|
26
26
|
Write: "#34d399",
|
|
27
|
-
|
|
27
|
+
Agent: "#fb923c",
|
|
28
|
+
default: "#94a3b8",
|
|
28
29
|
};
|
|
29
30
|
export function toolColor(name) {
|
|
30
31
|
return TOOL_COLORS[name] ?? TOOL_COLORS.default;
|
|
@@ -33,43 +34,47 @@ export function toolColor(name) {
|
|
|
33
34
|
function scale(value) {
|
|
34
35
|
return Math.sqrt(value) / 10;
|
|
35
36
|
}
|
|
36
|
-
/** Hub-and-tool aggregate graph
|
|
37
|
-
export function
|
|
37
|
+
/** Hub-and-tool aggregate graph: one root connected to tool nodes sized by token share. */
|
|
38
|
+
export function buildToolHubGraph(tools, rootLabel, rootTitle = "This session") {
|
|
38
39
|
const nodes = [];
|
|
39
40
|
const edges = [];
|
|
40
|
-
const rootId = "session";
|
|
41
41
|
nodes.push({
|
|
42
|
-
id:
|
|
43
|
-
label:
|
|
42
|
+
id: "session",
|
|
43
|
+
label: rootLabel,
|
|
44
44
|
value: 6,
|
|
45
45
|
color: { background: "#faff69", border: "#e6eb52" },
|
|
46
46
|
group: "session",
|
|
47
|
-
title:
|
|
47
|
+
title: rootTitle,
|
|
48
48
|
});
|
|
49
|
+
const sorted = [...tools].sort((a, b) => b.totalTokens - a.totalTokens);
|
|
50
|
+
const maxTokens = Math.max(1, ...[...tools].map((v) => v.totalTokens));
|
|
51
|
+
for (const t of sorted) {
|
|
52
|
+
const id = `tool:${t.name}`;
|
|
53
|
+
nodes.push({
|
|
54
|
+
id,
|
|
55
|
+
label: `${t.name}\n${t.calls}× · ${fmt(t.totalTokens)}`,
|
|
56
|
+
value: 1 + 5 * (t.totalTokens / maxTokens),
|
|
57
|
+
color: { background: toolColor(t.name), border: "#ffffff" },
|
|
58
|
+
group: "tool",
|
|
59
|
+
title: `${t.name}: ${t.calls} calls, ${fmt(t.totalTokens)} tokens`,
|
|
60
|
+
});
|
|
61
|
+
edges.push({ from: "session", to: id, value: 2 + 4 * (t.totalTokens / maxTokens), title: `${t.name}: ${fmt(t.totalTokens)}` });
|
|
62
|
+
}
|
|
63
|
+
return { nodes, edges };
|
|
64
|
+
}
|
|
65
|
+
/** Hub-and-tool aggregate graph for one session. */
|
|
66
|
+
export function buildAggregateGraph(session) {
|
|
49
67
|
// Group tool calls across the whole session (reuse aggregation).
|
|
50
68
|
const byTool = new Map();
|
|
51
69
|
for (const turn of session.turns) {
|
|
52
70
|
for (const call of turn.toolCalls) {
|
|
53
|
-
const entry = byTool.get(call.name) ?? { calls: 0,
|
|
71
|
+
const entry = byTool.get(call.name) ?? { name: call.name, calls: 0, totalTokens: 0 };
|
|
54
72
|
entry.calls += 1;
|
|
55
|
-
entry.
|
|
73
|
+
entry.totalTokens += call.totalTokens || 1;
|
|
56
74
|
byTool.set(call.name, entry);
|
|
57
75
|
}
|
|
58
76
|
}
|
|
59
|
-
|
|
60
|
-
for (const [name, agg] of byTool) {
|
|
61
|
-
const id = `tool:${name}`;
|
|
62
|
-
nodes.push({
|
|
63
|
-
id,
|
|
64
|
-
label: `${name}\n${agg.calls}× · ${fmt(agg.tokens)}`,
|
|
65
|
-
value: 1 + 5 * (agg.tokens / maxTokens),
|
|
66
|
-
color: { background: toolColor(name), border: "#ffffff" },
|
|
67
|
-
group: "tool",
|
|
68
|
-
title: `${name}: ${agg.calls} calls, ${fmt(agg.tokens)} tokens`,
|
|
69
|
-
});
|
|
70
|
-
edges.push({ from: rootId, to: id, value: 2 + 4 * (agg.tokens / maxTokens), title: `${name}: ${fmt(agg.tokens)}` });
|
|
71
|
-
}
|
|
72
|
-
return { nodes, edges };
|
|
77
|
+
return buildToolHubGraph([...byTool.values()], `${session.project}\n${session.turns.length} turns · ${fmt(session.totalTokens)} tok`, "This session");
|
|
73
78
|
}
|
|
74
79
|
/** Turn-by-turn chronological graph: session → prompts → their tool calls. */
|
|
75
80
|
export function buildChronologicalGraph(session) {
|
|
@@ -100,7 +105,7 @@ export function buildChronologicalGraph(session) {
|
|
|
100
105
|
nodes.push({
|
|
101
106
|
id: callId,
|
|
102
107
|
label: call.name,
|
|
103
|
-
value:
|
|
108
|
+
value: 2.5,
|
|
104
109
|
color: { background: toolColor(call.name), border: "#ffffff" },
|
|
105
110
|
group: "tool",
|
|
106
111
|
title: `${call.name} ${call.summary}`,
|
package/dist/graph.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph.js","sourceRoot":"","sources":["../src/graph.ts"],"names":[],"mappings":"AAaA;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,OAAgB;IAC3C,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QACjC,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,IAAI,CAAC,WAAW,GAAG,CAAC,EAAE,CAAC;YAClD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;YACvD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBAClC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAoBD,+
|
|
1
|
+
{"version":3,"file":"graph.js","sourceRoot":"","sources":["../src/graph.ts"],"names":[],"mappings":"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"}
|
package/dist/parser.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import type { Session, ToolAggregate } from "./types.js";
|
|
1
|
+
import type { Overview, Session, ToolAggregate } from "./types.js";
|
|
2
2
|
/** Extracts tool calls from a session's JSONL and groups them into turns. */
|
|
3
3
|
export declare function parseSession(filePath: string, project: string): Session;
|
|
4
4
|
/** Aggregate tool calls across a session (the collapse-by-tool model). */
|
|
5
5
|
export declare function aggregateTools(session: Session): ToolAggregate[];
|
|
6
|
+
/**
|
|
7
|
+
* Collapse many parsed sessions into a single overview: per-project rows,
|
|
8
|
+
* global totals, and tool aggregates across everything. Callers must run
|
|
9
|
+
* spreadTokens() on each session first so per-tool attribution is seeded.
|
|
10
|
+
*/
|
|
11
|
+
export declare function aggregateSessions(sessions: Session[]): Overview;
|
package/dist/parser.js
CHANGED
|
@@ -5,7 +5,15 @@
|
|
|
5
5
|
* unreadable file (callers decide how to surface partial failures).
|
|
6
6
|
*/
|
|
7
7
|
import { readFileSync } from "node:fs";
|
|
8
|
-
/**
|
|
8
|
+
/** Normalize a message's content field to an array for the rest of the parser. */
|
|
9
|
+
function normalizeContent(content) {
|
|
10
|
+
if (!content)
|
|
11
|
+
return [];
|
|
12
|
+
if (typeof content === "string")
|
|
13
|
+
return [{ type: "text", text: content }];
|
|
14
|
+
return Array.isArray(content) ? content : [content];
|
|
15
|
+
}
|
|
16
|
+
/** Parse one line of JSONL into the message we care about + its timestamp. */
|
|
9
17
|
function parseLine(raw) {
|
|
10
18
|
const line = raw.trim();
|
|
11
19
|
if (!line)
|
|
@@ -17,19 +25,13 @@ function parseLine(raw) {
|
|
|
17
25
|
catch {
|
|
18
26
|
return null; // malformed line — skip, don't crash
|
|
19
27
|
}
|
|
20
|
-
|
|
28
|
+
if (!obj?.message)
|
|
29
|
+
return null;
|
|
30
|
+
return { msg: obj.message, timestamp: obj.timestamp };
|
|
21
31
|
}
|
|
22
|
-
function contentSummary(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return content
|
|
26
|
-
.map((b) => {
|
|
27
|
-
if (b.type === "text")
|
|
28
|
-
return b.text ?? "";
|
|
29
|
-
if (b.type === "tool_use")
|
|
30
|
-
return "";
|
|
31
|
-
return "";
|
|
32
|
-
})
|
|
32
|
+
function contentSummary(blocks) {
|
|
33
|
+
return blocks
|
|
34
|
+
.map((b) => (b.type === "text" && typeof b.text === "string" ? b.text : ""))
|
|
33
35
|
.filter(Boolean)
|
|
34
36
|
.join(" ");
|
|
35
37
|
}
|
|
@@ -50,6 +52,7 @@ export function parseSession(filePath, project) {
|
|
|
50
52
|
project,
|
|
51
53
|
model: "unknown",
|
|
52
54
|
startedAt: "",
|
|
55
|
+
durationMs: null,
|
|
53
56
|
turns: [],
|
|
54
57
|
totalTokens: 0,
|
|
55
58
|
totalInput: 0,
|
|
@@ -57,17 +60,33 @@ export function parseSession(filePath, project) {
|
|
|
57
60
|
totalCacheRead: 0,
|
|
58
61
|
totalCacheWrite: 0,
|
|
59
62
|
};
|
|
63
|
+
// Track the earliest/latest timestamp across all lines to get real timings.
|
|
64
|
+
let startTs = null;
|
|
65
|
+
let lastTs = null;
|
|
66
|
+
const noteTime = (ts) => {
|
|
67
|
+
if (!ts)
|
|
68
|
+
return;
|
|
69
|
+
const n = Date.parse(ts);
|
|
70
|
+
if (Number.isNaN(n))
|
|
71
|
+
return;
|
|
72
|
+
if (startTs === null || n < startTs)
|
|
73
|
+
startTs = n;
|
|
74
|
+
if (lastTs === null || n > lastTs)
|
|
75
|
+
lastTs = n;
|
|
76
|
+
};
|
|
60
77
|
// We walk the stream and slice it into turns. A turn starts at a user message
|
|
61
78
|
// that contains a text prompt (i.e. a real request, not a tool_result).
|
|
62
79
|
let current = null;
|
|
63
80
|
for (const line of lines) {
|
|
64
|
-
const
|
|
65
|
-
if (!
|
|
81
|
+
const parsed = parseLine(line);
|
|
82
|
+
if (!parsed)
|
|
66
83
|
continue;
|
|
67
|
-
const
|
|
84
|
+
const { msg, timestamp } = parsed;
|
|
85
|
+
noteTime(timestamp);
|
|
86
|
+
const content = normalizeContent(msg.content);
|
|
87
|
+
const text = contentSummary(content);
|
|
68
88
|
// Determine message kind
|
|
69
89
|
const hasText = Boolean(text.trim());
|
|
70
|
-
const content = msg.content ?? [];
|
|
71
90
|
// A new turn begins when the user speaks with text (not a tool_result).
|
|
72
91
|
if (msg.role === "user" && hasText && content.every((b) => b.type !== "tool_result")) {
|
|
73
92
|
if (current)
|
|
@@ -108,9 +127,11 @@ export function parseSession(filePath, project) {
|
|
|
108
127
|
}
|
|
109
128
|
if (current)
|
|
110
129
|
session.turns.push(current);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
130
|
+
if (startTs !== null) {
|
|
131
|
+
session.startedAt = new Date(startTs).toISOString();
|
|
132
|
+
if (lastTs !== null)
|
|
133
|
+
session.durationMs = lastTs - startTs;
|
|
134
|
+
}
|
|
114
135
|
session.totalTokens = session.turns.reduce((a, t) => a + t.totalTokens, 0);
|
|
115
136
|
return session;
|
|
116
137
|
}
|
|
@@ -142,4 +163,67 @@ export function aggregateTools(session) {
|
|
|
142
163
|
}
|
|
143
164
|
return [...map.values()].sort((a, b) => b.totalTokens - a.totalTokens);
|
|
144
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* Collapse many parsed sessions into a single overview: per-project rows,
|
|
168
|
+
* global totals, and tool aggregates across everything. Callers must run
|
|
169
|
+
* spreadTokens() on each session first so per-tool attribution is seeded.
|
|
170
|
+
*/
|
|
171
|
+
export function aggregateSessions(sessions) {
|
|
172
|
+
const projectMap = new Map();
|
|
173
|
+
const toolMap = new Map();
|
|
174
|
+
const totals = {
|
|
175
|
+
sessions: sessions.length,
|
|
176
|
+
totalTokens: 0,
|
|
177
|
+
totalInput: 0,
|
|
178
|
+
totalOutput: 0,
|
|
179
|
+
totalCacheRead: 0,
|
|
180
|
+
totalCacheWrite: 0,
|
|
181
|
+
};
|
|
182
|
+
for (const s of sessions) {
|
|
183
|
+
totals.totalTokens += s.totalTokens;
|
|
184
|
+
totals.totalInput += s.totalInput;
|
|
185
|
+
totals.totalOutput += s.totalOutput;
|
|
186
|
+
totals.totalCacheRead += s.totalCacheRead;
|
|
187
|
+
totals.totalCacheWrite += s.totalCacheWrite;
|
|
188
|
+
const p = projectMap.get(s.project) ?? {
|
|
189
|
+
project: s.project,
|
|
190
|
+
sessions: 0,
|
|
191
|
+
totalTokens: 0,
|
|
192
|
+
totalInput: 0,
|
|
193
|
+
totalOutput: 0,
|
|
194
|
+
totalCacheRead: 0,
|
|
195
|
+
totalCacheWrite: 0,
|
|
196
|
+
};
|
|
197
|
+
p.sessions += 1;
|
|
198
|
+
p.totalTokens += s.totalTokens;
|
|
199
|
+
p.totalInput += s.totalInput;
|
|
200
|
+
p.totalOutput += s.totalOutput;
|
|
201
|
+
p.totalCacheRead += s.totalCacheRead;
|
|
202
|
+
p.totalCacheWrite += s.totalCacheWrite;
|
|
203
|
+
projectMap.set(s.project, p);
|
|
204
|
+
for (const agg of aggregateTools(s)) {
|
|
205
|
+
const t = toolMap.get(agg.name) ?? {
|
|
206
|
+
name: agg.name,
|
|
207
|
+
calls: 0,
|
|
208
|
+
totalTokens: 0,
|
|
209
|
+
inputTokens: 0,
|
|
210
|
+
outputTokens: 0,
|
|
211
|
+
cacheRead: 0,
|
|
212
|
+
cacheWrite: 0,
|
|
213
|
+
};
|
|
214
|
+
t.calls += agg.calls;
|
|
215
|
+
t.totalTokens += agg.totalTokens;
|
|
216
|
+
t.inputTokens += agg.inputTokens;
|
|
217
|
+
t.outputTokens += agg.outputTokens;
|
|
218
|
+
t.cacheRead += agg.cacheRead;
|
|
219
|
+
t.cacheWrite += agg.cacheWrite;
|
|
220
|
+
toolMap.set(agg.name, t);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
projects: [...projectMap.values()].sort((a, b) => b.totalTokens - a.totalTokens),
|
|
225
|
+
totals,
|
|
226
|
+
toolAggregates: [...toolMap.values()].sort((a, b) => b.totalTokens - a.totalTokens),
|
|
227
|
+
};
|
|
228
|
+
}
|
|
145
229
|
//# sourceMappingURL=parser.js.map
|
package/dist/parser.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -14,8 +14,8 @@ import { readdirSync } from "node:fs";
|
|
|
14
14
|
import { homedir } from "node:os";
|
|
15
15
|
import { join, relative, dirname, basename } from "node:path";
|
|
16
16
|
import { fileURLToPath } from "node:url";
|
|
17
|
-
import { parseSession, aggregateTools } from "./parser.js";
|
|
18
|
-
import { buildAggregateGraph, buildChronologicalGraph, toolColor, spreadTokens } from "./graph.js";
|
|
17
|
+
import { parseSession, aggregateTools, aggregateSessions } from "./parser.js";
|
|
18
|
+
import { buildAggregateGraph, buildChronologicalGraph, buildToolHubGraph, toolColor, spreadTokens } from "./graph.js";
|
|
19
19
|
export const DEFAULT_LOGS_DIR = join(homedir(), ".claude", "projects");
|
|
20
20
|
const DEFAULT_PORT = 9090;
|
|
21
21
|
/**
|
|
@@ -87,6 +87,8 @@ export function startServer(opts = {}) {
|
|
|
87
87
|
id: session.id,
|
|
88
88
|
project: session.project,
|
|
89
89
|
model: session.model,
|
|
90
|
+
startedAt: session.startedAt,
|
|
91
|
+
durationMs: session.durationMs,
|
|
90
92
|
turns: session.turns.length,
|
|
91
93
|
toolCalls: session.turns.reduce((a, t) => a + t.toolCalls.length, 0),
|
|
92
94
|
totalTokens: session.totalTokens,
|
|
@@ -95,11 +97,46 @@ export function startServer(opts = {}) {
|
|
|
95
97
|
totalCacheRead: session.totalCacheRead,
|
|
96
98
|
totalCacheWrite: session.totalCacheWrite,
|
|
97
99
|
},
|
|
100
|
+
turns: session.turns.map((t) => ({
|
|
101
|
+
index: t.index,
|
|
102
|
+
prompt: t.prompt,
|
|
103
|
+
toolCalls: t.toolCalls.length,
|
|
104
|
+
totalTokens: t.totalTokens,
|
|
105
|
+
})),
|
|
98
106
|
aggregate: buildAggregateGraph(session),
|
|
99
107
|
chronological: buildChronologicalGraph(session),
|
|
100
108
|
toolAggregates: aggregates,
|
|
101
109
|
});
|
|
102
110
|
});
|
|
111
|
+
// Multi-session overview: per-project totals + tool share across everything.
|
|
112
|
+
// Parses every session file; fine for a local tool, cached later if ever slow.
|
|
113
|
+
app.get("/api/overview", (_req, res) => {
|
|
114
|
+
const files = findSessionFiles(logsDir);
|
|
115
|
+
const sessions = [];
|
|
116
|
+
for (const file of files) {
|
|
117
|
+
try {
|
|
118
|
+
const s = parseSession(file, projectOf(logsDir, file));
|
|
119
|
+
spreadTokens(s);
|
|
120
|
+
sessions.push(s);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Skip unparseable files — a broken session shouldn't sink the overview.
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const overview = aggregateSessions(sessions);
|
|
127
|
+
res.json({
|
|
128
|
+
totals: overview.totals,
|
|
129
|
+
projects: overview.projects,
|
|
130
|
+
toolAggregates: overview.toolAggregates.map((a) => ({
|
|
131
|
+
name: a.name,
|
|
132
|
+
calls: a.calls,
|
|
133
|
+
totalTokens: a.totalTokens,
|
|
134
|
+
color: toolColor(a.name),
|
|
135
|
+
})),
|
|
136
|
+
graph: buildToolHubGraph(overview.toolAggregates.map((a) => ({ name: a.name, calls: a.calls, totalTokens: a.totalTokens })), `All sessions\n${overview.totals.sessions} sessions · ${overview.totals.totalTokens} tok`, "Every Claude Code session Graphtrack can see"),
|
|
137
|
+
logsDir,
|
|
138
|
+
});
|
|
139
|
+
});
|
|
103
140
|
// Static app (resolved relative to the installed package, not the cwd).
|
|
104
141
|
app.use(express.static(PUBLIC_DIR));
|
|
105
142
|
return app.listen(port, () => {
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|