8000 feat(collector): collect local Claude Code usage by InfinityPacer · Pull Request #2 · InfinityPacer/meterleaf · GitHub
[go: up one dir, main page]

Skip to content

feat(collector): collect local Claude Code usage - #2

Merged
InfinityPacer merged 7 commits into
mainfrom
feat/claude-code-collector
Sep 24, 2026
Merged

InfinityPacer merged 7 commits into
mainfrom
feat/claude-code-collector

Conversation

@InfinityPacer
@InfinityPacer InfinityPacer commented Sep 24, 2026 •
Copy link
Copy Markdown
Owner

问题

本地直连的 Claude Code 不经过 Sub2API,它的用量只保存在使用它的电脑上,Meterleaf 无法统计。Claude Code 自带的 /stats 把同一次请求拆分保存的多行逐行累加,显示的 Token 总数约为实际的两倍,不能直接作为账本依据。

变化

  • 新增本机采集器 src/collector,打包为带品牌图标的 Meterleaf.app,由 launchd 每分钟以低优先级运行一次同步。
    • 后台任务通过 SMAppService 随应用注册,「登录项与扩展」显示 Meterleaf.app 与图标。launchd 启动包内的小型 Swift 助手,再由它 exec 采集器。临时签名下系统会钉住被启动程序的签名,这样采集器升级后登记仍然有效。使用自定义路径时回退为普通 LaunchAgent。
    • 只读 ~/.claude/projects 会话记录与 ~/.claude.json,不写入 Claude Code 目录,不读取 OAuth 令牌、钥匙串或凭据,不调用 Anthropic 接口。
    • 按 requestId 与 message.id 合并拆分保存的行,每次请求只计一次;同组取四桶总量最大的一行。
    • 账户、套餐(如 Max 5x)与额度来自 ~/.claude.json;可选读取状态栏脚本写出的额度缓存,获得更及时的额度。
    • 会话行不带账户,按运行时观察到的登录区间归属,无法判断时记为「未归属」;bind-history 可明确声明首次运行前的历史归属。
    • server <url> 只更换服务地址,写入密钥与来源 ID 不变,便于从局域网地址切换到公网 HTTPS 地址。
    • 本地 outbox 至少一次投递,服务端按 (sourceId, externalId) 幂等写入,断网恢复后补发不重复。
  • 新增只写推送接口 POST /api/ingest/v1/batches。每台电脑一个写入密钥,METERLEAF_INGEST_KEYS 只保存 SHA-256 摘要并绑定来源;未配置时不注册路由。部署指南说明如何只在反向代理上公开这一个地址。
  • 价格表更名为 prices/meterleaf-2026-09-25.json,加入 Claude Fable 5.1、Opus 5.5、Opus 5、Sonnet 5、Haiku 4.5(含官方别名)的官方价格。新增可选 cacheWrite1h 费率,1 小时与 5 分钟缓存写入分开计价,缺少 TTL 拆分时保留未计价;快速模式记为 priority 档。
  • 账户套餐显示 Max 5x。文档新增 本机采集器,更新部署、计价、README 与 CHANGELOG。

验证

  • bun run typecheck、bun test tests(294 pass)、所改文件 Prettier 与 git diff --check 通过。
  • 真实数据端到端:本地临时 Meterleaf 接收采集器推送,截至 2026-09-23 的四桶 Token 与 ccusage 完全一致(输入 13,394、输出 5,390,294、缓存读取 1,542,502,859、缓存写入 15,101,759)。入库估值按模型与 ccusage 一致,合计 889.10 USD。重复 sync 只推送新事件。
  • 同期按 CC Switch 的单一缓存写入价计算为 841.84 USD,差额来自 1 小时缓存写入按 5 分钟价计。
  • 状态栏额度缓存:服务端额度从 ~/.claude.json 的旧快照更新为状态栏最新采样ï 8000 ¼Œ不再标记过时。
  • 真机后台任务:在 macOS 上安装后,登录项显示 Meterleaf.app 图标,后台同步按分钟成功推送到 NAS 上的 Meterleaf。换入改动过采集器的新构建后重新 install-launchd,系统沿用同一登记继续运行。改用助手之前,同样的升级会被 OS_REASON_CODESIGNING 拒绝。
  • 公网推送:反向代理只为 POST /api/ingest/v1/batches 绕过登录保护后,本机采集器改用公网 HTTPS 地址推送成功。无密钥或错误密钥返回 401,GET 被拒绝,3 MB 请求体未被代理截断,其余页面仍需登录。
  • 只读保证:测试把 Claude 目录设为只读后完整运行 init、sync、bind-history、status,所有文件大小、修改时间与权限不变。

已知限制

  • 本机没有快速模式请求,快速模式价格未经实数据验证。
  • 按模型区分的周额度(weekly_scoped)尚未接入。
  • 没有 Developer ID 签名,登录项中标注「来自身份不明的开发者」。修改 Swift 助手会使已有登记失效,需要换用新的后台任务标签。
  • ~/.claude.json 的额度缓存只在打开用量面板时刷新,未启用状态栏缓存时额度通常过时。

🤖 Generated with Claude Code

https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z

InfinityPacer and others added 7 commits September 25, 2026 01:06
Add a write-only ingest endpoint bound to per-device key digests, store
pushed accounts, usage and quota snapshots idempotently, and price Claude
models with separate 1-hour cache write rates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
Read Claude Code session logs and the cached account and quota state
read-only, dedupe split response lines per request, attribute usage to the
observed login, and push batches to the ingest endpoint from a launchd job
packaged as Meterleaf.app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
Optionally read the rate-limit TSV written by the user's statusline script,
use the file time as the sample time and the login observed at that time as
the account, and skip samples whose account cannot be determined.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
install-launchd now registers a bundled agent with SMAppService when it runs
from Meterleaf.app, so Login Items shows the app name and icon. Custom paths
still fall back to a plain LaunchAgent. sync --log writes the background
output into the data directory with a 1 MiB rotation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
macOS pins the launched program's ad-hoc code signature in the background
item record, so rebuilding the collector made launchd refuse it with
OS_REASON_CODESIGNING. The bundled agent now launches the small, rarely
changing service helper, which execs the collector. The label moves to
io.meterleaf.collector.background because the old one stays pinned to a
previous build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
meterleaf-collector server <url> rewrites only the server in the local
config, so a machine can move from a LAN address to a public HTTPS one
without regenerating its ingest key or touching the server. The deployment
guide shows how to expose only the push endpoint through a reverse proxy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
@InfinityPacer
InfinityPacer merged commit be7c023 into main Sep 24, 2026
2 checks passed
@InfinityPacer
InfinityPacer deleted the feat/claude-code-collector branch October 21, 2026 10:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

0