Windows 桌面挂件,常驻在桌面最前面显示自己 Cursor 账号的 Token 用量与 Requests 消耗。数据来自 Dashboard 导出的 CSV,或用登录 Cookie 直接联网拉取;支持手动更新和每天 0 点自动刷新。
- 零第三方依赖 — 只用 Python 3.11+ 标准库(Tkinter 界面),
git clone完可以直接跑 - 桌面常驻 — 无边框圆角面板墙,五个子面板一眼看完,可拖动、可置顶、可开机自启
- 双击展开明细 — 完整窗口含 KPI、图表和可翻查的表格
- 历史会累积 — 每次导入都与本地数据合并去重,不会因为 Dashboard 只保留近期数据而丢历史
- 打开即追平 — 错过的每日同步会在下次启动时自动补上,不用让程序一直挂着
- 本地时区统计 — 按你所在时区切分「每一天」,而不是按 UTC
git clone <your-repo-url> cursor-usage-tracker
cd cursor-usage-tracker
python app.py桌面右下角会出现小面板。Windows 上可以双击 start.cmd 启动(用 pythonw 运行,不留控制台窗口)。
首次运行没有数据,两种方式任选:
- 在 cursor.com/dashboard/usage 点 Export CSV,然后在面板上右键 →「导入 CSV…」;或者把文件留在
Downloads里点「⟳」,它会自动找最新的team-usage-events-*.csv。 - 配置 Session Token(见下),之后点「⟳」就直接联网拉取。
挂件有收起和展开两个形态,点底部的 ⊞ / ⊟ 切换,选择会记住。
默认是收起态,只有一张概览卡——今日和 7 天两个数字、各自的费用与 Token 量,加一条趋势曲线。深浅两套主题右键菜单切换:
点 ⊞ 展开成完整面板墙,两列网格摆五个子面板:
| 面板 | 内容 | |
|---|---|---|
| ① | 左上 | 今日 / 7 天 + 趋势曲线 |
| ② | 右上 | 活跃度热力图(每列一周) |
| ③ | 左下 | 今日 / 7 天 / 30 天 + 每日 Requests 柱状图 + 模型前三 |
| ④ | 右中 | 模型消耗排行(带占比条) |
| ⑤ | 右下 | Token 结构:缓存读取 / 缓存写入 / 直接输入 / 输出 |
收起态就是面板 ① 本身,宽度取它在网格里的实际列宽,所以两个形态里这张卡长得一模一样,只是周围的内容被裁掉了。
右列两块的高度之和决定左下大面板的高度,所以柱状图能拉得很高——这是参考设计的比例关系。
整块面板墙绘制在一张 Canvas 上,而不是拼 Tkinter 控件——Tk 控件永远是不透明矩形,画不出圆角、渐隐的图表填充和发丝级分隔线。圆角靠 Windows 的透明色键实现:窗口把一个特定颜色渲染成真正的空洞,所以边角是透的而不是糊一块底色。
| 操作 | 说明 |
|---|---|
| 拖动面板 | 按住任意空白处移动,位置会记住 |
点 ⊞ / ⊟ |
在收起态和完整面板墙之间切换 |
双击 / 点 ⤢ |
打开完整明细窗口(收起态下双击或走右键菜单) |
点 ⟳ |
立即更新 |
点 ✕ |
退出 |
| 右键 | 菜单:只显示概览、面板尺寸、置顶、开机自启、深浅主题、导入 CSV、设置 Session Token、打开网页版 |
操作图标在底板最下面一行,和更新时间同排。更新时间左边出现 ⚠ 表示还没配置 Session Token,此时「更新」不会联网,只能重读本地导出的 CSV。右边跟着一句 · 历史总费用 $172.50,是全部历史里 Cursor 实际计过价的美元合计,窄到放不下时会自动省略。
尺寸三档(右键菜单切换,逻辑像素,高 DPI 屏按缩放比例等比放大):
| 档位 | 收起态 | 展开态 |
|---|---|---|
| 小 | 242 × 148 | 470 × 403 |
| 中(默认) | 293 × 160 | 570 × 431 |
| 大 | 354 × 170 | 690 × 453 |
大号数字是 Requests,下面一行小字是费用和 Token 量,例如 $172.50 · 187.9M tok;「今日」那列右侧还有一个与昨天对比的涨跌幅,跌为绿色、涨为橙色。费用是 Cursor 原样返回的金额,不做任何折算。金额的结算比用量慢,刚产生的事件可能先报 $0.00,过一阵才补上(见下文「关于计费口径」)。列宽放不下完整数字时会退回缩写(12,480 → 12.5K),小字放不下时先丢掉 Token 量只留费用。柱状图和曲线的时间跨度随本地历史增长,数据只有几天时不会画出一长串空槽;热力图则一开始基本是空的,要攒够天数才好看。
双击挂件或点 ⤢ 打开:
顶部五个指标是合计 Requests、日均 Requests、本月预计、总 Token 和事件数,下面是每日 Requests 趋势、模型消耗排行、Token 结构(Cache Read / Input / Output 占比)和时段分布,再往下是按天 / 按模型 / 原始事件三张表。右上角可切换今天、近 7 天、30 天或全部。按天和按模型两张表额外带一列「费用」(见下文「关于计费口径」)。
窗口打开时不会超出屏幕,工具栏固定,内容区可滚动,所以在 1080p 及更小的屏幕上也能看到全部图表和表格。
两份模板,按需复制:
copy .env.example .env
copy config.example.toml config.toml.env 的优先级高于 config.toml,两者都是可选的。常用项:
| 配置 | 说明 |
|---|---|
CURSOR_SESSION_TOKEN |
浏览器 Cookie 里的 WorkosCursorSessionToken,填了才能联网拉取 |
CURSOR_TEAM_ID |
团队数字 ID,来自导出文件名 team-usage-events-<ID>-*.csv |
CSV_WATCH_DIR |
更新时扫描 CSV 的目录,默认 ~/Downloads |
APP_TIMEZONE |
按天统计和 0 点刷新用的时区,默认 Asia/Shanghai |
PORT |
网页版端口,默认 8787 |
最省事的方式是在界面里填:面板右键 →「设置 Session Token…」,或明细窗口点「连接」。填完会立刻联网自测一次,通过才保存到 .env,所以填错不会静默失败。
- 打开 https://cursor.com/dashboard/usage 并保持登录
- F12 → Application → Cookies →
https://cursor.com - 复制
WorkosCursorSessionToken的值粘贴进去
粘贴时可以只给值,也可以整段 WorkosCursorSessionToken=…,甚至整条 Cookie 字符串,程序会自己截取。
没配置 Token 时,「更新」只能重新读取 Downloads 里最新的导出文件——界面会明确标出 ⚠ 未连接,更新结果也会写明来源是本地文件而不是联网同步,不会让你误以为已经同步过了。
用的是 Dashboard 页面自己调的接口(非官方公开 API),Cursor 改版可能失效——这时退回 CSV 导入即可,功能不受影响。
Token 就是浏览器 Cookie,会自己过期,在别处退出登录、改密码同样会让它失效。麻烦的是接口并不总是老实回一个鉴权错误码,而是把登录页或人机校验页当作响应发回来。所以判断依据是更新失败弹窗里的那句提示:
| 提示 | 含义 | 要不要重填 Token |
|---|---|---|
Session Token 无效或已过期(401) |
明确的鉴权失败 | 要 |
接口返回了网页而不是数据(403) |
收到的是 HTML 页面,登录态已经不认了 | 要 |
登录状态可能已失效(返回的不是 JSON) |
同上,只是没带错误码 | 要 |
请求超时 / 网络请求失败 |
网络到 cursor.com 不通,与 Token 无关 | 不用 |
重填的办法和上面一样:浏览器重新登录 https://cursor.com/dashboard/usage,复制新的 WorkosCursorSessionToken,右键面板 →「设置 Session Token…」。填完会立刻联网自测,通过才写回 .env。
有一点要留意:挂件上的 ⚠ 只代表「没填过 Token」,不代表 Token 过期。过期的 Token 在程序看来仍是「已连接」,所以不会亮这个标记,表现只有两个——每次点更新都弹失败,以及底部的同步时间一直停在旧的那一刻。后台自动同步也会照样每 15 分钟重试、每次都失败,直到你换上新 Token。这期间本地已经攒下的历史数据不受任何影响。
Windows 没有系统 IANA 时区库。如果没装 tzdata,程序会退回 config.toml 里 [scheduler].utc_offset_hours 的固定偏移(默认 +8,对不实行夏令时的 Asia/Shanghai 完全正确)。需要严格时区支持就 pip install tzdata。
后台一个线程管两件事,任一条件满足就同步一次。
程序开着的时候,每隔 [scheduler].interval_minutes 分钟自动拉一次,默认 5 分钟:
[scheduler]
interval_minutes = 5 # 改成 0 就只保留下面的每日同步几个细节:
- 计时从上一次同步算起,不是固定的整点节拍。手动点一次「⟳」,下一次自动同步也跟着顺延 5 分钟,不会刚点完又立刻拉一遍
- 小于 1 分钟的值会被抬到 1 分钟,免得手滑写成
0.1就去砸接口 - 没配 Token 时这条不生效。没有 Token,「更新」只能重读
Downloads里的 CSV,每 5 分钟把同一个文件重写一遍既拿不到新数字,又白白磨硬盘 - 默认 5 分钟约等于每天 288 次请求。想更省就调大,实时性要求高就调小,但别低于 1 分钟
这一条是给「程序没开着」的情况兜底的。判断依据是「上次同步是否早于最近一个 00:00」,而不是死等定时器触发,所以:
- 应用启动时如果发现昨天那次没跑成(关着机、没开应用、或者休眠过去了),会立刻补一次
- 隔几天打开一次也不用担心,打开就会自己追上最新数据
- 同步期间挂件底部显示「自动同步中…」,完成后自动刷新面板
- 拉取失败(断网、Token 过期)不会每 5 分钟死磕,退避到 15 分钟后再试
- 自动同步和你手点的「⟳」共用一把写锁,两边撞在一起也不会互相覆盖掉刚写入的数据
拉取是分页的,所以有两层限制,避免「更新中…」一直转下去:
| 限制 | 时长 | 作用范围 |
|---|---|---|
TIMEOUT |
45 秒 | 单次 HTTP 请求;剩余预算不足时会自动缩短 |
BUDGET |
90 秒 | 整轮分页拉取,超时就带着已取回的条数中止 |
PROBE_TIMEOUT |
20 秒 | 校验 Session Token,比正式同步短,因为你正对着弹窗等 |
工作线程杀不掉,所以界面侧还有一层兜底:超过 120 秒仍没返回就放弃等待、把「⟳」按钮交还给你。这时后台线程可能还在跑,它写进本地库的数据不会丢,只是不再弹成功提示。
也可以从右键菜单打开开机自动启动,它会在 Windows 启动文件夹里放一个用 pythonw 启动的快捷命令。
如果不想让程序常驻,可以改用系统计划任务,只跑一次刷新后退出:
schtasks /create /tn "CursorUsageRefresh" /tr "python D:\SelfDev\cursor-usage-tracker\app.py --refresh" /sc daily /st 00:05python app.py 桌面挂件(默认)
python app.py --web 网页版看板,浏览器打开 127.0.0.1:8787
python app.py --refresh 只刷新一次数据后退出
python app.py --port 9000 指定网页版端口
python app.py --no-browser 网页版不自动打开浏览器
python app.py --verbose 输出调试日志
网页版和桌面版共用同一份本地数据,随时可以从右键菜单「打开网页版」切过去。
app.py 入口(桌面 / 网页 / 一次性刷新)
usage_tracker/
config.py .env / config.toml 加载,时区解析
parser.py CSV 解析、合并去重、聚合统计
store.py 本地 CSV 持久化与缓存
fetcher.py 联网拉取 + 本地导出文件发现
scheduler.py 后台刷新线程:定时间隔 + 每日 0 点补同步
server.py 网页版 HTTP 服务与 JSON API
desktop/
app.py 桌面应用调度
widget.py 桌面面板墙(五个子面板,整体 Canvas 绘制)
paint.py 绘图原语:圆角、曲线、柱状、热力图、排行条
window.py 明细窗口
charts.py 明细窗口图表:柱状 / 条形 / 环形
theme.py 配色、字体测量、DPI 适配
token_dialog.py Session Token 配置与连通性自测
state.py 界面偏好持久化
autostart.py 开机自启项
fmt.py 数字格式化
web/ 网页版 index.html / styles.css / app.js
docs/images/ README 里的界面截图
sample/ 合成示例数据(供测试与试跑)
tests/ 单元测试
tools/selftest.py 对本地真实数据做一次体检
tools/uicheck.py 不进主循环地检查桌面界面各条路径
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/summary?days=30 |
聚合数据,days 可为数字或 all |
| POST | /api/refresh |
触发一次更新(API 或本地 CSV) |
| POST | /api/upload |
请求体为 CSV 文本,合并进本地数据 |
| GET | /api/billing |
套餐与账单周期(需要 Token) |
服务只绑定 127.0.0.1,不对外暴露。
python -m unittest discover -s tests -v # 针对合成样例的单元测试
python tools/selftest.py # 检查本地真实数据并打印汇总
python tools/uicheck.py # 检查桌面界面(需要桌面会话)data/、.env、config.toml 都在 .gitignore 里,用量数据和 Token 不会进版本库。
一个用量事件的身份是「发生时刻 + 模型」,别的字段一律不参与判重,因为它们对同一个事件并不稳定:
User在 CSV 导出里是邮箱,在 JSON 接口里是数字 IDKind两个来源叫法不同(On-Demand/Usage-based),而且会随额度消耗被重新归类- Token 数在对话还没结束时会持续增长,同一个事件下次拉取时数值更大
Cost传输中会丢精度,2026-07-30 之后的导出里干脆没有了
合并同一个事件的两份副本时,取 Token 更完整的那份作基准,Cost 和 Requests 各取较大值,这样任何一个来源都不会把另一个来源独有的字段抹掉。已经存进去的重复行会在下次写入时自动塌缩。
2026-07-30 Cursor 加了一个计价单位。 在那之前,Dashboard 导出的 CSV 最后一列是 Cost(美元);之后这一列换成了 Requests(请求单位),JSON 接口里对应字段是 requestsCosts。
金额没有消失,只是结算得慢。 切换后的头两天(07-31、08-01)拉下来的事件 chargedCents 全是 0,看着像是 Cursor 不再报金额了;但到 08-03 再拉,这两天已经被补成 $2.88 和 $81.55。所以 $0.00 的正确含义是「还没算出来」,不是「免费」。合并逻辑对同一事件的两个副本取较大的金额,补上来的数值因此能覆盖掉先前存下的 0,不需要清库重拉。
单条事件换算不了,按天汇总倒是可以。 拿同时带有两个数值的历史事件实测,Cost / Requests 的比值在 0.0240.061 之间,金额较大的事件也有 0.04350.0560 的浮动,所以逐条折算没有意义。但同一天几十条事件的偏差会互相抵消:用留一验证(拿其余几天的单价预测被留出的那天),统一按 $0.05/request 折算,有量的日子误差只有 1~3%。
| 留出的日期 | 真实金额 | 按 $0.05 估算 | 误差 |
|---|---|---|---|
| 2026-07-28 | $29.04 | $28.26 | −3% |
| 2026-07-29 | $61.52 | $63.27 | +3% |
| 2026-07-30 | $81.60 | $82.40 | +1% |
即便如此,本工具不做这个折算,界面上的每一个金额都是 Cursor 原样返回的:
Requests是主指标,大号数字、图表、排行都按它统计。它随事件立刻到账,不会有延迟Cost原样显示,不估算、不外推。刚产生的窗口可能先是$0.00,等 Cursor 结算完再自己填上- 页脚的「历史总费用」是全部历史的实际计费合计
- 同一条事件如果分别从旧 CSV 和新接口进来,合并时两个字段各取各的,不会互相覆盖
另外,Kind = Included 的事件,Cost 是计入套餐额度的等价费用,不等于额外账单金额。真实账单和剩余额度仍以 Cursor Dashboard 为准。
MIT