Skip to content

About

[fork] WorkBuddy / CodeBuddy 的积分账单 × 本地 Token 消耗关联看板:看清每积分换多少 Token、缓存有没有省钱。纯本地、零依赖;本 fork 增加 0.0.0.0 网站化部署(局域网直接看,静态白名单防凭证泄露)与桌面端凭证不可用时回退 IDE 登录态。

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Latest commit

 

History

77 Commits

Folders and files

Repository files navigation

buddyToken · 积分消耗看板

把 AI 编码助手的积分账单和本机会话记录里的 Token 消耗关联起来,看清每一分积分到底换来了多少 Token。

Node dependencies license platform

适用于 WorkBuddy 桌面端与 CodeBuddy CLI。两者内核相同、会话记录格式一致,只是根目录不同,本项目同时扫描两边。

纯本地运行:零服务端部署、零依赖安装(脚本只用 Node 标准库,看板图表库走 CDN),一切数据留在你自己机器上。

和生态里其他工具的区别:WorkBuddy / CodeBuddy 周边的工具已经不少——转 API 的、自动签到的、多账号轮换的、账号池管理的。它们都在帮你多拿积分;这个项目反过来,只回答一个它们都不回答的问题:积分花在哪了、值不值。

差别来自关联:官方账单只知道「这次提问扣了 0.37 积分」,本机留痕只知道「这次请求用了多少 Token」,两份数据本来互不相干。本项目按回合 ID 把它们对上——这件事目前没有别的工具在做。


目录


它解决什么问题

AI 助手的积分账单只会告诉你「这一次提问扣了 0.37 积分」,但不会告诉你:

  • 这 0.37 积分背后实际消耗了多少 Token?
  • 缓存命中了多少?缓存到底有没有帮我省钱?
  • 哪个工具(读文件 / 执行命令 / 搜索)最烧 Token?
  • 哪个模型更划算?

这些信息在本地会话记录里其实都有,只是和账单是两份互不相干的数据。本项目把它们对上。


功能特性

账号与积分(官方接口自动同步,每账号一张卡片)

  • 剩余积分 / 总量 / 已用比例,带进度条;最近到期时间,7 天内到期变红警告
  • 今日 / 昨日消耗:按自然日聚合的账单消耗,悬停看口径说明
  • 预计可用天数:按最近三周的消耗节奏推算这份余额还够用多久,与「最近到期」取小——到期前用不完时直接提示,不给虚高天数;悬停看日均与样本天数
  • 套餐明细按名称聚合——几十个零散赠包会合并成几行,不会刷屏
  • 签到日历:签到格子(右端为今天)+ 连续天数 / 累计签到天数 / 累计积分
  • 状态 chips(纯图标,悬停看详情):签到、旅行剩余时间、429 限流(带数量角标)、建议优先使用的账号
  • 登录徽标与一键切换:卡片右上角三个应用图标(WorkBuddy 桌面端 / CodeBuddy IDE / CodeBuddy CLI),图标即状态——当前登录 ✓、可切换可点、置灰表示不可用(CodeBuddy IDE 的登录态不在本项目读取范围内,恒置灰);点击二次确认后切换账号——桌面端会重启 WorkBuddy 生效,CodeBuddy CLI 写完即生效、无需重启
  • 🔄 单账号刷新:每张卡片底部的刷新按钮只重拉该账号的账单与积分余额,其余账号原样保留,比整轮同步快得多

签到与派猫猫(每账号两个按钮,需要本地服务)

按钮 行为
✅ 签到 每日签到领积分。已签到会自动识别,不会重复提交;点完回显连续天数与累计积分
🐱 派猫猫 按状态机自动判断:空闲就派出去、已到达就领奖励、旅行中提示还要等多久、今日已派则跳过

两个动作都会真的调用官方接口改变服务端状态(等同你自己在官网点一下)。

自动任务(账号卡片上方,需要本地服务)

四个独立开关,默认全关:点开哪个哪个开始跑(开启即先执行一次给你反馈),之后按各自周期自动执行。每行显示周期、说明与最近一次结果——开关变绿=最近一次成功、变红=最近一次失败,失败原因与结果摘要直接写在开关下方,悬浮可见绝对时间。

任务 默认周期 行为
自动签到 每 6 小时 为全部账号签到,已签到的自动跳过(幂等,不会重复提交)
自动猫猫 每 30 分钟 按状态机照看全部账号的猫猫:空闲自动派出、到点自动领奖。查得勤才不漏领每日积分
自动切换账号 每 1 小时 有账号积分将在 14 天内到期(switchHorizonDays 可调)且还没用完时,自动把 WorkBuddy 桌面端与 CodeBuddy CLI 都切到它,优先用掉即将过期的积分。桌面端仅在其正在运行时才重启生效;切换前自动备份登录态,可随时手动切回。自动切换成功后 24 小时内不再切(switchCooldownHours 可调,手动切换不受限)
自动刷新积分 每 30 分钟 拉取官方账单与各账号余额,增量合并、历史不丢;开启后看板余额自动保鲜,无需手动点「💰 同步积分」

开关与参数都存在本地服务的 auto-config.json(不入库),字段如下:

字段 含义
四个开关 checkin / travel / switch / refresh,布尔值,由页面上的 chip 读写
intervalMinutes 各任务的执行周期,单位分钟(如 "checkin": 360 = 6 小时)
jitterPercent 触发抖动的随机幅度 ±N%,默认 10
switchHorizonDays 「自动切换账号」的触发阈值:有积分将在 N 天内到期才切,默认 14,下限 1
switchCooldownHours 「自动切换账号」成功后的冷却时长,单位小时,默认 24,下限 1(防数据抖动导致反复切)

_comment 是给单位说明用的自留字段(JSON 不支持注释)——服务端会原样保留它,页面上点开关触发的回写不会把它抹掉,你可以往里加自己的备注。所有 _ 开头的字段都享有这个待遇。

手改后不用重启服务——调度器每秒检查一次文件,变了就重载(页面上「每 X 小时」与「N 天内到期」的文字也跟着变,以服务端配置为准)。填错的值会回退默认(周期下限 1 分钟、抖动上限 ±50%);整份 JSON 写坏时沿用上一份配置,不会把开关静默关掉。失败不会立刻重试——等完整周期后再试,不会刷爆官方接口。各任务的实际触发间隔在标称周期上叠加随机抖动(默认 ±10%,如 30 分钟任务实际 27~33 分钟),避免固定间隔的规律性请求。

这些自动化只是顺手:凭证发现、接口封装、切换能力本来就在项目里,加个调度器而已。项目的主定位仍然是看板——看清楚积分花在哪、值不值。

多端云同步(可选,需要本地服务)

多台电脑各用一份 buddyToken 时,数据本来是互相看不见的。开启后每台机器把自己的数据推到云端一个「传阅箱」,再把别人推的拉回来合进本地,看板于是看到多端汇总:

  • 筛选栏多一组**「机器」**,能按机器筛选,也能一眼看出每台贡献了多少
  • 后台静默自动同步(默认 30 分钟一轮),看板上不增加任何按钮
  • 数据只增不减:合并只做「缺失才加 / 更优才覆盖」,任何一步都不会让历史变少
  • 「🗑 一键清空数据」只清本地,云端保留——避免一台机器误删掉别的机器贡献的数据

云端用 Supabase(免费额度足够),建表 SQL 就在仓库根目录的 supabase-cloud-sync.sql。看板不直连云端:密钥只存在本机 sync-config.json(已 gitignore),同步全在 Node 侧完成。详细步骤与注意事项见多端云同步一节。

429 限流台账

扫描本机日志还原每个账号、每个模型的限流记录,给出官方恢复时刻与倒计时;按 sessionId → 模型 归因,避免模型错归。

筛选

日期范围(含今日 / 近 7 / 14 / 30 / 60 / 180 日 / 近 1 年 / 全部快捷键,打开看板默认落在最近 60 天)、模型、客户端、账号四维筛选,可叠加;开启云同步后还有第五维「机器」。模型超过 8 个时自动折叠,已选中的始终可见。

日期范围不随快照保存——每次打开都回到默认窗口,模型与客户端筛选则沿用上次的选择。

KPI

  • Token 总量 / 缓存命中率
  • 每积分 Token、每 M Token 积分(把积分和 Token 换算起来)
  • 免费模型 Token(0 积分但确实消耗了 Token 的记录)
  • 积分覆盖(Token 数据覆盖了多少比例的实际花销)
  • 单次响应耗时(每次 API 请求,带 P50 / P90)
  • 模型耗时占比(用户在回合里等待的时间,有多少花在模型上)

图表

  • 每日趋势三轴对比(柱=积分,线=Token / 请求数)
  • Token 趋势(逐日堆叠:缓存读 + 未命中输入 + 输出,合计=总 Token)
  • 模型 × 时段热力图、各模型每日堆叠
  • 各模型响应耗时(横向条形,P50 与 P90 对照,差距即长尾程度)

表格

  • Token 消耗 Top 10 回合(按 Token 排序,带步骤数与回合时长)
  • 工具 Token 消耗排行
  • 模型 / 客户端 / 每日明细,均带 Token 与「Token/积分」列
  • 模型数据表额外带「响应 P50」「首字延迟」「输出速度」
  • Session 消耗:按会话聚合,带会话标题

报告与导出

  • 自动生成分析报告与优化建议
  • 导出 Markdown / CSV(CSV 带 BOM,Excel 打开不乱码),可打印或另存为 PDF

快速开始

环境要求

  • Node.js 18+(脚本用到 util.parseArgs)
  • Windows(路径与 bat 脚本按 %LOCALAPPDATA% / %USERPROFILE% 处理)
  • 看板需要联网加载 CDN 的 xlsx 和 chart.js

日常用法:两步

  1. 双击 打开看板.bat —— 启动本地服务并自动打开看板(保持那个命令行窗口开着)
  2. 看板顶部点 「⚡ 一键同步」 —— 拉官方账单与账号积分 + 重扫本地会话,完成后自动重载

首次同步会拉取最近 1 年的账单(约 13 个 30 天窗口,并发拉取约 8 秒),之后每次只拉最近 7 天增量(几秒)。要补更早的账单,用 --official-days 1095 跑一次即可。

另外两种情形:

想干什么 怎么做
不常驻服务,只同步一次 双击 同步Token.bat:一次性全量同步,跑完直接打开看板
只看已有数据、不更新 直接双击 workbuddy-token.html,零进程

关于打开方式

看板固定用 file:// 打开:双击 workbuddy-token.html,或用 打开看板.bat(它会顺带起本地服务,并打开同一个地址)。

为什么要统一:浏览器的本地存储(IndexedDB / localStorage)按地址隔离, file:// 与 http://127.0.0.1:8099 是两个互不相通的仓库。混着用的话, 「📂 历史」里的快照会分裂成互相看不见的两份,换个入口就"看不到历史"了。 所以服务只当后台接口用(页面会自动探测它的端口),不再由它托管页面。

顶部按钮

按钮 作用 需要服务
⚡ 一键同步 积分 + Token 一次跑完(串行:先积分后 Token,失败即停) 积分部分需要
⋯ 更多 ▸ 💰 同步积分 只拉官方账单与各账号积分余额 是
⋯ 更多 ▸ 🔄 同步 Token 只重扫本地会话记录,约 2 秒 否
⋯ 更多 ▸ 📁 连接本地会话 离线时授权会话目录(服务模式下只作状态提示) 否
⋯ 更多 ▸ 🗑 一键清空数据 删掉项目里的数据文件 + 浏览器本地快照,回到初始状态(连续确认两次) 删文件需要
⋯ 更多 ▸ 其余 追加 Excel 数据、手动快照、历史、打印 / PDF、导出 Markdown / CSV —

「一键清空数据」清的是看板展示的数据,不是你的原始记录:switch-backups/ 账号备份、手工导入的 Excel 账单,以及 ~/.workbuddy / ~/.codebuddy 下的会话 jsonl 都不动。清完重新同步即可再生成。没有本地服务时只能清浏览器这一半,会明确提示。

「同步积分」与「同步 Token」互不覆盖:只同步积分时,本地 Token 数据原样保留;只同步 Token 时,已有的账单与账号余额也不会被抹掉。各自只更新自己那一半。

要用同步按钮,先让服务在跑:双击 打开看板.bat,保持那个命令行窗口开着。关掉即停止服务——看板照常能看,只是同步按钮会提示「本地服务未启动」。

为什么同步必须靠服务:官方接口的跨域预检会被网关返回 401 且不带 CORS 头, 而且浏览器禁止 JS 设置 Origin / Referer,伪装不了官方 Web 端。所以拉数据必须由 Node 侧代劳。

手工导入的通道仍然保留:把积分账单 Excel 拖进去同样有效,两种来源按 RequestID 合并,同名记录以官方(更新)为准。


核心设计:积分与 Token 怎么对齐

关键点:账单里的 RequestID 对应会话记录里的 providerData.conversationRequestId,不是 messageId。

这是「回合级」ID。一次用户提问 = 一个回合,但 agent 会在其中串行发起多次 API 请求(实测平均 7~8 次,最多上百次)。所以:

一行积分  ==  一个回合  ==  该回合全部 API 请求的 Token 之和

按 messageId 去匹配是行不通的——那个粒度细 7 倍多,绝大多数都对不上。

Token 口径(两条铁律)

total_tokens = input_tokens + output_tokens          恒成立
cache_read_input_tokens <= input_tokens              恒成立

cache_read_input_tokens 是 input_tokens 的子集(命中缓存的那部分),不是额外增量,两者不可相加。正确算法:

  • 总输入 = input_tokens
  • 未命中缓存 = input_tokens - cache_read_input_tokens
  • 缓存命中率 = cache_read_input_tokens / input_tokens

另外,因为每次请求都要重发全量上下文,同一回合内 input_tokens 会逐次累加——这是 API 的真实计费方式,不是重复计数。


模型响应时间怎么来的

会话记录里没有现成的耗时字段(responseTimeMs 是搜索工具自己的耗时,turn-metrics.durationMs 是整个回合的时长)。但记录是按 agent 循环顺序写入的,可以据此推算:

用户提交 ──(等待起点)──> function_call  @ts   ← 模型响应完成
                            └─ 工具执行 ──> function_call_result @ts
                                             └─(等待起点)──> function_call @ts

function_call 的时间戳是模型响应完成时刻,function_call_result 与用户消息是新的等待起点。两者相减,得到的就是纯模型侧耗时——工具执行时间被干净地排除在外。

两个口径,别混

口径 含义 看哪里
单次响应耗时 一次 API 请求的端到端耗时,已排除工具执行,含网络往返与排队 模型表的「响应 P50」、响应耗时图表
回合级等待 用户从提交到拿到最终答复的完整时间,其中模型占比由「模型耗时占比」KPI 给出 明细表的「时长」列

「单次响应 7 秒」不等于「提问 7 秒就答完」。 一次提问(一个回合)平均会触发 7 次以上的 API 请求,中间还夹着工具执行,所以用户实际等待要长得多。两个指标都给了,别拿错。

首字延迟与输出速度

输出 token 是耗时的主要驱动因素,但直接算「平均耗时 ÷ 平均输出」会把首字延迟也摊进去。所以看板对每个模型做一次分箱回归:

耗时 ≈ 首字延迟 + 输出token ÷ 输出速度

按输出 token 排序分箱、每箱取中位数后再做最小二乘(单次请求波动大,直接对原始点拟合会被离群点带偏)。样本不足 60 条时留空,显示 —。

算这个指标的已知局限

  • 时间戳是记录写入时刻,所以耗时含网络往返与服务端排队,换网络环境数值会变。这既是缺点也是优点——它反映的是真实体验。
  • 一个回合内如果换了模型,请求会归到该回合消耗最大的那个模型名下。实测这种混合回合只占千分之几,误差可忽略。
  • 单次耗时 <0.5 秒或 >1 小时视为异常(记录不完整或会话中断残留)已剔除,所以样本数会略少于总请求数。
  • --light 模式不产出请求级耗时(明细随步骤一起被省略),此时响应时间列显示 —,但回合级的 lat 合计仍然保留。
  • 模型耗时占比的分布很宽:纯读文件的回合接近 100%,跑长命令或等外部接口的回合可以低到 30% 以下。看中位数,别当成恒定值。

自动同步官方账单

脚本会自己找到凭证并拉数据,全程不需要你导出任何文件。

账号从哪来

WorkBuddy 桌面端把每次登录的凭证写成快照文件,放在:

%LOCALAPPDATA%\CodeBuddyExtension\Data\Public\auth\*.info

这些快照不会被删除,所以你在这台机器上登录过的每个账号都在里面。脚本按 uid 去重、每个账号取最新的一份,就得到了全部账号——不需要 OAuth 扫码,也不需要手动录 token。

拉什么数据

数据 接口 用途
积分账单 /billing/meter/get-user-request-usage 替代手动导出的 Excel
积分余额 /billing/meter/get-user-resource-summary 账号还剩多少积分
套餐明细 …-paid-packages / …-free-packages 每个包的剩余量与到期时间
签到 /v2/billing/meter/checkin-activity-status、/daily-checkin 签到日历与签到动作
猫猫旅行 /activity/growth/buddy/travel/* 旅行状态与领奖

账单里的 requestId 就是会话记录里的 providerData.conversationRequestId(回合级 ID),所以两端天然对得上,不需要额外映射。

三个必须绕开的服务端行为

  1. 查询窗口超过约 31 天会直接返回空——脚本按 30 天自动切分时间段。
  2. 单次最多返回 3000 条,且是从窗口起点往新取——天真地拉一次会静默丢掉最新数据。 脚本按「取回本页最新时间 → 作为下一轮起点」推进,直到不再增长。
  3. 跨日记录的积分会「尾部结算」——同一条记录在当天窗口里取值偏小,窗口延伸到次日才是饱和值。 所以「今日消耗」在当天可能略偏小,之后同步会自动补正;「昨日消耗」已完成结算,是稳定值。

官方同步全程可失败:账号找不到、token 失效、接口变更都只打印警告并继续,本地扫描与看板产出不受影响。失败时看板仍可用手工导入的 Excel。

窗口是并发拉的:切分出来的十几个窗口互不依赖,脚本在同一账号内按 4 路并发拉取(账号之间仍串行,在飞的请求数始终不超过 4)。首次同步拉一年历史从约 18 秒降到约 8 秒。某个窗口失败只跳过它并记进错误列表,不会中断该账号的其余窗口。


多端云同步(可选)

多台电脑各用一份 buddyToken 时,数据天然互相看不见。开启云同步后,每台机器把自己的数据推到云端一个「传阅箱」,再把别人推的拉回来合进本地——看板于是看到多端汇总,并且可以按机器筛选(筛选栏多一组「机器」chip,会话表多一列机器)。

云端不是新的数据源,只是机器之间的中转站:本地 token-usage-data.js 仍是唯一数据源,看板读的还是那一个文件、位置和加载方式都不变。

看板不直连云端:file:// 页面里放密钥等于公开,所以 Supabase 的 anonKey 只存在本机 sync-config.json(已 gitignore),同步全在 Node 侧完成。

第一台机器

  1. 到 supabase.com 注册并建一个项目(建议用独立项目,不要和别的业务共用)。

  2. 进项目的 SQL Editor,把仓库根目录的 supabase-cloud-sync.sql 整段粘贴并执行(可重复执行,不会丢数据)。 执行完在 Table Editor 里应该能看到 6 张 bt_ 开头的表——看不到就是没执行成功,别急着往下走。

  3. 在 Project Settings → API 里复制 Project URL 与 anon public key。

  4. 启动一次服务(双击 打开看板.bat)。项目目录里会自动出现 sync-config.json 模板——不用自己新建。

  5. 打开它,填上 url、anonKey、machineName,把 enabled 改成 true,保存:

    {
      "enabled": true,
      "url": "https://xxxx.supabase.co",
      "anonKey": "eyJhbGci...",
      "proxy": "http://127.0.0.1:10235",
      "machineName": "台式机"
    }

其余机器

把项目拷过去(或 git clone)之后,只需要:

  1. 删掉 sync-state.json(那是上一台机器的身份文件)。不删也行——程序发现主机名对不上会自动换新身份并打提示。
  2. 启动服务,同样会自动生成 sync-config.json 模板。
  3. 填上 url 与 anonKey(和第一台相同)、machineName(取个不同的名字)、proxy(这台机器自己的,拿不准就先留空),enabled 改成 true。

不需要再执行一遍 SQL——建表是一次性的,所有机器连的是同一个项目。

配置改完保存即可,服务每 30 秒重读一次配置,不必重启。之后按 intervalMinutes(默认 30 分钟)后台自动同步;也可以手动 node cloud-sync.js 跑一轮、--dry-run 只看将要传输的行数。

新机器第一轮同步会把云端已有的数据全部拉回来,所以打开看板立刻就能看到其它机器的记录。

几点说明

  • 只增不减:合并只做「缺失才加 / 更优才覆盖」,任何一步都不会让历史数据变少。
  • 上传内容原样、不脱敏:本地回合的 token 统计与每步明细、会话标题、项目路径、会话 UUID、账号 uid/昵称、余额快照都会传上去。介意就不要开。
  • 「🗑 一键清空数据」只清本地,云端数据保留——下次同步会把云端数据拉回来,这是有意为之(避免一台机器误删掉其他机器贡献的数据)。
  • 网络(最容易卡住的一步):*.supabase.co 走的是 Cloudflare,国内直连常在 TLS 阶段被重置(表现为 ECONNRESET / fetch failed)——而 api.supabase.com 是通的,容易让人误判成 key 或项目有问题。 更迷惑的是:本机的系统代理只有浏览器类应用会走,Node 不会。所以要在 proxy 字段里显式填代理地址(如 http://127.0.0.1:10235,具体端口看你的代理软件),cloud-sync 会带着代理环境变量重跑自己一次。 另外 Node 的 NODE_USE_ENV_PROXY 只在进程启动时生效,这就是这里非要 re-exec / 起子进程、而不能在运行时设 process.env 的原因。每台机器的代理端口可能不同,各自填各自的。填了仍不通的话,把 url 换成自建反代地址也一样可行。
  • 同步失败只打警告,本地数据产出与看板一切照常;失败后会等一个完整周期再重试,不会反复刷日志。
  • 想关掉:把 enabled 改成 false,或直接删掉 sync-config.json,项目行为与纯本地完全一致。已经传到云端的行要去 Supabase 后台自己删。

数据来源与隐私

脚本扫描本地会话记录(只读;原始 jsonl 始终不外传——即使开了上一节的云同步,传的也只是统计产物,不含 jsonl 正文与凭证):

客户端 目录
WorkBuddy 桌面端 %USERPROFILE%\.workbuddy\projects\<工作区>\*.jsonl
CodeBuddy CLI %USERPROFILE%\.codebuddy\projects\<项目>\*.jsonl

每个 .jsonl 是一个会话,一条带 message.usage 的记录 = 一次 API 请求。

积分账单与账号余额来自官方接口,凭证读自本机登录态:

数据 来源
登录凭证 %LOCALAPPDATA%\CodeBuddyExtension\Data\Public\auth\*.info
积分账单 / 余额 https://www.workbuddy.cn/billing/meter/*
签到 / 猫猫旅行 https://www.codebuddy.cn/v2/billing/meter/*、/activity/growth/buddy/travel/*
账号归属 ~/.workbuddy/workbuddy.db 的 sessions.user_id(只读)
429 台账 ~/.workbuddy/logs、~/.codebuddy/logs(只读,最近 2 天)

注意账单在 workbuddy.cn、积分活动在 codebuddy.cn,两个域不能混用。

提问正文不会被采集:账单接口的响应里其实带了 input(你提问的原文),脚本按白名单只提取 requestId / credit / model / client / requestTime 五个字段,正文在解析时即被丢弃,不会进入内存结构,更不会写盘。

账号凭证不会写盘:accessToken / refreshToken 只在内存中使用,用完即弃。

为什么数据文件不入库

token-usage-data.js 里包含真实的会话 UUID、回合 ID 和完整的本机项目路径(例如 C:\Users\<你的用户名>\...),启用官方同步后还包括账号 uid 与昵称(昵称可能是手机号)。这些都属于个人使用痕迹,仓库里的 .gitignore 已默认排除它们。

所以你 clone 下来后打开看板会看到空看板——这是正常的,先同步一次就有数据了:双击 同步Token.bat,或双击 打开看板.bat 后点看板顶部的「⚡ 一键同步」。


命令行参数

日常用不到这一节。 看板上的按钮就是这些命令的封装:「⚡ 一键同步」= --emit-js、「💰 同步积分」= --only=credits、「🔄 同步 Token」= --only=tokens、账号卡片上的「🔄 刷新」= --only=credits --uid。 只有本地服务起不来要排障、或要写自动化 / 定时任务时,才需要直接敲命令。

# 常用
node token-usage-report.js --emit-js                    # 本机全量扫描 + 同步官方账单(官方窗口默认 7 天)
node token-usage-report.js --emit-js --official-days 30 # 官方数据取最近 30 天(拉取更慢)
node token-usage-report.js --emit-js --light            # 省略每步明细,产物约小一半
node token-usage-report.js --emit-js --no-merge         # 关闭增量合并,纯全量覆盖
node token-usage-report.js --emit-js --no-official      # 完全跳过官方同步(纯离线)

# 只刷新一半
node token-usage-report.js --emit-js --only=credits     # 只刷官方账单与积分余额
node token-usage-report.js --emit-js --only=tokens      # 只扫本地会话

# 只刷一个账号(看板卡片上的「🔄 刷新」就是这条)
node token-usage-report.js --emit-js --only=credits --uid <账号uid>

# 时间与输出
node token-usage-report.js --days 7                     # 只统计最近 7 天
node token-usage-report.js --since 2026-09-01           # 起始日期
node token-usage-report.js -o D:\out                    # 指定输出目录

# 清空所有数据(不可撤销,看板上的「🗑 一键清空数据」就是这条 + 清浏览器快照)
node token-usage-report.js --reset

# 云同步(多端汇总,需先在 sync-config.json 里配好 Supabase)
node cloud-sync.js              # 同步一轮:先推本机增量,再拉别人的增量合并进本地
node cloud-sync.js --dry-run    # 只统计将要推 / 拉的行数,不发写请求、不落盘
node cloud-sync.js --push       # 只推不拉(--pull 则只拉不推)

--only=credits 是部分更新:它读取已有数据文件,只替换其中的官方字段,本地 Token 部分原样保留(也不会重写 CSV 与汇总报告)。所以第一次必须先跑一次全量,之后才能只刷积分。

再加 --uid <账号uid> 就是只刷该账号:账单按 RequestID 合并、余额按 uid 覆盖,其余账号的账单与余额原样保留。uid 写在账号卡片名字后的标签里(平时只显示前 8 位,鼠标悬停可见完整值);它只能与 --only=credits 一起用。

--reset 是清空而不是重新生成:删掉 token-usage-data.js、明细 CSV、汇总 Markdown 与余额 / 签到历史,回到「从未同步过」的状态。它只按固定文件名白名单删除,不做通配符匹配——账号备份 switch-backups/、云同步配置 sync-config.json 与游标 sync-state.json、手工导入的 Excel 以及 ~/.workbuddy、~/.codebuddy 下的原始 jsonl 都不受影响。浏览器里的本地快照存在页面侧,只能用看板菜单里的「🗑 一键清空数据」一并清掉。

云同步是旁挂的一层,不在 token-usage-report.js 里:它只读写脚本已生成好的数据文件,因此 --reset、--only=* 这些命令的行为都不受它影响。反过来说,--reset 清空本地后,下一次云同步会把云端数据拉回来——这是有意为之,避免一台机器误删掉其他机器贡献的数据。

本地服务端点

server.js 只提供 API(不托管页面),同一时间只允许一次同步(并发返回 409):

端点 说明
POST /api/sync[/credits|/tokens] 跑同步脚本;不带后缀为全量。/credits 可带 body {"uid":"…"},只刷该账号
POST /api/checkin、POST /api/travel 签到、派猫猫(可带 {"uid":"…"} 只处理一个账号)
GET /api/checkin-status 签到 + 旅行状态(只读)
GET /api/limits 429 限流台账(只读)
POST /api/switch 切换账号,body 为 {"uid":"…","app":"workbuddy-desktop"|"codebuddy-cli","restart":true}
GET/POST /api/auto 自动任务配置:GET 返回完整配置(四个开关 + 周期 + 抖动)与各任务的上次尝试/成功/失败时间及结果摘要;POST 合并开关(如 {"checkin":true}),刚打开的开关会立即执行一次(周期与抖动只从 auto-config.json 读,不接受远程改)
POST /api/reset 清空项目里的数据文件(看板「🗑 一键清空数据」走这条)。与同步共用同一把锁,并发返回 409
GET /api/status 同步状态、扫描目录、可切换账号;另含 cloud 字段(云同步是否启用、上次结果、本机标识、周期),排障用

访问令牌(可选)

服务默认监听 0.0.0.0,同网段任何设备都能调写接口(同步 / 签到 / 切换账号 / 清空),浏览器里的任意网页也能跨域 POST 过来。要给写接口加一道门:

node server.js --auth-token 一串随机字符
# 或:set BUDDYTOKEN_TOKEN=一串随机字符 && node server.js
# 或在项目根目录放 auth-token.txt(一行,适合写进 .bat)

设了令牌后,所有 POST /api/* 必须带 X-Auth-Token 头,GET 只读接口不拦(看板首屏要探测 /api/status)。看板里在「⋯ 更多 → 🔑 访问令牌」填同一个值即可——令牌只存在当前浏览器的 localStorage,不写盘、不随快照导出、不出现在 URL 里。不设令牌则维持原行为,升级不会打断现有用法。

端口也可以用 --port 8100 固定(默认从 8099 起自动往后找空位)。注意 Windows 允许 0.0.0.0:8099 与 127.0.0.1:8099 同时监听,起第二个实例时若没注意会出现「请求打到哪个进程全看运气」,固定端口并让冲突直接报错更省事。


增量合并

token-usage-data.js 的两半都是增量追加写入的,只增不减:

本地 Token 部分:生成前读取已有的数据文件,把历次同步过、但本次本地已扫不到的回合原样保留,这样即使本地会话记录被清理,历史 Token 数据也不会凭空消失。同名回合以本次扫描为准(正在进行的会话会在后续同步里被补齐)。

官方积分部分:账单按 RequestID 去重合并,本次拉到的覆盖同 ID 的旧行,窗口外的历史行原样保留。账号下标表 uids 会一并重排(本次账号在前、历史账号追加在后),否则历史行的账号归属会指错人。余额 acct 是当前快照而非累加量,直接取本次结果。

多端云同步部分(可选):规则同样是只增不减——本地已有的回合一律保留本地(本机扫的是原始 jsonl,比云端副本权威),本地没有的才收下;账单积分取两者较大值、机器归属只补空;账号余额取官方 updatedAt 更新的一条,但 login(本机登录态)永远保留本机值。远端账单行的账号是 uid 文本,合并时会翻译成本机的 uids 下标(本机没有就追加),沿用远端下标会让历史行归属整体错位。另外余额历史合并后要回写 credit-history.json,否则下一轮刷积分会把刚拉回来的远端历史抹掉。

为什么这里必须合并:官方账单接口只能按窗口拉(默认近 7 天)。早先的实现是整体替换 data.bill,于是每同步一次就把窗口外的历史抹掉一次——同步过近 7 天后,更早的账单就永久没了。

想丢弃历史重来,加 --no-merge(对本地回合与官方账单同时生效);想连数据文件一起清掉、回到初始状态,用 --reset。

余额快照

credit-history.json 记录每次同步时各账号的余额(只在余额变化时追加,同值不重复记),看板据此显示「今日 / 昨日消耗」里的余额对照。

为什么不用它反推「每日获取」:remaining = total − used,而套餐的 used 会周期重置——重置时余额账面回升,看起来就像「获取」(实测出现过 7 分钟内"涨"201 分的情况)。官方又没有「获取」明细接口,无从把重置拆出来。所以看板不展示「每日获取」这个指标。


相关项目

WorkBuddy / CodeBuddy 生态里的一些互补工具(与本项目不重叠,一并列出方便你按需取用):

项目 干什么
changexbc/workbuddy-switch 账号切换桌面 App,带积分到期监控与自动签到
Sliverkiss/workbuddy2api 把订阅转成 OpenAI 兼容 API,支持多账号轮转
jlcodes99/cockpit-tools 通用 AI IDE 账号与配额管理

它们帮你多拿积分,本项目帮你看清积分花在哪。


文件说明

文件 说明
workbuddy-token.html 单文件看板,双击即用
打开看板.bat 推荐入口:起本地服务并打开看板(file://),之后点「⚡ 一键同步」即可拉最新数据
server.js 本地 HTTP 服务:只提供 API,不托管页面
token-usage-report.js 零依赖 Node 脚本,扫描会话记录并生成数据
workbuddy-api.js 官方接口封装:账号发现与切换、账单拉取、积分余额、签到、猫猫旅行、账号归属
rate-limits.js 429 限流台账:扫本机日志还原恢复时刻,按 sessionId → 模型 归因
cloud-sync.js 多端云同步(可选):把本机数据推给云端、把其它机器的拉回来合并;也能当命令行单独跑
supabase-cloud-sync.sql 云同步的建表 SQL,在 Supabase 的 SQL Editor 里整段执行一次
同步Token.bat 一键全量同步:扫本地会话 + 拉官方账单与积分,生成数据后打开看板(不必先起服务)

以下为脚本产物,不入库(.gitignore 已排除):

文件 内容
token-usage-data.js 看板数据(含真实会话 UUID、账号 uid)
token-usage-detail.csv 逐条请求明细
token-usage-summary.md 汇总报告
checkin-history.json 签到日期本地累积(签到日历用)
credit-history.json 余额观察点(仅供余额对照)
auto-config.json 自动任务配置:四个开关 + 各任务周期(分钟)+ 抖动幅度,手改无需重启服务
sync-config.json 云同步配置:开关、Supabase 地址与 anonKey、机器名、周期(不在清空白名单内)
sync-state.json 云同步游标与本机 machineId(不在清空白名单内)
switch-backups/ 账号切换前的凭证备份(含 token)

「🗑 一键清空数据」/ --reset 删的是上表中除 switch-backups/、sync-config.json、sync-state.json 以外的产物:账号凭证备份与云同步配置都留着(前者删了没法回滚切换,后者是配置不是数据产物),原始会话记录也不碰。

产物落在哪:以上文件一律写在项目目录下,脚本不使用系统临时目录(全文没有 os.tmpdir(),也没有别的临时文件写入)。 唯一一处跨目录写入是「切换账号」——它会覆盖 %LOCALAPPDATA%\CodeBuddyExtension\Data\Public\auth\ 下对应应用的登录文件, 覆盖前先把原文件备份进 switch-backups/。另外看板的「本地快照」存在浏览器自己的 IndexedDB 里,跟随浏览器 profile(在系统盘),删浏览器数据即消失。


常见问题

打开看板是空的? 数据文件不入库,clone 下来自然是空的。先同步一次生成数据:双击 同步Token.bat,或双击 打开看板.bat 后点顶部「⚡ 一键同步」。

点「⚡ 一键同步」提示需要本地服务? 积分部分要调官方接口,而浏览器的跨域预检过不了网关,必须由 Node 侧代劳。用 打开看板.bat 启动服务,并保持命令行窗口开着。离线状态下「一键同步」会退化成只重扫 Token。

为什么积分覆盖不是 100%? 积分账单是账号级计费口径,覆盖所有设备;本地 jsonl 只是本机留痕。同一账号在多台设备或不同入口使用时,账单里会有一部分记录在本机找不到对应会话。这是数据可得性的正常现象,不是错误。

「今日消耗」为什么和官网对不上? 当天数值会略偏小——官方对跨日记录做尾部结算,同一条记录在当天窗口里取值偏小、窗口延伸到次日才饱和。下一次同步会自动补正。「昨日消耗」已完成结算,是稳定值。

「预计可用天数」是怎么算的?准不准? 按最近三周(D-21 ~ D-1,不含今天——今天还在结算,计进去会把日均压低)的账单消耗算日均,再用余额除。两个口径要留意:样本不满三周就按实际天数当分母(新启用的账号只有几天数据,拿 21 天当分母会严重低估日均);积分到期即清零,所以结果再与「最近到期」取小——按节奏算能撑 41 天但 22 天后就过期时,显示的是「到期前用不完」而不是 41 天。

消耗节奏会随账号切换而突变(各账号并非稳定并行使用),所以这是量级估算,别当精确预测。近期几乎没有消耗、或账单样本不足 3 天时显示「样本不足」,不硬算。

为什么「历史」里的快照找不到了? 大概率是打开方式混用了。浏览器存储按地址隔离,file:// 和 http://127.0.0.1:8099 是两套互不相通的仓库。统一用 file:// 打开(两个 bat 现在都这么做)。

缓存命中了是不是就不花钱? 不是。cache_read_input_tokens 是 input_tokens 的子集,命中缓存的 token 计价更低但通常不为零。看板的「缓存命中率」和「每积分 Token」要一起看。

单次响应 7 秒,为什么实际等了一分钟? 那是两个口径。7 秒是一次 API 请求的模型侧耗时;一次提问(一个回合)平均触发 7 次以上请求,还夹着工具执行。回合级等待看明细表的「时长」列。

自动切换账号会不会把我的账号切来切去? 不会乱切。只在有账号积分将在 14 天内到期(默认阈值,switchHorizonDays 可配)且还没用完时才动作,把桌面端与 CLI 切到那个「再不用就浪费」的账号;目标账号已经是当前登录账号、或没有紧急到期的积分时,什么都不做。每次切换前都会备份登录态到 switch-backups/。

会把我本地的代码或提问上传到哪儿吗? 不会。脚本纯本地读写文件,只有拉取官方账单/余额/签到时会访问官方域名;账单响应里的提问正文在解析时即被丢弃。凭证只在内存使用,不写盘。

README 里为什么没有「每日获取多少积分」? 官方没有「获取」明细接口,而套餐额度的周期重置会污染余额差值(见余额快照),所以这个指标做不到可信,看板不提供。


实现说明

看板怎么加载数据

浏览器在 file:// 协议下无法用 fetch 读取同目录文件(会被 CORS 拦),但 <script> 标签可以跨同目录加载。所以脚本产出的是 window.__TOKEN_DATA__ = {...} 形式的 JS 文件,看板用动态注入 <script> 的方式加载,并带时间戳绕开缓存。

这样双击 HTML 就能用,不需要起本地服务器。

零依赖

token-usage-report.js、server.js、workbuddy-api.js 只用 Node 标准库,npm install 是不需要的。看板里的 xlsx(Excel 导入)和 chart.js(图表)从 CDN 加载。

目录布局

buddyToken/
├── workbuddy-token.html      # 看板(单文件,UI/逻辑/样式全内联)
├── server.js                 # 本地服务:API only
├── token-usage-report.js     # 数据生成主脚本
├── workbuddy-api.js          # 官方接口封装
├── rate-limits.js            # 429 台账
├── 打开看板.bat / 同步Token.bat
└── (产物,不入库)token-usage-data.js、*.csv、*.md、*-history.json

License

MIT © 2026 CaoDonald

About

[fork] WorkBuddy / CodeBuddy 的积分账单 × 本地 Token 消耗关联看板:看清每积分换多少 Token、缓存有没有省钱。纯本地、零依赖;本 fork 增加 0.0.0.0 网站化部署(局域网直接看,静态白名单防凭证泄露)与桌面端凭证不可用时回退 IDE 登录态。

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages