dsh-composer-ux 0.25.0 → 0.26.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,115 @@
2
2
 
3
3
  本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## [0.26.1] — 2026-10-28(升到 DSH 0.2.2-alpha.2 实测:三处真机 bug)
6
+
7
+ 用户 2026-10-28:「更新网页端到最新版,并在上面测试插件稳定性修bug」。按他选的 **B**
8
+ (`dsh-v0.2.2-alpha.2`)把源码 checkout 升上去、起在备用端口 3977(不动桌面端 6913),
9
+ 在**隔离 profile**(`ux-alpha`,只装本插件)上把功能过了一遍。
10
+
11
+ **三处真机 bug(都不是"看起来坏了",而是"看起来正常、数字是错的")**
12
+
13
+ 1. **会话 id 形状变了 ⇒ 所有会话都显示 ¥0**(最严重)。
14
+ 0.2.2-alpha.2 起 `sessionQuery.readSession()` 只认 `session-<uuid>` 全写;客户端半传下来的是
15
+ 裸 uuid(0.2.1 及以前两种写法都不认错)。实测同一个会话:裸 uuid ⇒
16
+ `session "…" not found`、`session-…` ⇒ **4036 条事件 / 4 条 route / 2 个子代理**。
17
+ 修法:宿主半两种写法都试,并把成功的那种**记住**(下次不再多问一次)。
18
+ 2. **读日志失败被静默吞掉**。这条路由过去 `catch { return undefined }`,于是失败 ⇒ 全 0 的账 ⇒
19
+ 屏幕上是一个「¥0」,看起来像"这个会话没花钱"。现在宿主半回 `readError`,浮层写明
20
+ 「读不到会话日志:…(金额按 0 计)」。
21
+ 3. **自检卡永远写「DSH (拿不到)」**。不是拿不到,是那一行**从来没去读**(写死空串)。
22
+ 新增 `src/runtime-version.ts`:按官方 `app-boot` 的判据读它自己的 `package.json`
23
+ (入口解析 → profiles 扫描 → 都没有就空串,绝不猜版本号),实测读到 `0.2.2-alpha.2`。
24
+
25
+ **另外两件与新版宿主有关的事(不是代码 bug,但会让人以为插件坏了)**
26
+
27
+ - **持久终端工具在 alpha 上被宿主禁用**:`@deepseek-ai/dsh-tool-terminal` 的版本要求是
28
+ **精确**的(`0.2.1-rc.4` 只认 rc.4 运行时),而 npm 的 semver 规则让我们的依赖区间
29
+ (`>=0.2.1-alpha.1 <0.3.0`)解析不到 `0.2.2-alpha.2`。修法是**在 profile 里装匹配版本**:
30
+ `dsh plugin --profile <名字> add @deepseek-ai/dsh-tool-terminal@0.2.2-alpha.2`(装完自检卡那一行变
31
+ `ok`,模型拿回那 6 个 `terminal_*` 工具)。这轮已在 `ux-alpha` 上验证。
32
+ - **dsh-purge 会改源码仓**(用户问「装在客户端为什么影响网页端」):它的补丁按**文件**解析,
33
+ `web-app` 这类键在找不到已装包时会回退到 `<repo>/packages/bundle/...`,所以客户端点「应用」
34
+ 会把共享 checkout 里 web 的那份也改掉。升级前已按用户意见**丢弃**这 10 个补丁
35
+ (桌面运行时那份独立存在,不受影响)。
36
+
37
+ **验证(都在 0.2.2-alpha.2 真机上跑过)**
38
+
39
+ - 市场/宿主:插件加载、46 个设置字段全在 schema、12 张卡渲染、`?` 说明、开关写入(`sections: 快捷指令、键位`);
40
+ - 金额:胶囊 `$1.67 订阅内`、浮层两套口径并列(额度 $1.67 / 折算 ¥11.52 / 占月额度 2.09%)、
41
+ 额度窗口 5h 1% · 本周 5% · 本月 55%(官方接口实时);
42
+ - 额度路由:`GET /composer-ux/quota` 首帧 `pending`、后台刷新后拿到真实窗口;
43
+ - 控制台:0 error / 0 warning。
44
+
45
+ **测试**:34 套件 **2858 条** 0 失败(新增 §6 会话 id 形状 4 条 + 运行时版本探测 4 条);
46
+ 变异 **229 条全部咬住**(新增 `SP`/`SQ`/`SR`)。
47
+
48
+ ## [0.26.0] — 2026-10-27(订阅通道的**额度**口径 + 设置页整体换肤)
49
+
50
+ 用户 2026-10-27:「**我总感觉子代理计费还是不准**,你参考一下这个是用什么方式计费的
51
+ (`kenz1117/dsh-ui-usage-billing`),并参考它的 UI 修改我整个插件设置的 UI」。
52
+
53
+ **先说结论:子代理的取数没错,错的是"价格口径"。**
54
+
55
+ - 本机最近 25 个会话里 **`opencode-go` 24 个、`go` 18 个** —— 用户走的是 **OpenCode Go 套餐**
56
+ ($10/月 + 分档额度)。这类通道**单次调用不扣钱、扣额度**;而 0.25.0 及以前只有一套算法:
57
+ **DeepSeek 官方刊例价 × token**(还分峰谷)。于是同一个会话算出"花了 ¥91",而他实际付的是
58
+ $10/月 —— 这个数字既不是钱包里的钱、也不是额度消耗,子代理越多偏得越多。
59
+ - 参考仓库的账本是**双口径**:`code` 计划(订阅)与 `token` 计划(按量)分开记,订阅通道
60
+ **调用成本记 0**、另列订阅费与额度窗口;它的子代理归并(header 的 `origin: 'subagent'` +
61
+ 跳过 `seq < inheritedEventCount` 的 fork 种子)与我们在 0.24.0 做的是同一套 ——
62
+ 所以差异确实在价格,不在取数。
63
+
64
+ **用户拍板的三件事(一问一答定下来的)**
65
+
66
+ 1. **C:两套口径并存** —— 额度(实付)与刊例价折算都要,标签写明各是什么;
67
+ 2. **A:胶囊主数字 = 本会话"额度消耗金额"**(美元,含子代理),旁边加 `订阅内` 小标;
68
+ 点开看 5 小时 / 本周 / 本月额度、按模型占月额度比例、以及按刊例价折算的 ¥;
69
+ 3. **B:12 张设置卡整体换视觉语言**(参考仓库那套卡片 / 标题 + `?` 说明 / 分段控件 / 行语言),
70
+ **不加**它的左侧锚点导航(会和 DSH 自己的 section 导航叠成双层)。
71
+
72
+ **额度是怎么算的**
73
+
74
+ - 官方 Go 价目表给出每模型每百万 token 的单价与**月度上限**(V4.1 Flash:谷
75
+ $0.15 / 命中 $0.003 / 输出 $0.60,峰翻倍,月上限 $80;V4 Pro 月上限 $15)。
76
+ 额度消耗 = **Σ token × 该档单价**,峰谷判定沿用插件自己那套(工作日 09–12 / 14–18 北京时间)。
77
+ - 额度窗口走官方接口 `GET https://opencode.ai/zen/go/v1/usage`(Bearer 该通道的 key),
78
+ 返回 `rolling` / `weekly` / `monthly` 三段**已用百分比**与重置时刻 —— 与 OpenCode 控制台
79
+ 上那三个进度条同源。本机 2026-10-27 实测:**5h 0% · 本周 3% · 本月 54%**。
80
+
81
+ **改动**
82
+
83
+ - 新增 `src/subscription-plans.ts`(零依赖):套餐表、模型名归一(`deepseek-chat` 这类旧名按
84
+ V4.1 Flash 计)、`quotaCostOf`(额度消耗 + 占月上限百分比)、额度响应解析(缺段不补 0、
85
+ 百分比钳到 0–100)、`degradeQuota`(失败**保留上一次窗口**,不许把「周 3%」擦成空白)。
86
+ - 宿主半(`src/host.ts`):`priceFold` 每条 route 多算一份 `quota`(不动原有 `cost`);
87
+ 合计时**按模型合并**(同一模型跨峰谷两条 route 要先合并再除上限,否则得出"每档各 x%"的假数);
88
+ 子代理的额度也并进来;`/composer-ux/usage` 多回 `quota` 与 `plan` 两个字段。
89
+ - 新增路由 `GET /composer-ux/quota`(同一道官方信任关卡):给设置页那张卡用。
90
+ **额度取数不阻塞**:路由只读缓存(没有就回 `pending`),后台刷新、60 秒 TTL、同套餐单飞
91
+ —— 胶囊 600ms 一轮,绝不能被一次网络往返拖住(`test/subscription-billing.mjs` 第 4 节用
92
+ "永不 resolve 的 fetch"把这条钉住)。
93
+ - 金额胶囊(`src/client/CostChipEntry.tsx`):主数字换成 `$额度` + `订阅内` 小标;浮层里
94
+ 额度消耗(含子代理)/ 按模型 + 占月额度% / 三段窗口 / **按刊例价折算 ¥X** 同屏并列,
95
+ 并写明"两套口径各是什么"。按量付费的会话与老宿主逐字节保持旧行为。
96
+ - 设置页(新建 `src/client/ui-kit.tsx` + `settings-style.ts` + `SettingsSection.tsx`):
97
+ 12 张卡换成统一外壳(12px 圆角 / 1px 描边 / hover / 未启用虚线)与行语言(标题 + 说明 +
98
+ 右侧控件、`?` 悬停说明、分段控件、卡内小标题)。旧类名一个没删(`client-registration`
99
+ 逐条仍绿)。金额卡正文新增「订阅额度」块:三段进度条 + 重置时间 + 未配 key 时点名该配哪个引用。
100
+ - 顺带修三处既有问题:`AutostartCard` / `DoctorCard` 正文里的字面 `**`(渲染出来是星号)、
101
+ 「OpenCode 请求头」关掉时概览渲染成「未启用 · 未启用」、共享行样式与卡片视觉不一致。
102
+
103
+ **验证**:34 套件 **2854 条** 0 失败;变异 **226 条全部咬住**(新增 `SJ`–`SO` 六条:
104
+ 峰谷、别名、月上限、跨档合并、缓存刷新、失败保留窗口)。新增套件
105
+ `test/subscription-billing.mjs`(43 条,含真路由往返与"额度接口挂住也不卡"这条性能契约);
106
+ `test/cost-panel-render.mjs` 新增 1.3 节(10 条)。真机:`GET /composer-ux/quota` 用用户凭据
107
+ 取到真实窗口(见上)。
108
+
109
+ **未做 / 说明**:只给 OpenCode Go 建了套餐表(用户实际在用这一条);其它订阅通道仍按刊例价
110
+ 折算,等真用到再加一行。官方没公布月度上限的模型不给百分比(不拿 0 冒充)。桌面端要重启并
111
+ 装 0.26.0 才能看到新版(运行中的应用会一直占着 profile 锁,CLI 装不进去)。
112
+
113
+
5
114
  ## [0.25.0] — 2026-10-18(「OpenCode 请求头」降级为旧宿主兼容层:宿主已原生支持就不注入)
6
115
 
7
116
  用户 2026-10-18 在设置页看到这一栏,问:**「是否还需要这个功能」**。查完上下游后答案是
package/README.md CHANGED
@@ -494,6 +494,19 @@ Windows 上 DSH 给模型的终端工具是 **PowerShell**(工具名 `pwsh`)
494
494
  - **内置第三方价目快照(0.10.0)**:`src/provider-prices.ts`(**生成**的,别手改:`node scripts/gen-provider-prices.mjs <models.dev-api.json>`)收了 **11 个 provider / 346 个模型**,快照日期写在文件头。**没点过同步时**就是非 DeepSeek 模型的兜底价;点过一次「同步第三方价目」后最新数据会盖住同名条目,界面分别标明"内置快照价"还是已同步。查价优先级:**用户覆盖价 > 已同步价目 > 内置快照 > 未定价**。
495
495
  - **非 DeepSeek 模型定价(0.10.0)**:按 `(provider, model)` 查价 —— 精确 provider → `PROVIDER_ALIASES` 别名(`deepseek-official`→`deepseek`、`kimi-coding`→`moonshotai`…)→ **按模型 id 全局唯一匹配**(`glm-5` 在两家都有的价时**拒绝猜**)。**认不出价就说"未定价"**(单价全 0,明细页写明"去同步第三方价目或给这行填个价"),**不再把 DeepSeek 的 flash 价静默套到第三方模型头上** —— 编一个看着合理的假数字,比承认不知道更糟。
496
496
  - **「OpenCode 请求头」这一栏(0.25.0 起只给旧宿主)**:OpenCode Go 要求客户端按**每段对话**发 `x-opencode-session`。上游 `@earendil-works/pi-ai` 从 **1.0.2** 起自带 `dist/providers/opencode-headers.js`(`opencode` / `opencode-go` 两个 provider 都被包过),用`options.sessionId` 发**真实会话 id**,且「已有同名头就不覆盖」。本插件注入的是**固定 UUID**,开着反而会顶掉真实 id ⇒ 0.25.0 起宿主半**先探测**(判据是宿主 pi-ai 里有没有那个模块;桌面 asar与 pnpm 检出两种布局都覆盖),探到原生支持就**不注入、不生成 UUID、不记账**,状态行写「宿主已原生支持…本栏不再注入」;探不到按旧宿主处理(继续注入),也可用`DSH_COMPOSER_UX_HEADER_NATIVE=1|0` 强制。`test/host-header-mirror.mjs` 第 19 / 20 节把两条分支与探测判据逐条钉住。
497
+ - **订阅通道的额度口径(0.26.0)**:OpenCode Go 这类「月费 + 额度」的套餐**单次调用不扣钱、扣额度**,
498
+ 所以"按刊例价折算"出来的 ¥ 既不是账单、也不是额度消耗(用户 2026-10-27 的「感觉还是不准」就是这个)。
499
+ 现在宿主半对订阅通道**另算一份额度**:`额度(USD) = Σ token × 该模型该档单价`(单价与月度上限来自
500
+ Go 官方表:V4.1 Flash 谷 $0.15 / 命中 $0.003 / 输出 $0.60、峰翻倍、月上限 $80;V4 Pro 月上限 $15),
501
+ 同一模型跨峰谷的两条 route **先合并再除上限**;额度窗口走官方 `GET /zen/go/v1/usage`
502
+ (5 小时 / 本周 / 本月三段**已用百分比**,与控制台同源),60 秒缓存、同套餐单飞、**不阻塞**路由
503
+ (拿不到先回 `pending`,失败保留上一次的窗口)。金额胶囊默认显示**本会话额度消耗**并标 `订阅内`,
504
+ 展开同时给出「按刊例价折算 ¥X」——两套口径并列、各自写明是什么;设置页「金额」卡多了订阅额度进度条。
505
+ `test/subscription-billing.mjs`(43 项)钉住峰谷、别名、月上限、跨档合并、缓存刷新、失败降级,以及
506
+ "额度接口挂住也不卡"这条性能契约。
507
+ - **设置页视觉(0.26.0)**:12 张卡统一成一套行语言(标题 + 说明 + 右侧控件、`?` 悬停说明、分段控件、
508
+ 卡内小标题、12px 圆角描边外壳),原语抽在 [src/client/ui-kit.tsx](src/client/ui-kit.tsx);**没有**引入
509
+ 社区插件那种左侧锚点导航(会和 DSH 自己的 section 导航叠成双层)。
497
510
  - **为什么金额必须自己算**:DSH 送到浏览器的 `tokenUsage` 投影**只有 token 桶**,全库没有一处把"钱"送到客户端;`llm-pi-ai` 里那个 `cost` 只活在 provider 内部,而且用户自定义的路由(profile 里手写的 provider)拿到的是 `NO_COST`(全 0)。所以费用只能由本插件按刊例价算。
498
511
  - **数据全部来自官方、不新增采集**:token 桶读 `tokenUsage` 投影(与官方统计行**同一份**),模型读 `modelSelection` 投影(来自 `request/header` 事件)。会话中途换过模型时按**最后一次请求**的模型计价(近似,浮层里写明)。
499
512
  - **按 route 分列与"逐笔准时"的取数**(点开才去问宿主半;折叠态那颗胶囊也走同一条路由):面板里列出每条 route(provider + model + 高峰/空闲)的 token 与花费,**金额由宿主半按档算好**(用户覆盖价一并读自设置),所以"各行加起来等于总额""三项相加等于合计"都精确成立。逐请求归因只能宿主半做,走只读路由 `GET /composer-ux/usage?sessionId=…`(`src/host.ts` 注册):首次(或发现 `seq` 落后)用官方 `sessionQuery.readSession(sessionId)` 完整读一次,之后靠 `session/event` 订阅**增量**喂折叠缓存(`createUsageCache`,纯逻辑在 [src/usage-fold.ts](src/usage-fold.ts))—— 所以胶囊取价是 O(1),不必反复重读整份日志。**播种窗口**(读日志期间追加的事件)会先缓冲、读完按 `seq` 补上:不这么做的话水位会跳过快照里那些中间事件,金额**永久少算**(`feed()` 的 seq 幂等,补也补不进去)。**事件形状按本版官方 `SessionEventMap` 的真实声明**(`assistant/attempt` 的 `stream` 里取最后一条 usage 块,或 `assistant/message` 的 `data.usage`;**本版没有 `assistant/chunk` 事件**),口径对齐官方 `token-meter` 的 `usage-projection`(同一步替换、`llm/retry-started` 重开替换槽)。`test/usage-fold.mjs`(89 项)逐条钉住差分、归属、重试、脏数据、逐笔分档与增量缓存;分列出不来时面板会显示"读了 N 条事件、M 条 usage、来源哪条路",一眼就能定位。
@@ -722,9 +735,9 @@ node build.mjs # 产出 lib/index.js + lib/client.js
722
735
  # ⚠️ 宿主半会**内联** schemastery / cosmokit:
723
736
  # 优先用 DSH 检出里的 vendor 副本,检出不在时退到 node_modules
724
737
  # 里同版本的 npm 包(两者逐字节相同)——CI 上走的就是退路
725
- npm test # 33 个套件;当前 2800 passed, 0 failed(2026-10-18 实测;CI 三平台同样全绿)
738
+ npm test # 34 个套件;当前 2858 passed, 0 failed(2026-10-28 实测;CI 三平台同样全绿)
726
739
  # 走 scripts/run-tests.mjs:顺带把"多少套件/多少条"记进 test/.last-run.json
727
- npm run test:mutations # 手动跑:变异测试,证明那套护栏真的在咬人(220 条,须单独跑)
740
+ npm run test:mutations # 手动跑:变异测试,证明那套护栏真的在咬人(229 条,须单独跑)
728
741
  # 同样记录结果,供下面的文档门禁核对
729
742
  npm run gates # 发版门禁三条一起跑:tag 指向 / 包内容 / 文档数字
730
743
  npm run check:tag # ① tag 名里的版本 == 该 tag 所指提交里的 package.json 版本
package/dsh-plugin.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "manifestVersion": "0.15",
4
4
  "id": "dsh-composer-ux.input-experience",
5
5
  "name": "dsh-composer-ux",
6
- "version": "0.25.0",
6
+ "version": "0.26.1",
7
7
  "license": "MIT",
8
8
  "facets": {
9
9
  "host": {