@winston.wan/burn-your-money 2.3.1 → 3.0.1

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/README.md CHANGED
@@ -71,6 +71,7 @@ npm install -g "@winston.wan/burn-your-money"
71
71
  | `/burn-your-money` | 查看详细账单(今日/周/月/总计 + 有趣统计) |
72
72
  | `/burn-your-money-stats` | 查看 token 使用趋势图 |
73
73
  | `/burn-your-money-export` | 导出 token 数据(CSV 格式) |
74
+ | `/burn-your-money-uninstall` | **完全卸载插件(推荐使用此命令卸载)** |
74
75
 
75
76
  ---
76
77
 
@@ -89,6 +90,18 @@ npm install -g "@winston.wan/burn-your-money"
89
90
  - **历史统计**:从 `~/.claude/stats-cache.json` 读取
90
91
  - **本地计算**:零网络请求,纯本地处理
91
92
 
93
+ ### 💰 价格说明
94
+
95
+ 费用计算基于 **Claude 模型官方定价**(截至 2025 年):
96
+
97
+ | Token 类型 | 价格 |
98
+ |-----------|------|
99
+ | Input Tokens | $3 / 百万 tokens |
100
+ | Output Tokens | $15 / 百万 tokens |
101
+ | Cache Read Tokens | $0.30 / 百万 tokens (90% 折扣) |
102
+
103
+ > **注意**:如果你使用的是其他兼容模型(如本地部署的模型),实际费用可能不同。本插件仅按 Claude 官方价格估算,不代表实际账单。
104
+
92
105
  ---
93
106
 
94
107
  ## 📋 常见问题
@@ -108,21 +121,16 @@ npm install -g "@winston.wan/burn-your-money"
108
121
 
109
122
  ### 3. 卸载后状态栏还显示?
110
123
 
111
- npm 的 `preuninstall` 钩子在某些情况下可能不触发。如果卸载后状态栏还显示:
112
-
113
- **方案 1**:手动运行清理脚本
114
- ```bash
115
- node D:/code/burn-your-money/uninstall.js
116
- ```
124
+ npm 的 `postuninstall` 钩子在某些情况下可能不触发。如果卸载后状态栏还显示:
117
125
 
118
- **方案 2**:手动删除文件和配置
126
+ **手动清理方法**:
119
127
  ```bash
120
128
  # 删除插件文件
121
129
  rm -f ~/.claude/statusline.js
122
130
  rm -f ~/.claude/scripts/token-history.js
123
131
  rm -f ~/.claude/commands/burn-your-money-*.md
124
132
 
125
- # 然后编辑 settings.json 删除 statusLine 配置项
133
+ # 编辑 ~/.claude/settings.json,删除 statusLine 配置项
126
134
  ```
127
135
 
128
136
  然后重启 Claude Code。
@@ -131,7 +139,15 @@ rm -f ~/.claude/commands/burn-your-money-*.md
131
139
 
132
140
  ## 🗑️ 卸载
133
141
 
134
- 如果现实太过沉重:
142
+ **推荐方式**:在 Claude Code 中使用自定义命令
143
+
144
+ ```
145
+ /burn-your-money-uninstall
146
+ ```
147
+
148
+ 这个命令会自动删除所有插件文件和配置。
149
+
150
+ **备选方式**:使用 npm 卸载(可能需要手动清理)
135
151
 
136
152
  ```bash
137
153
  npm uninstall -g "@winston.wan/burn-your-money"
package/install.js CHANGED
@@ -127,7 +127,8 @@ function configureSettings() {
127
127
  const commands = [
128
128
  'burn-your-money.md',
129
129
  'burn-your-money-stats.md',
130
- 'burn-your-money-export.md'
130
+ 'burn-your-money-export.md',
131
+ 'burn-your-money-uninstall.md'
131
132
  ];
132
133
 
133
134
  const projectCommandsDir = path.join(projectRoot, 'src', 'commands');
package/package.json CHANGED
@@ -1,29 +1,29 @@
1
- {
2
- "name": "@winston.wan/burn-your-money",
3
- "version": "2.3.1",
4
- "description": "💸 Burn Your Money - 实时显示 Claude Code 的 token 消耗,看着你的钱包燃烧!",
5
- "main": "src/statusline.js",
6
- "scripts": {
7
- "postinstall": "node install.js",
8
- "postuninstall": "node uninstall.js"
9
- },
10
- "repository": {
11
- "type": "git",
12
- "url": "git+https://github.com/winston-wwzhen/burn-your-money.git"
13
- },
14
- "keywords": [
15
- "claude-code",
16
- "token-monitor",
17
- "statusline",
18
- "claude",
19
- "ai",
20
- "cost-tracker",
21
- "burn-money"
22
- ],
23
- "author": "winston-wwzhen",
24
- "license": "MIT",
25
- "bugs": {
26
- "url": "https://github.com/winston-wwzhen/burn-your-money/issues"
27
- },
28
- "homepage": "https://github.com/winston-wwzhen/burn-your-money#readme"
29
- }
1
+ {
2
+ "name": "@winston.wan/burn-your-money",
3
+ "version": "3.0.1",
4
+ "description": "💸 Burn Your Money - 实时显示 Claude Code 的 token 消耗,看着你的钱包燃烧!",
5
+ "main": "src/statusline.js",
6
+ "scripts": {
7
+ "postinstall": "node install.js",
8
+ "postuninstall": "node uninstall.js"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/winston-wwzhen/burn-your-money.git"
13
+ },
14
+ "keywords": [
15
+ "claude-code",
16
+ "token-monitor",
17
+ "statusline",
18
+ "claude",
19
+ "ai",
20
+ "cost-tracker",
21
+ "burn-money"
22
+ ],
23
+ "author": "winston-wwzhen",
24
+ "license": "MIT",
25
+ "bugs": {
26
+ "url": "https://github.com/winston-wwzhen/burn-your-money/issues"
27
+ },
28
+ "homepage": "https://github.com/winston-wwzhen/burn-your-money#readme"
29
+ }
package/src/statusline.js CHANGED
@@ -166,6 +166,21 @@ function main() {
166
166
  const now = Math.floor(Date.now() / 1000);
167
167
  let historyData = readJsonFile(HISTORY_CACHE, {});
168
168
 
169
+ // 如果缓存过期(超过 5 分钟),重新获取
170
+ const CACHE_EXPIRY = 300; // 5 分钟
171
+ if (!historyData._cache_time || (now - historyData._cache_time) > CACHE_EXPIRY) {
172
+ try {
173
+ const { execSync } = require('child_process');
174
+ const scriptPath = path.join(__dirname, 'token-history.js');
175
+ const freshData = execSync(`node "${scriptPath}" summary`, { encoding: 'utf8' });
176
+ historyData = JSON.parse(freshData);
177
+ historyData._cache_time = now;
178
+ writeJsonFile(HISTORY_CACHE, historyData);
179
+ } catch (e) {
180
+ // 如果获取失败,使用旧缓存
181
+ }
182
+ }
183
+
169
184
  // 如果缓存不存在或无效,使用默认值(但保留已有数据)
170
185
  if (!historyData || typeof historyData !== 'object') {
171
186
  historyData = {};
@@ -176,34 +191,42 @@ function main() {
176
191
  historyData._cache_time = now;
177
192
  }
178
193
 
179
- // 今日数据持久化
194
+ // 使用历史数据的今日统计(包含缓存 tokens)
195
+ const historyTodayTokens = historyData.today_tokens || 0;
196
+ const historyTodayCost = parseFloat(historyData.today_cost || 0);
197
+
198
+ // 今日数据持久化(用于当前会话增量)
180
199
  const todayDate = new Date().toISOString().split('T')[0];
181
200
  ensureDir(TODAY_STATE);
182
201
  const todayState = readJsonFile(TODAY_STATE, {
183
202
  date: todayDate,
184
- tokens: 0,
185
- cost: 0,
203
+ tokens: historyTodayTokens,
204
+ cost: historyTodayCost,
186
205
  last_session_tokens: 0,
187
206
  last_session_cost: 0
188
207
  });
189
208
 
190
- // 如果是新的一天,重置
209
+ // 如果是新的一天,重置为历史数据
191
210
  if (todayState.date !== todayDate) {
192
211
  todayState.date = todayDate;
193
- todayState.tokens = 0;
194
- todayState.cost = 0;
212
+ todayState.tokens = historyTodayTokens;
213
+ todayState.cost = historyTodayCost;
195
214
  todayState.last_session_tokens = 0;
196
215
  todayState.last_session_cost = 0;
197
216
  }
198
217
 
199
- // 计算增量
218
+ // 计算增量(当前会话相对于历史数据的增量)
200
219
  let tokenIncrement = currentSessionTokens - (todayState.last_session_tokens || 0);
201
- if (tokenIncrement < 0) tokenIncrement = 0;
220
+ if (tokenIncrement < 0) {
221
+ tokenIncrement = currentSessionTokens;
222
+ }
202
223
 
203
224
  let costIncrement = currentCost - (todayState.last_session_cost || 0);
204
- if (costIncrement < 0) costIncrement = 0;
225
+ if (costIncrement < 0 || todayState.last_session_tokens === 0) {
226
+ costIncrement = currentCost;
227
+ }
205
228
 
206
- // 更新今日数据
229
+ // 新的今日数据 = 历史基础 + 当前增量
207
230
  const newTodayTokens = (todayState.tokens || 0) + tokenIncrement;
208
231
  const newTodayCost = (todayState.cost || 0) + costIncrement;
209
232
 
@@ -215,9 +238,9 @@ function main() {
215
238
  last_session_cost: currentCost
216
239
  });
217
240
 
218
- // 计算总计
219
- const totalTokens = (historyData.total_tokens_all || 0) + currentSessionTokens;
220
- const totalCost = (historyData.total_cost || 0) + currentCost;
241
+ // 计算总计(使用历史数据 + 当前会话)
242
+ const totalTokens = (historyData.total_tokens_all || 0);
243
+ const totalCost = parseFloat(historyData.total_cost || 0);
221
244
 
222
245
  // 检查是否超过警报阈值
223
246
  const isAlert = newTodayCost >= config.alert_daily;
@@ -78,18 +78,38 @@ function readStats() {
78
78
  const totalOutputTokens = Object.values(data.modelUsage || {}).reduce((sum, model) => sum + (model.outputTokens || 0), 0);
79
79
  const totalCacheReadTokens = Object.values(data.modelUsage || {}).reduce((sum, model) => sum + (model.cacheReadInputTokens || 0), 0);
80
80
 
81
- // 计算今日 tokens(总计 - 历史每日总和,排除今天)
81
+ // 获取今日 tokens
82
+ // 注意:dailyModelTokens 不包含缓存 tokens,且可能更新不及时
83
+ // 如果 lastComputedDate 不是今天,今日数据可能不准确
84
+ const lastComputedDate = data.lastComputedDate || '';
82
85
  const dailyTokens = data.dailyModelTokens || [];
83
- let historySum = 0;
84
- dailyTokens.forEach(day => {
85
- if (day.date !== TODAY) {
86
- const models = day.tokensByModel || {};
87
- historySum += Object.values(models).reduce((sum, tokens) => sum + (tokens || 0), 0);
88
- }
89
- });
90
86
 
91
- const todayTokens = totalInputTokens + totalOutputTokens - historySum;
92
- if (todayTokens < 0) todayTokens = 0;
87
+ const totalTokensAll = totalInputTokens + totalOutputTokens + totalCacheReadTokens;
88
+
89
+ // 检查是否有今日的 dailyModelTokens 记录
90
+ const todayEntry = dailyTokens.find(day => day.date === TODAY);
91
+ let todayTokens = 0;
92
+ if (todayEntry) {
93
+ const models = todayEntry.tokensByModel || {};
94
+ todayTokens = Object.values(models).reduce((sum, tokens) => sum + (tokens || 0), 0);
95
+ // 补偿缓存 tokens(按比例估算)
96
+ const cacheRatio = totalCacheReadTokens / (totalTokensAll || 1);
97
+ todayTokens = Math.floor(todayTokens * (1 + cacheRatio));
98
+ } else if (lastComputedDate !== TODAY) {
99
+ // lastComputedDate 不是今天,无法准确计算今日 tokens
100
+ todayTokens = 0;
101
+ } else {
102
+ // 尝试从总量推算(可能不准确)
103
+ let historySum = 0;
104
+ dailyTokens.forEach(day => {
105
+ if (day.date !== TODAY) {
106
+ const models = day.tokensByModel || {};
107
+ historySum += Object.values(models).reduce((sum, tokens) => sum + (tokens || 0), 0);
108
+ }
109
+ });
110
+ todayTokens = totalTokensAll - historySum;
111
+ if (todayTokens < 0) todayTokens = 0;
112
+ }
93
113
 
94
114
  // 本周 tokens(排除今天,用上面的 todayTokens)
95
115
  const WEEK_START = getWeekStart();
@@ -123,18 +143,17 @@ function readStats() {
123
143
  }
124
144
  });
125
145
 
126
- // 计算费用(简化:按 3/15/0.3 规则)
146
+ // 计算费用(按 3/15/0.3 规则:Input $3/M, Output $15/M, Cache $0.3/M)
127
147
  const totalCost = (totalInputTokens / 1000000 * 3) +
128
148
  (totalOutputTokens / 1000000 * 15) +
129
149
  (totalCacheReadTokens / 1000000 * 0.3);
130
150
 
131
- const totalTokensAll = totalInputTokens + totalOutputTokens + totalCacheReadTokens;
132
-
133
- // 计算各周期费用(按比例分摊)
134
- const todayCost = todayTokens > 0 ? (todayTokens / (totalInputTokens + totalOutputTokens) * totalCost) : 0;
135
- const weekCost = weekTokens > 0 ? (weekTokens / (totalInputTokens + totalOutputTokens) * totalCost) : 0;
136
- const monthCost = monthTokens > 0 ? (monthTokens / (totalInputTokens + totalOutputTokens) * totalCost) : 0;
137
- const lastMonthCost = lastMonthTokens > 0 ? (lastMonthTokens / (totalInputTokens + totalOutputTokens) * totalCost) : 0;
151
+ // 计算各周期费用(按 token 数量比例分摊)
152
+ const totalTokensForRatio = totalTokensAll || 1;
153
+ const todayCost = todayTokens > 0 ? (todayTokens / totalTokensForRatio * totalCost) : 0;
154
+ const weekCost = weekTokens > 0 ? (weekTokens / totalTokensForRatio * totalCost) : 0;
155
+ const monthCost = monthTokens > 0 ? (monthTokens / totalTokensForRatio * totalCost) : 0;
156
+ const lastMonthCost = lastMonthTokens > 0 ? (lastMonthTokens / totalTokensForRatio * totalCost) : 0;
138
157
 
139
158
  // 获取每日数据(用于趋势图)
140
159
  const dailyData = dailyTokens.slice(-7).map(day => ({
@@ -257,10 +276,11 @@ function main() {
257
276
  console.log('');
258
277
 
259
278
  // 统计数据
260
- console.log('📅 今日消费');
279
+ console.log('💰 Token 价格说明');
261
280
  console.log('─'.repeat(40));
262
- console.log(` Tokens: ${formatNumber(stats.today_tokens)}`);
263
- console.log(` 费用: $${stats.today_cost}`);
281
+ console.log(' Input: $3.00 / 百万 tokens');
282
+ console.log(' Output: $15.00 / 百万 tokens');
283
+ console.log(' Cache: $0.30 / 百万 tokens (90% 折扣)');
264
284
  console.log('');
265
285
 
266
286
  console.log('📆 本周消费');
@@ -321,6 +341,57 @@ function main() {
321
341
  console.log(' 🎮 "知识就是财富,而财富就是知识" 🎮');
322
342
  console.log('='.repeat(50) + '\n');
323
343
  break;
344
+ case 'uninstall':
345
+ // 卸载插件
346
+ console.log('\n🗑️ 卸载 Burn Your Money...\n');
347
+
348
+ const homeDir = os.homedir();
349
+ const filesToDelete = [
350
+ path.join(homeDir, '.claude', 'statusline.js'),
351
+ path.join(homeDir, '.claude', 'scripts', 'token-history.js'),
352
+ path.join(homeDir, '.claude', 'commands', 'burn-your-money.md'),
353
+ path.join(homeDir, '.claude', 'commands', 'burn-your-money-stats.md'),
354
+ path.join(homeDir, '.claude', 'commands', 'burn-your-money-export.md'),
355
+ path.join(homeDir, '.claude', 'commands', 'burn-your-money-uninstall.md')
356
+ ];
357
+
358
+ // 删除文件
359
+ let deletedCount = 0;
360
+ filesToDelete.forEach(file => {
361
+ if (fs.existsSync(file)) {
362
+ try {
363
+ fs.unlinkSync(file);
364
+ console.log(` ✓ 已删除: ${path.basename(file)}`);
365
+ deletedCount++;
366
+ } catch (e) {
367
+ console.log(` ✗ 删除失败: ${path.basename(file)} - ${e.message}`);
368
+ }
369
+ }
370
+ });
371
+
372
+ // 移除 settings.json 中的 statusLine 配置
373
+ const settingsFile = path.join(homeDir, '.claude', 'settings.json');
374
+ if (fs.existsSync(settingsFile)) {
375
+ try {
376
+ const settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8'));
377
+ if (settings.statusLine) {
378
+ delete settings.statusLine;
379
+ fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2));
380
+ console.log(` ✓ 已移除 settings.json 中的 statusLine 配置`);
381
+ deletedCount++;
382
+ }
383
+ } catch (e) {
384
+ console.log(` ✗ 更新 settings.json 失败: ${e.message}`);
385
+ }
386
+ }
387
+
388
+ console.log(`\n✅ 卸载完成!共处理 ${deletedCount} 项。`);
389
+ console.log(`\n请重启 Claude Code 使更改生效。\n`);
390
+
391
+ // 如果是通过 npm 安装的,提示也运行 npm uninstall
392
+ console.log('💡 提示:如果你是通过 npm 全局安装的,也可以运行:');
393
+ console.log(' npm uninstall -g @winston.wan/burn-your-money\n');
394
+ break;
324
395
  default:
325
396
  // summary
326
397
  console.log(JSON.stringify(stats, null, 2));
package/uninstall.js CHANGED
@@ -35,7 +35,8 @@ function uninstallFiles() {
35
35
  // 命令文件
36
36
  path.join(home, '.claude', 'commands', 'burn-your-money.md'),
37
37
  path.join(home, '.claude', 'commands', 'burn-your-money-stats.md'),
38
- path.join(home, '.claude', 'commands', 'burn-your-money-export.md')
38
+ path.join(home, '.claude', 'commands', 'burn-your-money-export.md'),
39
+ path.join(home, '.claude', 'commands', 'burn-your-money-uninstall.md')
39
40
  ];
40
41
 
41
42
  log("Removing plugin files...", colors.cyan);