dsh-connect-trae 4.1.0 → 4.3.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 CHANGED
@@ -1,5 +1,125 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.3.0 (2026-10-22)
4
+
5
+ > **设置卡片按「账号 / 模型 / 诊断」分栏,模型栏改回全部铺开。**
6
+ >
7
+ > 卡片原来是一列长页,把两类互不相干的事情混排在一起:运维账号(谁在服务、还剩多少、签到、
8
+ > 账号池及其日志)与编辑目录(勾模型、图片输入、上下文预算)。两者连提交语义都不同——账号改动
9
+ > 点一下即生效,目录改动是草稿、必须点「保存」——于是保存按钮常常离它要保存的东西隔着三屏。
10
+ > 学自同族 `dsh-connect-workbuddy` 的分栏实现。版本号取 **minor**:新增界面与一个只读栏,
11
+ > 接口、配置项与请求结构零变更。
12
+
13
+ ### 新增
14
+
15
+ - **设置卡片按「账号 / 模型 / 诊断」分栏**(`feat(client)`)。
16
+ - 顶部地区那排仍是**作用域**(这张卡片讲的是哪个供应商);其下新增分栏 tablist(账号 / 模型 /
17
+ 诊断),三者共享同一个地区作用域。两排视觉上明确区分:地区是带开关的方框 chip,分栏是设置窗
18
+ 口那种下划线 strip——两排方框会被读成一个六选一。
19
+ - 非活动栏用 `hidden` 留在页面里:切栏不重新拉取、不丢未保存的模型草稿、账号池日志不重建;
20
+ `hidden` 同时让非活动栏退出无障碍树,这正是 `role="tabpanel"` 要的语义。
21
+ - 新增只读「诊断」栏:面板名、供应商 id、服务账号(按名字,不打印 id)、访问令牌与重新登录两个
22
+ 到期时间、目录模型数与已启用数、账号池与签到支持与否、Raw 会话通道状态,以及上游最近一次错误
23
+ **的原文**(原样引用,不翻译、不概括)。这一栏没有任何会改设置的控件,存在的唯一理由是被整段
24
+ 贴进反馈。
25
+ - 签出态不画分栏:那一段说明不会被一个看不见的 tab 藏起来。
26
+ - 文案同时补 en/zh(`row.panel*` / `row.diag*`);样式新增 `.dsm-trae-panels` /
27
+ `.dsm-trae-panel-tab` / `.dsm-trae-panel` / `.dsm-trae-diag*`。
28
+
29
+ - **模型栏把所有模型全部铺开,没有内层滚动条**(`feat(client)`)。
30
+ - `.dsm-trae-model-list` 不设 `max-height`,`overflow-y` 也不是 `auto`:唯一滚动是卡片自己的
31
+ 页滚。`overflow: hidden` 保留,只为圆角描边——纵向不裁任何内容。
32
+ - **实测**(`tools/panels-shot.tsx`,30 个模型的目录):`scrollHeight == clientHeight == 1889`、
33
+ `clipped: false`、`max-height: none`、面板内自带滚动元素 `0` 个,保存栏落在页面末尾。
34
+
35
+ ### 重构
36
+
37
+ - **账号栏抽成 `src/client/TraeAccountPanel.tsx`**(520 行),`TraeUsageCard.tsx` 从 2354 行
38
+ 降到 1981 行。拆分是「笨」的那一半:所有判定仍留在卡片里(谁在服务、每行值多少、能不能签到、
39
+ 读数放哪儿),账号栏只接收算好的行视图;这样账号表与账号池表读的是同一份判定,不会各说各话。
40
+ - 卡片与账号栏共用的格式化/判定(`formatNumber` / `formatDateTime` / `dotStyle` /
41
+ `refreshTokenWarning`)下沉到 `src/client/display.ts`,避免两处各写一份而漂移。
42
+
43
+ ### 修复
44
+
45
+ - **诊断栏的「每日签到」不再依赖账号池**。原先读 `pool.checkinSupported`,而账号池是**可选功能**,
46
+ 没开池的国内账号会显示「本面板不提供」——恰好与事实相反。改为先读签到文档(`status.checkin`)。
47
+
48
+ ### 测试
49
+
50
+ - `tests/card-panels.spec.tsx`(10 条,新增):默认落在账号栏、点模型/诊断可切换、非活动栏仍挂载
51
+ (草稿不丢)、恰好一个 `aria-selected`、tab 与 tabpanel 互相指向、切栏不改地区作用域、诊断栏无
52
+ 任何可交互控件、签出态无分栏;另加两条:30 个模型全部渲染且列表样式里没有 `max-height` /
53
+ `overflow-y:auto`,「每日签到」按签到文档而非账号池判定。
54
+ - 顺带修好受影响的既有卡片测试(`card-test-models` / `card-context-bulk` / `card-promo` 的
55
+ `renderOpen` 先点开模型栏),因为模型控件现在确实在那一栏里。
56
+ - 新增 `tools/panels-shot.tsx` + `pnpm panels:shot`:把三个分栏各自渲染成
57
+ `/tmp/panels-preview-<section>.html`(真实中文文案、30 模型目录),页面里的 `#audit` 给出
58
+ 面板盒、分栏下划线与模型列表 `scrollHeight`/`clientHeight` 的实测值,用 `--dump-dom` 读回,
59
+ 用来证明「没有内滚」而不是靠肉眼。
60
+ - 全量 `vitest run` = 79 文件 / 961 测试全绿;两份 tsconfig 均 exit 0;`tsdown` 构建成功。
61
+
62
+ ### 文档
63
+
64
+ - README / README.en 的卡片特性列表补上分栏一节(含「为什么分栏 = 提交语义不同」与「模型全部铺开」)。
65
+
66
+ ## 4.2.0 (2026-10-21)
67
+
68
+ > **模型行显示活动优惠徽标:只在上游此刻真在打折时出现。**
69
+ >
70
+ > Trae 把促销放在模型目录的同一次响应里——remote 目录的 `features.activity_discount`、wire 名单的
71
+ > `display_contact_config.activity_discount`(都是 JSON 字符串)。上游发的是**规则**(折扣比例、原价、
72
+ > 闲时时段与命名时区),不是文案。插件解析规则、在卡片**每次渲染时**按当前时钟判定,在模型名旁边画一枚
73
+ > 胶囊:`5 折` / `闲时 5 折` / `限时 5 折`,悬停给出原价→现价与时段。请求数、请求身份、积分语义全部不变。
74
+ >
75
+ > 判定放在渲染时刻而不是解析时刻,是因为 `time_windows` 是「Asia/Shanghai 周一到周五 00:00–09:00、
76
+ > 12:00–14:00、18:00–24:00,周末全天」这种墙钟时段:若在解析时挑定结论,夜里读到的「闲时 5 折」会在
77
+ > 第二天早上继续挂着。实测同一份规则,时钟平移 +0/6/12/24/36h 分别画出 3/5/5/3/5 枚(2026-10-21 18:16)。
78
+
79
+ ### 新增
80
+
81
+ - **模型行显示活动优惠徽标**(`feat(model)`)。解析 Trae 目录里的 `activity_discount` 规则,按渲染时刻判定后
82
+ 在模型名旁边画一枚胶囊:`5 折` / `闲时 5 折` / `限时 5 折`,悬停给出原价→现价、时段与时区。
83
+ - 解析层只留规则、不挑徽标;规则随 `lastCatalog` 持久化,判定在卡片每次渲染时做,胶囊随窗口自动出现和消失。
84
+ - **先信上游的 `current`**(服务端算好的「此刻状态」):`discount_type !== 'none'` 且折后价真比折前低才显示;
85
+ 只有它报「没打折」时才用 `off_peak.time_windows` 按模型声明的时区自己算(跨零点不推断、`start` 含 `end` 不含)。
86
+ - 文案只认上游明说的:只有 `limited_discount` 写「限时」,其余按「补贴 / 闲时」;颜色是插件自己的选择。
87
+ - **不从倍率反推折扣**:那一行本来就显示 `x0.40`,再挂「5 折」是同一句话说两遍。
88
+ - 纯提示:不新增请求、不改计费语义;国际区(订阅制)目录里没有这份数据,显示路径同一条、上游补齐即生效。
89
+ - 详见 `docs/PROMO_BADGE.md`;复算脚本 `scripts/probe-promo.mjs`(只读,不消耗积分)。
90
+
91
+ ### 修复
92
+
93
+ - **跨 group 去重不再丢促销规则**(`fix(solo-remote)`)。同一次目录响应里,同一个模型在部分 group 带
94
+ `activity_discount`、在部分 group 完全没有,而 group 顺序每次调用都可能不同(issue #23 的同一现象)。
95
+ `preferStrongerRow` 原先只按容量选行(等容量时先到先得),会把带规则的那一行丢掉,导致同一份上游数据
96
+ 相邻两次调用一次 1 枚徽标、一次 0 枚。现在选行仍按容量,但把另一行携带的规则**并到赢家身上**;
97
+ 没有改成「选带规则的行」,因为那会丢掉 1M Max 档——徽标不能以用户失去上下文预算为代价。
98
+ - **活动徽标的两处边界收紧**(`fix(promo)`)。
99
+ - 两条规则评分并列(都没有 `current`、各 1 个窗口)时,原先留先到的那条,今晚有没有徽标就取决于网关的
100
+ 分组顺序;改按规则内容的规范化 JSON(键名排序)取定,任意分组顺序结果一致。
101
+ - 上游 `time_zone` 写坏时,判定退回账号时区但悬停仍报那个读不出来的名字;改为报**实际用于判定的**时区。
102
+
103
+ ### 文档
104
+
105
+ - 新增 `docs/PROMO_BADGE.md`(两处字段位置、逐字抓包、判定规则、与 WorkBuddy #91 的能力对照、实机复核输出)。
106
+ - README / README.en 功能列表各加一条「活动优惠徽标」。
107
+ - `docs/PROMO_BADGE.md` 按 2026-10-21 17:14 复测校正:wire 侧实测 214 行 / 26 行带规则 / 9 个模型
108
+ (旧记 54 行 / 8 行 / 3 行在打折);补记判不了的窗口直接丢弃(不推断「全天」或跨零点)、
109
+ `limited` 分支的 `end_at` 未被读取(实测限时活动 `current` 报 `none`,所以「限时」暂不会真的出现)。
110
+
111
+ ### 测试
112
+
113
+ - 新增 `tests/promo.spec.ts`(20 例):`enable: false` 丢弃、JSON 字符串形态、两段价格字段(`consumption_rate` /
114
+ `after_consumption_rate`)、窗口过滤与丢弃(缺周几、空区间、越界)、时区判定、坏时区退回账号时区、
115
+ 跨零点不外推、`current` 优先、非 `limited` 不写「限时」、无数字不画、以及「同一份规则在框内/框外判定不同」。
116
+ - 新增 `tests/card-promo.spec.tsx`(6 例):胶囊渲染在模型名旁边、三种类型的颜色类、悬停数字、无规则不渲染、
117
+ 窗口外不渲染。
118
+ - `tests/solo-remote.spec.ts` 补跨 group 去重 4 例:容量与规则并存、评分排序、并列按内容取定、
119
+ 同一份数据换 group 顺序结果不变。
120
+ - 全仓 **78 文件 951 例**通过;`tsc` 两个工程干净;`build` 通过(CJS 344.00 kB / ESM 426.33 kB);
121
+ CI(ubuntu + windows)全绿。
122
+
3
123
  ## 4.1.0 (2026-10-21)
4
124
 
5
125
  > **受限网络(企业网只放行少数域名)下「刷新模型 500 / 聊天 4001」的完整修复。**
package/README.en.md CHANGED
@@ -36,7 +36,9 @@ A [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) bund
36
36
  - **Enterprise is a separate billing channel, not an alias for CN** — the same CN credential, when it belongs to a `tob_online` tenant, is served by **two** gateways: the consumer pool (billed against personal credits) and the tenant pool (billed against the enterprise allowance). Same model, two payers, so **you** choose who pays; the plugin never guesses. `trae` never touches the enterprise gateway, and `trae-enterprise` **never falls back** — a refusal is reported as a refusal instead of quietly re-running on personal credits. Enterprise is **off by default**: a personal-only account cannot use the tenant pool, so shipping it enabled would mean handing everyone a provider that fails on first use.
37
37
  - **Any panel can be switched off on its own** — each tab carries its own checkbox (CN and international checked by default, enterprise not): unchecking **withdraws that provider from the DSH model picker entirely** (not merely hiding the tab) and stops all of its requests; checking it again restores it with the account, directory, picks, and context budgets intact. If you never use the international side, just uncheck it.
38
38
  - **Tabbed settings card** — the card's top carries a "Domestic / Enterprise / Global" tab bar; each tab holds its own account picker, usage overview, and model management. Account, directory, selection, and unsaved drafts are fully isolated per tab — switching accounts or refreshing models on one tab never touches the other side's runtime catalog or sessions. CN and enterprise share one CN credential pool but keep **separate account bindings and model picks**: the two rosters differ (`DeepSeek-V4.1-Flash` is listed only on the enterprise pool), so picks never overwrite each other.
39
+ - **The card then splits into "Account / Models / Diagnostics"** (learned from the sibling `dsh-connect-workbuddy`). The region row above is the **scope** — which provider this whole card is about — while these are sections of that one card: Account covers who serves, what is left, the check-in and the pool; Models covers which models to tick, image input, the context budget and Save; Diagnostics read-only lists the panel, the provider id, the serving account, both token deadlines, the catalog/enabled model counts, whether the pool and the check-in are offered, and the **verbatim** text of this panel's last reported failure. The split exists because the two halves **commit differently**: an account change lands on the click, a catalog change is a draft until Save — so a single long column put the save button three screens away from what it saves. Switching sections never refetches, never drops a half-filled model draft, and never rebuilds the pool's log. A signed-out card renders no section strip, so its one screen of instructions cannot be hidden behind a tab the user cannot see. **The Models section lays every model out at once, with no inner scrollbar** — it is not a little window scrolling inside a page that already scrolls; the card's own page is the only scroll.
39
40
  - **Multiplier in the model name** — model names show the credit multiplier in Trae's own menu format (e.g. `GLM-5.2 · x0.79`), updated with each directory refresh.
41
+ - **Promotion badge** — Trae publishes its discount rules in the model directory (`activity_discount`: pay ratio, list price, off-peak windows with their time zone). The plugin judges them against **the clock at render time** and paints one capsule beside the model name (`5 折` / `off-peak 5 折` / `limited 5 折`), with the old price, the new price and the window on hover. Judging at render time is what keeps a night-time discount from still hanging there the next morning; when upstream says nothing is discounted right now no badge is painted, the multiplier is never used to guess a promotion, and no campaign without an end time is labelled "limited". It is information only: no request and no billing changes, and the international region (subscription) has no such data and therefore shows no badge. See [docs/PROMO_BADGE.md](docs/PROMO_BADGE.md).
40
42
  - **DSH local tool loop** — gets pending structured `tool_calls` from Trae `llm_utils_chat`, lets DSH execute its own local tools, then returns tool results to the model.
41
43
  - **Dual-region detection (CN / international)** — detects local sign-ins across Trae CN, TRAE SOLO CN, Trae, and TRAE SOLO installs. The region is derived from the credential's own `userRegion` claim (host suffix and edition label as fallbacks), with no manual switch.
42
44
  - **Region-isolated directories and selections** — CN and international each keep their own model directory, enabled picks, image opt-ins, and context budgets, and each provider reads only its own slot.
package/README.md CHANGED
@@ -36,11 +36,13 @@
36
36
  - **企业版是独立计费通道,不是国内版的别名** —— 同一张 CN 凭据若属于 `tob_online` 租户,会被**两个**网关同时服务:消费池(扣个人积分)与租户池(扣企业额度)。同一模型、两种付费方,所以「谁付钱」由你显式选,插件不替你猜。`trae` 永不碰企业网关;`trae-enterprise` **不回落**,被拒绝就如实报错,不会悄悄改用个人额度重跑。企业版**默认关闭**——纯个人账号用不了企业池,默认放开等于塞一条一用就失败的 provider。
37
37
  - **可单独关闭任一面板** —— 三个 tab 各带一个勾选(国内版与国际版默认勾选,企业版默认不勾):取消勾选即把该面板的供应商**整个从 DSH 模型选择器撤掉**(不是只藏起 tab),并且不再发起任何请求;重新勾选即恢复,账号、目录、勾选与上下文预算全部保留。用不到国际版就把国际版关掉即可。
38
38
  - **插件卡片 tab 切换** —— 设置卡片顶部为「国内版 / 企业版 / 国际版」三个 tab,各含独立的账号选择、用量概览与模型管理;每个 tab 的账号、目录、勾选与未保存草稿完全隔离——在一个 tab 里切账号或刷新模型,不会触碰另一边的运行时目录与会话。国内版与企业版共用同一份 CN 凭据池,但**各有各的账号绑定与模型勾选**:两个池的名录并不相同(`DeepSeek-V4.1-Flash` 只有企业池列),所以勾选互不覆盖。
39
+ - **卡片内再按「账号 / 模型 / 诊断」分栏**(学自同族 `dsh-connect-workbuddy`)—— 上面那排地区是**作用域**(这张卡片讲的是哪个供应商),这一排是同一张卡片里的**分栏**:账号栏管「谁在服务、还剩多少、签到与账号池」,模型栏管「勾哪些模型、图片输入、上下文预算、保存」,诊断栏只读地列出面板名、供应商 id、服务账号、两种令牌到期时间、目录/已启用模型数、账号池与签到支持与否,以及本面板最近一次报错的**原文**。分栏的理由是**提交语义不同**:账号改动点一下即生效,模型改动是草稿、必须点「保存」——一列长页会把保存按钮放到离它要保存的东西三屏之外。切栏不重新拉取、不丢未保存的模型草稿、账号池日志也不重建;未登录时不出分栏,那一屏说明不会被看不见的 tab 藏起来。**模型栏把所有模型一次性铺开,没有内层滚动条**——不再是一个套在滚动页面里的小窗口,唯一滚动的就是卡片自己那一页。
39
40
  - **账号池:自动选号 + 失败换号(默认关闭)** —— 本机读完几个账号后,「该用谁的额度」可以交给插件按规则排序:**先排除不可用的**(凭据被拒、套餐不覆盖、账号已不存在、限流未到重试时间、额度耗尽),再在可用账号里**优先快到期的那点额度**(与其放着过期,不如先花掉),最后才比总额、token 新鲜度,并以账号 id 兜底成**全序**(同样的输入永远得到同样的顺序,不会在两行之间来回抖)。三个面板**各有各的池**:国内版与企业版共用同一份 CN 凭据,但企业版只挑租户账号、国内版不会漂到租户号上。开着池时,发请求前先按上面的顺序选中一个账号,并且**真实请求失败会换下一个账号重试(最多 3 个)**——只有账号级失败才换(`401` 凭据被拒、额度耗尽、限流、上游 5xx);模型 id 写错、请求本身不合法**不换号**,因为那不是账号的问题,换号只会把同一个错误烧两遍、还冤枉一个没机会服务的账号。换号失败的真相会写进池的探针记录,下一次排序直接把它排到后面,而每一次结论都能在卡片上看到:`可用` / `限流` / `额度耗尽` / `凭据被拒` / `套餐不覆盖` / `上游不提供` / `本次未测出`,以及「什么时候可以重试」。**关掉池就回到严格绑定**,不会有任何残留 override。卡片里的「全部测速」会把每个成员按目标模型各测一次,结果**边跑边显示**(一行一个答案,不等整批跑完),并落盘成下次排序的依据。
40
41
 
41
42
  > 池是**显式选择**:默认关闭。它不会替你在没打开池的情况下换账号——「换账号等于换人计费」,所以这件事只能由你点头。池打开后卡片会明确写出**当前是谁在付费**(Host 报告的是「下一个真正发送请求的账号」,不是保存的那个绑定)。
42
43
 
43
44
  - **倍率内嵌模型名** —— 模型名称按 Trae 自身菜单的格式显示积分倍率(如 `GLM-5.2 · x0.79`),倍率随目录刷新更新。
45
+ - **活动优惠徽标** —— Trae 把促销规则发在目录里(`activity_discount`:折扣比例、原价、闲时时段与时区),插件按**当前时钟**判定后,在模型名旁边画一枚胶囊:`5 折` / `闲时 5 折` / `限时 5 折`,悬停写明原价、现价与时段。判定放在**渲染时刻**而不是解析时刻,所以夜间折扣不会在第二天早上继续挂着;上游说「此刻没打折」就不画,**不从倍率反推**,也不给没有结束时间的活动编「限时」。这是纯提示:不改请求、不改计费,国际区(订阅制)没有这项数据因此不显示。详见 [docs/PROMO_BADGE.md](docs/PROMO_BADGE.md)。
44
46
  - **DSH 本地工具循环** —— 通过 Trae `llm_utils_chat` 获取待执行的结构化 `tool_calls`,交由 DSH 自带的本地工具执行,再将工具结果回传模型。
45
47
  - **国内国际双区域自动识别** —— 自动发现 Trae CN / TRAE SOLO CN / Trae / TRAE SOLO 四个本地安装的登录账号;区域由凭证自带的 `userRegion` 声明自动判定(host 后缀与 edition 标签兜底),无需手动指定。
46
48
  - **目录与勾选按面板隔离** —— 三个面板各一套模型目录、勾选、图片开关与上下文预算,各自读各自的槽位。名录从**该面板所钉的那条网关**读取——原本钉在企业池的客户端去读消费池名录,会永远发现不了自己池的独占模型。