dsh-all-usage 1.1.18 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +469 -394
  2. package/LICENSE +21 -21
  3. package/README.en.md +273 -0
  4. package/README.md +145 -279
  5. package/assets/model-icons/ai2-color.svg +1 -0
  6. package/assets/model-icons/ai21-brand-color.svg +1 -0
  7. package/assets/model-icons/aionlabs.svg +1 -0
  8. package/assets/model-icons/alephalpha.svg +1 -0
  9. package/assets/model-icons/baai.svg +1 -0
  10. package/assets/model-icons/baichuan-color.svg +1 -0
  11. package/assets/model-icons/baidu-color.svg +1 -0
  12. package/assets/model-icons/bytedance-color.svg +1 -0
  13. package/assets/model-icons/cohere-color.svg +1 -0
  14. package/assets/model-icons/hunyuan-color.svg +1 -0
  15. package/assets/model-icons/ibm.svg +1 -0
  16. package/assets/model-icons/iflytekcloud-color.svg +1 -0
  17. package/assets/model-icons/internlm-color.svg +1 -0
  18. package/assets/model-icons/kling-color.svg +1 -0
  19. package/assets/model-icons/liquid.svg +1 -0
  20. package/assets/model-icons/longcat-color.svg +1 -0
  21. package/assets/model-icons/manifest.json +739 -213
  22. package/assets/model-icons/microsoft-color.svg +1 -0
  23. package/assets/model-icons/mistral-color.svg +1 -0
  24. package/assets/model-icons/nousresearch.svg +1 -0
  25. package/assets/model-icons/nova-color.svg +1 -0
  26. package/assets/model-icons/nvidia-color.svg +1 -0
  27. package/assets/model-icons/perplexity-color.svg +1 -0
  28. package/assets/model-icons/sensenova-color.svg +1 -0
  29. package/assets/model-icons/skywork-color.svg +1 -0
  30. package/assets/model-icons/snowflake-color.svg +1 -0
  31. package/assets/model-icons/stepfun-color.svg +1 -0
  32. package/assets/model-icons/tencent-color.svg +1 -0
  33. package/assets/model-icons/tii-color.svg +1 -0
  34. package/assets/model-icons/upstage-color.svg +1 -0
  35. package/assets/model-icons/voyage-color.svg +1 -0
  36. package/assets/model-icons/xiaomimimo.svg +1 -0
  37. package/assets/model-icons/yandex.svg +1 -0
  38. package/assets/readme-architecture-en.svg +95 -0
  39. package/assets/readme-architecture-zh.svg +95 -0
  40. package/assets/readme-sync-en.svg +81 -0
  41. package/assets/readme-sync-zh.svg +81 -0
  42. package/assets/screenshots/en/01-overview.png +0 -0
  43. package/assets/screenshots/en/02-heatmap.png +0 -0
  44. package/assets/screenshots/en/03-model-usage.png +0 -0
  45. package/assets/screenshots/en/04-request-logs.png +0 -0
  46. package/assets/screenshots/en/05-cost-settings.png +0 -0
  47. package/assets/screenshots/zh/01-overview.png +0 -0
  48. package/assets/screenshots/zh/02-heatmap.png +0 -0
  49. package/assets/screenshots/zh/03-model-usage.png +0 -0
  50. package/assets/screenshots/zh/04-request-logs.png +0 -0
  51. package/assets/screenshots/zh/05-cost-settings.png +0 -0
  52. package/cordis.patch.yml +5 -5
  53. package/lib/aggregation.js +1850 -1112
  54. package/lib/client.js +1 -1
  55. package/lib/http.js +417 -385
  56. package/lib/icons.js +40 -0
  57. package/lib/ledger.js +1000 -531
  58. package/lib/plugin.js +348 -327
  59. package/lib/pricing-runtime.js +645 -551
  60. package/lib/pricing.js +1488 -1412
  61. package/lib/records-cache.js +113 -0
  62. package/lib/session-sync.js +976 -841
  63. package/lib/usage-core.js +33 -10
  64. package/lib/version-check.js +133 -133
  65. package/package.json +7 -3
  66. package/screenshots.json +10 -3
  67. package/assets/screenshot-1.png +0 -0
  68. package/assets/screenshot-2.png +0 -0
  69. package/assets/screenshot-3.png +0 -0
package/README.md CHANGED
@@ -7,47 +7,86 @@
7
7
  [![stars](https://img.shields.io/github/stars/ParticleLight/dsh-all-usage?label=stars&logo=github)](https://github.com/ParticleLight/dsh-all-usage/stargazers)
8
8
  [![license](https://img.shields.io/github/license/ParticleLight/dsh-all-usage?label=license&color=green)](LICENSE)
9
9
  [![CI](https://github.com/ParticleLight/dsh-all-usage/actions/workflows/ci.yml/badge.svg)](https://github.com/ParticleLight/dsh-all-usage/actions/workflows/ci.yml)
10
- [![DSH](https://img.shields.io/badge/DSH-0.1.1--0.2.0-4D6BFE)](#兼容性与已知限制)
10
+ [![DSH runtime compatibility](https://img.shields.io/badge/DSH%20runtime-9%20verified-4D6BFE)](#兼容性与已知限制)
11
11
  [![status](https://img.shields.io/badge/status-stable-brightgreen)](https://github.com/ParticleLight/dsh-all-usage/releases)
12
12
 
13
- [中文](#中文) · [English](#english)
13
+ [中文](README.md) · [English](README.en.md)
14
14
 
15
- ## 中文
15
+ ## 简介
16
16
 
17
17
  DeepSeek Harness 全量用量看板:按模型、供应商、工作区和时间范围分析 Token、缓存与账户余额。
18
18
 
19
- ### 功能
20
-
21
- - **热力图**:使用热力图可选**最近 30 天 / 90 天 / 12 个月**三种跨度(格子随跨度自适应,跨度越短格子越大);按工作区筛选并查看每日回合与 Token 明细
22
- - **模型统计**:支持混合查看、按模型合并、按供应商汇总三种维度,展示调用次数、各类 Token 与缓存命中率;模型行与筛选下拉显示真实厂商品牌 SVG 图标(未知/混牌保持中性)
23
- - **摘要与工作区**:Token 用量、缓存命中、估算成本、账户余额、连续使用、工作区 Token 分布和明细
24
- - **成本统计 / 价格表**:从 models.dev 同步模型价格;按输入、输出、缓存读取和缓存写入四个桶计算,保存价格快照,明确区分已计价、免费模型和未计价调用。成本设置是一张可编辑的价格表:每行一个账本模型,价格框直接编辑即写入该模型的手工价,「官方模型」列选定映射后该行自动改用官方目录价,并可为每行配置**上下文费率档位**与**分时段(UTC 峰谷)计费**;配置里存在但账本暂无用量的行同样可见、可改、可删。峰谷规则可额外开启「中国法定节假日全天按谷时计价」,节假日日期以显式列表保存在该模型的峰谷计划里(按北京时间 UTC+8 日历日判定,支持 2026-10-01..2026-10-07 区间写法,也可一键「从公开日历载入」当年放假安排后冻结进策略)。内置 DeepSeek 峰谷表已自带 2026 年官方放假安排(随插件版本更新),官方直连或已映射到官方条目的行无需任何配置即按节假日谷价计价;非官方直连的中转行仍按静态价,映射后生效
25
- - **导出**:按当前时间范围和模型聚合方式导出 CSV
26
- - **时间范围**:今日、**近 7 天**、近 30 天、近 90 天、全部,或在全部可扫描历史日数据中自定义起止日期;热力图跨度独立切换(30 天 / 90 天 / 12 个月),与时间范围互不影响
27
- - **工作区别名**:在侧栏入口打开看板后管理,持久化保存到 $DSH_HOME/storages 的 KV 单元 `all_usage_aliases`
28
- - **界面语言**:在看板顶部切换中文与 English;选择会保存到浏览器本地
29
- - **版本指示**:标题旁显示当前运行版本号——与 npm 上最新发布版一致时小字标注「已是最新」,落后时标出「最新 vX.Y.Z」并高亮,点击任意状态都打开 GitHub 仓库;检查由 Host 缓存(默认 6 小时),只有点「刷新统计数据」才会强制重新检查
30
- - **完整历史与增量重建**:基线扫描全部可读历史会话;独立用量账本同时作为每会话游标——未变化的会话直接复用账本,新增事件只增量回填,长历史重启不再全量重建
31
- - **重启免读**:用持久化日志的 revision 作为每会话的变更信号(只读头部行 + stat,不读全量)——日志未变的会话重启时连事件都不读,直接从账本复用;仅日志变化(新增/修改)的会话才做增量读取
32
- - **数据健康与按需刷新**:扫描完成后浏览器只检查轻量状态版本,只有用量、别名或同步状态变化时才拉完整历史;显示本次数据更新时间、历史扫描健康、revision 免读、实际读取、账本恢复和失败,网络异常保留上次成功数据并可重试
33
- - **工作区注册同步**:监听 DSH 的 `domain/changed` 事件自动跟随工作区注册表——注册表一有改动(创建/删除/重命名/重排/归档/成员变化)就重读 `workspaceRegistry.list()`;只对新增/删除的工作区做增量处理,未变化的已有工作区直接复用已计算账本(零重扫)。统计严格限定已注册工作区:未注册 cwd(含存在但未登记目录)一律忽略
34
- - **性能优化**:Host 在 ingest 时维护 local/UTC 的日期、工作区、模型身份日级 cube 与单日小时桶;scope 查询按 bucket 合并,成本使用精确 BigInt 小数累加,53 周热力图只生成实际需要的字段,并继续使用 revision-scoped snapshot/records 缓存和可回收的实时事件队列;Client 将热力图、tooltip、趋势、环形图、请求日志和定价对话框隔离为 memoized 边界,指针坐标通过 ref + requestAnimationFrame 更新,不再触发整页重渲染;浏览器入口在打包前确定性压缩
35
- - **趋势折线图**:按当前范围、时区、工作区、供应商和模型显示输入、缓存读写、输出、推理及总处理量;单日范围按小时聚合并显示小时轴,跨日范围按日聚合;使用平滑单调曲线与入场动画,悬停查看精确值,图例可切换曲线,点击点位进入当日明细
36
- - **统一筛选与审计**:工作区、供应商、模型和日期筛选贯穿摘要、热力图、趋势、表格与 CSV;工作区、供应商、模型三个筛选维度可独立自由组合,工作区、供应商和模型选项只展示当前日期范围内实际使用过的值;切换范围后失效筛选会自动清除;请求日志以紧凑分页表常驻显示,选择单条后查看分组 Token 详情
37
- - **Token 口径**:输入按「未含缓存命中」计,缓存命中 / 写入与推理独立成桶;全 0 用量的重放事件不会覆盖已记录的真实用量,仅缓存命中的请求也会计入
38
- - **成本口径**:模型价格来自 models.dev 的 USD / 1M Token 目录;成本快照按 DSH 已归一化的 fresh input 和四类价格桶计算,倍率只作用于最终总价,已有正成本历史不会因价格更新重算;只按模型选择官方厂商条目,未找到官方价格时显示为未计价。同一官方模型的价格在所有行之间共享:价格框写入的是该模型的手工价条目;价格改动永不重算已有正成本,而峰谷规则改动会按新政策重新对账该模型历史
19
+ 适合希望从 DSH 本地会话中核查 Token 分布、模型成本与工作区用量的用户。
20
+
21
+ ## 快速开始
22
+
23
+ ~~~bash
24
+ dsh plugin --profile web add github:ParticleLight/dsh-all-usage
25
+ ~~~
26
+
27
+ 安装命令会装配 bundle;通常刷新浏览器页面即可加载 Client,无需手动修改 profile。若当前 DSH 版本未动态装配新包,请按 DSH 的提示重载包或重启进程,然后刷新页面并从 DSH 侧栏打开看板。
28
+
29
+ ## 核心功能
30
+
31
+ - **多维用量分析**:以摘要、趋势、热力图和分页请求日志查看历史;可按日期、工作区、供应商和模型组合筛选,并将当前视图导出为 CSV。
32
+ - **模型与工作区洞察**:支持混合模型、按模型合并和按供应商汇总,提供连续使用、工作区分布与每日回合明细;内置 43 个品牌图标,未知模型身份保持中性。
33
+ - **可配置成本与余额**:按四类 Token 桶估算成本,支持官方模型映射、手工价格、上下文档位和峰谷规则,并可查询账户余额;配置细节见下文。
34
+ - **完整历史与增量同步**:首次扫描可读会话,后续按会话账本与持久化 revision 复用未变化数据,只增量读取变化,并跟随 DSH 工作区注册表更新。
35
+ - **可审计的日常操作**:显示扫描健康、实际读取、账本恢复和失败状态;支持中英文界面切换、工作区别名及版本状态提示。
36
+
37
+ ## 截图
38
+
39
+ ![dsh-all-usage 看板总览](assets/screenshots/zh/01-overview.png)
40
+
41
+ ![dsh-all-usage 使用热力图](assets/screenshots/zh/02-heatmap.png)
42
+
43
+ ![dsh-all-usage 模型用量](assets/screenshots/zh/03-model-usage.png)
44
+
45
+ ![dsh-all-usage 请求日志与审计](assets/screenshots/zh/04-request-logs.png)
46
+
47
+ ![dsh-all-usage 成本设置](assets/screenshots/zh/05-cost-settings.png)
48
+
49
+ 以上五张截图于 2026-10-14 从 v1.2.0 客户端界面截取,图中宿主插件版本为 v1.1.18;其中的实际用量与工作区数据属于该截图环境;成本设置中的版本不一致警告为有意保留。
50
+
51
+ ## 技术栈与架构
52
+
53
+ - **Host 端**(入口 `lib/index.js`,组装 `lib/plugin.js`):按职责拆分为 `aggregation.js`(聚合与查询)、`ledger.js`(持久账本)、`session-sync.js`(历史/实时同步)、`pricing-runtime.js`(运行时定价)、`balance.js`(余额)、`http.js`(安全路由);扫描 `turn/end`、`assistant/chunk` usage 和最终 `assistant/message.usage`,监听 `session/event` 实时折叠,并通过 `webServer` 服务注册数据路由。
54
+ - **Client 端**:可读源码位于 `src/client.js`,`npm run build:client` 使用固定版本 Terser 生成 `window.__ModuleLoader__` 工厂格式的 `lib/client.js` 浏览器 bundle,并注册侧边栏「用量统计」入口(`sidebar.footer.action` 槽位)。所有 API 仅接受本机 loopback 请求并拒绝显式跨域请求;余额读取与别名写入还要求插件启动时生成、仅在当前进程有效的令牌(余额 GET 兼容浏览器省略 Origin)。英文模式的日期分桶、范围筛选、连续使用、热力图和导出时间统一按 UTC;中文模式按本地时区。
55
+
56
+ ![用量看板系统架构图](assets/readme-architecture-zh.svg)
57
+
58
+ ### 历史与实时数据流
59
+
60
+ ![历史 revision 复用与实时会话账本写入流程图](assets/readme-sync-zh.svg)
61
+
62
+ ### 存储与性能
63
+
64
+ - **一个会话一条记录**:账本把每个会话写成独立文档(按 session ID 稳定 hash 到 32 个目录),一次更新只发布一个文件。实测同一份数据下,单次更新的落盘成本从「重写整个 13.27 MB 分片 / 273 ms」降到「写一条记录 / 中位 4.6 ms」。
65
+ - **同一路由只存一次身份**:记录里的用量条目只引用身份与价格定义,相同的供应商 / 模型不再逐条重复;实测本机 1059 条记录体积 **91.3 → 74.7 MB(−18%)**
66
+ - **快照只为当前时区买单**:`GET /api/all-usage?utc=0|1` 只返回一份日历(此前两份各占一半载荷),每个日期只保留最近 **180 天**的会话 ID 明细,更早的日期保留数量与聚合
67
+ - **趋势点数封顶 180**:超过半年自动降到 3 天一点,再按需要扩到周 / 月 / 季 / 年,图表不随历史无限变长
68
+ - **预算写进代码**:查询与记录缓存各 20 条、定价解析缓存 5000、每会话上下文归档 512、价格行 500、分片 32、单图点数 180、快照会话 ID 窗口 180 天 —— 由 `test/perf-budgets.test.js` 守着,放开任何一项都必须显式修改那张表
69
+ - **可复现的基准**:`node bench/ledger.mjs`(账本体积与单次重写成本)、`node bench/ledger-layout.mjs`(两种存储布局对比,在真实分片的副本上跑)、`node bench/endpoints.mjs`(5 个读接口的中位延迟与载荷)、`node scripts/dead-code.mjs`(死代码体检:无人引用的导出、客户端里没被调用的函数、样式表里再也渲染不出的 class)
70
+
71
+ ## 安装
72
+
73
+ 本插件是标准的 DSH 社区插件包,声明 `dsh.bundle` manifest 与 Web Client;数据来自持久化会话日志和独立用量账本,安装后自动回填历史。
39
74
 
40
75
  ### 兼容性与已知限制
41
76
 
42
77
  - **运行环境**:需要 Node.js `>=22 <25`;CI 会在 Node 22 和 Node 24 上运行测试、语法检查和 npm 包内容检查。
43
- - **DSH 兼容**:`package.json` 声明 DSH runtime `>=0.1.1-rc.1 <0.1.5-0 || >=0.1.5-rc.1 <0.1.6-0 || >=0.1.7-rc.2 <0.1.8-0 || >=0.2.0-rc.2 <0.2.1-0`,已使用 `0.2.0-rc.2`、`0.1.7-rc.2`、`0.1.5-rc.2`、`0.1.5-rc.1`、`0.1.1-rc.2` 和 `0.1.1-rc.1` 的真实 Cordis 服务链验证,且全部纳入 CI smoke 矩阵(Node 22/24 双档);`0.1.2-rc.1` 已通过实际使用验证兼容,但未纳入 CI smoke 矩阵。声明按元组拆成四段而非写成单一区间,是因为 node-semver 只有在范围里存在与目标版本同 `major.minor.patch` 且自身带预发布标签的比较符时,才会放行该预发布版本 —— 例如必须写成 `>=0.2.0-rc.2` 才能让 `0.2.0-rc.2` 进入范围。
44
- - **桌面客户端(Electron,Windows)**:本插件在 DSH 桌面客户端上实测运行正常(实测 `@deepseek-ai/dsh-desktop` `0.1.7-rc.2`)。桌面客户端会在窗口顶部为自身的最小化 / 最大化 / 关闭按钮保留一条 40px 顶栏,并在文档上标记 `data-windows-titlebar` 与 `--dsh-windows-titlebar-height`;面板据此从该条**下方**开始绘制,所以桌面端自己的关闭按钮永远不会被面板盖住。浏览器端没有该标记,不留白。顶栏高度优先取宿主声明,其次取 Window Controls Overlay 矩形,最后退回 UA;需要微调时可在 DevTools 执行 `localStorage.setItem('dsh-all-usage:topInset', '48')` 后刷新(`0` 表示不留白),无需重新构建。桌面客户端自带运行时,其运行时 `0.1.7-rc.2` 已纳入声明区间与 CI smoke 矩阵(见下表)。
78
+ - **DSH 兼容**:`package.json` 声明 DSH runtime `>=0.1.1-rc.1 <0.1.5-0 || >=0.1.5-rc.1 <0.1.6-0 || >=0.1.7-rc.2 <0.1.8-0 || >=0.2.0-rc.2 <0.2.1-0 || 0.2.1-alpha.2 || 0.2.1-rc.1 || 0.2.1-rc.2`,已在本机用真实 Cordis 服务链验证 `0.2.1-rc.1` / `0.2.1-rc.2`(Node 24 严格 smoke;这两条把 Cordis 提升为稳定版 4.0.5 / loader 1.0.6 / timer 1.1.7)、`0.2.1-alpha.2` 以及 `0.2.0-rc.2`、`0.1.7-rc.2`、`0.1.5-rc.2`、`0.1.5-rc.1`、`0.1.1-rc.2`、`0.1.1-rc.1`;全部已声明版本都进了 CI smoke 矩阵(Node 22/24 双档),但 `0.2.1` 那两条因上游从未发布对应版本的 `@deepseek-ai/dsh-invariants`,CI 会**如实标记 SKIPPED,不等于通过**;`0.1.2-rc.1` 已通过实际使用验证兼容,但未纳入 CI smoke 矩阵。声明按元组逐条拆开而非写成单一区间,是因为 node-semver 只有在范围里存在与目标版本同 `major.minor.patch` 且自身带预发布标签的比较符时,才会放行该预发布版本 —— 例如必须写成 `>=0.2.0-rc.2` 才能让 `0.2.0-rc.2` 进入范围。
79
+ - **桌面客户端(Electron,Windows)**:本插件在 DSH 桌面客户端上实测运行正常(实测 `@deepseek-ai/dsh-desktop` `0.1.7-rc.2`)。桌面客户端会在窗口顶部为自身的最小化 / 最大化 / 关闭按钮保留一条 40px 顶栏,并在文档上标记 `data-windows-titlebar` 与 `--dsh-windows-titlebar-height`;面板据此从该条**下方**开始绘制,所以桌面端自己的关闭按钮永远不会被面板盖住。浏览器端没有该标记,不留白。顶栏高度优先取宿主声明,其次取 Window Controls Overlay 矩形,最后退回 UA;需要微调时可在 DevTools 执行 `localStorage.setItem('dsh-all-usage:topInset', '48')` 后刷新(`0` 表示不留白),无需重新构建。桌面客户端自带运行时,实测版本 `0.1.7-rc.2` 已纳入声明区间与 CI smoke 矩阵(见下表)。
45
80
  - **Web 服务依赖**:Host 将 `webServer` 声明为必需依赖,确保服务晚挂载时由 DSH 等待后再执行插件;该包面向 DSH Web profile,不提供无 WebServer 的 headless 路由。HTTP 守卫还会检查真实 socket peer,反向代理只有在连接本身来自 loopback 时才会被接受。写接口(价格保存、立即同步、节假日抓取、别名)以**进程令牌**为授权凭据,不强制要求 `Origin`:DSH 桌面端把 UI 跑在自定义协议上、请求由 Electron 主进程转发,转发时会删掉 `Origin`(连同 `Host`/`Cookie`/`Sec-Fetch-*`),强制 Origin 会让桌面端所有写入 403;而 `Origin` 若存在仍必须是 loopback 或桌面端自身 scheme,`Host` 仍必须是 loopback(防 DNS rebinding)。
81
+ - **界面语言**:首次进入时面板跟随 DSH 的 **Language 设置**(`Settings → General → Language`,经宿主的 `locale` 服务读取);宿主未提供该服务时退回浏览器语言,最后退回英语。在面板里手动选过语言之后以面板的选择为准,宿主设置不再覆盖它。语言同时决定时区口径——中文按本地时区,英文按 UTC——所以切换语言会重新取一份对应时区的快照。
46
82
 
47
83
  | DSH runtime | Node.js 支持 | 真实 Cordis smoke | 结论 |
48
84
  | --- | --- | --- | --- |
49
- | `0.2.0-rc.2` | `>=22 <25`,CI 覆盖 22/24 | 通过(真实 Cordis 服务链,CI 覆盖 Node 22/24) | 已声明、已验证(DSH 官方 `next` 线;本机 web profile 即运行于此版本) |
50
- | `0.1.7-rc.2` | `>=22 <25`,CI 覆盖 22/24 | 通过(真实 Cordis 服务链,CI 覆盖 Node 22/24) | 已声明、已验证(DSH 桌面客户端即运行于此版本) |
85
+ | `0.2.1-rc.2` | `>=22 <25` | 通过(本机 Node 24 严格真实 Cordis smoke;稳定版 Cordis 4.0.5 / loader 1.0.6 / timer 1.1.7) | 已声明、已验证(仅此精确版本;CI 因上游缺 `dsh-invariants@0.2.1-rc.2` 如实标记 SKIPPED) |
86
+ | `0.2.1-rc.1` | `>=22 <25` | 通过(本机 Node 24 严格真实 Cordis smoke;稳定版 Cordis 4.0.5 / loader 1.0.6 / timer 1.1.7) | 已声明、已验证(仅此精确版本,不代表其他 rc 版本;CI 因上游缺 `dsh-invariants@0.2.1-rc.1` 如实标记 SKIPPED) |
87
+ | `0.2.1-alpha.2` | `>=22 <25` | 通过(本机 Node 24 严格真实 Cordis smoke;CI 因上游缺 `dsh-invariants@0.2.1-alpha.2` 如实标记 SKIPPED) | 已声明、已验证(仅此精确版本,不代表其他 alpha 版本) |
88
+ | `0.2.0-rc.2` | `>=22 <25`,CI 覆盖 22/24 | 通过(真实 Cordis 服务链,CI 覆盖 Node 22/24) | 已声明、已验证(历史验证结果,CI 覆盖 Node 22/24) |
89
+ | `0.1.7-rc.2` | `>=22 <25`,CI 覆盖 22/24 | 通过(真实 Cordis 服务链,CI 覆盖 Node 22/24) | 已声明、已验证(历史验证结果,CI 覆盖 Node 22/24) |
51
90
  | `0.1.5-rc.2` | `>=22 <25`,CI 覆盖 22/24 | 通过(真实 Cordis 服务链,CI 覆盖 Node 22/24) | 已声明、已验证 |
52
91
  | `0.1.5-rc.1` | `>=22 <25`,CI 覆盖 22/24 | 通过(真实 Cordis 服务链,CI 覆盖 Node 22/24) | 已声明、已验证 |
53
92
  | `0.1.2-rc.1` | `>=22 <25` | 通过(实际使用验证,未纳入 CI) | 已实际验证兼容 |
@@ -62,6 +101,32 @@ DeepSeek Harness 全量用量看板:按模型、供应商、工作区和时间
62
101
  - **分层价格**:models.dev 的 tiered/context-dependent 价格按本次请求的输入上下文(fresh input + cache read + cache write)选择对应档位;阈值边界遵循目录定义,无法验证的异常 tier 仍显示为 unsupported。
63
102
  - **工作区边界**:只有 cwd 能映射到 DSH 已注册工作区的会话才进入统计;未注册 cwd(包括已存在但未在 registry 中登记的目录)会被忽略。工作区注册列表通过 DSH 的 `domain/changed` 探针自动同步:注册表一有改动就重读并只对新增/删除的工作区做增量处理,未变化的已有工作区直接复用已计算账本,不会全量重扫。会话尚未成功 flush 前删除或损坏的日志无法由独立账本恢复。工作区被删除时历史用量不会丢失:它会被保留并汇总为一行「已删除」。
64
103
 
104
+ ### 手动注册(本地包)
105
+
106
+ 1. 把本目录放入任意位置,并在 $DSH_HOME/profiles/node_modules/ 下创建指向本目录的符号链接(Windows 用 junction):
107
+
108
+ ~~~powershell
109
+ New-Item -ItemType Junction -Path (Join-Path $env:DSH_HOME 'profiles/node_modules/dsh-all-usage') -Target '<本目录绝对路径>'
110
+ ~~~
111
+
112
+ 2. 在 $DSH_HOME/profiles/web/cordis.patch.yml 添加一行:
113
+
114
+ ~~~yaml
115
+ - insert:
116
+ - id: all-usage
117
+ name: dsh-all-usage
118
+ ~~~
119
+
120
+ 用户 patch 层会被热重载:保存后刷新页面即可。
121
+
122
+ ## 使用示例
123
+
124
+ ### 常见场景
125
+
126
+ - 选择今日、近 7 天、近 30/90 天、全部或自定义日期,再组合工作区、供应商和模型筛选;用趋势与热力图定位变化,打开请求日志检查单条记录并导出 CSV。
127
+ - 核对成本时,先在成本设置中检查模型映射和价格状态,再查看输入、输出、缓存读取与缓存写入的分项估算;未匹配或无法验证的价格会明确显示为未计价。
128
+ - 解释失败或不完整的请求时,查看下面的脱敏事件样例;本地记录表示 DSH 观察到的 usage,不等同于供应商确认收费。
129
+
65
130
  ### 本地统计与官方账单
66
131
 
67
132
  本插件展示的是 DSH 本地事件日志上的可重放统计,不是供应商账单的镜像:
@@ -110,79 +175,7 @@ node scripts/replay-fixture.mjs fixtures/usage-events.json
110
175
 
111
176
  该命令会加载真实插件 Host、调用兼容 API、校验预期 Token/records,并输出不含敏感信息的摘要。
112
177
 
113
- ### 报告问题
114
-
115
- - [数据不一致 / Data inconsistency](.github/ISSUE_TEMPLATE/data-inconsistency.md)
116
- - [插件启动失败 / Plugin startup failure](.github/ISSUE_TEMPLATE/startup-failure.md)
117
- - [成本计算问题 / Cost calculation issue](.github/ISSUE_TEMPLATE/cost-calculation.md)
118
-
119
- ### 最近更新
120
-
121
- **v1.1.18**
122
-
123
- - **新增「近 7 天」时间范围**:预设行变为 今日 / **近 7 天** / 近 30 天 / 近 90 天 / 全部 / 自定义;口径是含今天的 7 个日历日,摘要、趋势、模型表、请求日志、筛选与 CSV 导出同步生效,选择会记在浏览器里。
124
- - **热力图跨度可切换(30 天 / 90 天 / 12 个月)**:列数随跨度变化(5 / 13 / 53 列),每列一周、格子保持 1:1,所以跨度越短格子越大(实测 12 个月 18×18px、30/90 天 34×34px,并做了上限避免少数几列被拉成巨块);默认仍是 12 个月,与上方时间范围互不影响。
125
- - **标题旁新增版本指示**:显示当前运行版本,与 npm 最新版一致时小字「已是最新」,落后时高亮「最新 vX.Y.Z」,点击打开 GitHub 仓库;检查由宿主发起(只读一次 npm registry,缓存 6 小时,同一进程 30 秒内不重复请求),拿不到结论时降级为「未知」而不是猜。
126
-
127
- 完整版本记录见 [CHANGELOG.md](CHANGELOG.md)。
128
-
129
- ### 截图 / Screenshots
130
-
131
- ![dsh-all-usage 看板总览 / Dashboard overview](assets/screenshot-1.png)
132
-
133
- ![dsh-all-usage 成本统计设置 / Cost statistics settings](assets/screenshot-2.png)
134
-
135
- ![dsh-all-usage 请求日志与审计 / Request logs and audit](assets/screenshot-3.png)
136
-
137
- ### 安装
138
-
139
- 本插件是标准的 DSH 社区插件包(声明 `dsh.bundle` manifest 与 Web Client),数据来自持久化会话日志和独立用量账本,安装后自动回填历史。
140
-
141
- #### 官方插件命令(推荐)
142
-
143
- ~~~bash
144
- dsh plugin --profile web add github:ParticleLight/dsh-all-usage
145
- ~~~
146
-
147
- 安装命令会装配 bundle;随后刷新浏览器页面以加载 Client,无需手动修改 profile。若当前 DSH 版本未动态装配新包,请按 DSH 的提示重载包或重启进程。
148
-
149
- #### 手动注册(本地包)
150
-
151
- 1. 把本目录放入任意位置,并在 $DSH_HOME/profiles/node_modules/ 下创建指向本目录的符号链接(Windows 用 junction):
152
-
153
- ~~~powershell
154
- New-Item -ItemType Junction -Path (Join-Path $env:DSH_HOME 'profiles/node_modules/dsh-all-usage') -Target '<本目录绝对路径>'
155
- ~~~
156
-
157
- 2. 在 $DSH_HOME/profiles/web/cordis.patch.yml 添加一行:
158
-
159
- ~~~yaml
160
- - insert:
161
- - id: all-usage
162
- name: dsh-all-usage
163
- ~~~
164
-
165
- 用户 patch 层会被热重载:保存后刷新页面即可。
166
-
167
- ### 架构
168
-
169
- - **Host 端**(入口 `lib/index.js`,组装 `lib/plugin.js`):按职责拆分为 `aggregation.js`(聚合与查询)、`ledger.js`(持久账本)、`session-sync.js`(历史/实时同步)、`pricing-runtime.js`(运行时定价)、`balance.js`(余额)、`http.js`(安全路由);扫描 `turn/end`、`assistant/chunk` usage 和最终 `assistant/message.usage`,监听 `session/event` 实时折叠,并通过 `webServer` 服务注册数据路由:
170
- - `GET /api/all-usage` — 兼容统计快照
171
- - `GET /api/all-usage/status` — 轻量 revision 与同步健康状态,并附带最近一次客户端环境报告(`clientEnv`)、运行中插件的版本号(`pluginVersion`)与写入令牌的派生 ID(`capabilityId`,令牌轮换时变化,用于让页面察觉宿主被重新加载;令牌本身只在完整快照里下发)
172
- - `GET /api/all-usage/query` — 按 scope 返回聚合、daily/hourly 趋势和 heatmap 数据;单日 scope 填充 `hourly`,跨日 scope 的 `hourly` 为空
173
- - `GET /api/all-usage/records` — 按 scope 分页返回脱敏 canonical usage rows
174
- - `GET /api/all-usage/version?force=1` — 版本检查:返回运行中版本与 npm 上最新版本、比较结论(`latest` / `outdated` / `unknown`)、检查时间与来源;Host 侧缓存 6 小时,同一进程内对上游的请求最多每 30 秒一次(`force=1` 也受此下限约束),`force=1` 来自刷新按钮
175
- - `GET /api/all-usage/balance?force=1` — 账户余额(复用 `llm-deepseek` 的 API Key 配置)
176
- - `POST /api/all-usage/alias` — 设置工作区别名
177
- - `GET /api/all-usage/pricing` — 读取可编辑价格表所需的完整配置:逐模型的生效费率与状态、映射、手工价、峰谷计划(含内置表来源与完整规则)以及仅存在于配置中的行
178
- - `GET /api/all-usage/pricing/models?q=...` — 检索官方模型 ID 与名称匹配结果(含预览费率、是否分层计费、是否有内置峰谷表)
179
- - `POST /api/all-usage/pricing` — 保存同步、mapping、手工价,以及上下文费率档位与峰谷时段规则(请求形状不变)
180
- - `POST /api/all-usage/pricing/sync` — 手动同步 models.dev 并回填未计价调用
181
- - `POST /api/all-usage/pricing/holidays` — 按年份抓取中国法定节假日安排(holiday-cn 数据集,jsDelivr 主源、GitHub raw 兜底,主机侧缓存 24 小时),只返回放假日日期列表与来源;**不写入任何配置**,由客户端冻结进峰谷计划
182
- - `GET /api/all-usage/client-env` — 客户端环境诊断上报(UA、窗口控件遮罩高度、预留顶栏值**及其来源**、视口、面板矩形);客户端在加载与打开面板时各上报一次,只在内存保留
183
- - **Client 端**:可读源码位于 `src/client.js`,`npm run build:client` 使用固定版本 Terser 生成 `window.__ModuleLoader__` 工厂格式的 `lib/client.js` 浏览器 bundle,并注册侧边栏「用量统计」入口(`sidebar.footer.action` 槽位)。所有 API 仅接受本机 loopback 请求并拒绝显式跨域请求;余额读取与别名写入还要求插件启动时生成、仅在当前进程有效的令牌(余额 GET 兼容浏览器省略 Origin)。英文模式的日期分桶、范围筛选、连续使用、热力图和导出时间统一按 UTC;中文模式按本地时区。
184
-
185
- ### 数据说明
178
+ ### 数据口径、隐私与限制
186
179
 
187
180
  - 使用次数与 Token 来自 DSH 会话日志;`session/flush` 只在存在新的相关事件时重建并将派生账本写入异步队列,同一 session 的 pending record 会合并,插件退出时 drain;插件激活时会回填日志与账本历史,插件卸载/重启后已成功持久化的数据不丢
188
181
  - 按日范围统计会保留全部可读取历史会话的有使用记录日期;热力图是可切换跨度的视图窗口(最近 30 天 / 90 天 / 12 个月,默认 12 个月)
@@ -190,7 +183,7 @@ dsh plugin --profile web add github:ParticleLight/dsh-all-usage
190
183
  - 同一会话的同一 `turn / step` 只保留一份最终 usage;重试或替换消息会替换旧贡献,不重复累计
191
184
  - 输入 Token 按「未含缓存命中」计(缓存命中 / 写入独立成桶);全 0 用量的重放事件不会覆盖已记录的真实用量,纯缓存命中的请求仍会计入
192
185
  - 版本检查会向 `registry.npmjs.org` 发一次只读 GET(仅读取 `dsh-all-usage` 的版本号,无请求体、无 Cookie;由 Host 发起并缓存 6 小时,页面自身不联网),失败时降级为「未知」而非猜测
193
- - 轻量状态接口只公开 Host 实例、统计 revision、扫描进度、同步计数与最近一次客户端环境报告(UA、视口、预留顶栏值及其来源、面板矩形——由本机客户端上报且只存内存),不公开会话 ID、工作区路径、提示词或回复正文;完整快照仅在状态变化或手动刷新时获取
186
+ - 轻量状态接口公开 Host 实例、统计 revision、扫描进度、同步计数、`memory` 常驻计数、`counters` 诊断操作计数、`accounting` 当日聚合一致性、`mismatch` 首个桶增量不一致样本(无则为 null),以及最近一次客户端环境报告(UA、视口、预留顶栏值及其来源、面板矩形——由本机客户端上报且只存内存);`mismatch` 样本含内部用量 key 与工作区 ID(用量 key 由会话 ID 派生,可指向某一会话),但不含提示词或回复正文,也不含工作区路径或凭据;完整快照仅在状态变化或手动刷新时获取
194
187
  - scope query 将回合(turns)、模型调用(calls)和去重会话(sessions)分开统计;Provider/模型筛选缺少路由信息时明确归为 Unknown,不从展示字符串猜测
195
188
  - records 接口只返回短 hash、时间、工作区 ID、结构化模型身份、turn/step、Token buckets 和当前物化来源,不返回原始 session ID、路径、提示词、回复或凭据
196
189
  - 看板中的总处理量 = 输入 + 输出 + 缓存读写 + 推理;缓存命中表示复用的上下文 Token,不等于新生成 Token 或实际费用
@@ -201,206 +194,79 @@ dsh plugin --profile web add github:ParticleLight/dsh-all-usage
201
194
  - Mapping 语义:带 `identityKey` 的 mapping 只对精确路由身份生效;不带身份键的 mapping 才按模型做全局回退;旧配置中的 `usageIdentityKey` 会在加载时归一化。
202
195
  - 节假日日历只在用户点击「从公开日历载入」时抓取:目标主机是 `cdn.jsdelivr.net`(失败时 `raw.githubusercontent.com`),请求里只有年份,**不发送任何用量、会话或凭据数据**;返回的日期串在用户保存前不会生效,保存后连同来源说明一起冻结进该模型的峰谷计划(来源不参与政策哈希)
203
196
  - 余额查询走 DeepSeek 官方 `/user/balance` 接口;未配置 API Key 时卡片显示引导文案
204
- - 账本按 session ID 稳定 hash 到 32 个 JSON shard,单次 flush 只重写对应 shard;旧的 `all_usage_ledger.json` 会在首次加载时迁移,异步写失败或退出前未落盘不会丢失内存统计,只会让下次启动重新扫描
197
+ - 账本按 session ID 稳定 hash 到 32 个分片目录,**每个会话一条记录文件**:一次 flush 只发布这一个文件,不再重写整个分片;旧的整体式分片文件(含最初的 `all_usage_ledger.json`)在记录树确认已写入后会被清理,`DSH_ALL_USAGE_KEEP_LEGACY=1` 可保留它们。异步写失败或退出前未落盘不会丢失内存统计,只会让下次启动重新扫描
205
198
  - 仅统计能归属到已注册工作区(按会话 cwd 匹配)的会话
206
199
 
207
- ### 开发
200
+ ## 配置
208
201
 
209
- - 修改 `src/client.js` 后先运行 `npm run build:client`,再让 DSH 重载客户端模块并刷新页面;`lib/client.js` 是生成产物,不直接编辑。修改 `lib/plugin.js` 或其他 Host 模块后,需由 DSH 重载该包或重启进程
210
- - 插件无第三方运行时依赖:Host 端只使用 Cordis 服务,Client 端只使用 runtime 提供的 React 模块;Terser 仅作为固定版本开发依赖生成浏览器产物
211
- - 手动恢复 npm 发布时,GitHub Actions 要求输入目标 `v<package.version>` tag 和完整 commit SHA,并在 checkout 后校验 tag、SHA 与包版本一致;Release 事件同样执行 commit 校验。
212
-
213
- ## English
214
-
215
- A full usage dashboard for DeepSeek Harness. Analyze tokens, cache behavior, estimated cost, account balance, and activity by model, provider, workspace, and time range.
216
-
217
- ### Features
218
-
219
- - **Heatmap**: an activity heatmap with a switchable span (**last 30 days / 90 days / 12 months**; shorter spans get larger cells), workspace filters and daily turn/token details
220
- - **Model analytics**: mixed view, model-merged view, and provider summary with calls, token categories, and cache hit rate; model rows and the model filter dropdown render vendor brand SVG icons (neutral for unknown or mixed brands)
221
- - **Summary and workspaces**: processed tokens, cache hits, estimated cost, account balance, usage streaks, workspace distribution, and details
222
- - **Cost statistics / price table**: sync model prices from models.dev, calculate four cost buckets, persist price snapshots, and distinguish priced, free, ambiguous, and unpriced calls. Cost Settings is one editable price table: each row is a ledger model, its price boxes write that model's manual price, the official-model column switches the row to the catalog price, and every row can carry context rate bands and time-of-day (UTC peak/off-peak) rules, including an optional "Chinese statutory holidays price as off-peak all day" switch whose explicit date list lives in that model's peak plan (China Standard Time calendar days, 2026-10-01..2026-10-07 ranges supported, and one click loads the year's arrangement from a public calendar before it is frozen into the policy); rows that exist only in the saved configuration stay visible, editable, and removable
223
- - **CSV export**: export data using the selected time range and aggregation mode
224
- - **Time ranges**: today, **last 7 days**, last 30 days, last 90 days, all time, or a custom start/end date across all available historical daily data; the heatmap span switches independently (30 days / 90 days / 12 months)
225
- - **Workspace aliases**: manage aliases from the sidebar dashboard; values persist in the $DSH_HOME/storages KV cell `all_usage_aliases`
226
- - **Interface language**: switch between Chinese and English from the dashboard header; your choice persists locally in the browser
227
- - **Full history & incremental rebuild**: the baseline scans every readable historical session; the durable usage ledger doubles as a per-session cursor, so unchanged sessions are reused straight from the ledger and only newly appended events are folded — long histories restart without a full rebuild
228
- - **Restart with no re-read**: the persisted log revision (a header-line + stat via `sessionPersistence.listSnapshots()`) acts as a per-session change signal — sessions whose log is unchanged are applied from the ledger on restart without reading their events at all; only changed/new sessions are read incrementally
229
- - **Workspace registry sync**: follows DSH's `domain/changed` event so the workspace registry stays fresh automatically — any durable registry write (create/delete/rename/reorder/archive/membership) triggers a reread of `workspaceRegistry.list()`, with only added/removed workspaces reprocessed incrementally while unchanged workspaces reuse their computed aggregates and ledger (zero rescan). Usage is strictly limited to registered workspaces: unregistered cwds, including existing directories absent from the registry, are ignored.
230
- - **Data health and on-demand refresh**: after a scan completes, the browser polls only a lightweight status revision and fetches full history only after usage, alias, or sync state changes; it shows the latest full-data update, historical scan health, revision skips, rereads, ledger recovery, and failures while preserving last-good data on network errors
231
- - **Performance**: Host maintains ingest-time local/UTC day, workspace, model-identity cubes and single-day hour buckets; scope queries merge buckets, exact costs use BigInt decimal accumulators, and the 53-week heatmap emits only the fields it consumes, while revision-scoped snapshot/records caches and recyclable live-event queues remain in place. Client isolates the heatmap, tooltip, trend, donuts, request records, and pricing dialog behind memoized boundaries; pointer coordinates update through refs plus requestAnimationFrame instead of rerendering the page, and the browser entry is deterministically minified before packing
232
- - **Trend line chart**: show input, cache read/write, output, reasoning, and total processed tokens for the active range, timezone, workspace, provider, and model scope; use hourly buckets for a single-day scope and daily buckets for cross-day scopes, with smooth monotone curves, staged entrance animation, hover for exact values, and click a point to inspect that day
233
- - **Version indicator**: the header shows the running version next to the title — “Up to date” in small text when it matches the newest release on npm, “Latest vX.Y.Z” highlighted when it is behind, and a click opens the GitHub repository; the host caches the check (6 hours by default) and only the refresh button forces a new one
234
- - **Unified filters and audit**: workspace, provider, model, and date filters apply to the summary, heatmap, trend, tables, and CSV; workspace, provider, and model filters remain independent and can be combined freely, while workspace, provider, and model options are limited to values used in the selected date range and stale selections clear automatically; request logs stay visible as a compact paginated table with grouped Token details for the selected row
235
- - **Token accounting semantics**: input tokens are fresh (exclude cache hits/writes, which sit in separate buckets along with reasoning); all-zero usage replays never overwrite recorded usage, while cache-only requests still count
236
- - **Cost semantics**: prices come from the models.dev USD per 1M token catalog; DSH-normalized fresh input and the four cost buckets are snapshotted at calculation time, the multiplier applies only to final total, and existing positive historical costs are not recalculated; matching uses the model's official vendor entry and ignores the DSH provider, while missing official prices stay unpriced
237
-
238
- ### Compatibility and Known Limitations
239
-
240
- - **Runtime**: Node.js `>=22 <25` is required. CI runs the test suite, syntax checks, and package-content checks on Node 22 and Node 24.
241
- - **DSH compatibility**: `package.json` declares DSH runtime `>=0.1.1-rc.1 <0.1.5-0 || >=0.1.5-rc.1 <0.1.6-0 || >=0.1.7-rc.2 <0.1.8-0 || >=0.2.0-rc.2 <0.2.1-0`; the real Cordis service chain is verified on `0.2.0-rc.2`, `0.1.7-rc.2`, `0.1.5-rc.2`, `0.1.5-rc.1`, `0.1.1-rc.2` and `0.1.1-rc.1`, all of them covered by the CI smoke matrix on Node 22 and 24. `0.1.2-rc.1` has also been verified compatible through real-world use, but is not covered by the CI smoke matrix. The declaration is split per tuple rather than written as one interval because node-semver only admits a prerelease version when some comparator shares its exact `major.minor.patch` tuple and itself carries a prerelease tag — `0.2.0-rc.2` is admitted only because the range says `>=0.2.0-rc.2`.
242
- - **Desktop client (Electron, Windows)**: the plugin is verified running in the DSH desktop client (`@deepseek-ai/dsh-desktop` `0.1.7-rc.2` as measured). The desktop client keeps a 40px caption strip across the top of its window for its own minimise / maximise / close buttons and marks the document with `data-windows-titlebar` and `--dsh-windows-titlebar-height`; the panel draws from **below** that strip, so the client's own close button is never covered. Browsers carry no such marker and reserve nothing. The strip height comes from the host's declaration first, then the Window Controls Overlay rectangle, then the user agent; to tune it, run `localStorage.setItem('dsh-all-usage:topInset', '48')` in DevTools and reload (`0` reserves nothing) — no rebuild required. The desktop client ships its own runtime, and that runtime — `0.1.7-rc.2` — is now part of the declared range and of the CI smoke matrix below.
243
- - **Web service dependency**: the Host declares `webServer` as a required dependency, so DSH waits for a late-mounted service before applying the plugin; this package targets the DSH Web profile and does not expose routes without WebServer. The HTTP guard also checks the actual socket peer, so a reverse proxy is accepted only when the connection itself is loopback.
244
-
245
- | DSH runtime | Node.js support | Real Cordis smoke | Conclusion |
246
- | --- | --- | --- | --- |
247
- | `0.2.0-rc.2` | `>=22 <25`, CI covers 22/24 | Passed (real Cordis service chain, CI covers Node 22/24) | Declared and verified (the current DSH `next` line; this machine's web profile runs it) |
248
- | `0.1.7-rc.2` | `>=22 <25`, CI covers 22/24 | Passed (real Cordis service chain, CI covers Node 22/24) | Declared and verified (the DSH desktop client runs this version) |
249
- | `0.1.5-rc.2` | `>=22 <25`, CI covers 22/24 | Passed (real Cordis service chain, CI covers Node 22/24) | Declared and verified |
250
- | `0.1.5-rc.1` | `>=22 <25`, CI covers 22/24 | Passed (real Cordis service chain, CI covers Node 22/24) | Declared and verified |
251
- | `0.1.2-rc.1` | `>=22 <25` | Passed through real-world use (not in CI) | Verified compatible in real-world use |
252
- | `0.1.1-rc.2` | `>=22 <25`, CI covers 22/24 | Passed (real Cordis service chain, CI covers Node 22/24) | Declared and verified |
253
- | `0.1.1-rc.1` | `>=22 <25`, CI covers 22/24 | Passed (real Cordis service chain, CI covers Node 22/24) | Declared and verified |
254
- | Other versions | `>=22 <25` | Not tested | Outside the verified matrix |
255
-
256
- An unlisted DSH version is not necessarily incompatible. Include the DSH, Node.js, and plugin versions when reporting an issue.
257
-
258
- - **Interrupted requests**: an interrupted upstream request may emit only `assistant/chunk` usage and never produce a final `assistant/message`; that chunk is retained. A later final message for the same turn/step replaces it. If the upstream emits no usage event at all, exact token usage cannot be reconstructed from response text.
259
- - **Estimated cost**: cost is an estimate based on models.dev rates and DSH usage buckets, not a provider invoice. Unavailable catalogs and unmatched models remain unpriced instead of receiving guessed rates. Cache reads, cache writes, and reasoning follow the buckets reported by the upstream DSH event.
260
- - **Tiered prices**: models.dev context-tiered entries select the applicable rate from the request input context (fresh input plus cache read/write tokens); malformed schedules remain unsupported.
261
- - **Workspace boundary**: only sessions whose cwd maps to a DSH-registered workspace are included. Unregistered cwds, including existing directories absent from the registry, are ignored. The registry is kept fresh by a `domain/changed` probe: any durable workspace-domain write triggers a reread, and only added/removed workspaces are reprocessed incrementally — unchanged workspaces reuse their computed aggregates and ledger. Data deleted or corrupted before a successful session flush cannot be recovered from the separate ledger.
202
+ | 配置项 | 位置 | 说明 |
203
+ | --- | --- | --- |
204
+ | 模型价格与计费规则 | 看板「成本设置」 | models.dev 同步默认关闭;可映射官方模型、设置手工价、上下文费率档位、UTC 峰谷时段和中国法定节假日谷价。详细公式与边界见「数据口径、隐私与限制」。 |
205
+ | 工作区别名 | DSH 侧栏入口;存于 `$DSH_HOME/storages` 的 KV 单元 `all_usage_aliases` | 以别名替代工作区显示名称。 |
206
+ | 范围与明细偏好 | 浏览器本地存储 | 当前看板范围与明细视图保存在本地。 |
207
+ | Electron 顶栏留白 | 浏览器 `localStorage` 键 `dsh-all-usage:topInset` | 可用像素数微调;`0` 表示不留白,修改后刷新即可。 |
208
+ | 保留旧账本分片 | Host 环境变量 `DSH_ALL_USAGE_KEEP_LEGACY=1` | 默认在逐会话记录树确认写入后清理旧整体分片;设为 `1` 可保留。 |
209
+ | 常驻用量明细行预算 | Host 环境变量 `DSH_ALL_USAGE_RESIDENT_ROWS` | 单位为「行」,默认 12000;有限数字向下取整(`Math.floor`)。负数表示全部明细行常驻内存;设为 0 时在扫描结束后交还所有可交还的行;非数字值回退默认。这是软预算:定价未定稿的行,以及正在写入 / 读取 / 重同步 / 预取的会话的行不会被交还。 |
210
+ | 会话记录缓存上限 | Host 环境变量 `DSH_ALL_USAGE_RECORD_CACHE_MB` | 单位为 MiB,默认 16。只接受 ≥ 0 的有限数字,负数或非数字一律回退 16;设为 0 时只逐出**可逐出的干净记录**、至少保留最近一条,所以受保护的记录可以让实际常驻超过预算。这是软预算:脏 / 待写 / 写入中 / 重同步 / 预取 / 读取中的记录不会被逐出;账本存储不支持单条读回时一条都不逐出,记录全部常驻。占用由用量行数估算(不是实测分配),见 status 的 `recordCacheMB`。 |
262
211
 
263
- ### Local Statistics vs Official Billing
212
+ ## API 与 CLI
264
213
 
265
- This plugin reports replayable statistics from local DSH event logs; it is not a mirror of a provider invoice:
214
+ ### CLI
266
215
 
267
- - Local statistics read DSH `assistant/chunk`, final `assistant/message`, and related session events, then deduplicate and replace samples by logical `turn / step`. Official billing may use a provider tokenizer, rounding rules, discounts, free quotas, and billing periods.
268
- - A failed request is included locally whenever it leaves a usage chunk; whether the provider charged for that failed request must be checked against the official bill.
269
- - Prices come from the public models.dev catalog and local explicit overrides. Catalog prices can differ from provider prices, regional rates, and invoice discounts, so the cost field is an estimate. A price belongs to the model, so every row priced from the same official model shares one manual entry; price edits never rewrite existing positive costs, while a peak-plan change reconciles that model's history against the new policy.
270
- - Local statistics include registered workspaces only; old ledger rows for unregistered cwds are excluded. Usage recorded for a **deleted workspace** is kept and summed into one "Deleted" row, so removing a workspace never shrinks historical totals. Totals can still be lower than the official bill when logs are damaged, cleaned up, or the upstream emits no usage event.
216
+ 插件没有独立的运行时 CLI;官方安装命令见「快速开始」。开发和测试脚本见下文。
271
217
 
272
- ### Reproducible Event Examples
218
+ ### HTTP API
273
219
 
274
- The following are redacted minimal examples; the complete runnable data is in [`fixtures/usage-events.json`](fixtures/usage-events.json).
220
+ - `GET /api/all-usage` — 兼容统计快照
221
+ - `GET /api/all-usage/status` — 轻量 revision 与同步健康状态,并附带最近一次客户端环境报告(`clientEnv`)、运行中插件的版本号(`pluginVersion`)与写入令牌的派生 ID(`capabilityId`,令牌轮换时变化,用于让页面察觉宿主被重新加载;令牌本身只在完整快照里下发),以及 `memory` 常驻计数:`residentRows`(常驻用量明细行数)、`residentRecords`(常驻会话记录数)、`recordCacheMB`(会话记录缓存占用估算,MiB,一位小数;由用量行数推算,不是实测分配)、`sessions`(会话聚合数)、`recordsCache`(记录查询缓存统计)与 `heapUsedMB` / `rssMB`(均为整个 Host 进程的堆与 RSS,不是插件单独占用);同一响应还带 `counters`(诊断操作计数)、`accounting`(当日聚合一致性)与 `mismatch`(首个桶增量不一致样本,无则为 null)
222
+ - `GET /api/all-usage/query` — 按 scope 返回聚合、daily/hourly 趋势和 heatmap 数据;单日 scope 填充 `hourly`,跨日 scope 的 `hourly` 为空
223
+ - `GET /api/all-usage/records` — 按 scope 分页返回脱敏 canonical usage rows
224
+ - `GET /api/all-usage/icon?key=<品牌 key>` — 按需返回一个品牌图标(SVG,长缓存 + ETag);客户端 bundle 只携带解析元数据,图标本身由宿主按需发送,所以新增品牌不再增加 bundle 体积
225
+ - `GET /api/all-usage/version?force=1` — 版本检查:返回运行中版本与 npm 上最新版本、比较结论(`latest` / `outdated` / `unknown`)、检查时间与来源;Host 侧缓存 6 小时,同一进程内对上游的请求最多每 30 秒一次(`force=1` 也受此下限约束),`force=1` 来自刷新按钮
226
+ - `GET /api/all-usage/balance?force=1` — 账户余额(复用 `llm-deepseek` 的 API Key 配置)
227
+ - `POST /api/all-usage/alias` — 设置工作区别名
228
+ - `GET /api/all-usage/pricing` — 读取可编辑价格表所需的完整配置:逐模型的生效费率与状态、映射、手工价、峰谷计划(含内置表来源与完整规则)以及仅存在于配置中的行
229
+ - `GET /api/all-usage/pricing/models?q=...` — 检索官方模型 ID 与名称匹配结果(含预览费率、是否分层计费、是否有内置峰谷表)
230
+ - `POST /api/all-usage/pricing` — 保存同步、mapping、手工价,以及上下文费率档位与峰谷时段规则(请求形状不变)
231
+ - `POST /api/all-usage/pricing/sync` — 手动同步 models.dev 并回填未计价调用
232
+ - `POST /api/all-usage/pricing/holidays` — 按年份抓取中国法定节假日安排(holiday-cn 数据集,jsDelivr 主源、GitHub raw 兜底,主机侧缓存 24 小时),只返回放假日日期列表与来源;**不写入任何配置**,由客户端冻结进峰谷计划
233
+ - `GET /api/all-usage/client-env` — 客户端环境诊断上报(UA、窗口控件遮罩高度、预留顶栏值**及其来源**、视口、面板矩形);客户端在加载与打开面板时各上报一次,只在内存保留
275
234
 
276
- #### Retaining a failed request chunk
235
+ ## 开发与测试
277
236
 
278
- ~~~json
279
- [
280
- {"type": "assistant/chunk", "data": {"turn": 1, "step": 1, "chunk": {"type": "usage", "usage": {"inputTokens": 100, "outputTokens": 20}}}},
281
- {"type": "request/error", "data": {"code": "upstream-failed"}}
282
- ]
283
- ~~~
284
-
285
- Without a final `assistant/message`, the chunk remains one local call; this does not mean the provider necessarily charged for it.
286
-
287
- #### Orphan usage chunk
288
-
289
- ~~~json
290
- {"type": "assistant/chunk", "data": {"turn": 1, "step": 1, "chunk": {"type": "usage", "usage": {"inputTokens": 7, "cacheReadTokens": 8}}}}
291
- ~~~
292
-
293
- Without request/context or request/header, tokens are still counted, but the model identity is shown as Unknown; the plugin does not guess a model from a provider name or display string.
294
-
295
- #### Cache token buckets
296
-
297
- ~~~json
298
- {"inputTokens": 100, "outputTokens": 20, "cacheReadTokens": 40, "cacheWriteTokens": 5, "reasoningTokens": 3}
299
- ~~~
300
-
301
- The local processed total is `100 + 20 + 40 + 5 + 3 = 168`; cost uses the input, output, cacheRead, and cacheWrite buckets, and reasoning is not added to output again.
237
+ - 修改 `src/client.js` 后先运行 `npm run build:client`,再让 DSH 重载客户端模块并刷新页面;`lib/client.js` 是生成产物,不直接编辑。修改 `lib/plugin.js` 或其他 Host 模块后,需由 DSH 重载该包或重启进程
238
+ - 插件无第三方运行时依赖:Host 端只使用 Cordis 服务,Client 端只使用 runtime 提供的 React 模块;Terser 仅作为固定版本开发依赖生成浏览器产物
239
+ - 手动恢复 npm 发布时,GitHub Actions 要求输入目标 `v<package.version>` tag 和完整 commit SHA,并在 checkout 后校验 tag、SHA 与包版本一致;Release 事件同样执行 commit 校验。
302
240
 
303
- Replay the repository fixture:
241
+ ### 本地门禁
304
242
 
305
243
  ~~~bash
306
- node scripts/replay-fixture.mjs fixtures/usage-events.json
244
+ npm test
245
+ npm run fixture:check
246
+ npm run build:client
247
+ npm run check:pack
307
248
  ~~~
308
249
 
309
- The command loads the real plugin Host, calls its compatible APIs, checks the documented token/record totals, and prints a non-sensitive summary.
310
-
311
- ### Report An Issue
312
-
313
- - [Data inconsistency / 数据不一致](.github/ISSUE_TEMPLATE/data-inconsistency.md)
314
- - [Plugin startup failure / 插件启动失败](.github/ISSUE_TEMPLATE/startup-failure.md)
315
- - [Cost calculation issue / 成本计算问题](.github/ISSUE_TEMPLATE/cost-calculation.md)
316
-
317
- ### Latest Update
318
-
319
- **v1.1.18**
320
-
321
- - **A new “Last 7 Days” range**: the presets are now Today / **Last 7 Days** / Last 30 Days / Last 90 Days / All Time / Custom; it covers seven calendar days including today and applies to the summary, trend, model tables, request log, filters and CSV export, with the choice remembered in the browser.
322
- - **A switchable heatmap span (30 days / 90 days / 12 months)**: the column count follows the span (5 / 13 / 53 columns); each column is a week and the cells keep a 1:1 aspect ratio, so a shorter span also gets larger cells (18×18px at twelve months, 34×34px at 30/90 days, capped so a few columns cannot stretch into huge tiles). Twelve months stays the default and the span is independent of the time range above.
323
- - **A version indicator next to the title**: it shows the running version, “Up to date” in small text when it matches the newest release on npm, a highlighted “Latest vX.Y.Z” when it is behind, and a click opens the GitHub repository. The host performs the check (one read-only request to the npm registry, cached for six hours, at most one every 30 seconds per process) and degrades to “unknown” rather than guessing.
324
-
325
- See [CHANGELOG.md](CHANGELOG.md) for the complete version history.
250
+ ## 状态与更新
326
251
 
327
- ### Installation
252
+ 当前最新发布版本为 `v1.1.18`;`v1.2.0` 仍在开发中,尚未发布。完整开发与版本记录见 [CHANGELOG.md](CHANGELOG.md)。
328
253
 
329
- This is a standard DSH community bundle. It declares a `dsh.bundle` manifest and a web client, and backfills its data from persisted session logs and the durable usage ledger after installation.
254
+ ### 最近发布:v1.1.18
330
255
 
331
- #### Official plugin command (recommended)
332
-
333
- ~~~bash
334
- dsh plugin --profile web add github:ParticleLight/dsh-all-usage
335
- ~~~
336
-
337
- The installation command assembles the bundle. Refresh the browser page to load the client; no manual profile edits are required. If your DSH version does not dynamically assemble newly installed packages, use its supported package reload or restart the process.
338
-
339
- #### Manual local registration
256
+ - **新增「近 7 天」时间范围**:预设行变为 今日 / **近 7 天** / 近 30 天 / 近 90 天 / 全部 / 自定义;口径是含今天的 7 个日历日,摘要、趋势、模型表、请求日志、筛选与 CSV 导出同步生效,选择会记在浏览器里。
257
+ - **热力图跨度可切换(30 天 / 90 天 / 12 个月)**:列数随跨度变化(5 / 13 / 53 列),每列一周、格子保持 1:1,所以跨度越短格子越大(实测 12 个月 18×18px、30/90 天 34×34px,并做了上限避免少数几列被拉成巨块);默认仍是 12 个月,与上方时间范围互不影响。
258
+ - **标题旁新增版本指示**:显示当前运行版本,与 npm 最新版一致时小字「已是最新」,落后时高亮「最新 vX.Y.Z」,点击打开 GitHub 仓库;检查由宿主发起(只读一次 npm registry,缓存 6 小时,同一进程 30 秒内不重复请求),拿不到结论时降级为「未知」而不是猜。
340
259
 
341
- 1. Place this directory anywhere and create a symlink to it under $DSH_HOME/profiles/node_modules/ (use a junction on Windows):
260
+ 完整版本记录见 [CHANGELOG.md](CHANGELOG.md)。
342
261
 
343
- ~~~powershell
344
- New-Item -ItemType Junction -Path (Join-Path $env:DSH_HOME 'profiles/node_modules/dsh-all-usage') -Target '<absolute plugin path>'
345
- ~~~
262
+ ## 反馈、贡献与联系
346
263
 
347
- 2. Add this entry to $DSH_HOME/profiles/web/cordis.patch.yml:
264
+ 欢迎通过对应模板报告问题。一般问题或功能讨论可前往[项目仓库](https://github.com/ParticleLight/dsh-all-usage),也欢迎提交代码 PR;报告缺陷时请附 DSH、Node.js 和插件版本。
348
265
 
349
- ~~~yaml
350
- - insert:
351
- - id: all-usage
352
- name: dsh-all-usage
353
- ~~~
266
+ - [数据不一致](.github/ISSUE_TEMPLATE/data-inconsistency.md)
267
+ - [插件启动失败](.github/ISSUE_TEMPLATE/startup-failure.md)
268
+ - [成本计算问题](.github/ISSUE_TEMPLATE/cost-calculation.md)
354
269
 
355
- The profile patch layer hot-reloads; save the file and refresh the page.
356
-
357
- ### Architecture
358
-
359
- - **Host** (entry `lib/index.js`, assembled by `lib/plugin.js`): split by responsibility across `aggregation.js` (aggregation/query), `ledger.js` (durable ledger), `session-sync.js` (history/live sync), `pricing-runtime.js` (runtime pricing), `balance.js` (balance), and `http.js` (protected routes); aggregates `turn/end`, `assistant/chunk` usage, and final `assistant/message.usage`, folds live `session/event` updates, and exposes data routes through `webServer`:
360
- - `GET /api/all-usage` — compatible usage snapshot
361
- - `GET /api/all-usage/status` — lightweight revision and sync health, plus the last client environment report (`clientEnv`), the running plugin version (`pluginVersion`) and a derived id of the write capability (`capabilityId`, changes when the capability rotates so the page can notice a plugin reload; the capability itself is only ever sent in the full snapshot)
362
- - `GET /api/all-usage/query` — scoped aggregate, daily/hourly trend, and heatmap data; single-day scopes populate `hourly`, while cross-day scopes return an empty `hourly` array
363
- - `GET /api/all-usage/records` — paginated privacy-safe canonical usage rows
364
- - `GET /api/all-usage/version?force=1` — version check: the running version, the newest published version, the verdict (`latest` / `outdated` / `unknown`), when it was checked and where it came from; cached on the host for 6 hours with at most one upstream request every 30 seconds per process (the floor applies to `force=1` too), `force=1` comes from the refresh button
365
- - `GET /api/all-usage/balance?force=1` — account balance using the configured `llm-deepseek` API key
366
- - `POST /api/all-usage/alias` — update workspace aliases
367
- - `GET /api/all-usage/pricing` — read the full editable price table: per-row effective rates and status, mappings, manual prices, peak plans (with their built-in/explicit origin and full rules), and rows that exist only in the configuration
368
- - `GET /api/all-usage/pricing/models?q=...` — search official model IDs and display names, including preview rates, tiered-pricing flags, and whether a built-in peak table exists
369
- - `POST /api/all-usage/pricing` — save sync, mappings, manual prices, context rate bands, and peak-window rules (request shape unchanged)
370
- - `POST /api/all-usage/pricing/sync` — sync models.dev and backfill unpriced calls
371
- - `POST /api/all-usage/pricing/holidays` — fetch one year of the Chinese holiday arrangement (the holiday-cn dataset, jsDelivr first with a GitHub raw fallback, cached host-side for 24 hours) and return only the off-day list plus its source; **nothing is written to the configuration** — the client freezes the dates into a peak plan
372
- - `GET /api/all-usage/client-env` — client environment report (user agent, window-controls overlay height, the reserved strip **and its source**, viewport, panel rectangles); the client reports it at load and whenever the sheet opens, kept in memory only
373
- - **Client**: readable source lives in `src/client.js`; `npm run build:client` uses the pinned Terser version to generate the `window.__ModuleLoader__` bundle at `lib/client.js`, which registers the “Usage statistics” sidebar entry through the `sidebar.footer.action` slot. All API routes accept loopback requests and reject an explicit cross-origin Origin; balance reads and alias writes also require a process-scoped token generated when the plugin starts (the balance GET tolerates browsers omitting Origin). **Writes are authorized by that token, not by an Origin**: the DSH desktop shell runs the UI on its own scheme and forwards requests through Electron, which strips `Origin` (with `Host`/`Cookie`/`Sec-Fetch-*`), so requiring one made every desktop save fail with 403. A present `Origin` must still be loopback or the shell's own scheme, and `Host` must still be loopback (DNS-rebinding guard).
374
-
375
- ### Data semantics
376
-
377
- - Calls and tokens come from DSH session logs; `session/flush` rebuilds and queues the derived ledger only when related events are dirty, coalescing the latest pending record per session and draining on plugin disposal. Readable logs and ledger history are backfilled when the plugin activates, so successfully persisted data survives reloads or session deletion; deleting a workspace likewise keeps its history, summed into one "Deleted" row together with usage that had not reached the ledger yet
378
- - Day-level range data retains every readable historical session date with tracked usage; the heatmap is a switchable-span view (last 30 days / 90 days / 12 months, 12 months by default), not a fixed latest-53-week view
379
- - After a session is deleted, successfully flushed usage is restored from the separate ledger; disposal hints and periodic reconciliation trigger rebuilds without deleting ledger rows
380
- - For each session and logical `turn / step`, only the final usage contribution is kept; retries or replaced messages do not double-count
381
- - Input tokens are fresh (exclude cache hits/writes, which sit in their own buckets); all-zero usage replays do not overwrite recorded usage and pure cache-read requests still count
382
- - The version check is a single read-only GET to `registry.npmjs.org` (the version number of `dsh-all-usage` only: no body, no cookies; issued by the host and cached for 6 hours, the page itself never reaches the network), and a failed check degrades to “unknown” instead of guessing
383
- - The lightweight status endpoint exposes only the Host instance, stats revision, scan progress, sync counters, and the last client environment report (user agent, viewport, reserved strip and its source, panel rectangles — reported by a local client and kept in memory only). It does not expose session IDs, workspace paths, prompts, or reply bodies; full snapshots are fetched only after status changes or a manual refresh
384
- - Scoped results keep turns, model calls, and distinct sessions as separate metrics; missing route identity is explicitly Unknown rather than inferred from a display label
385
- - The records endpoint returns only a short hash, time, workspace ID, structured model identity, turn/step, token buckets, and current materialization source. It omits raw session IDs, paths, prompts, replies, and credentials
386
- - Processed tokens = input + output + cache read/write + reasoning; a cache hit means reused context, not newly generated tokens or actual cost
387
- - Cost follows the cc-switch four-bucket formula: input, output, cache-read, and cache-write tokens are priced independently, summed, then multiplied by the final multiplier; when input context is strictly greater than a context-tier threshold, all four rates switch for the whole request instead of progressive band splitting, and DSH reasoning is not added to output a second time
388
- - Time-of-day pricing matches UTC weekdays against half-open windows: a matching request uses the rule's peak rates, anything else uses the row base rates (off-peak = base); usage before a plan's effective instant has no verifiable band and fails closed as unpriced instead of inheriting today's rates, and one plan is shared by every row priced from the same entry (for DeepSeek the built-in plan carries the official State Council holiday list, refreshed with plugin releases); with the holiday switch on, a statutory holiday never matches a peak window and the cost snapshot records pricingHoliday, so records show "Holiday off-peak" instead of a plain off-peak band. The built-in DeepSeek table already ships the 2026 arrangement (refreshed with plugin releases), so first-party or mapped DeepSeek rows price holidays as off-peak with no configuration; reseller routes stay static until they are mapped to the official entry
389
- - Legacy ledger costs carrying `tiered` are migrated to `unsupported` (`tiered-pricing-not-modeled`) on load instead of remaining falsely marked as current flat priced estimates; token statistics are unchanged.
390
- - Pricing sync is off by default; when models.dev is unavailable the last good catalog remains in use, and unmatched models never receive a guessed default price; the Cost Settings price boxes are the manual-price entry point (shared by every row priced from the same official model), each row expands into context bands and UTC peak rules (including optional all-day off-peak pricing on Chinese statutory holidays), and the built-in DeepSeek table can be copied for customisation or restored; dashboard range and detail-view preferences are stored in browser storage, while the 6-hour sync toggle is immediately saved through the protected pricing API
391
- - Mapping semantics: a mapping with `identityKey` applies only to that exact route identity; a mapping without an identity key is the model-wide fallback. Legacy `usageIdentityKey` values are normalized when loaded.
392
- - The holiday calendar is fetched only when the user clicks "Load from public calendar": the request goes to `cdn.jsdelivr.net` (falling back to `raw.githubusercontent.com`), carries nothing but the year, and **never includes usage, session, or credential data**; the returned dates stay inert until saved, and saving freezes them together with their source note into that model's peak plan (the source is excluded from the policy hash)
393
- - Balance data comes from DeepSeek’s official `/user/balance` endpoint; the card shows guidance when no API key is configured
394
- - English mode uses UTC for date buckets, range filters, streaks, heatmap dates, and export timestamps; Chinese mode uses local time
395
- - The ledger assigns each session ID to one of 32 stable-hash JSON shards, so a flush rewrites only its shard; the old `all_usage_ledger.json` is migrated on first load. An async write failure or an unflushed shutdown does not lose in-memory statistics; the next startup simply scans that session again
396
- - Only sessions that can be mapped to a registered workspace by their working directory are included
397
-
398
- ### Development
399
-
400
- - After editing `src/client.js`, run `npm run build:client`, reload the DSH client module, and refresh the page; `lib/client.js` is generated and should not be edited directly. After editing `lib/plugin.js` or another Host module, reload the package through DSH or restart the process
401
- - The plugin has no third-party runtime dependencies: the Host uses Cordis services and the Client uses the runtime-provided React module; pinned Terser is only a development dependency for generating the browser artifact
402
- - Manual npm recovery publishes require a target `v<package.version>` tag and full commit SHA; GitHub Actions checks both against the checked-out tag and package version. Release events perform the same commit check.
403
-
404
- ## License / 许可证
270
+ ## 许可证
405
271
 
406
272
  MIT
@@ -0,0 +1 @@
1
+ <svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Ai2</title><path d="M9.553 9.378H4.777V4.835H8.62c.513 0 .932-.42.932-.932V.058h4.544v4.777a4.542 4.542 0 01-4.544 4.543zm-4.776.467H0v4.543h3.845c.512 0 .932.42.932.932v3.845H9.32v-4.777a4.542 4.542 0 00-4.543-4.543zM20.05 9.61a.935.935 0 01-.932-.932V4.835h-4.543V9.61a4.542 4.542 0 004.543 4.544h4.777V9.612H20.05zM9.787 19.166v4.777h4.544v-3.845c0-.513.42-.932.932-.932h3.845V14.62H14.33a4.542 4.542 0 00-4.544 4.544z" fill="#F0529C"></path></svg>
@@ -0,0 +1 @@
1
+ <svg fill="#8a8f98" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 103 24" xmlns="http://www.w3.org/2000/svg"><title>AI21</title><path d="M15.064 21.643l-.74-2.335H7.487l-.741 2.335H2L8.862 2.414h4.09l6.944 19.23h-4.832zM10.92 7.908L8.56 15.819h4.666l-2.305-7.911zm9.504-5.494h4.501v19.23h-4.501V2.413zm5.714 15.3a8.84 8.84 0 011.057-2.691 7.78 7.78 0 011.606-1.868 16.915 16.915 0 012.045-1.456c.567-.33 1.093-.646 1.578-.948.447-.275.874-.582 1.276-.92.348-.289.64-.638.865-1.03.214-.391.323-.832.315-1.278 0-.769-.21-1.323-.63-1.662-.447-.347-1-.526-1.565-.508A2.475 2.475 0 0030.943 6c-.467.43-.7 1.15-.7 2.156h-4.42a6.493 6.493 0 01.454-2.445c.294-.74.749-1.406 1.33-1.95a6.26 6.26 0 012.142-1.291A8.363 8.363 0 0132.657 2a9.048 9.048 0 012.512.344c.76.21 1.472.565 2.1 1.044a4.972 4.972 0 011.44 1.813c.374.823.557 1.72.536 2.623a4.64 4.64 0 01-.522 2.198 7.454 7.454 0 01-1.276 1.758c-.497.508-1.044.963-1.633 1.36-.586.394-1.117.728-1.592 1.003-.66.44-1.204.82-1.633 1.14a7.753 7.753 0 00-1.03.892 2.403 2.403 0 00-.535.852 3.128 3.128 0 00-.15 1.017h8.234v3.598h-13.34c-.023-1.32.102-2.637.371-3.928zM39.958 5.792c.769.016 1.538-.053 2.292-.206a3.307 3.307 0 001.386-.618 2.14 2.14 0 00.686-1.044c.137-.491.202-1 .192-1.51h3.926v19.23h-4.528V8.923h-3.954V5.792z"></path><path d="M53.534 2.414h4.199v19.23h-4.2V2.413zm19.704 5.978v13.241h-4.2v-1.868a3.434 3.434 0 01-.659.865 4.24 4.24 0 01-.946.686c-.371.198-.762.354-1.167.466-.41.118-.835.178-1.262.179a6.33 6.33 0 01-2.622-.536 6.207 6.207 0 01-2.044-1.456 6.455 6.455 0 01-1.318-2.197 8.402 8.402 0 010-5.522 6.455 6.455 0 011.318-2.198 6.207 6.207 0 012.044-1.455 6.329 6.329 0 012.622-.535c.427.001.852.061 1.263.179.406.113.798.274 1.166.48.347.194.666.434.947.715.257.252.48.539.659.851V8.392h4.199zm-7.356 10c.445.006.886-.088 1.29-.275a3.147 3.147 0 001.634-1.8c.156-.417.235-.859.233-1.304a3.546 3.546 0 00-.879-2.363 3.056 3.056 0 00-.989-.742 3.168 3.168 0 00-2.58 0 3.06 3.06 0 00-.989.742 3.549 3.549 0 00-.878 2.363c-.002.445.077.888.233 1.305a3.153 3.153 0 001.634 1.8c.404.187.845.28 1.29.273zm12.459 3.251h-4.2V2.414h4.2v7.883a4.218 4.218 0 011.619-1.566c.37-.202.76-.364 1.166-.48.415-.12.845-.18 1.276-.179a6.33 6.33 0 012.622.536c.773.34 1.47.836 2.044 1.456a6.47 6.47 0 011.318 2.197 8.413 8.413 0 010 5.522 6.47 6.47 0 01-1.318 2.198 6.203 6.203 0 01-2.044 1.456 6.33 6.33 0 01-2.622.535 4.577 4.577 0 01-1.276-.178 6.213 6.213 0 01-1.166-.466 4.12 4.12 0 01-.96-.687 3.435 3.435 0 01-.66-.865v1.867zm3.184-3.241c.436.004.868-.09 1.263-.275a3.148 3.148 0 001.634-1.8c.156-.416.235-.859.232-1.304a3.547 3.547 0 00-.878-2.363 3.056 3.056 0 00-.99-.741 2.901 2.901 0 00-1.261-.276 3.012 3.012 0 00-2.305 1.016 3.546 3.546 0 00-.879 2.362 3.66 3.66 0 00.233 1.305c.145.395.364.759.645 1.072a3.1 3.1 0 002.306 1.004zm15.219-4.56a20.56 20.56 0 011.646.535c.479.175.932.415 1.345.714.38.28.695.642.92 1.058.244.494.362 1.042.343 1.593a3.937 3.937 0 01-.467 1.992 3.71 3.71 0 01-1.29 1.319c-.587.353-1.234.6-1.907.727-.765.15-1.542.224-2.32.22-1.885 0-3.372-.412-4.46-1.236-1.09-.824-1.413-2.006-1.413-3.544h3.897c0 .696.197 1.195.59 1.497a2.43 2.43 0 001.524.453c.434.019.866-.08 1.248-.288a1.018 1.018 0 00.48-.948.898.898 0 00-.205-.618 1.982 1.982 0 00-.632-.44 7.884 7.884 0 00-1.097-.412c-.45-.137-.985-.306-1.606-.508a21.019 21.019 0 01-1.565-.535 5.688 5.688 0 01-1.304-.7 2.978 2.978 0 01-.891-1.045 3.427 3.427 0 01-.33-1.593c0-1.374.526-2.39 1.578-3.05 1.053-.659 2.475-.989 4.268-.99a8.335 8.335 0 012.512.344c.652.192 1.26.514 1.784.948.457.39.818.878 1.057 1.429.238.555.36 1.153.357 1.758h-4.145a1.798 1.798 0 00-.425-1.278 1.71 1.71 0 00-1.304-.453 2.04 2.04 0 00-1.098.289.972.972 0 00-.467.892.828.828 0 00.22.591c.185.182.405.327.645.426.344.15.697.279 1.057.384.42.13.905.285 1.455.468z" fill="#E91E63"></path></svg>