dsh-tokenlens 1.3.0 → 1.3.3

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.
package/CHANGELOG.md CHANGED
@@ -1,105 +1,61 @@
1
1
  # Changelog
2
2
 
3
- 本项目遵循 [Semantic Versioning](https://semver.org/):修复 = PATCH,新功能 = MINOR,不兼容变更 = MAJOR。发布时同步更新 `package.json` 的 `version`、`index.js` 的 `PLUGIN_VERSION` 与本文件(`node scripts/bump-version.mjs <version> "<note>"`);已发布的条目保持原样。
3
+ 本项目遵循 [Semantic Versioning](https://semver.org/):修复 = PATCH,新功能 = MINOR,不兼容变更 = MAJOR。发布时同步更新 `package.json` 的 `version`、`index.js` 的 `PLUGIN_VERSION` 与本文件(`node scripts/bump-version.mjs <version> "<note>"`)。
4
4
 
5
- ## 1.3.0 — 2026-11-01
5
+ **版本记录约定**:同一发布日只保留一个章节,标题取当日**最后一个**已发布版本号;当日多次发布的改动合并到该章节,并在标题下注明当日实际发布过的版本号(npm 上仍可安装这些独立版本)。若当日已有发布,后续小改动不再单独增加版本号,而是并入下一篇日志。这样日志按天规整、不出现同日多个小版本。
6
6
 
7
- - feat(update): 可选的**启动时检查更新**(默认关闭):开启后仅向 npm registry 查询一次 `dsh-tokenlens` 的 latest 版本号,不携带凭据/账号/用量信息;主机离线或接口失败时记录错误并说明,不阻塞启动
8
- - feat(update): 供应商管理页「插件设置」新增「更新检查」块——开关、隐私提示、状态行(尚未检查 / 正在检查 / 已是最新 / 发现新版本 / 检查失败可重试)与「立即检查」;详情弹窗底部在发现新版本时显示一行「发现新版本 vX · 打开 npm」(链接到 npm 包页)
9
- - feat(wire): `hello` 与 `providers` 载荷新增 `update`(`UpdateInfo`)与 `updatePackageUrl`;`settingsUpdate` 新增 `settings.updateCheck`;新增 bridge action `updateCheck`(Agent 工具同步支持)
10
- - fix(update): 并发检查单飞(共享同一次在途请求);仅在开关从关→开时触发检查,已开启时保存其它显示设置不会重复请求 npm
11
- - 测试:277 个自动化用例(新增 Host 版本比较/检查/设置/并发与客户端 UI 回归)
7
+ ## 1.3.3 — 2026-11-01
12
8
 
13
- ## 1.2.2 — 2026-11-01
9
+ (当日已发布版本:v1.2.2 / v1.3.0 / v1.3.1 / v1.3.3;v1.3.2 因仓库迁移未发布)
14
10
 
15
- - fix(workspaces): 修复「工作区悬浮卡片不显示 Token 明细」——Host 在裁剪本地路径时把 `path` 字段误删成空串,客户端只剩标题精确匹配;默认工作区等「界面标题 ≠ 统计标签」的场景因此匹配不到统计(影响 v1.2.1 及更早版本)
16
- - fix(workspaces): 悬浮卡片匹配增强——标题匹配改为大小写不敏感;Host 只下发文件夹名 basename(不含完整路径);旧 Host 未下发 `path` 时用文件夹名与标签兜底
17
- - docs: README 增加「用 `link:` 安装的插件不要从插件市场重复安装/升级」的排错说明(Windows 下 pnpm 会遇到 EPERM)
18
- - 测试:256 个自动化用例(新增工作区 wire basename 回归;客户端匹配逻辑断言)
11
+ - feat(update): 「启动时检查更新」改为**默认开启**(可在插件设置关闭);启动时向 npm registry 查询一次 latest 版本号,不携带凭据 / 账号 / 用量,失败只记录错误、不阻塞启动
12
+ - feat(ui): 详情弹窗(统计首页)右下角的**插件版本处**显示更新状态——有新版本时显示「有新版本 vX」并可点击打开 npm;无新版本时显示「已是最新版本」;检查失败或关闭检查时不显示
13
+ - feat(update): 供应商管理页「插件设置 → 更新检查」保留开关、隐私提示、状态行(尚未检查 / 正在检查 / 已是最新 / 发现新版本 / 检查失败可重试)与「立即检查」;并发检查单飞,仅在开关关→开时补查
14
+ - feat(wire): `hello` / `providers` 携带 `update`(`UpdateInfo`)与 `updatePackageUrl`;`settings.updateCheck` 默认 `true`;新增 bridge action `updateCheck`(Agent 工具同步支持)
15
+ - fix(workspaces): 修复「工作区悬浮卡片不显示 Token 明细」——Host 裁剪本地路径时把 `path` 误删成空串,默认工作区等「界面标题 ≠ 统计标签」的场景匹配失败;现只下发文件夹名 basename,客户端标题匹配大小写不敏感并兼容旧 Host
16
+ - chore(repo): 仓库迁移到 **senyingxidi/dsh-tokenlens**(原作者账号改名);同步更新 `package.json` 的 repository/homepage/bugs、README 徽章与 7 张截图、`market/index.json`、CONTRIBUTING / SECURITY / CODE_OF_CONDUCT / Issue 模板与 LICENSE 署名;旧 GitHub 地址会 301 重定向,旧市场索引 raw 地址继续可用
17
+ - fix(ui): 插件版本处自愈——页面在插件更新前加载时,`hello` 的旧版本号不再覆盖新的 Host 版本(取 hello / providers / 更新检查 `current` 中最新的版本),无需刷新页面即可显示正确版本
18
+ - docs: README 增加「用 `link:` 安装的插件不要从插件市场重复安装/升级」的 Windows pnpm EPERM 排错说明;CHANGELOG 改为按发布日合并
19
+ - 测试:280 个自动化用例(版本比较/检查/并发、设置开关、状态行、图表下钻与窗口、工作区 wire 回归)
19
20
 
20
21
  ## 1.2.1 — 2026-10-31
21
22
 
22
- - feat(settings): 图表模式「明细」环形图的窗口选择(今日 / 最近7天 / 最近X天(或最近30天)/ 总计)持久化到插件设置:点击 pill 立即保存,重开弹窗、切换供应商或重启 DSH 后仍保留上次选择;当前供应商没有该窗口时自动回落到总计
23
- - feat(wire): `providers` 载荷与 `settingsUpdate` 增加 `settings.detailsWindow`(`today|last7Days|second|total`),旧 Host 会忽略未知字段、旧 client 读取时回落 `total`,双向兼容
24
- - 修复:请求进行中连续点击窗口时只保留最后一个选择(单定时器 200ms 重试);「恢复默认设置」同时把窗口重置为总计
25
- - 测试:255 个自动化用例(新增 5 个持久化回归,更新 8 个既有设置/窗口用例)
23
+ (当日已发布版本:v1.2.0 / v1.2.1)
26
24
 
27
- ## 1.2.0 — 2026-10-31
28
-
29
- - feat(chart): 图表模式点击某一天的柱子可下钻查看**当天 24 小时**的 Token 消耗棒状图(横轴 00:00–23:00,悬浮显示小时与占比,回车/空格同样可操作,返回或再次点击回到日趋势)
25
+ - feat(chart): 图表模式点击某一天的柱子可下钻查看**当天 24 小时**的 Token 消耗(横轴 00:00–23:00,悬浮显示小时与占比,回车/空格可操作,返回或再次点击回到日趋势)
30
26
  - feat(chart): 小时数据覆盖全部 5 个本机 harness(DSH、ZCode、OpenCode CLI、WorkBuddy、Qoder);无小时数据的旧日期不可下钻,不伪造全零序列
31
- - feat(chart): 图表模式「明细」的三张环形图(按 Harness / 按供应商 / Top 模型)新增 **今日 / 最近7天 / 最近X天(或最近30天)/ 总计** 窗口切换,默认总计;切换 scope 或数据/图表视图时保持,缺失窗口自动隐藏
32
- - feat(wire): `stats.daily[]` 新增可选 `hours`(24 个本地小时 Token 整数)与 `providers[pid].hours`(v1.2.0 协议,已在 PROTOCOL 中写明)
33
- - 测试:250 个自动化用例(新增 13 个:小时聚合、外部 harness 小时分组、下钻交互与窗口切换的前后端回归)
27
+ - feat(chart): 明细三张环形图(按 Harness / 按供应商 / Top 模型)支持 **今日 / 最近7天 / 最近X天(或最近30天)/ 总计** 窗口切换,默认总计,缺失窗口自动隐藏
28
+ - feat(settings): 明细窗口选择持久化到插件设置——点击 pill 立即保存,重开弹窗、切换供应商或重启 DSH 后仍保留,窗口不可用时回落到总计
29
+ - feat(wire): `stats.daily[]` 新增 `hours` 与 `providers[pid].hours`(24 个本地小时 Token 整数);`settings.detailsWindow`
30
+ - 修复:请求进行中连续点击窗口只保留最后一次选择(单定时器 200ms 重试);「恢复默认设置」同时把窗口重置为总计
31
+ - 测试:250 → 255(小时聚合、外部 harness 小时分组、下钻交互、窗口切换与持久化回归)
34
32
 
35
33
  ## 1.1.0 — 2026-10-30
36
34
 
37
35
  - feat(providers): 接入 Open Design(OpenDesign)只读额度查询——Design Plan 的 5 小时 / 7 天窗口已用百分比与重置时间,以及独立的钱包 USD 余额
38
36
  - feat(providers): Open Design 使用工作区 `workspaceId` + Control Key(`OPENDESIGN_CONTROL_KEY` / `VELA_CONTROL_KEY`)鉴权;推理 API Key 不能查询计费接口
39
- - feat(ui): `windows` 快照现在可以附带钱包余额——详情弹窗在额度窗口下方多一行「账户余额」,chip 悬浮提示追加金额
37
+ - feat(ui): `windows` 快照可附带钱包余额——详情弹窗在额度窗口下方多一行「账户余额」,chip 悬浮提示追加金额
40
38
  - docs: 中英 README 与 PROTOCOL 补充 Open Design;说明其余仅元数据供应商没有可用 API Key 查询的公开用量接口(千问/百炼额度仅官方 CLI 可查,腾讯混元/百度千帆只有云账户余额 API)
41
39
 
42
40
  ## 1.0.9 — 2026-10-29
43
41
 
44
- - ci: GitHub Actions 升级到 v7(actions/checkout、actions/setup-node),发布链路仅依赖 OIDC
45
-
46
- ## 1.0.8 — 2026-10-29
47
-
48
- - fix(release): npm registry 发布延迟时索引更新回退到 PUBLISH_TIMESTAMP;补录 1.0.7 到市场索引
49
-
50
- ## 1.0.7 — 2026-10-29
51
-
52
- - ci: 发布改用 npm Trusted Publishing(OIDC),不再依赖仓库令牌;docs: 归并 1.0.0 之前版本历史并修订文档措辞
53
-
54
- ## 1.0.6 — 2026-10-29
55
-
56
- - fix(market): 发布后等待 npm registry 同步再写索引(自动重试);索引补录 1.0.5
57
-
58
- ## 1.0.5 — 2026-10-29
59
-
60
- - feat(market): 提供 DSH 插件市场索引(market/index.json + icon)并支持发布后自动同步;README 增加从 DSH 市场安装的说明
61
-
62
- ## 1.0.4 — 2026-10-29
63
-
64
- - chore: 插件市场元数据(dsh-plugin 关键词、engines.dsh)与 GitHub 源安装说明
65
-
66
- ## 1.0.3 — 2026-10-29
42
+ (当日已发布版本:v1.0.3 / v1.0.4 / v1.0.5 / v1.0.6 / v1.0.7 / v1.0.8 / v1.0.9)
67
43
 
44
+ - feat(market): 提供 DSH 插件市场索引(`market/index.json` + icon)并支持发布后自动同步;README 增加从 DSH 市场安装的说明
45
+ - feat: 插件市场元数据(`dsh-plugin` 关键词、`engines.dsh`)与 GitHub 源安装说明
68
46
  - fix(stats): 逐帧解压 zstd,兼容 Node >= 24.22(尾部撕裂帧不再拖垮整份日志,修复 CI 三平台失败)
47
+ - fix(market): 发布后等待 npm registry 同步再写索引(自动重试,`PUBLISH_TIMESTAMP` 回退);补录历史版本到索引
48
+ - ci: 发布改用 npm Trusted Publishing(OIDC),不再依赖仓库令牌;GitHub Actions 升级到 v7
49
+ - docs: 归并 1.0.0 之前版本历史并修订文档措辞
69
50
 
70
51
  ## 1.0.2 — 2026-10-28
71
52
 
72
- - fix(security+privacy): audit hardening — OpenCode auth key scoping, workspace badge observer loop, canonical provider cache keys, stats path redaction incl. free-form errors, baseURL HTTPS validation (profile/custom), effective-day full history, prototype-pollution-safe maps, WorkBuddy no-id dedupe, immutable provider store writes, secure temp files, DST-safe windows, console link/provider aliases, management scroll; screenshots sanitized
73
-
74
- ## 1.0.1 — 2026-10-28
75
-
76
- - fix(privacy): 状态文件/诊断/管理页路径脱敏(~)、状态文件 0600、网络错误 URL 去凭据/查询串;匿名化测试 fixture 与公共文档中的个人邮箱;工作区统计 tick 增加隐藏页保护;新增 redact 单测与网络错误回归测试
77
-
78
- ## 1.0.0 — 2026-10-28
79
-
80
- 首个公开版本:插件定名 **dsh-TokenLens · 词元透镜**,技术包名与内部标识统一为 `dsh-tokenlens` / `tokenlens`,以 MIT 许可开源。本版本合并了此前 0.1.0–0.36.3 的全部开发历史:
81
-
82
- **供应商与套餐容量**
83
-
84
- - OpenCode(Go 套餐的 5 小时 / 本周 / 本月窗口)、Command Code(美元额度与方案)、DeepSeek 余额、DeepSeek Account 钱包、OpenRouter、Moonshot / Kimi、SiliconFlow、StepFun、Novita、Hyperbolic、DeepInfra、Z.ai GLM Coding Plan、MiniMax Coding Plan;随后接入智谱 AI 国内版与火山引擎 Coding / Agent Plan
85
- - 内置供应商元数据池扩充到 25 家(商汤 SenseNova、通义千问、百度千帆、腾讯混元、讯飞星火、零一万物、百川智能、Intern AI、Agnes AI、Open Design 等)
86
- - 按 DSH 模型设置自动发现路由、自动匹配用量接口并继承凭据;支持短标签 / 全名、启用开关、订阅徽章与控制台链接
87
-
88
- **本机 Token 统计**
89
-
90
- - 接入 5 个 harness:DSH 会话日志(zstd 逐帧解码、seed 前缀与重试口径)、ZCode(只读 SQLite)、OpenCode CLI、WorkBuddy / CodeBuddy CLI、Qoder CN CLI
91
- - 跨 harness 按供应商合并(alias、同 id、vendor-namespaced 模型唯一匹配),无法映射的供应商单列
92
- - 窗口:今日 / 最近 7 天 / 最近 30 天 / 总计、订阅供应商的「最近 X 天」、滚动 5 小时;按 Harness / 供应商 / Top 模型 / 工作区拆分
93
- - 工作区 Token 徽标与悬浮卡片
94
-
95
- **界面**
96
-
97
- - 输入框下方的用量 chip(跟随当前供应商)与详情弹窗:套餐容量、本机 Token 两套口径分区展示
98
- - 数据 / 图表两种模式:每日趋势柱状图(坐标轴、悬浮读数)、环形占比图、明细表格
99
- - 供应商管理页(已配置 / 内置 / 全部、凭据状态、短标签编辑、测试连接)与插件设置页;深色主题与窄屏适配
100
-
101
- **工程**
53
+ (当日已发布版本:v1.0.0 / v1.0.1 / v1.0.2)
102
54
 
103
- - Host ↔ Client bridge 协议 v3、`planUsage` 服务与 `tokenlens_usage` Agent 工具
104
- - 配置覆盖(profile patch 与 providers.json)、统计缓存、诊断与状态文件
105
- - 231 个自动化测试(Node 内置测试器,三平台 CI),零运行时依赖,要求 Node ≥ 24(`node:sqlite`、zstd)
55
+ - fix(security+privacy): 安全 / 隐私审计加固——OpenCode key 作用域、工作区徽标观察器循环、缓存 canonical、统计路径与自由文本脱敏、baseURL HTTPS 校验、effective-day 全历史、原型污染防护、WorkBuddy 去重、0600 原子写、DST 窗口、控制台链接、管理页滚动;截图脱敏(账号 / 本机路径)
56
+ - fix(privacy): 状态文件 / 诊断 / 管理页路径脱敏(`~`)、状态文件 0600、网络错误 URL 去凭据 / 查询串;测试 fixture 与公开文档去除个人邮箱
57
+ - 首个公开版本(v1.0.0):插件定名 **dsh-TokenLens · 词元透镜**,包名与内部标识统一为 `dsh-tokenlens` / `tokenlens`,以 MIT 许可开源;本版本合并了此前 0.1.0–0.36.3 的全部开发历史:
58
+ - **供应商与套餐容量**:OpenCode(Go 套餐窗口)、Command Code、DeepSeek / DeepSeek Account、OpenRouter、Moonshot / Kimi、SiliconFlow、StepFun、Novita、Hyperbolic、DeepInfra、Z.ai GLM、MiniMax、智谱国内版、火山引擎 Coding / Agent Plan;内置 25 家供应商元数据池;按 DSH 模型设置自动发现路由并继承凭据
59
+ - **本机 Token 统计**:接入 5 个 harness(DSH、ZCode、OpenCode CLI、WorkBuddy / CodeBuddy CLI、Qoder CN CLI);跨 harness 按供应商合并;窗口 今日 / 最近 7 天 / 最近 30 天 / 总计 与订阅供应商「最近 X 天」、滚动 5 小时;按 Harness / 供应商 / Top 模型 / 工作区拆分
60
+ - **界面**:输入框下方用量 chip(跟随当前供应商)与详情弹窗(套餐容量 vs 本机 Token 分区)、数据 / 图表两种模式、供应商管理页与插件设置页
61
+ - **工程**:Host ↔ Client bridge 协议 v3、`planUsage` 服务、`tokenlens_usage` Agent 工具、配置覆盖、统计缓存、诊断与状态文件、231 个自动化测试、零运行时依赖、Node ≥ 24
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 daigahou
3
+ Copyright (c) 2026 senyingxidi
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/PROTOCOL.md CHANGED
@@ -327,7 +327,7 @@ needs no provider catalog of its own.
327
327
 
328
328
  The dialog 「供应商管理」 page talks to three additional actions:
329
329
 
330
- - `{action:"providers"}` → `{ok:true, providers:[{providerId,label,shortLabel,kind,enabled,hasKey,keySource,apiKeyRef,baseURL,consoleUrl,builtin,dshManaged,dshProviderId}], dshProviders:[{providerId,label,supported,usageProviderId}], settings:{labelStyle,defaultView,defaultScope,detailsExpanded,detailsWindow,updateCheck}, update:<UpdateInfo|null>, updatePackageUrl, storeFile, storeFileDisplay}`. `storeFile` is the real path (local tooling); `storeFileDisplay` replaces the user's home directory with `~` and is what the management page renders. `update` (v1.3.0) is the last optional update-check result and `updatePackageUrl` the npm page the management UI links to.
330
+ - `{action:"providers"}` → `{ok:true, version, providers:[{providerId,label,shortLabel,kind,enabled,hasKey,keySource,apiKeyRef,baseURL,consoleUrl,builtin,dshManaged,dshProviderId}], dshProviders:[{providerId,label,supported,usageProviderId}], settings:{labelStyle,defaultView,defaultScope,detailsExpanded,detailsWindow,updateCheck}, update:<UpdateInfo|null>, updatePackageUrl, storeFile, storeFileDisplay}`. `storeFile` is the real path (local tooling); `storeFileDisplay` replaces the user's home directory with `~` and is what the management page renders. `version` (v1.3.2) is the running Host version, so a page loaded before an in-place plugin update can refresh a stale `hello` label without a reload. `update` (v1.3.0) is the last update-check result and `updatePackageUrl` the npm page the management UI links to.
331
331
  - `{action:"providerUpdate", providerId, update:{enabled?,apiKey?,apiKeyRef?,baseURL?,consoleUrl?,label?,shortLabel?}}` → same list payload;
332
332
  empty strings clear a stored value (falling back to the profile/default). `label` (全名, ≤40 chars) and
333
333
  `shortLabel` (短标签, ≤12 chars) let the management page rename a provider. The shipped management page no
@@ -335,17 +335,17 @@ The dialog 「供应商管理」 page talks to three additional actions:
335
335
  saves enabled/consoleUrl/label/shortLabel. Overrides are persisted atomically to
336
336
  `$DSH_HOME/dsh-tokenlens/providers.json` (plugin-owned, not the profile patch); the same file also
337
337
  - `{action:"providerTest", providerId}` → the normal snapshot envelope (`{ok,snapshot}` or `{ok:false,error}`), forcing one upstream read.
338
- - `{action:"settingsUpdate", settings:{labelStyle?:"short"|"full", defaultView?:"chart"|"data", defaultScope?:"provider"|"all", detailsExpanded?:"auto"|"expanded"|"collapsed", detailsWindow?:"today"|"last7Days"|"second"|"total", updateCheck?:boolean}}` → the same providers payload; every field is optional and persisted in the store (`settings.*`). `labelStyle` controls whether the composer chip shows each provider's `shortLabel` or full `label`; `defaultView` / `defaultScope` / `detailsExpanded` are the usage-dialog defaults; `detailsWindow` (v1.2.1) is the remembered 明细 donut window (falls back visually to `total` when that window is unavailable on the current provider/host); `updateCheck` (v1.3.0, default `false`) enables the optional startup update check — enabling it runs one check immediately. Invalid values answer `unsupported-provider`.
338
+ - `{action:"settingsUpdate", settings:{labelStyle?:"short"|"full", defaultView?:"chart"|"data", defaultScope?:"provider"|"all", detailsExpanded?:"auto"|"expanded"|"collapsed", detailsWindow?:"today"|"last7Days"|"second"|"total", updateCheck?:boolean}}` → the same providers payload; every field is optional and persisted in the store (`settings.*`). `labelStyle` controls whether the composer chip shows each provider's `shortLabel` or full `label`; `defaultView` / `defaultScope` / `detailsExpanded` are the usage-dialog defaults; `detailsWindow` (v1.2.1) is the remembered 明细 donut window (falls back visually to `total` when that window is unavailable on the current provider/host); `updateCheck` (v1.3.0, default `true` since v1.3.1) enables the startup update check — the off→on transition runs one check immediately. Invalid values answer `unsupported-provider`.
339
339
 
340
- ### Optional update check (v1.3.0)
340
+ ### Update check (v1.3.0, on by default since v1.3.1)
341
341
 
342
- Off by default: the plugin stays local-only unless the user enables
343
- `settings.updateCheck`. When enabled, one HTTPS `GET` to the fixed endpoint
344
- `https://registry.npmjs.org/dsh-tokenlens/latest` runs at activation, on the
345
- off→on transition of the setting, and on the `updateCheck` action. Concurrent
346
- checks are single-flight (one registry request shared by every caller). Nothing
347
- but the request itself leaves the machine — no credentials, identifiers or
348
- usage data.
342
+ On by default: at activation the Host makes one HTTPS `GET` to the fixed endpoint
343
+ `https://registry.npmjs.org/dsh-tokenlens/latest`; the same request also runs on
344
+ the off→on transition of `settings.updateCheck` and on the `updateCheck` action.
345
+ Users can switch it off (`settings.updateCheck: false`) to keep the plugin fully
346
+ local-only. Concurrent checks are single-flight (one registry request shared by
347
+ every caller). Nothing but the request itself leaves the machine — no
348
+ credentials, identifiers or usage data.
349
349
 
350
350
  ```jsonc
351
351
  // UpdateInfo (host → client)
package/README.en.md CHANGED
@@ -4,12 +4,12 @@
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/dsh-tokenlens.svg)](https://www.npmjs.com/package/dsh-tokenlens)
6
6
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
- [![CI](https://github.com/daigahou/dsh-tokenlens/actions/workflows/ci.yml/badge.svg)](https://github.com/daigahou/dsh-tokenlens/actions/workflows/ci.yml)
7
+ [![CI](https://github.com/senyingxidi/dsh-tokenlens/actions/workflows/ci.yml/badge.svg)](https://github.com/senyingxidi/dsh-tokenlens/actions/workflows/ci.yml)
8
8
  [![Node.js](https://img.shields.io/badge/node-%3E%3D24-brightgreen)](package.json)
9
9
 
10
10
  > One glance at your usage: plan capacity for the selected provider plus local token statistics across harnesses, right below the DSH composer.
11
11
 
12
- **dsh-TokenLens** is a usage plugin for DSH (DeepSeek Harness), version v1.3.0. It keeps two kinds of numbers separate:
12
+ **dsh-TokenLens** is a usage plugin for DSH (DeepSeek Harness), version v1.3.3. It keeps two kinds of numbers separate:
13
13
 
14
14
  - **Plan capacity** — the provider's authoritative server-side values (percentage windows, used / cap, balance or remaining credit);
15
15
  - **Local token stats** — tokens consumed on this machine across every connected harness, for the selected provider or for all providers combined.
@@ -21,7 +21,7 @@ Everything is computed locally: zero runtime dependencies, no external database,
21
21
  - **Usage chip below the composer** — follows the selected model provider and shows its plan capacity; providers without an adapter or credentials stay hidden.
22
22
  - **Details dialog** — plan capacity windows with reset times (plus an optional "consumed · estimated consumable" hint) and local token stats for today / last 7 days / last 30 days / all-time, scoped to the current provider or all harnesses.
23
23
  - **Data and chart modes** — tables, a daily trend chart and ring charts with breakdowns by harness, provider, top model and unmapped providers. In chart mode, clicking a day's bar drills into that day's 24 hourly bars; the 明细 rings switch between today / last 7 days / last X days (or last 30 days) / all time.
24
- - **Provider management page** — enabled / built-in / all filters, enable switches, credential status, console links, label editing, connection tests, subscription badges and plugin settings (label style, default view/scope, details expansion, default 明细 donut window, optional startup update check with a "check now" action).
24
+ - **Provider management page** — enabled / built-in / all filters, enable switches, credential status, console links, label editing, connection tests, subscription badges and plugin settings (label style, default view/scope, details expansion, default 明细 donut window, startup update check — on by default — with a "check now" action).
25
25
  - **Auto-discovery and agent tool** — providers configured in DSH Settings → Model Services are detected by route id or base-URL host and reuse their DSH credentials; `tokenlens_usage` lets the model query snapshots, local stats and diagnostics.
26
26
  - **Local-only** — 5 harnesses (DSH, ZCode, OpenCode CLI, WorkBuddy / CodeBuddy CLI, Qoder CN CLI), 25 built-in providers (16 with plan / balance adapters), read-only local data, no telemetry.
27
27
 
@@ -31,27 +31,27 @@ Everything is computed locally: zero runtime dependencies, no external database,
31
31
 
32
32
  **Usage chip below the composer**
33
33
 
34
- ![Usage chip below the composer](https://raw.githubusercontent.com/daigahou/dsh-tokenlens/main/docs/images/composer-chip.png)
34
+ ![Usage chip below the composer](https://raw.githubusercontent.com/senyingxidi/dsh-tokenlens/main/docs/images/composer-chip.png)
35
35
 
36
36
  **Workspace token badge and hover card**
37
37
 
38
- ![Workspace token badge and hover card](https://raw.githubusercontent.com/daigahou/dsh-tokenlens/main/docs/images/workspace-badge-hover.png)
38
+ ![Workspace token badge and hover card](https://raw.githubusercontent.com/senyingxidi/dsh-tokenlens/main/docs/images/workspace-badge-hover.png)
39
39
 
40
40
  **Usage dialog**
41
41
 
42
42
  | Chart mode | Data mode |
43
43
  |:---:|:---:|
44
- | ![Chart mode](https://raw.githubusercontent.com/daigahou/dsh-tokenlens/main/docs/images/stats-chart.png) | ![Data mode](https://raw.githubusercontent.com/daigahou/dsh-tokenlens/main/docs/images/stats-data.png) |
44
+ | ![Chart mode](https://raw.githubusercontent.com/senyingxidi/dsh-tokenlens/main/docs/images/stats-chart.png) | ![Data mode](https://raw.githubusercontent.com/senyingxidi/dsh-tokenlens/main/docs/images/stats-data.png) |
45
45
 
46
46
  **Provider management page**
47
47
 
48
48
  | Provider settings | Plugin settings |
49
49
  |:---:|:---:|
50
- | ![Provider settings](https://raw.githubusercontent.com/daigahou/dsh-tokenlens/main/docs/images/settings-providers.png) | ![Plugin settings](https://raw.githubusercontent.com/daigahou/dsh-tokenlens/main/docs/images/settings-plugin.png) |
50
+ | ![Provider settings](https://raw.githubusercontent.com/senyingxidi/dsh-tokenlens/main/docs/images/settings-providers.png) | ![Plugin settings](https://raw.githubusercontent.com/senyingxidi/dsh-tokenlens/main/docs/images/settings-plugin.png) |
51
51
 
52
52
  **Built-in provider pool**
53
53
 
54
- ![Built-in provider pool](https://raw.githubusercontent.com/daigahou/dsh-tokenlens/main/docs/images/settings-builtin.png)
54
+ ![Built-in provider pool](https://raw.githubusercontent.com/senyingxidi/dsh-tokenlens/main/docs/images/settings-builtin.png)
55
55
 
56
56
  ## Install
57
57
 
@@ -66,16 +66,16 @@ dsh plugin --profile <profile> add dsh-tokenlens
66
66
  Then make sure `dsh.profile.bundles` in `$DSH_HOME/profiles/<profile>/package.json` contains `"dsh-tokenlens"`:
67
67
 
68
68
  ```jsonc
69
- { "dependencies": { "dsh-tokenlens": "^1.3.0" },
69
+ { "dependencies": { "dsh-tokenlens": "^1.3.3" },
70
70
  "dsh": { "profile": { "bundles": ["…", "dsh-tokenlens"] } } }
71
71
  ```
72
72
 
73
73
  Restart DSH or wait for profile hot recomposition.
74
74
 
75
- **Option B — from GitHub.** In DSH, open **Plugins → Add plugin**, enter `https://github.com/daigahou/dsh-tokenlens` (or `github:daigahou/dsh-tokenlens`) and install. DSH selects the bundle in `dsh.profile.bundles` and enables it by default. CLI equivalent (installs the dependency only; add `"dsh-tokenlens"` to `dsh.profile.bundles` yourself):
75
+ **Option B — from GitHub.** In DSH, open **Plugins → Add plugin**, enter `https://github.com/senyingxidi/dsh-tokenlens` (or `github:senyingxidi/dsh-tokenlens`) and install. DSH selects the bundle in `dsh.profile.bundles` and enables it by default. CLI equivalent (installs the dependency only; add `"dsh-tokenlens"` to `dsh.profile.bundles` yourself):
76
76
 
77
77
  ```powershell
78
- dsh plugin --profile <profile> add "github:daigahou/dsh-tokenlens"
78
+ dsh plugin --profile <profile> add "github:senyingxidi/dsh-tokenlens"
79
79
  ```
80
80
 
81
81
  GitHub installs track the latest commit on the default branch (not a tag); updates require uninstall + reinstall, and GitHub access can be unreliable from some networks.
@@ -89,7 +89,7 @@ GitHub installs track the latest commit on the default branch (not a tag); updat
89
89
  1. DSH → **Plugins** → **More** next to *Experimental plugins* → enable **Custom plugin market**;
90
90
  2. Open **Discover** → **Add index source** → paste:
91
91
  ```
92
- https://raw.githubusercontent.com/daigahou/dsh-tokenlens/main/market/index.json
92
+ https://raw.githubusercontent.com/senyingxidi/dsh-tokenlens/main/market/index.json
93
93
  ```
94
94
  3. Search for `TokenLens` in **Discover** and install.
95
95
 
@@ -123,7 +123,7 @@ Host bridge route: `/api/tokenlens`; agent tool: `tokenlens_usage`.
123
123
 
124
124
  - **Read-only**: only local session logs and harness databases are read; SQLite is opened read-only and only token counters, timestamps and provider / model fields are queried — never message contents.
125
125
  - **Keys stay local**: API keys are resolved in the Host process from DSH credentials, environment variables or the DSH credentials file, and are sent only to the matching provider's official HTTPS endpoint. They are never exposed to the browser, logged or included in diagnostics.
126
- - **No telemetry**: nothing is collected, uploaded, or fetched from remote config / CDN sources (icons are inline SVG). The only optional outbound request is the startup update check (v1.3.0, off by default): when enabled it asks the npm registry once for the published version number, without credentials, identifiers or usage data.
126
+ - **No telemetry**: nothing is collected, uploaded, or fetched from remote config / CDN sources (icons are inline SVG). The only outbound request is the startup update check (introduced in v1.3.0, on by default since v1.3.1 and switchable off): it asks the npm registry once for the published version number, without credentials, identifiers or usage data.
127
127
  - **Owner-only data**: management overrides are written atomically to `$DSH_HOME/dsh-tokenlens/providers.json` with owner-only permissions; DSH model settings are never modified.
128
128
 
129
129
  ## Limitations
@@ -131,16 +131,16 @@ Host bridge route: `/api/tokenlens`; agent tool: `tokenlens_usage`.
131
131
  - Provider usage endpoints are mostly private / undocumented and may change at any time; the plugin degrades gracefully (retryable errors, hidden chips, per-row fallbacks) but availability is not guaranteed.
132
132
  - Plan capacity shows native server values only: no token → money conversion; Command Code monthly percentage is not guessed when no grant is reported.
133
133
  - "Estimated consumable" is derived from local tokens and the plan percentage; it is only a hint, not a provider commitment.
134
- - The startup update check compares the **latest npm release** only (off by default); commits on the GitHub repository are not detected — a git-installed plugin must be uninstalled and reinstalled to update.
134
+ - The startup update check is on by default (switchable off) and compares the **latest npm release** only; commits on the GitHub repository are not detected — a git-installed plugin must be uninstalled and reinstalled to update.
135
135
  - Local stats cover connected harnesses on this machine only; uninstalled harnesses are hidden by default (`stats.showUninstalled: true` shows them). Workspace token badges are experimental.
136
136
  - **Trademarks**: OpenCode, Command Code, DeepSeek, ZCode, WorkBuddy, CodeBuddy, Qoder and other names are trademarks of their respective owners. This is an independent third-party project, not affiliated with or endorsed by them.
137
137
 
138
138
  ## Development
139
139
 
140
- Node.js ≥ 24, zero runtime dependencies: `node --test` runs the 277 tests.
140
+ Node.js ≥ 24, zero runtime dependencies: `node --test` runs the 280 tests.
141
141
 
142
142
  Release: `node scripts/bump-version.mjs <version> "<note>"` updates `package.json`, `PLUGIN_VERSION` and `CHANGELOG.md`; see [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md).
143
143
 
144
144
  ## License
145
145
 
146
- [MIT](LICENSE) © daigahou
146
+ [MIT](LICENSE) © senyingxidi
package/README.md CHANGED
@@ -4,12 +4,12 @@
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/dsh-tokenlens.svg)](https://www.npmjs.com/package/dsh-tokenlens)
6
6
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
- [![CI](https://github.com/daigahou/dsh-tokenlens/actions/workflows/ci.yml/badge.svg)](https://github.com/daigahou/dsh-tokenlens/actions/workflows/ci.yml)
7
+ [![CI](https://github.com/senyingxidi/dsh-tokenlens/actions/workflows/ci.yml/badge.svg)](https://github.com/senyingxidi/dsh-tokenlens/actions/workflows/ci.yml)
8
8
  [![Node.js](https://img.shields.io/badge/node-%3E%3D24-brightgreen)](package.json)
9
9
 
10
10
  > **一眼看清你的用量**:在 DSH 输入框下方显示当前供应商的套餐容量,并汇总本机跨 Harness 的 Token 消耗。
11
11
 
12
- **dsh-TokenLens(词元透镜)** 是 DSH(DeepSeek Harness)的用量插件(v1.3.0,公开版本)。它把两类数字**按不同口径**分开呈现:
12
+ **dsh-TokenLens(词元透镜)** 是 DSH(DeepSeek Harness)的用量插件(v1.3.3,公开版本)。它把两类数字**按不同口径**分开呈现:
13
13
 
14
14
  - **套餐容量**(供应商服务端权威值):百分比窗口、`已用 / 上限`、账户余额或剩余额度;
15
15
  - **本机 Token 统计**(本机记录):当前供应商在本机所有已接入 Harness 上的合计用量,也可切到「全部 Harness」看总账。
@@ -41,7 +41,7 @@
41
41
  - **本机 Token 统计**:今日 / 最近 7 天 / 最近 30 天 / 总计(订阅类供应商显示「最近 X 天」生效窗口),可按「当前供应商 · 全部 Harness」与「全部 Harness」两个视角查看。
42
42
  - **明细与图表**:按 Harness、按供应商、Top 模型、未映射供应商分组;支持「数据」(表格)与「图表」两种模式切换。图表模式里点击某一天的柱子可下钻查看**当天 24 小时**的 Token 消耗,底部「明细」环形图支持 **今日 / 最近 7 天 / 最近 X 天(或最近 30 天)/ 总计** 窗口切换。
43
43
  - **工作区徽标**(实验性):左侧工作区列表的名称旁显示该工作区的本机 Token 合计,悬浮卡片显示今日 / 7 天 / 30 天 / 总计;纯展示,找不到对应 UI 时自动降级。
44
- - **供应商管理页**:「已配置 / 内置 / 全部」筛选、启用开关、凭据状态、控制台链接、短标签与全名编辑、测试连接、已订阅徽章;另有「插件设置」可配置标签样式、默认展示模式、默认统计范围、明细展开状态、图表「明细」环形图的默认窗口(今日 / 最近 7 天 / 最近 X 天 / 总计,记住上次选择)与「启动时检查更新」(默认关闭,可手动立即检查)。
44
+ - **供应商管理页**:「已配置 / 内置 / 全部」筛选、启用开关、凭据状态、控制台链接、短标签与全名编辑、测试连接、已订阅徽章;另有「插件设置」可配置标签样式、默认展示模式、默认统计范围、明细展开状态、图表「明细」环形图的默认窗口(今日 / 最近 7 天 / 最近 X 天 / 总计,记住上次选择)与「启动时检查更新」(默认开启,可关闭,也可手动立即检查)。
45
45
  - **自动识别**:在 DSH「设置 → 模型服务」中配置过的供应商会自动出现在管理页,并按路由 id 或 baseURL 主机匹配内置用量适配器,自动继承 DSH 中的凭据,无需重复填写。
46
46
  - **Agent 工具**:注册 `tokenlens_usage`,模型可以自行查询套餐容量与本机用量。
47
47
 
@@ -53,27 +53,27 @@
53
53
 
54
54
  ### 输入框下方的用量 chip
55
55
 
56
- ![输入框下方的用量 chip](https://raw.githubusercontent.com/daigahou/dsh-tokenlens/main/docs/images/composer-chip.png)
56
+ ![输入框下方的用量 chip](https://raw.githubusercontent.com/senyingxidi/dsh-tokenlens/main/docs/images/composer-chip.png)
57
57
 
58
58
  ### 工作区 Token 徽标与悬浮统计
59
59
 
60
- ![工作区 Token 徽标与悬浮统计](https://raw.githubusercontent.com/daigahou/dsh-tokenlens/main/docs/images/workspace-badge-hover.png)
60
+ ![工作区 Token 徽标与悬浮统计](https://raw.githubusercontent.com/senyingxidi/dsh-tokenlens/main/docs/images/workspace-badge-hover.png)
61
61
 
62
62
  ### 用量详情弹窗
63
63
 
64
64
  | 图表模式 | 数据模式 |
65
65
  |:---:|:---:|
66
- | ![统计弹窗 · 图表模式](https://raw.githubusercontent.com/daigahou/dsh-tokenlens/main/docs/images/stats-chart.png) | ![统计弹窗 · 数据模式](https://raw.githubusercontent.com/daigahou/dsh-tokenlens/main/docs/images/stats-data.png) |
66
+ | ![统计弹窗 · 图表模式](https://raw.githubusercontent.com/senyingxidi/dsh-tokenlens/main/docs/images/stats-chart.png) | ![统计弹窗 · 数据模式](https://raw.githubusercontent.com/senyingxidi/dsh-tokenlens/main/docs/images/stats-data.png) |
67
67
 
68
68
  ### 供应商管理页
69
69
 
70
70
  | 供应商配置 | 插件设置 |
71
71
  |:---:|:---:|
72
- | ![供应商配置](https://raw.githubusercontent.com/daigahou/dsh-tokenlens/main/docs/images/settings-providers.png) | ![插件设置](https://raw.githubusercontent.com/daigahou/dsh-tokenlens/main/docs/images/settings-plugin.png) |
72
+ | ![供应商配置](https://raw.githubusercontent.com/senyingxidi/dsh-tokenlens/main/docs/images/settings-providers.png) | ![插件设置](https://raw.githubusercontent.com/senyingxidi/dsh-tokenlens/main/docs/images/settings-plugin.png) |
73
73
 
74
74
  ### 内置供应商池
75
75
 
76
- ![内置供应商池](https://raw.githubusercontent.com/daigahou/dsh-tokenlens/main/docs/images/settings-builtin.png)
76
+ ![内置供应商池](https://raw.githubusercontent.com/senyingxidi/dsh-tokenlens/main/docs/images/settings-builtin.png)
77
77
 
78
78
  ## 安装
79
79
 
@@ -106,7 +106,7 @@ dsh plugin --profile <profile> add dsh-tokenlens
106
106
  ```jsonc
107
107
  {
108
108
  "dependencies": {
109
- "dsh-tokenlens": "^1.3.0"
109
+ "dsh-tokenlens": "^1.3.3"
110
110
  },
111
111
  "dsh": {
112
112
  "profile": {
@@ -128,13 +128,13 @@ dsh plugin --profile <profile> add dsh-tokenlens
128
128
  无法访问 npm(或想直接跟踪仓库最新代码)时,可以用仓库地址安装(需要能访问 GitHub):
129
129
 
130
130
  1. DSH → 侧边栏「插件」→「添加插件」;
131
- 2. 输入 `https://github.com/daigahou/dsh-tokenlens`(或 `github:daigahou/dsh-tokenlens`),选择安装源后点「安装」;
131
+ 2. 输入 `https://github.com/senyingxidi/dsh-tokenlens`(或 `github:senyingxidi/dsh-tokenlens`),选择安装源后点「安装」;
132
132
  3. 安装完成后 DSH 会把它选入 profile 的 `dsh.profile.bundles` 并默认启用;若界面没有变化,重启 DSH。
133
133
 
134
134
  等价的 CLI 写法(只安装依赖,仍需手动把 `dsh-tokenlens` 加入 `dsh.profile.bundles`):
135
135
 
136
136
  ```powershell
137
- dsh plugin --profile <profile> add "github:daigahou/dsh-tokenlens"
137
+ dsh plugin --profile <profile> add "github:senyingxidi/dsh-tokenlens"
138
138
  ```
139
139
 
140
140
  > 注意:GitHub 源安装的是**默认分支最新提交**(不是 tag),DSH 不支持原地升级,升级时先卸载再安装。国内网络访问 GitHub 可能不稳定;插件发布到 npm 后建议改用方式 A。
@@ -165,7 +165,7 @@ dsh plugin --profile <profile> add "link:<path-to-dsh-tokenlens>"
165
165
  1. 打开 DSH → 侧边栏「插件」→「实验性插件」旁的「更多」→ 打开 **自定义插件市场**;
166
166
  2. 「已安装」旁出现 **发现** 标签 → 点 **添加索引源**,粘贴:
167
167
  ```
168
- https://raw.githubusercontent.com/daigahou/dsh-tokenlens/main/market/index.json
168
+ https://raw.githubusercontent.com/senyingxidi/dsh-tokenlens/main/market/index.json
169
169
  ```
170
170
  3. 在「发现」里搜索 `TokenLens`(或浏览 `dsh-TokenLens` 卡片)→ 安装。
171
171
 
@@ -371,7 +371,7 @@ Host 半注册 `tokenlens_usage`,参数 `action`(默认 `snapshot`)与可
371
371
 
372
372
  - **只读**:插件只读取本机会话日志与各 harness 的本地数据库 / JSONL;SQLite 一律以只读方式打开,只取 token 计数、时间、provider / model 等字段,不读取对话正文。
373
373
  - **凭据不出本机**:API Key 只在 Host 进程内从 DSH 凭据服务 / 环境变量 / `.credentials.yaml` / OpenCode CLI 登录文件解析,只发往对应供应商的官方 HTTPS 接口;不下发浏览器、不写日志、不进诊断输出。
374
- - **无遥测**:插件不收集、不上传任何使用数据,不请求远程配置或 CDN 资源(图标均为内联 SVG)。唯一的可选外网请求是「启动时检查更新」(v1.3.0,默认关闭):开启后仅向 npm registry 查询一次最新版本号,不携带任何账号、凭据或用量信息。
374
+ - **无遥测**:插件不收集、不上传任何使用数据,不请求远程配置或 CDN 资源(图标均为内联 SVG)。唯一的外网请求是「启动时检查更新」(v1.3.0 引入,v1.3.1 起默认开启,可在插件设置关闭):仅向 npm registry 查询一次最新版本号,不携带任何账号、凭据或用量信息。
375
375
  - **覆盖文件仅本人可读**:供应商管理页的覆盖写入 `$DSH_HOME/dsh-tokenlens/providers.json`(原子写入,权限仅当前用户),不会改动 DSH 模型设置或 profile patch。
376
376
  - **可控降级**:上游接口不可用时只影响对应 chip / 行,不影响 DSH 本身;`enabled: false` 可整体停用插件。
377
377
 
@@ -380,7 +380,7 @@ Host 半注册 `tokenlens_usage`,参数 `action`(默认 `snapshot`)与可
380
380
  - 各供应商的用量接口多为**未公开的私有接口**,可能随时变更;插件对此做了优雅降级(可重试错误、静默隐藏、行级降级),但不保证长期可用。
381
381
  - 套餐容量只显示服务端原生数字:不做 token → 金额换算;Command Code 未报告 grant 时不计算月度百分比。
382
382
  - 「预计可消耗」是基于本机 Token 与套餐百分比的**估算**,仅供参考,不代表供应商承诺的额度。
383
- - 「启动时检查更新」只对比 **npm 上的最新发布版本**(默认关闭);GitHub 仓库的提交更新不会被感知,需要卸载重装 git 安装的版本才会更新。
383
+ - 「启动时检查更新」默认开启(可在插件设置关闭),只对比 **npm 上的最新发布版本**;GitHub 仓库的提交更新不会被感知,需要卸载重装 git 安装的版本才会更新。
384
384
  - 本机 Token 统计只覆盖**已接入 harness**(当前 DSH / ZCode / OpenCode CLI / WorkBuddy / Qoder CN),只统计本机的本地记录。
385
385
  - Harness 卸载后其数据目录消失即判定为未安装,默认从弹窗隐藏(可用 `stats.showUninstalled: true` 显示「未安装」行);数据目录仍在磁盘时按历史记录继续计入。
386
386
  - ZCode 数据库通常很大且持续写入:插件只做聚合查询并缓存结果,数据库被锁时会短暂显示不可用。
@@ -392,7 +392,7 @@ Host 半注册 `tokenlens_usage`,参数 `action`(默认 `snapshot`)与可
392
392
 
393
393
  ## 验证
394
394
 
395
- - `node --test`:**277 个自动化用例**,覆盖 Host 数据层、供应商适配器、统计折叠、各 harness 读取、插件集成与浏览器端渲染 / 交互。
395
+ - `node --test`:**280 个自动化用例**,覆盖 Host 数据层、供应商适配器、统计折叠、各 harness 读取、插件集成与浏览器端渲染 / 交互。
396
396
  - 在真实环境中验证过 **5 个 harness** 的数据源;本机统计与独立的 SQLite 查询、独立的日志解析逐项对账(差额为 0),跨 harness 合并与供应商映射算术自洽。
397
397
  - 供应商接口按各官方文档与参考客户端行为验证;未配置凭据时 chip 静默隐藏,接口异常时按可重试错误降级。
398
398
 
@@ -401,9 +401,9 @@ Host 半注册 `tokenlens_usage`,参数 `action`(默认 `snapshot`)与可
401
401
  要求 **Node.js ≥ 24**(DSH Desktop 自带;用到 `node:sqlite` 与 zstd 解压),无运行时 npm 依赖:
402
402
 
403
403
  ```powershell
404
- git clone https://github.com/daigahou/dsh-tokenlens.git
404
+ git clone https://github.com/senyingxidi/dsh-tokenlens.git
405
405
  cd dsh-tokenlens
406
- node --test # 277 个用例
406
+ node --test # 280 个用例
407
407
  ```
408
408
 
409
409
  本地联调安装方式见[方式 C](#方式-c本地开发从源码-link)。
@@ -437,4 +437,4 @@ node scripts/bump-version.mjs <version> "<说明>"
437
437
 
438
438
  ## 许可
439
439
 
440
- [MIT](LICENSE) © daigahou
440
+ [MIT](LICENSE) © senyingxidi
package/client.js CHANGED
@@ -145,9 +145,10 @@ window.__ModuleLoader__.load({
145
145
  defaultScope: 'provider',
146
146
  detailsExpanded: 'auto',
147
147
  detailsWindow: 'total',
148
- // 「启动时检查更新」 is persisted too; off by default so an old Host and a
149
- // fresh install never contact the npm registry (privacy first).
150
- updateCheck: false,
148
+ // 「启动时检查更新」 is persisted too. ON by default (v1.3.1) so users learn
149
+ // about new releases; switching it off keeps the plugin fully local-only.
150
+ // Every other value (an old Host omitting the field included) means ON.
151
+ updateCheck: true,
151
152
  }
152
153
  /** The npm package page the 「打开 npm」 update links point at. */
153
154
  const NPM_PACKAGE_URL = 'https://www.npmjs.com/package/dsh-tokenlens'
@@ -354,7 +355,7 @@ window.__ModuleLoader__.load({
354
355
  settingsResetDefaults: '恢复默认设置',
355
356
  settingsResetDefaultsHint: '恢复为插件默认的显示设置',
356
357
  updateCheckLabel: '启动时检查更新',
357
- updateCheckHint: '仅当开启时才访问 npm registry 检查新版本;默认关闭',
358
+ updateCheckHint: '默认开启:开启时访问 npm registry 检查新版本;关闭后插件完全本地运行',
358
359
  updateCheckOff: '关闭',
359
360
  updateCheckOn: '开启',
360
361
  updateChecking: '正在检查…',
@@ -364,6 +365,11 @@ window.__ModuleLoader__.load({
364
365
  updateCheckNow: '立即检查',
365
366
  updateCheckNever: '尚未检查',
366
367
  updateOpenNpm: '打开 npm',
368
+ // The update status now lives on the plugin-version line of the dialog
369
+ // body (footer line removed); these two key the version-line suffix.
370
+ updateVersionNew: '有新版本 v{latest}',
371
+ updateVersionLatest: '已是最新版本',
372
+ // Kept for compatibility (the footer line is no longer rendered).
367
373
  updateDialogLine: '发现新版本 v{latest} · 打开 npm',
368
374
  providerBothLabels: '短标签:{short} · 全名:{full}',
369
375
  providerShortLabel: '短标签',
@@ -594,7 +600,7 @@ window.__ModuleLoader__.load({
594
600
  settingsResetDefaults: 'Restore defaults',
595
601
  settingsResetDefaultsHint: 'Restore the plugin default display settings',
596
602
  updateCheckLabel: 'Check for updates at startup',
597
- updateCheckHint: 'Contacts the npm registry for a new version only when enabled; off by default',
603
+ updateCheckHint: 'On by default: contacts the npm registry to check for a new version; switch it off to keep the plugin fully local',
598
604
  updateCheckOff: 'Off',
599
605
  updateCheckOn: 'On',
600
606
  updateChecking: 'Checking…',
@@ -604,6 +610,11 @@ window.__ModuleLoader__.load({
604
610
  updateCheckNow: 'Check now',
605
611
  updateCheckNever: 'Not checked yet',
606
612
  updateOpenNpm: 'Open on npm',
613
+ // The update status now lives on the plugin-version line of the dialog
614
+ // body (footer line removed); these two key the version-line suffix.
615
+ updateVersionNew: 'New version v{latest}',
616
+ updateVersionLatest: 'Up to date',
617
+ // Kept for compatibility (the footer line is no longer rendered).
607
618
  updateDialogLine: 'New version v{latest} · Open on npm',
608
619
  providerBothLabels: 'Short label: {short} · Full name: {full}',
609
620
  providerShortLabel: 'Short label',
@@ -1160,9 +1171,10 @@ window.__ModuleLoader__.load({
1160
1171
  defaultScope: normalizeDisplayScope(source.defaultScope),
1161
1172
  detailsExpanded: normalizeDetailsExpanded(source.detailsExpanded),
1162
1173
  detailsWindow: normalizeDetailsWindow(source.detailsWindow),
1163
- // The startup update check is opt-in: a value other than `true` (an old
1164
- // Host omits the field entirely) means off.
1165
- updateCheck: source.updateCheck === true,
1174
+ // The startup update check is ON by default (v1.3.1): only an explicit
1175
+ // boolean is honored, so an old Host that omits the field stays on.
1176
+ updateCheck:
1177
+ typeof source.updateCheck === 'boolean' ? source.updateCheck : DEFAULT_PLUGIN_SETTINGS.updateCheck,
1166
1178
  }
1167
1179
  }
1168
1180
 
@@ -1280,6 +1292,9 @@ window.__ModuleLoader__.load({
1280
1292
  settings: normalizePluginSettings(settings),
1281
1293
  update: hasUpdate ? normalizeUpdate(raw.update) : null,
1282
1294
  hasUpdate,
1295
+ // v1.3.2: the running Host version. A page whose one-time `hello` was
1296
+ // answered before a plugin update can adopt the new value from here.
1297
+ version: stringOrEmpty(raw.version).trim(),
1283
1298
  // Optional: the Host may advertise the npm page its links should open.
1284
1299
  updatePackageUrl: stringOrEmpty(raw.updatePackageUrl).trim(),
1285
1300
  labelCatalog,
@@ -2174,6 +2189,12 @@ window.__ModuleLoader__.load({
2174
2189
  if (parsed.updatePackageUrl !== '' && current.updatePackageUrl !== parsed.updatePackageUrl) {
2175
2190
  patch.updatePackageUrl = parsed.updatePackageUrl
2176
2191
  }
2192
+ // v1.3.2: the answered Host version refreshes a stale hello version (the
2193
+ // page may have loaded before a plugin update/recomposition). Never
2194
+ // downgrade: hello and this answer race, and the newest version wins.
2195
+ if (parsed.version !== '' && newerVersionOf(current.pluginVersion, parsed.version) === parsed.version) {
2196
+ if (current.pluginVersion !== parsed.version) patch.pluginVersion = parsed.version
2197
+ }
2177
2198
  if (parsed.labelCatalog !== null && typeof parsed.labelCatalog === 'object') {
2178
2199
  const catalog = { ...current.labelCatalog, ...parsed.labelCatalog }
2179
2200
  if (Object.keys(catalog).length > 0) patch.labelCatalog = catalog
@@ -2879,7 +2900,13 @@ window.__ModuleLoader__.load({
2879
2900
  if (data === null || typeof data !== 'object') return
2880
2901
  const patch = {}
2881
2902
  if (typeof data.version === 'string' && data.version !== '') {
2882
- if (store.get().pluginVersion !== data.version) patch.pluginVersion = data.version
2903
+ // v1.3.2: hello and the startup providers bootstrap race; only the
2904
+ // newest known Host version wins (a stale hello must not downgrade the
2905
+ // label after an in-place plugin update).
2906
+ const known = store.get().pluginVersion
2907
+ if (newerVersionOf(known, data.version) === data.version && known !== data.version) {
2908
+ patch.pluginVersion = data.version
2909
+ }
2883
2910
  }
2884
2911
  // `update` is optional on the wire: only an explicit field (null included)
2885
2912
  // replaces what the store already knows.
@@ -3238,7 +3265,11 @@ window.__ModuleLoader__.load({
3238
3265
  .dsh-tokenlens-chart-drill-title{color:var(--dsw-alias-label-secondary);font-size:var(--dsw-alias-font-size-sm,12px);line-height:1.4;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-variant-numeric:tabular-nums}
3239
3266
  /* 明细 donut window switch (v1.2.0): one pill row under the details title. */
3240
3267
  .dsh-tokenlens-details-window{display:flex;align-items:center;gap:6px;flex-wrap:wrap;min-width:0}
3241
- .dsh-tokenlens-version{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.5;text-align:right;opacity:.75}
3268
+ .dsh-tokenlens-version{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.5;text-align:right;opacity:.75;overflow-wrap:anywhere}
3269
+ /* Update status on the plugin-version line (dialog body): wraps on narrow screens. */
3270
+ .dsh-tokenlens-version-update{white-space:normal;overflow-wrap:anywhere}
3271
+ .dsh-tokenlens-version .dsh-tokenlens-update-link{color:inherit;font-size:inherit;vertical-align:baseline}
3272
+ .dsh-tokenlens-version .dsh-tokenlens-update-link .dsh-tokenlens-icon{margin-left:2px;vertical-align:-1px}
3242
3273
  .dsh-tokenlens-cards{gap:6px;flex-wrap:wrap;display:flex}
3243
3274
  .dsh-tokenlens-cards-caption{color:var(--dsw-alias-label-tertiary);font-size:var(--dsw-alias-font-size-sm,12px);line-height:1.4;margin-bottom:-3px}
3244
3275
  .dsh-tokenlens-harness-breakdown{display:flex;flex-direction:column;gap:4px}
@@ -6786,43 +6817,104 @@ window.__ModuleLoader__.load({
6786
6817
  }
6787
6818
 
6788
6819
  /**
6789
- * The usage dialog's 「发现新版本」 footer line, or null when the last check
6790
- * found no update (or never ran). The i18n sentence names 「打开 npm」 as its
6791
- * tail; that tail becomes the anchor that opens the npm package page, so the
6792
- * line itself stays a single sentence with one real link inside it.
6820
+ * Numeric parts of a plain `x.y.z` version, or null (v1.3.2).
6821
+ */
6822
+ function versionPartsOf(value) {
6823
+ if (typeof value !== 'string') return null
6824
+ const match = /^v?(\d{1,9})\.(\d{1,9})\.(\d{1,9})$/.exec(value.trim())
6825
+ return match === null ? null : [Number(match[1]), Number(match[2]), Number(match[3])]
6826
+ }
6827
+
6828
+ /**
6829
+ * The newer of two version strings (v1.3.2). Invalid or empty sides fall
6830
+ * back to the other one, so the dialog label never goes blank.
6831
+ */
6832
+ function newerVersionOf(left, right) {
6833
+ const a = versionPartsOf(left)
6834
+ const b = versionPartsOf(right)
6835
+ if (a === null) return b === null ? '' : right.trim()
6836
+ if (b === null) return left.trim()
6837
+ for (let index = 0; index < 3; index += 1) {
6838
+ if (a[index] !== b[index]) return a[index] > b[index] ? left.trim() : right.trim()
6839
+ }
6840
+ return left.trim()
6841
+ }
6842
+
6843
+ /**
6844
+ * The best-known running Host version for the dialog label (v1.3.2).
6845
+ *
6846
+ * `state.pluginVersion` is captured from the one-time `hello` announcement,
6847
+ * while `state.update.current` arrives with the startup update check and the
6848
+ * providers payload carries the same Host version. Picking the newest of the
6849
+ * two makes the label self-heal after a plugin update without a page reload.
6850
+ */
6851
+ function currentPluginVersionOf(state) {
6852
+ const fromHello = typeof state.pluginVersion === 'string' ? state.pluginVersion : ''
6853
+ const fromCheck = state.update !== null && typeof state.update.current === 'string' ? state.update.current : ''
6854
+ return newerVersionOf(fromHello, fromCheck).trim()
6855
+ }
6856
+
6857
+ /**
6858
+ * The update status appended to the plugin-version line of the usage dialog
6859
+ * body, or null when it must stay silent.
6860
+ *
6861
+ * Rules (a check result only): an available release renders as
6862
+ * 「 · 有新版本 vX」 with the version phrase linking to the npm page; a
6863
+ * successful check without a newer release renders 「 · 已是最新版本」.
6864
+ * No result, a failed check or an explicitly disabled setting adds nothing
6865
+ * (never show a misleading status).
6793
6866
  */
6794
- function buildUpdateFooterLine(state, t) {
6867
+ function buildVersionUpdateStatus(state, t) {
6868
+ if (state.updateCheck !== true) return null
6795
6869
  const update = state.update
6796
- if (update === null || update.available !== true) return null
6797
- const text = t('updateDialogLine', { latest: update.latest })
6798
- const linkLabel = t('updateOpenNpm')
6799
- const packageUrl = updatePackageUrlOf(state)
6800
- const link = h(
6801
- 'a',
6802
- {
6803
- key: 'link',
6804
- className: 'dsh-tokenlens-update-link dsh-tokenlens-update-footer-link',
6805
- 'data-update-link': 'true',
6806
- href: packageUrl,
6807
- target: '_blank',
6808
- rel: 'noreferrer noopener',
6809
- title: packageUrl,
6810
- onClick: (event) => openConsole(packageUrl, event),
6811
- },
6812
- linkLabel,
6813
- Icon('external', 12),
6814
- )
6815
- const index = text.indexOf(linkLabel)
6816
- const children =
6817
- index < 0
6818
- ? [h('span', { key: 'text' }, text), h('span', { key: 'sep' }, ' · '), link]
6819
- : [h('span', { key: 'text' }, text.slice(0, index)), link]
6820
- return h(
6821
- 'div',
6822
- { key: 'update-line', className: 'dsh-tokenlens-update-line', 'data-update-line': 'true' },
6823
- Icon('bolt', 12),
6824
- children,
6825
- )
6870
+ if (update === null || update.error !== null) return null
6871
+ if (update.available === true && update.latest !== null) {
6872
+ const packageUrl = updatePackageUrlOf(state)
6873
+ // The version NUMBER inside the sentence is the real npm link; the i18n
6874
+ // sentence itself stays a single string (`updateVersionNew`).
6875
+ const sentence = t('updateVersionNew', { latest: update.latest })
6876
+ const versionText = `v${update.latest}`
6877
+ const index = sentence.indexOf(versionText)
6878
+ const link = h(
6879
+ 'a',
6880
+ {
6881
+ className: 'dsh-tokenlens-update-link dsh-tokenlens-version-link',
6882
+ 'data-update-link': 'true',
6883
+ 'data-update-version-link': 'true',
6884
+ href: packageUrl,
6885
+ target: '_blank',
6886
+ rel: 'noreferrer noopener',
6887
+ title: packageUrl,
6888
+ onClick: (event) => openConsole(packageUrl, event),
6889
+ },
6890
+ index >= 0 ? versionText : `v${update.latest}`,
6891
+ Icon('external', 12),
6892
+ )
6893
+ return h(
6894
+ 'span',
6895
+ {
6896
+ key: 'update-status',
6897
+ className: 'dsh-tokenlens-version-update',
6898
+ 'data-update-version-status': 'available',
6899
+ },
6900
+ ' · ',
6901
+ index >= 0 ? sentence.slice(0, index) : `${sentence} `,
6902
+ link,
6903
+ )
6904
+ }
6905
+ if (update.latest !== null) {
6906
+ return h(
6907
+ 'span',
6908
+ {
6909
+ key: 'update-status',
6910
+ className: 'dsh-tokenlens-version-update',
6911
+ 'data-update-version-status': 'latest',
6912
+ },
6913
+ ' · ',
6914
+ t('updateVersionLatest'),
6915
+ )
6916
+ }
6917
+ return null
6826
6918
  }
6827
6919
 
6828
6920
  /** The dialog contents (body + footer); may throw, the caller guards it. */
@@ -6850,12 +6942,16 @@ window.__ModuleLoader__.load({
6850
6942
  ),
6851
6943
  )
6852
6944
  const body = [buildManageButton(state, t), statsArea]
6853
- if (typeof state.pluginVersion === 'string' && state.pluginVersion !== '') {
6945
+ const displayVersion = currentPluginVersionOf(state)
6946
+ if (displayVersion !== '') {
6854
6947
  body.push(
6855
6948
  h(
6856
6949
  'div',
6857
6950
  { key: 'version', className: 'dsh-tokenlens-version' },
6858
- t('pluginVersion', { version: state.pluginVersion }),
6951
+ t('pluginVersion', { version: displayVersion }),
6952
+ // Update status sits right next to the plugin version: 有新版本 vX
6953
+ // (link to the npm page) or 已是最新版本, only for a successful check.
6954
+ buildVersionUpdateStatus(state, t),
6859
6955
  ),
6860
6956
  )
6861
6957
  }
@@ -6884,9 +6980,6 @@ window.__ModuleLoader__.load({
6884
6980
  Icon('external', 12),
6885
6981
  consoleLabel,
6886
6982
  ),
6887
- // 「发现新版本」 line (only while an update is available): its 「打开 npm」
6888
- // tail is the real npm-package link, next to the refresh/close buttons.
6889
- buildUpdateFooterLine(state, t),
6890
6983
  h(
6891
6984
  Button,
6892
6985
  {
package/index.js CHANGED
@@ -74,7 +74,7 @@ export const inject = []
74
74
  export const PLUGIN_NAME = 'dsh-tokenlens'
75
75
 
76
76
  /** Plugin version; mirrors package.json (kept here so diagnostics need no fs read). */
77
- export const PLUGIN_VERSION = '1.3.0'
77
+ export const PLUGIN_VERSION = '1.3.3'
78
78
 
79
79
  /** Documented defaults; every field of the raw row config is optional. */
80
80
  const DEFAULTS = {
@@ -733,12 +733,13 @@ export function apply(ctx, rawConfig) {
733
733
 
734
734
  return {
735
735
  ok: true,
736
+ // The running Host version: a page whose `hello` was answered before a
737
+ // plugin update can adopt the new version from this payload (v1.3.2).
738
+ version: PLUGIN_VERSION,
736
739
  providers,
737
740
  dshProviders,
738
741
  settings: sanitizeSettings(providerStore.settings),
739
- // Optional startup update check (settings.updateCheck): null until the
740
- // first check; never fetched unless the setting is enabled or the user
741
- // presses "check now".
742
+ // Update check result (settings.updateCheck): null until the first check.
742
743
  update: state.update,
743
744
  updatePackageUrl: UPDATE_PACKAGE_URL,
744
745
  storeFile: redactPaths(providerStoreFile),
@@ -1846,9 +1847,9 @@ export function apply(ctx, rawConfig) {
1846
1847
  }
1847
1848
 
1848
1849
  noteStatus('activate', { pollIntervalMs: config.pollIntervalMs })
1849
- // Optional startup update check: one plain npm-registry request, only when the
1850
- // user enabled `settings.updateCheck`. Off by default to keep the plugin
1851
- // local-only; failures are recorded and never break activation.
1850
+ // Startup update check: one plain npm-registry request unless the user turned
1851
+ // `settings.updateCheck` off (default on since v1.3.1). Failures are recorded
1852
+ // and never break activation.
1852
1853
  if (sanitizeSettings(providerStore.settings).updateCheck === true) {
1853
1854
  void runUpdateCheck()
1854
1855
  }
@@ -40,9 +40,10 @@ export const DEFAULT_SETTINGS = {
40
40
  detailsExpanded: 'auto',
41
41
  // v1.2.1 图表明细窗口默认值: 总计 (the last choice is persisted there).
42
42
  detailsWindow: 'total',
43
- // Optional startup update check (npm registry); off by default so the plugin
44
- // stays local-only unless the user opts in.
45
- updateCheck: false,
43
+ // Optional startup update check (npm registry); ON by default (v1.3.1) so users
44
+ // learn about new releases, and can be switched off to keep the plugin fully
45
+ // local-only. Only the published version number is requested.
46
+ updateCheck: true,
46
47
  }
47
48
 
48
49
  /** The default override file: `$DSH_HOME/dsh-tokenlens/providers.json`. */
@@ -129,7 +130,7 @@ export function sanitizeSettings(value) {
129
130
  detailsWindow: DETAILS_WINDOWS.includes(source.detailsWindow)
130
131
  ? source.detailsWindow
131
132
  : DEFAULT_SETTINGS.detailsWindow,
132
- updateCheck: source.updateCheck === true,
133
+ updateCheck: typeof source.updateCheck === 'boolean' ? source.updateCheck : DEFAULT_SETTINGS.updateCheck,
133
134
  }
134
135
  }
135
136
 
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "dsh-tokenlens",
3
- "version": "1.3.0",
3
+ "version": "1.3.3",
4
4
  "type": "module",
5
5
  "description": "dsh-TokenLens (词元透镜) — provider-aware plan capacity plus local token statistics (today/7d/30d/all-time, by harness/provider/model/workspace) for DSH.",
6
6
  "license": "MIT",
7
- "author": "daigahou",
7
+ "author": "senyingxidi",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "git+https://github.com/daigahou/dsh-tokenlens.git"
10
+ "url": "git+https://github.com/senyingxidi/dsh-tokenlens.git"
11
11
  },
12
12
  "bugs": {
13
- "url": "https://github.com/daigahou/dsh-tokenlens/issues"
13
+ "url": "https://github.com/senyingxidi/dsh-tokenlens/issues"
14
14
  },
15
- "homepage": "https://github.com/daigahou/dsh-tokenlens#readme",
15
+ "homepage": "https://github.com/senyingxidi/dsh-tokenlens#readme",
16
16
  "keywords": [
17
17
  "dsh",
18
18
  "dsh-plugin",