@tokscale/cli 1.0.17 → 1.0.18
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/cli.js +214 -91
- package/dist/cli.js.map +1 -1
- package/dist/graph-types.d.ts +1 -1
- package/dist/graph-types.d.ts.map +1 -1
- package/dist/native-runner.js +5 -5
- package/dist/native-runner.js.map +1 -1
- package/dist/native.d.ts +9 -30
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +18 -134
- package/dist/native.js.map +1 -1
- package/dist/sessions/types.d.ts +1 -1
- package/dist/sessions/types.d.ts.map +1 -1
- package/dist/submit.d.ts +2 -0
- package/dist/submit.d.ts.map +1 -1
- package/dist/submit.js +32 -16
- package/dist/submit.js.map +1 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +13 -6
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/components/DailyView.d.ts.map +1 -1
- package/dist/tui/components/DailyView.js +25 -8
- package/dist/tui/components/DailyView.js.map +1 -1
- package/dist/tui/components/DateBreakdownPanel.js +2 -2
- package/dist/tui/components/DateBreakdownPanel.js.map +1 -1
- package/dist/tui/components/Footer.d.ts.map +1 -1
- package/dist/tui/components/Footer.js +2 -3
- package/dist/tui/components/Footer.js.map +1 -1
- package/dist/tui/components/LoadingSpinner.d.ts.map +1 -1
- package/dist/tui/components/LoadingSpinner.js +1 -2
- package/dist/tui/components/LoadingSpinner.js.map +1 -1
- package/dist/tui/components/ModelView.js +2 -2
- package/dist/tui/components/ModelView.js.map +1 -1
- package/dist/tui/config/settings.d.ts +4 -4
- package/dist/tui/config/settings.d.ts.map +1 -1
- package/dist/tui/config/settings.js +11 -4
- package/dist/tui/config/settings.js.map +1 -1
- package/dist/tui/hooks/useData.d.ts.map +1 -1
- package/dist/tui/hooks/useData.js +29 -42
- package/dist/tui/hooks/useData.js.map +1 -1
- package/dist/tui/types/index.d.ts +2 -2
- package/dist/tui/types/index.d.ts.map +1 -1
- package/dist/tui/types/index.js +3 -1
- package/dist/tui/types/index.js.map +1 -1
- package/dist/tui/utils/colors.d.ts +1 -0
- package/dist/tui/utils/colors.d.ts.map +1 -1
- package/dist/tui/utils/colors.js +7 -0
- package/dist/tui/utils/colors.js.map +1 -1
- package/dist/wrapped.d.ts.map +1 -1
- package/dist/wrapped.js +20 -48
- package/dist/wrapped.js.map +1 -1
- package/package.json +2 -2
- package/src/cli.ts +232 -97
- package/src/graph-types.ts +1 -1
- package/src/native-runner.ts +5 -5
- package/src/native.ts +35 -200
- package/src/sessions/types.ts +1 -1
- package/src/submit.ts +36 -22
- package/src/tui/App.tsx +9 -6
- package/src/tui/components/DailyView.tsx +29 -11
- package/src/tui/components/DateBreakdownPanel.tsx +2 -2
- package/src/tui/components/Footer.tsx +7 -2
- package/src/tui/components/LoadingSpinner.tsx +1 -2
- package/src/tui/components/ModelView.tsx +2 -2
- package/src/tui/config/settings.ts +18 -9
- package/src/tui/hooks/useData.ts +36 -47
- package/src/tui/types/index.ts +5 -4
- package/src/tui/utils/colors.ts +7 -0
- package/src/wrapped.ts +21 -54
- package/dist/graph.d.ts +0 -29
- package/dist/graph.d.ts.map +0 -1
- package/dist/graph.js +0 -383
- package/dist/graph.js.map +0 -1
- package/dist/pricing.d.ts +0 -58
- package/dist/pricing.d.ts.map +0 -1
- package/dist/pricing.js +0 -232
- package/dist/pricing.js.map +0 -1
- package/dist/sessions/claudecode.d.ts +0 -8
- package/dist/sessions/claudecode.d.ts.map +0 -1
- package/dist/sessions/claudecode.js +0 -84
- package/dist/sessions/claudecode.js.map +0 -1
- package/dist/sessions/codex.d.ts +0 -8
- package/dist/sessions/codex.d.ts.map +0 -1
- package/dist/sessions/codex.js +0 -158
- package/dist/sessions/codex.js.map +0 -1
- package/dist/sessions/gemini.d.ts +0 -8
- package/dist/sessions/gemini.d.ts.map +0 -1
- package/dist/sessions/gemini.js +0 -66
- package/dist/sessions/gemini.js.map +0 -1
- package/dist/sessions/index.d.ts +0 -32
- package/dist/sessions/index.d.ts.map +0 -1
- package/dist/sessions/index.js +0 -96
- package/dist/sessions/index.js.map +0 -1
- package/dist/sessions/opencode.d.ts +0 -9
- package/dist/sessions/opencode.d.ts.map +0 -1
- package/dist/sessions/opencode.js +0 -69
- package/dist/sessions/opencode.js.map +0 -1
- package/dist/sessions/reports.d.ts +0 -58
- package/dist/sessions/reports.d.ts.map +0 -1
- package/dist/sessions/reports.js +0 -337
- package/dist/sessions/reports.js.map +0 -1
- package/src/graph.ts +0 -485
- package/src/pricing.ts +0 -309
- package/src/sessions/claudecode.ts +0 -119
- package/src/sessions/codex.ts +0 -227
- package/src/sessions/gemini.ts +0 -108
- package/src/sessions/index.ts +0 -126
- package/src/sessions/opencode.ts +0 -117
- package/src/sessions/reports.ts +0 -475
package/src/graph-types.ts
CHANGED
package/src/native-runner.ts
CHANGED
|
@@ -76,16 +76,16 @@ async function main() {
|
|
|
76
76
|
result = nativeCore.parseLocalSources(args[0] as Parameters<typeof nativeCore.parseLocalSources>[0]);
|
|
77
77
|
break;
|
|
78
78
|
case "finalizeReport":
|
|
79
|
-
result = nativeCore.finalizeReport(args[0] as Parameters<typeof nativeCore.finalizeReport>[0]);
|
|
79
|
+
result = await nativeCore.finalizeReport(args[0] as Parameters<typeof nativeCore.finalizeReport>[0]);
|
|
80
80
|
break;
|
|
81
81
|
case "finalizeMonthlyReport":
|
|
82
|
-
result = nativeCore.finalizeMonthlyReport(args[0] as Parameters<typeof nativeCore.finalizeMonthlyReport>[0]);
|
|
82
|
+
result = await nativeCore.finalizeMonthlyReport(args[0] as Parameters<typeof nativeCore.finalizeMonthlyReport>[0]);
|
|
83
83
|
break;
|
|
84
84
|
case "finalizeGraph":
|
|
85
|
-
result = nativeCore.finalizeGraph(args[0] as Parameters<typeof nativeCore.finalizeGraph>[0]);
|
|
85
|
+
result = await nativeCore.finalizeGraph(args[0] as Parameters<typeof nativeCore.finalizeGraph>[0]);
|
|
86
86
|
break;
|
|
87
|
-
case "
|
|
88
|
-
result = nativeCore.
|
|
87
|
+
case "finalizeReportAndGraph":
|
|
88
|
+
result = await nativeCore.finalizeReportAndGraph(args[0] as Parameters<typeof nativeCore.finalizeReportAndGraph>[0]);
|
|
89
89
|
break;
|
|
90
90
|
default:
|
|
91
91
|
throw new Error(`Unknown method: ${method}`);
|
package/src/native.ts
CHANGED
|
@@ -2,48 +2,19 @@
|
|
|
2
2
|
* Native module loader for Rust core
|
|
3
3
|
*
|
|
4
4
|
* Exposes all Rust functions with proper TypeScript types.
|
|
5
|
-
*
|
|
5
|
+
* Native module is REQUIRED - no TypeScript fallback.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { PricingEntry } from "./pricing.js";
|
|
9
8
|
import type {
|
|
10
9
|
TokenContributionData,
|
|
11
10
|
GraphOptions as TSGraphOptions,
|
|
12
11
|
SourceType,
|
|
13
12
|
} from "./graph-types.js";
|
|
14
|
-
import {
|
|
15
|
-
parseLocalSources as parseLocalSourcesTS,
|
|
16
|
-
type ParsedMessages as TSParsedMessages,
|
|
17
|
-
type UnifiedMessage,
|
|
18
|
-
} from "./sessions/index.js";
|
|
19
|
-
import {
|
|
20
|
-
generateModelReport as generateModelReportTS,
|
|
21
|
-
generateMonthlyReport as generateMonthlyReportTS,
|
|
22
|
-
generateGraphData as generateGraphDataTS,
|
|
23
|
-
} from "./sessions/reports.js";
|
|
24
|
-
import { readCursorMessagesFromCache } from "./cursor.js";
|
|
25
13
|
|
|
26
14
|
// =============================================================================
|
|
27
15
|
// Types matching Rust exports
|
|
28
16
|
// =============================================================================
|
|
29
17
|
|
|
30
|
-
interface NativeGraphOptions {
|
|
31
|
-
homeDir?: string;
|
|
32
|
-
sources?: string[];
|
|
33
|
-
since?: string;
|
|
34
|
-
until?: string;
|
|
35
|
-
year?: string;
|
|
36
|
-
threads?: number;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
interface NativeScanStats {
|
|
40
|
-
opencodeFiles: number;
|
|
41
|
-
claudeFiles: number;
|
|
42
|
-
codexFiles: number;
|
|
43
|
-
geminiFiles: number;
|
|
44
|
-
totalFiles: number;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
18
|
interface NativeTokenBreakdown {
|
|
48
19
|
input: number;
|
|
49
20
|
output: number;
|
|
@@ -109,21 +80,9 @@ interface NativeGraphResult {
|
|
|
109
80
|
contributions: NativeDailyContribution[];
|
|
110
81
|
}
|
|
111
82
|
|
|
112
|
-
// Types for pricing-aware APIs
|
|
113
|
-
interface NativePricingEntry {
|
|
114
|
-
modelId: string;
|
|
115
|
-
pricing: {
|
|
116
|
-
inputCostPerToken: number;
|
|
117
|
-
outputCostPerToken: number;
|
|
118
|
-
cacheReadInputTokenCost?: number;
|
|
119
|
-
cacheCreationInputTokenCost?: number;
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
83
|
interface NativeReportOptions {
|
|
124
84
|
homeDir?: string;
|
|
125
85
|
sources?: string[];
|
|
126
|
-
pricing: NativePricingEntry[];
|
|
127
86
|
since?: string;
|
|
128
87
|
until?: string;
|
|
129
88
|
year?: string;
|
|
@@ -192,6 +151,8 @@ interface NativeParsedMessages {
|
|
|
192
151
|
claudeCount: number;
|
|
193
152
|
codexCount: number;
|
|
194
153
|
geminiCount: number;
|
|
154
|
+
ampCount: number;
|
|
155
|
+
droidCount?: number;
|
|
195
156
|
processingTimeMs: number;
|
|
196
157
|
}
|
|
197
158
|
|
|
@@ -206,7 +167,6 @@ interface NativeLocalParseOptions {
|
|
|
206
167
|
interface NativeFinalizeReportOptions {
|
|
207
168
|
homeDir?: string;
|
|
208
169
|
localMessages: NativeParsedMessages;
|
|
209
|
-
pricing: NativePricingEntry[];
|
|
210
170
|
includeCursor: boolean;
|
|
211
171
|
since?: string;
|
|
212
172
|
until?: string;
|
|
@@ -216,12 +176,6 @@ interface NativeFinalizeReportOptions {
|
|
|
216
176
|
interface NativeCore {
|
|
217
177
|
version(): string;
|
|
218
178
|
healthCheck(): string;
|
|
219
|
-
generateGraph(options: NativeGraphOptions): NativeGraphResult;
|
|
220
|
-
generateGraphWithPricing(options: NativeReportOptions): NativeGraphResult;
|
|
221
|
-
scanSessions(homeDir?: string, sources?: string[]): NativeScanStats;
|
|
222
|
-
getModelReport(options: NativeReportOptions): NativeModelReport;
|
|
223
|
-
getMonthlyReport(options: NativeReportOptions): NativeMonthlyReport;
|
|
224
|
-
// Two-phase processing (parallel optimization)
|
|
225
179
|
parseLocalSources(options: NativeLocalParseOptions): NativeParsedMessages;
|
|
226
180
|
finalizeReport(options: NativeFinalizeReportOptions): NativeModelReport;
|
|
227
181
|
finalizeMonthlyReport(options: NativeFinalizeReportOptions): NativeMonthlyReport;
|
|
@@ -236,7 +190,11 @@ let nativeCore: NativeCore | null = null;
|
|
|
236
190
|
let loadError: Error | null = null;
|
|
237
191
|
|
|
238
192
|
try {
|
|
239
|
-
|
|
193
|
+
// Type assertion needed because dynamic import returns module namespace
|
|
194
|
+
// nativeCore.version() is called directly, async functions go through subprocess
|
|
195
|
+
nativeCore = await import("@tokscale/core").then(
|
|
196
|
+
(m) => (m.default || m) as unknown as NativeCore
|
|
197
|
+
);
|
|
240
198
|
} catch (e) {
|
|
241
199
|
loadError = e as Error;
|
|
242
200
|
}
|
|
@@ -252,13 +210,6 @@ export function isNativeAvailable(): boolean {
|
|
|
252
210
|
return nativeCore !== null;
|
|
253
211
|
}
|
|
254
212
|
|
|
255
|
-
/**
|
|
256
|
-
* Get native module load error (if any)
|
|
257
|
-
*/
|
|
258
|
-
export function getNativeLoadError(): Error | null {
|
|
259
|
-
return loadError;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
213
|
/**
|
|
263
214
|
* Get native module version
|
|
264
215
|
*/
|
|
@@ -266,33 +217,6 @@ export function getNativeVersion(): string | null {
|
|
|
266
217
|
return nativeCore?.version() ?? null;
|
|
267
218
|
}
|
|
268
219
|
|
|
269
|
-
/**
|
|
270
|
-
* Scan sessions using native module
|
|
271
|
-
*/
|
|
272
|
-
export function scanSessionsNative(homeDir?: string, sources?: string[]): NativeScanStats | null {
|
|
273
|
-
if (!nativeCore) {
|
|
274
|
-
return null;
|
|
275
|
-
}
|
|
276
|
-
return nativeCore.scanSessions(homeDir, sources);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// =============================================================================
|
|
280
|
-
// Graph generation
|
|
281
|
-
// =============================================================================
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Convert TypeScript graph options to native format
|
|
285
|
-
*/
|
|
286
|
-
function toNativeOptions(options: TSGraphOptions): NativeGraphOptions {
|
|
287
|
-
return {
|
|
288
|
-
homeDir: undefined,
|
|
289
|
-
sources: options.sources,
|
|
290
|
-
since: options.since,
|
|
291
|
-
until: options.until,
|
|
292
|
-
year: options.year,
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
|
|
296
220
|
/**
|
|
297
221
|
* Convert native result to TypeScript format
|
|
298
222
|
*/
|
|
@@ -358,22 +282,6 @@ function fromNativeResult(result: NativeGraphResult): TokenContributionData {
|
|
|
358
282
|
};
|
|
359
283
|
}
|
|
360
284
|
|
|
361
|
-
/**
|
|
362
|
-
* Generate graph data using native module (without pricing - uses embedded costs)
|
|
363
|
-
* @deprecated Use generateGraphWithPricing instead
|
|
364
|
-
*/
|
|
365
|
-
export function generateGraphNative(options: TSGraphOptions = {}): TokenContributionData {
|
|
366
|
-
if (!nativeCore) {
|
|
367
|
-
throw new Error("Native module not available: " + (loadError?.message || "unknown error"));
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
const nativeOptions = toNativeOptions(options);
|
|
371
|
-
const result = nativeCore.generateGraph(nativeOptions);
|
|
372
|
-
return fromNativeResult(result);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
285
|
// =============================================================================
|
|
378
286
|
// Reports
|
|
379
287
|
// =============================================================================
|
|
@@ -442,6 +350,8 @@ export interface ParsedMessages {
|
|
|
442
350
|
claudeCount: number;
|
|
443
351
|
codexCount: number;
|
|
444
352
|
geminiCount: number;
|
|
353
|
+
ampCount: number;
|
|
354
|
+
droidCount: number;
|
|
445
355
|
processingTimeMs: number;
|
|
446
356
|
}
|
|
447
357
|
|
|
@@ -450,13 +360,10 @@ export interface LocalParseOptions {
|
|
|
450
360
|
since?: string;
|
|
451
361
|
until?: string;
|
|
452
362
|
year?: string;
|
|
453
|
-
/** Force TypeScript fallback even when native module is available (needed for agent field) */
|
|
454
|
-
forceTypescript?: boolean;
|
|
455
363
|
}
|
|
456
364
|
|
|
457
365
|
export interface FinalizeOptions {
|
|
458
366
|
localMessages: ParsedMessages;
|
|
459
|
-
pricing: PricingEntry[];
|
|
460
367
|
includeCursor: boolean;
|
|
461
368
|
since?: string;
|
|
462
369
|
until?: string;
|
|
@@ -641,36 +548,8 @@ async function runInSubprocess<T>(method: string, args: unknown[]): Promise<T> {
|
|
|
641
548
|
}
|
|
642
549
|
|
|
643
550
|
export async function parseLocalSourcesAsync(options: LocalParseOptions): Promise<ParsedMessages> {
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
const result = parseLocalSourcesTS({
|
|
647
|
-
sources: options.sources,
|
|
648
|
-
since: options.since,
|
|
649
|
-
until: options.until,
|
|
650
|
-
year: options.year,
|
|
651
|
-
});
|
|
652
|
-
|
|
653
|
-
return {
|
|
654
|
-
messages: result.messages.map((msg) => ({
|
|
655
|
-
source: msg.source,
|
|
656
|
-
modelId: msg.modelId,
|
|
657
|
-
providerId: msg.providerId,
|
|
658
|
-
timestamp: msg.timestamp,
|
|
659
|
-
date: msg.date,
|
|
660
|
-
input: msg.tokens.input,
|
|
661
|
-
output: msg.tokens.output,
|
|
662
|
-
cacheRead: msg.tokens.cacheRead,
|
|
663
|
-
cacheWrite: msg.tokens.cacheWrite,
|
|
664
|
-
reasoning: msg.tokens.reasoning,
|
|
665
|
-
sessionId: msg.sessionId,
|
|
666
|
-
agent: msg.agent,
|
|
667
|
-
})),
|
|
668
|
-
opencodeCount: result.opencodeCount,
|
|
669
|
-
claudeCount: result.claudeCount,
|
|
670
|
-
codexCount: result.codexCount,
|
|
671
|
-
geminiCount: result.geminiCount,
|
|
672
|
-
processingTimeMs: result.processingTimeMs,
|
|
673
|
-
};
|
|
551
|
+
if (!isNativeAvailable()) {
|
|
552
|
+
throw new Error("Native module required. Run: bun run build:core");
|
|
674
553
|
}
|
|
675
554
|
|
|
676
555
|
const nativeOptions: NativeLocalParseOptions = {
|
|
@@ -684,52 +563,14 @@ export async function parseLocalSourcesAsync(options: LocalParseOptions): Promis
|
|
|
684
563
|
return runInSubprocess<ParsedMessages>("parseLocalSources", [nativeOptions]);
|
|
685
564
|
}
|
|
686
565
|
|
|
687
|
-
function buildMessagesForFallback(options: FinalizeOptions): UnifiedMessage[] {
|
|
688
|
-
const messages: UnifiedMessage[] = options.localMessages.messages.map((msg) => ({
|
|
689
|
-
source: msg.source,
|
|
690
|
-
modelId: msg.modelId,
|
|
691
|
-
providerId: msg.providerId,
|
|
692
|
-
sessionId: msg.sessionId,
|
|
693
|
-
timestamp: msg.timestamp,
|
|
694
|
-
date: msg.date,
|
|
695
|
-
tokens: {
|
|
696
|
-
input: msg.input,
|
|
697
|
-
output: msg.output,
|
|
698
|
-
cacheRead: msg.cacheRead,
|
|
699
|
-
cacheWrite: msg.cacheWrite,
|
|
700
|
-
reasoning: msg.reasoning,
|
|
701
|
-
},
|
|
702
|
-
cost: 0,
|
|
703
|
-
agent: msg.agent,
|
|
704
|
-
}));
|
|
705
|
-
|
|
706
|
-
if (options.includeCursor) {
|
|
707
|
-
const cursorMessages = readCursorMessagesFromCache();
|
|
708
|
-
for (const cursor of cursorMessages) {
|
|
709
|
-
const inRange =
|
|
710
|
-
(!options.year || cursor.date.startsWith(options.year)) &&
|
|
711
|
-
(!options.since || cursor.date >= options.since) &&
|
|
712
|
-
(!options.until || cursor.date <= options.until);
|
|
713
|
-
if (inRange) {
|
|
714
|
-
messages.push(cursor);
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
return messages;
|
|
720
|
-
}
|
|
721
|
-
|
|
722
566
|
export async function finalizeReportAsync(options: FinalizeOptions): Promise<ModelReport> {
|
|
723
567
|
if (!isNativeAvailable()) {
|
|
724
|
-
|
|
725
|
-
const messages = buildMessagesForFallback(options);
|
|
726
|
-
return generateModelReportTS(messages, options.pricing, startTime);
|
|
568
|
+
throw new Error("Native module required. Run: bun run build:core");
|
|
727
569
|
}
|
|
728
570
|
|
|
729
571
|
const nativeOptions: NativeFinalizeReportOptions = {
|
|
730
572
|
homeDir: undefined,
|
|
731
573
|
localMessages: options.localMessages,
|
|
732
|
-
pricing: options.pricing,
|
|
733
574
|
includeCursor: options.includeCursor,
|
|
734
575
|
since: options.since,
|
|
735
576
|
until: options.until,
|
|
@@ -741,15 +582,12 @@ export async function finalizeReportAsync(options: FinalizeOptions): Promise<Mod
|
|
|
741
582
|
|
|
742
583
|
export async function finalizeMonthlyReportAsync(options: FinalizeOptions): Promise<MonthlyReport> {
|
|
743
584
|
if (!isNativeAvailable()) {
|
|
744
|
-
|
|
745
|
-
const messages = buildMessagesForFallback(options);
|
|
746
|
-
return generateMonthlyReportTS(messages, options.pricing, startTime);
|
|
585
|
+
throw new Error("Native module required. Run: bun run build:core");
|
|
747
586
|
}
|
|
748
587
|
|
|
749
588
|
const nativeOptions: NativeFinalizeReportOptions = {
|
|
750
589
|
homeDir: undefined,
|
|
751
590
|
localMessages: options.localMessages,
|
|
752
|
-
pricing: options.pricing,
|
|
753
591
|
includeCursor: options.includeCursor,
|
|
754
592
|
since: options.since,
|
|
755
593
|
until: options.until,
|
|
@@ -761,15 +599,12 @@ export async function finalizeMonthlyReportAsync(options: FinalizeOptions): Prom
|
|
|
761
599
|
|
|
762
600
|
export async function finalizeGraphAsync(options: FinalizeOptions): Promise<TokenContributionData> {
|
|
763
601
|
if (!isNativeAvailable()) {
|
|
764
|
-
|
|
765
|
-
const messages = buildMessagesForFallback(options);
|
|
766
|
-
return generateGraphDataTS(messages, options.pricing, startTime);
|
|
602
|
+
throw new Error("Native module required. Run: bun run build:core");
|
|
767
603
|
}
|
|
768
604
|
|
|
769
605
|
const nativeOptions: NativeFinalizeReportOptions = {
|
|
770
606
|
homeDir: undefined,
|
|
771
607
|
localMessages: options.localMessages,
|
|
772
|
-
pricing: options.pricing,
|
|
773
608
|
includeCursor: options.includeCursor,
|
|
774
609
|
since: options.since,
|
|
775
610
|
until: options.until,
|
|
@@ -780,33 +615,33 @@ export async function finalizeGraphAsync(options: FinalizeOptions): Promise<Toke
|
|
|
780
615
|
return fromNativeResult(result);
|
|
781
616
|
}
|
|
782
617
|
|
|
783
|
-
export
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
sources: options.sources,
|
|
793
|
-
since: options.since,
|
|
794
|
-
until: options.until,
|
|
795
|
-
year: options.year,
|
|
796
|
-
});
|
|
618
|
+
export interface ReportAndGraph {
|
|
619
|
+
report: ModelReport;
|
|
620
|
+
graph: TokenContributionData;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
interface NativeReportAndGraph {
|
|
624
|
+
report: NativeModelReport;
|
|
625
|
+
graph: NativeGraphResult;
|
|
626
|
+
}
|
|
797
627
|
|
|
798
|
-
|
|
628
|
+
export async function finalizeReportAndGraphAsync(options: FinalizeOptions): Promise<ReportAndGraph> {
|
|
629
|
+
if (!isNativeAvailable()) {
|
|
630
|
+
throw new Error("Native module required. Run: bun run build:core");
|
|
799
631
|
}
|
|
800
632
|
|
|
801
|
-
const nativeOptions:
|
|
633
|
+
const nativeOptions: NativeFinalizeReportOptions = {
|
|
802
634
|
homeDir: undefined,
|
|
803
|
-
|
|
804
|
-
|
|
635
|
+
localMessages: options.localMessages,
|
|
636
|
+
includeCursor: options.includeCursor,
|
|
805
637
|
since: options.since,
|
|
806
638
|
until: options.until,
|
|
807
639
|
year: options.year,
|
|
808
640
|
};
|
|
809
641
|
|
|
810
|
-
const result = await runInSubprocess<
|
|
811
|
-
return
|
|
642
|
+
const result = await runInSubprocess<NativeReportAndGraph>("finalizeReportAndGraph", [nativeOptions]);
|
|
643
|
+
return {
|
|
644
|
+
report: result.report,
|
|
645
|
+
graph: fromNativeResult(result.graph),
|
|
646
|
+
};
|
|
812
647
|
}
|
package/src/sessions/types.ts
CHANGED
|
@@ -22,7 +22,7 @@ export interface UnifiedMessage {
|
|
|
22
22
|
agent?: string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export type SourceType = "opencode" | "claude" | "codex" | "gemini" | "cursor";
|
|
25
|
+
export type SourceType = "opencode" | "claude" | "codex" | "gemini" | "cursor" | "amp" | "droid";
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Convert Unix milliseconds timestamp to YYYY-MM-DD date string
|
package/src/submit.ts
CHANGED
|
@@ -5,11 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
import pc from "picocolors";
|
|
7
7
|
import { loadCredentials, getApiBaseUrl } from "./credentials.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
isNativeAvailable,
|
|
11
|
-
generateGraphWithPricingAsync,
|
|
12
|
-
} from "./native.js";
|
|
8
|
+
import { parseLocalSourcesAsync, finalizeReportAndGraphAsync, type ParsedMessages } from "./native.js";
|
|
9
|
+
import { syncCursorCache, loadCursorCredentials } from "./cursor.js";
|
|
13
10
|
import type { TokenContributionData } from "./graph-types.js";
|
|
14
11
|
import { formatCurrency } from "./table.js";
|
|
15
12
|
|
|
@@ -19,6 +16,8 @@ interface SubmitOptions {
|
|
|
19
16
|
codex?: boolean;
|
|
20
17
|
gemini?: boolean;
|
|
21
18
|
cursor?: boolean;
|
|
19
|
+
amp?: boolean;
|
|
20
|
+
droid?: boolean;
|
|
22
21
|
since?: string;
|
|
23
22
|
until?: string;
|
|
24
23
|
year?: string;
|
|
@@ -44,7 +43,7 @@ interface SubmitResponse {
|
|
|
44
43
|
details?: string[];
|
|
45
44
|
}
|
|
46
45
|
|
|
47
|
-
type SourceType = "opencode" | "claude" | "codex" | "gemini" | "cursor";
|
|
46
|
+
type SourceType = "opencode" | "claude" | "codex" | "gemini" | "cursor" | "amp" | "droid";
|
|
48
47
|
|
|
49
48
|
/**
|
|
50
49
|
* Submit command - sends usage data to the platform
|
|
@@ -58,24 +57,13 @@ export async function submit(options: SubmitOptions = {}): Promise<void> {
|
|
|
58
57
|
process.exit(1);
|
|
59
58
|
}
|
|
60
59
|
|
|
61
|
-
// Step 2: Log native module status (TS fallback available)
|
|
62
|
-
if (!isNativeAvailable()) {
|
|
63
|
-
console.log(pc.yellow("\n Note: Using TypeScript fallback (native module not available)"));
|
|
64
|
-
console.log(pc.gray(" Run 'bun run build:core' for faster processing.\n"));
|
|
65
|
-
}
|
|
66
|
-
|
|
67
60
|
console.log(pc.cyan("\n Tokscale - Submit Usage Data\n"));
|
|
68
61
|
|
|
69
|
-
// Step 3: Generate graph data
|
|
70
62
|
console.log(pc.gray(" Scanning local session data..."));
|
|
71
63
|
|
|
72
|
-
const
|
|
73
|
-
await fetcher.fetchPricing();
|
|
74
|
-
const pricingEntries = fetcher.toPricingEntries();
|
|
75
|
-
|
|
76
|
-
// Determine sources
|
|
77
|
-
const hasFilter = options.opencode || options.claude || options.codex || options.gemini || options.cursor;
|
|
64
|
+
const hasFilter = options.opencode || options.claude || options.codex || options.gemini || options.cursor || options.amp || options.droid;
|
|
78
65
|
let sources: SourceType[] | undefined;
|
|
66
|
+
let includeCursor = true;
|
|
79
67
|
if (hasFilter) {
|
|
80
68
|
sources = [];
|
|
81
69
|
if (options.opencode) sources.push("opencode");
|
|
@@ -83,17 +71,43 @@ export async function submit(options: SubmitOptions = {}): Promise<void> {
|
|
|
83
71
|
if (options.codex) sources.push("codex");
|
|
84
72
|
if (options.gemini) sources.push("gemini");
|
|
85
73
|
if (options.cursor) sources.push("cursor");
|
|
74
|
+
if (options.amp) sources.push("amp");
|
|
75
|
+
if (options.droid) sources.push("droid");
|
|
76
|
+
includeCursor = sources.includes("cursor");
|
|
86
77
|
}
|
|
87
78
|
|
|
79
|
+
// Filter out cursor from local sources (it's handled separately via sync)
|
|
80
|
+
const localSources = sources?.filter((s): s is Exclude<SourceType, "cursor"> => s !== "cursor");
|
|
81
|
+
|
|
88
82
|
let data: TokenContributionData;
|
|
89
83
|
try {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
84
|
+
// Two-phase processing (same as TUI) for consistency:
|
|
85
|
+
// Phase 1: Parse local sources + sync cursor in parallel
|
|
86
|
+
const [localMessages, cursorSync] = await Promise.all([
|
|
87
|
+
parseLocalSourcesAsync({
|
|
88
|
+
sources: localSources,
|
|
89
|
+
since: options.since,
|
|
90
|
+
until: options.until,
|
|
91
|
+
year: options.year,
|
|
92
|
+
}),
|
|
93
|
+
includeCursor && loadCursorCredentials()
|
|
94
|
+
? syncCursorCache()
|
|
95
|
+
: Promise.resolve({ synced: false, rows: 0 }),
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
// Phase 2: Finalize with pricing (combines local + cursor)
|
|
99
|
+
// Single subprocess call ensures consistent pricing for both report and graph
|
|
100
|
+
const { report, graph } = await finalizeReportAndGraphAsync({
|
|
101
|
+
localMessages,
|
|
102
|
+
includeCursor: includeCursor && cursorSync.synced,
|
|
93
103
|
since: options.since,
|
|
94
104
|
until: options.until,
|
|
95
105
|
year: options.year,
|
|
96
106
|
});
|
|
107
|
+
|
|
108
|
+
// Use graph structure for submission, report's cost for display
|
|
109
|
+
data = graph;
|
|
110
|
+
data.summary.totalCost = report.totalCost;
|
|
97
111
|
} catch (error) {
|
|
98
112
|
console.error(pc.red(`\n Error generating data: ${(error as Error).message}\n`));
|
|
99
113
|
process.exit(1);
|
package/src/tui/App.tsx
CHANGED
|
@@ -162,8 +162,12 @@ export function App(props: AppProps) {
|
|
|
162
162
|
};
|
|
163
163
|
|
|
164
164
|
const handleSortChange = (sort: SortType) => {
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
if (sortBy() === sort) {
|
|
166
|
+
setSortDesc(!sortDesc());
|
|
167
|
+
} else {
|
|
168
|
+
setSortBy(sort);
|
|
169
|
+
setSortDesc(true);
|
|
170
|
+
}
|
|
167
171
|
};
|
|
168
172
|
|
|
169
173
|
useKeyboard((key) => {
|
|
@@ -216,8 +220,7 @@ export function App(props: AppProps) {
|
|
|
216
220
|
}
|
|
217
221
|
|
|
218
222
|
if (key.name === "c" && !key.meta && !key.ctrl) {
|
|
219
|
-
|
|
220
|
-
setSortDesc(true);
|
|
223
|
+
handleSortChange("cost");
|
|
221
224
|
return;
|
|
222
225
|
}
|
|
223
226
|
|
|
@@ -263,8 +266,7 @@ export function App(props: AppProps) {
|
|
|
263
266
|
return;
|
|
264
267
|
}
|
|
265
268
|
if (key.name === "t") {
|
|
266
|
-
|
|
267
|
-
setSortDesc(true);
|
|
269
|
+
handleSortChange("tokens");
|
|
268
270
|
return;
|
|
269
271
|
}
|
|
270
272
|
|
|
@@ -278,6 +280,7 @@ export function App(props: AppProps) {
|
|
|
278
280
|
if (key.name === "3") { handleSourceToggle("codex"); return; }
|
|
279
281
|
if (key.name === "4") { handleSourceToggle("cursor"); return; }
|
|
280
282
|
if (key.name === "5") { handleSourceToggle("gemini"); return; }
|
|
283
|
+
if (key.name === "6") { handleSourceToggle("amp"); return; }
|
|
281
284
|
|
|
282
285
|
if (key.name === "up") {
|
|
283
286
|
if (activeTab() === "overview") {
|
|
@@ -55,7 +55,26 @@ export function DailyView(props: DailyViewProps) {
|
|
|
55
55
|
});
|
|
56
56
|
});
|
|
57
57
|
|
|
58
|
-
const visibleEntries = createMemo(() =>
|
|
58
|
+
const visibleEntries = createMemo(() => {
|
|
59
|
+
const maxRows = Math.max(props.height - 3, 0);
|
|
60
|
+
return sortedEntries().slice(0, maxRows);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const formattedRows = createMemo(() => {
|
|
64
|
+
const dateColWidth = dateColumnWidths().column;
|
|
65
|
+
const narrow = isNarrowTerminal();
|
|
66
|
+
return visibleEntries().map((entry) => ({
|
|
67
|
+
entry,
|
|
68
|
+
dateColWidth,
|
|
69
|
+
narrow,
|
|
70
|
+
input: formatTokensCompact(entry.input),
|
|
71
|
+
output: formatTokensCompact(entry.output),
|
|
72
|
+
cacheRead: formatTokensCompact(entry.cacheRead),
|
|
73
|
+
cacheWrite: formatTokensCompact(entry.cacheWrite),
|
|
74
|
+
total: formatTokensCompact(entry.total),
|
|
75
|
+
cost: formatCostFull(entry.cost),
|
|
76
|
+
}));
|
|
77
|
+
});
|
|
59
78
|
|
|
60
79
|
const sortArrow = () => (props.sortDesc ? "▼" : "▲");
|
|
61
80
|
const dateHeader = () => "Date";
|
|
@@ -70,12 +89,11 @@ export function DailyView(props: DailyViewProps) {
|
|
|
70
89
|
return `${(" " + dateHeader()).padEnd(dateColWidth)}${"Input".padStart(INPUT_COL_WIDTH)}${"Output".padStart(OUTPUT_COL_WIDTH)}${"C.Read".padStart(CACHE_READ_COL_WIDTH)}${"C.Write".padStart(CACHE_WRITE_COL_WIDTH)}${totalHeader().padStart(TOTAL_COL_WIDTH)}${costHeader().padStart(COST_COL_WIDTH)}`;
|
|
71
90
|
};
|
|
72
91
|
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return `${entry.date.padEnd(dateColWidth)}${formatTokensCompact(entry.total).padStart(TOTAL_COL_WIDTH)}`;
|
|
92
|
+
const renderRowData = (row: typeof formattedRows extends () => (infer T)[] ? T : never) => {
|
|
93
|
+
if (row.narrow) {
|
|
94
|
+
return `${row.entry.date.padEnd(row.dateColWidth)}${row.total.padStart(TOTAL_COL_WIDTH)}`;
|
|
77
95
|
}
|
|
78
|
-
return `${entry.date.padEnd(dateColWidth)}${
|
|
96
|
+
return `${row.entry.date.padEnd(row.dateColWidth)}${row.input.padStart(INPUT_COL_WIDTH)}${row.output.padStart(OUTPUT_COL_WIDTH)}${row.cacheRead.padStart(CACHE_READ_COL_WIDTH)}${row.cacheWrite.padStart(CACHE_WRITE_COL_WIDTH)}${row.total.padStart(TOTAL_COL_WIDTH)}`;
|
|
79
97
|
};
|
|
80
98
|
|
|
81
99
|
return (
|
|
@@ -86,24 +104,24 @@ export function DailyView(props: DailyViewProps) {
|
|
|
86
104
|
</text>
|
|
87
105
|
</box>
|
|
88
106
|
|
|
89
|
-
<For each={
|
|
90
|
-
{(
|
|
107
|
+
<For each={formattedRows()}>
|
|
108
|
+
{(row, i) => {
|
|
91
109
|
const isActive = createMemo(() => i() === props.selectedIndex());
|
|
92
110
|
const rowBg = createMemo(() => isActive() ? "blue" : (i() % 2 === 1 ? STRIPE_BG : undefined));
|
|
93
|
-
|
|
111
|
+
|
|
94
112
|
return (
|
|
95
113
|
<box flexDirection="row">
|
|
96
114
|
<text
|
|
97
115
|
bg={rowBg()}
|
|
98
116
|
fg={isActive() ? "white" : undefined}
|
|
99
117
|
>
|
|
100
|
-
{
|
|
118
|
+
{renderRowData(row)}
|
|
101
119
|
</text>
|
|
102
120
|
<text
|
|
103
121
|
fg="green"
|
|
104
122
|
bg={rowBg()}
|
|
105
123
|
>
|
|
106
|
-
{
|
|
124
|
+
{row.cost.padStart(COST_COL_WIDTH)}
|
|
107
125
|
</text>
|
|
108
126
|
</box>
|
|
109
127
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { For, createMemo } from "solid-js";
|
|
2
2
|
import type { DailyModelBreakdown } from "../types/index.js";
|
|
3
|
-
import { getSourceColor } from "../utils/colors.js";
|
|
3
|
+
import { getSourceColor, getSourceDisplayName } from "../utils/colors.js";
|
|
4
4
|
import { formatTokens, formatCost } from "../utils/format.js";
|
|
5
5
|
import { ModelRow } from "./ModelRow.js";
|
|
6
6
|
|
|
@@ -48,7 +48,7 @@ export function DateBreakdownPanel(props: DateBreakdownPanelProps) {
|
|
|
48
48
|
{([source, models]) => (
|
|
49
49
|
<box flexDirection="column">
|
|
50
50
|
<box flexDirection="row" gap={1}>
|
|
51
|
-
<text fg={getSourceColor(source)} bold>{`● ${source
|
|
51
|
+
<text fg={getSourceColor(source)} bold>{`● ${getSourceDisplayName(source)}`}</text>
|
|
52
52
|
<text dim>{`(${models.length} model${models.length > 1 ? "s" : ""})`}</text>
|
|
53
53
|
</box>
|
|
54
54
|
<For each={models}>
|