@winston.wan/burn-your-money 2.0.3 → 2.0.5

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
@@ -140,6 +140,24 @@ today_color="\\033[0;31m" # 红色(代表燃烧)
140
140
 
141
141
  实际费用可能因供应商而异。
142
142
 
143
+ ## 🗑️ 卸载
144
+
145
+ 由于 NPM 的限制,`npm uninstall` 无法自动清理配置文件。请手动运行以下命令来彻底卸载:
146
+
147
+ 1. **清理配置和文件**:
148
+ ```bash
149
+ # 如果你还在项目目录下
150
+ npm run uninstall
151
+
152
+ # 或者手动执行卸载脚本 (如果使用 Git Bash)
153
+ curl -fsSL https://raw.githubusercontent.com/winston-wwzhen/burn-your-money/main/uninstall.js | node
154
+ ```
155
+
156
+ 2. **移除 NPM 包**:
157
+ ```bash
158
+ npm uninstall -g @winston.wan/burn-your-money
159
+ ```
160
+
143
161
  ## 🤝 贡献
144
162
 
145
163
  欢迎 PR!特别是:
package/install.js CHANGED
@@ -42,11 +42,29 @@ function checkDependencies() {
42
42
  execSync('jq --version', { stdio: 'ignore' });
43
43
  success("jq is installed");
44
44
  } catch (e) {
45
- warning("jq not found! It is required for the statusline script.");
46
- log(" Please install jq manually:", colors.yellow);
47
- log(" Windows: winget install jqlang.jq", colors.yellow);
48
- log(" macOS: brew install jq", colors.yellow);
49
- log(" Linux: sudo apt-get install jq", colors.yellow);
45
+ warning("jq not found! Attempting valid auto-installation...");
46
+
47
+ try {
48
+ if (os.platform() === 'win32') {
49
+ log("Attempting to install jq via winget...", colors.cyan);
50
+ // silent install might need admin, but let's try
51
+ execSync('winget install jqlang.jq --accept-source-agreements --accept-package-agreements', { stdio: 'inherit' });
52
+ success("jq installed successfully!");
53
+ } else if (os.platform() === 'darwin') {
54
+ log("Attempting to install jq via brew...", colors.cyan);
55
+ execSync('brew install jq', { stdio: 'inherit' });
56
+ success("jq installed successfully!");
57
+ } else {
58
+ // Linux: too many variants (apt, dnf, pacman...), just warn
59
+ throw new Error("Linux auto-install not supported");
60
+ }
61
+ } catch (installError) {
62
+ error("Auto-installation failed.");
63
+ log(" Please install jq manually:", colors.yellow);
64
+ log(" Windows: winget install jqlang.jq", colors.yellow);
65
+ log(" macOS: brew install jq", colors.yellow);
66
+ log(" Linux: sudo apt-get install jq", colors.yellow);
67
+ }
50
68
  }
51
69
 
52
70
  // Check for Claude Code (informational only)
@@ -78,7 +96,7 @@ function installScripts() {
78
96
  log("Installing scripts...", colors.cyan);
79
97
  const home = getHomeDir();
80
98
  const srcDir = path.join(__dirname, 'src');
81
-
99
+
82
100
  const files = [
83
101
  { src: 'statusline.sh', dest: path.join(home, '.claude', 'statusline.sh') },
84
102
  { src: 'token-history.sh', dest: path.join(home, '.claude', 'scripts', 'token-history.sh') }
@@ -121,10 +139,49 @@ function configureSettings() {
121
139
  // accessing existing wsl path if needed? No, standard path is fine.
122
140
  // The previous implementation used `~/.claude/statusline.sh`.
123
141
  // Claude might process `~`? Let's stick to what the bash script did: `~/.claude/statusline.sh`
124
-
142
+
143
+ // Configure command based on platform
144
+ let commandEnv = "~/.claude/statusline.sh";
145
+
146
+ if (os.platform() === 'win32') {
147
+ // On Windows, expand home directory and ensure forward slashes
148
+ const scriptPath = path.join(home, '.claude', 'statusline.sh').replace(/\\/g, '/');
149
+
150
+ // Robust bash detection: try Git Bash first, then fallback to 'bash'
151
+ let bashPath = 'bash';
152
+ // Common Git Bash locations
153
+ const gitBashPaths = [
154
+ 'C:/Program Files/Git/bin/bash.exe',
155
+ 'C:/Program Files/Git/cmd/bash.exe',
156
+ 'C:/Program Files (x86)/Git/bin/bash.exe',
157
+ 'C:/Program Files (x86)/Git/cmd/bash.exe'
158
+ ];
159
+
160
+ let foundBash = false;
161
+ for (const p of gitBashPaths) {
162
+ if (fs.existsSync(p)) {
163
+ bashPath = `"${p}"`;
164
+ foundBash = true;
165
+ break;
166
+ }
167
+ }
168
+
169
+ if (!foundBash) {
170
+ // Warn if we are falling back to potentially broken 'bash' (WSL default)
171
+ try {
172
+ execSync('bash --version', { stdio: 'ignore' });
173
+ } catch (e) {
174
+ warning("Warning: 'bash' command seems broken on this system.");
175
+ log(" Recommend installing Git Bash: https://git-scm.com/download/win", colors.yellow);
176
+ }
177
+ }
178
+
179
+ commandEnv = `${bashPath} "${scriptPath}"`;
180
+ }
181
+
125
182
  settings.statusLine = {
126
183
  type: "command",
127
- command: "~/.claude/statusline.sh"
184
+ command: commandEnv
128
185
  };
129
186
 
130
187
  try {
@@ -143,7 +200,7 @@ function main() {
143
200
  createDirectories();
144
201
  installScripts();
145
202
  configureSettings();
146
-
203
+
147
204
  log("\n✅ Installation complete!", colors.green);
148
205
  log("Please restart Claude Code to see the status bar.\n", colors.cyan);
149
206
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@winston.wan/burn-your-money",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "description": "💸 Burn Your Money - 实时显示 Claude Code 的 token 消耗,看着你的钱包燃烧!",
5
5
  "main": "src/statusline.sh",
6
6
  "bin": {
@@ -8,6 +8,7 @@
8
8
  },
9
9
  "scripts": {
10
10
  "install": "node install.js",
11
+ "uninstall": "node uninstall.js",
11
12
  "test": "bash ./test.sh"
12
13
  },
13
14
  "repository": {
@@ -29,4 +30,4 @@
29
30
  "url": "https://github.com/winston-wwzhen/burn-your-money/issues"
30
31
  },
31
32
  "homepage": "https://github.com/winston-wwzhen/burn-your-money#readme"
32
- }
33
+ }
package/src/statusline.sh CHANGED
@@ -20,12 +20,20 @@ init_config() {
20
20
  fi
21
21
  }
22
22
 
23
+ # 读取配置
23
24
  # 读取配置
24
25
  init_config
25
- THEME=$(jq -r '.theme // "fire"' "$CONFIG_FILE")
26
- ALERT_DAILY=$(jq -r '.alert_daily // 10' "$CONFIG_FILE")
27
- SHOW_BURN_RATE=$(jq -r '.show_burn_rate // true' "$CONFIG_FILE")
28
- SHOW_ETA=$(jq -r '.show_eta // true' "$CONFIG_FILE")
26
+ if [ -f "$CONFIG_FILE" ]; then
27
+ THEME=$(cat "$CONFIG_FILE" | jq -r '.theme // "fire"' 2>/dev/null)
28
+ ALERT_DAILY=$(cat "$CONFIG_FILE" | jq -r '.alert_daily // 10' 2>/dev/null)
29
+ SHOW_BURN_RATE=$(cat "$CONFIG_FILE" | jq -r '.show_burn_rate // true' 2>/dev/null)
30
+ SHOW_ETA=$(cat "$CONFIG_FILE" | jq -r '.show_eta // true' 2>/dev/null)
31
+ else
32
+ THEME="fire"
33
+ ALERT_DAILY=10
34
+ SHOW_BURN_RATE=true
35
+ SHOW_ETA=true
36
+ fi
29
37
 
30
38
  # 主题颜色定义
31
39
  case "$THEME" in
@@ -88,12 +96,12 @@ format_burn_rate() {
88
96
 
89
97
  # 获取或创建会话状态
90
98
  get_session_start_time() {
91
- local session_id=$(jq -r '.session_id // "default"' "$TEMP_JSON" 2>/dev/null || echo "default")
99
+ local session_id=$(cat "$TEMP_JSON" | jq -r '.session_id // "default"' 2>/dev/null || echo "default")
92
100
  local now=$(date +%s)
93
101
 
94
102
  if [ -f "$SESSION_STATE" ]; then
95
- local cached_id=$(jq -r '.session_id // ""' "$SESSION_STATE" 2>/dev/null || echo "")
96
- local cached_time=$(jq -r '.start_time // 0' "$SESSION_STATE" 2>/dev/null || echo "0")
103
+ local cached_id=$(cat "$SESSION_STATE" | jq -r '.session_id // ""' 2>/dev/null || echo "")
104
+ local cached_time=$(cat "$SESSION_STATE" | jq -r '.start_time // 0' 2>/dev/null || echo "0")
97
105
 
98
106
  if [ "$cached_id" = "$session_id" ] && [ "$cached_time" -gt 0 ]; then
99
107
  echo "$cached_time"
@@ -113,11 +121,11 @@ trap "rm -f $TEMP_JSON" EXIT
113
121
  cat > "$TEMP_JSON"
114
122
 
115
123
  # 读取模型名称
116
- model=$(jq -r '.model.display_name // "Unknown"' "$TEMP_JSON" 2>/dev/null || echo "Unknown")
124
+ model=$(cat "$TEMP_JSON" | jq -r '.model.display_name // "Unknown"' 2>/dev/null || echo "Unknown")
117
125
 
118
126
  # 读取当前会话的 token 数据(从 context_window)
119
- current_input=$(jq -r '.context_window.total_input_tokens // 0' "$TEMP_JSON" 2>/dev/null || echo "0")
120
- current_output=$(jq -r '.context_window.total_output_tokens // 0' "$TEMP_JSON" 2>/dev/null || echo "0")
127
+ current_input=$(cat "$TEMP_JSON" | jq -r '.context_window.total_input_tokens // 0' 2>/dev/null || echo "0")
128
+ current_output=$(cat "$TEMP_JSON" | jq -r '.context_window.total_output_tokens // 0' 2>/dev/null || echo "0")
121
129
  current_session_tokens=$((current_input + current_output))
122
130
 
123
131
  # 计算燃烧速度
@@ -46,26 +46,26 @@ read_stats() {
46
46
  local cost_per_m_output=15.0
47
47
 
48
48
  # 获取今日 token:总计 - 历史每日总和(排除今天)
49
- local total_io_tokens=$(jq -r '[.modelUsage["glm-4.7"].inputTokens, .modelUsage["glm-4.7"].outputTokens] | add // 0' "$STATS_CACHE" 2>/dev/null || echo "0")
50
- local history_sum=$(jq -r "[.dailyModelTokens[] | select(.date != \"$TODAY\") | .tokensByModel[\"glm-4.7\"] // 0] | add // 0" "$STATS_CACHE" 2>/dev/null || echo "0")
49
+ local total_io_tokens=$(cat "$STATS_CACHE" | jq -r '[.modelUsage["glm-4.7"].inputTokens, .modelUsage["glm-4.7"].outputTokens] | add // 0' 2>/dev/null || echo "0")
50
+ local history_sum=$(cat "$STATS_CACHE" | jq -r "[.dailyModelTokens[] | select(.date != \"$TODAY\") | .tokensByModel[\"glm-4.7\"] // 0] | add // 0" 2>/dev/null || echo "0")
51
51
  local today_data=$((total_io_tokens - history_sum))
52
52
  [ "$today_data" -lt 0 ] && today_data=0
53
53
 
54
54
  # 本周 token(排除今天,用上面的 today_data)
55
- local week_tokens=$(jq -r "[.dailyModelTokens[] | select(.date >= \"$WEEK_START\" and .date != \"$TODAY\") | .tokensByModel[\"glm-4.7\"] // 0] | add // 0" "$STATS_CACHE" 2>/dev/null || echo "0")
55
+ local week_tokens=$(cat "$STATS_CACHE" | jq -r "[.dailyModelTokens[] | select(.date >= \"$WEEK_START\" and .date != \"$TODAY\") | .tokensByModel[\"glm-4.7\"] // 0] | add // 0" 2>/dev/null || echo "0")
56
56
  week_tokens=$((week_tokens + today_data))
57
57
 
58
58
  # 本月 token
59
- local month_tokens=$(jq -r "[.dailyModelTokens[] | select(.date >= \"$MONTH_START\" and .date != \"$TODAY\") | .tokensByModel[\"glm-4.7\"] // 0] | add // 0" "$STATS_CACHE" 2>/dev/null || echo "0")
59
+ local month_tokens=$(cat "$STATS_CACHE" | jq -r "[.dailyModelTokens[] | select(.date >= \"$MONTH_START\" and .date != \"$TODAY\") | .tokensByModel[\"glm-4.7\"] // 0] | add // 0" 2>/dev/null || echo "0")
60
60
  month_tokens=$((month_tokens + today_data))
61
61
 
62
62
  # 上月 token
63
- local last_month_tokens=$(jq -r "[.dailyModelTokens[] | select(.date >= \"$LAST_MONTH_START\" and .date < \"$MONTH_START\") | .tokensByModel[\"glm-4.7\"] // 0] | add // 0" "$STATS_CACHE" 2>/dev/null || echo "0")
63
+ local last_month_tokens=$(cat "$STATS_CACHE" | jq -r "[.dailyModelTokens[] | select(.date >= \"$LAST_MONTH_START\" and .date < \"$MONTH_START\") | .tokensByModel[\"glm-4.7\"] // 0] | add // 0" 2>/dev/null || echo "0")
64
64
 
65
65
  # 计算全部历史费用
66
- local total_input=$(jq -r '.modelUsage["glm-4.7"].inputTokens // 0' "$STATS_CACHE" 2>/dev/null || echo "0")
67
- local total_output=$(jq -r '.modelUsage["glm-4.7"].outputTokens // 0' "$STATS_CACHE" 2>/dev/null || echo "0")
68
- local total_cache_read=$(jq -r '.modelUsage["glm-4.7"].cacheReadInputTokens // 0' "$STATS_CACHE" 2>/dev/null || echo "0")
66
+ local total_input=$(cat "$STATS_CACHE" | jq -r '.modelUsage["glm-4.7"].inputTokens // 0' 2>/dev/null || echo "0")
67
+ local total_output=$(cat "$STATS_CACHE" | jq -r '.modelUsage["glm-4.7"].outputTokens // 0' 2>/dev/null || echo "0")
68
+ local total_cache_read=$(cat "$STATS_CACHE" | jq -r '.modelUsage["glm-4.7"].cacheReadInputTokens // 0' 2>/dev/null || echo "0")
69
69
  local total_cost=$(awk "BEGIN {input_cost = ${total_input} / 1000000 * ${cost_per_m_input}; output_cost = ${total_output} / 1000000 * ${cost_per_m_output}; cache_cost = ${total_cache_read} / 1000000 * ${cost_per_m_input} * 0.1; printf \"%.2f\", input_cost + output_cost + cache_cost}")
70
70
 
71
71
  # 总 token 数量(包含 cache)
@@ -93,7 +93,7 @@ read_stats() {
93
93
  fi
94
94
 
95
95
  # 获取每日数据(用于趋势图)
96
- local daily_data=$(jq -c "[.dailyModelTokens[-7:][] | {date: .date, tokens: (.tokensByModel[\"glm-4.7\"] // 0)}]" "$STATS_CACHE" 2>/dev/null || echo "[]")
96
+ local daily_data=$(cat "$STATS_CACHE" | jq -c "[.dailyModelTokens[-7:][] | {date: .date, tokens: (.tokensByModel[\"glm-4.7\"] // 0)}]" 2>/dev/null || echo "[]")
97
97
 
98
98
  # 输出 JSON
99
99
  jq -n \
package/uninstall.js ADDED
@@ -0,0 +1,88 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+
5
+ // ANSI colors
6
+ const colors = {
7
+ reset: "\x1b[0m",
8
+ red: "\x1b[31m",
9
+ green: "\x1b[32m",
10
+ yellow: "\x1b[33m",
11
+ cyan: "\x1b[36m"
12
+ };
13
+
14
+ function log(message, color = colors.reset) {
15
+ console.log(`${color}${message}${colors.reset}`);
16
+ }
17
+
18
+ function success(message) {
19
+ log(`✓ ${message}`, colors.green);
20
+ }
21
+
22
+ function warning(message) {
23
+ log(`⚠ ${message}`, colors.yellow);
24
+ }
25
+
26
+ function getHomeDir() {
27
+ return os.homedir();
28
+ }
29
+
30
+ function uninstallFiles() {
31
+ const home = getHomeDir();
32
+ const files = [
33
+ path.join(home, '.claude', 'statusline.sh'),
34
+ path.join(home, '.claude', 'scripts', 'token-history.sh')
35
+ ];
36
+
37
+ log("Removing plugin files...", colors.cyan);
38
+
39
+ files.forEach(file => {
40
+ if (fs.existsSync(file)) {
41
+ try {
42
+ fs.unlinkSync(file);
43
+ success(`Removed ${file}`);
44
+ } catch (e) {
45
+ warning(`Failed to remove ${file}: ${e.message}`);
46
+ }
47
+ } else {
48
+ // log(`File not found: ${file}`, colors.yellow);
49
+ }
50
+ });
51
+ }
52
+
53
+ function restoreSettings() {
54
+ const home = getHomeDir();
55
+ const settingsFile = path.join(home, '.claude', 'settings.json');
56
+
57
+ log("Restoring settings.json...", colors.cyan);
58
+
59
+ if (fs.existsSync(settingsFile)) {
60
+ try {
61
+ let settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8'));
62
+
63
+ if (settings.statusLine) {
64
+ delete settings.statusLine;
65
+ fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2));
66
+ success("Removed statusLine configuration from settings.json");
67
+ } else {
68
+ log("No statusLine configuration found.", colors.yellow);
69
+ }
70
+ } catch (e) {
71
+ warning(`Failed to update settings.json: ${e.message}`);
72
+ }
73
+ } else {
74
+ warning("settings.json not found.");
75
+ }
76
+ }
77
+
78
+ function main() {
79
+ log("\n💸 Burn Your Money - Uninstaller\n", colors.red);
80
+
81
+ uninstallFiles();
82
+ restoreSettings();
83
+
84
+ log("\n✅ Uninstallation complete!", colors.green);
85
+ log("Run 'npm uninstall -g @winston.wan/burn-your-money' to remove the CLI tool.\n", colors.cyan);
86
+ }
87
+
88
+ main();