@zhangferry-dev/tokendash 1.0.1 → 1.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/dist/client/assets/index-BaY47OM2.css +1 -0
- package/dist/client/assets/{index-DOeZtR1c.js → index-CVEOcjaP.js} +50 -50
- package/dist/client/index.html +3 -3
- package/dist/server/ccusage.d.ts +0 -1
- package/dist/server/ccusage.js +6 -10
- package/dist/server/claudeBlocksParser.d.ts +6 -0
- package/dist/server/claudeBlocksParser.js +150 -0
- package/dist/server/codexParser.d.ts +47 -0
- package/dist/server/codexParser.js +379 -0
- package/dist/server/codexPricing.d.ts +32 -0
- package/dist/server/codexPricing.js +43 -0
- package/dist/server/index.js +97 -12
- package/dist/server/routes/blocks.js +28 -3
- package/dist/server/routes/daily.js +5 -7
- package/dist/server/routes/projects.js +5 -7
- package/dist/shared/schemas.d.ts +17 -17
- package/package.json +2 -2
- package/dist/client/assets/index-C9UxEhwo.css +0 -1
- package/dist/server/codexNormalizer.d.ts +0 -4
- package/dist/server/codexNormalizer.js +0 -50
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex token pricing configuration.
|
|
3
|
+
*
|
|
4
|
+
* Pricing formula (confirmed by reverse-engineering @ccusage/codex):
|
|
5
|
+
* cost = (inputTokens - cachedInputTokens) * input_rate
|
|
6
|
+
* + cachedInputTokens * cached_rate
|
|
7
|
+
* + outputTokens * output_rate
|
|
8
|
+
*
|
|
9
|
+
* Reasoning tokens are NOT billed separately (included in outputTokens).
|
|
10
|
+
*
|
|
11
|
+
* Update rates from https://openai.com/api/pricing/ when models change.
|
|
12
|
+
* All prices are USD per 1M tokens.
|
|
13
|
+
*/
|
|
14
|
+
const MODEL_PRICING = {
|
|
15
|
+
'gpt-5.4': {
|
|
16
|
+
inputPer1M: 2.50,
|
|
17
|
+
cachedInputPer1M: 0.25,
|
|
18
|
+
outputPer1M: 15.00,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
const DEFAULT_PRICING = {
|
|
22
|
+
inputPer1M: 2.50,
|
|
23
|
+
cachedInputPer1M: 0.25,
|
|
24
|
+
outputPer1M: 15.00,
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Calculate cost in USD from token counts and model pricing.
|
|
28
|
+
* Matches the @ccusage/codex calculateCostUSD function exactly.
|
|
29
|
+
*/
|
|
30
|
+
export function calculateCost(tokens, models) {
|
|
31
|
+
const model = [...models][0] ?? '';
|
|
32
|
+
const pricing = MODEL_PRICING[model] ?? DEFAULT_PRICING;
|
|
33
|
+
const nonCachedInput = Math.max(tokens.inputTokens - tokens.cachedInputTokens, 0);
|
|
34
|
+
const cachedInput = Math.min(tokens.cachedInputTokens, tokens.inputTokens);
|
|
35
|
+
const outputTokens = tokens.outputTokens;
|
|
36
|
+
const inputCost = (nonCachedInput / 1_000_000) * pricing.inputPer1M;
|
|
37
|
+
const cachedCost = (cachedInput / 1_000_000) * pricing.cachedInputPer1M;
|
|
38
|
+
const outputCost = (outputTokens / 1_000_000) * pricing.outputPer1M;
|
|
39
|
+
return inputCost + cachedCost + outputCost;
|
|
40
|
+
}
|
|
41
|
+
export function getModelPricing(model) {
|
|
42
|
+
return MODEL_PRICING[model] ?? DEFAULT_PRICING;
|
|
43
|
+
}
|
package/dist/server/index.js
CHANGED
|
@@ -1,19 +1,52 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
2
3
|
import { registerApiRoutes } from './routes/api.js';
|
|
3
4
|
import { ensureUsageToolsReady } from './ccusage.js';
|
|
4
5
|
import open from 'open';
|
|
6
|
+
const CLI_USAGE = [
|
|
7
|
+
'Usage:',
|
|
8
|
+
' tokendash',
|
|
9
|
+
' tokendash --version',
|
|
10
|
+
' tokendash --port <number> [--no-open]',
|
|
11
|
+
].join('\n');
|
|
12
|
+
function getPackageVersion() {
|
|
13
|
+
const packageJson = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8'));
|
|
14
|
+
return packageJson.version ?? 'unknown';
|
|
15
|
+
}
|
|
16
|
+
function exitWithCliError(message) {
|
|
17
|
+
console.error(message);
|
|
18
|
+
console.error(`\n${CLI_USAGE}`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
5
21
|
function parseCliArgs() {
|
|
6
22
|
const args = process.argv.slice(2);
|
|
7
23
|
const result = {};
|
|
24
|
+
if (args.length === 1 && (args[0] === '--version' || args[0] === '-v')) {
|
|
25
|
+
result.showVersion = true;
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
8
28
|
for (let i = 0; i < args.length; i++) {
|
|
9
29
|
const arg = args[i];
|
|
10
|
-
if (arg === '--
|
|
11
|
-
|
|
30
|
+
if (arg === '--version' || arg === '-v') {
|
|
31
|
+
exitWithCliError('The --version flag must be used by itself.');
|
|
32
|
+
}
|
|
33
|
+
if (arg === '--port') {
|
|
34
|
+
if (i + 1 >= args.length) {
|
|
35
|
+
exitWithCliError('Missing value for --port.');
|
|
36
|
+
}
|
|
37
|
+
const value = parseInt(args[i + 1], 10);
|
|
38
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
39
|
+
exitWithCliError(`Invalid port value: ${args[i + 1]}`);
|
|
40
|
+
}
|
|
41
|
+
result.port = value;
|
|
12
42
|
i++;
|
|
13
43
|
}
|
|
14
44
|
else if (arg === '--no-open') {
|
|
15
45
|
result.noOpen = true;
|
|
16
46
|
}
|
|
47
|
+
else {
|
|
48
|
+
exitWithCliError(`Unsupported argument: ${arg}`);
|
|
49
|
+
}
|
|
17
50
|
}
|
|
18
51
|
return result;
|
|
19
52
|
}
|
|
@@ -28,10 +61,55 @@ async function ensureUsageSupportAvailable() {
|
|
|
28
61
|
return false;
|
|
29
62
|
}
|
|
30
63
|
}
|
|
64
|
+
function resolvePort(value) {
|
|
65
|
+
return Number.isInteger(value) && value && value > 0 ? value : 3456;
|
|
66
|
+
}
|
|
67
|
+
function listen(app, port) {
|
|
68
|
+
return new Promise((resolve, reject) => {
|
|
69
|
+
const server = app.listen(port);
|
|
70
|
+
const handleListening = () => {
|
|
71
|
+
cleanup();
|
|
72
|
+
resolve(server);
|
|
73
|
+
};
|
|
74
|
+
const handleError = (error) => {
|
|
75
|
+
cleanup();
|
|
76
|
+
reject(error);
|
|
77
|
+
};
|
|
78
|
+
const cleanup = () => {
|
|
79
|
+
server.off('listening', handleListening);
|
|
80
|
+
server.off('error', handleError);
|
|
81
|
+
};
|
|
82
|
+
server.once('listening', handleListening);
|
|
83
|
+
server.once('error', handleError);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
async function listenWithPortFallback(app, preferredPort) {
|
|
87
|
+
let port = preferredPort;
|
|
88
|
+
for (let attempt = 0; attempt < 20; attempt++, port++) {
|
|
89
|
+
try {
|
|
90
|
+
const server = await listen(app, port);
|
|
91
|
+
return { server, port, usedFallback: port !== preferredPort };
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
const err = error;
|
|
95
|
+
if (err.code !== 'EADDRINUSE') {
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
throw new Error(`Could not find an available port starting from ${preferredPort}`);
|
|
101
|
+
}
|
|
31
102
|
async function main() {
|
|
32
103
|
const args = parseCliArgs();
|
|
33
|
-
|
|
104
|
+
if (args.showVersion) {
|
|
105
|
+
console.log(getPackageVersion());
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const version = getPackageVersion();
|
|
109
|
+
const preferredPort = resolvePort(args.port ?? (process.env.PORT ? parseInt(process.env.PORT, 10) : undefined));
|
|
34
110
|
const shouldOpenBrowser = !args.noOpen;
|
|
111
|
+
console.log(`Starting tokendash v${version}...`);
|
|
112
|
+
console.log(`Checking local usage data sources...`);
|
|
35
113
|
const isUsageSupportAvailable = await ensureUsageSupportAvailable();
|
|
36
114
|
if (!isUsageSupportAvailable) {
|
|
37
115
|
process.exit(1);
|
|
@@ -53,24 +131,31 @@ async function main() {
|
|
|
53
131
|
res.sendFile(clientIndexPath);
|
|
54
132
|
});
|
|
55
133
|
}
|
|
56
|
-
const server = app
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
134
|
+
const { server, port, usedFallback } = await listenWithPortFallback(app, preferredPort);
|
|
135
|
+
if (usedFallback) {
|
|
136
|
+
console.warn(`tokendash detected that port ${preferredPort} is already in use, switched to http://localhost:${port}`);
|
|
137
|
+
}
|
|
138
|
+
console.log(`tokendash running on http://localhost:${port}`);
|
|
139
|
+
console.log(`API available at http://localhost:${port}/api`);
|
|
140
|
+
if (isProduction) {
|
|
141
|
+
console.log('Serving production build');
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
console.log('Development mode - use "npm run dev" for full dev experience');
|
|
145
|
+
}
|
|
65
146
|
// Open browser if requested
|
|
66
147
|
if (shouldOpenBrowser) {
|
|
67
148
|
// Small delay to ensure server is ready
|
|
68
149
|
setTimeout(() => {
|
|
150
|
+
console.log('Opening dashboard in your browser...');
|
|
69
151
|
open(`http://localhost:${port}`).catch((err) => {
|
|
70
152
|
console.warn('Could not open browser:', err.message);
|
|
71
153
|
});
|
|
72
154
|
}, 100);
|
|
73
155
|
}
|
|
156
|
+
else {
|
|
157
|
+
console.log('Browser auto-open disabled (--no-open)');
|
|
158
|
+
}
|
|
74
159
|
// Graceful shutdown
|
|
75
160
|
process.on('SIGTERM', () => {
|
|
76
161
|
server.close(() => {
|
|
@@ -1,15 +1,40 @@
|
|
|
1
1
|
import { runCcusage } from '../ccusage.js';
|
|
2
2
|
import { cache } from '../cache.js';
|
|
3
3
|
import { validateBlocks } from '../../shared/schemas.js';
|
|
4
|
-
import {
|
|
4
|
+
import { getBlocksResponse } from '../codexParser.js';
|
|
5
|
+
import { getClaudeBlocksByProject } from '../claudeBlocksParser.js';
|
|
5
6
|
export async function getBlocks(req, res) {
|
|
6
7
|
const agent = req.query.agent || 'claude';
|
|
7
|
-
const
|
|
8
|
+
const project = req.query.project || undefined;
|
|
8
9
|
try {
|
|
9
10
|
if (agent === 'codex') {
|
|
10
|
-
|
|
11
|
+
const projectCacheKey = `blocks:${agent}:${project || 'all'}`;
|
|
12
|
+
const cached = cache.get(projectCacheKey);
|
|
13
|
+
if (cached) {
|
|
14
|
+
res.json(cached);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const data = getBlocksResponse({ project: project || null });
|
|
18
|
+
cache.set(projectCacheKey, data);
|
|
19
|
+
res.json(data);
|
|
11
20
|
return;
|
|
12
21
|
}
|
|
22
|
+
// Claude Code with project filter: use custom JSONL parser
|
|
23
|
+
if (project) {
|
|
24
|
+
const projectCacheKey = `blocks:claude:${project}`;
|
|
25
|
+
const cached = cache.get(projectCacheKey);
|
|
26
|
+
if (cached) {
|
|
27
|
+
res.json(cached);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const blocks = getClaudeBlocksByProject(project);
|
|
31
|
+
const data = { blocks };
|
|
32
|
+
cache.set(projectCacheKey, data);
|
|
33
|
+
res.json(data);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
// Claude Code without project filter: use ccusage blocks
|
|
37
|
+
const cacheKey = `blocks:${agent}`;
|
|
13
38
|
const cached = cache.get(cacheKey);
|
|
14
39
|
if (cached) {
|
|
15
40
|
res.json(cached);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { runCcusage
|
|
1
|
+
import { runCcusage } from '../ccusage.js';
|
|
2
2
|
import { cache } from '../cache.js';
|
|
3
3
|
import { validateDaily } from '../../shared/schemas.js';
|
|
4
|
-
import {
|
|
4
|
+
import { getDailyResponse } from '../codexParser.js';
|
|
5
5
|
export async function getDaily(req, res) {
|
|
6
6
|
const agent = req.query.agent || 'claude';
|
|
7
7
|
const cacheKey = `daily:${agent}`;
|
|
@@ -12,11 +12,9 @@ export async function getDaily(req, res) {
|
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
if (agent === 'codex') {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
cache.set(cacheKey, normalized);
|
|
19
|
-
res.json(normalized);
|
|
15
|
+
const data = getDailyResponse();
|
|
16
|
+
cache.set(cacheKey, data);
|
|
17
|
+
res.json(data);
|
|
20
18
|
}
|
|
21
19
|
else {
|
|
22
20
|
const stdout = await runCcusage(['daily', '--breakdown']);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { runCcusage
|
|
1
|
+
import { runCcusage } from '../ccusage.js';
|
|
2
2
|
import { cache } from '../cache.js';
|
|
3
3
|
import { validateProjects } from '../../shared/schemas.js';
|
|
4
|
-
import {
|
|
4
|
+
import { getProjectsResponse } from '../codexParser.js';
|
|
5
5
|
export async function getProjects(req, res) {
|
|
6
6
|
const agent = req.query.agent || 'claude';
|
|
7
7
|
const cacheKey = `projects:${agent}`;
|
|
@@ -12,11 +12,9 @@ export async function getProjects(req, res) {
|
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
if (agent === 'codex') {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
cache.set(cacheKey, normalized);
|
|
19
|
-
res.json(normalized);
|
|
15
|
+
const data = getProjectsResponse();
|
|
16
|
+
cache.set(cacheKey, data);
|
|
17
|
+
res.json(data);
|
|
20
18
|
}
|
|
21
19
|
else {
|
|
22
20
|
const stdout = await runCcusage(['daily', '--instances', '--breakdown']);
|
package/dist/shared/schemas.d.ts
CHANGED
|
@@ -53,11 +53,11 @@ export declare const DailyEntrySchema: z.ZodObject<{
|
|
|
53
53
|
cost?: number | undefined;
|
|
54
54
|
}>, "many">>;
|
|
55
55
|
}, "strip", z.ZodTypeAny, {
|
|
56
|
+
date: string;
|
|
56
57
|
inputTokens: number;
|
|
57
58
|
outputTokens: number;
|
|
58
59
|
cacheCreationTokens: number;
|
|
59
60
|
cacheReadTokens: number;
|
|
60
|
-
date: string;
|
|
61
61
|
totalTokens: number;
|
|
62
62
|
totalCost: number;
|
|
63
63
|
modelsUsed: string[];
|
|
@@ -142,11 +142,11 @@ export declare const DailyResponseSchema: z.ZodObject<{
|
|
|
142
142
|
cost?: number | undefined;
|
|
143
143
|
}>, "many">>;
|
|
144
144
|
}, "strip", z.ZodTypeAny, {
|
|
145
|
+
date: string;
|
|
145
146
|
inputTokens: number;
|
|
146
147
|
outputTokens: number;
|
|
147
148
|
cacheCreationTokens: number;
|
|
148
149
|
cacheReadTokens: number;
|
|
149
|
-
date: string;
|
|
150
150
|
totalTokens: number;
|
|
151
151
|
totalCost: number;
|
|
152
152
|
modelsUsed: string[];
|
|
@@ -200,11 +200,11 @@ export declare const DailyResponseSchema: z.ZodObject<{
|
|
|
200
200
|
}>;
|
|
201
201
|
}, "strip", z.ZodTypeAny, {
|
|
202
202
|
daily: {
|
|
203
|
+
date: string;
|
|
203
204
|
inputTokens: number;
|
|
204
205
|
outputTokens: number;
|
|
205
206
|
cacheCreationTokens: number;
|
|
206
207
|
cacheReadTokens: number;
|
|
207
|
-
date: string;
|
|
208
208
|
totalTokens: number;
|
|
209
209
|
totalCost: number;
|
|
210
210
|
modelsUsed: string[];
|
|
@@ -287,11 +287,11 @@ export declare const ProjectEntrySchema: z.ZodObject<{
|
|
|
287
287
|
cost?: number | undefined;
|
|
288
288
|
}>, "many">>;
|
|
289
289
|
}, "strip", z.ZodTypeAny, {
|
|
290
|
+
date: string;
|
|
290
291
|
inputTokens: number;
|
|
291
292
|
outputTokens: number;
|
|
292
293
|
cacheCreationTokens: number;
|
|
293
294
|
cacheReadTokens: number;
|
|
294
|
-
date: string;
|
|
295
295
|
totalTokens: number;
|
|
296
296
|
totalCost: number;
|
|
297
297
|
modelsUsed: string[];
|
|
@@ -324,11 +324,11 @@ export declare const ProjectEntrySchema: z.ZodObject<{
|
|
|
324
324
|
}, "strip", z.ZodTypeAny, {
|
|
325
325
|
projectPath: string;
|
|
326
326
|
instances: {
|
|
327
|
+
date: string;
|
|
327
328
|
inputTokens: number;
|
|
328
329
|
outputTokens: number;
|
|
329
330
|
cacheCreationTokens: number;
|
|
330
331
|
cacheReadTokens: number;
|
|
331
|
-
date: string;
|
|
332
332
|
totalTokens: number;
|
|
333
333
|
totalCost: number;
|
|
334
334
|
modelsUsed: string[];
|
|
@@ -395,11 +395,11 @@ export declare const ProjectsResponseSchema: z.ZodObject<{
|
|
|
395
395
|
cost?: number | undefined;
|
|
396
396
|
}>, "many">>;
|
|
397
397
|
}, "strip", z.ZodTypeAny, {
|
|
398
|
+
date: string;
|
|
398
399
|
inputTokens: number;
|
|
399
400
|
outputTokens: number;
|
|
400
401
|
cacheCreationTokens: number;
|
|
401
402
|
cacheReadTokens: number;
|
|
402
|
-
date: string;
|
|
403
403
|
totalTokens: number;
|
|
404
404
|
totalCost: number;
|
|
405
405
|
modelsUsed: string[];
|
|
@@ -431,11 +431,11 @@ export declare const ProjectsResponseSchema: z.ZodObject<{
|
|
|
431
431
|
}>, "many">>>>;
|
|
432
432
|
}, "strip", z.ZodTypeAny, {
|
|
433
433
|
projects: Record<string, {
|
|
434
|
+
date: string;
|
|
434
435
|
inputTokens: number;
|
|
435
436
|
outputTokens: number;
|
|
436
437
|
cacheCreationTokens: number;
|
|
437
438
|
cacheReadTokens: number;
|
|
438
|
-
date: string;
|
|
439
439
|
totalTokens: number;
|
|
440
440
|
totalCost: number;
|
|
441
441
|
modelsUsed: string[];
|
|
@@ -470,11 +470,11 @@ export declare const ProjectsResponseSchema: z.ZodObject<{
|
|
|
470
470
|
}>;
|
|
471
471
|
export declare function validateDaily(data: unknown): {
|
|
472
472
|
daily: {
|
|
473
|
+
date: string;
|
|
473
474
|
inputTokens: number;
|
|
474
475
|
outputTokens: number;
|
|
475
476
|
cacheCreationTokens: number;
|
|
476
477
|
cacheReadTokens: number;
|
|
477
|
-
date: string;
|
|
478
478
|
totalTokens: number;
|
|
479
479
|
totalCost: number;
|
|
480
480
|
modelsUsed: string[];
|
|
@@ -498,11 +498,11 @@ export declare function validateDaily(data: unknown): {
|
|
|
498
498
|
};
|
|
499
499
|
export declare function validateProjects(data: unknown): {
|
|
500
500
|
projects: Record<string, {
|
|
501
|
+
date: string;
|
|
501
502
|
inputTokens: number;
|
|
502
503
|
outputTokens: number;
|
|
503
504
|
cacheCreationTokens: number;
|
|
504
505
|
cacheReadTokens: number;
|
|
505
|
-
date: string;
|
|
506
506
|
totalTokens: number;
|
|
507
507
|
totalCost: number;
|
|
508
508
|
modelsUsed: string[];
|
|
@@ -546,8 +546,9 @@ export declare const BlocksResponseSchema: z.ZodObject<{
|
|
|
546
546
|
models: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
547
547
|
}, "strip", z.ZodTypeAny, {
|
|
548
548
|
entries: number;
|
|
549
|
-
totalTokens: number;
|
|
550
549
|
id: string;
|
|
550
|
+
models: string[];
|
|
551
|
+
totalTokens: number;
|
|
551
552
|
startTime: string;
|
|
552
553
|
endTime: string;
|
|
553
554
|
actualEndTime: string | null;
|
|
@@ -560,12 +561,12 @@ export declare const BlocksResponseSchema: z.ZodObject<{
|
|
|
560
561
|
cacheReadInputTokens: number;
|
|
561
562
|
};
|
|
562
563
|
costUSD: number;
|
|
563
|
-
models: string[];
|
|
564
564
|
}, {
|
|
565
565
|
id: string;
|
|
566
566
|
startTime: string;
|
|
567
567
|
endTime: string;
|
|
568
568
|
entries?: number | undefined;
|
|
569
|
+
models?: string[] | undefined;
|
|
569
570
|
totalTokens?: number | undefined;
|
|
570
571
|
actualEndTime?: string | null | undefined;
|
|
571
572
|
isActive?: boolean | undefined;
|
|
@@ -577,13 +578,13 @@ export declare const BlocksResponseSchema: z.ZodObject<{
|
|
|
577
578
|
cacheReadInputTokens?: number | undefined;
|
|
578
579
|
} | undefined;
|
|
579
580
|
costUSD?: number | undefined;
|
|
580
|
-
models?: string[] | undefined;
|
|
581
581
|
}>, "many">>;
|
|
582
582
|
}, "strip", z.ZodTypeAny, {
|
|
583
583
|
blocks: {
|
|
584
584
|
entries: number;
|
|
585
|
-
totalTokens: number;
|
|
586
585
|
id: string;
|
|
586
|
+
models: string[];
|
|
587
|
+
totalTokens: number;
|
|
587
588
|
startTime: string;
|
|
588
589
|
endTime: string;
|
|
589
590
|
actualEndTime: string | null;
|
|
@@ -596,7 +597,6 @@ export declare const BlocksResponseSchema: z.ZodObject<{
|
|
|
596
597
|
cacheReadInputTokens: number;
|
|
597
598
|
};
|
|
598
599
|
costUSD: number;
|
|
599
|
-
models: string[];
|
|
600
600
|
}[];
|
|
601
601
|
}, {
|
|
602
602
|
blocks?: {
|
|
@@ -604,6 +604,7 @@ export declare const BlocksResponseSchema: z.ZodObject<{
|
|
|
604
604
|
startTime: string;
|
|
605
605
|
endTime: string;
|
|
606
606
|
entries?: number | undefined;
|
|
607
|
+
models?: string[] | undefined;
|
|
607
608
|
totalTokens?: number | undefined;
|
|
608
609
|
actualEndTime?: string | null | undefined;
|
|
609
610
|
isActive?: boolean | undefined;
|
|
@@ -615,14 +616,14 @@ export declare const BlocksResponseSchema: z.ZodObject<{
|
|
|
615
616
|
cacheReadInputTokens?: number | undefined;
|
|
616
617
|
} | undefined;
|
|
617
618
|
costUSD?: number | undefined;
|
|
618
|
-
models?: string[] | undefined;
|
|
619
619
|
}[] | undefined;
|
|
620
620
|
}>;
|
|
621
621
|
export declare function validateBlocks(data: unknown): {
|
|
622
622
|
blocks: {
|
|
623
623
|
entries: number;
|
|
624
|
-
totalTokens: number;
|
|
625
624
|
id: string;
|
|
625
|
+
models: string[];
|
|
626
|
+
totalTokens: number;
|
|
626
627
|
startTime: string;
|
|
627
628
|
endTime: string;
|
|
628
629
|
actualEndTime: string | null;
|
|
@@ -635,6 +636,5 @@ export declare function validateBlocks(data: unknown): {
|
|
|
635
636
|
cacheReadInputTokens: number;
|
|
636
637
|
};
|
|
637
638
|
costUSD: number;
|
|
638
|
-
models: string[];
|
|
639
639
|
}[];
|
|
640
640
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhangferry-dev/tokendash",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Token Usage Analytics Dashboard",
|
|
6
6
|
"publishConfig": {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"main": "./dist/server/index.js",
|
|
10
10
|
"types": "./dist/server/index.d.ts",
|
|
11
11
|
"bin": {
|
|
12
|
-
"tokendash": "
|
|
12
|
+
"tokendash": "bin/tokendash.js"
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
15
|
"dist",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-duration:initial}}}@layer theme{:root,:host{--font-sans:"Geist", "Geist Fallback", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--font-mono:"Geist Mono", "Geist Mono Fallback", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-200:oklch(88.5% .062 18.334);--color-red-600:oklch(57.7% .245 27.325);--color-emerald-50:oklch(97.9% .021 166.113);--color-emerald-100:oklch(95% .052 163.051);--color-emerald-200:oklch(90.5% .093 164.15);--color-emerald-400:oklch(76.5% .177 163.223);--color-emerald-600:oklch(59.6% .145 163.225);--color-emerald-700:oklch(50.8% .118 165.612);--color-indigo-50:oklch(96.2% .018 272.314);--color-indigo-500:oklch(58.5% .233 277.117);--color-indigo-600:oklch(51.1% .262 276.966);--color-stone-50:oklch(98.5% .001 106.423);--color-stone-100:oklch(97% .001 106.424);--color-stone-200:oklch(92.3% .003 48.717);--color-stone-400:oklch(70.9% .01 56.259);--color-stone-500:oklch(55.3% .013 58.071);--color-stone-600:oklch(44.4% .011 73.639);--color-stone-700:oklch(37.4% .01 67.558);--color-stone-800:oklch(26.8% .007 34.298);--color-stone-900:oklch(21.6% .006 56.043);--color-white:#fff;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--font-weight-black:900;--tracking-tighter:-.05em;--tracking-tight:-.025em;--tracking-wide:.025em;--tracking-wider:.05em;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.absolute{position:absolute}.relative{position:relative}.static{position:static}.start{inset-inline-start:var(--spacing)}.bottom-full{bottom:100%}.left-1\/2{left:50%}.z-20{z-index:20}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2\.5{margin-top:calc(var(--spacing) * 2.5)}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.mb-8{margin-bottom:calc(var(--spacing) * 8)}.ml-10{margin-left:calc(var(--spacing) * 10)}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-4{height:calc(var(--spacing) * 4)}.h-8{height:calc(var(--spacing) * 8)}.h-10{height:calc(var(--spacing) * 10)}.h-20{height:calc(var(--spacing) * 20)}.h-48{height:calc(var(--spacing) * 48)}.h-72{height:calc(var(--spacing) * 72)}.h-\[22px\]{height:22px}.min-h-0{min-height:calc(var(--spacing) * 0)}.min-h-dvh{min-height:100dvh}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-4{width:calc(var(--spacing) * 4)}.w-8{width:calc(var(--spacing) * 8)}.w-48{width:calc(var(--spacing) * 48)}.w-72{width:calc(var(--spacing) * 72)}.w-fit{width:fit-content}.w-full{width:100%}.w-px{width:1px}.max-w-\[200px\]{max-width:200px}.max-w-\[220px\]{max-width:220px}.max-w-\[1440px\]{max-width:1440px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-around{justify-content:space-around}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-6{gap:calc(var(--spacing) * 6)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-\[3px\]{border-radius:3px}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-emerald-100\/50{border-color:#d0fae580}@supports (color:color-mix(in lab,red,red)){.border-emerald-100\/50{border-color:color-mix(in oklab,var(--color-emerald-100) 50%,transparent)}}.border-red-200\/60{border-color:#ffcaca99}@supports (color:color-mix(in lab,red,red)){.border-red-200\/60{border-color:color-mix(in oklab,var(--color-red-200) 60%,transparent)}}.border-stone-100{border-color:var(--color-stone-100)}.border-stone-200{border-color:var(--color-stone-200)}.border-stone-200\/40{border-color:#e7e5e466}@supports (color:color-mix(in lab,red,red)){.border-stone-200\/40{border-color:color-mix(in oklab,var(--color-stone-200) 40%,transparent)}}.border-stone-200\/50{border-color:#e7e5e480}@supports (color:color-mix(in lab,red,red)){.border-stone-200\/50{border-color:color-mix(in oklab,var(--color-stone-200) 50%,transparent)}}.bg-emerald-50\/50{background-color:#ecfdf580}@supports (color:color-mix(in lab,red,red)){.bg-emerald-50\/50{background-color:color-mix(in oklab,var(--color-emerald-50) 50%,transparent)}}.bg-emerald-200\/50{background-color:#a4f4cf80}@supports (color:color-mix(in lab,red,red)){.bg-emerald-200\/50{background-color:color-mix(in oklab,var(--color-emerald-200) 50%,transparent)}}.bg-indigo-50{background-color:var(--color-indigo-50)}.bg-red-50{background-color:var(--color-red-50)}.bg-stone-50\/40{background-color:#fafaf966}@supports (color:color-mix(in lab,red,red)){.bg-stone-50\/40{background-color:color-mix(in oklab,var(--color-stone-50) 40%,transparent)}}.bg-stone-100{background-color:var(--color-stone-100)}.bg-stone-200\/50{background-color:#e7e5e480}@supports (color:color-mix(in lab,red,red)){.bg-stone-200\/50{background-color:color-mix(in oklab,var(--color-stone-200) 50%,transparent)}}.bg-stone-200\/60{background-color:#e7e5e499}@supports (color:color-mix(in lab,red,red)){.bg-stone-200\/60{background-color:color-mix(in oklab,var(--color-stone-200) 60%,transparent)}}.bg-stone-800{background-color:var(--color-stone-800)}.bg-stone-900{background-color:var(--color-stone-900)}.bg-white{background-color:var(--color-white)}.bg-\[radial-gradient\(ellipse_at_top\,\#eef2ff_0\%\,\#faf9f7_35\%\,\#faf9f7_100\%\)\]{background-image:radial-gradient(at top,#eef2ff,#faf9f7 35%,#faf9f7)}.p-0\.5{padding:calc(var(--spacing) * .5)}.p-1{padding:calc(var(--spacing) * 1)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-10{padding-block:calc(var(--spacing) * 10)}.pt-0\.5{padding-top:calc(var(--spacing) * .5)}.pt-1{padding-top:calc(var(--spacing) * 1)}.pt-2\.5{padding-top:calc(var(--spacing) * 2.5)}.pb-0\.5{padding-bottom:calc(var(--spacing) * .5)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-black{--tw-font-weight:var(--font-weight-black);font-weight:var(--font-weight-black)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-tighter{--tw-tracking:var(--tracking-tighter);letter-spacing:var(--tracking-tighter)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.whitespace-nowrap{white-space:nowrap}.text-emerald-600{color:var(--color-emerald-600)}.text-emerald-600\/70{color:#009767b3}@supports (color:color-mix(in lab,red,red)){.text-emerald-600\/70{color:color-mix(in oklab,var(--color-emerald-600) 70%,transparent)}}.text-emerald-700\/80{color:#007956cc}@supports (color:color-mix(in lab,red,red)){.text-emerald-700\/80{color:color-mix(in oklab,var(--color-emerald-700) 80%,transparent)}}.text-indigo-500\/70{color:#625fffb3}@supports (color:color-mix(in lab,red,red)){.text-indigo-500\/70{color:color-mix(in oklab,var(--color-indigo-500) 70%,transparent)}}.text-indigo-600{color:var(--color-indigo-600)}.text-red-600{color:var(--color-red-600)}.text-stone-400{color:var(--color-stone-400)}.text-stone-500{color:var(--color-stone-500)}.text-stone-600{color:var(--color-stone-600)}.text-stone-700{color:var(--color-stone-700)}.text-stone-800{color:var(--color-stone-800)}.text-stone-900{color:var(--color-stone-900)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-0{opacity:0}.shadow-\[0_1px_3px_rgba\(0\,0\,0\,0\.1\)\]{--tw-shadow:0 1px 3px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_1px_3px_rgba\(120\,113\,108\,0\.06\)\]{--tw-shadow:0 1px 3px var(--tw-shadow-color,#78716c0f);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_8px_30px_rgba\(120\,113\,108\,0\.12\)\]{--tw-shadow:0 8px 30px var(--tw-shadow-color,#78716c1f);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-inner{--tw-shadow:inset 0 2px 4px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-stone-900\/5{--tw-ring-color:#1c19170d}@supports (color:color-mix(in lab,red,red)){.ring-stone-900\/5{--tw-ring-color:color-mix(in oklab, var(--color-stone-900) 5%, transparent)}}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.outline-none{--tw-outline-style:none;outline-style:none}@media(hover:hover){.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}.hover\:z-10:hover{z-index:10}.hover\:bg-stone-50:hover{background-color:var(--color-stone-50)}.hover\:bg-stone-50\/60:hover{background-color:#fafaf999}@supports (color:color-mix(in lab,red,red)){.hover\:bg-stone-50\/60:hover{background-color:color-mix(in oklab,var(--color-stone-50) 60%,transparent)}}.hover\:bg-stone-200\/50:hover{background-color:#e7e5e480}@supports (color:color-mix(in lab,red,red)){.hover\:bg-stone-200\/50:hover{background-color:color-mix(in oklab,var(--color-stone-200) 50%,transparent)}}.hover\:text-stone-800:hover{color:var(--color-stone-800)}.hover\:shadow-\[0_4px_12px_rgba\(120\,113\,108\,0\.09\)\]:hover{--tw-shadow:0 4px 12px var(--tw-shadow-color,#78716c17);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:ring-2:hover{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:ring-emerald-400:hover{--tw-ring-color:var(--color-emerald-400)}.hover\:ring-offset-1:hover{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}}.focus\:border-indigo-500:focus{border-color:var(--color-indigo-500)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-indigo-500\/20:focus{--tw-ring-color:#625fff33}@supports (color:color-mix(in lab,red,red)){.focus\:ring-indigo-500\/20:focus{--tw-ring-color:color-mix(in oklab, var(--color-indigo-500) 20%, transparent)}}@media(min-width:40rem){.sm\:block{display:block}}@media(min-width:48rem){.md\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}}@media(min-width:64rem){.lg\:col-span-2{grid-column:span 2/span 2}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}}}html,body{background-color:#faf9f7!important}@font-face{font-family:Geist;src:url(https://cdn.jsdelivr.net/npm/geist@1.3.1/dist/fonts/geist-sans/Geist-Variable.woff2)format("woff2");font-weight:100 900;font-display:swap;font-style:normal}@font-face{font-family:Geist Mono;src:url(https://cdn.jsdelivr.net/npm/geist@1.3.1/dist/fonts/geist-mono/GeistMono-Variable.woff2)format("woff2");font-weight:100 900;font-display:swap;font-style:normal}body:before{content:"";z-index:9999;pointer-events:none;opacity:.015;background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");background-repeat:repeat;background-size:200px 200px;position:fixed;top:0;right:0;bottom:0;left:0}.recharts-cartesian-grid-horizontal line,.recharts-cartesian-grid-vertical line{stroke:#e7e5e4}.recharts-text{fill:#78716c;font-family:var(--font-sans);font-size:11px}.recharts-legend-item-text{color:#57534e;font-size:12px}.recharts-tooltip-wrapper{outline:none}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:#c8c4be;border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#a8a29e}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.skeleton{background:linear-gradient(90deg,#e7e5e4 25%,#d6d3d1,#e7e5e4 75%) 0 0/200% 100%;animation:1.5s ease-in-out infinite shimmer}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-duration{syntax:"*";inherits:false}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { DailyResponse, ProjectsResponse, BlocksResponse } from '../shared/types.js';
|
|
2
|
-
export declare function normalizeCodexDailyResponse(data: unknown): DailyResponse;
|
|
3
|
-
export declare function normalizeCodexProjectsResponse(data: unknown): ProjectsResponse;
|
|
4
|
-
export declare function emptyBlocksResponse(): BlocksResponse;
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
function parseCodexDate(dateStr) {
|
|
2
|
-
// "Mar 31, 2026" → "2026-03-31"
|
|
3
|
-
const d = new Date(dateStr);
|
|
4
|
-
return d.toISOString().slice(0, 10);
|
|
5
|
-
}
|
|
6
|
-
function normalizeCodexDaily(entry) {
|
|
7
|
-
const modelBreakdowns = Object.entries(entry.models).map(([name, m]) => ({
|
|
8
|
-
modelName: name,
|
|
9
|
-
inputTokens: m.inputTokens,
|
|
10
|
-
outputTokens: m.outputTokens,
|
|
11
|
-
cacheCreationTokens: 0,
|
|
12
|
-
cacheReadTokens: m.cachedInputTokens,
|
|
13
|
-
cost: entry.costUSD / Object.keys(entry.models).length,
|
|
14
|
-
}));
|
|
15
|
-
return {
|
|
16
|
-
date: parseCodexDate(entry.date),
|
|
17
|
-
inputTokens: entry.inputTokens,
|
|
18
|
-
outputTokens: entry.outputTokens,
|
|
19
|
-
cacheCreationTokens: 0,
|
|
20
|
-
cacheReadTokens: entry.cachedInputTokens,
|
|
21
|
-
totalTokens: entry.totalTokens,
|
|
22
|
-
totalCost: entry.costUSD,
|
|
23
|
-
modelsUsed: Object.keys(entry.models),
|
|
24
|
-
modelBreakdowns,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
export function normalizeCodexDailyResponse(data) {
|
|
28
|
-
const codex = data;
|
|
29
|
-
return {
|
|
30
|
-
daily: (codex.daily || []).map(normalizeCodexDaily),
|
|
31
|
-
totals: {
|
|
32
|
-
inputTokens: codex.totals?.inputTokens ?? 0,
|
|
33
|
-
outputTokens: codex.totals?.outputTokens ?? 0,
|
|
34
|
-
cacheCreationTokens: 0,
|
|
35
|
-
cacheReadTokens: codex.totals?.cachedInputTokens ?? 0,
|
|
36
|
-
totalTokens: codex.totals?.totalTokens ?? 0,
|
|
37
|
-
totalCost: codex.totals?.costUSD ?? 0,
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
export function normalizeCodexProjectsResponse(data) {
|
|
42
|
-
const codex = data;
|
|
43
|
-
const entries = (codex.daily || []).map(normalizeCodexDaily);
|
|
44
|
-
return {
|
|
45
|
-
projects: { 'OpenAI Codex': entries },
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
export function emptyBlocksResponse() {
|
|
49
|
-
return { blocks: [] };
|
|
50
|
-
}
|