codebuddy-stats 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +475 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/data-loader.js +205 -0
- package/dist/lib/data-loader.js.map +1 -0
- package/dist/lib/paths.js +42 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/pricing.js +105 -0
- package/dist/lib/pricing.js.map +1 -0
- package/dist/lib/utils.js +58 -0
- package/dist/lib/utils.js.map +1 -0
- package/index.js +16 -0
- package/package.json +35 -0
- package/src/index.ts +549 -0
- package/src/lib/data-loader.ts +302 -0
- package/src/lib/paths.ts +45 -0
- package/src/lib/pricing.ts +128 -0
- package/src/lib/utils.ts +61 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import fsSync from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { createInterface } from 'node:readline';
|
|
5
|
+
import { getProjectsDir, getSettingsPath } from './paths.js';
|
|
6
|
+
import { DEFAULT_MODEL_ID, getPricingForModel, tokensToCost } from './pricing.js';
|
|
7
|
+
export const BASE_DIR = getProjectsDir();
|
|
8
|
+
async function loadModelFromSettings() {
|
|
9
|
+
try {
|
|
10
|
+
const settingsPath = getSettingsPath();
|
|
11
|
+
const settingsRaw = await fs.readFile(settingsPath, 'utf8');
|
|
12
|
+
const settings = JSON.parse(settingsRaw);
|
|
13
|
+
return typeof settings?.model === 'string' ? settings.model : DEFAULT_MODEL_ID;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return DEFAULT_MODEL_ID;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async function findJsonlFiles(dir) {
|
|
20
|
+
try {
|
|
21
|
+
const dirents = await fs.readdir(dir, { withFileTypes: true });
|
|
22
|
+
const files = await Promise.all(dirents.map(async (dirent) => {
|
|
23
|
+
const res = path.resolve(dir, dirent.name);
|
|
24
|
+
if (dirent.isDirectory()) {
|
|
25
|
+
return findJsonlFiles(res);
|
|
26
|
+
}
|
|
27
|
+
return res.endsWith('.jsonl') ? [res] : [];
|
|
28
|
+
}));
|
|
29
|
+
return files.flat();
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (typeof error === 'object' && error && 'code' in error && error.code === 'ENOENT') {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function getProjectName(filePath) {
|
|
39
|
+
const parts = filePath.split(path.sep);
|
|
40
|
+
const projectsIndex = parts.lastIndexOf('projects');
|
|
41
|
+
if (projectsIndex !== -1 && projectsIndex < parts.length - 1) {
|
|
42
|
+
return parts[projectsIndex + 1] ?? 'unknown-project';
|
|
43
|
+
}
|
|
44
|
+
return 'unknown-project';
|
|
45
|
+
}
|
|
46
|
+
function extractUsageStats(usage) {
|
|
47
|
+
const promptTokens = usage.prompt_tokens ?? 0;
|
|
48
|
+
const completionTokens = usage.completion_tokens ?? 0;
|
|
49
|
+
const totalTokens = usage.total_tokens ?? promptTokens + completionTokens;
|
|
50
|
+
const cacheHitTokens = usage.prompt_cache_hit_tokens ?? 0;
|
|
51
|
+
const cacheMissTokens = usage.prompt_cache_miss_tokens ?? (cacheHitTokens > 0 ? Math.max(promptTokens - cacheHitTokens, 0) : 0);
|
|
52
|
+
const cacheWriteTokens = usage.cache_creation_input_tokens ?? 0;
|
|
53
|
+
return {
|
|
54
|
+
promptTokens,
|
|
55
|
+
completionTokens,
|
|
56
|
+
totalTokens,
|
|
57
|
+
cacheHitTokens,
|
|
58
|
+
cacheMissTokens,
|
|
59
|
+
cacheWriteTokens,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function computeUsageCost(usage, modelId) {
|
|
63
|
+
const stats = extractUsageStats(usage);
|
|
64
|
+
const pricing = getPricingForModel(modelId);
|
|
65
|
+
let inputCost = 0;
|
|
66
|
+
if (stats.cacheHitTokens || stats.cacheMissTokens || stats.cacheWriteTokens) {
|
|
67
|
+
inputCost += tokensToCost(stats.cacheHitTokens, pricing.cacheRead);
|
|
68
|
+
inputCost += tokensToCost(stats.cacheMissTokens, pricing.prompt);
|
|
69
|
+
inputCost += tokensToCost(stats.cacheWriteTokens, pricing.cacheWrite);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
inputCost += tokensToCost(stats.promptTokens, pricing.prompt);
|
|
73
|
+
}
|
|
74
|
+
const outputCost = tokensToCost(stats.completionTokens, pricing.completion);
|
|
75
|
+
return { cost: inputCost + outputCost, stats, modelId: modelId || DEFAULT_MODEL_ID };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 加载所有用量数据
|
|
79
|
+
*/
|
|
80
|
+
export async function loadUsageData(options = {}) {
|
|
81
|
+
const defaultModelId = await loadModelFromSettings();
|
|
82
|
+
const jsonlFiles = await findJsonlFiles(BASE_DIR);
|
|
83
|
+
// 计算日期过滤范围
|
|
84
|
+
let minDate = null;
|
|
85
|
+
if (options.days) {
|
|
86
|
+
const d = new Date();
|
|
87
|
+
d.setDate(d.getDate() - options.days + 1);
|
|
88
|
+
d.setHours(0, 0, 0, 0);
|
|
89
|
+
minDate = d.toISOString().split('T')[0] ?? null;
|
|
90
|
+
}
|
|
91
|
+
// 按日期 -> 项目 -> 模型 组织的数据
|
|
92
|
+
const dailyData = {};
|
|
93
|
+
// 按模型汇总
|
|
94
|
+
const modelTotals = {};
|
|
95
|
+
// 按项目汇总
|
|
96
|
+
const projectTotals = {};
|
|
97
|
+
// 总计
|
|
98
|
+
const grandTotal = {
|
|
99
|
+
cost: 0,
|
|
100
|
+
tokens: 0,
|
|
101
|
+
requests: 0,
|
|
102
|
+
cacheHitTokens: 0,
|
|
103
|
+
cacheMissTokens: 0,
|
|
104
|
+
};
|
|
105
|
+
for (const filePath of jsonlFiles) {
|
|
106
|
+
const fileStat = await fs.stat(filePath);
|
|
107
|
+
if (fileStat.size === 0)
|
|
108
|
+
continue;
|
|
109
|
+
const fileStream = fsSync.createReadStream(filePath);
|
|
110
|
+
const rl = createInterface({
|
|
111
|
+
input: fileStream,
|
|
112
|
+
crlfDelay: Number.POSITIVE_INFINITY,
|
|
113
|
+
});
|
|
114
|
+
const projectName = getProjectName(filePath);
|
|
115
|
+
for await (const line of rl) {
|
|
116
|
+
try {
|
|
117
|
+
const record = JSON.parse(line);
|
|
118
|
+
const usage = record?.providerData?.rawUsage;
|
|
119
|
+
const timestamp = record?.timestamp;
|
|
120
|
+
if (!usage || timestamp == null)
|
|
121
|
+
continue;
|
|
122
|
+
const dateObj = new Date(timestamp);
|
|
123
|
+
if (Number.isNaN(dateObj.getTime()))
|
|
124
|
+
continue;
|
|
125
|
+
const date = dateObj.toISOString().split('T')[0];
|
|
126
|
+
if (!date)
|
|
127
|
+
continue;
|
|
128
|
+
// 日期过滤
|
|
129
|
+
if (minDate && date < minDate)
|
|
130
|
+
continue;
|
|
131
|
+
const recordModelId = record?.providerData?.model;
|
|
132
|
+
const modelId = typeof recordModelId === 'string' ? recordModelId : null;
|
|
133
|
+
const { cost, stats: usageStats, modelId: usedModelId } = computeUsageCost(usage, modelId);
|
|
134
|
+
dailyData[date] ??= {};
|
|
135
|
+
dailyData[date][projectName] ??= {};
|
|
136
|
+
dailyData[date][projectName][usedModelId] ??= {
|
|
137
|
+
cost: 0,
|
|
138
|
+
promptTokens: 0,
|
|
139
|
+
completionTokens: 0,
|
|
140
|
+
totalTokens: 0,
|
|
141
|
+
cacheHitTokens: 0,
|
|
142
|
+
cacheMissTokens: 0,
|
|
143
|
+
cacheWriteTokens: 0,
|
|
144
|
+
requests: 0,
|
|
145
|
+
};
|
|
146
|
+
const dayStats = dailyData[date][projectName][usedModelId];
|
|
147
|
+
dayStats.cost += cost;
|
|
148
|
+
dayStats.promptTokens += usageStats.promptTokens;
|
|
149
|
+
dayStats.completionTokens += usageStats.completionTokens;
|
|
150
|
+
dayStats.totalTokens += usageStats.totalTokens;
|
|
151
|
+
dayStats.cacheHitTokens += usageStats.cacheHitTokens;
|
|
152
|
+
dayStats.cacheMissTokens += usageStats.cacheMissTokens;
|
|
153
|
+
dayStats.cacheWriteTokens += usageStats.cacheWriteTokens;
|
|
154
|
+
dayStats.requests += 1;
|
|
155
|
+
modelTotals[usedModelId] ??= { cost: 0, tokens: 0, requests: 0 };
|
|
156
|
+
modelTotals[usedModelId].cost += cost;
|
|
157
|
+
modelTotals[usedModelId].tokens += usageStats.totalTokens;
|
|
158
|
+
modelTotals[usedModelId].requests += 1;
|
|
159
|
+
projectTotals[projectName] ??= { cost: 0, tokens: 0, requests: 0 };
|
|
160
|
+
projectTotals[projectName].cost += cost;
|
|
161
|
+
projectTotals[projectName].tokens += usageStats.totalTokens;
|
|
162
|
+
projectTotals[projectName].requests += 1;
|
|
163
|
+
grandTotal.cost += cost;
|
|
164
|
+
grandTotal.tokens += usageStats.totalTokens;
|
|
165
|
+
grandTotal.requests += 1;
|
|
166
|
+
grandTotal.cacheHitTokens += usageStats.cacheHitTokens;
|
|
167
|
+
grandTotal.cacheMissTokens += usageStats.cacheMissTokens;
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// 忽略无法解析的行
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// 计算每日汇总
|
|
175
|
+
const dailySummary = {};
|
|
176
|
+
for (const date of Object.keys(dailyData)) {
|
|
177
|
+
dailySummary[date] = { cost: 0, tokens: 0, requests: 0 };
|
|
178
|
+
for (const project of Object.values(dailyData[date] ?? {})) {
|
|
179
|
+
for (const model of Object.values(project ?? {})) {
|
|
180
|
+
dailySummary[date].cost += model.cost;
|
|
181
|
+
dailySummary[date].tokens += model.totalTokens;
|
|
182
|
+
dailySummary[date].requests += model.requests;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const topModelEntry = Object.entries(modelTotals).sort((a, b) => b[1].cost - a[1].cost)[0];
|
|
187
|
+
const topProjectEntry = Object.entries(projectTotals).sort((a, b) => b[1].cost - a[1].cost)[0];
|
|
188
|
+
// 计算缓存命中率
|
|
189
|
+
const cacheHitRate = grandTotal.cacheHitTokens + grandTotal.cacheMissTokens > 0
|
|
190
|
+
? grandTotal.cacheHitTokens / (grandTotal.cacheHitTokens + grandTotal.cacheMissTokens)
|
|
191
|
+
: 0;
|
|
192
|
+
return {
|
|
193
|
+
defaultModelId,
|
|
194
|
+
dailyData,
|
|
195
|
+
dailySummary,
|
|
196
|
+
modelTotals,
|
|
197
|
+
projectTotals,
|
|
198
|
+
grandTotal,
|
|
199
|
+
topModel: topModelEntry ? { id: topModelEntry[0], ...topModelEntry[1] } : null,
|
|
200
|
+
topProject: topProjectEntry ? { name: topProjectEntry[0], ...topProjectEntry[1] } : null,
|
|
201
|
+
cacheHitRate,
|
|
202
|
+
activeDays: Object.keys(dailyData).length,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=data-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-loader.js","sourceRoot":"","sources":["../../src/lib/data-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* 获取 CodeBuddy 配置目录
|
|
5
|
+
* - Windows: %APPDATA%/CodeBuddy
|
|
6
|
+
* - macOS: ~/.codebuddy
|
|
7
|
+
* - Linux: $XDG_CONFIG_HOME/codebuddy 或 ~/.codebuddy
|
|
8
|
+
*/
|
|
9
|
+
export function getConfigDir() {
|
|
10
|
+
if (process.platform === 'win32') {
|
|
11
|
+
const appData = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming');
|
|
12
|
+
return path.join(appData, 'CodeBuddy');
|
|
13
|
+
}
|
|
14
|
+
if (process.platform === 'linux') {
|
|
15
|
+
const xdgConfigHome = process.env.XDG_CONFIG_HOME;
|
|
16
|
+
if (xdgConfigHome) {
|
|
17
|
+
return path.join(xdgConfigHome, 'codebuddy');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// macOS 和 Linux 默认
|
|
21
|
+
return path.join(os.homedir(), '.codebuddy');
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 获取项目数据目录
|
|
25
|
+
*/
|
|
26
|
+
export function getProjectsDir() {
|
|
27
|
+
return path.join(getConfigDir(), 'projects');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 获取设置文件路径
|
|
31
|
+
*/
|
|
32
|
+
export function getSettingsPath() {
|
|
33
|
+
return path.join(getConfigDir(), 'settings.json');
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 简化项目路径显示
|
|
37
|
+
* 保持原始名称不变
|
|
38
|
+
*/
|
|
39
|
+
export function shortenProjectName(name) {
|
|
40
|
+
return name;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/lib/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B;;;;;GAKG;AACH,MAAM,UAAU,YAAY;IAC1B,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,SAAS,CAAC,CAAA;QACpF,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAA;IACxC,CAAC;IACD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAA;QACjD,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,CAAA;QAC9C,CAAC;IACH,CAAC;IACD,mBAAmB;IACnB,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAA;AAC9C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc;IAC5B,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,UAAU,CAAC,CAAA;AAC9C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,eAAe,CAAC,CAAA;AACnD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,IAAI,CAAA;AACb,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// 模型价格 (USD / 1M tokens)
|
|
2
|
+
function createPricing(inputPrice, cachedInputPrice, outputPrice, cacheWritePrice) {
|
|
3
|
+
return {
|
|
4
|
+
prompt: [{ limit: Number.POSITIVE_INFINITY, pricePerMTok: inputPrice }],
|
|
5
|
+
completion: [{ limit: Number.POSITIVE_INFINITY, pricePerMTok: outputPrice }],
|
|
6
|
+
cacheRead: [{ limit: Number.POSITIVE_INFINITY, pricePerMTok: cachedInputPrice }],
|
|
7
|
+
cacheWrite: [
|
|
8
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: cacheWritePrice ?? inputPrice },
|
|
9
|
+
],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export const MODEL_PRICING = {
|
|
13
|
+
'gpt-5.2': createPricing(1.75, 0.175, 14.0),
|
|
14
|
+
'gpt-5.1': createPricing(1.25, 0.125, 10.0),
|
|
15
|
+
'gpt-5': createPricing(1.25, 0.125, 10.0),
|
|
16
|
+
'gpt-5-mini': createPricing(0.25, 0.025, 2.0),
|
|
17
|
+
'gpt-5-nano': createPricing(0.05, 0.005, 0.4),
|
|
18
|
+
'gpt-5.1-chat-latest': createPricing(1.25, 0.125, 10.0),
|
|
19
|
+
'gpt-5-chat-latest': createPricing(1.25, 0.125, 10.0),
|
|
20
|
+
'gpt-5.1-codex': createPricing(1.25, 0.125, 10.0),
|
|
21
|
+
'gpt-5-codex': createPricing(1.25, 0.125, 10.0),
|
|
22
|
+
'claude-opus-4.5': createPricing(5.0, 0.5, 25.0, 10.0),
|
|
23
|
+
'claude-4.5': {
|
|
24
|
+
prompt: [
|
|
25
|
+
{ limit: 200_000, pricePerMTok: 3.0 },
|
|
26
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 6.0 },
|
|
27
|
+
],
|
|
28
|
+
completion: [
|
|
29
|
+
{ limit: 200_000, pricePerMTok: 15.0 },
|
|
30
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 22.5 },
|
|
31
|
+
],
|
|
32
|
+
cacheRead: [
|
|
33
|
+
{ limit: 200_000, pricePerMTok: 0.3 },
|
|
34
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 0.6 },
|
|
35
|
+
],
|
|
36
|
+
cacheWrite: [
|
|
37
|
+
{ limit: 200_000, pricePerMTok: 6.0 },
|
|
38
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 12.0 },
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
'gemini-3-pro': {
|
|
42
|
+
prompt: [
|
|
43
|
+
{ limit: 200_000, pricePerMTok: 2.0 },
|
|
44
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 4.0 },
|
|
45
|
+
],
|
|
46
|
+
completion: [
|
|
47
|
+
{ limit: 200_000, pricePerMTok: 12.0 },
|
|
48
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 18.0 },
|
|
49
|
+
],
|
|
50
|
+
cacheRead: [
|
|
51
|
+
{ limit: 200_000, pricePerMTok: 0.2 },
|
|
52
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 0.4 },
|
|
53
|
+
],
|
|
54
|
+
cacheWrite: [
|
|
55
|
+
{ limit: 200_000, pricePerMTok: 0.2 },
|
|
56
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 0.4 },
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
'gemini-2.5-pro': {
|
|
60
|
+
prompt: [
|
|
61
|
+
{ limit: 200_000, pricePerMTok: 1.25 },
|
|
62
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 2.5 },
|
|
63
|
+
],
|
|
64
|
+
completion: [
|
|
65
|
+
{ limit: 200_000, pricePerMTok: 10.0 },
|
|
66
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 15.0 },
|
|
67
|
+
],
|
|
68
|
+
cacheRead: [
|
|
69
|
+
{ limit: 200_000, pricePerMTok: 0.125 },
|
|
70
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 0.25 },
|
|
71
|
+
],
|
|
72
|
+
cacheWrite: [
|
|
73
|
+
{ limit: 200_000, pricePerMTok: 0.125 },
|
|
74
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 0.25 },
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
export const DEFAULT_MODEL_ID = 'gpt-5.1';
|
|
79
|
+
export function selectTierPrice(tokens, tiers) {
|
|
80
|
+
if (tokens <= 0)
|
|
81
|
+
return tiers[0]?.pricePerMTok ?? 0;
|
|
82
|
+
for (const tier of tiers) {
|
|
83
|
+
if (tokens <= tier.limit) {
|
|
84
|
+
return tier.pricePerMTok;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return tiers[tiers.length - 1]?.pricePerMTok ?? 0;
|
|
88
|
+
}
|
|
89
|
+
export function tokensToCost(tokens, tiers) {
|
|
90
|
+
if (!tokens)
|
|
91
|
+
return 0;
|
|
92
|
+
const price = selectTierPrice(tokens, tiers);
|
|
93
|
+
return (tokens / 1_000_000) * price;
|
|
94
|
+
}
|
|
95
|
+
export function getPricingForModel(modelId) {
|
|
96
|
+
if (modelId && MODEL_PRICING[modelId]) {
|
|
97
|
+
return MODEL_PRICING[modelId];
|
|
98
|
+
}
|
|
99
|
+
const fallback = MODEL_PRICING[DEFAULT_MODEL_ID];
|
|
100
|
+
if (!fallback) {
|
|
101
|
+
throw new Error(`Missing pricing for default model: ${DEFAULT_MODEL_ID}`);
|
|
102
|
+
}
|
|
103
|
+
return fallback;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=pricing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pricing.js","sourceRoot":"","sources":["../../src/lib/pricing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 格式化数字,添加千分位分隔符
|
|
3
|
+
*/
|
|
4
|
+
export function formatNumber(num) {
|
|
5
|
+
const n = typeof num === 'number' ? num : Number(num ?? 0);
|
|
6
|
+
if (!Number.isFinite(n))
|
|
7
|
+
return '0';
|
|
8
|
+
return n.toLocaleString('en-US');
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 格式化金额
|
|
12
|
+
*/
|
|
13
|
+
export function formatCost(cost) {
|
|
14
|
+
const n = Number.isFinite(cost) ? cost : 0;
|
|
15
|
+
return `$${n.toFixed(2)}`;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 格式化 tokens 数量 (K/M/B)
|
|
19
|
+
*/
|
|
20
|
+
export function formatTokens(tokens) {
|
|
21
|
+
if (tokens >= 1_000_000_000) {
|
|
22
|
+
return `${(tokens / 1_000_000_000).toFixed(1)}B`;
|
|
23
|
+
}
|
|
24
|
+
if (tokens >= 1_000_000) {
|
|
25
|
+
return `${(tokens / 1_000_000).toFixed(1)}M`;
|
|
26
|
+
}
|
|
27
|
+
if (tokens >= 1_000) {
|
|
28
|
+
return `${(tokens / 1_000).toFixed(1)}K`;
|
|
29
|
+
}
|
|
30
|
+
return String(tokens);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 格式化百分比
|
|
34
|
+
*/
|
|
35
|
+
export function formatPercent(ratio) {
|
|
36
|
+
return `${(ratio * 100).toFixed(1)}%`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 截断字符串
|
|
40
|
+
*/
|
|
41
|
+
export function truncate(str, maxLen) {
|
|
42
|
+
if (str.length <= maxLen)
|
|
43
|
+
return str;
|
|
44
|
+
return str.slice(0, maxLen - 3) + '...';
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 右对齐字符串
|
|
48
|
+
*/
|
|
49
|
+
export function padLeft(str, len) {
|
|
50
|
+
return str.padStart(len);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 左对齐字符串
|
|
54
|
+
*/
|
|
55
|
+
export function padRight(str, len) {
|
|
56
|
+
return str.padEnd(len);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,GAAuC;IAClE,MAAM,CAAC,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAA;IAC1D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,GAAG,CAAA;IACnC,OAAO,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,CAAA;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;IAC1C,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;AAC3B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,IAAI,MAAM,IAAI,aAAa,EAAE,CAAC;QAC5B,OAAO,GAAG,CAAC,MAAM,GAAG,aAAa,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAA;IAClD,CAAC;IACD,IAAI,MAAM,IAAI,SAAS,EAAE,CAAC;QACxB,OAAO,GAAG,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAA;IAC9C,CAAC;IACD,IAAI,MAAM,IAAI,KAAK,EAAE,CAAC;QACpB,OAAO,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAA;IAC1C,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAA;AACvB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAA;AACvC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAW,EAAE,MAAc;IAClD,IAAI,GAAG,CAAC,MAAM,IAAI,MAAM;QAAE,OAAO,GAAG,CAAA;IACpC,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,KAAK,CAAA;AACzC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,OAAO,CAAC,GAAW,EAAE,GAAW;IAC9C,OAAO,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;AAC1B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAW,EAAE,GAAW;IAC/C,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;AACxB,CAAC"}
|
package/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// ESM wrapper entrypoint (kept for backward compatibility)
|
|
4
|
+
// If you run this file directly, it will execute the compiled CLI in ./dist.
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
await import('./dist/index.js')
|
|
8
|
+
} catch (err) {
|
|
9
|
+
console.error('Build output not found. Please run:')
|
|
10
|
+
console.error(' npm run build')
|
|
11
|
+
console.error('or just:')
|
|
12
|
+
console.error(' npm start')
|
|
13
|
+
console.error('\nOriginal error:')
|
|
14
|
+
console.error(err)
|
|
15
|
+
process.exit(1)
|
|
16
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "codebuddy-stats",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/AnotiaWang/codebuddy-stats"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"codebuddy-stats": "./dist/index.js",
|
|
12
|
+
"cbs": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc -p tsconfig.json",
|
|
16
|
+
"dev": "tsx --watch src/index.ts",
|
|
17
|
+
"prestart": "npm run build",
|
|
18
|
+
"start": "node dist/index.js",
|
|
19
|
+
"pretext": "npm run build",
|
|
20
|
+
"text": "node dist/index.js --no-tui"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [],
|
|
23
|
+
"author": "",
|
|
24
|
+
"license": "ISC",
|
|
25
|
+
"description": "CodeBuddy AI cost analyzer with TUI",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"blessed": "^0.1.81"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/blessed": "^0.1.27",
|
|
31
|
+
"@types/node": "^25.0.3",
|
|
32
|
+
"tsx": "^4.21.0",
|
|
33
|
+
"typescript": "^5.9.3"
|
|
34
|
+
}
|
|
35
|
+
}
|