dsh-connect-trae 2.8.0 → 3.0.0
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 +596 -0
- package/README.en.md +4 -0
- package/README.md +4 -0
- package/lib/client.js +796 -14
- package/lib/index.d.ts +17 -9
- package/lib/index.js +75 -7
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,601 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.0.0 (2026-10-07)
|
|
4
|
+
|
|
5
|
+
> **积分从「侧边栏常驻一行」改为「输入框工具栏的按供应商读数 + 账号表格浮层」。**
|
|
6
|
+
>
|
|
7
|
+
> 这是一次**大版本**,因为它含一处功能移除与一处行为变更:
|
|
8
|
+
>
|
|
9
|
+
> - **侧边栏积分行已移除**(2.13.0)。它与同族插件 `dsh-connect-workbuddy` 争同一条共享行,实测被挤成裁切的碎片;现在积分只在输入框工具栏出现,且**只在选中的模型属于本插件时**出现。
|
|
10
|
+
> - **`showPointsInMainUi` 重新生效**:2.11.0–2.13.2 期间读数无视该字段、始终渲染;现在重新遵守它(**默认关闭**)。字段为 `true` 的用户读数保留。
|
|
11
|
+
>
|
|
12
|
+
> npm 上的 `latest` 此前是 **2.10.0**——2.11.0 至 2.14.0 **均未发布**,因此从 npm 升级会一次性拿到下面全部内容。
|
|
13
|
+
|
|
14
|
+
### 自 2.10.0 以来的变化
|
|
15
|
+
|
|
16
|
+
**新增**
|
|
17
|
+
|
|
18
|
+
- 输入框工具栏积分读数(2.11.0):`Trae CN · <数值>`,5 分钟自动刷新,**仅当选中本插件模型时出现**。
|
|
19
|
+
- 读数整块可点击 → 锚定浮层(2.12.0),`Esc` 或点击外部关闭。
|
|
20
|
+
- **浮层改为账号表格**(2.14.0):本区域每个账号一行、**只列通用积分**、**点行即切换账号**,手动刷新按钮在浮层内。
|
|
21
|
+
- 卡片恢复「在输入框工具栏显示积分读数」开关(2.13.3),并只出现在**国内版**面板(2.13.4)。
|
|
22
|
+
|
|
23
|
+
**移除 / 行为变更**
|
|
24
|
+
|
|
25
|
+
- 侧边栏积分行及其行内 `×` 关闭按钮已移除(2.13.0)。
|
|
26
|
+
- 读数限定国内版(2.13.4):国际版是订阅制,其用量文档不含 `credits` 字段。
|
|
27
|
+
- `showPointsInMainUi` 重新生效,**默认关闭**。
|
|
28
|
+
|
|
29
|
+
**修复**
|
|
30
|
+
|
|
31
|
+
- 与 `dsh-connect-workbuddy` 侧边栏行重叠、被挤成裁切(2.10.3)。
|
|
32
|
+
- 读数字号过大、颜色过深(2.13.6)。
|
|
33
|
+
- composer 整段样式丢失导致默认按钮方框、浮层不显示(2.13.5)。
|
|
34
|
+
- 客户端整半无法加载:`react-dom` 被内联,`process.env` 在浏览器抛错(2.13.2)。
|
|
35
|
+
- 账号表格:绑定账号无数字、切换账号后另一账号丢失积分(本版)。
|
|
36
|
+
|
|
37
|
+
### Fixes
|
|
38
|
+
|
|
39
|
+
- **切换账号后,另一个账号不再丢失积分**。真机复现:绑定账号切换到 B 后,A 那行变成「—」。
|
|
40
|
+
|
|
41
|
+
根因是 `readAll()` 的副本合并规则——**桌面副本先入,own 副本同 id 即跳过**,于是插件自己刷新出来的有效凭据被丢弃,账号永远「token 过期」,而有效 token 就在隔壁文件里:
|
|
42
|
+
|
|
43
|
+
| | 访问 token |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| `~/.dsh/.trae-auth.cn.json`(15:08 刚写入) | **有效到 2026-10-14** ✅ |
|
|
46
|
+
| `credentialOf()` 实际取到的桌面副本 | 过期于 **2026-09-27** ❌ |
|
|
47
|
+
|
|
48
|
+
修复:**按账号归并,同一 id 保留访问 token 更长寿的副本**。插入顺序仍跟随桌面列表,因此「选哪个账号」不变——只改「同一账号取哪份副本」。这也解释了为何该 bug 只在**切换之后**出现:`ownAuthPath()` 每区域一个文件,绑定账号刷新会覆盖它。
|
|
49
|
+
|
|
50
|
+
- **绑定账号的取数改走会刷新 token 的路径**(`/account-credits` 端点)。它的存储 token 是已过期状态,而 `creditsOfAccount` 是纯查找、按设计原样返回过期 token(`creditAlternatives` 因此专门排除选中账号);表格不排除,故改用与用量接口同一条 `deps.client(region)` 路径,并在客户端用量文档兜底——同一屏上读数与表格不能对同一账号给出两个答案。
|
|
51
|
+
|
|
52
|
+
- **积分浮层表格样式**:统一行高与首列对齐(按钮承担全部左右内边距并回拉,使标签与表头对齐)、表头细分割线、当前账号加粗与绿点、悬停只作用于可点的按钮。
|
|
53
|
+
|
|
54
|
+
### Screenshot
|
|
55
|
+
|
|
56
|
+
本图为修复后的实际呈现——两个账号各有数字,输入框读数与选中账号一致:
|
|
57
|
+
|
|
58
|
+
<img src="docs/assets/dsh-connect-trae-composer-credits.png" width="536" alt="Trae credits panel: a table of accounts with their general balance, and the composer readout below" />
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
## 2.14.0 (2026-10-07)
|
|
62
|
+
|
|
63
|
+
> **积分浮层改为表格:本区域每个账号一行、只列通用积分、点行即可切换账号。**
|
|
64
|
+
|
|
65
|
+
### Features
|
|
66
|
+
|
|
67
|
+
- **浮层改成一张表格**:表头「账号 | 通用积分」,**本区域有几个账号就显示几行**。
|
|
68
|
+
- **只列通用积分**,去掉 Work 积分那一栏。表格里放两列数字是噪音,而 Work 积分本来就不参与消耗——明细该去卡片看。
|
|
69
|
+
- **点行直接切换账号**。当前账号那行标记为当前(绿色圆点)且**禁用**(点了是无效写入);其余行可点。
|
|
70
|
+
- 刷新按钮保留在浮层里。
|
|
71
|
+
|
|
72
|
+
### 切换的正确性
|
|
73
|
+
|
|
74
|
+
`accounts` 是**共享字段**,卡片和这个表格是它的**两个写入者**。因此把 `configuredAccountsOf`(深解包 + 合并规则)从卡片私有**提到 `status-paths.ts` 共用**:两个写入者必须共用同一条合并规则,否则会分叉。这条规则本身是踩出来的——`{ ...configured, [region]: id }` 若不解包会把 `{get()}` 引用散播进配置文档,**丢掉另一个区域的选择并把函数泄漏进去**。
|
|
75
|
+
|
|
76
|
+
写入仍然**读后校验**:`set()` resolve 不等于落库,读回不是目标值就报错,而不是把标记画在一个从未被选中的账号上。
|
|
77
|
+
|
|
78
|
+
### 新端点:`/plugins/dsh-connect-trae/account-credits`
|
|
79
|
+
|
|
80
|
+
返回本区域**每个账号的通用积分**。三个取舍:
|
|
81
|
+
|
|
82
|
+
1. **只在浮层打开时才取**(每账号 1 次上游读)。5 分钟的读数**不按账号数量翻倍**——健康余额不该为一张没人看的表付费,这与 `creditAlternatives` 只在余额归零时才触发是同一条原则。有测试锁住这一点。
|
|
83
|
+
2. **不排除已选账号、不排除 0 余额账号**。`creditAlternatives` 的那两条排除是为「你可以切过去的账号」设计的,而这张表是「**这里有什么**」的清单,两条都不适用。
|
|
84
|
+
3. **读失败的账号仍在表里**(只是没有数字)——面板的目的是**看见有哪些账号并切过去**,一个过期 token 不该让它从表里消失。
|
|
85
|
+
|
|
86
|
+
未提供 `creditsOfAccount` 的宿主也会列出账号(无数字)。
|
|
87
|
+
|
|
88
|
+
### Tests
|
|
89
|
+
|
|
90
|
+
- 全仓 476 → **480**。
|
|
91
|
+
- **客户端**(新增 4 例):只在**浮层打开**时取账号积分(锁住成本决策);当前行禁用、其余行可点;切换写入 `accounts` 且**保留另一个区域**的选择;按当前账号行不产生任何写入。
|
|
92
|
+
- **宿主**(新增 4 例):列出**全部**账号(含已选、含 0 余额);读失败的账号保留但无数字;无 `creditsOfAccount` 时仍列账号;**不混入其它区域的账号**。
|
|
93
|
+
- **变异验证 4+1 次全部被抓**:合并丢掉另一区域 → 1 例;表格不显示数值 → 1 例;打开前就取积分 → 2 例;当前行不禁用 → 3 例;像 alternatives 那样过滤 0 余额 → 1 例。
|
|
94
|
+
- 顺带修复测试桩:stub 之前对**所有** URL 返回用量文档,会把新端点的 `accounts` 填成 `[]`,使表格为空——测试会因**错误的原因**通过/失败。现已按 URL 分流应答。
|
|
95
|
+
|
|
96
|
+
## 2.13.6 (2026-10-07)
|
|
97
|
+
|
|
98
|
+
> **输入框积分读数:字号 12px、颜色改为浅一级的 `label-tertiary`。**
|
|
99
|
+
|
|
100
|
+
### 变更
|
|
101
|
+
|
|
102
|
+
2.13.5 把触发器改成了纯文本外观,但用了 `font: inherit` / `color: inherit`——**继承了输入框正文自己的字号与正文色**,于是读数比旁边的权限/专家/模型控件更醒目,用户反馈「字太大了,颜色也深」。
|
|
103
|
+
|
|
104
|
+
积分是**次要信息**,不该抢眼。改成显式值:
|
|
105
|
+
|
|
106
|
+
| | 之前 | 现在 |
|
|
107
|
+
| --- | --- | --- |
|
|
108
|
+
| 字号 | `inherit`(输入框正文,约 14–15px) | **`12px`**(本插件 2.11.x 的原设计值) |
|
|
109
|
+
| 颜色 | `inherit`(正文色,深) | **`--dsw-alias-label-tertiary`**(浅一级) |
|
|
110
|
+
|
|
111
|
+
参照的是 shell 自己的同排控件 `ContextMeter`——它用的正是 `label-tertiary`,本行的读数与它并排,口径一致。
|
|
112
|
+
|
|
113
|
+
顺带删掉多余的 `font: inherit`:一旦 `font-size` / `line-height` 是显式的,它既多余又会与之后的声明争夺层叠顺序。
|
|
114
|
+
|
|
115
|
+
### Tests
|
|
116
|
+
|
|
117
|
+
- 全仓 472 保持通过;守卫测试补上这两条断言(`font-size:12px` 与 `label-tertiary`),使这次的反馈不会再被后续改动悄悄退回。
|
|
118
|
+
- **变异验证**:改回 `14px` + `inherit` → 1 例失败。
|
|
119
|
+
|
|
120
|
+
## 2.13.5 (2026-10-07)
|
|
121
|
+
|
|
122
|
+
> **修复:输入框积分读数完全没有样式——丑按钮方框、点开没有账号面板,是同一个原因。**
|
|
123
|
+
|
|
124
|
+
### 根因
|
|
125
|
+
|
|
126
|
+
**composer 的样式根本没有被注入。** 2.12.0 我把 `.dsm-trae-composer-*` 写进了 `TRAE_SIDEBAR_CSS` 那个模板里,而 2.13.0 移除侧边栏时把整个常量删掉——**连带删掉了 composer 的样式**。此后(2.13.0–2.13.4)这个组件渲染时没有任何规则可用:
|
|
127
|
+
|
|
128
|
+
- 触发器 → 退回浏览器**默认按钮样式**,于是变成截图里那个带边框的方块;
|
|
129
|
+
- 浮层 → 没有 `position: fixed`,内联的 `left/top` 因此**被忽略**,它落在文档流里、根本不在触发器附近——所以你**看不到账号面板**。
|
|
130
|
+
|
|
131
|
+
两件事是同一个原因。
|
|
132
|
+
|
|
133
|
+
### 修复
|
|
134
|
+
|
|
135
|
+
- 新增**独立**的 `COMPOSER_POINTS_CSS`,由 `ComposerPoints` 在**模块加载时注入 `<head>`**(与卡片同一套幂等模式,重复渲染不会重复插入)。
|
|
136
|
+
- **触发器改为纯文本外观**:`border:0`、`background:transparent`、`color/font: inherit`——静止时和周围的输入框文字完全一致,只有 `padding` 提供点击区域,悬停时才有底色提示可点。即「保留之前的样式,只加一个点击区域」。
|
|
137
|
+
- **浮层恢复 `position: fixed` + `z-index`**,材质取自 shell 的 `--dsw-*` 令牌(跟随主题,不再硬编码明暗值)。
|
|
138
|
+
- 浮层的刷新按钮**不再依赖 `.dsm-btn`**(那个类在只随卡片注入的 `TRAE_CARD_CSS` 里,composer 永远拿不到),改为自带样式。
|
|
139
|
+
|
|
140
|
+
### 防复发
|
|
141
|
+
|
|
142
|
+
新增 **`tests/composer-styles.spec.tsx`**(5 例),把这次漏掉的东西变成断言:
|
|
143
|
+
|
|
144
|
+
- 组件确实在 `<head>` 写入了样式标签,且**重复渲染只写一次**;
|
|
145
|
+
- **组件渲染的每一个 `dsm-*` 类,在它注入的样式表里都必须有规则**;
|
|
146
|
+
- 浮层规则必须含 `position:fixed`(这一个属性的缺失就是面板消失的原因);
|
|
147
|
+
- 触发器必须 `border:0` + 透明背景,且存在 `:hover` 状态。
|
|
148
|
+
|
|
149
|
+
**变异验证 4 次全部被抓**:构造了 `<style>` 却不挂进 `<head>`、触发器规则丢失、浮层改为 `absolute`、触发器改回带边框——各 1 例失败。
|
|
150
|
+
|
|
151
|
+
之所以必须写成这种结构断言:jsdom **不做布局、不参与层叠**,所以「样式被整个删掉」在原有 460+ 个测试里一条都不会红。这已经是本次会话里第三类「注释/删块副作用导致的静默破坏」,现在有了针对它的守卫。
|
|
152
|
+
|
|
153
|
+
### Tests
|
|
154
|
+
|
|
155
|
+
- 全仓 467 → **472**。
|
|
156
|
+
|
|
157
|
+
## 2.13.4 (2026-10-07)
|
|
158
|
+
|
|
159
|
+
> **修正积分开关的位置:它是国内版专属,却放在了卡片顶部,看起来对两个区域都生效。**
|
|
160
|
+
|
|
161
|
+
### 变更
|
|
162
|
+
|
|
163
|
+
- **开关移入「国内版」面板**,紧邻它的账号区,不再挂在卡片顶部:位置本身就是一种声明——放在顶部意味着它对两个 tab 都成立。
|
|
164
|
+
- **读数限定国内版**。这是一处**潜在 bug 的修复**,不只是配合开关:国际版的用量文档是由 `payStatus` 构建的,**根本不带 `credits` 字段**(宿主 `webStatusFor` 对 `region === 'ai'` 提前返回),因为国际版是订阅制、没有可比余额。此前 `trae-global` 也被映射到读数上,于是选中**国际版模型**时会渲染出 `Trae CN · —`——一个写死「CN」的标签配一个空数字。
|
|
165
|
+
- **开关放在「已登录」子树之外**。它关联的积分数字确实只在已登录时才有,但把开关嵌在那里会导致**未登录的用户恰恰找不到它**——而「找不到这个开关」正是上一个版本要解决的问题。
|
|
166
|
+
|
|
167
|
+
### Tests
|
|
168
|
+
|
|
169
|
+
- 全仓 466 → **467**。
|
|
170
|
+
- 新增「**国际版 tab 上开关不存在**,切回国内版又出现」的用例——这是本次修正的核心契约。
|
|
171
|
+
- 把「国际版也渲染并抓取财务数据」的旧用例反转为「**国际版不渲染、不抓取**」,并注明这是**数据决定**而非供应商决定(该用例传入的是**已开启**的开关,以证明拦下它的是区域规则)。
|
|
172
|
+
- **变异验证 2 次全部被抓**:去掉「仅国内版」限制 → 1 例失败;把国际版映射加回读数 → 2 例失败。
|
|
173
|
+
|
|
174
|
+
## 2.13.3 (2026-10-07)
|
|
175
|
+
|
|
176
|
+
> **把「显示积分」开关加回设置卡片,这次控制的是输入框的积分读数。**
|
|
177
|
+
|
|
178
|
+
### 背景
|
|
179
|
+
|
|
180
|
+
2.13.0 移除侧边栏积分行时,我把卡片里那两处「在DSH主界面显示积分状态」复选框一并删了——理由是输入框读数会自动出现、不需要开关。用户随后去找这个开关,发现没有了。
|
|
181
|
+
|
|
182
|
+
用户是对的:**输入框工具栏是与 shell 自己的权限/专家/模型控件共享的一行**,那里的读数应当由用户主动开启,而不是默认塞进去。2.13.0 到 2.13.2 之间它是无条件渲染的。
|
|
183
|
+
|
|
184
|
+
### 变更
|
|
185
|
+
|
|
186
|
+
- **恢复开关**:卡片顶部一个复选框「在输入框工具栏显示积分读数」(英文 `Show credits in the composer row`),带说明性 tooltip。
|
|
187
|
+
- **字段复用 `showPointsInMainUi`**,而不是新建一个:已经把它设为 `true` 的用户(例如为了旧侧边栏行开启过的人)升级后**读数直接保留**,不会发现它悄悄关掉了。
|
|
188
|
+
- **默认关闭**(与之前对侧边栏行声明的偏好一致)。因此:字段为 `true` 的用户保持显示;字段缺失或为 `false` 的用户在 2.11.0–2.13.2 期间**曾看到过**读数,升级后不再看到,需要勾选开启。
|
|
189
|
+
- 这一条是**行为变更**,明确记录在此:2.11.0 起读数无视该字段、始终渲染;现在它重新遵守这个字段所表达的用户意图。
|
|
190
|
+
- 开关是**唯一写入者**:读数本身不带控件。写入沿用卡片其余设置同样的「写后读回校验」,避免出现渲染了却存不下去的假开关。
|
|
191
|
+
|
|
192
|
+
### 实现
|
|
193
|
+
|
|
194
|
+
- `ComposerPointsGate` 接收注入的 `settingsScope`,订阅它,只在 `showPointsInMainUi === true` **且**当前模型属于本插件(`trae`/`trae-global`)时渲染。两个条件相互独立,任一不满足即零 DOM、零请求。
|
|
195
|
+
- 客户端入口把 `settingsScope` 一并注入 `conversation.input.left` 槽位。
|
|
196
|
+
|
|
197
|
+
### Tests
|
|
198
|
+
|
|
199
|
+
- 全仓 461 → **466**(+5 卡片开关测试,gate 测试从 5 → 8)。
|
|
200
|
+
- **刻意修正了一处会“假通过”的测试**:`renders nothing while ANOTHER provider is selected` 与新开关叠加后,会因为「开关关闭」而不是「供应商不匹配」通过——即那条断言会失去意义。现在它显式传入**已开启**的 scope,确保仍在验证供应商判断。同理处理「投影尚未就绪」那条。
|
|
201
|
+
- 新增卡片测试直接点击**真实复选框**并断言 `scope.set('showPointsInMainUi', …)`:本插件曾出过「复选框渲染了但存不下去」的假开关,所以断言写在该字段上,且控件按**标签**选取而非位置(卡片里还有两个区域开关)。
|
|
202
|
+
- **变异验证 4 次全部被抓**:缺失即视为开启 → 1 例失败;完全不理会开关 → 4 例;复选框点了不写入 → 2 例;字段名大小写写错 → 2 例。
|
|
203
|
+
|
|
204
|
+
## 2.13.2 (2026-10-07)
|
|
205
|
+
|
|
206
|
+
> **修复 2.12.0 起客户端整半无法加载。2.13.1 的归因是错的,这一版是真正的原因。**
|
|
207
|
+
>
|
|
208
|
+
> ```
|
|
209
|
+
> web boot: 1 entry did not activate
|
|
210
|
+
> dsh-connect-trae: import failed
|
|
211
|
+
> ```
|
|
212
|
+
>
|
|
213
|
+
> 受影响版本:**2.12.0、2.13.0、2.13.1**。
|
|
214
|
+
|
|
215
|
+
### 真正的根因
|
|
216
|
+
|
|
217
|
+
2.12.0 为积分浮层引入了 `import { createPortal } from 'react-dom'`。**`react-dom` 不在 `tsdown.config.ts` 的 `CLIENT_EXTERNALS` 名单里,于是被内联打包**,其 CJS 构建开头就是:
|
|
218
|
+
|
|
219
|
+
```js
|
|
220
|
+
if (process.env.NODE_ENV !== "production") { … }
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
浏览器里没有 `process` → 模块**求值时**抛 `ReferenceError: process is not defined` → 整个客户端半边 import 失败。
|
|
224
|
+
|
|
225
|
+
证据链:
|
|
226
|
+
|
|
227
|
+
| | |
|
|
228
|
+
| --- | --- |
|
|
229
|
+
| 产物里 `process.env` 出现次数 | 2.13.1 为 **5**,修复后为 **0** |
|
|
230
|
+
| 产物体积 | 2.13.1 为 **1,072,561 B**(含内联 react-dom),修复后 **119,427 B** |
|
|
231
|
+
| 宿主是否注册 `react-dom` | 是(`staticModules` 里有 `"react-dom":Rf,"react-dom/client":Df`) |
|
|
232
|
+
| 同族插件 `dsh-better-sidebar` | 产物为 `require("react-dom")`(外部化),因此正常 |
|
|
233
|
+
|
|
234
|
+
### 修复
|
|
235
|
+
|
|
236
|
+
- `react-dom` 与 `react-dom/client` 加入 `CLIENT_EXTERNALS`,改为向 shell 的模块加载器 `require`,不再内联。
|
|
237
|
+
|
|
238
|
+
### 2.13.1 的归因错在哪(保留记录)
|
|
239
|
+
|
|
240
|
+
2.13.1 判定原因是「值导入了 `@deepseek-ai/dsh-client-ui-primitives`,浏览器无法解析」。**这是错的**:我把宿主的前端 bundle 反出来看了它的模块加载器,`staticModules` 明确注册了该包:
|
|
241
|
+
|
|
242
|
+
```js
|
|
243
|
+
{ react, "react/jsx-runtime", "react-dom", "react-dom/client",
|
|
244
|
+
"@deepseek-ai/cordis", "@deepseek-ai/dsh-client-store",
|
|
245
|
+
"@deepseek-ai/dsh-client-ui-slots",
|
|
246
|
+
"@deepseek-ai/dsh-client-ui-primitives", // ← 注册了
|
|
247
|
+
"@deepseek-ai/dsh-client-ui-dockkit" }
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
我当时只对比了「2.11.1 有两个 require、2.12.0 有三个」,看到多的那个就下了结论,**没有去验证那个 require 在浏览器里到底能不能解析**。真正变化的是 `createPortal` 带来的 react-dom 内联,证据(`process.env`、1MB 体积)一直都在产物里。
|
|
251
|
+
|
|
252
|
+
2.13.1 顺带把两个浮层 hook 改成了本地实现(`src/client/popover.ts`)。这个改动本身是好的(少一个运行时依赖、测试无需 mock),因此**保留**;但它的注释里那段错误归因已改正。
|
|
253
|
+
|
|
254
|
+
### 防止复发(这一版的重点)
|
|
255
|
+
|
|
256
|
+
`tests/client-runtime-imports.spec.ts` 重写为两条**真正的不变量**——之前的版本禁止一切 DSH 值导入,那是基于错误前提的:
|
|
257
|
+
|
|
258
|
+
1. **凡值导入必须已外部化**。若一个 shell 已提供的包没进 `CLIENT_EXTERNALS`,它就会被内联——这正是 react-dom 事故。
|
|
259
|
+
2. **凡外部化的导入必须是 shell 注册过的模块**,否则产出的 `require(...)` 解析不到。
|
|
260
|
+
|
|
261
|
+
宿主注册表在测试里以常量记录,来源已在注释里写明(前端 bundle 的 `staticModules`)。
|
|
262
|
+
|
|
263
|
+
- **已验证能抓住真实事故**:把 `react-dom` 移出 `CLIENT_EXTERNALS`(即还原 2.12.0 的状态)→ 2 例失败;导入一个未注册的包 → 1 例失败。
|
|
264
|
+
- 该守卫跑在常规测试里、**不需要构建产物**,在引入问题的那一行就失败。
|
|
265
|
+
- 附带修掉一个解析 bug:解析 `CLIENT_EXTERNALS` 时注释里的撇号(`shell's`)会骗过引号匹配、把注释文本当成条目,现改为先剥离注释再按行匹配。
|
|
266
|
+
|
|
267
|
+
### Tests
|
|
268
|
+
|
|
269
|
+
- 全仓 455 → **457**。
|
|
270
|
+
|
|
271
|
+
## 2.13.1 (2026-10-07)
|
|
272
|
+
|
|
273
|
+
> **紧急修复:2.12.0 引入的客户端整半无法加载。**
|
|
274
|
+
>
|
|
275
|
+
> ```
|
|
276
|
+
> web boot: 1 entry did not activate
|
|
277
|
+
> dsh-connect-trae: import failed
|
|
278
|
+
> ```
|
|
279
|
+
>
|
|
280
|
+
> 受影响版本:**2.12.0、2.13.0**。2.11.1 及更早不受影响。
|
|
281
|
+
|
|
282
|
+
### 根因
|
|
283
|
+
|
|
284
|
+
2.12.0 为积分浮层加了锚定定位,为此在客户端入口**值导入**了 `@deepseek-ai/dsh-client-ui-primitives`。
|
|
285
|
+
|
|
286
|
+
插件的客户端 bundle 由 shell 的模块加载器载入(`window.__ModuleLoader__.load({ factory: (require) => … })`),它只注册了极少数模块名。tsdown 会把该值导入转成一个外部 `require(...)`,而浏览器无法解析它——**整个客户端半边因此加载失败**。
|
|
287
|
+
|
|
288
|
+
**这个不变量一直写在仓库里**:`tests/client-activation.spec.tsx` 明确记录「入口的运行时依赖只有 React 和本地文件——所有 DSH 包都是 type-only 导入、编译期被擦除」。2.12.0 破坏了它,而且**所有测试依然全绿**:测试里该包被 mock、Node 也能从 node_modules 解析它,**问题只存在于浏览器中**。
|
|
289
|
+
|
|
290
|
+
### 修复
|
|
291
|
+
|
|
292
|
+
- 两个 hook(`useAnchoredPosition` / `useDismissOnOutsidePointer`)**改为本地实现**,新增 `src/client/popover.ts`,零 DSH 导入。行为对齐 shell 版本:同一套上/下放置与视口夹取、同一套滚动(capture)/缩放/浮层尺寸变化时重测、同一条「按下位置既不在触发区也不在浮层内则关闭」规则。
|
|
293
|
+
- 移除入口的 primitives 值导入,以及为此引入的依赖注入绕路。
|
|
294
|
+
- 产物恢复为**只有 `react` 与 `react/jsx-runtime` 两个 require**——即 2.11.1 那个已验证可用的状态。
|
|
295
|
+
|
|
296
|
+
### 防止复发
|
|
297
|
+
|
|
298
|
+
- **新增 `tests/client-runtime-imports.spec.ts`**:静态扫描 `src/client/**`,**若有任何 DSH 包的值导入即失败**(`import type` 允许,编译期被擦除)。这条守卫**已验证能抓住 2.12.0 的那个错误**。它跑在常规测试里、不需要构建产物,**在引入问题的那一行就失败,而不是等到启动时**。
|
|
299
|
+
- 新增 `tests/popover.spec.tsx`(14 例)覆盖本地 hook 的真实几何与关闭规则。
|
|
300
|
+
|
|
301
|
+
### Tests
|
|
302
|
+
|
|
303
|
+
- 全仓 438 → **455**。**变异验证 5 次全部被抓**:定位忘记减去浮层高度 → 2 例;`align=end` 失效 → 1 例;去掉水平夹取 → 2 例;点浮层自身被当成外部点击 → 1 例;关闭状态下仍挂监听 → 1 例。
|
|
304
|
+
- 另:`client-activation.spec.tsx` 中 2.12.0 为此加的 primitives mock **已删除**——该测试恢复为「加载真实入口、零 mock」。
|
|
305
|
+
|
|
306
|
+
### 教训
|
|
307
|
+
|
|
308
|
+
「所有测试全绿」不能证明插件能加载。这个仓库的测试把 DSH 包都 mock 掉了(Node 需要),因此**构建产物里出现什么 require,测试看不见**。现在这条静态守卫补上了这个盲区。
|
|
309
|
+
|
|
310
|
+
## 2.13.0 (2026-10-07)
|
|
311
|
+
|
|
312
|
+
> **移除侧边栏积分行。侧边栏不再有任何本插件的痕迹,积分只在输入框工具栏、且仅在选用本插件模型时出现。**
|
|
313
|
+
|
|
314
|
+
### 变更
|
|
315
|
+
|
|
316
|
+
- **删除侧边栏积分行**(`sidebar.footer.action` 槽位已不再注册)。侧边栏底部是**一条根级共享行**,两个连接器插件各占一行必然互相挤压——2.10.3 的修复只是让本插件的行不被裁切,位置依然各占一行。现在积分只在输入框工具栏出现,而那里是**按会话、按供应商**的,同一时刻只有一个插件的额度是相关的。
|
|
317
|
+
- **删除卡片里两处「在DSH主界面显示积分状态」复选框**:它们控制的侧边栏行已不存在,留着就是死开关。
|
|
318
|
+
- 删除随之无用的代码:`SidebarPoints.tsx`、`SidebarPointsGate.tsx`、`TRAE_SIDEBAR_CSS` 全部侧边栏样式、12 个仅服务于该行的文案键(`sidebar.*` 与 `row.showPointsInMainUi*`)、以及 `togglingMainUi` 状态。
|
|
319
|
+
|
|
320
|
+
### 关于配置字段 `showPointsInMainUi`
|
|
321
|
+
|
|
322
|
+
**保留在 schema 中,但已废弃、不再被读写。** 用户可能已经保存了这个字段的值;从 schema 里删掉一个已持久化的配置字段,会让那份数据对宿主设置存储变成未知键。保留一个死字段的代价,远小于数据变动。字段描述已改为说明它已废弃。
|
|
323
|
+
|
|
324
|
+
### Tests
|
|
325
|
+
|
|
326
|
+
- 全仓 49 → **47** 个文件、450 → **438** 个测试(删除 `sidebar-points` 10 例、`card-mainui-switch` 2 例——它们测的正是被删掉的功能)。
|
|
327
|
+
- 槽位断言更新为三个(`plugins.bundle.config`、`plugins.row.config`、`conversation.input.left`)。
|
|
328
|
+
- **过程中的一个真实错误**:用脚本按注释边界删除侧边栏注册块时,把紧邻其后的 composer 注册块**一起吞掉了**(两块的结束边界重叠)。是激活测试的槽位数量断言立刻发现的(注册数从 3 少到 2)——这正是那条断言存在的价值。已恢复 composer 块。
|
|
329
|
+
|
|
330
|
+
## 2.12.0 (2026-10-07)
|
|
331
|
+
|
|
332
|
+
> **输入框积分读数改为可点击:整块点击打开浮层,浮层内显示账号与积分,手动刷新按钮移入浮层。**
|
|
333
|
+
|
|
334
|
+
### Features
|
|
335
|
+
|
|
336
|
+
- **整块可点击**:`Trae CN · 3,392` 本身就是一个按钮(`aria-haspopup="dialog"`),**行内不再有刷新按钮**。这一行与权限/专家/模型控件共享,必须保持窄;而把操作收进浮层,正好与旁边的「专家」控件是同一套交互。
|
|
337
|
+
- **点击弹出锚定浮层**(浮现在控件上方,`Esc` 或点击外部关闭),内含:
|
|
338
|
+
- **账号**(回答「这是谁的额度」)
|
|
339
|
+
- **Work 积分** 与 **通用积分** 两个桶,各自数值
|
|
340
|
+
- **最近刷新时间**
|
|
341
|
+
- **手动刷新按钮**(从行内移到这里)
|
|
342
|
+
- 刷新失败时在浮层底部报错;未登录时显示「未登录」
|
|
343
|
+
- 沿用宿主原语 `useAnchoredPosition` + `useDismissOnOutsidePointer`,与 shell 其它浮层(ContextMeter 等)行为一致。
|
|
344
|
+
|
|
345
|
+
### 依赖注入的架构决定
|
|
346
|
+
|
|
347
|
+
浮层所需的那两个 hook **由客户端入口注入**,组件本身不 import `@deepseek-ai/dsh-client-ui-primitives`。原因:该包是浏览器 bundle(自身 `import '*.module.css'`,并依赖 `clsx`/`mdast-util-*` 等 Node 无法解析的传递依赖),组件一旦 import 它,Node 测试环境就无法加载它。
|
|
348
|
+
|
|
349
|
+
- 组件对 `popover` 缺省时使用**惰性桩**(不定位、不自动关闭),保证两个 hook **每次渲染都被调用**——条件调用会改变 hook 数量、违反 hooks 规则。
|
|
350
|
+
- 组件测试因此无需 mock 任何东西即可渲染。
|
|
351
|
+
- `client-activation.spec.tsx`(该测试要加载**真实入口**)沿用本仓库既有约定 mock primitives——与 `card-*.spec.tsx`、`sidebar-points.spec.tsx` 一致;mock 的是第三方 UI 库,不影响它验证的 `inject` 数组。
|
|
352
|
+
|
|
353
|
+
### Tests
|
|
354
|
+
|
|
355
|
+
- 全仓 449 → **450**(+1):新增「`Esc` 关闭浮层」用例。
|
|
356
|
+
- 补强了浮层内容断言:此前只断言了字段名存在,**没验证数值**;现在断言账号名与两个桶的**具体数值**(fixture 中 work=1,777 / general=3,392,两者互不相同,避免「两个桶显示同一个数」这种错误不被发现)。
|
|
357
|
+
- **变异验证 6 次全部被抓**:去掉圆点 → 2 例失败;去掉 `aria-haspopup` → 1 例;点击不再打开浮层 → 1 例;浮层不显示 Work 积分数值 → 1 例;浮层不显示账号 → 1 例;`Esc` 不再关闭 → 1 例。
|
|
358
|
+
- 其中「浮层不显示 Work 积分数值」与「`Esc` 不再关闭」**最初都没被抓到**:前者因为断言只看字段名、不看值;后者因为根本没测。两处已补。
|
|
359
|
+
|
|
360
|
+
## 2.11.1 (2026-10-07)
|
|
361
|
+
|
|
362
|
+
### 变更
|
|
363
|
+
|
|
364
|
+
- **输入框积分读数去掉「积分」二字,改用中心圆点分隔**:由 `Trae CN 积分 3,392` 改为 **`Trae CN · 3,392`**(英文同样是 `Trae CN · 3,392`)。
|
|
365
|
+
- 这一行与权限/专家/模型控件共享空间,必须保持窄;圆点与旁边模型选择器的写法(`DeepSeek-V4.1-Flash · x0.08`)属于同一套视觉语言。
|
|
366
|
+
|
|
367
|
+
### Tests
|
|
368
|
+
|
|
369
|
+
- 全仓 447 → **449**:新增「真实文案断言」——直接对**打包的中英文文案表**断言(`Trae CN`、无 `积分`/`credits`),并用真实中文 `t` 渲染断言文本恰为 `Trae CN · 3,392`。
|
|
370
|
+
- 之所以加这条:组件测试里的 `t` 返回的是**键名**,因此「真实文案里又混回 `积分`」这种改动永远不会让组件测试变红——必须直接断言文案表本身。
|
|
371
|
+
- **变异验证 2 次全部被抓**:圆点换成空格 → 1 例失败;真实文案里加回 `积分` → 1 例失败。
|
|
372
|
+
|
|
373
|
+
## 2.11.0 (2026-10-07)
|
|
374
|
+
|
|
375
|
+
> **新增:在输入框工具栏显示「Trae CN 积分」——只在当前模型属于本插件时出现。**
|
|
376
|
+
>
|
|
377
|
+
> 这样两个连接器插件不再争同一个位置:侧边栏底部是**一条共享的根级行**,两个插件各放一行就会互相挤压(实测结果是本插件的行被压成裁切的「Tr」,见 2.10.3);而输入框工具栏是**按会话、按供应商**的,同一时刻只有一个插件的额度是相关的。
|
|
378
|
+
|
|
379
|
+
### Features
|
|
380
|
+
|
|
381
|
+
- **输入框工具栏的积分读数**(`conversation.input.left` 槽位,位于「专家」与模型选择器之间):
|
|
382
|
+
- 显示 `Trae CN 积分 <数值>` + **一个刷新图标按钮**——不占标签、不放第二个按钮,因为这一行与权限/专家/模型控件共享,必须保持窄。
|
|
383
|
+
- **按供应商显示**:读宿主自己的 `modelSelection` 投影,只有当前模型的 provider 是 `trae` / `trae-global` 时才渲染。选其它供应商的模型时**完全不渲染**(无 DOM、无请求)。
|
|
384
|
+
- `next` 优先于 `lastUsed`:已经切换但尚未发送的选择,才是输入框上显示给用户看的那个。
|
|
385
|
+
- 5 分钟自动刷新;点击图标可手动刷新;失败保留上次数值。
|
|
386
|
+
- 数值为**通用积分**(`credits.generalAvailable`),与卡片上那一栏口径一致。
|
|
387
|
+
|
|
388
|
+
### 为什么用投影而不是插件自己的状态
|
|
389
|
+
|
|
390
|
+
`modelSelection` 投影是**模型选择器写入、下一次请求实际使用**的那个值,因此读数不可能与生效的模型不一致。
|
|
391
|
+
|
|
392
|
+
### 为什么改到输入框工具栏
|
|
393
|
+
|
|
394
|
+
侧边栏底部(`sidebar.footer.action`)是**根级共享行**:它按横向 flex 排列 shell 自己的按钮与所有插件的贡献,两个插件各占一行就会互相挤压。输入框工具栏是**会话作用域**且能读到当前供应商,因此天然只显示相关的那一个——**这是「各自占位置」问题的结构性解法,而不是把行做窄一点**。
|
|
395
|
+
|
|
396
|
+
### Tests
|
|
397
|
+
|
|
398
|
+
- 全仓 435 → **446**(+11):`selectedProviderOf` 的四种退化输入;**另一个供应商时零渲染零请求**;投影未就绪时零渲染;`trae` → CN 区域、`trae-global` → 国际区域;**直接以非本插件 provider 渲染组件时零渲染零请求**;读数取通用积分而非汇总;行内**只有 1 个按钮**且是刷新图标。
|
|
399
|
+
- **变异验证 3 次全部被抓**:忽略 `next` 只看 `lastUsed` → 1 例失败;改回汇总口径 `available` → 2 例失败;去掉组件内层的归属守卫 → 1 例失败。
|
|
400
|
+
- 其中「去掉归属守卫」最初**没被抓到**:外层 Gate 已经过滤,组件内层守卫是纵深防御,两条都被删也不会红。为此补了一条**直接以外部 provider 渲染组件**的用例——这个盲区正是「冗余守卫无人覆盖」的典型。
|
|
401
|
+
|
|
402
|
+
## 2.10.3 (2026-10-07)
|
|
403
|
+
|
|
404
|
+
> **修复:与同族插件 `dsh-connect-workbuddy` 的积分行重叠,我们的行被挤成裁切的一小条。**
|
|
405
|
+
|
|
406
|
+
### 问题
|
|
407
|
+
|
|
408
|
+
侧边栏底部同时显示两个插件的积分时,实际呈现是:
|
|
409
|
+
|
|
410
|
+
```
|
|
411
|
+
WB CN 积分: 3,392 刷新
|
|
412
|
+
WB AI 积分: 0 刷新
|
|
413
|
+
Tr ← 我们的行只剩开头两个字母
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
根因:**shell 把 `sidebar.footer.action` 渲染成一个横向 flex 行**(它自己的按钮 + 每个插件的贡献都在同一行)。我们的行容器**没有声明占满整行宽度**,于是被排到其他行旁边、挤进剩余空间里。
|
|
417
|
+
|
|
418
|
+
### 修复
|
|
419
|
+
|
|
420
|
+
- 行容器加上 **`flex: 0 0 100%` 与 `min-width: 0`**,声明独占整行宽度——这样无论 footer 里有多少其他贡献,本行都占满一行而不是被挤压。
|
|
421
|
+
- 这与 `dsh-connect-workbuddy` 的做法一致:它在自己的 Gate 注释里记录过同一问题(「fragment 让两行成为横向 flex 的兄弟节点,两行被并排挤在一起,用户报告不可读」),并同样用「声明宽度」解决。
|
|
422
|
+
|
|
423
|
+
### Tests
|
|
424
|
+
|
|
425
|
+
- 全仓 435 保持通过。这一处是**纯布局**问题:jsdom 不做布局计算,因此**单元测试无法覆盖**——判断依据是真实界面截图与两处 CSS 的逐行对比。这一点如实记录,避免留下「测试全绿所以没问题」的错觉。
|
|
426
|
+
|
|
427
|
+
## 2.10.2 (2026-10-07)
|
|
428
|
+
|
|
429
|
+
### 变更
|
|
430
|
+
|
|
431
|
+
- **侧边栏标签改为 `Trae CN 积分`**(英文 `Trae CN credits`),对齐同族插件 `dsh-connect-workbuddy` 的呈现格式(`WB CN 积分: 3,392`)——即「**产品缩写 + 区域 + 积分**」。
|
|
432
|
+
- 上一版是 `Trae通用积分`,没有区域标记。加上 `CN` 后,与 workbuddy 的两行(`WB CN 积分` / `WB AI 积分`)在视觉上属于同一套语言。
|
|
433
|
+
- 数值仍是**通用积分**(`credits.generalAvailable`),未改动。
|
|
434
|
+
|
|
435
|
+
## 2.10.1 (2026-10-07)
|
|
436
|
+
|
|
437
|
+
> **对齐 `dsh-connect-workbuddy` 的侧边栏积分呈现方式:去掉行内开关,默认不显示。**
|
|
438
|
+
|
|
439
|
+
### 变更
|
|
440
|
+
|
|
441
|
+
- **默认不显示**(`showPointsInMainUi` 默认 `false`)。侧边栏底部与 shell 自己的控件共享空间,一行常驻信息应当是用户主动要来的,而不是升级后自动出现的。开关在卡片里——**和它控制的设置(账号、模型)待在一起**。
|
|
442
|
+
- **去掉积分行里的 `×` 关闭按钮**。行内不再有任何控件,只有数值与「刷新」。理由有两条,都是同族插件 `dsh-connect-workbuddy` 先踩到并记录下来的:
|
|
443
|
+
1. 行内的 `scope.set()` 是**同一个字段的第二个写入者**,而它的成功判定比卡片的「写入后读回校验」弱——**同一个设置存在两条可以互相矛盾的写入路径**;
|
|
444
|
+
2. 它和「让这一行保持清爽」的目标相冲突。
|
|
445
|
+
- 随之删除不再使用的样式(`.dsm-trae-sidebar-points-hide`)与文案键(`sidebar.hide`)。
|
|
446
|
+
|
|
447
|
+
### 保留不变
|
|
448
|
+
|
|
449
|
+
- **关闭后整行彻底消失**(无 DOM、无残迹),轮询一并停止。
|
|
450
|
+
- **侧边栏收起(56px 导轨)时整行不渲染**。
|
|
451
|
+
- **5 分钟**刷新间隔;开启后先立即抓取一次,完成后再启动定时器。
|
|
452
|
+
- 显示**通用积分**(`credits.generalAvailable`)——对齐卡片上真实存在的「通用积分」那一栏。卡片没有「合计」栏,所以不采用 workbuddy 的 `credits.total`(否则侧边栏的数字在卡片上找不到对应)。
|
|
453
|
+
- 卡片里**两处**复选框(卡片顶部 + 模型区标题旁)保留,作为唯一的开关入口。
|
|
454
|
+
|
|
455
|
+
### Tests
|
|
456
|
+
|
|
457
|
+
- 全仓 435:`sidebar-points` 的用例改写为新契约——**「字段缺失即关闭」**(原先断言「缺失即开启」)、**「行内不携带任何控件」**(断言行内**只有 1 个按钮**且是刷新、且不产生任何写入)、**「关闭后容器内无 DOM」**。
|
|
458
|
+
- **变异验证 2 次全部被抓**:改回「缺失即开启」→ 1 例失败;行内多出一个按钮 → 1 例失败。
|
|
459
|
+
|
|
460
|
+
### 致谢
|
|
461
|
+
|
|
462
|
+
同族插件 **dsh-connect-workbuddy** 在它的 `SidebarCreditsGate.tsx` 里明确记录了「行内 `×` 作为第二个写入者曾导致关不掉」这一结论,本版据此对齐。
|
|
463
|
+
|
|
464
|
+
## 2.10.0 (2026-10-07)
|
|
465
|
+
|
|
466
|
+
> **新增:在 DSH 主界面侧边栏常驻一行「Trae通用积分」**([#26](https://github.com/dingminhua/dsh-connect-trae/issues/26),**实现由报告人 JiewiW 贡献**)。
|
|
467
|
+
>
|
|
468
|
+
> 看积分原先必须打开插件设置卡片、展开、找到额度区。现在侧边栏底部(工作区列表下方、设置上方)常驻一行「Trae通用积分:<数值>」与刷新按钮,**默认显示**,每 5 分钟自动更新,也可手动刷新。
|
|
469
|
+
|
|
470
|
+
> 这是 2.9.0 起五个版本合并后的首个公开版本:npm 上的 `latest` 此前仍是 2.8.0,因此**从 2.8.0 升级会一次拿到下面全部内容**。
|
|
471
|
+
|
|
472
|
+
### Features
|
|
473
|
+
|
|
474
|
+
- **侧边栏积分行**:在 `sidebar.footer.action` 槽位渲染「Trae通用积分:<数值>」+ 「刷新」按钮。
|
|
475
|
+
- **默认显示**(opt-out):`showPointsInMainUi` 默认 `true`。
|
|
476
|
+
- **关闭即彻底消失**:行内的 `×` 或卡片里的复选框都能关闭,关闭后**侧边栏不留任何 DOM**,轮询一并停止。
|
|
477
|
+
- 开启后**先立即抓取一次,完成后再启动 5 分钟周期**的定时器,不叠加;关闭或卸载即清除定时器并中止在途请求。
|
|
478
|
+
- 抓取失败保留上一次数值并提示;刷新进行中忽略重复点击。
|
|
479
|
+
- 显示的是**通用积分**(`credits.generalAvailable`,SOLO 聊天实际消耗的那个池子),与卡片上的数字口径一致。
|
|
480
|
+
- 侧边栏收起(56px 导轨)时整行不渲染;国际版为订阅制、无可比数字,因此不显示。
|
|
481
|
+
- **开关在三处,写同一个字段**(`showPointsInMainUi`),永不冲突:侧边栏那一行的 `×`、卡片顶部、卡片「模型」区标题旁(在「从 Trae 刷新」「一键测试启用模型」左侧)。
|
|
482
|
+
|
|
483
|
+
### 为什么不只用插件卡片里的复选框
|
|
484
|
+
|
|
485
|
+
第一版把开关**只**放在插件卡片里,结果在装了 **dshmarket** 的宿主上等于不存在:市场插件接管了插件页,而它的 UI **完全不渲染插件自带的配置卡片**(其客户端 bundle 里 `plugins.row.config` / `plugins.bundle.config` **0 处引用**)。于是积分行被挡住、开关又在打不开的页面里。现在把开关同时放在**模型区**(用户管理模型时视线所在)与**积分行本身**,并默认显示——不再依赖任何一个可能不可达的页面。
|
|
486
|
+
|
|
487
|
+
### 修复(本版合并的 2.9.1–2.9.4)
|
|
488
|
+
|
|
489
|
+
- **2.9.1**:积分行默认显示,关闭入口移到行内(原为默认关闭 + 开关藏在插件卡片)。
|
|
490
|
+
- **2.9.2**:标签由「通用积分」改为「**Trae通用积分**」,避免与侧边栏里其他插件的数字混淆。
|
|
491
|
+
- **2.9.3**:**补上复选框缺失的 CSS**——移植贡献者实现时只搬了侧边栏积分行的样式,漏掉了卡片里复选框的样式,导致它一直在渲染但**没有布局与配色、等同于不可见**;同时在「模型」区标题旁加了同一个复选框。
|
|
492
|
+
- **2.9.4**:关闭后**彻底不显示**,不再残留「积分」小按钮(回到开关的路已在卡片两处)。
|
|
493
|
+
|
|
494
|
+
### Tests
|
|
495
|
+
|
|
496
|
+
- 全仓 423 → **435**:开关默认即显示;关闭后容器内**无任何 DOM**(`container.innerHTML === ''`)且轮询停止;行内 `×` 与卡片两处复选框**写同一字段、互相同步**;`preferStrongerRow` 等既有用例不变。
|
|
497
|
+
- **变异验证多次全部被抓**:关闭时残留元素 → 2 例失败;去掉模型区的复选框 → 2 例失败;「缺失即开启」改回「必须显式 true」→ 1 例失败;收起态不退出 → 1 例失败。
|
|
498
|
+
|
|
499
|
+
### 致谢
|
|
500
|
+
|
|
501
|
+
报告人 **JiewiW** 独立完成了实现、430 个测试(含假定时器),并在独立 DSH 实例上做了端到端验证——包括「关闭开关后跨越两个应触发周期点无任何请求」这类定时器泄漏检查,还明确声明未改动版本号与无关代码。他当初在说明里提醒的「该 rc.2 宿主未内联挂载插件配置面板」后来被证明是真实的坑,本版据此把开关放到了可达位置。
|
|
502
|
+
|
|
503
|
+
## 2.9.4 (2026-10-07)
|
|
504
|
+
|
|
505
|
+
### 变更
|
|
506
|
+
|
|
507
|
+
- **关闭积分显示后,侧边栏彻底不留任何东西**(`×` 或取消勾选都是如此)。之前关闭后会在原位置留一个「积分」小按钮,理由是可以随时找回开关;但 2.9.3 已经把同一个复选框放进卡片**两处**(卡片顶部 + 模型区),回到开关的路本来就在配置它的地方。**用户要的是清干净,残留一个按钮仍然占着他们想腾空的位置。**
|
|
508
|
+
- 随之删除不再使用的样式(`.dsm-trae-sidebar-points-off` / `-show`)与文案键(`sidebar.show` / `sidebar.showHint`)。
|
|
509
|
+
|
|
510
|
+
### Tests
|
|
511
|
+
|
|
512
|
+
- `sidebar-points` 的用例改为断言**关闭后容器里没有任何 DOM**(`container.innerHTML === ''`),而不是「还剩一个可恢复的入口」;并断言轮询也随之停止。行内 `×` 的用例同样断言完全清空。
|
|
513
|
+
- **变异验证被抓**:关闭时改为残留一个元素 → 2 例失败。
|
|
514
|
+
|
|
515
|
+
## 2.9.3 (2026-10-07)
|
|
516
|
+
|
|
517
|
+
> **修复:「显示积分」复选框实际上看不见**——它一直在卡片里渲染,但**没有任何 CSS**,所以读起来像是不存在。
|
|
518
|
+
|
|
519
|
+
### 问题
|
|
520
|
+
|
|
521
|
+
2.9.0 移植贡献者的实现时,我只搬了侧边栏积分行的样式,**漏掉了卡片里那个复选框的样式**(`.dsm-trae-mainui-switch*`)。复选框本身渲染了,但没有布局、没有间距、没有配色——在卡片里等同于不可见。用户因此反复找不到「在DSH主界面显示积分状态」这个开关。
|
|
522
|
+
|
|
523
|
+
### 修复
|
|
524
|
+
|
|
525
|
+
- **补上缺失的样式**(`.dsm-trae-mainui-switch` / `-label` / `input`),复选框现在正常显示、可点、有品牌色。
|
|
526
|
+
- **在「模型」区标题旁再加一个同样的复选框**(就在「从 Trae 刷新」「一键测试启用模型」两个按钮的左侧)。理由:管理模型时视线就在那里,而**需要用户去找的设置等于没有设置**——这正是这个开关此前丢失的原因。两处写**同一个字段**,永不冲突;顶部的那个也保留。
|
|
527
|
+
|
|
528
|
+
### Tests
|
|
529
|
+
|
|
530
|
+
- 全仓 435(用例数不变,但 `card-mainui-switch` 的两条被**加强**):现在断言卡片里**两处都有**复选框、默认都未勾选、且**勾任意一处另一处同步**(共享字段);「配置已开启」的用例也改为断言**两处都是勾选态**。
|
|
531
|
+
- **变异验证被抓**:删掉模型区那一个复选框 → 2 例失败。
|
|
532
|
+
|
|
533
|
+
## 2.9.2 (2026-10-07)
|
|
534
|
+
|
|
535
|
+
### 变更
|
|
536
|
+
|
|
537
|
+
- **侧边栏积分行的标签改为「Trae通用积分」**(英文 `Trae credits`)。原先只写「通用积分」,而侧边栏底部可能同时出现其他插件的数字,读者无法确定这个余额属于谁。加上产品名前缀后一眼可辨。
|
|
538
|
+
|
|
539
|
+
## 2.9.1 (2026-10-07)
|
|
540
|
+
|
|
541
|
+
> **修复:积分行默认不显示,而它的开关在一个部分宿主上根本打不开。**
|
|
542
|
+
|
|
543
|
+
### 问题
|
|
544
|
+
|
|
545
|
+
2.9.0 把积分行做成 **opt-in**(`showPointsInMainUi` 默认 `false`),开关只放在插件卡片里。这在部分宿主上等于**功能不存在**:
|
|
546
|
+
|
|
547
|
+
- 用户装了 **dshmarket**,插件页默认停在市场的 tab,而市场 UI **完全不渲染插件自带的配置卡片**(我在它的客户端 bundle 里搜 `plugins.row.config` / `plugins.bundle.config`,**0 处引用**;它只注册自己的卡片,key 是 `dshmarket`)。
|
|
548
|
+
- 于是积分行被 Gate 挡住不渲染,而唯一能打开它的开关在用户到不了的页面里 —— 既看不到功能,也找不到开关。
|
|
549
|
+
|
|
550
|
+
**根因是我把「可关闭」实现成了「默认关闭」**:用户的原话是「**如果要加这个显示**,再加一个开关让不想显示的用户可以关闭」——那本该是**默认显示 + 可关闭**。
|
|
551
|
+
|
|
552
|
+
### 修复
|
|
553
|
+
|
|
554
|
+
- **`showPointsInMainUi` 默认改为 `true`**(opt-out)。侧边栏底部直接显示通用积分。
|
|
555
|
+
- **关闭开关移到积分行自己身上**:积分行右侧多一个 `×`,点一下即隐藏;隐藏后**同一位置留一个极小的「积分」按钮**,一点即恢复。
|
|
556
|
+
- 两个控件写的是**同一个字段**(`showPointsInMainUi`),因此与插件卡片里的复选框永不冲突。
|
|
557
|
+
- 关键点:**控制入口和功能渲染在同一处**,所以「宿主能画这一行」就等于「用户能控制它」,不再依赖任何设置页。
|
|
558
|
+
- 侧边栏收起(56px 导轨)时仍然整行不渲染:收起态塞不下标签+数值+按钮,会变成「积…」。
|
|
559
|
+
|
|
560
|
+
### 兼容性说明
|
|
561
|
+
|
|
562
|
+
- 之前**明确关闭过**该开关的用户不受影响(显式 `false` 仍是关闭);「字段缺失」现在按**开启**处理(原先按关闭),所以升级后会直接看到积分行。
|
|
563
|
+
- 插件卡片里的复选框**保留**:在能渲染卡片的宿主上(如内置插件管理器),它依然可用。
|
|
564
|
+
|
|
565
|
+
### Tests
|
|
566
|
+
|
|
567
|
+
- 全仓 433 → **435**:新增「默认即显示」「关闭后留下可恢复的入口」「行内关闭按钮写入共享字段」;并把原「关闭时不渲染任何 DOM」的用例改写为**「关闭时仍留一个可操作入口」**——那条旧断言正是把这次的问题固化下来的地方。
|
|
568
|
+
- **变异验证 2 次全部被抓**:把「缺失即开启」改回「必须显式 true」→ 1 例失败;关闭时改为不渲染入口 → 1 例失败。
|
|
569
|
+
|
|
570
|
+
## 2.9.0 (2026-10-06)
|
|
571
|
+
|
|
572
|
+
> **可选:在 DSH 主界面侧边栏常驻一行通用积分**([#26](https://github.com/dingminhua/dsh-connect-trae/issues/26),**由报告人 JiewiW 提交实现**)。
|
|
573
|
+
>
|
|
574
|
+
> 看积分原先必须打开插件设置卡片、展开、找到额度区;现在勾一个开关,侧边栏底部(工作区列表下方、设置上方)常驻一行「通用积分:<数值>」与刷新按钮。
|
|
575
|
+
|
|
576
|
+
### Features
|
|
577
|
+
|
|
578
|
+
- **侧边栏积分行**(开关 `showPointsInMainUi`,**默认关闭**):在 `sidebar.footer.action` 槽位渲染「通用积分:<数值>」+ 「刷新」按钮;`title` 显示最近刷新时间;抓取失败保留上一次数值并提示;刷新进行中忽略重复点击。
|
|
579
|
+
- 开关关闭时**不渲染任何 DOM**(不是隐藏),因此不产生任何请求;关闭开关或插件卸载即清除定时器并中止在途请求。
|
|
580
|
+
- 开启后**先立即抓取一次,完成后再启动 5 分钟周期**的定时器,不叠加。
|
|
581
|
+
|
|
582
|
+
### 合入时相对贡献者实现的三处调整
|
|
583
|
+
|
|
584
|
+
1. **数字口径:`credits.generalAvailable`(通用积分),不是 `credits.available`。** `available` 是上游汇总(`summary.totalAmount - consumedAmount`),而卡片展示的是按额度包分出的 Work / 通用两桶;汇总**不保证**等于任一桶。一个用户在侧边栏找不到对应数字的余额,比没有数字更糟。通用桶也正是 SOLO 聊天路径消耗的那个。
|
|
585
|
+
2. **轮询 60s → 5 分钟。** 卡片的轮询本身就是 60s,而每次 usage 读取会打**两个**上游请求;积分以「模型调用」而非秒为单位变化,60s 的边际价值很低。
|
|
586
|
+
3. **侧边栏收起时整行不渲染**(`wide === false` → `null`)。宿主 CSS 并**不会**在收起时隐藏底部区域,而是把它压成 56px 居中窄条(`SidebarRoot.module.css` 的 `.collapsed .footerActions`);标签 + 余额 + 按钮塞进 56px 会被裁成「积…」。而**收起正是长时间工作的常态**——这个功能最需要成立的时刻不能是它最坏看的时刻。提前退出也意味着收起期间不启动抓取循环。
|
|
587
|
+
|
|
588
|
+
### Tests
|
|
589
|
+
|
|
590
|
+
- 全仓 423 → **433**:移植贡献者的 7 例(开关默认关闭/持久化/读回/回显;挂载即抓取、首次抓取后启定时器、卸载即清除;失败保留上次数值;进行中点击被忽略;Gate 关闭时零 DOM),并新增 3 例锁定上述调整。
|
|
591
|
+
- **测试加固**:贡献者的 fixture 让 `available` 与 `generalAvailable` **恰好相等**,所以换成哪个字段测试都过——这正是能悄悄退回汇总口径的盲区。现已让两者**不相等**,字段选错会立刻失败。
|
|
592
|
+
- 同时把 `card-region-switch` 的取元素方式从「按下标取前两个 checkbox」改为**按 aria-label 精确匹配区域开关**:新开关让卡片多了第三个 checkbox,按下标会开始断言到错误的控件。
|
|
593
|
+
- **变异验证 3 次全部被抓**:收起态不再退出 → 1 例失败;取字段改回 `available` → 3 例失败;轮询改回 60s → 1 例失败。
|
|
594
|
+
|
|
595
|
+
### 致谢
|
|
596
|
+
|
|
597
|
+
报告人 **JiewiW** 独立完成了实现、430 个测试(含假定时器)、并在独立 DSH 实例上做了端到端验证(含「关闭开关后跨越两个应触发周期点无任何请求」这类定时器泄漏检查),还明确声明未改动版本号与无关代码。
|
|
598
|
+
|
|
3
599
|
## 2.8.0 (2026-10-06)
|
|
4
600
|
|
|
5
601
|
> **绑定账号额度耗尽时不再是一块没有解释的空面板**([#25](https://github.com/dingminhua/dsh-connect-trae/issues/25),报告人 JiewiW)。
|
package/README.en.md
CHANGED
|
@@ -44,6 +44,10 @@ A [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) bund
|
|
|
44
44
|
- **Read-only usage and model management** — Work/general credits on CN accounts, subscription/trial status on international ones; enable Trae models freely. Read-only queries consume nothing.
|
|
45
45
|
- **One-click test of the enabled models** — sends each **enabled** model one minimal message (a single `ping`, no tools) and reports whether it actually answers: **works** / **needs a higher plan** (Trae `1005`; your plan does not cover it) / **not served** (`4001`/`4011`; no SOLO function serves it) / **throttled** / **sign-in rejected** / **not determined**. The last one is a first-class outcome: a timeout, a dropped connection, a proxy or a 5xx is **never** reported as unusable — only an explicit upstream refusal is a negative verdict. This is the plugin's **only** operation that spends quota (check-in changes account state but spends nothing), it runs only when you click, and its results live in the current session rather than in your settings.
|
|
46
46
|
- **Daily check-in claiming** — a one-click claim on the CN card (the button shows the daily reward, and reads "Claimed today" and disables itself once that account has been paid). This is the plugin's **only** operation that changes account state, and it runs only when you click. Trae allows **one check-in per device per day**: after switching accounts, if this machine's slot is already spent (by any account), the selected account cannot claim here that day — the card explains that rather than pretending the account was paid. The international side has no check-in activity, so no button is shown there.
|
|
47
|
+
- **Composer credit readout** — the composer tool row (between "Expert" and the model selector) shows "Trae CN · <n>", refreshing every 5 minutes. The whole readout is clickable and opens a panel showing a TABLE: one row per account in the region with its GENERAL balance only, where clicking a row SWITCHES accounts (through the same merged `accounts` write the card uses, so the other region's selection survives), with the manual refresh button in the panel. It appears **only while the session's selected model belongs to this plugin**, and renders nothing for any other provider — which is what keeps two connector plugins from competing for one shared row (the sidebar foot is a shared root row; the composer row is per-session and provider-scoped).
|
|
48
|
+
|
|
49
|
+
<img src="docs/assets/dsh-connect-trae-composer-credits.png" width="536" alt="Trae credits panel: a table of accounts with their general balance, and the composer readout below" />
|
|
50
|
+
|
|
47
51
|
- **Secure loopback shim** — one random port + in-process random secret per region; the real Trae token is never handed to pi-ai.
|
|
48
52
|
|
|
49
53
|
## How it works
|
package/README.md
CHANGED
|
@@ -44,6 +44,10 @@
|
|
|
44
44
|
- **只读用量与模型管理** —— 国内版查看 Work 积分与通用积分,国际版查看订阅/试用状态;刷新/启用 Trae 模型;只读查询不消耗额度。
|
|
45
45
|
- **一键测试启用模型** —— 给**已启用**的每个模型发一条最小消息(一句 `ping`、不带工具),逐个告诉你能不能真的应答:**可用** / **需要更高档位**(Trae `1005`,你的套餐不覆盖)/ **上游不提供**(`4001`/`4011`,SOLO 通道没有这个模型)/ **被限流** / **登录凭据被拒** / **本次未测出**。最后一项是一等公民:超时、断连、代理、5xx **一律不算「不可用」**——只有上游明确的拒绝才算负面结论。**这是插件里唯一消耗额度的操作**(签到改变的是账号状态,不消耗),只在你点击时运行,结果只留在当前会话里不写入设置。
|
|
46
46
|
- **每日签到领取** —— 国内版卡片上一键领取当日签到积分(按钮显示每日奖励,该账号已领取则显示「今日已领取」并禁用);这是插件里**唯一会改变账号状态**的操作,由你主动点击触发。Trae 的签到按「**每台设备每天一次**」限制:切换账号后如果本机今天的名额已经用掉(无论是哪个账号用的),当前账号在本机当天无法再签,卡片会说明原因而不是谎报「已领取」。国际版没有签到活动,因此不显示该按钮。
|
|
47
|
+
- **输入框积分读数** —— 在输入框工具栏(「专家」与模型选择器之间)显示「Trae CN · <数值>」,5 分钟自动刷新。**整块可点击**,弹出浮层显示一张**表格**:本区域**每个账号一行**、只列**通用积分**,**点行即可切换账号**(写入与卡片相同的 `accounts` 字段、合并时不丢掉另一个区域的选择),并把手动刷新按钮放在浮层里。**只在当前会话选中的模型属于本插件时出现**——选其它供应商的模型时整块不渲染。这样两个连接器插件不会争同一个位置(侧边栏底部是共享的根级行,输入框工具栏是按会话、按供应商的)。
|
|
48
|
+
|
|
49
|
+
<img src="docs/assets/dsh-connect-trae-composer-credits.png" width="536" alt="Trae credits panel: a table of accounts with their general balance, and the composer readout below" />
|
|
50
|
+
|
|
47
51
|
- **安全 loopback shim** —— 每区域一个随机端口 + 进程内随机 secret,真实 Trae token 不交给 pi-ai。
|
|
48
52
|
|
|
49
53
|
## 工作原理
|