Token 用量统计 zhujunzhujunzhu/ai-token-report
DeepSeek Harnessv0.1.0AI token 用量统计平台。四种形态:命令行 / 本地页面 / 部门看板 / DSH 插件。
30.8/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/14 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
自动兼容检查未通过:缺少 main/exports/bin 入口声明。以下命令仍可尝试,但可能失败。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add zhujunzhujunzhu/ai-token-report该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · other
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 22 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装缺少 main/exports/bin 入口声明
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✓npm 包ai-token-report @ 0.1.0
✓Node 引擎要求 >=22 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✗入口文件缺少入口声明
源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/14 00:32:49
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 87 处,展示前 76 条)
- 写入本地文件packages/cli/src/cli.ts:1498
await writeFile(opts.out, text, 'utf8') - 写入本地文件packages/cli/src/cli.ts:1522
await writeFile(opts.out, text, 'utf8') - 读取本地文件packages/cli/src/cli.ts:1795
if (existsSync(join(dir, 'index.html'))) return dir - 执行外部命令来自配置/变量packages/cli/src/cli.ts:1815
const child = spawn(cmd[0] as string, cmd[1] as string[], { - 读取本地文件packages/core/src/connection-store.ts:94
return { text: readFileSync(path, 'utf8') } - 删除本地文件packages/core/src/connection-store.ts:158
rmSync(path, { force: true }) - 写入本地文件packages/core/src/connection-store.ts:161
mkdirSync(dirname(path), { recursive: true }) - 写入本地文件packages/core/src/connection-store.ts:165
writeFileSync(temp, text, { mode: 0o600 }) - 写入本地文件packages/core/src/connection-store.ts:166
renameSync(temp, path) - 删除本地文件packages/core/src/connection-store.ts:169
rmSync(temp, { force: true }) - 读取本地文件packages/core/src/home.ts:232
return readdirSync(dir, { withFileTypes: true }) - 读取本地文件packages/core/src/home.ts:449
return { home, root, exists: existsSync(root) } - 读取本地文件packages/core/src/identity-store.ts:68
if (!existsSync(path)) { - 读取本地文件packages/core/src/identity-store.ts:74
raw = readFileSync(path, 'utf8') - 写入本地文件packages/core/src/identity-store.ts:170
mkdirSync(dirname(path), { recursive: true }) - 写入本地文件packages/core/src/identity-store.ts:174
writeFileSync(tmp, JSON.stringify(identity, null, 2) + '\n', 'utf8') - 写入本地文件packages/core/src/identity-store.ts:178
chmodSync(tmp, 0o600) - 写入本地文件packages/core/src/identity-store.ts:183
renameSync(tmp, path) - 读取本地文件packages/core/src/identity-store.ts:193
if (!existsSync(path)) return false - 删除本地文件packages/core/src/identity-store.ts:195
unlinkSync(path) - 读取本地文件packages/core/src/scanner.ts:108
projects = await readdir(sessionsRoot, { withFileTypes: true }) - 读取本地文件packages/core/src/scanner.ts:119
if (!project.isDirectory() && !(project.isSymbolicLink() && (await stat(projPath)).isDirectory())) continue - 读取本地文件packages/core/src/scanner.ts:127
sessionIds = await readdir(projPath, { withFileTypes: true }) - 读取本地文件packages/core/src/scanner.ts:137
if (!session.isDirectory() && !(session.isSymbolicLink() && (await stat(sessPath)).isDirectory())) continue - 读取本地文件packages/core/src/scanner.ts:146
entries = await readdir(sessPath) - 读取本地文件packages/core/src/scanner.ts:212
try { entries = await readdir(dir) } catch { - 读取本地文件packages/core/src/state.ts:138
raw = readFileSync(path, 'utf8') - 写入本地文件packages/core/src/state.ts:192
mkdirSync(dirname(path), { recursive: true }) - 写入本地文件packages/core/src/state.ts:194
writeFileSync(tmp, JSON.stringify(state, null, 2), 'utf8') - 写入本地文件packages/core/src/state.ts:196
renameSync(tmp, path) - 删除本地文件packages/core/src/state.ts:200
rmSync(tmp, { force: true }) - 删除本地文件packages/core/src/state.ts:210
rmSync(path, { force: true }) - 删除本地文件packages/core/src/state.ts:211
rmSync(`${path}.tmp`, { force: true }) - 读取本地文件packages/dsh-plugin/src/backfill-runner.ts:97
const value = JSON.parse(await readFile(path, 'utf8')) as Partial<Checkpoint> - 写入本地文件packages/dsh-plugin/src/backfill-runner.ts:113
await writeFile(temporary, JSON.stringify({ version: 1, file } satisfies Checkpoint), 'utf8') - 写入本地文件packages/dsh-plugin/src/backfill-runner.ts:114
await rename(temporary, path) - 删除本地文件packages/dsh-plugin/src/backfill-runner.ts:116
try { await unlink(temporary) } catch { } - 读取本地文件packages/dsh-plugin/src/backfill-runner.ts:123
const value = JSON.parse(await readFile(path, 'utf8')) as Partial<PlainWatermarks> - 写入本地文件packages/dsh-plugin/src/backfill-runner.ts:138
await writeFile(temporary, JSON.stringify(value), 'utf8') - 写入本地文件packages/dsh-plugin/src/backfill-runner.ts:139
await rename(temporary, path) - 删除本地文件packages/dsh-plugin/src/backfill-runner.ts:141
try { await unlink(temporary) } catch { } - 读取本地文件packages/dsh-plugin/src/backfill-runner.ts:240
if (!existsSync(root)) throw new Error(`会话日志根不存在:${root}`) - 读取本地文件packages/dsh-plugin/src/backfill-runner.ts:241
if (!(await stat(root)).isDirectory()) throw new Error(`会话日志根路径不是目录:${root}`) - 读取本地文件packages/dsh-plugin/src/backfill-runner.ts:242
await readdir(root) - 写入本地文件packages/dsh-plugin/src/backfill-runner.ts:247
await mkdir(checkpointDir, { recursive: true }) - 写入本地文件packages/dsh-plugin/src/outbox.ts:107
mkdirSync(this.#dir, { recursive: true }) - 读取本地文件packages/dsh-plugin/src/outbox.ts:122
return readdirSync(this.#dir) - 写入本地文件packages/dsh-plugin/src/outbox.ts:161
writeFileSync(temporary, text, 'utf8') - 写入本地文件packages/dsh-plugin/src/outbox.ts:162
renameSync(temporary, this.#pathOf(file)) - 删除本地文件packages/dsh-plugin/src/outbox.ts:164
try { unlinkSync(temporary) } catch { } - 写入本地文件packages/dsh-plugin/src/outbox.ts:183
renameSync(this.#pathOf(file), this.#pathOf(file.replace(PENDING_PREFIX, INFLIGHT_PREFIX))) - 删除本地文件packages/dsh-plugin/src/outbox.ts:199
unlinkSync(this.#pathOf(candidate)) - 读取本地文件packages/dsh-plugin/src/outbox.ts:217
if (!existsSync(from)) return - 写入本地文件packages/dsh-plugin/src/outbox.ts:219
renameSync(from, this.#pathOf(file)) - 写入本地文件packages/dsh-plugin/src/outbox.ts:239
renameSync(this.#pathOf(f), this.#pathOf(back)) - 读取本地文件packages/dsh-plugin/src/outbox.ts:279
text = readFileSync(this.#pathOf(file), 'utf8') - 删除本地文件packages/dsh-plugin/src/outbox.ts:325
unlinkSync(this.#pathOf(f)) - 读取本地文件packages/dsh-plugin/src/outbox.ts:335
try { return statSync(this.#pathOf(file)).mtimeMs } catch { return -1 } - 读取本地文件packages/dsh-plugin/src/outbox.ts:340
const s = statSync(this.#dir) - 读取本地文件packages/dsh-plugin/src/outbox.ts:395
const st = statSync(this.#pathOf(file)) - 删除本地文件packages/dsh-plugin/src/outbox.ts:412
unlinkSync(this.#pathOf(f)) - 发起网络请求packages/dsh-plugin/src/reporter.ts:490
const res = await this.#fetch(this.#config.endpoint, { - 读取本地文件packages/server/src/credentials.ts:307
if (!existsSync(path)) { - 读取本地文件packages/server/src/credentials.ts:313
raw = readFileSync(path, 'utf8') - 发起网络请求packages/server/src/identity-route.ts:279
const res = await this.#fetch(url, { - 读取密钥环境变量packages/server/src/index.ts:265
adminToken: options.adminToken ?? process.env.ATR_ADMIN_TOKEN, - 读取密钥环境变量packages/server/src/index.ts:268
adminPassword: options.adminPassword ?? process.env.ATR_ADMIN_PASSWORD, - 读取密钥环境变量packages/server/src/index.ts:380
captchaHmacKey: options.captchaHmacKey ?? process.env.ATR_CAPTCHA_HMAC_KEY, - 发起网络请求packages/server/src/index.ts:409
handler: (req: Request) => app.fetch(req), - 读取本地文件packages/server/src/main.ts:272
if (existsSync(resolve(dir, 'index.html'))) return dir - 写入本地文件packages/server/src/member-admin.ts:425
mkdirSync(dirname(this.#credentialsPath), { recursive: true }) - 写入本地文件packages/server/src/member-admin.ts:429
writeFileSync(tmp, serializeCredentials(next), 'utf8') - 写入本地文件packages/server/src/member-admin.ts:433
chmodSync(tmp, 0o600) - 写入本地文件packages/server/src/member-admin.ts:438
renameSync(tmp, this.#credentialsPath) - 读取本地文件packages/server/src/member-admin.ts:530
if (existsSync(dir)) { - 读取本地文件packages/server/src/member-admin.ts:532
accessSync(dir, constants.W_OK)
扫描于 2026/11/3(1 天前) · 范围 全包扫描 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 统计本机Token消耗
- 统计部门Token消耗
- 生成消耗看板
README
由 DeepSeek 最新模型翻译生成AI Token Report
AI token 用量统计平台。四种形态:命令行 / 本地页面 / 部门看板 / DSH 插件。
📖 先读 ARCHITECTURE.md —— 整体结构与职责划分。
口径细节见 docs/口径实测结论.md,插件方案见 docs/插件方案.md。
快速开始
bun install # 安装全部工作区依赖
① 终端里看统计(读本机日志,不联网)—— ✅ 已可用
bun run stats -- --period today
② 本地页面:内嵌服务 + 自动开浏览器 —— ✅ 已可用
bun run build:local # 首次需先构建前端产物
bun run web
③ 独立部署部门服务端(含部门看板页面)—— ✅ 已可用
bun run build:portal # 首次需先构建看板前端产物
bun run server # 先按下文配置数据库及首次管理员账号
④ 增量上报到部门服务端(由计划任务每 10 分钟调用)—— ✅ 已可用
bun run report -- --endpoint http://<服务端>:8787/api/v1/token-usage --token <管理员发的 token>
展开完整文档(README 剩余 1 节)
AI Token Report AI token 用量统计平台。四种形态:命令行 / 本地页面 / 部门看板 / DSH 插件。 📖 先读 ARCHITECTURE.md —— 整体结构与职责划分。 口径细节见 docs/口径实测结论.md,插件方案见 docs/插件方案.md。 快速开始 bun install # 安装全部工作区依赖 ① 终端里看统计(读本机日志,不联网)—— ✅ 已可用 bun run stats -- --period today ② 本地页面:内嵌服务 + 自动开浏览器 —— ✅ 已可用 bun run build:local # 首次需先构建前端产物 bun run web ③ 独立部署部门服务端(含部门看板页面)—— ✅ 已可用 bun run build:portal # 首次需先构建看板前端产物 bun run server # 先按下文配置数据库及首次管理员账号 ④ 增量上报到部门服务端(由计划任务每 10 分钟调用)—— ✅ 已可用 bun run report -- --endpoint http://<服务端>:8787/api/v1/token-usage --token <管理员发的 token> 实现进度:stats(终端统计)、web(本地页面)、server(上报接收 + 部门看板)与 report(CLI 上报)均已可用。 四种形态(CLI / 本地页面 / 部门看板 / DSH 插件)全部落地,详见 ARCHITECTURE.md §8 阶段表。 部门看板:bun run server 起服务后打开 http://<服务端>:8787/ (页面由服务端静态托管,构建产物在 packages/web-portal/dist),使用账号密码和验证码登录,即可看到本部门的人员排行、部门趋势、模型分布、用量明细与采集覆盖率诊断。 筛选栏支持时间窗(今天 / 上周 / 本月 / 最近 90 天…或自定义起止时间)、人员多选、分组多选与厂商 / 模型。 人员管理:管理员登录后右上多一个「人员管理」页签 ——在页面上维护人员、分组(可多选)、角色、登录账号及上报 Token,数据统一保存在部门数据库。分组本身在「分组管理」页维护(/groups)。 目录 | 路径 | 职责 | | packages/shared | 契约单一真源:上报 DTO、查询响应、口径公式 | | packages/core | 统计内核:解码 / 扫描 / 聚合 / 时间范围 / 水位线 / 身份存储 | | packages/cli | 命令行入口:stats / report / --web | | packages/server | 后端:上报接收 + 本地直查 + 部门统计(含分组目录与分组维度查询)+ 数据库身份、人员与分组管理 + 静态托管 | | packages/web-local | 本地页面:只看本机,数据来自 /api/local/ | | packages/web-portal | 部门看板 + 人员管理页 + 分组管理页:看全员,数据来自 /api/v1/stats/(含分组候选项 /api/v1/stats/groups 与人员候选项 /api/v1/stats/members)、/api/v1/admin/members 与 /api/v1/admin/groups(数据库会话鉴权) | | packages/dsh-plugin | DSH 插件:实时上报 + 界面用量面板(安装含 DSH Desktop 桌面端,见 docs/桌面端安装交付清单.md) | DSH 插件安装 命令行版 DSH:一条命令,add 会自动登记到 profile 的 dsh.profile.bundles dsh plugin --profile web add dsh-plugin-token-report@latest DSH Desktop(桌面端)的图形入口装不了本插件 —— 侧边栏「插件」市场只接受 awesome-dsh-plugin 精选列表内的来源,本插件不在其中。 走命令行,把 home / pnpm 换成 Desktop 自己那一套即可: $env:DSH_HOME = "$env:APPDATA\dsh-desktop\harness" $env:PATH = "$env:DSH_HOME\.desktop-bin;$env:PATH" # ★ 用 Desktop 自己的 pnpm / node node "<安装目录>\resources\app.asar.unpacked\node_modules\@deepseek-ai\dsh\lib\bin.js" plugin --profile web add dsh-plugin-token-report@latest 逐条步骤、核对命令、日志取证与回滚见 docs/桌面端安装交付清单.md,插件自身的配置与排障见 packages/dsh-plugin/README.md。 🚨 三件事必须同时成立,否则症状是「面板不见了、而且一条也不上报」,不是报错: ① 官方 OTel 后端已 disabled: true(同一时刻只能有一个 sessionTelemetry); ② 装的是发布包 / 构建产物,不是仓内源码包(它的 main 指向 src/index.ts,Node 加载即失败); ③ 宿主的 @deepseek-ai/dsh 落在插件的 peer 兼容窗口 >=0.1.7-rc.2 <0.3.0-0 内。 三条铁律 1. 口径只在 shared/src/metrics.ts 定义。 任何地方要算缓存命中率等指标,必须调用它,不要各自重写 —— 两端口径不一致的 bug 极难排查。 2. cacheRead 不是 input 的一部分。 input 是「未命中缓存」那部分,实测 cacheRead 占总用量 94.3%,漏掉它等于漏掉 94% 的用量。 3. 上报只需 at-least-once。 幂等键 event_id = sessionId:seq 由服务端 ON CONFLICT DO NOTHING 去重,所以插件与命令行可以同时上报,无需协调。 身份署名 首次使用本地页面或插件时只要求填两样:部门服务端地址 + appKey(管理员发放)。 姓名与分组不由用户填 —— 它们由服务端按 appKey 解析(填了也不作数)。在此之前: - ❌ 不采集、也不向任何服务端发送数据 - ✅ 仍可查看本机统计(那是你自己的数据) 填写后保存在数据目录下:identity.json(谁)+ plugin-connection.json(连哪台、拿什么凭证),缺省 ~/.ai-token-report/,可用配置项 dataDir 或 DSH_TOKEN_REPORT_DATA_DIR 覆盖; 本地页与插件的「配置」是同一形态、写同一份配置,在哪里填一次都生效。 appKey 是身份凭证 —— 姓名以服务端凭证表为准,改本地文件无法冒用他人身份。 DSH Desktop 与命令行版 DSH 缺省就共用这份署名(数据目录不再跟随各自的 home),想各用一套时才在插件配置里指 dataDir(不要改 dshHome),见 packages/dsh-plugin/README.md §1.1。 数据库与管理员初始化 正式部署采用 MySQL,本地运行和测试保留 SQLite。人员、分组、角色权限、账号、Token、会话、验证码、限流和审计均落数据库;正常请求不再读取凭证文件。 空库首次启动时,通过部署秘密配置注入 ATR_ADMIN_USERNAME、ATR_ADMIN_PASSWORD 及至少 32 个字符的 ATR_CAPTCHA_HMAC_KEY;MySQL 再配置 ATR_MYSQL_URL。 管理员配置只初始化一次,后续以数据库为准。MySQL 正式部署建议使用已验证的 Node/mysql2 入口,完整命令见 数据库部署与迁移。 本机自用不必手配这些:bun run server 在「本机 SQLite 上报库 + 监听 127.0.0.1」时,会把缺失的验证码密钥与随机生成的管理员口令写进数据目录下的 server.env (缺省 ~/.ai-token-report/server.env),下次启动直接复用 —— 配了 --mysql / ATR_MYSQL_URL(共享库)或 --host 非回环地址时不自动生成,仍按上面显式配置。 管理员登录后创建人员,分配分组(一个人可同属多个分组)和角色,再按需要开通登录或签发上报 Token。 默认 Token 只有署名与上报权限,明文只显示一次。轮换、撤销和停用立即生效。 人员用稳定 ID 标识,可以同名;姓名不能作为权限或归属的依据。 最后一个有效管理员入口不可被删除、停用或降级。旧 v3 / v4 数据库和 credentials.json 必须按文档显式迁移(v3 → v4 冻结基线 → v5)并保留备份,服务不会自动重建唯一的历史数据。 环境要求 - Bun ≥ 1.1 - Node ≥ 22(zlib.zstdDecompressSync 需要) 开发 bun test # 全仓测试 bun run --filter '' typecheck bun run build # 构建两个 web 应用
预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
zhujunzhujunzhu/ai-token-report
AI token 用量统计平台。四种形态:命令行 / 本地页面 / 部门看板 / DSH 插件。
相关插件 · 其他
模型破甲清洗HunYiSage/dsh-purge
⚔️ DeepSeek Harness 网络安全红队工具dsh-purge
★ 16,993已汉化DeepSeek 客户端
73
女娲蒸馏alchaincyf/nuwa-skill
输入名人名字,提炼其思维模型与决策方式供你调用
★ 37,308已汉化DSH
72
逆向分析桥接Fanqi89/dsh-rea
REA(Reverse Engineer Anything)MCP 桥接,用来把 REA 逆向工具接入 DSH 插件
★ 51已汉化DSH
72
语音女友beiyege-01/dsh-voice-ai-girlfriend
语音 AI 女友Voice AI girlfriend for DeepSeek Harness:Whisper…
★ 888已汉化DSH
71.6
红队越狱测试套件Minglink/dsh-infinite-gen-5
DeepSeek v4.1 flash 逆向工具无限五代 求 Star 收藏 ⭐欢迎大家提交项目的改进
★ 3,759已汉化DSH
71.4
协同办公空间dream-num/univer-workspace
一个让人类与 AI Agent 共同创作、协作和审阅的开源 Office 工作空间。
★ 2,269已汉化DSH
70.8
这是你的仓库?认领条目