token-tracker 0.3.8__tar.gz → 0.4.0__tar.gz

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.
Files changed (67) hide show
  1. token_tracker-0.4.0/PKG-INFO +240 -0
  2. token_tracker-0.4.0/README.md +187 -0
  3. {token_tracker-0.3.8 → token_tracker-0.4.0}/pyproject.toml +4 -3
  4. {token_tracker-0.3.8/src → token_tracker-0.4.0/src/token_tracker}/adapters/claude.py +2 -2
  5. {token_tracker-0.3.8/src → token_tracker-0.4.0/src/token_tracker}/adapters/codex.py +28 -4
  6. {token_tracker-0.3.8/src → token_tracker-0.4.0/src/token_tracker}/adapters/rate_limits.py +2 -1
  7. {token_tracker-0.3.8/src → token_tracker-0.4.0/src/token_tracker}/adapters/types.py +23 -1
  8. token_tracker-0.4.0/src/token_tracker/adapters/util.py +68 -0
  9. {token_tracker-0.3.8/src → token_tracker-0.4.0/src/token_tracker}/analyzer/aggregator.py +26 -6
  10. {token_tracker-0.3.8/src → token_tracker-0.4.0/src/token_tracker}/analyzer/cost.py +8 -1
  11. token_tracker-0.4.0/src/token_tracker/cli.py +455 -0
  12. token_tracker-0.4.0/src/token_tracker/config.py +151 -0
  13. token_tracker-0.4.0/src/token_tracker/hooks.py +1005 -0
  14. token_tracker-0.4.0/src/token_tracker/i18n.py +310 -0
  15. token_tracker-0.4.0/src/token_tracker/ui/console.py +101 -0
  16. token_tracker-0.4.0/src/token_tracker/ui/format.py +144 -0
  17. token_tracker-0.4.0/src/token_tracker/ui/heatmap.py +164 -0
  18. token_tracker-0.4.0/src/token_tracker/ui/panels.py +65 -0
  19. token_tracker-0.4.0/src/token_tracker/ui/status.py +183 -0
  20. token_tracker-0.4.0/src/token_tracker/ui/tables.py +452 -0
  21. token_tracker-0.4.0/src/token_tracker/ui/theme.py +100 -0
  22. token_tracker-0.4.0/src/token_tracker/ui/themes.py +177 -0
  23. token_tracker-0.4.0/src/token_tracker/wizard.py +264 -0
  24. token_tracker-0.4.0/src/token_tracker.egg-info/PKG-INFO +240 -0
  25. token_tracker-0.4.0/src/token_tracker.egg-info/SOURCES.txt +46 -0
  26. token_tracker-0.4.0/src/token_tracker.egg-info/entry_points.txt +2 -0
  27. {token_tracker-0.3.8 → token_tracker-0.4.0/src}/token_tracker.egg-info/requires.txt +1 -0
  28. token_tracker-0.4.0/src/token_tracker.egg-info/top_level.txt +1 -0
  29. token_tracker-0.4.0/tests/test_aggregator.py +141 -0
  30. {token_tracker-0.3.8 → token_tracker-0.4.0}/tests/test_blocks.py +2 -2
  31. token_tracker-0.4.0/tests/test_cli.py +38 -0
  32. {token_tracker-0.3.8 → token_tracker-0.4.0}/tests/test_codex.py +46 -1
  33. {token_tracker-0.3.8 → token_tracker-0.4.0}/tests/test_cost.py +17 -3
  34. token_tracker-0.4.0/tests/test_heatmap.py +60 -0
  35. token_tracker-0.4.0/tests/test_hooks.py +375 -0
  36. token_tracker-0.4.0/tests/test_status.py +136 -0
  37. token_tracker-0.4.0/tests/test_tables.py +69 -0
  38. token_tracker-0.4.0/tests/test_theme.py +284 -0
  39. token_tracker-0.4.0/tests/test_util.py +34 -0
  40. token_tracker-0.3.8/PKG-INFO +0 -179
  41. token_tracker-0.3.8/README.md +0 -127
  42. token_tracker-0.3.8/src/adapters/util.py +0 -39
  43. token_tracker-0.3.8/src/cli.py +0 -426
  44. token_tracker-0.3.8/src/hooks.py +0 -518
  45. token_tracker-0.3.8/src/i18n.py +0 -197
  46. token_tracker-0.3.8/src/ui/console.py +0 -31
  47. token_tracker-0.3.8/src/ui/format.py +0 -86
  48. token_tracker-0.3.8/src/ui/panels.py +0 -257
  49. token_tracker-0.3.8/src/ui/tables.py +0 -469
  50. token_tracker-0.3.8/src/ui/theme.py +0 -48
  51. token_tracker-0.3.8/src/ui/widgets.py +0 -55
  52. token_tracker-0.3.8/tests/test_aggregator.py +0 -85
  53. token_tracker-0.3.8/tests/test_cli.py +0 -95
  54. token_tracker-0.3.8/tests/test_hooks.py +0 -18
  55. token_tracker-0.3.8/token_tracker.egg-info/PKG-INFO +0 -179
  56. token_tracker-0.3.8/token_tracker.egg-info/SOURCES.txt +0 -37
  57. token_tracker-0.3.8/token_tracker.egg-info/entry_points.txt +0 -2
  58. token_tracker-0.3.8/token_tracker.egg-info/top_level.txt +0 -1
  59. {token_tracker-0.3.8 → token_tracker-0.4.0}/LICENSE +0 -0
  60. {token_tracker-0.3.8 → token_tracker-0.4.0}/setup.cfg +0 -0
  61. {token_tracker-0.3.8/src → token_tracker-0.4.0/src/token_tracker}/__init__.py +0 -0
  62. {token_tracker-0.3.8/src → token_tracker-0.4.0/src/token_tracker}/adapters/__init__.py +0 -0
  63. {token_tracker-0.3.8/src → token_tracker-0.4.0/src/token_tracker}/adapters/registry.py +0 -0
  64. {token_tracker-0.3.8/src → token_tracker-0.4.0/src/token_tracker}/analyzer/__init__.py +0 -0
  65. {token_tracker-0.3.8/src → token_tracker-0.4.0/src/token_tracker}/analyzer/blocks.py +0 -0
  66. {token_tracker-0.3.8/src → token_tracker-0.4.0/src/token_tracker}/ui/__init__.py +0 -0
  67. {token_tracker-0.3.8 → token_tracker-0.4.0/src}/token_tracker.egg-info/dependency_links.txt +0 -0
@@ -0,0 +1,240 @@
1
+ Metadata-Version: 2.4
2
+ Name: token-tracker
3
+ Version: 0.4.0
4
+ Summary: Track token usage across local AI agents (Claude Code, Codex)
5
+ Author-email: stormzhang <stormzhang.dev@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 stormzhang
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/stormzhang/token-tracker
29
+ Project-URL: Repository, https://github.com/stormzhang/token-tracker
30
+ Project-URL: Issues, https://github.com/stormzhang/token-tracker/issues
31
+ Keywords: claude-code,codex,token,usage,cost,ai-agent,statusline,cli
32
+ Classifier: Development Status :: 4 - Beta
33
+ Classifier: Environment :: Console
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Operating System :: OS Independent
37
+ Classifier: Programming Language :: Python :: 3.11
38
+ Classifier: Programming Language :: Python :: 3.12
39
+ Classifier: Topic :: Software Development
40
+ Classifier: Topic :: Utilities
41
+ Requires-Python: >=3.11
42
+ Description-Content-Type: text/markdown
43
+ License-File: LICENSE
44
+ Requires-Dist: rich>=13.7
45
+ Requires-Dist: questionary>=2.0
46
+ Provides-Extra: test
47
+ Requires-Dist: pytest>=8.0; extra == "test"
48
+ Provides-Extra: dev
49
+ Requires-Dist: pytest>=8.0; extra == "dev"
50
+ Requires-Dist: ruff>=0.6; extra == "dev"
51
+ Requires-Dist: mypy>=1.8; extra == "dev"
52
+ Dynamic: license-file
53
+
54
+ # Token Tracker (tt)
55
+
56
+ 本地 AI Agent Token 消耗追踪/分析工具,支持 **Claude Code** 和 **Codex** 。
57
+
58
+ 自定义 StatusLine 状态栏 + CLI Dashboard,实时查看 token 用量、等效成本、限额状态。
59
+
60
+ ![Python](https://img.shields.io/badge/python-3.11+-blue) ![CI](https://github.com/stormzhang/token-tracker/actions/workflows/ci.yml/badge.svg) ![License](https://img.shields.io/badge/license-MIT-green)
61
+
62
+ [English](README_EN.md)
63
+
64
+ ## StatusLine 状态栏
65
+
66
+ 自动为 Claude Code 和 Codex 配置状态栏,`tt setup` 一键配置,脚本更新时自动升级。
67
+
68
+ **Claude Code**:基于官方自定义 StatusLine 接口,数据完全来自本地 Claude,准确无任何推测
69
+
70
+ ![Claude Code StatusLine](assets/screenshot-statusline-cc.png)
71
+
72
+ 状态栏共四行,从左到右:
73
+
74
+ | 行 | 字段 | 说明 |
75
+ |----|------|------|
76
+ | 1 | `[项目](分支 +12 -3)` | 项目名(加粗)+ Git 分支(未提交修改标 `*`),括号内附工作区相对 HEAD 的增删行数 |
77
+ | 1 | `Total: 1.2M` | 本次会话累计消耗 token(输入+输出+cache,解析 transcript 得出) |
78
+ | 1 | `Cost: $35.51` | 本次会话等效成本(Claude Code 自带,按官方计费,准确) |
79
+ | 1 | `Code: +208 -8` | 本会话 Claude 写 / 删的代码行数(`+` 绿 `-` 红,与 git 变动同配色) |
80
+ | 2 | `Limit: 5h: ██░ 31% (1h19m)` | 5 小时滑动窗口配额(仅订阅模式;括号内重置倒计时) |
81
+ | 2 | `7d: ██░ 11% (5d8h)` | 7 天滑动窗口配额 |
82
+ | 2 | `1.0M Ctx: ██░ 20%` | 上下文窗口总大小及已用占比 |
83
+ | 3 | `Tokens: in 392k, out 937, cache 388k` | **当前上下文窗口**的 token 构成(注意:非会话累计,会随 compact 变化) |
84
+ | 3 | `Out TPS: 60 tokens/s` | 本轮 output token 生成速度(含 thinking;空闲帧保留上次值) |
85
+ | 4 | `Model: Opus 4.8/xhigh/nofast` | 模型名 / reasoning 级别 / 是否 fast 模式 |
86
+ | 4 | `Duration: 1h33m` | 当前会话已持续时间 |
87
+ | 4 | `Remote: github` | 代码仓库 host(去顶级域) |
88
+
89
+ > 终端宽度不足时会自动降级:先隐藏重置倒计时,再将进度条简化为百分比数字。**API 模式**无订阅配额,第 2 行只显示 Ctx。
90
+
91
+ **Codex**:官方暂不支持自定义 StatusLine,`tt setup` 通过 Codex `Stop` hook 装一个**伪 statusline**——每次回答完成后在回答尾部追加**两行**彩色 status,仿 Claude Code 状态栏(不再接管 Codex 官方 `status_line`,伪 statusline 信息比官方字段更全):
92
+
93
+ ![Codex StatusLine](assets/screenshot-statusline-codex.png)
94
+
95
+ **伪 statusline 两行布局**:
96
+
97
+ - **L1** `[项目](分支 +A -D) | Total: <会话累计 token> | Model: <模型 推理强度>` —— Total 橙、Model 红
98
+ - **L2** `Limit: 5h <进度条> % (reset <倒计时>) | 7d <进度条> % (reset <倒计时>) | <窗口> Ctx <进度条> %`
99
+
100
+ 通过 `Stop` hook 注入 `systemMessage` 实现,渲染 24-bit 真彩色、**不进模型上下文**(实测),**配色跟随当前主题**(与 CLI 报表 / CC 状态栏同源,`tt theme` 切换三者一起变)。`tt unsetup` 一并移除。
101
+
102
+ ## Status 实时面板和 日/周/月 数据报表分析
103
+
104
+ `tt`(无参)/ `tt status`:聚焦**过去 5 小时**的实时面板——顶部多 Agent **合并**概览(Token / Cost / Sessions / Messages / Top Model),中间 **5h / 7d 订阅额度**进度条(Claude Code / Codex 分开;都没订阅额度时换成 per-agent 的 token/cost/sessions/messages 统计),底部**近期会话**列表(CC + Codex 合并、带 Agent 列、按 Cost 倒序、Cost 前三名高亮)。所有时间按**系统时区**显示,配色跟随当前主题。
105
+
106
+ ![Token Tracker Status](assets/screenshot.png)
107
+
108
+ ![Token Tracker Daily](assets/screenshot-daily.png)
109
+
110
+ ![Token Tracker Weekly](assets/screenshot-weekly.png)
111
+
112
+ ![Token Tracker Monthly](assets/screenshot-monthly.png)
113
+
114
+ ## 功能
115
+
116
+ - **多 Agent 追踪** — Claude Code + Codex 统一面板,左右键切换
117
+ - **状态栏集成** — Claude Code statusLine + Codex status_line,首次运行自动配置,脚本更新自动升级
118
+ - **限额监控** — 实时 5h / 7d 配额百分比 + 重置倒计时
119
+ - **成本分析** — 按会话、日、周、月维度的等效成本统计,多 Agent 按来源分组展示
120
+ - **定价识别** — litellm 在线定价 + 内置官方价双层兜底;同系列新模型自动套用本档定价(含 Claude Fable 5 / Opus 4.8),全新系列缺价时明确提示,不静默按 $0 统计
121
+ - **会话洞察** — 项目、模型、时长、消息数一览
122
+ - **多主题统一配色** — 6 套主题(Catppuccin Mocha/Latte/Frappe/Macchiato + Nord + Dracula),CLI 报表、CC 状态栏与 Codex 伪 statusline **三者同源**;`tt theme` 一键切换 / 预览,暗 / 亮终端自动选 Catppuccin,首次运行交互向导引导选择,`TT_THEME` 可覆盖;终端不支持 truecolor 时自动降级到 256 色近似
123
+ - **零配置** — 自动检测已安装的 Agent,直接读取本地数据
124
+ - **隐私安全** — 数据纯本地存储,不采集、不上传任何用户信息,极轻量无后顾之忧
125
+
126
+ ## 安装
127
+
128
+ ```bash
129
+ curl -sSL https://raw.githubusercontent.com/stormzhang/token-tracker/main/install.sh | bash
130
+ ```
131
+
132
+ 或者通过 pip:
133
+
134
+ ```bash
135
+ pip install --force-reinstall token-tracker && tt setup
136
+ ```
137
+
138
+ ## 使用
139
+
140
+ ```bash
141
+ tt setup # 交互配置向导(终端:上下键选语言 / 主题 / 各组件);非 tty 环境自动全装
142
+ tt # 过去 5h 实时面板(合并概览 + 5h/7d 额度 + 近期会话,= tt status)
143
+ tt status # 同上(tt 无参即进 status)
144
+ tt daily # 过去一年 token 贡献热力图(GitHub 风格)+ 年度分析卡片
145
+ tt weekly # 周报:本周分析卡片 + 每日趋势柱状图 + 周 / 项目 / 模型趋势
146
+ tt monthly # 按月汇总(多 Agent 分组展示)
147
+ tt sessions # 最近 20 条会话明细(按 cost 倒序展示;tt sessions <n> 改条数、--sort 改排序)
148
+ tt theme # 查看 / 切换配色主题(show / list / set / preview)
149
+ tt unsetup # 卸载并恢复安装前的配置
150
+ ```
151
+
152
+ > 💡 `tt daily` 是 GitHub 风格的 token 贡献热力图(深浅绿方格)。在 Claude Code 会话里输入 `!tt daily` 即可看到彩色热力图 —— 用户主动用 `!` 执行的命令,Claude Code 会渲染其 24-bit 真彩色输出。
153
+
154
+ ### 首次运行向导
155
+
156
+ 第一次跑 `tt`(或通过 curl 一键安装脚本装完自动启动)会进入**交互式配置向导**,全程上下键选 + 回车确认:
157
+
158
+ 1. **选语言** — 中文 / English(落 `~/.config/token-tracker/config.json`,之后所有命令跟随)
159
+ 2. **选配色主题** — 6 套主题上下键选择,每个选项右侧内联色板预览
160
+ 3. **启用 Codex 伪 statusline** — Yes/No,默认 Yes(仅检测到 Codex 时)
161
+
162
+ 选完给一份键值对齐的配置总结 + 重启 / 下一步提示。CI / 非 tty 环境(Docker / 脚本)自动按默认全装:**语言跟随系统设置**(读系统语言、不被 CLI 的 `LANG` 误导)、主题 mocha、组件全开。装好后想改任何一项,再跑一次 `tt setup` 即可(终端里每次 `tt setup` 都进向导)。
163
+
164
+ ### 配色主题
165
+
166
+ 内置 6 套主题,CLI 报表、CC 状态栏与 Codex 伪 statusline **统一同源**(切主题三者一起变):
167
+
168
+ | 主题 | 说明 |
169
+ |------|------|
170
+ | `mocha` / `latte` / `frappe` / `macchiato` | Catppuccin 全家(暗 / 亮终端自动选 mocha / latte) |
171
+ | `nord` | Nord |
172
+ | `dracula` | Dracula |
173
+
174
+ ```bash
175
+ tt theme # 显示当前主题及来源
176
+ tt theme list # 列出全部主题 + 色块预览
177
+ tt theme preview nord # 预览某主题(CLI 样例 + 状态栏样例行)
178
+ tt theme set nord # 切换主题(持久化 + 重烘焙状态栏)
179
+ tt monthly --theme nord # 任意报表临时换主题渲染(不持久化、不动状态栏,适合对比)
180
+ ```
181
+
182
+ - **首次运行**(终端内、非 AI 会话)会进入交互向导引导选主题;CI / 脚本 / 会话内自动跳过、静默用默认。
183
+ - 切换持久化到 `~/.config/token-tracker/config.json`(主题 + 语言 + 组件意图统一存这一个文件);优先级 `--theme` 参数 > `TT_THEME` 环境变量 > 配置文件 > 自动(`--theme` 和 `TT_THEME` 都只临时生效、不写配置)。
184
+ - 终端支持 truecolor 用精确配色;不支持的(如 macOS 自带 Terminal.app)自动降级到当前主题的 **256 色近似**,8 色老终端不再适配。
185
+
186
+ ### 报告排序
187
+
188
+ 所有报告命令支持 `--sort` 和 `--asc/--desc` 参数:
189
+
190
+ ```bash
191
+ tt weekly --sort cost --desc # 按成本降序
192
+ tt sessions --sort tokens --asc # 按 token 升序
193
+ ```
194
+
195
+ 可选排序字段:`tokens` / `cost` / `messages` / `time` / `input` / `output`
196
+
197
+ ### Dashboard 快捷键
198
+
199
+ | 按键 | 功能 |
200
+ |------|------|
201
+ | `←` `→` | 切换 Agent |
202
+ | `↑` `↓` | 滚动内容 |
203
+ | `s` | 切换排序字段(时间 → Token → 等效成本 → 消息数) |
204
+ | `r` | 反转排序方向 |
205
+ | `+` / `-` | 调整会话显示条数(±10,最少 10 条) |
206
+ | `q` | 退出 |
207
+
208
+ ## 数据来源
209
+
210
+ | Agent | 路径 | 格式 |
211
+ |-------|------|------|
212
+ | Claude Code | `~/.claude/projects/*/` | JSONL(逐消息用量) |
213
+ | Codex | `~/.codex/sessions/` | JSONL + SQLite |
214
+
215
+ 路径跨平台:Windows 下 `~` 解析到 `%USERPROFILE%`(如 `C:\Users\xxx\.claude`)。设了 `CLAUDE_CONFIG_DIR` / `CODEX_HOME` 环境变量(官方支持的自定义目录)时自动跟随。
216
+
217
+ Token Tracker 对 Agent 数据**只读**,不做任何修改。
218
+
219
+ ## 环境要求
220
+
221
+ - Python 3.11+
222
+ - [Rich](https://github.com/Textualize/rich)(自动安装)
223
+
224
+ ## 开发
225
+
226
+ ```bash
227
+ git clone https://github.com/stormzhang/token-tracker && cd token-tracker
228
+ uv run --extra dev pytest # 运行测试
229
+ uv run --extra dev ruff check src tests # Lint
230
+ ```
231
+
232
+ 包采用标准 src layout(`src/token_tracker/`):发行名 `token-tracker`,导入名 `token_tracker`(0.4.0 起)。
233
+
234
+ ## TODO
235
+
236
+ 未来持续增加更多数据报表,多维度分析。
237
+
238
+ ## License
239
+
240
+ Copyright (c) 2026 stormzhang. MIT License.
@@ -0,0 +1,187 @@
1
+ # Token Tracker (tt)
2
+
3
+ 本地 AI Agent Token 消耗追踪/分析工具,支持 **Claude Code** 和 **Codex** 。
4
+
5
+ 自定义 StatusLine 状态栏 + CLI Dashboard,实时查看 token 用量、等效成本、限额状态。
6
+
7
+ ![Python](https://img.shields.io/badge/python-3.11+-blue) ![CI](https://github.com/stormzhang/token-tracker/actions/workflows/ci.yml/badge.svg) ![License](https://img.shields.io/badge/license-MIT-green)
8
+
9
+ [English](README_EN.md)
10
+
11
+ ## StatusLine 状态栏
12
+
13
+ 自动为 Claude Code 和 Codex 配置状态栏,`tt setup` 一键配置,脚本更新时自动升级。
14
+
15
+ **Claude Code**:基于官方自定义 StatusLine 接口,数据完全来自本地 Claude,准确无任何推测
16
+
17
+ ![Claude Code StatusLine](assets/screenshot-statusline-cc.png)
18
+
19
+ 状态栏共四行,从左到右:
20
+
21
+ | 行 | 字段 | 说明 |
22
+ |----|------|------|
23
+ | 1 | `[项目](分支 +12 -3)` | 项目名(加粗)+ Git 分支(未提交修改标 `*`),括号内附工作区相对 HEAD 的增删行数 |
24
+ | 1 | `Total: 1.2M` | 本次会话累计消耗 token(输入+输出+cache,解析 transcript 得出) |
25
+ | 1 | `Cost: $35.51` | 本次会话等效成本(Claude Code 自带,按官方计费,准确) |
26
+ | 1 | `Code: +208 -8` | 本会话 Claude 写 / 删的代码行数(`+` 绿 `-` 红,与 git 变动同配色) |
27
+ | 2 | `Limit: 5h: ██░ 31% (1h19m)` | 5 小时滑动窗口配额(仅订阅模式;括号内重置倒计时) |
28
+ | 2 | `7d: ██░ 11% (5d8h)` | 7 天滑动窗口配额 |
29
+ | 2 | `1.0M Ctx: ██░ 20%` | 上下文窗口总大小及已用占比 |
30
+ | 3 | `Tokens: in 392k, out 937, cache 388k` | **当前上下文窗口**的 token 构成(注意:非会话累计,会随 compact 变化) |
31
+ | 3 | `Out TPS: 60 tokens/s` | 本轮 output token 生成速度(含 thinking;空闲帧保留上次值) |
32
+ | 4 | `Model: Opus 4.8/xhigh/nofast` | 模型名 / reasoning 级别 / 是否 fast 模式 |
33
+ | 4 | `Duration: 1h33m` | 当前会话已持续时间 |
34
+ | 4 | `Remote: github` | 代码仓库 host(去顶级域) |
35
+
36
+ > 终端宽度不足时会自动降级:先隐藏重置倒计时,再将进度条简化为百分比数字。**API 模式**无订阅配额,第 2 行只显示 Ctx。
37
+
38
+ **Codex**:官方暂不支持自定义 StatusLine,`tt setup` 通过 Codex `Stop` hook 装一个**伪 statusline**——每次回答完成后在回答尾部追加**两行**彩色 status,仿 Claude Code 状态栏(不再接管 Codex 官方 `status_line`,伪 statusline 信息比官方字段更全):
39
+
40
+ ![Codex StatusLine](assets/screenshot-statusline-codex.png)
41
+
42
+ **伪 statusline 两行布局**:
43
+
44
+ - **L1** `[项目](分支 +A -D) | Total: <会话累计 token> | Model: <模型 推理强度>` —— Total 橙、Model 红
45
+ - **L2** `Limit: 5h <进度条> % (reset <倒计时>) | 7d <进度条> % (reset <倒计时>) | <窗口> Ctx <进度条> %`
46
+
47
+ 通过 `Stop` hook 注入 `systemMessage` 实现,渲染 24-bit 真彩色、**不进模型上下文**(实测),**配色跟随当前主题**(与 CLI 报表 / CC 状态栏同源,`tt theme` 切换三者一起变)。`tt unsetup` 一并移除。
48
+
49
+ ## Status 实时面板和 日/周/月 数据报表分析
50
+
51
+ `tt`(无参)/ `tt status`:聚焦**过去 5 小时**的实时面板——顶部多 Agent **合并**概览(Token / Cost / Sessions / Messages / Top Model),中间 **5h / 7d 订阅额度**进度条(Claude Code / Codex 分开;都没订阅额度时换成 per-agent 的 token/cost/sessions/messages 统计),底部**近期会话**列表(CC + Codex 合并、带 Agent 列、按 Cost 倒序、Cost 前三名高亮)。所有时间按**系统时区**显示,配色跟随当前主题。
52
+
53
+ ![Token Tracker Status](assets/screenshot.png)
54
+
55
+ ![Token Tracker Daily](assets/screenshot-daily.png)
56
+
57
+ ![Token Tracker Weekly](assets/screenshot-weekly.png)
58
+
59
+ ![Token Tracker Monthly](assets/screenshot-monthly.png)
60
+
61
+ ## 功能
62
+
63
+ - **多 Agent 追踪** — Claude Code + Codex 统一面板,左右键切换
64
+ - **状态栏集成** — Claude Code statusLine + Codex status_line,首次运行自动配置,脚本更新自动升级
65
+ - **限额监控** — 实时 5h / 7d 配额百分比 + 重置倒计时
66
+ - **成本分析** — 按会话、日、周、月维度的等效成本统计,多 Agent 按来源分组展示
67
+ - **定价识别** — litellm 在线定价 + 内置官方价双层兜底;同系列新模型自动套用本档定价(含 Claude Fable 5 / Opus 4.8),全新系列缺价时明确提示,不静默按 $0 统计
68
+ - **会话洞察** — 项目、模型、时长、消息数一览
69
+ - **多主题统一配色** — 6 套主题(Catppuccin Mocha/Latte/Frappe/Macchiato + Nord + Dracula),CLI 报表、CC 状态栏与 Codex 伪 statusline **三者同源**;`tt theme` 一键切换 / 预览,暗 / 亮终端自动选 Catppuccin,首次运行交互向导引导选择,`TT_THEME` 可覆盖;终端不支持 truecolor 时自动降级到 256 色近似
70
+ - **零配置** — 自动检测已安装的 Agent,直接读取本地数据
71
+ - **隐私安全** — 数据纯本地存储,不采集、不上传任何用户信息,极轻量无后顾之忧
72
+
73
+ ## 安装
74
+
75
+ ```bash
76
+ curl -sSL https://raw.githubusercontent.com/stormzhang/token-tracker/main/install.sh | bash
77
+ ```
78
+
79
+ 或者通过 pip:
80
+
81
+ ```bash
82
+ pip install --force-reinstall token-tracker && tt setup
83
+ ```
84
+
85
+ ## 使用
86
+
87
+ ```bash
88
+ tt setup # 交互配置向导(终端:上下键选语言 / 主题 / 各组件);非 tty 环境自动全装
89
+ tt # 过去 5h 实时面板(合并概览 + 5h/7d 额度 + 近期会话,= tt status)
90
+ tt status # 同上(tt 无参即进 status)
91
+ tt daily # 过去一年 token 贡献热力图(GitHub 风格)+ 年度分析卡片
92
+ tt weekly # 周报:本周分析卡片 + 每日趋势柱状图 + 周 / 项目 / 模型趋势
93
+ tt monthly # 按月汇总(多 Agent 分组展示)
94
+ tt sessions # 最近 20 条会话明细(按 cost 倒序展示;tt sessions <n> 改条数、--sort 改排序)
95
+ tt theme # 查看 / 切换配色主题(show / list / set / preview)
96
+ tt unsetup # 卸载并恢复安装前的配置
97
+ ```
98
+
99
+ > 💡 `tt daily` 是 GitHub 风格的 token 贡献热力图(深浅绿方格)。在 Claude Code 会话里输入 `!tt daily` 即可看到彩色热力图 —— 用户主动用 `!` 执行的命令,Claude Code 会渲染其 24-bit 真彩色输出。
100
+
101
+ ### 首次运行向导
102
+
103
+ 第一次跑 `tt`(或通过 curl 一键安装脚本装完自动启动)会进入**交互式配置向导**,全程上下键选 + 回车确认:
104
+
105
+ 1. **选语言** — 中文 / English(落 `~/.config/token-tracker/config.json`,之后所有命令跟随)
106
+ 2. **选配色主题** — 6 套主题上下键选择,每个选项右侧内联色板预览
107
+ 3. **启用 Codex 伪 statusline** — Yes/No,默认 Yes(仅检测到 Codex 时)
108
+
109
+ 选完给一份键值对齐的配置总结 + 重启 / 下一步提示。CI / 非 tty 环境(Docker / 脚本)自动按默认全装:**语言跟随系统设置**(读系统语言、不被 CLI 的 `LANG` 误导)、主题 mocha、组件全开。装好后想改任何一项,再跑一次 `tt setup` 即可(终端里每次 `tt setup` 都进向导)。
110
+
111
+ ### 配色主题
112
+
113
+ 内置 6 套主题,CLI 报表、CC 状态栏与 Codex 伪 statusline **统一同源**(切主题三者一起变):
114
+
115
+ | 主题 | 说明 |
116
+ |------|------|
117
+ | `mocha` / `latte` / `frappe` / `macchiato` | Catppuccin 全家(暗 / 亮终端自动选 mocha / latte) |
118
+ | `nord` | Nord |
119
+ | `dracula` | Dracula |
120
+
121
+ ```bash
122
+ tt theme # 显示当前主题及来源
123
+ tt theme list # 列出全部主题 + 色块预览
124
+ tt theme preview nord # 预览某主题(CLI 样例 + 状态栏样例行)
125
+ tt theme set nord # 切换主题(持久化 + 重烘焙状态栏)
126
+ tt monthly --theme nord # 任意报表临时换主题渲染(不持久化、不动状态栏,适合对比)
127
+ ```
128
+
129
+ - **首次运行**(终端内、非 AI 会话)会进入交互向导引导选主题;CI / 脚本 / 会话内自动跳过、静默用默认。
130
+ - 切换持久化到 `~/.config/token-tracker/config.json`(主题 + 语言 + 组件意图统一存这一个文件);优先级 `--theme` 参数 > `TT_THEME` 环境变量 > 配置文件 > 自动(`--theme` 和 `TT_THEME` 都只临时生效、不写配置)。
131
+ - 终端支持 truecolor 用精确配色;不支持的(如 macOS 自带 Terminal.app)自动降级到当前主题的 **256 色近似**,8 色老终端不再适配。
132
+
133
+ ### 报告排序
134
+
135
+ 所有报告命令支持 `--sort` 和 `--asc/--desc` 参数:
136
+
137
+ ```bash
138
+ tt weekly --sort cost --desc # 按成本降序
139
+ tt sessions --sort tokens --asc # 按 token 升序
140
+ ```
141
+
142
+ 可选排序字段:`tokens` / `cost` / `messages` / `time` / `input` / `output`
143
+
144
+ ### Dashboard 快捷键
145
+
146
+ | 按键 | 功能 |
147
+ |------|------|
148
+ | `←` `→` | 切换 Agent |
149
+ | `↑` `↓` | 滚动内容 |
150
+ | `s` | 切换排序字段(时间 → Token → 等效成本 → 消息数) |
151
+ | `r` | 反转排序方向 |
152
+ | `+` / `-` | 调整会话显示条数(±10,最少 10 条) |
153
+ | `q` | 退出 |
154
+
155
+ ## 数据来源
156
+
157
+ | Agent | 路径 | 格式 |
158
+ |-------|------|------|
159
+ | Claude Code | `~/.claude/projects/*/` | JSONL(逐消息用量) |
160
+ | Codex | `~/.codex/sessions/` | JSONL + SQLite |
161
+
162
+ 路径跨平台:Windows 下 `~` 解析到 `%USERPROFILE%`(如 `C:\Users\xxx\.claude`)。设了 `CLAUDE_CONFIG_DIR` / `CODEX_HOME` 环境变量(官方支持的自定义目录)时自动跟随。
163
+
164
+ Token Tracker 对 Agent 数据**只读**,不做任何修改。
165
+
166
+ ## 环境要求
167
+
168
+ - Python 3.11+
169
+ - [Rich](https://github.com/Textualize/rich)(自动安装)
170
+
171
+ ## 开发
172
+
173
+ ```bash
174
+ git clone https://github.com/stormzhang/token-tracker && cd token-tracker
175
+ uv run --extra dev pytest # 运行测试
176
+ uv run --extra dev ruff check src tests # Lint
177
+ ```
178
+
179
+ 包采用标准 src layout(`src/token_tracker/`):发行名 `token-tracker`,导入名 `token_tracker`(0.4.0 起)。
180
+
181
+ ## TODO
182
+
183
+ 未来持续增加更多数据报表,多维度分析。
184
+
185
+ ## License
186
+
187
+ Copyright (c) 2026 stormzhang. MIT License.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "token-tracker"
7
- version = "0.3.8"
7
+ version = "0.4.0"
8
8
  description = "Track token usage across local AI agents (Claude Code, Codex)"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -26,6 +26,7 @@ classifiers = [
26
26
  ]
27
27
  dependencies = [
28
28
  "rich>=13.7",
29
+ "questionary>=2.0",
29
30
  ]
30
31
 
31
32
  [project.optional-dependencies]
@@ -44,10 +45,10 @@ Repository = "https://github.com/stormzhang/token-tracker"
44
45
  Issues = "https://github.com/stormzhang/token-tracker/issues"
45
46
 
46
47
  [project.scripts]
47
- tt = "src.cli:main"
48
+ tt = "token_tracker.cli:main"
48
49
 
49
50
  [tool.setuptools.packages.find]
50
- include = ["src*"]
51
+ where = ["src"]
51
52
 
52
53
  [tool.ruff]
53
54
  line-length = 120
@@ -3,10 +3,10 @@ from datetime import UTC, datetime
3
3
  from pathlib import Path
4
4
 
5
5
  from .types import AgentInfo, UsageEntry
6
- from .util import iter_jsonl_dicts, project_from_cwd
6
+ from .util import claude_home, iter_jsonl_dicts, project_from_cwd
7
7
 
8
8
  CLAUDE_DIRS = [
9
- os.path.expanduser("~/.claude/projects"),
9
+ os.path.join(claude_home(), "projects"),
10
10
  os.path.expanduser("~/.config/claude/projects"),
11
11
  ]
12
12
 
@@ -4,16 +4,29 @@ from datetime import UTC, datetime, timedelta
4
4
  from pathlib import Path
5
5
 
6
6
  from .types import AgentInfo, RateLimits, UsageEntry, normalize_pct
7
- from .util import iter_jsonl_dicts, project_from_cwd
7
+ from .util import codex_home, iter_jsonl_dicts, project_from_cwd
8
8
 
9
- CODEX_DIR = os.path.expanduser("~/.codex")
9
+ CODEX_DIR = codex_home()
10
10
  SESSIONS_DIR = os.path.join(CODEX_DIR, "sessions")
11
11
  STATE_DB = os.path.join(CODEX_DIR, "state_5.sqlite")
12
12
  _RATE_LIMIT_SCAN_FILES = 5 # 只扫最近改动的 N 个 session 文件找限额信息
13
13
 
14
+ # Codex 内部虚拟 model 改写到背后真实 model,避免它们在 Model Trend / sessions 等报表里独占行;
15
+ # 同时让 cost 走真实 model 的精确定价(cost.py 的 codex- 系列兜底是双保险)。
16
+ # 已知虚拟 model:
17
+ # codex-auto-review —— stop-time auto-review gate,背后跑当前主 codex 模型(gpt-5.5)
18
+ _VIRTUAL_MODEL_REWRITE = {
19
+ "codex-auto-review": "gpt-5.5",
20
+ }
21
+
22
+
23
+ def _rewrite_virtual_model(model: str) -> str:
24
+ return _VIRTUAL_MODEL_REWRITE.get(model, model)
25
+
14
26
 
15
27
  def detect() -> AgentInfo | None:
16
- if Path(SESSIONS_DIR).is_dir():
28
+ # 以 ~/.codex 目录判断是否安装(与 hooks._has_codex 一致;不要求已产生 sessions/)
29
+ if Path(CODEX_DIR).is_dir():
17
30
  return AgentInfo(id="codex", name="Codex")
18
31
  return None
19
32
 
@@ -45,7 +58,7 @@ def _load_thread_models() -> dict[str, str]:
45
58
  conn = sqlite3.connect(f"file:{STATE_DB}?mode=ro", uri=True)
46
59
  rows = conn.execute("SELECT id, model FROM threads WHERE model IS NOT NULL").fetchall()
47
60
  conn.close()
48
- return {row[0]: row[1] for row in rows}
61
+ return {row[0]: _rewrite_virtual_model(row[1]) for row in rows}
49
62
  except (sqlite3.Error, OSError):
50
63
  return {}
51
64
 
@@ -137,9 +150,19 @@ def _parse_jsonl(
137
150
  model = "unknown"
138
151
  last_usage = None
139
152
  msg_count = 0
153
+ session_end_ts: datetime | None = None
140
154
 
141
155
  for data in iter_jsonl_dicts(path):
142
156
  row_type = data.get("type")
157
+ # 取所有事件里最大的 timestamp 作会话结束时间(与 session 开始的差 = 真实跨度,供 sessions 报表)
158
+ ts_raw = data.get("timestamp")
159
+ if ts_raw:
160
+ try:
161
+ et = datetime.fromisoformat(ts_raw.replace("Z", "+00:00"))
162
+ if session_end_ts is None or et > session_end_ts:
163
+ session_end_ts = et
164
+ except (ValueError, AttributeError):
165
+ pass
143
166
 
144
167
  if row_type == "session_meta":
145
168
  payload = data.get("payload", {})
@@ -197,4 +220,5 @@ def _parse_jsonl(
197
220
  project=project,
198
221
  agent_id="codex",
199
222
  message_count=msg_count,
223
+ session_end=session_end_ts,
200
224
  ))
@@ -4,7 +4,8 @@ from datetime import UTC, datetime
4
4
 
5
5
  from .types import RateLimits, normalize_pct
6
6
 
7
- STATUS_FILE = os.path.expanduser("~/.claude/tt-status.json")
7
+ # 与 hooks.STATUS_FILE 一致:tt 自己的产物集中放 ~/.config/token-tracker(XDG)
8
+ STATUS_FILE = os.path.join(os.path.expanduser("~/.config/token-tracker"), "tt-status.json")
8
9
 
9
10
 
10
11
  def load_rate_limits() -> RateLimits | None:
@@ -29,6 +29,8 @@ class UsageEntry:
29
29
  project: str
30
30
  agent_id: str
31
31
  message_count: int = 1
32
+ # 仅 codex:单条 entry 即整段会话,记录会话结束时间,让 aggregate_sessions 能算真实跨度(claude 留 None 走多条 entry)
33
+ session_end: datetime | None = None
32
34
 
33
35
  @property
34
36
  def total_tokens(self) -> int:
@@ -57,6 +59,7 @@ class DailyStats:
57
59
  session_count: int = 0
58
60
  message_count: int = 0
59
61
  models: dict[str, int] = field(default_factory=dict)
62
+ projects: dict[str, int] = field(default_factory=dict)
60
63
  agent_id: str = ""
61
64
 
62
65
 
@@ -74,6 +77,7 @@ class WeeklyStats:
74
77
  session_count: int = 0
75
78
  message_count: int = 0
76
79
  models: dict[str, int] = field(default_factory=dict)
80
+ projects: dict[str, int] = field(default_factory=dict)
77
81
  agent_id: str = ""
78
82
 
79
83
 
@@ -84,7 +88,8 @@ class SessionStats:
84
88
  model: str
85
89
  start_time: datetime
86
90
  end_time: datetime
87
- duration_minutes: float
91
+ duration_minutes: float # 会话跨度(首尾时间差),仅用于显示
92
+ active_minutes: float = 0.0 # 活跃时长:相邻 entry 间隔 ≤ CAP 才累加、大空隙丢弃;也用于过滤 <5min 短会话
88
93
  input_tokens: int = 0
89
94
  output_tokens: int = 0
90
95
  cache_creation_tokens: int = 0
@@ -93,6 +98,8 @@ class SessionStats:
93
98
  cost_usd: float = 0.0
94
99
  message_count: int = 0
95
100
  agent_id: str = ""
101
+ # 会话内各 model 的 output_tokens(按生成量降序),渲染时取前若干个展示
102
+ models: dict[str, int] = field(default_factory=dict)
96
103
 
97
104
 
98
105
  @dataclass
@@ -107,6 +114,7 @@ class MonthlyStats:
107
114
  session_count: int = 0
108
115
  message_count: int = 0
109
116
  models: dict[str, int] = field(default_factory=dict)
117
+ projects: dict[str, int] = field(default_factory=dict)
110
118
  agent_id: str = ""
111
119
 
112
120
 
@@ -142,3 +150,17 @@ class SessionBlock:
142
150
  is_active: bool = False
143
151
  burn_rate: float = 0.0
144
152
  is_gap: bool = False
153
+
154
+
155
+ @dataclass
156
+ class StatusSummary:
157
+ """tt status 头图面板:当天多 agent 合并的消耗汇总(add_token_fields 累加用)。"""
158
+ input_tokens: int = 0
159
+ output_tokens: int = 0
160
+ cache_creation_tokens: int = 0
161
+ cache_read_tokens: int = 0
162
+ total_tokens: int = 0
163
+ cost_usd: float = 0.0
164
+ message_count: int = 0
165
+ session_count: int = 0
166
+ models: dict[str, int] = field(default_factory=dict)