@zhangferry-dev/tokendash 1.1.4 → 1.2.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.
@@ -1,8 +1,8 @@
1
- import { runCcusage } from '../ccusage.js';
2
1
  import { cache } from '../cache.js';
3
2
  import { validateProjects } from '../../shared/schemas.js';
4
- import { getProjectsResponse } from '../codexParser.js';
3
+ import { getProjectsResponse as getCodexProjectsResponse } from '../codexParser.js';
5
4
  import { getProjectsResponse as getOpenClawProjectsResponse } from '../openclawParser.js';
5
+ import { getProjectsResponse as getClaudeProjectsResponse } from '../claudeJsonlParser.js';
6
6
  export async function getProjects(req, res) {
7
7
  const agent = req.query.agent || 'claude';
8
8
  const cacheKey = `projects:${agent}`;
@@ -12,24 +12,16 @@ export async function getProjects(req, res) {
12
12
  res.json(cached);
13
13
  return;
14
14
  }
15
- if (agent === 'codex') {
16
- const data = getProjectsResponse();
17
- cache.set(cacheKey, data);
18
- res.json(data);
19
- }
20
- else if (agent === 'openclaw') {
21
- const data = getOpenClawProjectsResponse();
22
- const validated = validateProjects(data);
23
- cache.set(cacheKey, validated);
24
- res.json(validated);
25
- }
26
- else {
27
- const stdout = await runCcusage(['daily', '--instances', '--breakdown']);
28
- const data = JSON.parse(stdout);
29
- const validated = validateProjects(data);
30
- cache.set(cacheKey, validated);
31
- res.json(validated);
15
+ // Stale-while-revalidate
16
+ const stale = cache.getStale(cacheKey);
17
+ if (stale) {
18
+ refreshProjectsCache(agent, cacheKey);
19
+ res.json(stale);
20
+ return;
32
21
  }
22
+ const data = fetchProjectsData(agent);
23
+ cache.set(cacheKey, data);
24
+ res.json(data);
33
25
  }
34
26
  catch (error) {
35
27
  const message = error instanceof Error ? error.message : 'Unknown error';
@@ -40,3 +32,20 @@ export async function getProjects(req, res) {
40
32
  });
41
33
  }
42
34
  }
35
+ function fetchProjectsData(agent) {
36
+ if (agent === 'codex') {
37
+ return getCodexProjectsResponse();
38
+ }
39
+ else if (agent === 'openclaw') {
40
+ return validateProjects(getOpenClawProjectsResponse());
41
+ }
42
+ else {
43
+ // Claude Code: parse JSONL directly (fast, no CLI)
44
+ return validateProjects(getClaudeProjectsResponse());
45
+ }
46
+ }
47
+ function refreshProjectsCache(agent, cacheKey) {
48
+ Promise.resolve()
49
+ .then(() => { const data = fetchProjectsData(agent); cache.set(cacheKey, data); })
50
+ .catch(err => console.error('Background refresh failed (projects):', err));
51
+ }
@@ -1,2 +1,2 @@
1
1
  import { type Request, type Response } from 'express';
2
- export declare function getSession(_req: Request, res: Response): Promise<void>;
2
+ export declare function getSession(req: Request, res: Response): Promise<void>;
@@ -1,24 +1,41 @@
1
- import { runCcusage } from '../ccusage.js';
2
1
  import { cache } from '../cache.js';
3
2
  import { validateDaily } from '../../shared/schemas.js';
4
- export async function getSession(_req, res) {
3
+ import { getDailyResponse as getClaudeDailyResponse } from '../claudeJsonlParser.js';
4
+ import { getDailyResponse as getCodexDailyResponse } from '../codexParser.js';
5
+ import { getDailyResponse as getOpenClawDailyResponse } from '../openclawParser.js';
6
+ export async function getSession(req, res) {
7
+ const agent = req.query.agent || 'claude';
8
+ const cacheKey = `session:${agent}`;
5
9
  try {
6
- const cached = cache.get('session');
10
+ const cached = cache.get(cacheKey);
7
11
  if (cached) {
8
12
  res.json(cached);
9
13
  return;
10
14
  }
11
- const stdout = await runCcusage(['session']);
12
- const data = JSON.parse(stdout);
13
- const validated = validateDaily(data);
14
- cache.set('session', validated);
15
- res.json(validated);
15
+ const stale = cache.getStale(cacheKey);
16
+ if (stale) {
17
+ res.json(stale);
18
+ return;
19
+ }
20
+ // Session data uses same daily aggregation
21
+ let data;
22
+ if (agent === 'codex') {
23
+ data = validateDaily(getCodexDailyResponse());
24
+ }
25
+ else if (agent === 'openclaw') {
26
+ data = validateDaily(getOpenClawDailyResponse());
27
+ }
28
+ else {
29
+ data = validateDaily(getClaudeDailyResponse());
30
+ }
31
+ cache.set(cacheKey, data);
32
+ res.json(data);
16
33
  }
17
34
  catch (error) {
18
35
  const message = error instanceof Error ? error.message : 'Unknown error';
19
36
  console.error('Error fetching session data:', error);
20
37
  res.status(502).json({
21
- error: 'Failed to fetch session data from ccusage',
38
+ error: 'Failed to fetch session data',
22
39
  hint: message,
23
40
  });
24
41
  }
@@ -7,19 +7,19 @@ export declare const ModelBreakdownSchema: z.ZodObject<{
7
7
  cacheReadTokens: z.ZodDefault<z.ZodNumber>;
8
8
  cost: z.ZodDefault<z.ZodNumber>;
9
9
  }, "strip", z.ZodTypeAny, {
10
+ cost: number;
10
11
  modelName: string;
11
12
  inputTokens: number;
12
13
  outputTokens: number;
13
14
  cacheCreationTokens: number;
14
15
  cacheReadTokens: number;
15
- cost: number;
16
16
  }, {
17
17
  modelName: string;
18
+ cost?: number | undefined;
18
19
  inputTokens?: number | undefined;
19
20
  outputTokens?: number | undefined;
20
21
  cacheCreationTokens?: number | undefined;
21
22
  cacheReadTokens?: number | undefined;
22
- cost?: number | undefined;
23
23
  }>;
24
24
  export declare const DailyEntrySchema: z.ZodObject<{
25
25
  date: z.ZodString;
@@ -38,19 +38,19 @@ export declare const DailyEntrySchema: z.ZodObject<{
38
38
  cacheReadTokens: z.ZodDefault<z.ZodNumber>;
39
39
  cost: z.ZodDefault<z.ZodNumber>;
40
40
  }, "strip", z.ZodTypeAny, {
41
+ cost: number;
41
42
  modelName: string;
42
43
  inputTokens: number;
43
44
  outputTokens: number;
44
45
  cacheCreationTokens: number;
45
46
  cacheReadTokens: number;
46
- cost: number;
47
47
  }, {
48
48
  modelName: string;
49
+ cost?: number | undefined;
49
50
  inputTokens?: number | undefined;
50
51
  outputTokens?: number | undefined;
51
52
  cacheCreationTokens?: number | undefined;
52
53
  cacheReadTokens?: number | undefined;
53
- cost?: number | undefined;
54
54
  }>, "many">>;
55
55
  }, "strip", z.ZodTypeAny, {
56
56
  date: string;
@@ -62,12 +62,12 @@ export declare const DailyEntrySchema: z.ZodObject<{
62
62
  totalCost: number;
63
63
  modelsUsed: string[];
64
64
  modelBreakdowns: {
65
+ cost: number;
65
66
  modelName: string;
66
67
  inputTokens: number;
67
68
  outputTokens: number;
68
69
  cacheCreationTokens: number;
69
70
  cacheReadTokens: number;
70
- cost: number;
71
71
  }[];
72
72
  }, {
73
73
  date: string;
@@ -80,11 +80,11 @@ export declare const DailyEntrySchema: z.ZodObject<{
80
80
  modelsUsed?: string[] | undefined;
81
81
  modelBreakdowns?: {
82
82
  modelName: string;
83
+ cost?: number | undefined;
83
84
  inputTokens?: number | undefined;
84
85
  outputTokens?: number | undefined;
85
86
  cacheCreationTokens?: number | undefined;
86
87
  cacheReadTokens?: number | undefined;
87
- cost?: number | undefined;
88
88
  }[] | undefined;
89
89
  }>;
90
90
  export declare const TotalsSchema: z.ZodObject<{
@@ -127,19 +127,19 @@ export declare const DailyResponseSchema: z.ZodObject<{
127
127
  cacheReadTokens: z.ZodDefault<z.ZodNumber>;
128
128
  cost: z.ZodDefault<z.ZodNumber>;
129
129
  }, "strip", z.ZodTypeAny, {
130
+ cost: number;
130
131
  modelName: string;
131
132
  inputTokens: number;
132
133
  outputTokens: number;
133
134
  cacheCreationTokens: number;
134
135
  cacheReadTokens: number;
135
- cost: number;
136
136
  }, {
137
137
  modelName: string;
138
+ cost?: number | undefined;
138
139
  inputTokens?: number | undefined;
139
140
  outputTokens?: number | undefined;
140
141
  cacheCreationTokens?: number | undefined;
141
142
  cacheReadTokens?: number | undefined;
142
- cost?: number | undefined;
143
143
  }>, "many">>;
144
144
  }, "strip", z.ZodTypeAny, {
145
145
  date: string;
@@ -151,12 +151,12 @@ export declare const DailyResponseSchema: z.ZodObject<{
151
151
  totalCost: number;
152
152
  modelsUsed: string[];
153
153
  modelBreakdowns: {
154
+ cost: number;
154
155
  modelName: string;
155
156
  inputTokens: number;
156
157
  outputTokens: number;
157
158
  cacheCreationTokens: number;
158
159
  cacheReadTokens: number;
159
- cost: number;
160
160
  }[];
161
161
  }, {
162
162
  date: string;
@@ -169,11 +169,11 @@ export declare const DailyResponseSchema: z.ZodObject<{
169
169
  modelsUsed?: string[] | undefined;
170
170
  modelBreakdowns?: {
171
171
  modelName: string;
172
+ cost?: number | undefined;
172
173
  inputTokens?: number | undefined;
173
174
  outputTokens?: number | undefined;
174
175
  cacheCreationTokens?: number | undefined;
175
176
  cacheReadTokens?: number | undefined;
176
- cost?: number | undefined;
177
177
  }[] | undefined;
178
178
  }>, "many">>;
179
179
  totals: z.ZodObject<{
@@ -209,12 +209,12 @@ export declare const DailyResponseSchema: z.ZodObject<{
209
209
  totalCost: number;
210
210
  modelsUsed: string[];
211
211
  modelBreakdowns: {
212
+ cost: number;
212
213
  modelName: string;
213
214
  inputTokens: number;
214
215
  outputTokens: number;
215
216
  cacheCreationTokens: number;
216
217
  cacheReadTokens: number;
217
- cost: number;
218
218
  }[];
219
219
  }[];
220
220
  totals: {
@@ -245,11 +245,11 @@ export declare const DailyResponseSchema: z.ZodObject<{
245
245
  modelsUsed?: string[] | undefined;
246
246
  modelBreakdowns?: {
247
247
  modelName: string;
248
+ cost?: number | undefined;
248
249
  inputTokens?: number | undefined;
249
250
  outputTokens?: number | undefined;
250
251
  cacheCreationTokens?: number | undefined;
251
252
  cacheReadTokens?: number | undefined;
252
- cost?: number | undefined;
253
253
  }[] | undefined;
254
254
  }[] | undefined;
255
255
  }>;
@@ -272,19 +272,19 @@ export declare const ProjectEntrySchema: z.ZodObject<{
272
272
  cacheReadTokens: z.ZodDefault<z.ZodNumber>;
273
273
  cost: z.ZodDefault<z.ZodNumber>;
274
274
  }, "strip", z.ZodTypeAny, {
275
+ cost: number;
275
276
  modelName: string;
276
277
  inputTokens: number;
277
278
  outputTokens: number;
278
279
  cacheCreationTokens: number;
279
280
  cacheReadTokens: number;
280
- cost: number;
281
281
  }, {
282
282
  modelName: string;
283
+ cost?: number | undefined;
283
284
  inputTokens?: number | undefined;
284
285
  outputTokens?: number | undefined;
285
286
  cacheCreationTokens?: number | undefined;
286
287
  cacheReadTokens?: number | undefined;
287
- cost?: number | undefined;
288
288
  }>, "many">>;
289
289
  }, "strip", z.ZodTypeAny, {
290
290
  date: string;
@@ -296,12 +296,12 @@ export declare const ProjectEntrySchema: z.ZodObject<{
296
296
  totalCost: number;
297
297
  modelsUsed: string[];
298
298
  modelBreakdowns: {
299
+ cost: number;
299
300
  modelName: string;
300
301
  inputTokens: number;
301
302
  outputTokens: number;
302
303
  cacheCreationTokens: number;
303
304
  cacheReadTokens: number;
304
- cost: number;
305
305
  }[];
306
306
  }, {
307
307
  date: string;
@@ -314,11 +314,11 @@ export declare const ProjectEntrySchema: z.ZodObject<{
314
314
  modelsUsed?: string[] | undefined;
315
315
  modelBreakdowns?: {
316
316
  modelName: string;
317
+ cost?: number | undefined;
317
318
  inputTokens?: number | undefined;
318
319
  outputTokens?: number | undefined;
319
320
  cacheCreationTokens?: number | undefined;
320
321
  cacheReadTokens?: number | undefined;
321
- cost?: number | undefined;
322
322
  }[] | undefined;
323
323
  }>, "many">>;
324
324
  }, "strip", z.ZodTypeAny, {
@@ -333,12 +333,12 @@ export declare const ProjectEntrySchema: z.ZodObject<{
333
333
  totalCost: number;
334
334
  modelsUsed: string[];
335
335
  modelBreakdowns: {
336
+ cost: number;
336
337
  modelName: string;
337
338
  inputTokens: number;
338
339
  outputTokens: number;
339
340
  cacheCreationTokens: number;
340
341
  cacheReadTokens: number;
341
- cost: number;
342
342
  }[];
343
343
  }[];
344
344
  }, {
@@ -354,11 +354,11 @@ export declare const ProjectEntrySchema: z.ZodObject<{
354
354
  modelsUsed?: string[] | undefined;
355
355
  modelBreakdowns?: {
356
356
  modelName: string;
357
+ cost?: number | undefined;
357
358
  inputTokens?: number | undefined;
358
359
  outputTokens?: number | undefined;
359
360
  cacheCreationTokens?: number | undefined;
360
361
  cacheReadTokens?: number | undefined;
361
- cost?: number | undefined;
362
362
  }[] | undefined;
363
363
  }[] | undefined;
364
364
  }>;
@@ -380,19 +380,19 @@ export declare const ProjectsResponseSchema: z.ZodObject<{
380
380
  cacheReadTokens: z.ZodDefault<z.ZodNumber>;
381
381
  cost: z.ZodDefault<z.ZodNumber>;
382
382
  }, "strip", z.ZodTypeAny, {
383
+ cost: number;
383
384
  modelName: string;
384
385
  inputTokens: number;
385
386
  outputTokens: number;
386
387
  cacheCreationTokens: number;
387
388
  cacheReadTokens: number;
388
- cost: number;
389
389
  }, {
390
390
  modelName: string;
391
+ cost?: number | undefined;
391
392
  inputTokens?: number | undefined;
392
393
  outputTokens?: number | undefined;
393
394
  cacheCreationTokens?: number | undefined;
394
395
  cacheReadTokens?: number | undefined;
395
- cost?: number | undefined;
396
396
  }>, "many">>;
397
397
  }, "strip", z.ZodTypeAny, {
398
398
  date: string;
@@ -404,12 +404,12 @@ export declare const ProjectsResponseSchema: z.ZodObject<{
404
404
  totalCost: number;
405
405
  modelsUsed: string[];
406
406
  modelBreakdowns: {
407
+ cost: number;
407
408
  modelName: string;
408
409
  inputTokens: number;
409
410
  outputTokens: number;
410
411
  cacheCreationTokens: number;
411
412
  cacheReadTokens: number;
412
- cost: number;
413
413
  }[];
414
414
  }, {
415
415
  date: string;
@@ -422,11 +422,11 @@ export declare const ProjectsResponseSchema: z.ZodObject<{
422
422
  modelsUsed?: string[] | undefined;
423
423
  modelBreakdowns?: {
424
424
  modelName: string;
425
+ cost?: number | undefined;
425
426
  inputTokens?: number | undefined;
426
427
  outputTokens?: number | undefined;
427
428
  cacheCreationTokens?: number | undefined;
428
429
  cacheReadTokens?: number | undefined;
429
- cost?: number | undefined;
430
430
  }[] | undefined;
431
431
  }>, "many">>>>;
432
432
  }, "strip", z.ZodTypeAny, {
@@ -440,12 +440,12 @@ export declare const ProjectsResponseSchema: z.ZodObject<{
440
440
  totalCost: number;
441
441
  modelsUsed: string[];
442
442
  modelBreakdowns: {
443
+ cost: number;
443
444
  modelName: string;
444
445
  inputTokens: number;
445
446
  outputTokens: number;
446
447
  cacheCreationTokens: number;
447
448
  cacheReadTokens: number;
448
- cost: number;
449
449
  }[];
450
450
  }[]>;
451
451
  }, {
@@ -460,11 +460,11 @@ export declare const ProjectsResponseSchema: z.ZodObject<{
460
460
  modelsUsed?: string[] | undefined;
461
461
  modelBreakdowns?: {
462
462
  modelName: string;
463
+ cost?: number | undefined;
463
464
  inputTokens?: number | undefined;
464
465
  outputTokens?: number | undefined;
465
466
  cacheCreationTokens?: number | undefined;
466
467
  cacheReadTokens?: number | undefined;
467
- cost?: number | undefined;
468
468
  }[] | undefined;
469
469
  }[] | undefined> | undefined;
470
470
  }>;
@@ -479,12 +479,12 @@ export declare function validateDaily(data: unknown): {
479
479
  totalCost: number;
480
480
  modelsUsed: string[];
481
481
  modelBreakdowns: {
482
+ cost: number;
482
483
  modelName: string;
483
484
  inputTokens: number;
484
485
  outputTokens: number;
485
486
  cacheCreationTokens: number;
486
487
  cacheReadTokens: number;
487
- cost: number;
488
488
  }[];
489
489
  }[];
490
490
  totals: {
@@ -507,12 +507,12 @@ export declare function validateProjects(data: unknown): {
507
507
  totalCost: number;
508
508
  modelsUsed: string[];
509
509
  modelBreakdowns: {
510
+ cost: number;
510
511
  modelName: string;
511
512
  inputTokens: number;
512
513
  outputTokens: number;
513
514
  cacheCreationTokens: number;
514
515
  cacheReadTokens: number;
515
- cost: number;
516
516
  }[];
517
517
  }[]>;
518
518
  };
@@ -545,9 +545,9 @@ export declare const BlocksResponseSchema: z.ZodObject<{
545
545
  costUSD: z.ZodDefault<z.ZodNumber>;
546
546
  models: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
547
547
  }, "strip", z.ZodTypeAny, {
548
+ models: string[];
548
549
  entries: number;
549
550
  id: string;
550
- models: string[];
551
551
  totalTokens: number;
552
552
  startTime: string;
553
553
  endTime: string;
@@ -565,8 +565,8 @@ export declare const BlocksResponseSchema: z.ZodObject<{
565
565
  id: string;
566
566
  startTime: string;
567
567
  endTime: string;
568
- entries?: number | undefined;
569
568
  models?: string[] | undefined;
569
+ entries?: number | undefined;
570
570
  totalTokens?: number | undefined;
571
571
  actualEndTime?: string | null | undefined;
572
572
  isActive?: boolean | undefined;
@@ -581,9 +581,9 @@ export declare const BlocksResponseSchema: z.ZodObject<{
581
581
  }>, "many">>;
582
582
  }, "strip", z.ZodTypeAny, {
583
583
  blocks: {
584
+ models: string[];
584
585
  entries: number;
585
586
  id: string;
586
- models: string[];
587
587
  totalTokens: number;
588
588
  startTime: string;
589
589
  endTime: string;
@@ -603,8 +603,8 @@ export declare const BlocksResponseSchema: z.ZodObject<{
603
603
  id: string;
604
604
  startTime: string;
605
605
  endTime: string;
606
- entries?: number | undefined;
607
606
  models?: string[] | undefined;
607
+ entries?: number | undefined;
608
608
  totalTokens?: number | undefined;
609
609
  actualEndTime?: string | null | undefined;
610
610
  isActive?: boolean | undefined;
@@ -620,9 +620,9 @@ export declare const BlocksResponseSchema: z.ZodObject<{
620
620
  }>;
621
621
  export declare function validateBlocks(data: unknown): {
622
622
  blocks: {
623
+ models: string[];
623
624
  entries: number;
624
625
  id: string;
625
- models: string[];
626
626
  totalTokens: number;
627
627
  startTime: string;
628
628
  endTime: string;
@@ -638,3 +638,25 @@ export declare function validateBlocks(data: unknown): {
638
638
  costUSD: number;
639
639
  }[];
640
640
  };
641
+ export declare function validateAnalytics(data: unknown): {
642
+ codeChangeTrend: {
643
+ date: string;
644
+ linesAdded: number;
645
+ linesDeleted: number;
646
+ netChange: number;
647
+ filesModified: number;
648
+ }[];
649
+ toolUsageDistribution: {
650
+ name: string;
651
+ count: number;
652
+ }[];
653
+ productivityKPIs: {
654
+ avgLinesPerEdit: number;
655
+ filesModifiedPerDay: number;
656
+ addDeleteRatio: number;
657
+ totalEdits: number;
658
+ totalFilesModified: number;
659
+ activeDaysWithEdits: number;
660
+ };
661
+ toolCallTrend: Record<string, string | number>[];
662
+ };
@@ -67,3 +67,32 @@ export const BlocksResponseSchema = z.object({
67
67
  export function validateBlocks(data) {
68
68
  return BlocksResponseSchema.parse(data);
69
69
  }
70
+ // --- Analytics schemas ---
71
+ const DailyCodeChangeSchema = z.object({
72
+ date: z.string(),
73
+ linesAdded: z.number().default(0),
74
+ linesDeleted: z.number().default(0),
75
+ netChange: z.number().default(0),
76
+ filesModified: z.number().default(0),
77
+ });
78
+ const ToolUsageEntrySchema = z.object({
79
+ name: z.string(),
80
+ count: z.number().default(0),
81
+ });
82
+ const ProductivityKPIsSchema = z.object({
83
+ avgLinesPerEdit: z.number().default(0),
84
+ filesModifiedPerDay: z.number().default(0),
85
+ addDeleteRatio: z.number().default(0),
86
+ totalEdits: z.number().default(0),
87
+ totalFilesModified: z.number().default(0),
88
+ activeDaysWithEdits: z.number().default(0),
89
+ });
90
+ const AnalyticsResponseSchema = z.object({
91
+ codeChangeTrend: z.array(DailyCodeChangeSchema).default([]),
92
+ toolUsageDistribution: z.array(ToolUsageEntrySchema).default([]),
93
+ productivityKPIs: ProductivityKPIsSchema,
94
+ toolCallTrend: z.array(z.record(z.union([z.string(), z.number()]))).default([]),
95
+ });
96
+ export function validateAnalytics(data) {
97
+ return AnalyticsResponseSchema.parse(data);
98
+ }
@@ -63,3 +63,32 @@ export interface BlocksResponse {
63
63
  }
64
64
  export type MetricMode = 'tokens' | 'usd';
65
65
  export type GranularityMode = 'day' | 'hour';
66
+ export interface ToolUsageEntry {
67
+ name: string;
68
+ count: number;
69
+ }
70
+ export interface DailyCodeChange {
71
+ date: string;
72
+ linesAdded: number;
73
+ linesDeleted: number;
74
+ netChange: number;
75
+ filesModified: number;
76
+ }
77
+ export interface DailyToolCall {
78
+ date: string;
79
+ [toolName: string]: string | number;
80
+ }
81
+ export interface ProductivityKPIs {
82
+ avgLinesPerEdit: number;
83
+ filesModifiedPerDay: number;
84
+ addDeleteRatio: number;
85
+ totalEdits: number;
86
+ totalFilesModified: number;
87
+ activeDaysWithEdits: number;
88
+ }
89
+ export interface AnalyticsResponse {
90
+ codeChangeTrend: DailyCodeChange[];
91
+ toolUsageDistribution: ToolUsageEntry[];
92
+ productivityKPIs: ProductivityKPIs;
93
+ toolCallTrend: DailyToolCall[];
94
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhangferry-dev/tokendash",
3
- "version": "1.1.4",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
5
  "description": "Token Usage Analytics Dashboard",
6
6
  "publishConfig": {
@@ -22,6 +22,9 @@
22
22
  "build": "vite build && tsc -p tsconfig.json",
23
23
  "start": "node dist/server/index.js",
24
24
  "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.frontend.json --noEmit",
25
+ "test": "vitest run",
26
+ "test:watch": "vitest",
27
+ "test:e2e": "playwright test",
25
28
  "prepack": "npm run build"
26
29
  },
27
30
  "dependencies": {
@@ -33,6 +36,7 @@
33
36
  "zod": "^3.24.3"
34
37
  },
35
38
  "devDependencies": {
39
+ "@playwright/test": "^1.59.1",
36
40
  "@types/express": "^5.0.2",
37
41
  "@types/node": "^22.15.2",
38
42
  "@types/react": "^19.1.2",
@@ -41,6 +45,7 @@
41
45
  "concurrently": "^9.1.2",
42
46
  "tsx": "^4.19.3",
43
47
  "typescript": "^5.8.3",
44
- "vite": "^6.3.3"
48
+ "vite": "^6.3.3",
49
+ "vitest": "^4.1.4"
45
50
  }
46
- }
51
+ }