Skip to content

Repository files navigation

Cursor Usage Tracker

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 运行,不留控制台窗口)。

首次运行没有数据,两种方式任选:

  1. cursor.com/dashboard/usageExport CSV,然后在面板上右键 →「导入 CSV…」;或者把文件留在 Downloads 里点「⟳」,它会自动找最新的 team-usage-events-*.csv
  2. 配置 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,48012.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

最省事的方式是在界面里填:面板右键 →「设置 Session Token…」,或明细窗口点「连接」。填完会立刻联网自测一次,通过才保存到 .env,所以填错不会静默失败。

  1. 打开 https://cursor.com/dashboard/usage 并保持登录
  2. F12 → Application → Cookies → https://cursor.com
  3. 复制 WorkosCursorSessionToken 的值粘贴进去

粘贴时可以只给值,也可以整段 WorkosCursorSessionToken=…,甚至整条 Cookie 字符串,程序会自己截取。

没配置 Token 时,「更新」只能重新读取 Downloads 里最新的导出文件——界面会明确标出 ⚠ 未连接,更新结果也会写明来源是本地文件而不是联网同步,不会让你误以为已经同步过了。

用的是 Dashboard 页面自己调的接口(非官方公开 API),Cursor 改版可能失效——这时退回 CSV 导入即可,功能不受影响。

Token 过期

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

自动更新

后台一个线程管两件事,任一条件满足就同步一次。

每 5 分钟(默认)

程序开着的时候,每隔 [scheduler].interval_minutes 分钟自动拉一次,默认 5 分钟

[scheduler]
interval_minutes = 5   # 改成 0 就只保留下面的每日同步

几个细节:

  • 计时从上一次同步算起,不是固定的整点节拍。手动点一次「⟳」,下一次自动同步也跟着顺延 5 分钟,不会刚点完又立刻拉一遍
  • 小于 1 分钟的值会被抬到 1 分钟,免得手滑写成 0.1 就去砸接口
  • 没配 Token 时这条不生效。没有 Token,「更新」只能重读 Downloads 里的 CSV,每 5 分钟把同一个文件重写一遍既拿不到新数字,又白白磨硬盘
  • 默认 5 分钟约等于每天 288 次请求。想更省就调大,实时性要求高就调小,但别低于 1 分钟

每日 00:00

这一条是给「程序没开着」的情况兜底的。判断依据是「上次同步是否早于最近一个 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:05

命令行参数

python 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              不进主循环地检查桌面界面各条路径

HTTP API(网页版)

方法 路径 说明
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/.envconfig.toml 都在 .gitignore 里,用量数据和 Token 不会进版本库。

数据去重

一个用量事件的身份是「发生时刻 + 模型」,别的字段一律不参与判重,因为它们对同一个事件并不稳定:

  • User 在 CSV 导出里是邮箱,在 JSON 接口里是数字 ID
  • Kind 两个来源叫法不同(On-Demand / Usage-based),而且会随额度消耗被重新归类
  • Token 数在对话还没结束时会持续增长,同一个事件下次拉取时数值更大
  • Cost 传输中会丢精度,2026-07-30 之后的导出里干脆没有了

合并同一个事件的两份副本时,取 Token 更完整的那份作基准,CostRequests 各取较大值,这样任何一个来源都不会把另一个来源独有的字段抹掉。已经存进去的重复行会在下次写入时自动塌缩。

关于计费口径

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 为准。

License

MIT

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages