{"_id":"@abcdefu_cja/dsh-usage-stats","_rev":"4-223dda98b78b485537de736e312af628","name":"@abcdefu_cja/dsh-usage-stats","dist-tags":{"latest":"0.3.0"},"versions":{"0.1.0":{"name":"@abcdefu_cja/dsh-usage-stats","version":"0.1.0","license":"BSD-3-Clause","_id":"@abcdefu_cja/dsh-usage-stats@0.1.0","maintainers":[{"name":"abcdefu_cja","email":"jianwei.deng0515@gmail.com"}],"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["@deepseek-ai/dsh-client-runtime","@deepseek-ai/dsh-client-connection","@deepseek-ai/dsh-client-ui-settings","@deepseek-ai/dsh-client-ui-slots"],"platform":"web"}},"dist":{"shasum":"edb3b6488b66c1ed4e6784d70344d7fa94ae6e9f","tarball":"https://registry.npmjs.org/@abcdefu_cja/dsh-usage-stats/-/dsh-usage-stats-0.1.0.tgz","fileCount":40,"integrity":"sha512-Qygoho8LeVG9l/V5+wSsa5O7J32m/9grY7plqg99d9Yhu9YRErcQZ7m5rUKkN7PZ2Yis28EahatNq76I4zG65Q==","signatures":[{"sig":"MEYCIQC+7giXiosVLpKYFNbgNXXckoXkkE1JmcNTSyumD6djEwIhAJ9QG5nuIMEl5h+wXu3Wf+H3mpkMYem5Fs5soi6pRXiO","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":430646},"main":"lib/index.js","type":"module","types":"lib/types/index.d.ts","exports":{".":{"types":"./lib/types/index.d.ts","default":"./lib/index.js"},"./src/*":"./src/*","./client":{"types":"./lib/types/client/index.d.ts","default":"./lib/client.js"},"./package.json":"./package.json"},"gitHead":"c0ccb7c66813dc6d749e1a8fad7c5791a49d0fa0","scripts":{"test":"vitest run --passWithNoTests","build":"tsc -b && tsdown","watch":"tsdown --watch","prepare":"tsc -b && tsdown","typecheck":"tsc -b --pretty false"},"_npmUser":{"name":"abcdefu_cja","email":"jianwei.deng0515@gmail.com"},"_npmVersion":"11.13.0","description":"DSH API 用量统计：token、请求、轮次、缓存命中、费用估算与多提供商余额/配额。","directories":{},"_nodeVersion":"24.16.0","dependencies":{"zod":"^4.4.3","schemastery":"^3.18.0"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^26.0.0","react":"^18.2.0","tsdown":"^0.22.2","vitest":"^4.1.1","react-dom":"^18.2.0","typescript":"^5.9.3","@types/node":"^22.20.0","@types/react":"~18.3.1","lightningcss":"^1.32.0","@deepseek-ai/cordis":"^4.0.1","vite-tsconfig-paths":"^6.1.1","@deepseek-ai/dsh-llm":"^0.1.0-rc.6","@deepseek-ai/dsh-brand":"^0.1.0-rc.6","@deepseek-ai/dsh-scope":"^0.1.0-rc.6","@testing-library/react":"^16.1.0","@deepseek-ai/dsh-session":"^0.1.0-rc.6","@deepseek-ai/dsh-timeout":"^0.1.0-rc.6","@deepseek-ai/dsh-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-invariants":"^0.1.0-rc.6","@deepseek-ai/dsh-client-locale":"^0.1.0-rc.6","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.6","@deepseek-ai/dsh-host-webserver":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-session-projection":"^0.1.0-rc.6","@deepseek-ai/dsh-agent-default-model":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-conversation":"^0.1.0-rc.6"},"peerDependencies":{"react":"^18.2.0","@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-llm":"^0.1.0-rc.6","@deepseek-ai/dsh-session":"^0.1.0-rc.6","@deepseek-ai/dsh-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-invariants":"^0.1.0-rc.6","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.6","@deepseek-ai/dsh-client-connection":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-session-projection":"^0.1.0-rc.6"},"_npmOperationalInternal":{"tmp":"tmp/dsh-usage-stats_0.1.0_1786821903039_0.8206315359126184","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@abcdefu_cja/dsh-usage-stats","version":"0.2.0","license":"BSD-3-Clause","_id":"@abcdefu_cja/dsh-usage-stats@0.2.0","maintainers":[{"name":"abcdefu_cja","email":"jianwei.deng0515@gmail.com"}],"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["@deepseek-ai/dsh-client-runtime","@deepseek-ai/dsh-client-connection","@deepseek-ai/dsh-client-ui-settings","@deepseek-ai/dsh-client-ui-slots"],"platform":"web"}},"dist":{"shasum":"6fa4a1013ebf0b10bcaab5d1ba2e4094c563c19c","tarball":"https://registry.npmjs.org/@abcdefu_cja/dsh-usage-stats/-/dsh-usage-stats-0.2.0.tgz","fileCount":40,"integrity":"sha512-RFk72+Dy2Qgogfbta+dQ93f5TgSLW5vqy+IHAzg8jmuvgjsrJKwE0DHRv7vHKYnmbzt1xpb6f2+0gdxhMeUDyQ==","signatures":[{"sig":"MEYCIQCQt8CJijEjyjhj9HHp9Y/cuHUD/MRuiX/CInznD3tERwIhALWQCF6ofMN+9ar3CPOBAe/8ERQB5YWPJIRQx1Aj0IUm","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":444337},"main":"lib/index.js","type":"module","types":"lib/types/index.d.ts","exports":{".":{"types":"./lib/types/index.d.ts","default":"./lib/index.js"},"./src/*":"./src/*","./client":{"types":"./lib/types/client/index.d.ts","default":"./lib/client.js"},"./package.json":"./package.json"},"gitHead":"acf530b608a0e4a4d97de293360a60a24047f073","scripts":{"test":"vitest run --passWithNoTests","build":"tsc -b && tsdown","watch":"tsdown --watch","prepare":"tsc -b && tsdown","typecheck":"tsc -b --pretty false"},"_npmUser":{"name":"abcdefu_cja","email":"jianwei.deng0515@gmail.com"},"_npmVersion":"11.13.0","description":"DSH API 用量统计：token、请求、轮次、缓存命中、费用估算与多提供商余额/配额。","directories":{},"_nodeVersion":"24.16.0","dependencies":{"zod":"^4.4.3","schemastery":"^3.18.0"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^26.0.0","react":"^18.2.0","tsdown":"^0.22.2","vitest":"^4.1.1","react-dom":"^18.2.0","typescript":"^5.9.3","@types/node":"^22.20.0","@types/react":"~18.3.1","lightningcss":"^1.32.0","@deepseek-ai/cordis":"^4.0.1","vite-tsconfig-paths":"^6.1.1","@deepseek-ai/dsh-llm":"^0.1.0-rc.6","@deepseek-ai/dsh-brand":"^0.1.0-rc.6","@deepseek-ai/dsh-scope":"^0.1.0-rc.6","@testing-library/react":"^16.1.0","@deepseek-ai/dsh-session":"^0.1.0-rc.6","@deepseek-ai/dsh-timeout":"^0.1.0-rc.6","@deepseek-ai/dsh-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-invariants":"^0.1.0-rc.6","@deepseek-ai/dsh-client-locale":"^0.1.0-rc.6","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.6","@deepseek-ai/dsh-host-webserver":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-session-projection":"^0.1.0-rc.6","@deepseek-ai/dsh-agent-default-model":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-conversation":"^0.1.0-rc.6"},"peerDependencies":{"react":"^18.2.0","@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-llm":"^0.1.0-rc.6","@deepseek-ai/dsh-session":"^0.1.0-rc.6","@deepseek-ai/dsh-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-invariants":"^0.1.0-rc.6","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.6","@deepseek-ai/dsh-client-connection":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-session-projection":"^0.1.0-rc.6"},"_npmOperationalInternal":{"tmp":"tmp/dsh-usage-stats_0.2.0_1787304890728_0.6161702173757637","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@abcdefu_cja/dsh-usage-stats","version":"0.2.1","license":"BSD-3-Clause","_id":"@abcdefu_cja/dsh-usage-stats@0.2.1","maintainers":[{"name":"abcdefu_cja","email":"jianwei.deng0515@gmail.com"}],"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["@deepseek-ai/dsh-client-runtime","@deepseek-ai/dsh-client-connection","@deepseek-ai/dsh-client-ui-settings","@deepseek-ai/dsh-client-ui-slots"],"platform":"web"}},"dist":{"shasum":"d8ffb15f7f2e982e10a11d62d98380319edd6b7d","tarball":"https://registry.npmjs.org/@abcdefu_cja/dsh-usage-stats/-/dsh-usage-stats-0.2.1.tgz","fileCount":40,"integrity":"sha512-SnLNGl9jt9U6+X0E1YvakUzKGB+ISK1QlsmVxXkxektNF7K0WhUCUXrLu0CXnsfhtBHmfd2VxgIhN98qeJsLgw==","signatures":[{"sig":"MEQCIEUqTFxFhb3bb0vtHlAQk/jemh9tTl17+/LA8HJsMnSpAiBqe8CJRR8/N6AUWM2GpCaDGAFaBMIdu1l5QGzTozs8kA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":444613},"main":"lib/index.js","type":"module","types":"lib/types/index.d.ts","exports":{".":{"types":"./lib/types/index.d.ts","default":"./lib/index.js"},"./src/*":"./src/*","./client":{"types":"./lib/types/client/index.d.ts","default":"./lib/client.js"},"./package.json":"./package.json"},"gitHead":"56db119f726aa3d8e7e39bc378f87ffa7e3622a5","scripts":{"test":"vitest run --passWithNoTests","build":"tsc -b && tsdown","watch":"tsdown --watch","prepare":"tsc -b && tsdown","typecheck":"tsc -b --pretty false"},"_npmUser":{"name":"abcdefu_cja","email":"jianwei.deng0515@gmail.com"},"_npmVersion":"11.13.0","description":"DSH API 用量统计：token、请求、轮次、缓存命中、费用估算与多提供商余额/配额。","directories":{},"_nodeVersion":"24.16.0","dependencies":{"zod":"^4.4.3","schemastery":"^3.18.0"},"_hasShrinkwrap":false,"devDependencies":{"jsdom":"^26.0.0","react":"^18.2.0","tsdown":"^0.22.2","vitest":"^4.1.1","react-dom":"^18.2.0","typescript":"^5.9.3","@types/node":"^22.20.0","@types/react":"~18.3.1","lightningcss":"^1.32.0","@deepseek-ai/cordis":"^4.0.1","vite-tsconfig-paths":"^6.1.1","@deepseek-ai/dsh-llm":"^0.1.0-rc.6","@deepseek-ai/dsh-brand":"^0.1.0-rc.6","@deepseek-ai/dsh-scope":"^0.1.0-rc.6","@testing-library/react":"^16.1.0","@deepseek-ai/dsh-session":"^0.1.0-rc.6","@deepseek-ai/dsh-timeout":"^0.1.0-rc.6","@deepseek-ai/dsh-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-invariants":"^0.1.0-rc.6","@deepseek-ai/dsh-client-locale":"^0.1.0-rc.6","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.6","@deepseek-ai/dsh-host-webserver":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-session-projection":"^0.1.0-rc.6","@deepseek-ai/dsh-agent-default-model":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-conversation":"^0.1.0-rc.6"},"peerDependencies":{"react":"^18.2.0","@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-llm":"^0.1.0-rc.6","@deepseek-ai/dsh-session":"^0.1.0-rc.6","@deepseek-ai/dsh-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-invariants":"^0.1.0-rc.6","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.6","@deepseek-ai/dsh-client-connection":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-session-projection":"^0.1.0-rc.6"},"_npmOperationalInternal":{"tmp":"tmp/dsh-usage-stats_0.2.1_1787305777451_0.2889437388949745","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@abcdefu_cja/dsh-usage-stats","description":"DSH API 用量统计：token、请求、轮次、缓存命中、费用估算与多提供商余额/配额。","version":"0.3.0","type":"module","main":"lib/index.js","types":"lib/types/index.d.ts","exports":{".":{"types":"./lib/types/index.d.ts","default":"./lib/index.js"},"./client":{"types":"./lib/types/client/index.d.ts","default":"./lib/client.js"},"./src/*":"./src/*","./package.json":"./package.json"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["@deepseek-ai/dsh-client-runtime","@deepseek-ai/dsh-client-connection","@deepseek-ai/dsh-client-ui-settings","@deepseek-ai/dsh-client-ui-slots"],"platform":"web"}},"scripts":{"build":"tsc -b && tsdown","prepare":"tsc -b && tsdown","typecheck":"tsc -b --pretty false","test":"vitest run --passWithNoTests","watch":"tsdown --watch"},"license":"BSD-3-Clause","peerDependencies":{"@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-client-connection":"^0.1.0-rc.6","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.6","@deepseek-ai/dsh-invariants":"^0.1.0-rc.6","@deepseek-ai/dsh-llm":"^0.1.0-rc.6","@deepseek-ai/dsh-session":"^0.1.0-rc.6","@deepseek-ai/dsh-session-projection":"^0.1.0-rc.6","@deepseek-ai/dsh-settings":"^0.1.0-rc.6","react":"^18.2.0"},"dependencies":{"schemastery":"^3.18.0","zod":"^4.4.3"},"devDependencies":{"@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-agent-default-model":"^0.1.0-rc.6","@deepseek-ai/dsh-brand":"^0.1.0-rc.6","@deepseek-ai/dsh-client-locale":"^0.1.0-rc.6","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-conversation":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.6","@deepseek-ai/dsh-host-webserver":"^0.1.0-rc.6","@deepseek-ai/dsh-invariants":"^0.1.0-rc.6","@deepseek-ai/dsh-llm":"^0.1.0-rc.6","@deepseek-ai/dsh-scope":"^0.1.0-rc.6","@deepseek-ai/dsh-session":"^0.1.0-rc.6","@deepseek-ai/dsh-session-projection":"^0.1.0-rc.6","@deepseek-ai/dsh-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-timeout":"^0.1.0-rc.6","@testing-library/react":"^16.1.0","@types/node":"^22.20.0","@types/react":"~18.3.1","jsdom":"^26.0.0","lightningcss":"^1.32.0","react":"^18.2.0","react-dom":"^18.2.0","tsdown":"^0.22.2","typescript":"^5.9.3","vite-tsconfig-paths":"^6.1.1","vitest":"^4.1.1"},"gitHead":"b3f019ce345a0ddd23f1fda7586aae8d448a4414","_id":"@abcdefu_cja/dsh-usage-stats@0.3.0","_nodeVersion":"24.16.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-udH0xCNSCFBOgZYiF0LwG4WM66ZA3gJHswQ7rkrfcNvn64hmJ/21lNLxM8MYJAH9UdkS7vg6S7JdU+oERXDOag==","shasum":"91f6e530843f398a336390a08eee35cf21971cb3","tarball":"https://registry.npmjs.org/@abcdefu_cja/dsh-usage-stats/-/dsh-usage-stats-0.3.0.tgz","fileCount":50,"unpackedSize":438216,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICT5/kOZCJ8oqSYTAbGtNBdEMbr5sE98pLxxsSl2PUBhAiBpObL2yE5gNHlBmZ6DSUg/bYTv8DpWoI7FMlhdq2nyFA=="}]},"_npmUser":{"name":"abcdefu_cja","email":"jianwei.deng0515@gmail.com"},"directories":{},"maintainers":[{"name":"abcdefu_cja","email":"jianwei.deng0515@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dsh-usage-stats_0.3.0_1787308341841_0.4039222862437226"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-15T19:25:02.938Z","modified":"2026-08-21T10:32:22.154Z","0.1.0":"2026-08-15T19:25:03.166Z","0.2.0":"2026-08-21T09:34:50.870Z","0.2.1":"2026-08-21T09:49:37.616Z","0.3.0":"2026-08-21T10:32:21.976Z"},"license":"BSD-3-Clause","description":"DSH API 用量统计：token、请求、轮次、缓存命中、费用估算与多提供商余额/配额。","maintainers":[{"name":"abcdefu_cja","email":"jianwei.deng0515@gmail.com"}],"readme":"# @abcdefu_cja/dsh-usage-stats\n\n[![npm version](https://img.shields.io/npm/v/@abcdefu_cja/dsh-usage-stats)](https://www.npmjs.com/package/@abcdefu_cja/dsh-usage-stats)\n[![license](https://img.shields.io/npm/l/@abcdefu_cja/dsh-usage-stats)](./LICENSE)\n[![GitHub](https://img.shields.io/badge/GitHub-jianweideng0515--create%2Fdsh--usage--stats-blue)](https://github.com/jianweideng0515-create/dsh-usage-stats)\n\nDSH Web 的 API 用量统计插件：精确统计 token、请求、轮次、活跃天数、缓存命中率与费用，并同时监控 OpenCode 订阅配额与 DeepSeek 官方余额。\n\n- **精确计量**：直接读取 provider `usage` 报告（`inputTokens` / `outputTokens` / `cacheReadTokens` / `cacheWriteTokens`），采用 DSH 自身的 `(turn, step)` 替换语义，最终消息替换先前用量块而不重复累计——非启发式估算\n- **独立插件**：不属于 dsh-web-ui 家族，经官方 `settings.section` 槽挂载为设置页左侧导航专属 Tab\n- **多提供商快照**：OpenCode 配额与 DeepSeek 余额并行拉取、各自失败互不影响\n\n## 目录\n\n- [功能特性](#功能特性)\n- [截图](#截图)\n- [安装](#安装)\n- [配置](#配置)\n- [余额自动检测](#余额自动检测)\n- [架构](#架构)\n- [开发](#开发)\n- [已知限制](#已知限制)\n- [许可](#许可)\n\n## 功能特性\n\n- **用量概览 Tab**\n  - 常驻 KPI 区：Token 总量（含费用）、请求数、完成轮次、活跃天数、平均缓存命中率、提供商动态卡（OpenCode 周配额 / DeepSeek 余额）\n  - Token 四分色拆分条（输入 / 缓存读 / 缓存写 / 输出）\n  - 堆叠柱状趋势图：按模型分段着色，Y 轴中文单位刻度（万/亿），悬停柱子显示当日明细（总用量 / 费用 / 分模型 Top5+其他 / 缓存命中率）\n  - 模型明细表（请求数 / token / 费用）\n- **模型与缓存 Tab**：模型占比 Donut 图 + 缓存效率诊断（命中率、节省 token、节省比例）\n- **余额与配额 Tab**：OpenCode 订阅配额三窗口进度条（滚动 / 每周 / 每月 + 重置倒计时）；DeepSeek 官方余额（金额 / 预计可用天数 / 充值页跳转 / 手动刷新）\n- **会话用量面板**：会话页按钮展开当前会话用量（累计 / 最近请求 / 进行中轮次实时消耗）\n- **数据导出**：一键下载当前范围的按日 × 分模型明细 CSV（UTF-8 BOM，Excel 直接打开）\n- **最贵会话排行**：按费用降序 Top 10，快速定位异常消耗\n- **日费用阈值提醒**：配置 `alertDailyCost` 后，今日费用超限即在用量页顶部常驻横幅提示\n- 7 / 14 / 30 / 90 天与自定义范围切换，展开时 30s 轮询（ETag 条件请求，未变化 304 短路）\n\n## 截图\n\n### 用量概览\n\n<img src=\"docs/image-20260816035032614.png\" alt=\"用量概览 Tab：KPI 卡、Token 拆分、堆叠柱状趋势图、模型明细\" width=\"480\" />\n\n### 模型与缓存\n\n<img src=\"docs/image-20260816035059875.png\" alt=\"模型与缓存 Tab：模型占比 Donut 与缓存效率诊断\" width=\"480\" />\n\n### 余额与配额\n\n<img src=\"docs/image-20260816035141846.png\" alt=\"余额与配额 Tab：OpenCode 配额三窗口与 DeepSeek 余额\" width=\"480\" />\n\n### 会话用量面板\n\n<img src=\"docs/image-20260816035247662.png\" alt=\"会话页用量面板：累计消耗与最近请求\" width=\"640\" />\n\n## 安装\n\n### npm（推荐）\n\n```sh\nnpm i @abcdefu_cja/dsh-usage-stats\ndsh plugin --profile web add @abcdefu_cja/dsh-usage-stats\n```\n\n### GitHub 克隆 / 本地开发\n\n```sh\ngit clone https://github.com/jianweideng0515-create/dsh-usage-stats\ndsh plugin --profile web add link:/path/to/dsh-usage-stats\n```\n\n安装后重启 `dsh web`，设置页左侧导航出现「用量统计」入口：\n\n<img src=\"docs/image-20260816034838256.png\" alt=\"设置页左侧导航中的用量统计入口\" style=\"width: 280px;\" />\n\n### 配置文件方式（可选）\n\n也可写入个人 DSH 覆盖层 `~/.dsh/config.yaml`（保存即热加载）：\n\n```yaml\n- insert:\n    - id: usage-stats\n      name: '@abcdefu_cja/dsh-usage-stats'\n      config:\n        enabled: true\n        currency: CNY\n        balance:\n          mode: auto\n          refreshMs: 600000\n```\n\n所有配置项均可选，默认值见下表。\n\n## 配置\n\n| Key | 类型 | 默认 | 含义 |\n|---|---|---|---|\n| `enabled` | `boolean` | `true` | 总开关；关闭后停止事件订阅、落盘与计量 |\n| `prices` | `Record<string, ModelPrice>` | 内置 DeepSeek 价目表 | 每百万 token 单价，按模型键（`input` / `cacheRead` / `cacheWrite` / `output`）；用户项覆盖内置表 |\n| `defaultPrice` | `ModelPrice` | 无 | 未在 `prices` 中的模型的兜底单价；缺省时未知模型按 0 计价 |\n| `currency` | `string` | `CNY` | 费用与余额的显示货币（CNY 显示 ¥，USD 显示 $） |\n| `alertDailyCost` | `number` | 无 | 日费用阈值：今日费用达到该值时，用量页顶部渲染超限横幅；未配置关闭 |\n| `balance.mode` | `'auto' \\| 'manual' \\| 'off'` | `auto` | `auto` 自动检测全部已知 provider（OpenCode 配额 + DeepSeek 余额）；`manual` 使用固定 `baseUrl`；`off` 关闭余额拉取 |\n| `balance.baseUrl` | `string` | 无 | 余额端点基址（`manual` 模式必填） |\n| `balance.path` | `string` | `/user/balance` | 追加到 `baseUrl` 的余额路径 |\n| `balance.apiKeyEnv` | `string` | `DEEPSEEK_API_KEY` | 存放 provider API key 的环境变量名（优先进程环境变量，其次 `~/.dsh/.credentials.yaml`） |\n| `balance.refreshMs` | `number` | `600000` | 余额刷新间隔（毫秒，最小 1000） |\n\n`ModelPrice` 为 `{ input, cacheRead, cacheWrite, output }`，非负数。内置 DeepSeek 价目：\n\n| 模型 | input | cacheRead | cacheWrite | output |\n|---|---|---|---|---|\n| `deepseek-chat` | 2 | 0.5 | 2 | 8 |\n| `deepseek-reasoner` | 4 | 1 | 4 | 16 |\n\n（每百万 token，CNY）\n\n## 余额自动检测\n\n`auto` 模式同时检测以下 provider（内置端点表，profile 无 baseURL 也可推断）：\n\n| provider | 端点 | 展示 |\n|---|---|---|\n| OpenCode Go（`opencode-go`） | `GET https://opencode.ai/zen/go/v1/usage`，key 环境变量 `OPENCODE_GO_API_KEY` | 订阅配额三窗口（滚动 / 每周 / 每月） |\n| DeepSeek（`deepseek`） | `GET https://api.deepseek.com/user/balance`，key 环境变量 `DEEPSEEK_API_KEY` | 金额余额 + 预计可用天数 |\n\n## 架构\n\n```\nsession/event 流（全局）\n      │\n      ▼\n宿主端 UsageStatsMeter ──► 按日 / 分模型桶 ──► ~/.dsh/dsh-usage-stats.json（防抖落盘）\n      │\n      ▼\n只读 HTTP 路由 /api/dsh-usage-stats/*（loopback 围栏）──► 浏览器端 Tab / 会话面板（30s 轮询）\n      │\n      ▼\n余额客户端（并行）：OpenCode /v1/usage 配额 + DeepSeek /user/balance 金额\n```\n\n- **宿主端**：订阅 `session/event`（全局、所有会话），把每次请求折入 `UsageStatsMeter`（token / 请求 / 轮次 / 费用 / 最近请求元数据）。按日（本地时区 `YYYY-MM-DD`）与分模型桶聚合，落盘 `~/.dsh/dsh-usage-stats.json`（30s 防抖 + flush/dispose 即时写，原子 `tmp + rename`，损坏文件转 `.bak` 重建）。余额客户端并行拉取全部已检测 provider 的快照，各自失败互不影响。\n- **浏览器端**：注册设置页左侧导航独立 Tab（官方 `settings.section` 槽，id `usage-stats`）与会话页用量按钮（`conversation.session.header.utilities` 槽）。\n\n插件为函数/命名空间形态：`inject` / `Config` / `apply`，无默认导出。宿主端另导出 `USAGE_STATS_METER_KEY`（挂到上下文的 meter symbol）与 `USAGE_STATS_SETTINGS_NAMESPACE`。计量、计价、存储、查询与 provider 检测模块均为纯函数并有单元测试。\n\n对模型透明：不注入任何提示片段、不注册任何工具，每请求零额外 token，无 KV 缓存稳定性影响。\n\n## 开发\n\n```sh\npnpm install\npnpm build    # tsc -b && tsdown（宿主 ESM + 浏览器闭包工厂 bundle）\npnpm test     # vitest：宿主纯函数单测 + jsdom 组件测试\n```\n\n## 已知限制\n\n- **费用是估算**：按内置或用户价目表 × provider 上报用量计算，非账单方发票；请以实际账单为准。\n- **余额取决于 provider 端点**：DeepSeek 官方余额接口要求有效官方 key（OpenCode 的 key 不被接受）；OpenCode 配额接口可能受 Cloudflare 对非浏览器 UA 的延迟惩罚（已用浏览器 UA + 25s 超时缓解）。\n- **历史自启用时起算**：日聚合只记录插件启用后观察到的事件，之前的使用不回填。\n- **留存**：`byDay` 保留最近 730 天，`sessions` 保留最近 500 个；更早数据在保存时裁剪。\n\n## 许可\n\nBSD-3-Clause，见 [LICENSE](./LICENSE)。\n","readmeFilename":"README.md"}