@winston.wan/burn-your-money 2.1.0 → 2.1.2
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 +88 -57
- package/docs/images/where_is_my_money.png +0 -0
- package/package.json +2 -2
- package/src/statusline.sh +250 -250
- package/src/token-history.sh +268 -268
- package/bash.exe.stackdump +0 -28
package/README.md
CHANGED
|
@@ -1,55 +1,41 @@
|
|
|
1
1
|
# 💸 Burn Your Money
|
|
2
2
|
|
|
3
|
-
> **"Watch your bank account drain in real-time."**
|
|
4
3
|
> **"看着你的存款实时归零。"**
|
|
5
4
|
|
|
6
5
|

|
|
7
6
|

|
|
8
|
-

|
|
9
|
-

|
|
10
|
-
|
|
11
|
-
A **Claude Code** status line plugin that displays your token usage and costs in real-time. Because awareness is the first step towards pain.
|
|
12
7
|
|
|
13
8
|
一个为 **Claude Code** 准备的状态栏插件。它能让你实时看到每一秒花了多少钱——因为知情是痛苦的第一步。
|
|
14
9
|
|
|
15
10
|
---
|
|
16
11
|
|
|
17
|
-
##
|
|
18
|
-
|
|
19
|
-
Using Claude Code feels like coding with superpowers. It also feels like standing in front of a cash incinerator.
|
|
20
|
-
|
|
21
|
-
This plugin adds a simple, Anxiety-Inducing™ meter to your terminal:
|
|
22
|
-
- **Real-time Burn Rate**: See exactly how many tokens/sec you are wasting.
|
|
23
|
-
- **Session Cost**: Watch the dollars tick up while you debug that `undefined` error.
|
|
24
|
-
- **Total Damage**: A cumulative record of your poor financial decisions.
|
|
12
|
+
## 📸 效果预览
|
|
25
13
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
```
|
|
29
|
-
[Claude 3.5 Sonnet] Today: 78.2K $0.47 🔥1.2K tok/s | Total: 285.1M $20.12
|
|
30
|
-
```
|
|
14
|
+

|
|
31
15
|
|
|
32
16
|
---
|
|
33
17
|
|
|
34
|
-
## 🔥 为什么要用?
|
|
18
|
+
## 🔥 为什么要用?
|
|
35
19
|
|
|
36
20
|
Claude Code 很好用,就像在你的命令行里装了一个核动力引擎。但是每次它思考的时候,我都仿佛听到了碎钞机的声音。
|
|
37
21
|
|
|
38
22
|
这就诞生了 **Burn Your Money**。它能在你的状态栏显示:
|
|
39
|
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
23
|
+
|
|
24
|
+
- **实时烧钱速度**:精准显示每秒烧掉多少 token,让你的每一次回车都充满罪恶感
|
|
25
|
+
- **今日战绩**:看看今天又给 Anthropic 贡献了几杯咖啡
|
|
26
|
+
- **历史总计**:累计伤害。建议心脏不好的人不要开启此功能
|
|
42
27
|
|
|
43
28
|
---
|
|
44
29
|
|
|
45
|
-
## 🚀
|
|
30
|
+
## 🚀 安装
|
|
31
|
+
|
|
32
|
+
### 方式 1: NPM 安装(推荐)
|
|
46
33
|
|
|
47
|
-
### Option 1: NPM (Recommended / 推荐)
|
|
48
34
|
```bash
|
|
49
35
|
npm install -g "@winston.wan/burn-your-money"
|
|
50
36
|
```
|
|
51
37
|
|
|
52
|
-
###
|
|
38
|
+
### 方式 2: 脚本安装
|
|
53
39
|
|
|
54
40
|
**Linux / macOS / Git Bash**
|
|
55
41
|
```bash
|
|
@@ -63,66 +49,111 @@ curl.exe -fsSL https://raw.githubusercontent.com/winston-wwzhen/burn-your-money/
|
|
|
63
49
|
|
|
64
50
|
---
|
|
65
51
|
|
|
66
|
-
##
|
|
52
|
+
## ⚙️ 配置
|
|
67
53
|
|
|
68
|
-
|
|
54
|
+
修改 `~/.claude/burn-your-money-config.json` 自定义你的痛苦程度:
|
|
69
55
|
|
|
70
|
-
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"theme": "fire", // 主题: fire, ocean, forest, golden
|
|
59
|
+
"alert_daily": 10.0, // 每日预警金额(美元)
|
|
60
|
+
"show_burn_rate": true // 是否显示燃烧速度
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 主题说明
|
|
65
|
+
|
|
66
|
+
| 主题 | 风格 |
|
|
67
|
+
|------|------|
|
|
68
|
+
| `fire` | 红色火焰风格 |
|
|
69
|
+
| `ocean` | 蓝青海洋风格 |
|
|
70
|
+
| `forest` | 绿色森林风格 |
|
|
71
|
+
| `golden` | 金色奢华风格 |
|
|
71
72
|
|
|
72
73
|
---
|
|
73
74
|
|
|
74
|
-
##
|
|
75
|
+
## 📊 使用命令
|
|
75
76
|
|
|
76
|
-
|
|
77
|
-
修改配置文件来自定义你的痛苦程度。
|
|
77
|
+
### CLI 命令
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
79
|
+
安装后可以在终端使用 `burn-your-money` 命令:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
burn-your-money status # 查看完整状态
|
|
83
|
+
burn-your-money theme ocean # 切换主题
|
|
84
|
+
burn-your-money alert 20 # 设置每日预警金额
|
|
85
|
+
burn-your-money chart # 显示趋势图表
|
|
86
|
+
burn-your-money today # 今日统计
|
|
87
|
+
burn-your-money week # 本周统计
|
|
88
|
+
burn-your-money month # 本月统计
|
|
89
|
+
burn-your-money total # 总计统计
|
|
85
90
|
```
|
|
86
91
|
|
|
92
|
+
### Claude Code 内置命令
|
|
93
|
+
|
|
94
|
+
在 Claude Code 对话中可以直接使用:
|
|
95
|
+
|
|
96
|
+
| 命令 | 说明 |
|
|
97
|
+
|------|------|
|
|
98
|
+
| `/burn-your-money-stats` | 查看 token 使用趋势图 |
|
|
99
|
+
| `/burn-your-money-today` | 查看今日 token 使用情况 |
|
|
100
|
+
| `/burn-your-money-week` | 查看本周 token 使用情况 |
|
|
101
|
+
| `/burn-your-money-month` | 查看本月 token 使用情况 |
|
|
102
|
+
| `/burn-your-money-export` | 导出 token 数据(JSON 格式) |
|
|
103
|
+
| `/burn-your-money-export-csv` | 导出 token 数据(CSV 格式) |
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 🛠️ 性能说明
|
|
108
|
+
|
|
109
|
+
我们深知现在的 API 有多贵,所以本插件**绝不**发起任何网络请求。所有数据纯本地计算,它只烧你的钱,不烧你的 CPU。
|
|
110
|
+
|
|
87
111
|
---
|
|
88
112
|
|
|
89
|
-
##
|
|
113
|
+
## 📋 常见问题
|
|
114
|
+
|
|
115
|
+
### 1. Token 统计和 `/stats` 不一致?
|
|
116
|
+
|
|
117
|
+
本插件统计**包含所有类型的 token**(输入、输出、缓存读取)。Claude Code 内置的 `/stats` 命令可能只显示"完整价格"的 tokens(输入 + 输出),不包含享受 90% 折扣的缓存读取 tokens。
|
|
118
|
+
|
|
119
|
+
示例对比:
|
|
120
|
+
- 本插件:`3.93B tokens`(包含缓存)
|
|
121
|
+
- `/stats`:`188M tokens`(不含缓存)
|
|
90
122
|
|
|
91
|
-
|
|
92
|
-
This plugin includes **all token types** (input, output, and cache-read tokens) in its statistics. Claude Code's built-in `/stats` command may only display "full-price" tokens (input + output), excluding cache-read tokens which receive a 90% discount.
|
|
123
|
+
### 2. 费用计算准确吗?
|
|
93
124
|
|
|
94
|
-
|
|
125
|
+
- **当前会话费用**:完全准确,使用 Claude Code 提供的实时数据
|
|
126
|
+
- **历史费用统计**:基于固定费率估算(Input $3/M, Output $15/M, Cache Read $0.3/M),如果频繁切换不同模型,可能略有偏差
|
|
95
127
|
|
|
96
|
-
|
|
97
|
-
- This plugin / 本插件: `3.93B tokens` (includes cache-read / 包含缓存)
|
|
98
|
-
- `/stats` command: `188M tokens` (excludes cache-read / 不含缓存)
|
|
128
|
+
### 3. Windows 上出现 fork 错误?
|
|
99
129
|
|
|
100
|
-
|
|
101
|
-
The *historical* cost calculation (in `Total` and `History`) uses a fixed rate estimate (based on Sonnet 3.5). If you frequently switch between Haiku/Opus, the historical dollar amount might be slightly off. (Real-time session cost is always accurate though!)
|
|
102
|
-
*历史*费用统计基于固定费率估算。如果你经常切换不同模型,历史总金额可能不完全准确。(但当前会话的实时扣费是绝对准的!)
|
|
130
|
+
这是 Git Bash 和 Windows Defender 的兼容性问题。运行以下脚本修复:
|
|
103
131
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
132
|
+
```powershell
|
|
133
|
+
# 以管理员身份运行 PowerShell
|
|
134
|
+
.\fix-git-bash.ps1
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
或手动将以下目录添加到 Windows Defender 排除列表:
|
|
138
|
+
- `C:\Program Files\Git`
|
|
139
|
+
- `%USERPROFILE%\.claude`
|
|
107
140
|
|
|
108
141
|
---
|
|
109
142
|
|
|
110
|
-
##
|
|
143
|
+
## 🗑️ 卸载
|
|
111
144
|
|
|
112
|
-
If the truth is too painful:
|
|
113
145
|
如果现实太过沉重:
|
|
114
146
|
|
|
115
147
|
```bash
|
|
116
|
-
npm uninstall -g @winston.wan/burn-your-money
|
|
117
|
-
# Then restart Claude Code / 然后重启 Claude Code
|
|
148
|
+
npm uninstall -g "@winston.wan/burn-your-money"
|
|
118
149
|
```
|
|
119
150
|
|
|
151
|
+
然后重启 Claude Code。
|
|
152
|
+
|
|
120
153
|
---
|
|
121
154
|
|
|
122
|
-
## 😄
|
|
155
|
+
## 😄 免责声明
|
|
123
156
|
|
|
124
|
-
This plugin does not actually reduce your token usage. It only increases your blood pressure.
|
|
125
157
|
本插件并不能通过任何技术手段减少你的 Token 消耗,它只能(显著地)升高你的血压。
|
|
126
158
|
|
|
127
|
-
**Enjoy the burn. 🔥**
|
|
128
159
|
**享受燃烧的感觉吧。🔥**
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@winston.wan/burn-your-money",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "💸 Burn Your Money - 实时显示 Claude Code 的 token 消耗,看着你的钱包燃烧!",
|
|
5
5
|
"main": "src/statusline.sh",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"postinstall": "node install.js",
|
|
11
|
-
"
|
|
11
|
+
"preuninstall": "node uninstall.js",
|
|
12
12
|
"test": "bash ./test.sh"
|
|
13
13
|
},
|
|
14
14
|
"repository": {
|
package/src/statusline.sh
CHANGED
|
@@ -1,250 +1,250 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Burn Your Money - Claude Code 状态栏脚本
|
|
3
|
-
# 格式:[模型] 今日:token 费用 🔥速度 | 总计:token 费用
|
|
4
|
-
|
|
5
|
-
# 设置 jq 命令路径(支持自定义安装的 jq)
|
|
6
|
-
if [ -n "$JQ_PATH" ]; then
|
|
7
|
-
JQ_CMD="$JQ_PATH"
|
|
8
|
-
elif [ -f "$HOME/.claude/bin/jq.exe" ]; then
|
|
9
|
-
JQ_CMD="$HOME/.claude/bin/jq.exe"
|
|
10
|
-
elif [ -f "$HOME/.claude/bin/jq" ]; then
|
|
11
|
-
JQ_CMD="$HOME/.claude/bin/jq"
|
|
12
|
-
else
|
|
13
|
-
JQ_CMD="jq"
|
|
14
|
-
fi
|
|
15
|
-
|
|
16
|
-
# 验证 jq 是否可用
|
|
17
|
-
if ! command -v "$JQ_CMD" >/dev/null 2>&1; then
|
|
18
|
-
# jq 不可用,返回简单错误信息
|
|
19
|
-
printf "[BurnYourMoney] Error: jq not found. Run: npm install -g @winston.wan/burn-your-money"
|
|
20
|
-
exit 0
|
|
21
|
-
fi
|
|
22
|
-
|
|
23
|
-
# 配置文件
|
|
24
|
-
CONFIG_FILE="$HOME/.claude/burn-your-money-config.json"
|
|
25
|
-
SESSION_STATE="$HOME/.claude/cache/session-state.json"
|
|
26
|
-
TODAY_STATE="$HOME/.claude/cache/today-state.json"
|
|
27
|
-
HISTORY_CACHE="$HOME/.claude/cache/history-cache.json"
|
|
28
|
-
|
|
29
|
-
# 初始化配置
|
|
30
|
-
init_config() {
|
|
31
|
-
if [ ! -f "$CONFIG_FILE" ]; then
|
|
32
|
-
$JQ_CMD -n '{
|
|
33
|
-
theme: "fire",
|
|
34
|
-
alert_daily: 10.0,
|
|
35
|
-
alert_weekly: 50.0,
|
|
36
|
-
show_burn_rate: true,
|
|
37
|
-
show_eta: true,
|
|
38
|
-
show_trend: false
|
|
39
|
-
}' > "$CONFIG_FILE"
|
|
40
|
-
fi
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
# 1. 批量读取配置 (One JQ pass)
|
|
44
|
-
init_config
|
|
45
|
-
if [ -f "$CONFIG_FILE" ]; then
|
|
46
|
-
# 使用 eval 批量赋值变量
|
|
47
|
-
eval $("$JQ_CMD" -r '@sh "THEME=\(.theme // "fire") ALERT_DAILY=\(.alert_daily // 10) SHOW_BURN_RATE=\(.show_burn_rate // true)"' "$CONFIG_FILE" 2>/dev/null)
|
|
48
|
-
else
|
|
49
|
-
THEME="fire"
|
|
50
|
-
ALERT_DAILY=10
|
|
51
|
-
SHOW_BURN_RATE=true
|
|
52
|
-
fi
|
|
53
|
-
|
|
54
|
-
# 主题颜色定义
|
|
55
|
-
case "$THEME" in
|
|
56
|
-
fire) TODAY_COLOR="0;31"; COST_COLOR="0;31"; BURN_COLOR="0;31" ;;
|
|
57
|
-
ocean) TODAY_COLOR="0;34"; COST_COLOR="0;36"; BURN_COLOR="0;36" ;;
|
|
58
|
-
forest) TODAY_COLOR="0;32"; COST_COLOR="0;33"; BURN_COLOR="0;32" ;;
|
|
59
|
-
golden) TODAY_COLOR="0;33"; COST_COLOR="0;33"; BURN_COLOR="1;33" ;;
|
|
60
|
-
*) TODAY_COLOR="0;36"; COST_COLOR="0;34"; BURN_COLOR="0;31" ;;
|
|
61
|
-
esac
|
|
62
|
-
|
|
63
|
-
# 格式化函数 (纯 Bash/Awk 实现,无外部进程调用)
|
|
64
|
-
format_number() {
|
|
65
|
-
local num=$1
|
|
66
|
-
if [ "$num" -ge 1000000000 ]; then awk "BEGIN {printf \"%.1fB\", $num/1000000000}";
|
|
67
|
-
elif [ "$num" -ge 1000000 ]; then awk "BEGIN {printf \"%.1fM\", $num/1000000}";
|
|
68
|
-
elif [ "$num" -ge 1000 ]; then awk "BEGIN {printf \"%.1fK\", $num/1000}";
|
|
69
|
-
else echo "$num"; fi
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
format_cost() {
|
|
73
|
-
awk "BEGIN {printf \"\$%.2f\", $1}"
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
format_burn_rate() {
|
|
77
|
-
local rate=$1
|
|
78
|
-
if [ "$rate" -ge 1000 ]; then awk "BEGIN {printf \"%.1fK tok/s\", $rate/1000}";
|
|
79
|
-
else awk "BEGIN {printf \"%.0f tok/s\", $rate}"; fi
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
# 2. 核心数据解析 (One JQ pass from stdin)
|
|
83
|
-
# 将 stdin 存入临时文件(避免大 JSON 管道卡顿)
|
|
84
|
-
TEMP_JSON=$(mktemp)
|
|
85
|
-
trap "rm -f $TEMP_JSON" EXIT
|
|
86
|
-
cat > "$TEMP_JSON"
|
|
87
|
-
|
|
88
|
-
# 一次性提取所有需要的字段
|
|
89
|
-
# 注意:使用 @sh 确保字符串安全引用
|
|
90
|
-
eval $("$JQ_CMD" -r '
|
|
91
|
-
@sh "model_name=\(.model.display_name // "Unknown")
|
|
92
|
-
session_id=\(.session_id // "default")
|
|
93
|
-
input=\(.context_window.total_input_tokens // 0)
|
|
94
|
-
output=\(.context_window.total_output_tokens // 0)
|
|
95
|
-
cache=\(.context_window.current_usage.cache_read_input_tokens // 0)
|
|
96
|
-
current_cost=\(.cost.total_cost_usd // 0)"
|
|
97
|
-
' "$TEMP_JSON" 2>/dev/null)
|
|
98
|
-
|
|
99
|
-
# 计算当前会话 Token 总量
|
|
100
|
-
current_session_tokens=$((input + output + cache))
|
|
101
|
-
|
|
102
|
-
# 3. 燃烧速度计算 (Burn Rate)
|
|
103
|
-
current_time=$(date +%s)
|
|
104
|
-
start_time_cache=0
|
|
105
|
-
|
|
106
|
-
# 读取 Session State (尽可能不做写入,除非是新 Session)
|
|
107
|
-
if [ -f "$SESSION_STATE" ]; then
|
|
108
|
-
eval $("$JQ_CMD" -r '@sh "cached_id=\(.session_id // "") cached_time=\(.start_time // 0)"' "$SESSION_STATE" 2>/dev/null)
|
|
109
|
-
else
|
|
110
|
-
cached_id=""
|
|
111
|
-
cached_time=0
|
|
112
|
-
fi
|
|
113
|
-
|
|
114
|
-
if [ "$cached_id" = "$session_id" ] && [ "$cached_time" -ne 0 ]; then
|
|
115
|
-
start_time_cache=$cached_time
|
|
116
|
-
else
|
|
117
|
-
# New session detected - 使用原子写入
|
|
118
|
-
start_time_cache=$current_time
|
|
119
|
-
SESSION_STATE_TMP="${SESSION_STATE}.tmp.$$"
|
|
120
|
-
$JQ_CMD -n --arg id "$session_id" --arg time "$current_time" '{session_id: $id, start_time: ($time | tonumber)}' > "$SESSION_STATE_TMP" 2>/dev/null && mv "$SESSION_STATE_TMP" "$SESSION_STATE"
|
|
121
|
-
fi
|
|
122
|
-
|
|
123
|
-
elapsed=$((current_time - start_time_cache))
|
|
124
|
-
burn_rate=0
|
|
125
|
-
if [ "$elapsed" -gt 0 ] && [ "$current_session_tokens" -gt 0 ]; then
|
|
126
|
-
burn_rate=$(awk "BEGIN {printf \"%.0f\", ${current_session_tokens} / ${elapsed}}")
|
|
127
|
-
fi
|
|
128
|
-
|
|
129
|
-
# 4. 历史数据缓存 (每 30 秒刷新)
|
|
130
|
-
# 确保 cache 目录存在
|
|
131
|
-
mkdir -p "$(dirname "$HISTORY_CACHE")" 2>/dev/null
|
|
132
|
-
|
|
133
|
-
now=$(date +%s)
|
|
134
|
-
cache_age=999999
|
|
135
|
-
if [ -f "$HISTORY_CACHE" ]; then
|
|
136
|
-
# Portable modification time check
|
|
137
|
-
if [ "$(uname)" = "Darwin" ]; then
|
|
138
|
-
cache_time=$(stat -f %m "$HISTORY_CACHE" 2>/dev/null || echo 0)
|
|
139
|
-
else
|
|
140
|
-
cache_time=$(stat -c %Y "$HISTORY_CACHE" 2>/dev/null || echo 0)
|
|
141
|
-
fi
|
|
142
|
-
cache_age=$((now - cache_time))
|
|
143
|
-
else
|
|
144
|
-
echo '{}' > "$HISTORY_CACHE"
|
|
145
|
-
fi
|
|
146
|
-
|
|
147
|
-
# 如果缓存太旧,并在后台刷新(虽然后台刷新可能在 statusline 不合适,但我们可以同步调用历史脚本)
|
|
148
|
-
# 为了性能,这里我们仍然做同步调用,但减少频率
|
|
149
|
-
if [ $cache_age -gt 30 ]; then
|
|
150
|
-
# 重新生成历史数据
|
|
151
|
-
~/.claude/scripts/token-history.sh summary > "$HISTORY_CACHE" 2>/dev/null
|
|
152
|
-
fi
|
|
153
|
-
|
|
154
|
-
# 读取历史总计 (One pass)
|
|
155
|
-
eval $("$JQ_CMD" -r '@sh "history_total_tokens=\(.total_tokens_all // 0) history_total_cost=\(.total_cost // 0)"' "$HISTORY_CACHE" 2>/dev/null)
|
|
156
|
-
|
|
157
|
-
# 5. 今日数据持久化 (One-pass Read & Write)
|
|
158
|
-
# 逻辑:读取旧状态 -> 计算差值 -> 写入新状态
|
|
159
|
-
today_date=$(date +%Y-%m-%d)
|
|
160
|
-
|
|
161
|
-
if [ -f "$TODAY_STATE" ]; then
|
|
162
|
-
eval $("$JQ_CMD" -r '@sh "saved_date=\(.date // "") saved_tokens=\(.tokens // 0) saved_cost=\(.cost // 0) last_session_tokens=\(.last_session_tokens // 0) last_session_cost=\(.last_session_cost // 0)"' "$TODAY_STATE" 2>/dev/null)
|
|
163
|
-
else
|
|
164
|
-
saved_date=""
|
|
165
|
-
saved_tokens=0
|
|
166
|
-
saved_cost=0
|
|
167
|
-
last_session_tokens=0
|
|
168
|
-
last_session_cost=0
|
|
169
|
-
fi
|
|
170
|
-
|
|
171
|
-
if [ "$saved_date" != "$today_date" ]; then
|
|
172
|
-
# 新的一天,重置累计数据
|
|
173
|
-
saved_tokens=0
|
|
174
|
-
saved_cost=0
|
|
175
|
-
last_session_tokens=0
|
|
176
|
-
last_session_cost=0
|
|
177
|
-
fi
|
|
178
|
-
|
|
179
|
-
# 计算增量
|
|
180
|
-
token_increment=$((current_session_tokens - last_session_tokens))
|
|
181
|
-
if [ "$token_increment" -lt 0 ]; then token_increment=0; fi # 防御性编程
|
|
182
|
-
|
|
183
|
-
# 浮点数增量计算
|
|
184
|
-
cost_increment=$(awk "BEGIN {print ($current_cost - $last_session_cost < 0 ? 0 : $current_cost - $last_session_cost)}")
|
|
185
|
-
|
|
186
|
-
# 计算新的今日累计
|
|
187
|
-
new_today_tokens=$((saved_tokens + token_increment))
|
|
188
|
-
new_today_cost=$(awk "BEGIN {printf \"%.5f\", $saved_cost + $cost_increment}")
|
|
189
|
-
|
|
190
|
-
# 写入新状态 (一次性写入,使用原子写入避免并发问题)
|
|
191
|
-
# 先写入临时文件,然后重命名,确保原子性
|
|
192
|
-
TODAY_STATE_TMP="${TODAY_STATE}.tmp.$$"
|
|
193
|
-
$JQ_CMD -n \
|
|
194
|
-
--arg date "$today_date" \
|
|
195
|
-
--arg tokens "$new_today_tokens" \
|
|
196
|
-
--arg cost "$new_today_cost" \
|
|
197
|
-
--arg last_sess_tok "$current_session_tokens" \
|
|
198
|
-
--arg last_sess_cost "$current_cost" \
|
|
199
|
-
'{
|
|
200
|
-
date: $date,
|
|
201
|
-
tokens: ($tokens|tonumber),
|
|
202
|
-
cost: ($cost|tonumber),
|
|
203
|
-
last_session_tokens: ($last_sess_tok|tonumber),
|
|
204
|
-
last_session_cost: ($last_sess_cost|tonumber)
|
|
205
|
-
}' > "$TODAY_STATE_TMP" 2>/dev/null && mv "$TODAY_STATE_TMP" "$TODAY_STATE"
|
|
206
|
-
|
|
207
|
-
# 6. 最终计算与显示
|
|
208
|
-
total_tokens_all=$((history_total_tokens + current_session_tokens))
|
|
209
|
-
total_cost_all=$(awk "BEGIN {print $history_total_cost + $current_cost}")
|
|
210
|
-
|
|
211
|
-
# 警报检查
|
|
212
|
-
is_alert=false
|
|
213
|
-
if awk "BEGIN {exit !($new_today_cost >= $ALERT_DAILY)}"; then
|
|
214
|
-
is_alert=true
|
|
215
|
-
fi
|
|
216
|
-
|
|
217
|
-
# 构建输出字符串
|
|
218
|
-
output=""
|
|
219
|
-
|
|
220
|
-
# [Model]
|
|
221
|
-
if [ "$is_alert" = true ]; then
|
|
222
|
-
output+="\\033[1;31m[${model_name}]\\033[0m "
|
|
223
|
-
else
|
|
224
|
-
output+="\\033[0;90m[${model_name}]\\033[0m "
|
|
225
|
-
fi
|
|
226
|
-
|
|
227
|
-
# Today
|
|
228
|
-
d_today_tokens=$(format_number $new_today_tokens)
|
|
229
|
-
d_today_cost=$(format_cost $new_today_cost)
|
|
230
|
-
|
|
231
|
-
if [ "$new_today_tokens" -gt 0 ]; then
|
|
232
|
-
output+="\\033[${TODAY_COLOR}m今日:${d_today_tokens}\\033[0m "
|
|
233
|
-
output+="\\033[${COST_COLOR}m${d_today_cost}\\033[0m "
|
|
234
|
-
else
|
|
235
|
-
output+="\\033[0;90m今日:0\\033[0m \\033[0;90m\$0.00\\033[0m "
|
|
236
|
-
fi
|
|
237
|
-
|
|
238
|
-
# Burn Rate
|
|
239
|
-
if [ "$SHOW_BURN_RATE" = "true" ] && [ "$burn_rate" -gt 0 ]; then
|
|
240
|
-
d_burn=$(format_burn_rate $burn_rate)
|
|
241
|
-
output+="\\033[${BURN_COLOR}m🔥${d_burn}\\033[0m "
|
|
242
|
-
fi
|
|
243
|
-
|
|
244
|
-
# Total
|
|
245
|
-
d_total_tokens=$(format_number $total_tokens_all)
|
|
246
|
-
d_total_cost=$(format_cost $total_cost_all)
|
|
247
|
-
output+="| \\033[0;90m总计:${d_total_tokens}\\033[0m \\033[0;90m${d_total_cost}\\033[0m "
|
|
248
|
-
|
|
249
|
-
# Print final result
|
|
250
|
-
printf "%b" "$output"
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Burn Your Money - Claude Code 状态栏脚本
|
|
3
|
+
# 格式:[模型] 今日:token 费用 🔥速度 | 总计:token 费用
|
|
4
|
+
|
|
5
|
+
# 设置 jq 命令路径(支持自定义安装的 jq)
|
|
6
|
+
if [ -n "$JQ_PATH" ]; then
|
|
7
|
+
JQ_CMD="$JQ_PATH"
|
|
8
|
+
elif [ -f "$HOME/.claude/bin/jq.exe" ]; then
|
|
9
|
+
JQ_CMD="$HOME/.claude/bin/jq.exe"
|
|
10
|
+
elif [ -f "$HOME/.claude/bin/jq" ]; then
|
|
11
|
+
JQ_CMD="$HOME/.claude/bin/jq"
|
|
12
|
+
else
|
|
13
|
+
JQ_CMD="jq"
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
# 验证 jq 是否可用
|
|
17
|
+
if ! command -v "$JQ_CMD" >/dev/null 2>&1; then
|
|
18
|
+
# jq 不可用,返回简单错误信息
|
|
19
|
+
printf "[BurnYourMoney] Error: jq not found. Run: npm install -g @winston.wan/burn-your-money"
|
|
20
|
+
exit 0
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# 配置文件
|
|
24
|
+
CONFIG_FILE="$HOME/.claude/burn-your-money-config.json"
|
|
25
|
+
SESSION_STATE="$HOME/.claude/cache/session-state.json"
|
|
26
|
+
TODAY_STATE="$HOME/.claude/cache/today-state.json"
|
|
27
|
+
HISTORY_CACHE="$HOME/.claude/cache/history-cache.json"
|
|
28
|
+
|
|
29
|
+
# 初始化配置
|
|
30
|
+
init_config() {
|
|
31
|
+
if [ ! -f "$CONFIG_FILE" ]; then
|
|
32
|
+
$JQ_CMD -n '{
|
|
33
|
+
theme: "fire",
|
|
34
|
+
alert_daily: 10.0,
|
|
35
|
+
alert_weekly: 50.0,
|
|
36
|
+
show_burn_rate: true,
|
|
37
|
+
show_eta: true,
|
|
38
|
+
show_trend: false
|
|
39
|
+
}' > "$CONFIG_FILE"
|
|
40
|
+
fi
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# 1. 批量读取配置 (One JQ pass)
|
|
44
|
+
init_config
|
|
45
|
+
if [ -f "$CONFIG_FILE" ]; then
|
|
46
|
+
# 使用 eval 批量赋值变量
|
|
47
|
+
eval $("$JQ_CMD" -r '@sh "THEME=\(.theme // "fire") ALERT_DAILY=\(.alert_daily // 10) SHOW_BURN_RATE=\(.show_burn_rate // true)"' "$CONFIG_FILE" 2>/dev/null)
|
|
48
|
+
else
|
|
49
|
+
THEME="fire"
|
|
50
|
+
ALERT_DAILY=10
|
|
51
|
+
SHOW_BURN_RATE=true
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# 主题颜色定义
|
|
55
|
+
case "$THEME" in
|
|
56
|
+
fire) TODAY_COLOR="0;31"; COST_COLOR="0;31"; BURN_COLOR="0;31" ;;
|
|
57
|
+
ocean) TODAY_COLOR="0;34"; COST_COLOR="0;36"; BURN_COLOR="0;36" ;;
|
|
58
|
+
forest) TODAY_COLOR="0;32"; COST_COLOR="0;33"; BURN_COLOR="0;32" ;;
|
|
59
|
+
golden) TODAY_COLOR="0;33"; COST_COLOR="0;33"; BURN_COLOR="1;33" ;;
|
|
60
|
+
*) TODAY_COLOR="0;36"; COST_COLOR="0;34"; BURN_COLOR="0;31" ;;
|
|
61
|
+
esac
|
|
62
|
+
|
|
63
|
+
# 格式化函数 (纯 Bash/Awk 实现,无外部进程调用)
|
|
64
|
+
format_number() {
|
|
65
|
+
local num=$1
|
|
66
|
+
if [ "$num" -ge 1000000000 ]; then awk "BEGIN {printf \"%.1fB\", $num/1000000000}";
|
|
67
|
+
elif [ "$num" -ge 1000000 ]; then awk "BEGIN {printf \"%.1fM\", $num/1000000}";
|
|
68
|
+
elif [ "$num" -ge 1000 ]; then awk "BEGIN {printf \"%.1fK\", $num/1000}";
|
|
69
|
+
else echo "$num"; fi
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
format_cost() {
|
|
73
|
+
awk "BEGIN {printf \"\$%.2f\", $1}"
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
format_burn_rate() {
|
|
77
|
+
local rate=$1
|
|
78
|
+
if [ "$rate" -ge 1000 ]; then awk "BEGIN {printf \"%.1fK tok/s\", $rate/1000}";
|
|
79
|
+
else awk "BEGIN {printf \"%.0f tok/s\", $rate}"; fi
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# 2. 核心数据解析 (One JQ pass from stdin)
|
|
83
|
+
# 将 stdin 存入临时文件(避免大 JSON 管道卡顿)
|
|
84
|
+
TEMP_JSON=$(mktemp)
|
|
85
|
+
trap "rm -f $TEMP_JSON" EXIT
|
|
86
|
+
cat > "$TEMP_JSON"
|
|
87
|
+
|
|
88
|
+
# 一次性提取所有需要的字段
|
|
89
|
+
# 注意:使用 @sh 确保字符串安全引用
|
|
90
|
+
eval $("$JQ_CMD" -r '
|
|
91
|
+
@sh "model_name=\(.model.display_name // "Unknown")
|
|
92
|
+
session_id=\(.session_id // "default")
|
|
93
|
+
input=\(.context_window.total_input_tokens // 0)
|
|
94
|
+
output=\(.context_window.total_output_tokens // 0)
|
|
95
|
+
cache=\(.context_window.current_usage.cache_read_input_tokens // 0)
|
|
96
|
+
current_cost=\(.cost.total_cost_usd // 0)"
|
|
97
|
+
' "$TEMP_JSON" 2>/dev/null)
|
|
98
|
+
|
|
99
|
+
# 计算当前会话 Token 总量
|
|
100
|
+
current_session_tokens=$((input + output + cache))
|
|
101
|
+
|
|
102
|
+
# 3. 燃烧速度计算 (Burn Rate)
|
|
103
|
+
current_time=$(date +%s)
|
|
104
|
+
start_time_cache=0
|
|
105
|
+
|
|
106
|
+
# 读取 Session State (尽可能不做写入,除非是新 Session)
|
|
107
|
+
if [ -f "$SESSION_STATE" ]; then
|
|
108
|
+
eval $("$JQ_CMD" -r '@sh "cached_id=\(.session_id // "") cached_time=\(.start_time // 0)"' "$SESSION_STATE" 2>/dev/null)
|
|
109
|
+
else
|
|
110
|
+
cached_id=""
|
|
111
|
+
cached_time=0
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
if [ "$cached_id" = "$session_id" ] && [ "$cached_time" -ne 0 ]; then
|
|
115
|
+
start_time_cache=$cached_time
|
|
116
|
+
else
|
|
117
|
+
# New session detected - 使用原子写入
|
|
118
|
+
start_time_cache=$current_time
|
|
119
|
+
SESSION_STATE_TMP="${SESSION_STATE}.tmp.$$"
|
|
120
|
+
$JQ_CMD -n --arg id "$session_id" --arg time "$current_time" '{session_id: $id, start_time: ($time | tonumber)}' > "$SESSION_STATE_TMP" 2>/dev/null && mv "$SESSION_STATE_TMP" "$SESSION_STATE"
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
elapsed=$((current_time - start_time_cache))
|
|
124
|
+
burn_rate=0
|
|
125
|
+
if [ "$elapsed" -gt 0 ] && [ "$current_session_tokens" -gt 0 ]; then
|
|
126
|
+
burn_rate=$(awk "BEGIN {printf \"%.0f\", ${current_session_tokens} / ${elapsed}}")
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
# 4. 历史数据缓存 (每 30 秒刷新)
|
|
130
|
+
# 确保 cache 目录存在
|
|
131
|
+
mkdir -p "$(dirname "$HISTORY_CACHE")" 2>/dev/null
|
|
132
|
+
|
|
133
|
+
now=$(date +%s)
|
|
134
|
+
cache_age=999999
|
|
135
|
+
if [ -f "$HISTORY_CACHE" ]; then
|
|
136
|
+
# Portable modification time check
|
|
137
|
+
if [ "$(uname)" = "Darwin" ]; then
|
|
138
|
+
cache_time=$(stat -f %m "$HISTORY_CACHE" 2>/dev/null || echo 0)
|
|
139
|
+
else
|
|
140
|
+
cache_time=$(stat -c %Y "$HISTORY_CACHE" 2>/dev/null || echo 0)
|
|
141
|
+
fi
|
|
142
|
+
cache_age=$((now - cache_time))
|
|
143
|
+
else
|
|
144
|
+
echo '{}' > "$HISTORY_CACHE"
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
# 如果缓存太旧,并在后台刷新(虽然后台刷新可能在 statusline 不合适,但我们可以同步调用历史脚本)
|
|
148
|
+
# 为了性能,这里我们仍然做同步调用,但减少频率
|
|
149
|
+
if [ $cache_age -gt 30 ]; then
|
|
150
|
+
# 重新生成历史数据
|
|
151
|
+
~/.claude/scripts/token-history.sh summary > "$HISTORY_CACHE" 2>/dev/null
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
# 读取历史总计 (One pass)
|
|
155
|
+
eval $("$JQ_CMD" -r '@sh "history_total_tokens=\(.total_tokens_all // 0) history_total_cost=\(.total_cost // 0)"' "$HISTORY_CACHE" 2>/dev/null)
|
|
156
|
+
|
|
157
|
+
# 5. 今日数据持久化 (One-pass Read & Write)
|
|
158
|
+
# 逻辑:读取旧状态 -> 计算差值 -> 写入新状态
|
|
159
|
+
today_date=$(date +%Y-%m-%d)
|
|
160
|
+
|
|
161
|
+
if [ -f "$TODAY_STATE" ]; then
|
|
162
|
+
eval $("$JQ_CMD" -r '@sh "saved_date=\(.date // "") saved_tokens=\(.tokens // 0) saved_cost=\(.cost // 0) last_session_tokens=\(.last_session_tokens // 0) last_session_cost=\(.last_session_cost // 0)"' "$TODAY_STATE" 2>/dev/null)
|
|
163
|
+
else
|
|
164
|
+
saved_date=""
|
|
165
|
+
saved_tokens=0
|
|
166
|
+
saved_cost=0
|
|
167
|
+
last_session_tokens=0
|
|
168
|
+
last_session_cost=0
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
if [ "$saved_date" != "$today_date" ]; then
|
|
172
|
+
# 新的一天,重置累计数据
|
|
173
|
+
saved_tokens=0
|
|
174
|
+
saved_cost=0
|
|
175
|
+
last_session_tokens=0
|
|
176
|
+
last_session_cost=0
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
# 计算增量
|
|
180
|
+
token_increment=$((current_session_tokens - last_session_tokens))
|
|
181
|
+
if [ "$token_increment" -lt 0 ]; then token_increment=0; fi # 防御性编程
|
|
182
|
+
|
|
183
|
+
# 浮点数增量计算
|
|
184
|
+
cost_increment=$(awk "BEGIN {print ($current_cost - $last_session_cost < 0 ? 0 : $current_cost - $last_session_cost)}")
|
|
185
|
+
|
|
186
|
+
# 计算新的今日累计
|
|
187
|
+
new_today_tokens=$((saved_tokens + token_increment))
|
|
188
|
+
new_today_cost=$(awk "BEGIN {printf \"%.5f\", $saved_cost + $cost_increment}")
|
|
189
|
+
|
|
190
|
+
# 写入新状态 (一次性写入,使用原子写入避免并发问题)
|
|
191
|
+
# 先写入临时文件,然后重命名,确保原子性
|
|
192
|
+
TODAY_STATE_TMP="${TODAY_STATE}.tmp.$$"
|
|
193
|
+
$JQ_CMD -n \
|
|
194
|
+
--arg date "$today_date" \
|
|
195
|
+
--arg tokens "$new_today_tokens" \
|
|
196
|
+
--arg cost "$new_today_cost" \
|
|
197
|
+
--arg last_sess_tok "$current_session_tokens" \
|
|
198
|
+
--arg last_sess_cost "$current_cost" \
|
|
199
|
+
'{
|
|
200
|
+
date: $date,
|
|
201
|
+
tokens: ($tokens|tonumber),
|
|
202
|
+
cost: ($cost|tonumber),
|
|
203
|
+
last_session_tokens: ($last_sess_tok|tonumber),
|
|
204
|
+
last_session_cost: ($last_sess_cost|tonumber)
|
|
205
|
+
}' > "$TODAY_STATE_TMP" 2>/dev/null && mv "$TODAY_STATE_TMP" "$TODAY_STATE"
|
|
206
|
+
|
|
207
|
+
# 6. 最终计算与显示
|
|
208
|
+
total_tokens_all=$((history_total_tokens + current_session_tokens))
|
|
209
|
+
total_cost_all=$(awk "BEGIN {print $history_total_cost + $current_cost}")
|
|
210
|
+
|
|
211
|
+
# 警报检查
|
|
212
|
+
is_alert=false
|
|
213
|
+
if awk "BEGIN {exit !($new_today_cost >= $ALERT_DAILY)}"; then
|
|
214
|
+
is_alert=true
|
|
215
|
+
fi
|
|
216
|
+
|
|
217
|
+
# 构建输出字符串
|
|
218
|
+
output=""
|
|
219
|
+
|
|
220
|
+
# [Model]
|
|
221
|
+
if [ "$is_alert" = true ]; then
|
|
222
|
+
output+="\\033[1;31m[${model_name}]\\033[0m "
|
|
223
|
+
else
|
|
224
|
+
output+="\\033[0;90m[${model_name}]\\033[0m "
|
|
225
|
+
fi
|
|
226
|
+
|
|
227
|
+
# Today
|
|
228
|
+
d_today_tokens=$(format_number $new_today_tokens)
|
|
229
|
+
d_today_cost=$(format_cost $new_today_cost)
|
|
230
|
+
|
|
231
|
+
if [ "$new_today_tokens" -gt 0 ]; then
|
|
232
|
+
output+="\\033[${TODAY_COLOR}m今日:${d_today_tokens}\\033[0m "
|
|
233
|
+
output+="\\033[${COST_COLOR}m${d_today_cost}\\033[0m "
|
|
234
|
+
else
|
|
235
|
+
output+="\\033[0;90m今日:0\\033[0m \\033[0;90m\$0.00\\033[0m "
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
# Burn Rate
|
|
239
|
+
if [ "$SHOW_BURN_RATE" = "true" ] && [ "$burn_rate" -gt 0 ]; then
|
|
240
|
+
d_burn=$(format_burn_rate $burn_rate)
|
|
241
|
+
output+="\\033[${BURN_COLOR}m🔥${d_burn}\\033[0m "
|
|
242
|
+
fi
|
|
243
|
+
|
|
244
|
+
# Total
|
|
245
|
+
d_total_tokens=$(format_number $total_tokens_all)
|
|
246
|
+
d_total_cost=$(format_cost $total_cost_all)
|
|
247
|
+
output+="| \\033[0;90m总计:${d_total_tokens}\\033[0m \\033[0;90m${d_total_cost}\\033[0m "
|
|
248
|
+
|
|
249
|
+
# Print final result
|
|
250
|
+
printf "%b" "$output"
|
package/src/token-history.sh
CHANGED
|
@@ -1,268 +1,268 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Burn Your Money - Token 历史统计脚本
|
|
3
|
-
# 从 stats-cache.json 读取历史数据,返回今日/本周/本月统计
|
|
4
|
-
|
|
5
|
-
set -e
|
|
6
|
-
|
|
7
|
-
# 设置 jq 命令路径(支持自定义安装的 jq)
|
|
8
|
-
if [ -n "$JQ_PATH" ]; then
|
|
9
|
-
JQ_CMD="$JQ_PATH"
|
|
10
|
-
elif [ -f "$HOME/.claude/bin/jq.exe" ]; then
|
|
11
|
-
JQ_CMD="$HOME/.claude/bin/jq.exe"
|
|
12
|
-
elif [ -f "$HOME/.claude/bin/jq" ]; then
|
|
13
|
-
JQ_CMD="$HOME/.claude/bin/jq"
|
|
14
|
-
else
|
|
15
|
-
JQ_CMD="jq"
|
|
16
|
-
fi
|
|
17
|
-
|
|
18
|
-
# 验证 jq 是否可用
|
|
19
|
-
if ! command -v "$JQ_CMD" >/dev/null 2>&1; then
|
|
20
|
-
echo "Error: jq not found. Please install jq first:"
|
|
21
|
-
echo " Windows: npm install -g @winston.wan/burn-your-money"
|
|
22
|
-
echo " macOS: brew install jq"
|
|
23
|
-
echo " Linux: sudo apt-get install jq"
|
|
24
|
-
exit 1
|
|
25
|
-
fi
|
|
26
|
-
|
|
27
|
-
# 文件路径
|
|
28
|
-
STATS_CACHE="$HOME/.claude/stats-cache.json"
|
|
29
|
-
TODAY=$(date +%Y-%m-%d)
|
|
30
|
-
CURRENT_MONTH=$(date +%Y-%m)
|
|
31
|
-
LAST_MONTH=$(date -d "1 month ago" +%Y-%m 2>/dev/null || python3 -c "from datetime import datetime; print((datetime.now().replace(day=1) - __import__('datetime').timedelta(days=1)).strftime('%Y-%m'))")
|
|
32
|
-
|
|
33
|
-
# 获取本周一的日期
|
|
34
|
-
get_week_start() {
|
|
35
|
-
local day_of_week=$(date +%u) # 1=Monday, 7=Sunday
|
|
36
|
-
local days_to_subtract=$((day_of_week - 1))
|
|
37
|
-
date -d "$days_to_subtract days ago" +%Y-%m-%d 2>/dev/null || \
|
|
38
|
-
python3 -c "from datetime import datetime, timedelta; d = datetime.now() - timedelta(days=$(date +%u) - 1); print(d.strftime('%Y-%m-%d'))"
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
# 获取本月第一天
|
|
42
|
-
get_month_start() {
|
|
43
|
-
date +%Y-%m-01
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
WEEK_START=$(get_week_start)
|
|
47
|
-
MONTH_START=$(get_month_start)
|
|
48
|
-
LAST_MONTH_START=$(date -d "$LAST_MONTH-01" +%Y-%m-%d 2>/dev/null || echo "$LAST_MONTH-01")
|
|
49
|
-
|
|
50
|
-
# 从 stats-cache.json 读取统计数据
|
|
51
|
-
read_stats() {
|
|
52
|
-
if [ ! -f "$STATS_CACHE" ]; then
|
|
53
|
-
"$JQ_CMD" -n '{
|
|
54
|
-
today_tokens:0,today_cost:0,
|
|
55
|
-
week_tokens:0,week_cost:0,
|
|
56
|
-
month_tokens:0,month_cost:0,
|
|
57
|
-
last_month_tokens:0,last_month_cost:0,
|
|
58
|
-
total_tokens_all:0,total_cost:0,
|
|
59
|
-
daily_data: []
|
|
60
|
-
}'
|
|
61
|
-
return
|
|
62
|
-
fi
|
|
63
|
-
|
|
64
|
-
# 获取今日 token:总计 - 历史每日总和(排除今天)
|
|
65
|
-
# 修改:聚合所有模型的 input 和 output tokens
|
|
66
|
-
local total_io_tokens=$(cat "$STATS_CACHE" | $JQ_CMD -r '[.modelUsage[] | (.inputTokens + .outputTokens)] | add // 0' 2>/dev/null || echo "0")
|
|
67
|
-
|
|
68
|
-
# 修改:聚合每日数据中所有模型的 token
|
|
69
|
-
local history_sum=$(cat "$STATS_CACHE" | $JQ_CMD -r "[.dailyModelTokens[] | select(.date != \"$TODAY\") | (.tokensByModel[] // 0)] | add // 0" 2>/dev/null || echo "0")
|
|
70
|
-
local today_data=$((total_io_tokens - history_sum))
|
|
71
|
-
[ "$today_data" -lt 0 ] && today_data=0
|
|
72
|
-
|
|
73
|
-
# 本周 token(排除今天,用上面的 today_data)
|
|
74
|
-
local week_tokens=$(cat "$STATS_CACHE" | $JQ_CMD -r "[.dailyModelTokens[] | select(.date >= \"$WEEK_START\" and .date != \"$TODAY\") | (.tokensByModel[] // 0)] | add // 0" 2>/dev/null || echo "0")
|
|
75
|
-
week_tokens=$((week_tokens + today_data))
|
|
76
|
-
|
|
77
|
-
# 本月 token
|
|
78
|
-
local month_tokens=$(cat "$STATS_CACHE" | $JQ_CMD -r "[.dailyModelTokens[] | select(.date >= \"$MONTH_START\" and .date != \"$TODAY\") | (.tokensByModel[] // 0)] | add // 0" 2>/dev/null || echo "0")
|
|
79
|
-
month_tokens=$((month_tokens + today_data))
|
|
80
|
-
|
|
81
|
-
# 上月 token
|
|
82
|
-
local last_month_tokens=$(cat "$STATS_CACHE" | $JQ_CMD -r "[.dailyModelTokens[] | select(.date >= \"$LAST_MONTH_START\" and .date < \"$MONTH_START\") | (.tokensByModel[] // 0)] | add // 0" 2>/dev/null || echo "0")
|
|
83
|
-
|
|
84
|
-
# 计算全部历史费用(使用 jq 进行精确计算,避免 awk 大数字精度问题)
|
|
85
|
-
# 修改:聚合所有模型的 usage
|
|
86
|
-
local total_input=$(cat "$STATS_CACHE" | $JQ_CMD -r '[.modelUsage[].inputTokens // 0] | add // 0' 2>/dev/null || echo "0")
|
|
87
|
-
local total_output=$(cat "$STATS_CACHE" | $JQ_CMD -r '[.modelUsage[].outputTokens // 0] | add // 0' 2>/dev/null || echo "0")
|
|
88
|
-
local total_cache_read=$(cat "$STATS_CACHE" | $JQ_CMD -r '[.modelUsage[].cacheReadInputTokens // 0] | add // 0' 2>/dev/null || echo "0")
|
|
89
|
-
|
|
90
|
-
# 估算总费用 (简单按 3/15/0.3 规则,虽然不同模型费率不同,但作为估算足够)
|
|
91
|
-
# 如果要精确需针对模型 loop,这里简化为聚合
|
|
92
|
-
local total_cost=$(cat "$STATS_CACHE" | $JQ_CMD -r '
|
|
93
|
-
[.modelUsage[]] | map(
|
|
94
|
-
((.inputTokens // 0) / 1000000 * 3) +
|
|
95
|
-
((.outputTokens // 0) / 1000000 * 15) +
|
|
96
|
-
((.cacheReadInputTokens // 0) / 1000000 * 0.3)
|
|
97
|
-
) | add | floor * 100 / 100
|
|
98
|
-
' 2>/dev/null || echo "0")
|
|
99
|
-
|
|
100
|
-
# 总 token 数量(包含 cache)
|
|
101
|
-
local total_tokens_all=$((total_input + total_output + total_cache_read))
|
|
102
|
-
|
|
103
|
-
# 计算各周期费用(按比例分摊)
|
|
104
|
-
local week_cost="0.00"
|
|
105
|
-
local month_cost="0.00"
|
|
106
|
-
local last_month_cost="0.00"
|
|
107
|
-
local today_cost="0.00"
|
|
108
|
-
|
|
109
|
-
if [ "$total_io_tokens" != "0" ] && [ "$total_io_tokens" != "" ]; then
|
|
110
|
-
week_cost=$(awk "BEGIN {printf \"%.2f\", ${week_tokens} / ${total_io_tokens} * ${total_cost}}")
|
|
111
|
-
month_cost=$(awk "BEGIN {printf \"%.2f\", ${month_tokens} / ${total_io_tokens} * ${total_cost}}")
|
|
112
|
-
if [ "$last_month_tokens" -gt 0 ]; then
|
|
113
|
-
last_month_cost=$(awk "BEGIN {printf \"%.2f\", ${last_month_tokens} / ${total_io_tokens} * ${total_cost}}")
|
|
114
|
-
fi
|
|
115
|
-
else
|
|
116
|
-
week_cost=$total_cost
|
|
117
|
-
month_cost=$total_cost
|
|
118
|
-
fi
|
|
119
|
-
|
|
120
|
-
if [ "$week_tokens" != "0" ] && [ "$week_tokens" != "" ]; then
|
|
121
|
-
today_cost=$(awk "BEGIN {printf \"%.2f\", ${today_data} / ${week_tokens} * ${week_cost}}")
|
|
122
|
-
fi
|
|
123
|
-
|
|
124
|
-
# 获取每日数据(用于趋势图)
|
|
125
|
-
# 修改:聚合每日所有模型的 tokens
|
|
126
|
-
local daily_data=$(cat "$STATS_CACHE" | $JQ_CMD -c "[.dailyModelTokens[-7:][] | {date: .date, tokens: ([.tokensByModel[]] | add // 0)}]" 2>/dev/null || echo "[]")
|
|
127
|
-
|
|
128
|
-
# 输出 JSON
|
|
129
|
-
"$JQ_CMD" -n \
|
|
130
|
-
--arg today_tokens "$today_data" \
|
|
131
|
-
--arg today_cost "$today_cost" \
|
|
132
|
-
--arg week_tokens "$week_tokens" \
|
|
133
|
-
--arg week_cost "$week_cost" \
|
|
134
|
-
--arg month_tokens "$month_tokens" \
|
|
135
|
-
--arg month_cost "$month_cost" \
|
|
136
|
-
--arg last_month_tokens "$last_month_tokens" \
|
|
137
|
-
--arg last_month_cost "$last_month_cost" \
|
|
138
|
-
--arg total_tokens_all "$total_tokens_all" \
|
|
139
|
-
--arg total_cost "$total_cost" \
|
|
140
|
-
--argjson daily_data "$daily_data" \
|
|
141
|
-
'{
|
|
142
|
-
today_tokens: ($today_tokens | tonumber),
|
|
143
|
-
today_cost: ($today_cost | tonumber),
|
|
144
|
-
week_tokens: ($week_tokens | tonumber),
|
|
145
|
-
week_cost: ($week_cost | tonumber),
|
|
146
|
-
month_tokens: ($month_tokens | tonumber),
|
|
147
|
-
month_cost: ($month_cost | tonumber),
|
|
148
|
-
last_month_tokens: ($last_month_tokens | tonumber),
|
|
149
|
-
last_month_cost: ($last_month_cost | tonumber),
|
|
150
|
-
total_tokens_all: ($total_tokens_all | tonumber),
|
|
151
|
-
total_cost: ($total_cost | tonumber),
|
|
152
|
-
daily_data: $daily_data
|
|
153
|
-
}'
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
# 生成 ASCII 趋势图
|
|
157
|
-
generate_trend() {
|
|
158
|
-
local data="$1"
|
|
159
|
-
local max_tokens=0
|
|
160
|
-
|
|
161
|
-
# 找到最大值
|
|
162
|
-
for row in $(echo "$data" | $JQ_CMD -r '.[].tokens'); do
|
|
163
|
-
if [ "$row" -gt "$max_tokens" ]; then
|
|
164
|
-
max_tokens=$row
|
|
165
|
-
fi
|
|
166
|
-
done
|
|
167
|
-
|
|
168
|
-
# 生成图表
|
|
169
|
-
echo ""
|
|
170
|
-
echo "📊 过去 7 天 Token 趋势:"
|
|
171
|
-
echo ""
|
|
172
|
-
|
|
173
|
-
local count=0
|
|
174
|
-
for entry in $(echo "$data" | $JQ_CMD -c '.[]'); do
|
|
175
|
-
local date=$(echo "$entry" | $JQ_CMD -r '.date' | cut -c 6-)
|
|
176
|
-
local tokens=$(echo "$entry" | $JQ_CMD -r '.tokens')
|
|
177
|
-
|
|
178
|
-
# 计算条形长度
|
|
179
|
-
if [ "$max_tokens" -gt 0 ]; then
|
|
180
|
-
local bars=$(awk "BEGIN {printf \"%.0f\", $tokens / $max_tokens * 20}")
|
|
181
|
-
else
|
|
182
|
-
local bars=0
|
|
183
|
-
fi
|
|
184
|
-
|
|
185
|
-
# 生成条形
|
|
186
|
-
local bar=""
|
|
187
|
-
for i in $(seq 1 20); do
|
|
188
|
-
if [ "$i" -le "$bars" ]; then
|
|
189
|
-
bar+="█"
|
|
190
|
-
else
|
|
191
|
-
bar+="░"
|
|
192
|
-
fi
|
|
193
|
-
done
|
|
194
|
-
|
|
195
|
-
# 格式化 token 数量
|
|
196
|
-
local tokens_display=$(echo "$tokens" | awk '{
|
|
197
|
-
if ($1 >= 1000000) printf "%.1fM", $1/1000000
|
|
198
|
-
else if ($1 >= 1000) printf "%.1fK", $1/1000
|
|
199
|
-
else printf "%d", $1
|
|
200
|
-
}')
|
|
201
|
-
|
|
202
|
-
printf " %s │ %s │ %s\n" "$date" "$bar" "$tokens_display"
|
|
203
|
-
count=$((count + 1))
|
|
204
|
-
done
|
|
205
|
-
echo ""
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
# 导出数据
|
|
209
|
-
export_data() {
|
|
210
|
-
local format="$1"
|
|
211
|
-
local stats=$(read_stats)
|
|
212
|
-
|
|
213
|
-
case "$format" in
|
|
214
|
-
csv)
|
|
215
|
-
echo "date,tokens,cost"
|
|
216
|
-
echo "$stats" | $JQ_CMD -r '.daily_data[] | "\(.date),\(.tokens),\(.tokens * 0.000006)"'
|
|
217
|
-
;;
|
|
218
|
-
json)
|
|
219
|
-
echo "$stats" | $JQ_CMD '.'
|
|
220
|
-
;;
|
|
221
|
-
*)
|
|
222
|
-
echo "支持的格式: csv, json"
|
|
223
|
-
exit 1
|
|
224
|
-
;;
|
|
225
|
-
esac
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
# 根据 mode 返回不同数据
|
|
229
|
-
MODE=${1:-summary}
|
|
230
|
-
|
|
231
|
-
case "$MODE" in
|
|
232
|
-
today_tokens)
|
|
233
|
-
read_stats | $JQ_CMD -r '.today_tokens'
|
|
234
|
-
;;
|
|
235
|
-
today_cost)
|
|
236
|
-
read_stats | $JQ_CMD -r '.today_cost'
|
|
237
|
-
;;
|
|
238
|
-
week_tokens)
|
|
239
|
-
read_stats | $JQ_CMD -r '.week_tokens'
|
|
240
|
-
;;
|
|
241
|
-
week_cost)
|
|
242
|
-
read_stats | $JQ_CMD -r '.week_cost'
|
|
243
|
-
;;
|
|
244
|
-
month_tokens)
|
|
245
|
-
read_stats | $JQ_CMD -r '.month_tokens'
|
|
246
|
-
;;
|
|
247
|
-
month_cost)
|
|
248
|
-
read_stats | $JQ_CMD -r '.month_cost'
|
|
249
|
-
;;
|
|
250
|
-
last_month_tokens)
|
|
251
|
-
read_stats | $JQ_CMD -r '.last_month_tokens'
|
|
252
|
-
;;
|
|
253
|
-
last_month_cost)
|
|
254
|
-
read_stats | $JQ_CMD -r '.last_month_cost'
|
|
255
|
-
;;
|
|
256
|
-
trend)
|
|
257
|
-
read_stats | $JQ_CMD -r '.daily_data'
|
|
258
|
-
;;
|
|
259
|
-
chart)
|
|
260
|
-
generate_trend "$(read_stats | $JQ_CMD -r '.daily_data')"
|
|
261
|
-
;;
|
|
262
|
-
export)
|
|
263
|
-
export_data "$2"
|
|
264
|
-
;;
|
|
265
|
-
summary|json|*)
|
|
266
|
-
read_stats
|
|
267
|
-
;;
|
|
268
|
-
esac
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Burn Your Money - Token 历史统计脚本
|
|
3
|
+
# 从 stats-cache.json 读取历史数据,返回今日/本周/本月统计
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
# 设置 jq 命令路径(支持自定义安装的 jq)
|
|
8
|
+
if [ -n "$JQ_PATH" ]; then
|
|
9
|
+
JQ_CMD="$JQ_PATH"
|
|
10
|
+
elif [ -f "$HOME/.claude/bin/jq.exe" ]; then
|
|
11
|
+
JQ_CMD="$HOME/.claude/bin/jq.exe"
|
|
12
|
+
elif [ -f "$HOME/.claude/bin/jq" ]; then
|
|
13
|
+
JQ_CMD="$HOME/.claude/bin/jq"
|
|
14
|
+
else
|
|
15
|
+
JQ_CMD="jq"
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# 验证 jq 是否可用
|
|
19
|
+
if ! command -v "$JQ_CMD" >/dev/null 2>&1; then
|
|
20
|
+
echo "Error: jq not found. Please install jq first:"
|
|
21
|
+
echo " Windows: npm install -g @winston.wan/burn-your-money"
|
|
22
|
+
echo " macOS: brew install jq"
|
|
23
|
+
echo " Linux: sudo apt-get install jq"
|
|
24
|
+
exit 1
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# 文件路径
|
|
28
|
+
STATS_CACHE="$HOME/.claude/stats-cache.json"
|
|
29
|
+
TODAY=$(date +%Y-%m-%d)
|
|
30
|
+
CURRENT_MONTH=$(date +%Y-%m)
|
|
31
|
+
LAST_MONTH=$(date -d "1 month ago" +%Y-%m 2>/dev/null || python3 -c "from datetime import datetime; print((datetime.now().replace(day=1) - __import__('datetime').timedelta(days=1)).strftime('%Y-%m'))")
|
|
32
|
+
|
|
33
|
+
# 获取本周一的日期
|
|
34
|
+
get_week_start() {
|
|
35
|
+
local day_of_week=$(date +%u) # 1=Monday, 7=Sunday
|
|
36
|
+
local days_to_subtract=$((day_of_week - 1))
|
|
37
|
+
date -d "$days_to_subtract days ago" +%Y-%m-%d 2>/dev/null || \
|
|
38
|
+
python3 -c "from datetime import datetime, timedelta; d = datetime.now() - timedelta(days=$(date +%u) - 1); print(d.strftime('%Y-%m-%d'))"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# 获取本月第一天
|
|
42
|
+
get_month_start() {
|
|
43
|
+
date +%Y-%m-01
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
WEEK_START=$(get_week_start)
|
|
47
|
+
MONTH_START=$(get_month_start)
|
|
48
|
+
LAST_MONTH_START=$(date -d "$LAST_MONTH-01" +%Y-%m-%d 2>/dev/null || echo "$LAST_MONTH-01")
|
|
49
|
+
|
|
50
|
+
# 从 stats-cache.json 读取统计数据
|
|
51
|
+
read_stats() {
|
|
52
|
+
if [ ! -f "$STATS_CACHE" ]; then
|
|
53
|
+
"$JQ_CMD" -n '{
|
|
54
|
+
today_tokens:0,today_cost:0,
|
|
55
|
+
week_tokens:0,week_cost:0,
|
|
56
|
+
month_tokens:0,month_cost:0,
|
|
57
|
+
last_month_tokens:0,last_month_cost:0,
|
|
58
|
+
total_tokens_all:0,total_cost:0,
|
|
59
|
+
daily_data: []
|
|
60
|
+
}'
|
|
61
|
+
return
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# 获取今日 token:总计 - 历史每日总和(排除今天)
|
|
65
|
+
# 修改:聚合所有模型的 input 和 output tokens
|
|
66
|
+
local total_io_tokens=$(cat "$STATS_CACHE" | $JQ_CMD -r '[.modelUsage[] | (.inputTokens + .outputTokens)] | add // 0' 2>/dev/null || echo "0")
|
|
67
|
+
|
|
68
|
+
# 修改:聚合每日数据中所有模型的 token
|
|
69
|
+
local history_sum=$(cat "$STATS_CACHE" | $JQ_CMD -r "[.dailyModelTokens[] | select(.date != \"$TODAY\") | (.tokensByModel[] // 0)] | add // 0" 2>/dev/null || echo "0")
|
|
70
|
+
local today_data=$((total_io_tokens - history_sum))
|
|
71
|
+
[ "$today_data" -lt 0 ] && today_data=0
|
|
72
|
+
|
|
73
|
+
# 本周 token(排除今天,用上面的 today_data)
|
|
74
|
+
local week_tokens=$(cat "$STATS_CACHE" | $JQ_CMD -r "[.dailyModelTokens[] | select(.date >= \"$WEEK_START\" and .date != \"$TODAY\") | (.tokensByModel[] // 0)] | add // 0" 2>/dev/null || echo "0")
|
|
75
|
+
week_tokens=$((week_tokens + today_data))
|
|
76
|
+
|
|
77
|
+
# 本月 token
|
|
78
|
+
local month_tokens=$(cat "$STATS_CACHE" | $JQ_CMD -r "[.dailyModelTokens[] | select(.date >= \"$MONTH_START\" and .date != \"$TODAY\") | (.tokensByModel[] // 0)] | add // 0" 2>/dev/null || echo "0")
|
|
79
|
+
month_tokens=$((month_tokens + today_data))
|
|
80
|
+
|
|
81
|
+
# 上月 token
|
|
82
|
+
local last_month_tokens=$(cat "$STATS_CACHE" | $JQ_CMD -r "[.dailyModelTokens[] | select(.date >= \"$LAST_MONTH_START\" and .date < \"$MONTH_START\") | (.tokensByModel[] // 0)] | add // 0" 2>/dev/null || echo "0")
|
|
83
|
+
|
|
84
|
+
# 计算全部历史费用(使用 jq 进行精确计算,避免 awk 大数字精度问题)
|
|
85
|
+
# 修改:聚合所有模型的 usage
|
|
86
|
+
local total_input=$(cat "$STATS_CACHE" | $JQ_CMD -r '[.modelUsage[].inputTokens // 0] | add // 0' 2>/dev/null || echo "0")
|
|
87
|
+
local total_output=$(cat "$STATS_CACHE" | $JQ_CMD -r '[.modelUsage[].outputTokens // 0] | add // 0' 2>/dev/null || echo "0")
|
|
88
|
+
local total_cache_read=$(cat "$STATS_CACHE" | $JQ_CMD -r '[.modelUsage[].cacheReadInputTokens // 0] | add // 0' 2>/dev/null || echo "0")
|
|
89
|
+
|
|
90
|
+
# 估算总费用 (简单按 3/15/0.3 规则,虽然不同模型费率不同,但作为估算足够)
|
|
91
|
+
# 如果要精确需针对模型 loop,这里简化为聚合
|
|
92
|
+
local total_cost=$(cat "$STATS_CACHE" | $JQ_CMD -r '
|
|
93
|
+
[.modelUsage[]] | map(
|
|
94
|
+
((.inputTokens // 0) / 1000000 * 3) +
|
|
95
|
+
((.outputTokens // 0) / 1000000 * 15) +
|
|
96
|
+
((.cacheReadInputTokens // 0) / 1000000 * 0.3)
|
|
97
|
+
) | add | floor * 100 / 100
|
|
98
|
+
' 2>/dev/null || echo "0")
|
|
99
|
+
|
|
100
|
+
# 总 token 数量(包含 cache)
|
|
101
|
+
local total_tokens_all=$((total_input + total_output + total_cache_read))
|
|
102
|
+
|
|
103
|
+
# 计算各周期费用(按比例分摊)
|
|
104
|
+
local week_cost="0.00"
|
|
105
|
+
local month_cost="0.00"
|
|
106
|
+
local last_month_cost="0.00"
|
|
107
|
+
local today_cost="0.00"
|
|
108
|
+
|
|
109
|
+
if [ "$total_io_tokens" != "0" ] && [ "$total_io_tokens" != "" ]; then
|
|
110
|
+
week_cost=$(awk "BEGIN {printf \"%.2f\", ${week_tokens} / ${total_io_tokens} * ${total_cost}}")
|
|
111
|
+
month_cost=$(awk "BEGIN {printf \"%.2f\", ${month_tokens} / ${total_io_tokens} * ${total_cost}}")
|
|
112
|
+
if [ "$last_month_tokens" -gt 0 ]; then
|
|
113
|
+
last_month_cost=$(awk "BEGIN {printf \"%.2f\", ${last_month_tokens} / ${total_io_tokens} * ${total_cost}}")
|
|
114
|
+
fi
|
|
115
|
+
else
|
|
116
|
+
week_cost=$total_cost
|
|
117
|
+
month_cost=$total_cost
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
if [ "$week_tokens" != "0" ] && [ "$week_tokens" != "" ]; then
|
|
121
|
+
today_cost=$(awk "BEGIN {printf \"%.2f\", ${today_data} / ${week_tokens} * ${week_cost}}")
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
# 获取每日数据(用于趋势图)
|
|
125
|
+
# 修改:聚合每日所有模型的 tokens
|
|
126
|
+
local daily_data=$(cat "$STATS_CACHE" | $JQ_CMD -c "[.dailyModelTokens[-7:][] | {date: .date, tokens: ([.tokensByModel[]] | add // 0)}]" 2>/dev/null || echo "[]")
|
|
127
|
+
|
|
128
|
+
# 输出 JSON
|
|
129
|
+
"$JQ_CMD" -n \
|
|
130
|
+
--arg today_tokens "$today_data" \
|
|
131
|
+
--arg today_cost "$today_cost" \
|
|
132
|
+
--arg week_tokens "$week_tokens" \
|
|
133
|
+
--arg week_cost "$week_cost" \
|
|
134
|
+
--arg month_tokens "$month_tokens" \
|
|
135
|
+
--arg month_cost "$month_cost" \
|
|
136
|
+
--arg last_month_tokens "$last_month_tokens" \
|
|
137
|
+
--arg last_month_cost "$last_month_cost" \
|
|
138
|
+
--arg total_tokens_all "$total_tokens_all" \
|
|
139
|
+
--arg total_cost "$total_cost" \
|
|
140
|
+
--argjson daily_data "$daily_data" \
|
|
141
|
+
'{
|
|
142
|
+
today_tokens: ($today_tokens | tonumber),
|
|
143
|
+
today_cost: ($today_cost | tonumber),
|
|
144
|
+
week_tokens: ($week_tokens | tonumber),
|
|
145
|
+
week_cost: ($week_cost | tonumber),
|
|
146
|
+
month_tokens: ($month_tokens | tonumber),
|
|
147
|
+
month_cost: ($month_cost | tonumber),
|
|
148
|
+
last_month_tokens: ($last_month_tokens | tonumber),
|
|
149
|
+
last_month_cost: ($last_month_cost | tonumber),
|
|
150
|
+
total_tokens_all: ($total_tokens_all | tonumber),
|
|
151
|
+
total_cost: ($total_cost | tonumber),
|
|
152
|
+
daily_data: $daily_data
|
|
153
|
+
}'
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
# 生成 ASCII 趋势图
|
|
157
|
+
generate_trend() {
|
|
158
|
+
local data="$1"
|
|
159
|
+
local max_tokens=0
|
|
160
|
+
|
|
161
|
+
# 找到最大值
|
|
162
|
+
for row in $(echo "$data" | $JQ_CMD -r '.[].tokens'); do
|
|
163
|
+
if [ "$row" -gt "$max_tokens" ]; then
|
|
164
|
+
max_tokens=$row
|
|
165
|
+
fi
|
|
166
|
+
done
|
|
167
|
+
|
|
168
|
+
# 生成图表
|
|
169
|
+
echo ""
|
|
170
|
+
echo "📊 过去 7 天 Token 趋势:"
|
|
171
|
+
echo ""
|
|
172
|
+
|
|
173
|
+
local count=0
|
|
174
|
+
for entry in $(echo "$data" | $JQ_CMD -c '.[]'); do
|
|
175
|
+
local date=$(echo "$entry" | $JQ_CMD -r '.date' | cut -c 6-)
|
|
176
|
+
local tokens=$(echo "$entry" | $JQ_CMD -r '.tokens')
|
|
177
|
+
|
|
178
|
+
# 计算条形长度
|
|
179
|
+
if [ "$max_tokens" -gt 0 ]; then
|
|
180
|
+
local bars=$(awk "BEGIN {printf \"%.0f\", $tokens / $max_tokens * 20}")
|
|
181
|
+
else
|
|
182
|
+
local bars=0
|
|
183
|
+
fi
|
|
184
|
+
|
|
185
|
+
# 生成条形
|
|
186
|
+
local bar=""
|
|
187
|
+
for i in $(seq 1 20); do
|
|
188
|
+
if [ "$i" -le "$bars" ]; then
|
|
189
|
+
bar+="█"
|
|
190
|
+
else
|
|
191
|
+
bar+="░"
|
|
192
|
+
fi
|
|
193
|
+
done
|
|
194
|
+
|
|
195
|
+
# 格式化 token 数量
|
|
196
|
+
local tokens_display=$(echo "$tokens" | awk '{
|
|
197
|
+
if ($1 >= 1000000) printf "%.1fM", $1/1000000
|
|
198
|
+
else if ($1 >= 1000) printf "%.1fK", $1/1000
|
|
199
|
+
else printf "%d", $1
|
|
200
|
+
}')
|
|
201
|
+
|
|
202
|
+
printf " %s │ %s │ %s\n" "$date" "$bar" "$tokens_display"
|
|
203
|
+
count=$((count + 1))
|
|
204
|
+
done
|
|
205
|
+
echo ""
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
# 导出数据
|
|
209
|
+
export_data() {
|
|
210
|
+
local format="$1"
|
|
211
|
+
local stats=$(read_stats)
|
|
212
|
+
|
|
213
|
+
case "$format" in
|
|
214
|
+
csv)
|
|
215
|
+
echo "date,tokens,cost"
|
|
216
|
+
echo "$stats" | $JQ_CMD -r '.daily_data[] | "\(.date),\(.tokens),\(.tokens * 0.000006)"'
|
|
217
|
+
;;
|
|
218
|
+
json)
|
|
219
|
+
echo "$stats" | $JQ_CMD '.'
|
|
220
|
+
;;
|
|
221
|
+
*)
|
|
222
|
+
echo "支持的格式: csv, json"
|
|
223
|
+
exit 1
|
|
224
|
+
;;
|
|
225
|
+
esac
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
# 根据 mode 返回不同数据
|
|
229
|
+
MODE=${1:-summary}
|
|
230
|
+
|
|
231
|
+
case "$MODE" in
|
|
232
|
+
today_tokens)
|
|
233
|
+
read_stats | $JQ_CMD -r '.today_tokens'
|
|
234
|
+
;;
|
|
235
|
+
today_cost)
|
|
236
|
+
read_stats | $JQ_CMD -r '.today_cost'
|
|
237
|
+
;;
|
|
238
|
+
week_tokens)
|
|
239
|
+
read_stats | $JQ_CMD -r '.week_tokens'
|
|
240
|
+
;;
|
|
241
|
+
week_cost)
|
|
242
|
+
read_stats | $JQ_CMD -r '.week_cost'
|
|
243
|
+
;;
|
|
244
|
+
month_tokens)
|
|
245
|
+
read_stats | $JQ_CMD -r '.month_tokens'
|
|
246
|
+
;;
|
|
247
|
+
month_cost)
|
|
248
|
+
read_stats | $JQ_CMD -r '.month_cost'
|
|
249
|
+
;;
|
|
250
|
+
last_month_tokens)
|
|
251
|
+
read_stats | $JQ_CMD -r '.last_month_tokens'
|
|
252
|
+
;;
|
|
253
|
+
last_month_cost)
|
|
254
|
+
read_stats | $JQ_CMD -r '.last_month_cost'
|
|
255
|
+
;;
|
|
256
|
+
trend)
|
|
257
|
+
read_stats | $JQ_CMD -r '.daily_data'
|
|
258
|
+
;;
|
|
259
|
+
chart)
|
|
260
|
+
generate_trend "$(read_stats | $JQ_CMD -r '.daily_data')"
|
|
261
|
+
;;
|
|
262
|
+
export)
|
|
263
|
+
export_data "$2"
|
|
264
|
+
;;
|
|
265
|
+
summary|json|*)
|
|
266
|
+
read_stats
|
|
267
|
+
;;
|
|
268
|
+
esac
|
package/bash.exe.stackdump
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
Stack trace:
|
|
2
|
-
Frame Function Args
|
|
3
|
-
0007FFFFBF30 00021005FE8E (000210285F68, 00021026AB6E, 000000000000, 0007FFFFAE30) msys-2.0.dll+0x1FE8E
|
|
4
|
-
0007FFFFBF30 0002100467F9 (000000000000, 000000000000, 000000000000, 0007FFFFC208) msys-2.0.dll+0x67F9
|
|
5
|
-
0007FFFFBF30 000210046832 (000210286019, 0007FFFFBDE8, 000000000000, 000000000000) msys-2.0.dll+0x6832
|
|
6
|
-
0007FFFFBF30 000210068CF6 (000000000000, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x28CF6
|
|
7
|
-
0007FFFFBF30 000210068E24 (0007FFFFBF40, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x28E24
|
|
8
|
-
0007FFFFC210 00021006A225 (0007FFFFBF40, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x2A225
|
|
9
|
-
End of stack trace
|
|
10
|
-
Loaded modules:
|
|
11
|
-
000100400000 bash.exe
|
|
12
|
-
7FFAC83A0000 ntdll.dll
|
|
13
|
-
7FFAC7620000 KERNEL32.DLL
|
|
14
|
-
7FFAC57F0000 KERNELBASE.dll
|
|
15
|
-
7FFAC7C30000 USER32.dll
|
|
16
|
-
7FFAC54F0000 win32u.dll
|
|
17
|
-
000210040000 msys-2.0.dll
|
|
18
|
-
7FFAC74C0000 GDI32.dll
|
|
19
|
-
7FFAC5EA0000 gdi32full.dll
|
|
20
|
-
7FFAC5CA0000 msvcp_win.dll
|
|
21
|
-
7FFAC5D50000 ucrtbase.dll
|
|
22
|
-
7FFAC7070000 advapi32.dll
|
|
23
|
-
7FFAC7570000 msvcrt.dll
|
|
24
|
-
7FFAC6140000 sechost.dll
|
|
25
|
-
7FFAC69C0000 RPCRT4.dll
|
|
26
|
-
7FFAC4AF0000 CRYPTBASE.DLL
|
|
27
|
-
7FFAC5BF0000 bcryptPrimitives.dll
|
|
28
|
-
7FFAC7E00000 IMM32.DLL
|