@zhangferry-dev/tokendash 1.4.2 → 1.6.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.
- package/dist/client/assets/{index-B4YgU_cb.js → index-CY4G_b0x.js} +49 -49
- package/dist/client/assets/index-_yA9tOzZ.css +1 -0
- package/dist/client/index.html +2 -2
- package/dist/client/popover.html +822 -396
- package/dist/electron-server.cjs +255 -101
- package/dist/electron-server.cjs.map +3 -3
- package/dist/server/analyticsParser.js +66 -2
- package/dist/server/claudeBlocksParser.js +50 -7
- package/dist/server/claudeJsonlParser.d.ts +7 -1
- package/dist/server/claudeJsonlParser.js +75 -23
- package/dist/server/codexParser.d.ts +6 -2
- package/dist/server/codexParser.js +134 -80
- package/dist/server/index.d.ts +4 -0
- package/dist/server/index.js +25 -9
- package/electron/main.cjs +35 -50
- package/electron/preload.cjs +11 -0
- package/electron/updateService.cjs +148 -0
- package/package.json +1 -1
- package/dist/client/assets/index-iYDpTV63.css +0 -1
|
@@ -48,9 +48,56 @@ function countLines(text) {
|
|
|
48
48
|
// Claude Code session scanning & tool extraction
|
|
49
49
|
// ---------------------------------------------------------------------------
|
|
50
50
|
const CLAUDE_PROJECTS_DIR = join(homedir(), '.claude', 'projects');
|
|
51
|
+
const projectNameCache = new Map();
|
|
52
|
+
/** Decode Claude's encoded project directory name.
|
|
53
|
+
* Claude encodes paths: /Users/foo/bar → -Users-foo-bar
|
|
54
|
+
* Since '-' replaces '/' and project names can contain '-',
|
|
55
|
+
* we use filesystem checks to find the correct last segment.
|
|
56
|
+
*/
|
|
51
57
|
function extractProjectName(dirName) {
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
if (!dirName.startsWith('-'))
|
|
59
|
+
return dirName;
|
|
60
|
+
const cached = projectNameCache.get(dirName);
|
|
61
|
+
if (cached)
|
|
62
|
+
return cached;
|
|
63
|
+
const segments = dirName.replace(/^-/, '').split('-').filter(Boolean);
|
|
64
|
+
if (segments.length === 0) {
|
|
65
|
+
projectNameCache.set(dirName, dirName);
|
|
66
|
+
return dirName;
|
|
67
|
+
}
|
|
68
|
+
if (segments.length === 1) {
|
|
69
|
+
projectNameCache.set(dirName, segments[0]);
|
|
70
|
+
return segments[0];
|
|
71
|
+
}
|
|
72
|
+
let bestName = segments[segments.length - 1];
|
|
73
|
+
for (let splitAt = segments.length - 1; splitAt >= 1; splitAt--) {
|
|
74
|
+
const parentSegments = segments.slice(0, splitAt);
|
|
75
|
+
const candidateName = segments.slice(splitAt).join('-');
|
|
76
|
+
let parentPath = '/';
|
|
77
|
+
let valid = true;
|
|
78
|
+
for (const seg of parentSegments) {
|
|
79
|
+
const regular = join(parentPath, seg);
|
|
80
|
+
const hidden = join(parentPath, '.' + seg);
|
|
81
|
+
if (existsSync(regular)) {
|
|
82
|
+
parentPath = regular;
|
|
83
|
+
}
|
|
84
|
+
else if (existsSync(hidden)) {
|
|
85
|
+
parentPath = hidden;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
valid = false;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (!valid)
|
|
93
|
+
continue;
|
|
94
|
+
if (existsSync(join(parentPath, candidateName)) || existsSync(join(parentPath, '.' + candidateName))) {
|
|
95
|
+
bestName = candidateName;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
projectNameCache.set(dirName, bestName);
|
|
100
|
+
return bestName;
|
|
54
101
|
}
|
|
55
102
|
function matchesProject(dirName, filter) {
|
|
56
103
|
return extractProjectName(dirName) === extractProjectName(filter);
|
|
@@ -273,5 +320,22 @@ export function computeAnalytics(toolCalls, timezone = 'Asia/Shanghai') {
|
|
|
273
320
|
toolCallTrend.push(entry);
|
|
274
321
|
}
|
|
275
322
|
toolCallTrend.sort((a, b) => a.date.localeCompare(b.date));
|
|
323
|
+
// Fill missing tool values with 0 so chart lines don't break
|
|
324
|
+
if (toolCallTrend.length > 0) {
|
|
325
|
+
const allTools = new Set();
|
|
326
|
+
for (const entry of toolCallTrend) {
|
|
327
|
+
for (const key of Object.keys(entry)) {
|
|
328
|
+
if (key !== 'date')
|
|
329
|
+
allTools.add(key);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
for (const entry of toolCallTrend) {
|
|
333
|
+
for (const tool of allTools) {
|
|
334
|
+
if (entry[tool] === undefined) {
|
|
335
|
+
entry[tool] = 0;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
276
340
|
return { codeChangeTrend, toolUsageDistribution, productivityKPIs, toolCallTrend };
|
|
277
341
|
}
|
|
@@ -24,13 +24,56 @@ const ClaudeEventSchema = z.object({
|
|
|
24
24
|
// Helpers
|
|
25
25
|
// ---------------------------------------------------------------------------
|
|
26
26
|
const CLAUDE_PROJECTS_DIR = join(homedir(), '.claude', 'projects');
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
* -Users-
|
|
27
|
+
const projectNameCache = new Map();
|
|
28
|
+
/** Decode Claude's encoded project directory name.
|
|
29
|
+
* Claude encodes paths: /Users/foo/bar → -Users-foo-bar
|
|
30
|
+
* Since '-' replaces '/' and project names can contain '-',
|
|
31
|
+
* we use filesystem checks to find the correct last segment.
|
|
30
32
|
*/
|
|
31
33
|
function extractProjectName(dirName) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
if (!dirName.startsWith('-'))
|
|
35
|
+
return dirName;
|
|
36
|
+
const cached = projectNameCache.get(dirName);
|
|
37
|
+
if (cached)
|
|
38
|
+
return cached;
|
|
39
|
+
const segments = dirName.replace(/^-/, '').split('-').filter(Boolean);
|
|
40
|
+
if (segments.length === 0) {
|
|
41
|
+
projectNameCache.set(dirName, dirName);
|
|
42
|
+
return dirName;
|
|
43
|
+
}
|
|
44
|
+
if (segments.length === 1) {
|
|
45
|
+
projectNameCache.set(dirName, segments[0]);
|
|
46
|
+
return segments[0];
|
|
47
|
+
}
|
|
48
|
+
let bestName = segments[segments.length - 1];
|
|
49
|
+
for (let splitAt = segments.length - 1; splitAt >= 1; splitAt--) {
|
|
50
|
+
const parentSegments = segments.slice(0, splitAt);
|
|
51
|
+
const candidateName = segments.slice(splitAt).join('-');
|
|
52
|
+
let parentPath = '/';
|
|
53
|
+
let valid = true;
|
|
54
|
+
for (const seg of parentSegments) {
|
|
55
|
+
const regular = join(parentPath, seg);
|
|
56
|
+
const hidden = join(parentPath, '.' + seg);
|
|
57
|
+
if (existsSync(regular)) {
|
|
58
|
+
parentPath = regular;
|
|
59
|
+
}
|
|
60
|
+
else if (existsSync(hidden)) {
|
|
61
|
+
parentPath = hidden;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
valid = false;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (!valid)
|
|
69
|
+
continue;
|
|
70
|
+
if (existsSync(join(parentPath, candidateName)) || existsSync(join(parentPath, '.' + candidateName))) {
|
|
71
|
+
bestName = candidateName;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
projectNameCache.set(dirName, bestName);
|
|
76
|
+
return bestName;
|
|
34
77
|
}
|
|
35
78
|
/** Match project display name against a filter (also normalizes the filter) */
|
|
36
79
|
function matchesProject(dirName, filter) {
|
|
@@ -99,7 +142,7 @@ export function getClaudeBlocksByProject(project) {
|
|
|
99
142
|
const outputTokens = usage.output_tokens;
|
|
100
143
|
const cacheCreationTokens = usage.cache_creation_input_tokens;
|
|
101
144
|
const cacheReadTokens = usage.cache_read_input_tokens;
|
|
102
|
-
const totalTokens = inputTokens + outputTokens + cacheReadTokens;
|
|
145
|
+
const totalTokens = inputTokens + outputTokens + cacheCreationTokens + cacheReadTokens;
|
|
103
146
|
if (totalTokens === 0)
|
|
104
147
|
continue;
|
|
105
148
|
const hourKey = getHourKey(event.timestamp);
|
|
@@ -124,7 +167,7 @@ export function getClaudeBlocksByProject(project) {
|
|
|
124
167
|
const blocks = [];
|
|
125
168
|
let idx = 0;
|
|
126
169
|
for (const [hourKey, bucket] of hourMap) {
|
|
127
|
-
const totalTokens = bucket.inputTokens + bucket.outputTokens + bucket.cacheReadTokens;
|
|
170
|
+
const totalTokens = bucket.inputTokens + bucket.outputTokens + bucket.cacheCreationTokens + bucket.cacheReadTokens;
|
|
128
171
|
blocks.push({
|
|
129
172
|
id: `claude-project-${idx}`,
|
|
130
173
|
startTime: `${hourKey}:00:00.000Z`,
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { DailyResponse, ProjectsResponse, BlockEntry } from '../shared/types.js';
|
|
2
|
-
export declare function calculateCost(inputTokens: number, cacheReadTokens: number, outputTokens: number, model: string): number;
|
|
2
|
+
export declare function calculateCost(inputTokens: number, cacheReadTokens: number, outputTokens: number, model: string, cacheCreationTokens?: number): number;
|
|
3
|
+
/** Decode Claude's encoded project directory name.
|
|
4
|
+
* Claude encodes paths: /Users/foo/bar → -Users-foo-bar
|
|
5
|
+
* Since '-' replaces '/' and project names can contain '-',
|
|
6
|
+
* we use filesystem checks to find the correct last segment.
|
|
7
|
+
*/
|
|
8
|
+
export declare function extractProjectName(dirName: string): string;
|
|
3
9
|
export declare function getDateKey(timestamp: string, tz: string): string;
|
|
4
10
|
export declare function getHourKey(timestamp: string, tz: string): string;
|
|
5
11
|
export declare function getDailyResponse(project?: string | null, tz?: string): DailyResponse;
|
|
@@ -3,18 +3,18 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
const MODEL_PRICING = {
|
|
5
5
|
// Claude 4.6
|
|
6
|
-
'claude-opus-4-6': { inputPer1M: 15, cacheReadPer1M: 1.50, outputPer1M: 75 },
|
|
7
|
-
'claude-sonnet-4-6': { inputPer1M: 3, cacheReadPer1M: 0.30, outputPer1M: 15 },
|
|
6
|
+
'claude-opus-4-6': { inputPer1M: 15, cacheCreationPer1M: 18.75, cacheReadPer1M: 1.50, outputPer1M: 75 },
|
|
7
|
+
'claude-sonnet-4-6': { inputPer1M: 3, cacheCreationPer1M: 3.75, cacheReadPer1M: 0.30, outputPer1M: 15 },
|
|
8
8
|
// Claude 4.5
|
|
9
|
-
'claude-sonnet-4-5-20250514': { inputPer1M: 3, cacheReadPer1M: 0.30, outputPer1M: 15 },
|
|
10
|
-
'claude-haiku-4-5-20251001': { inputPer1M: 0.80, cacheReadPer1M: 0.08, outputPer1M: 4 },
|
|
9
|
+
'claude-sonnet-4-5-20250514': { inputPer1M: 3, cacheCreationPer1M: 3.75, cacheReadPer1M: 0.30, outputPer1M: 15 },
|
|
10
|
+
'claude-haiku-4-5-20251001': { inputPer1M: 0.80, cacheCreationPer1M: 1, cacheReadPer1M: 0.08, outputPer1M: 4 },
|
|
11
11
|
// Older Claude models
|
|
12
|
-
'claude-3-5-sonnet-20241022': { inputPer1M: 3, cacheReadPer1M: 0.30, outputPer1M: 15 },
|
|
13
|
-
'claude-3-5-haiku-20241022': { inputPer1M: 0.80, cacheReadPer1M: 0.08, outputPer1M: 4 },
|
|
14
|
-
'claude-3-opus-20240229': { inputPer1M: 15, cacheReadPer1M: 1.50, outputPer1M: 75 },
|
|
15
|
-
'claude-3-haiku-20240307': { inputPer1M: 0.25, cacheReadPer1M: 0.03, outputPer1M: 1.25 },
|
|
12
|
+
'claude-3-5-sonnet-20241022': { inputPer1M: 3, cacheCreationPer1M: 3.75, cacheReadPer1M: 0.30, outputPer1M: 15 },
|
|
13
|
+
'claude-3-5-haiku-20241022': { inputPer1M: 0.80, cacheCreationPer1M: 1, cacheReadPer1M: 0.08, outputPer1M: 4 },
|
|
14
|
+
'claude-3-opus-20240229': { inputPer1M: 15, cacheCreationPer1M: 18.75, cacheReadPer1M: 1.50, outputPer1M: 75 },
|
|
15
|
+
'claude-3-haiku-20240307': { inputPer1M: 0.25, cacheCreationPer1M: 0.30, cacheReadPer1M: 0.03, outputPer1M: 1.25 },
|
|
16
16
|
};
|
|
17
|
-
const DEFAULT_PRICING = { inputPer1M: 3, cacheReadPer1M: 0.30, outputPer1M: 15 };
|
|
17
|
+
const DEFAULT_PRICING = { inputPer1M: 3, cacheCreationPer1M: 3.75, cacheReadPer1M: 0.30, outputPer1M: 15 };
|
|
18
18
|
function getPricing(model) {
|
|
19
19
|
// Try exact match first, then prefix match
|
|
20
20
|
if (MODEL_PRICING[model])
|
|
@@ -26,21 +26,73 @@ function getPricing(model) {
|
|
|
26
26
|
}
|
|
27
27
|
return DEFAULT_PRICING;
|
|
28
28
|
}
|
|
29
|
-
export function calculateCost(inputTokens, cacheReadTokens, outputTokens, model) {
|
|
29
|
+
export function calculateCost(inputTokens, cacheReadTokens, outputTokens, model, cacheCreationTokens = 0) {
|
|
30
30
|
const p = getPricing(model);
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
return (inputTokens / 1_000_000) * p.inputPer1M
|
|
32
|
+
+ (cacheCreationTokens / 1_000_000) * p.cacheCreationPer1M
|
|
33
33
|
+ (cacheReadTokens / 1_000_000) * p.cacheReadPer1M
|
|
34
34
|
+ (outputTokens / 1_000_000) * p.outputPer1M;
|
|
35
35
|
}
|
|
36
|
+
function totalClaudeTokens(inputTokens, outputTokens, cacheCreationTokens, cacheReadTokens) {
|
|
37
|
+
return inputTokens + outputTokens + cacheCreationTokens + cacheReadTokens;
|
|
38
|
+
}
|
|
36
39
|
// ---------------------------------------------------------------------------
|
|
37
40
|
// JSONL parsing with mtime cache
|
|
38
41
|
// ---------------------------------------------------------------------------
|
|
39
42
|
const CLAUDE_PROJECTS_DIR = join(homedir(), '.claude', 'projects');
|
|
40
43
|
const fileCache = new Map();
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
const projectNameCache = new Map();
|
|
45
|
+
/** Decode Claude's encoded project directory name.
|
|
46
|
+
* Claude encodes paths: /Users/foo/bar → -Users-foo-bar
|
|
47
|
+
* Since '-' replaces '/' and project names can contain '-',
|
|
48
|
+
* we use filesystem checks to find the correct last segment.
|
|
49
|
+
*/
|
|
50
|
+
export function extractProjectName(dirName) {
|
|
51
|
+
if (!dirName.startsWith('-'))
|
|
52
|
+
return dirName;
|
|
53
|
+
const cached = projectNameCache.get(dirName);
|
|
54
|
+
if (cached)
|
|
55
|
+
return cached;
|
|
56
|
+
const segments = dirName.replace(/^-/, '').split('-').filter(Boolean);
|
|
57
|
+
if (segments.length === 0) {
|
|
58
|
+
projectNameCache.set(dirName, dirName);
|
|
59
|
+
return dirName;
|
|
60
|
+
}
|
|
61
|
+
if (segments.length === 1) {
|
|
62
|
+
projectNameCache.set(dirName, segments[0]);
|
|
63
|
+
return segments[0];
|
|
64
|
+
}
|
|
65
|
+
let bestName = segments[segments.length - 1];
|
|
66
|
+
// Try from right: find the longest last segment that forms a valid path
|
|
67
|
+
for (let splitAt = segments.length - 1; splitAt >= 1; splitAt--) {
|
|
68
|
+
const parentSegments = segments.slice(0, splitAt);
|
|
69
|
+
const candidateName = segments.slice(splitAt).join('-');
|
|
70
|
+
// Build parent path, handling hidden directories (dot prefix)
|
|
71
|
+
let parentPath = '/';
|
|
72
|
+
let valid = true;
|
|
73
|
+
for (const seg of parentSegments) {
|
|
74
|
+
const regular = join(parentPath, seg);
|
|
75
|
+
const hidden = join(parentPath, '.' + seg);
|
|
76
|
+
if (existsSync(regular)) {
|
|
77
|
+
parentPath = regular;
|
|
78
|
+
}
|
|
79
|
+
else if (existsSync(hidden)) {
|
|
80
|
+
parentPath = hidden;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
valid = false;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (!valid)
|
|
88
|
+
continue;
|
|
89
|
+
if (existsSync(join(parentPath, candidateName)) || existsSync(join(parentPath, '.' + candidateName))) {
|
|
90
|
+
bestName = candidateName;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
projectNameCache.set(dirName, bestName);
|
|
95
|
+
return bestName;
|
|
44
96
|
}
|
|
45
97
|
function matchesProject(dirName, filter) {
|
|
46
98
|
return extractProjectName(dirName) === extractProjectName(filter);
|
|
@@ -111,7 +163,7 @@ function parseAllSessions(project) {
|
|
|
111
163
|
const outputTokens = usage.output_tokens || 0;
|
|
112
164
|
const cacheCreationTokens = usage.cache_creation_input_tokens || 0;
|
|
113
165
|
const cacheReadTokens = usage.cache_read_input_tokens || 0;
|
|
114
|
-
const totalTokens = inputTokens
|
|
166
|
+
const totalTokens = totalClaudeTokens(inputTokens, outputTokens, cacheCreationTokens, cacheReadTokens);
|
|
115
167
|
if (totalTokens === 0)
|
|
116
168
|
continue;
|
|
117
169
|
entries.push({
|
|
@@ -199,8 +251,8 @@ export function getDailyResponse(project, tz = DEFAULT_TZ) {
|
|
|
199
251
|
agg.outputTokens += e.outputTokens;
|
|
200
252
|
agg.cacheCreationTokens += e.cacheCreationTokens;
|
|
201
253
|
agg.cacheReadTokens += e.cacheReadTokens;
|
|
202
|
-
agg.totalTokens += e.inputTokens
|
|
203
|
-
const cost = calculateCost(e.inputTokens, e.cacheReadTokens, e.outputTokens, e.model);
|
|
254
|
+
agg.totalTokens += totalClaudeTokens(e.inputTokens, e.outputTokens, e.cacheCreationTokens, e.cacheReadTokens);
|
|
255
|
+
const cost = calculateCost(e.inputTokens, e.cacheReadTokens, e.outputTokens, e.model, e.cacheCreationTokens);
|
|
204
256
|
agg.totalCost += cost;
|
|
205
257
|
if (!agg.models.has(e.model)) {
|
|
206
258
|
agg.models.set(e.model, { input: 0, output: 0, cacheCreation: 0, cacheRead: 0, cost: 0 });
|
|
@@ -228,7 +280,7 @@ export function getProjectsResponse(tz = DEFAULT_TZ) {
|
|
|
228
280
|
const projectMap = new Map();
|
|
229
281
|
for (const e of entries) {
|
|
230
282
|
const date = getDateKey(e.timestamp, tz);
|
|
231
|
-
const projectName = e.projectDir;
|
|
283
|
+
const projectName = extractProjectName(e.projectDir);
|
|
232
284
|
if (!projectMap.has(projectName)) {
|
|
233
285
|
projectMap.set(projectName, new Map());
|
|
234
286
|
}
|
|
@@ -245,8 +297,8 @@ export function getProjectsResponse(tz = DEFAULT_TZ) {
|
|
|
245
297
|
agg.outputTokens += e.outputTokens;
|
|
246
298
|
agg.cacheCreationTokens += e.cacheCreationTokens;
|
|
247
299
|
agg.cacheReadTokens += e.cacheReadTokens;
|
|
248
|
-
agg.totalTokens += e.inputTokens
|
|
249
|
-
const cost = calculateCost(e.inputTokens, e.cacheReadTokens, e.outputTokens, e.model);
|
|
300
|
+
agg.totalTokens += totalClaudeTokens(e.inputTokens, e.outputTokens, e.cacheCreationTokens, e.cacheReadTokens);
|
|
301
|
+
const cost = calculateCost(e.inputTokens, e.cacheReadTokens, e.outputTokens, e.model, e.cacheCreationTokens);
|
|
250
302
|
agg.totalCost += cost;
|
|
251
303
|
if (!agg.models.has(e.model)) {
|
|
252
304
|
agg.models.set(e.model, { input: 0, output: 0, cacheCreation: 0, cacheRead: 0, cost: 0 });
|
|
@@ -282,13 +334,13 @@ export function getBlocksResponse(project, tz = DEFAULT_TZ) {
|
|
|
282
334
|
bucket.outputTokens += e.outputTokens;
|
|
283
335
|
bucket.cacheCreationTokens += e.cacheCreationTokens;
|
|
284
336
|
bucket.cacheReadTokens += e.cacheReadTokens;
|
|
285
|
-
bucket.costUSD += calculateCost(e.inputTokens, e.cacheReadTokens, e.outputTokens, e.model);
|
|
337
|
+
bucket.costUSD += calculateCost(e.inputTokens, e.cacheReadTokens, e.outputTokens, e.model, e.cacheCreationTokens);
|
|
286
338
|
bucket.models.add(e.model);
|
|
287
339
|
}
|
|
288
340
|
const blocks = [];
|
|
289
341
|
let idx = 0;
|
|
290
342
|
for (const [hourKey, bucket] of hourMap) {
|
|
291
|
-
const totalTokens = bucket.inputTokens
|
|
343
|
+
const totalTokens = totalClaudeTokens(bucket.inputTokens, bucket.outputTokens, bucket.cacheCreationTokens, bucket.cacheReadTokens);
|
|
292
344
|
blocks.push({
|
|
293
345
|
id: `claude-${idx}`,
|
|
294
346
|
startTime: `${hourKey}:00:00`,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DailyResponse, ProjectsResponse, BlocksResponse } from '../shared/types.js';
|
|
2
|
-
interface ParsedTokenEvent {
|
|
2
|
+
export interface ParsedTokenEvent {
|
|
3
3
|
timestamp: string;
|
|
4
4
|
inputTokens: number;
|
|
5
5
|
cachedInputTokens: number;
|
|
@@ -37,10 +37,14 @@ export declare function scanCodexSessions(): string[];
|
|
|
37
37
|
export declare function parseCodexSession(filepath: string): ParsedSession | null;
|
|
38
38
|
/** Parse all Codex sessions. */
|
|
39
39
|
export declare function parseAllSessions(): ParsedSession[];
|
|
40
|
+
export declare function buildCodexResponsesFromSessions(sessions: ParsedSession[], options?: Partial<AggregateOptions>): {
|
|
41
|
+
daily: DailyResponse;
|
|
42
|
+
projects: ProjectsResponse;
|
|
43
|
+
blocks: BlocksResponse;
|
|
44
|
+
};
|
|
40
45
|
/** Aggregate and return DailyResponse format (for /daily?agent=codex) */
|
|
41
46
|
export declare function getDailyResponse(options?: Partial<AggregateOptions>): DailyResponse;
|
|
42
47
|
/** Aggregate and return ProjectsResponse format (for /projects?agent=codex) */
|
|
43
48
|
export declare function getProjectsResponse(options?: Partial<AggregateOptions>): ProjectsResponse;
|
|
44
49
|
/** Aggregate and return BlocksResponse format (hourly, for /blocks?agent=codex) */
|
|
45
50
|
export declare function getBlocksResponse(options?: Partial<AggregateOptions>): BlocksResponse;
|
|
46
|
-
export {};
|