Skip to main content

Local LLM coding usage collector and Feishu Bitable sync

Project description

llm-usage-horizon

本地优先的 LLM 编码工具用量采集器,支持聚合以下来源并输出到终端、CSV 或飞书多维表格:

  • Claude Code
  • Codex
  • Cursor
  • GitHub Copilot CLI
  • GitHub Copilot VS Code Chat
  • OpenCode
  • 通过 SSH 拉取的远端日志

设计目标:

  • 默认只在本地读取原始日志
  • 上传时只发送聚合后的白名单字段
  • 支持桌面端统一汇总多台服务器数据

功能概览

  • llm-usage collect:采集并汇总本地 + 已选远端数据,输出终端表格和 reports/usage_report.csv
  • llm-usage sync:在 collect 基础上,将聚合结果同步到飞书多维表格
  • llm-usage export-bundle:采集并生成离线 bundle,拷回联网机器后可用 sync --from-bundle 上传
  • llm-usage doctor:检查配置和各采集器可用性
  • llm-usage init:生成 .env.env.examplereports/
  • llm-usage config:打开交互式菜单编辑器,编辑当前运行时 .env

快速开始

python -m venv .venv
source .venv/bin/activate
pip install -e '.[dev]'

llm-usage init
# 用菜单编辑器配置当前运行时 .env,至少补全 HASH_SALT
llm-usage config
# ORG_USERNAME 缺失时,命令行运行会提示输入并自动写回 .env

llm-usage doctor
llm-usage collect --ui auto

如果你要同步到飞书,再补全飞书相关环境变量后执行:

llm-usage sync --ui auto

Node 版本

仓库中的 node/ 目录提供一个 Node.js CLI 实现。

当前 Node 版本特性:

  • 已补齐 initwhoamiimport-configexport-bundlesync --from-bundle
  • 本地采集、聚合、报表输出、飞书同步均可直接由 Node 执行
  • 运行本地命令时不再依赖 Python collector bridge
  • 远端 SSH 采集暂未在 Node 版本中实现;检测到远端配置时会提示并忽略

如果你要验证 Node 版本:

cd node
node --test
node ./bin/llm-usage-node.js init
node ./bin/llm-usage-node.js whoami
node ./bin/llm-usage-node.js import-config --from /path/to/old/repo --dry-run
node ./bin/llm-usage-node.js doctor
node ./bin/llm-usage-node.js collect --ui none
node ./bin/llm-usage-node.js export-bundle
node ./bin/llm-usage-node.js sync --from-bundle /tmp/offline.zip --dry-run

最小配置

.env 中至少建议配置:

ORG_USERNAME=san.zhang
HASH_SALT=change-me
TIMEZONE=Asia/Shanghai
LOOKBACK_DAYS=30

说明:

  • ORG_USERNAME:必填,用于生成稳定的匿名身份哈希
  • HASH_SALT:必填,决定匿名字段的稳定性与不可逆性
  • TIMEZONE:聚合时按该时区落到 date_local
  • LOOKBACK_DAYS:采集窗口,默认 30

如果缺少 ORG_USERNAME,交互终端下运行时会提示输入并写回 .env

远端不建议手工编辑 REMOTE_* 配置。推荐直接运行 llm-usage config,在菜单里编辑当前运行时 .env,包括远端 SSH 主机、用户、端口和路径列表。保存前的修改都停留在草稿里,不会直接写盘。

如果你是从旧版仓库迁移,旧配置通常还在仓库根目录的 .envreports/runtime_state.json。这类配置现在需要一次性迁移到新的运行时路径,推荐在旧仓库根目录执行:

llm-usage import-config --from /path/to/old/repo

可选参数:

  • --dry-run:先预览会复制哪些文件
  • --force:覆盖新位置里已存在的目标文件

如果你就在旧仓库根目录执行,也可以省略 --from。迁移完成后,后续直接使用 llm-usage doctorllm-usage whoamillm-usage collectllm-usage sync 即可。

命令说明

先查看顶层帮助:

llm-usage --help

顶层 help 会列出所有子命令,并附带常用示例:

  • llm-usage doctor
  • llm-usage whoami
  • llm-usage config
  • llm-usage collect --ui auto
  • llm-usage sync --ui cli
  • llm-usage export-bundle --output /tmp/offline.zip
  • llm-usage import-config --from /path/to/legacy/repo

llm-usage init

初始化:

  • .env.example
  • .env
  • reports/
  • 默认写入 LOOKBACK_DAYS=30

llm-usage doctor

检查:

  • ORG_USERNAMEHASH_SALTTIMEZONE
  • 本地采集器是否能找到对应数据源
  • .env 中配置的远端采集器是否可探测

查看帮助:

llm-usage doctor --help

常用参数:

  • --lookback-days N:覆盖 .env 中的 LOOKBACK_DAYS

llm-usage config

推荐的配置编辑入口:

  • 打开当前运行时 .env 的交互式菜单编辑器
  • 支持分组编辑基础配置、飞书配置、Cursor 配置、远端配置和原始环境变量
  • 修改先保存在草稿里,确认 Save 后才写回文件

查看帮助:

llm-usage config --help

llm-usage whoami

输出:

  • 当前 ORG_USERNAME
  • 当前 user_hash
  • source_host_hash(local)
  • 每个已配置远端各自的 source_host_hash(<alias>)

查看帮助:

llm-usage whoami --help

llm-usage collect

行为:

  • 读取本地日志
  • 按需选择远端 SSH 来源
  • 输出终端汇总表
  • 写入 reports/usage_report.csv

说明:终端表格按 日期 + 工具 + 模型 合并展示,不区分单个 session 或来源机器。CSV 仍保留原始聚合结果,不会因为终端显示合并而改变存储内容。

查看帮助:

llm-usage collect --help

常用参数:

  • --lookback-days N:覆盖 .env 中的 LOOKBACK_DAYS
  • --ui auto|tui|cli|none:远端选择界面。auto 自动选最合适的交互方式,tui 强制终端选择器,cli 使用逐项提示,none 跳过远端选择
  • --cursor-login-mode:Cursor 登录模式。默认 auto;Windows Chromium 浏览器下会自动切到 managed-profile;也可显式选择 manual
  • --cursor-login-timeout-sec:Cursor 浏览器登录等待时间,默认 600
  • --cursor-login-browser:指定 Cursor 登录捕获所用浏览器;默认 default
  • --cursor-login-user-data-dirmanaged-profile 模式下的专用浏览器 profile 目录;留空时使用工具默认的受控目录

示例:

llm-usage collect --ui auto
llm-usage collect --ui cli --cursor-login-browser safari

llm-usage sync

collect 相同,但会额外:

  • 自动获取飞书访问令牌
  • 在目标多维表格中按 row_key 执行插入或更新

说明:sync 的终端显示也按 日期 + 工具 + 模型 合并,但上传到飞书的记录内容和粒度保持不变。

查看帮助:

llm-usage sync --help

常用参数:

  • --lookback-days N:覆盖 .env 中的 LOOKBACK_DAYS
  • --ui auto|tui|cli|none:远端选择界面。auto 自动选最合适的交互方式,tui 强制终端选择器,cli 使用逐项提示,none 跳过远端选择
  • --cursor-login-mode:Cursor 登录模式。默认 auto;Windows Chromium 浏览器下会自动切到 managed-profile;也可显式选择 manual
  • --cursor-login-timeout-sec:Cursor 浏览器登录等待时间,默认 600
  • --cursor-login-browser:指定 Cursor 登录捕获所用浏览器;默认 default
  • --cursor-login-user-data-dirmanaged-profile 模式下的专用浏览器 profile 目录;留空时使用工具默认的受控目录

示例:

llm-usage sync --ui auto
llm-usage sync --ui cli --cursor-login-browser chrome
llm-usage sync --from-bundle ~/Downloads/llm-usage-devbox-a.zip --dry-run

离线导入参数:

  • --from-bundle PATH:从离线 bundle 读取聚合结果,跳过本地/远端在线采集
  • --dry-run:只校验 bundle 并输出终端汇总,不上传到飞书

注意:--from-bundle 模式下不应再同时传 --ui--lookback-days 或 Cursor 登录相关参数。

llm-usage export-bundle

行为:

  • 读取本地日志并按当前配置聚合
  • 生成单个 zip bundle,默认写到 reports/llm-usage-bundle-<timestamp>.zip
  • bundle 内固定包含 manifest.jsonrows.jsonl
  • 默认还会附带 usage_report.csv,方便人工检查

查看帮助:

llm-usage export-bundle --help

常用参数:

  • --output PATH:指定输出 zip 路径
  • --lookback-days N:覆盖 .env 中的 LOOKBACK_DAYS
  • --ui auto|tui|cli|none:与 collect 相同,用于远端选择
  • --no-csv:bundle 中不附带 usage_report.csv

示例:

llm-usage export-bundle
llm-usage export-bundle --output ~/llm-usage-devbox-a.zip
llm-usage export-bundle --no-csv

离线 Bundle 工作流

适用场景:

  • 开发机无法联网
  • 只能通过远程桌面进入
  • 但可以把文件从远端拷回本地联网机器

推荐流程:

  1. 在远端开发机执行:
llm-usage export-bundle --output ~/llm-usage-devbox-a.zip
  1. ~/llm-usage-devbox-a.zip 拷回本地联网机器

  2. 在本地先校验:

llm-usage sync --from-bundle ~/Downloads/llm-usage-devbox-a.zip --dry-run
  1. 校验无误后正式上传:
llm-usage sync --from-bundle ~/Downloads/llm-usage-devbox-a.zip

bundle 只包含聚合后的白名单字段,不包含提示词原文、响应原文、本地路径、命令内容或原始主机名。

读取端除了支持默认的 .zip bundle,也兼容已经解压出来、且目录中包含 manifest.jsonrows.jsonl 的 bundle 目录。

输出与隐私

上传到飞书的字段是固定白名单:

  • date_local
  • user_hash
  • source_host_hash
  • tool
  • model
  • input_tokens_sum
  • cache_tokens_sum
  • output_tokens_sum
  • row_key
  • updated_at

不会上传:

  • 提示词 / 响应原文
  • 会话 ID
  • 本地路径
  • 命令内容
  • 原始主机名或 SSH 连接信息

其中:

  • user_hash 基于 ORG_USERNAME + HASH_SALT
  • source_host_hash 基于 ORG_USERNAME + source_label + HASH_SALT

如需查看当前机器和已配置远端对应的哈希值,可直接运行:

llm-usage whoami

这意味着同一台共享服务器上的不同用户不会发生来源冲突。

支持的数据源

本地来源

默认支持:

  • Claude Code
  • Codex
  • Cursor
  • Copilot CLI
  • Copilot VS Code Chat
  • OpenCode

如默认路径不足,可在 .env 中覆盖:

  • CLAUDE_LOG_PATHS
  • CODEX_LOG_PATHS
  • COPILOT_CLI_LOG_PATHS
  • COPILOT_VSCODE_SESSION_PATHS
  • CURSOR_LOG_PATHS

这些值使用逗号分隔的 glob 匹配模式。

OpenCode

OpenCode 采集器从 SQLite 读取 token 使用量:

  • 默认路径:~/.local/share/opencode/opencode.db
  • 可通过 OPENCODE_DB_PATH 覆盖

Cursor 网页仪表盘(可选)

如果本地 Cursor 日志不可用,或当前 lookback 内没有数据,collect / sync 会尝试使用 Cursor 网页端数据。

相关环境变量:

  • CURSOR_WEB_SESSION_TOKEN
  • CURSOR_WEB_WORKOS_ID
  • CURSOR_DASHBOARD_BASE_URL,默认 https://cursor.com
  • CURSOR_DASHBOARD_TEAM_ID,默认 0
  • CURSOR_DASHBOARD_PAGE_SIZE,默认 300
  • CURSOR_DASHBOARD_TIMEOUT_SEC,默认 15

行为说明:

  • CURSOR_WEB_SESSION_TOKEN 已配置,优先使用网页仪表盘接口
  • 若 token 失效,会清空旧 token,并引导重新登录
  • 若 token 为空且本地日志不可用,会尝试打开登录页并刷新 .env 中的网页登录凭证

Windows 下使用 default / chrome / chromium / edge / msedge 时,默认不会扫描系统浏览器默认 profile 的 cookie。 程序会优先使用受控浏览器 profile 登录流程;若失败,再回退到手动粘贴 WorkosCursorSessionToken

Windows 受控 profile 登录流程:

  1. 运行 llm-usage collectllm-usage sync
  2. 若是 Windows Chromium 浏览器,程序会打开一个由工具管理的专用浏览器 profile
  3. 在该窗口中完成 https://cursor.com/dashboard/usage 登录
  4. 程序会轮询该 profile 中的 cookie,并自动写入 .env
  5. 如果自动捕获失败,程序才会回退到手动粘贴 WorkosCursorSessionToken

远端 SSH 采集

远端采集由桌面机发起,通过 SSH 拉取日志后在本地统一聚合。

当前远端支持:

  • Claude Code
  • Codex
  • Copilot CLI
  • Copilot VS Code Chat

不支持远端 Cursor,本项目中的 Cursor 仍以桌面端本地 / 网页数据为主。

推荐使用命令行交互添加远端,而不是手工编辑 .env

llm-usage collect --ui auto

典型流程:

  1. 首次运行时选择 + 新增一个临时远端
  2. 按提示输入 SSH 主机SSH 用户SSH 端口
  3. 程序会先执行 SSH 连通性检查
  4. 检查通过后继续采集
  5. 退出前会询问是否将该远端保存到 .env

如果远端机器在堡垒机 / 跳板机后面,当前也支持将目标机器信息直接嵌入 SSH 登录串,例如:

ssh username@username@host_server_ip@host_jumpserver_ip

这种场景下,命令行交互录入时可按下面填写:

  • SSH 主机username@host_server_ip@host_jumpserver_ip
  • SSH 用户username
  • SSH 端口:按实际堡垒机端口填写

这样程序最终拼接出的 SSH 目标会是:

username@username@host_server_ip@host_jumpserver_ip

只要你的堡垒机环境本身支持这种格式,llm-usage 当前也可以正常连通并采集。

只有在需要批量预置配置、或做非交互部署时,才建议手工维护 REMOTE_*

运行时行为:

  • --ui auto 优先使用轻量 TUI,失败时回落到 CLI
  • --ui none 会禁用所有远端,不会沿用 runtime_state.json 里上次选中的静态远端
  • 上次选择的静态远端会保存到当前运行时数据目录下的 runtime_state.json
  • 可以在运行时临时添加远端
  • 推荐通过运行时交互添加远端,临时远端只有确认后才会追加写入 .env
  • 临时远端默认来源标签为 ssh_user@ssh_host
  • 远端机器只要求有 ssh 和基础 python3 / python

飞书多维表格同步

兼容性说明:

  • 旧版单目标 .env 配置无需修改即可继续使用
  • 不带新的目标选择参数时,sync 仍只上传到默认目标

单目标配置(兼容旧版)

需要的环境变量:

  • FEISHU_APP_TOKEN
  • FEISHU_TABLE_ID,可选;为空时自动选择第一个表
  • FEISHU_APP_ID
  • FEISHU_APP_SECRET
  • FEISHU_BOT_TOKEN,可选;若提供则直接作为 bearer token 使用

示例:

FEISHU_APP_TOKEN=app_default
FEISHU_TABLE_ID=tbl_default
FEISHU_APP_ID=cli_default
FEISHU_APP_SECRET=sec_default
FEISHU_BOT_TOKEN=

多目标配置

新增 FEISHU_TARGETS 和每个目标的 FEISHU_<TARGET>_* 键:

FEISHU_APP_TOKEN=app_default
FEISHU_TABLE_ID=tbl_default
FEISHU_APP_ID=cli_default
FEISHU_APP_SECRET=sec_default

FEISHU_TARGETS=team_b,finance

FEISHU_TEAM_B_APP_TOKEN=app_team_b
FEISHU_TEAM_B_TABLE_ID=tbl_team_b

FEISHU_FINANCE_APP_TOKEN=app_finance
FEISHU_FINANCE_TABLE_ID=
FEISHU_FINANCE_APP_ID=cli_finance
FEISHU_FINANCE_APP_SECRET=sec_finance

规则:

  • named target 会继承顶层 FEISHU_APP_ID / FEISHU_APP_SECRET / FEISHU_BOT_TOKEN(若对应前缀键为空)
  • named target 不继承 FEISHU_APP_TOKEN
  • 某个 target 的 TABLE_ID 为空时,会为该 target 自动选择第一个表

检查目标表结构

可以只读检查目标表是否可访问、以及标准字段是否齐全:

llm-usage doctor --feishu
llm-usage doctor --feishu --feishu-target team_b
llm-usage doctor --feishu --all-feishu-targets

说明:

  • 缺失字段和类型不匹配会显示为 warning
  • 认证失败、无权限、无法读取字段列表等会返回非零退出码

初始化目标表结构

可以按标准 schema 自动补齐缺失字段:

llm-usage init --feishu-bitable-schema --dry-run
llm-usage init --feishu-bitable-schema --feishu-target finance
llm-usage init --feishu-bitable-schema --all-feishu-targets

说明:

  • 该命令只会创建缺失字段
  • 不会删除字段
  • 不会重命名字段
  • 不会修改已有字段类型

同步到一个或多个目标表

llm-usage sync
llm-usage sync --feishu-target team_b
llm-usage sync --feishu-target team_b --feishu-target finance
llm-usage sync --all-feishu-targets

默认行为:

  • 不带目标参数时,只同步到默认目标
  • --all-feishu-targets 才会显式 fan-out 到所有目标

标准字段参考

字段名 作用 推荐类型 说明
date_local 聚合日期 日期时间 当前实现会按日期/时间字符串做兼容归一化
user_hash 脱敏后的用户标识 文本 whoami 输出一致
source_host_hash 脱敏后的主机标识 文本 区分 local 和远端来源
tool 工具名称 文本 例如 codexcursor
model 模型名称 文本 原始聚合维度之一
input_tokens_sum 输入 tokens 数字 聚合求和
cache_tokens_sum cache tokens 数字 聚合求和
output_tokens_sum 输出 tokens 数字 聚合求和
row_key 幂等 upsert 键 文本 用于在飞书侧定位已存在记录
updated_at 最近更新时间 日期时间 sync 会按毫秒时间戳归一化

注意:

  • 飞书应用权限不等于多维表格协作权限
  • 即使应用有写权限,如果表格本身只读,写入仍会失败
  • 应确保应用或其运行身份对目标表保有编辑权限

开发

安装开发依赖:

pip install -e '.[dev]'
pytest

发布到 PyPI

先准备发布工具:

python -m pip install -U build twine

每次发布前都要先修改 pyproject.toml 里的 version,避免重复上传同一版本。

构建并检查 PyPI 分发文件:

./scripts/build_pypi_release.sh

这个脚本会把产物单独输出到 dist/pypi/,不会碰现有 dist/ 目录中的业务压缩包。

如果你想自定义输出目录:

./scripts/build_pypi_release.sh /tmp/llm-usage-pypi

上传命令单独执行,不放进脚本里:

python -m twine upload dist/pypi/*

如果要先用 TestPyPI 验证:

python -m twine upload --repository testpypi dist/pypi/*

GitHub Actions 自动发布也已支持:

  • 推送 tag py-vX.Y.Z 会触发 .github/workflows/publish-pypi.yml
  • 也可以在 Actions 页面手动运行 Publish PyPI
  • tag 触发时,workflow 会校验 tag 版本和 pyproject.toml 中的 version 完全一致

启用前提:

  • 在 PyPI 项目 llm-usage-horizon 中配置 GitHub trusted publisher
  • publisher 的仓库填 zaxliu/agent_coding_usage
  • workflow 名称填 Publish PyPI
  • environment 名称填 pypi

手动触发不会改版本号,只会发布当前仓库里已经写入 pyproject.toml 的版本。

发布到 npm

先确认 node/package.json 里的 version 已更新,且包名仍然是 @llm-usage-horizon/llm-usage-node

本地发布前校验:

cd node
npm install
npm test
npm pack --dry-run

GitHub Actions 自动发布也已支持:

  • 推送 tag node-vX.Y.Z 会触发 .github/workflows/publish-npm.yml
  • 也可以在 Actions 页面手动运行 Publish npm
  • tag 触发时,workflow 会校验 tag 版本和 node/package.json 中的 version 完全一致

启用前提:

  • 在 npm 上为包 @llm-usage-horizon/llm-usage-node 启用 trusted publishing
  • 关联 GitHub 仓库 zaxliu/agent_coding_usage
  • workflow 名称填 Publish npm
  • environment 名称填 npm

手动触发不会改版本号,只会发布当前仓库里已经写入 node/package.json 的版本。

采集器扩展说明见 docs/ADAPTERS.md

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

llm_usage_horizon-0.1.5.tar.gz (127.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

llm_usage_horizon-0.1.5-py3-none-any.whl (90.1 kB view details)

Uploaded Python 3

File details

Details for the file llm_usage_horizon-0.1.5.tar.gz.

File metadata

  • Download URL: llm_usage_horizon-0.1.5.tar.gz
  • Upload date:
  • Size: 127.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for llm_usage_horizon-0.1.5.tar.gz
Algorithm Hash digest
SHA256 aa2bff3c886803c56765a549a10a1505068a5fb504270d6f6386a718452525cf
MD5 c8d547e725d97a8628bb3c0f7bbaff89
BLAKE2b-256 efc99ab02bd753cef6022ee9495f666c1d5a58951b754e6f410c2694c1522f4d

See more details on using hashes here.

Provenance

The following attestation bundles were made for llm_usage_horizon-0.1.5.tar.gz:

Publisher: publish-pypi.yml on zaxliu/agent_coding_usage

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file llm_usage_horizon-0.1.5-py3-none-any.whl.

File metadata

File hashes

Hashes for llm_usage_horizon-0.1.5-py3-none-any.whl
Algorithm Hash digest
SHA256 5e85afd993447a26d2e02c3686ecb9f5c267fa952653128b78592834413fe925
MD5 4cb98a58f335594f7bac2e1fe37fa5c1
BLAKE2b-256 998f77e47efd008a612fda4ad00c376da6c0156400224e3c152bdb970169af02

See more details on using hashes here.

Provenance

The following attestation bundles were made for llm_usage_horizon-0.1.5-py3-none-any.whl:

Publisher: publish-pypi.yml on zaxliu/agent_coding_usage

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page