dsh-connect-trae 2.0.4 → 2.0.6
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 +30 -0
- package/README.en.md +1 -0
- package/README.md +1 -0
- package/lib/client.js +141 -16
- package/lib/index.d.ts +79 -1
- package/lib/index.js +171 -37
- package/package.json +7 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.0.6 (2026-09-22)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- **新增「可单独关闭任一版本的供应商」**(issue #11:用户问「能禁用国际版或国内版吗,有没有配置的地方」——「比如说我根本用不到国际版」):
|
|
8
|
+
- **此前确实无解,而且最容易被误用的那条路走不通**:`edition` 配置只收窄**凭据扫描范围**(找账号用),不参与 provider 注册;`src/index.ts` 的注册循环对 `REGION_KEYS = ['cn','ai']` 是无条件注册的,所以设 `edition: "cn"` 之后 `trae-global` 依旧出现在模型选择器里。卸载国际版 App 同理——无账号时该区域回落到**插件内置的兜底目录**(Gemini / GPT / MiniMax / Kimi 共 7 条),这些模型一个都调不通,却和可用模型并排显示(这正是 issue #8 的成因)。DSH 侧也没有「停用某供应商」的入口。
|
|
9
|
+
- **交互**:设置卡片两个 tab 各带一个勾选,**默认都勾选**(老用户零感知);取消勾选即关闭该供应商,重新勾选即恢复。
|
|
10
|
+
- **关闭是「真撤掉」,不是「藏 tab」**:抽掉 adapter 路由(`AdapterRegistrationHandle.replace([])`)——这是让模型**从 DSH 模型选择器消失**的那一步;同时抽掉供应商目录条目(`DirectoryRegistrationHandle.replace([])`),否则「设置 → 模型」仍会列出它。DSH 的类型契约明确允许持零路由而保持注册("An empty array is legal here ... unlike an empty initial registration"),因此实现是**先正常注册、再 `replace([])`**,两步在同一同步段内完成,无可观测的路由空窗,且开关**立即生效、无需重启**。
|
|
11
|
+
- **顺带消掉一个失败面**:关闭的区域连启动时的模型目录拉取都跳过,不再发任何 Remote/SOLO 请求——「没装国际版每次启动报一次错」那类噪音一并消失。
|
|
12
|
+
- **状态不丢**:开关写入只改目标槽位的 `enabled` 一个字段,该区域的目录 / 勾选 / 图片开关 / 上下文预算与另一区域的整个槽位原样带过,所以「关掉 → 再打开」能完整恢复。配套地在卡片保存模型目录时**回写 `enabled`**——那次写入会替换整个槽位,漏掉它就会「保存一次模型列表 = 悄悄重新打开已关闭的供应商」。
|
|
13
|
+
- **已知代价(明确提示而非静默失败)**:若某会话此前选中了被关闭供应商的模型,路由撤掉后再调用会报 `NO_ADAPTER`;这是「彻底移除」的必然结果,卡片在该 tab 上给出明确文案说明设置均已保留、勾选即可恢复。
|
|
14
|
+
- **两侧读同一条规则**:Host 的 `regionEnabled()` 与卡片的 `regionEnabledOf()` 共用「仅显式 `false` 关闭」的判定,回归测试用 8 种正常/畸形形状交叉比对二者,锁定永不分歧;卡片另读 Host 随文档下发的 `enabled`(signed-in / signed-out 两个分支都带),因为**未登录的区域恰恰是用户最想关掉的那一个**。
|
|
15
|
+
- **修复开发期发现的「勾选点了没反应」**:卡片把**整个 settings 段**传给了只接受 **`regions` 子对象**的判定函数,于是它去查 `section['cn']`(不存在)并**永远返回 `true`**——复选框恒定显示为勾选、点击看起来毫无变化;而**写入其实已经成功**(设置里确实落下了 `enabled: false`),Host 侧路由也确实撤掉了,问题只在卡片的读取。修复方式不是「把参数改对」了事,而是让 `regionsMapOf()` 统一收口、**同时接受整段与子对象两种入参**,使传错一半在结构上不再可能静默返回 `true`;并补 2 条回归测试覆盖「整段入参」与「返回值必须是 `regions` 子对象」(把拆包逻辑改回原样,这 2 条立刻失败,已实测)。
|
|
16
|
+
- **补齐「真实点击」测试,堵住让该 bug 溜过的结构性缺口**:此前 `vitest.config.ts` 只收 `tests/**/*.spec.ts` 且 `environment: 'node'`,**根本无法渲染 React 组件**——仓库里唯一的客户端测试还是自己标注了 DRIFT WARNING 的「手工镜像副本」,不是产品代码,所以「点一下复选框」从来没有被自动化验证过(这正是 bug 能发布出去的原因)。现在 `tests/card-region-switch.spec.tsx` 在 jsdom 里渲染**真组件**、`fireEvent.click` 真实 `<input type="checkbox">`,断言 settings 落值与勾选态翻转,8 条用例覆盖空配置/已关闭/点击写入/重新启用保留选择/点第二个框/不切 tab/关闭态文案/不可写禁用。**变异验证**:把拆包逻辑改回出 bug 的样子,4 条立刻失败(含「存了 false 却显示已勾选」这一条,与用户症状一致)。配套:放开 `.spec.tsx`、`tsconfig` 分派、新增 `jsdom` / `@testing-library/react` / `@testing-library/dom` / `react-dom@18` 四个 devDependencies;`dsh-client-ui-primitives` 因带浏览器专用 CSS modules 在测试中整体 mock(产品依赖契约不变)。
|
|
17
|
+
- 新增 10 条回归测试(区域开关判定与两侧一致性、整段/子对象双入参、开关往返不丢状态、关闭后 `listProviders()` 与目录条目同步消失且不影响另一侧、**模型选择器分组也消失**、重启后仍生效、两个都关仍可恢复、旧配置两边都开、`enabled` 在两种文档分支都下发)。开发中做过**变异验证**:把「抽掉路由」改成「照常注册」(即只隐藏 tab 的假实现),集成测试立刻失败;把目录 `replace` 改回按区域调用,4 条测试失败;把整段入参的拆包逻辑去掉,2 条测试失败——测试确实咬得住。
|
|
18
|
+
- 取证与回复草稿:`docs/ISSUE11_DIAGNOSIS.md`、`docs/ISSUE11_REPLY_DRAFT.md`。
|
|
19
|
+
|
|
20
|
+
## 2.0.5 (2026-09-22)
|
|
21
|
+
|
|
22
|
+
### Fixes
|
|
23
|
+
|
|
24
|
+
- **修复「缓存统计一直是 0」**(issue #10:用户反馈「我测试了一下一直0缓存」,并猜测是接口原因或 Trae 本身不缓存):
|
|
25
|
+
- **根因在插件自己这一层**:Trae 上游**确实有前缀缓存、也确实在报**(实测逐字节相同的长 prompt 连发,第二次起 `cache_read_input_tokens` 从 0 升到 5248、9216),但插件解码 `token_usage` 事件时**只搬了 4 个计数器**(`prompt_tokens` / `completion_tokens` / `total_tokens` / `reasoning_tokens`),`cache_read_input_tokens` 与 `cache_creation_input_tokens` 被直接丢弃;`TraeStreamEvent.usage` 在**类型层就无法表达**这两个值,转发层自然也无从搬运。
|
|
26
|
+
- **不是「放行」就够,必须做字段名映射**:`dsh-llm-pi-ai` 只在 `usage.cacheRead > 0` 时写入 `cacheReadTokens`,而 pi-ai 的 `parseChunkUsage` 只识别 `prompt_tokens_details.cached_tokens` / `prompt_cache_hit_tokens` / `cached_tokens` 三种拼写——Trae 的原生字段名不在其中,原样透传依然读不到。现在映射为规范的 `prompt_tokens_details.cached_tokens` / `.cache_write_tokens`。
|
|
27
|
+
- **先确认了语义再动手**:pi-ai 会做 `input = prompt_tokens - cacheRead - cacheWrite`,该减法仅在「`prompt_tokens` 包含缓存量」(OpenAI 口径)时成立。实测同一份 prompt 的 `prompt_tokens` 恒为 9224 而 `cache_read` 从 5248 涨到 9216,若为 Anthropic 的排除口径则同一输入的实际规模会在 14472 → 18440 之间跳变,不可能——据此确认 Trae 用包含口径。端到端复核账目守恒:`8 + 9216 + 0 = 9224`,与 `prompt_tokens` 完全相等,无重复扣减。
|
|
28
|
+
- **`src/raw-chat.ts` 存在同一类缺口**(Raw Chat 通道将来启用时会踩同一个坑):`RawChatDelta.usage` 补上缓存字段,并同时接受 OpenAI 嵌套拼写与 DeepSeek/Kimi 的顶层拼写。两个通道均保证「上游没报缓存时 wire 形状不变」,不产生空 `prompt_tokens_details`。
|
|
29
|
+
- 新增 4 个回归测试,其中 bridge 用例直接以真机捕获的 `token_usage` 事件原文作为输入。
|
|
30
|
+
- 取证与复现脚本:`docs/ISSUE10_DIAGNOSIS.md`、`scripts/probe-cache-convention.mjs`。
|
|
31
|
+
- **说明**:修复只保证「上游报了缓存就如实上报」。新会话/新前缀的第一轮必然 `cache_read=0`(冷启动),第二轮起才有值;实测 Trae 当前只报读不报写(`cache_creation_input_tokens` 恒为 0)。
|
|
32
|
+
|
|
3
33
|
## 2.0.4 (2026-09-16)
|
|
4
34
|
|
|
5
35
|
### Fixes
|
package/README.en.md
CHANGED
|
@@ -29,6 +29,7 @@ A [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) bund
|
|
|
29
29
|
## Features
|
|
30
30
|
|
|
31
31
|
- **Dual parallel providers** — the CN side registers as the `trae` provider (e.g. `DeepSeek-V4-Flash`, `DeepSeek-V4-Pro`), the international side as `trae-global` (the Gemini/GPT/MiniMax roster), and **both rosters appear in the DSH model picker simultaneously**: different sessions can each pick a side without interfering.
|
|
32
|
+
- **Either side can be switched off** — each tab carries its own checkbox (both checked by default): 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.
|
|
32
33
|
- **Tabbed settings card** — the card's top carries a "Domestic / 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.
|
|
33
34
|
- **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.
|
|
34
35
|
- **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.
|
package/README.md
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
## 功能特性
|
|
30
30
|
|
|
31
31
|
- **双供应商并行接入** —— 国内版注册为 DSH 的 `trae` provider(`DeepSeek-V4-Flash`、`DeepSeek-V4-Pro` 等),国际版注册为 `trae-global`(Gemini / GPT / MiniMax 阵容),**两边模型同时出现在 DSH 模型选择器里**:不同会话可以各选一边,互不干扰。
|
|
32
|
+
- **可单独关闭任一版本** —— 两个 tab 各带一个勾选(默认都勾选):取消勾选即把该版本的供应商**整个从 DSH 模型选择器撤掉**(不是只藏起 tab),并且不再发起任何请求;重新勾选即恢复,账号、目录、勾选与上下文预算全部保留。用不到国际版就把国际版关掉即可。
|
|
32
33
|
- **插件卡片 tab 切换** —— 设置卡片顶部为「国内版 / 国际版」两个 tab,各含独立的账号选择、用量概览与模型管理;每个 tab 的账号、目录、勾选与未保存草稿完全隔离——在一个 tab 里切账号或刷新模型,不会触碰另一边的运行时目录与会话。
|
|
33
34
|
- **倍率内嵌模型名** —— 模型名称按 Trae 自身菜单的格式显示积分倍率(如 `GLM-5.2 · x0.79`),倍率随目录刷新更新。
|
|
34
35
|
- **DSH 本地工具循环** —— 通过 Trae `llm_utils_chat` 获取待执行的结构化 `tool_calls`,交由 DSH 自带的本地工具执行,再将工具结果回传模型。
|
package/lib/client.js
CHANGED
|
@@ -38,6 +38,61 @@ window.__ModuleLoader__.load({
|
|
|
38
38
|
[region]: slot
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Narrow a settings value to the `regions` map. Accepts EITHER the whole
|
|
43
|
+
* settings section (`{ regions: {...}, ... }`) or the `regions` map itself, and
|
|
44
|
+
* unwraps the former. This tolerance is deliberate: passing the whole section
|
|
45
|
+
* where the map was expected was a real shipped bug — the lookup then read
|
|
46
|
+
* `section['cn']` (absent), so the card's checkbox reported `true` forever and
|
|
47
|
+
* clicking it appeared to do nothing even though the write succeeded.
|
|
48
|
+
*/
|
|
49
|
+
function regionsMapOf(value) {
|
|
50
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return {};
|
|
51
|
+
const record = value;
|
|
52
|
+
const nested = record["regions"];
|
|
53
|
+
if (typeof nested === "object" && nested !== null && !Array.isArray(nested)) return nested;
|
|
54
|
+
return record;
|
|
55
|
+
}
|
|
56
|
+
/** One region's stored slot as a plain object; any other shape reads as empty. */
|
|
57
|
+
function regionSlotOf(value, region) {
|
|
58
|
+
const slot = regionsMapOf(value)[region];
|
|
59
|
+
return typeof slot === "object" && slot !== null && !Array.isArray(slot) ? slot : {};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Whether one region's provider is switched on. Opt-out semantics: only an
|
|
63
|
+
* explicit `false` disables it, so a config written before this switch existed
|
|
64
|
+
* (and the pre-region-split flat fields, which never carry `enabled`) keep both
|
|
65
|
+
* providers running exactly as before. The Host reads the same rule through
|
|
66
|
+
* `regionStateOf`, so card and Host can never disagree about a region's state.
|
|
67
|
+
*
|
|
68
|
+
* `value` may be the whole settings section or the `regions` map (see
|
|
69
|
+
* {@link regionsMapOf}).
|
|
70
|
+
*/
|
|
71
|
+
function regionEnabledOf(value, region) {
|
|
72
|
+
return regionSlotOf(value, region)["enabled"] !== false;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Build the next `regions` settings value for a provider on/off toggle. ONLY
|
|
76
|
+
* the target region's `enabled` flag changes: every other field of that slot
|
|
77
|
+
* (its directory, selection, image opt-ins, context budgets) and every other
|
|
78
|
+
* region's slot are carried over verbatim, so switching a provider off never
|
|
79
|
+
* discards the user's model picks and switching it back on restores them.
|
|
80
|
+
*
|
|
81
|
+
* This is deliberately separate from {@link nextRegionSlots}: that helper
|
|
82
|
+
* writes a whole slot from a signed-in tab's draft, while this one must work
|
|
83
|
+
* for a region that is signed OUT — which is precisely the region a user wants
|
|
84
|
+
* to switch off (no international install, no international account).
|
|
85
|
+
*
|
|
86
|
+
* `value` may be the whole settings section or the `regions` map; the RETURN
|
|
87
|
+
* value is always the `regions` map, i.e. exactly what `settingsScope.set(
|
|
88
|
+
* 'regions', ...)` needs.
|
|
89
|
+
*/
|
|
90
|
+
function nextRegionEnabled(value, region, enabled) {
|
|
91
|
+
return nextRegionSlots(regionsMapOf(value), region, {
|
|
92
|
+
...regionSlotOf(value, region),
|
|
93
|
+
enabled
|
|
94
|
+
});
|
|
95
|
+
}
|
|
41
96
|
//#endregion
|
|
42
97
|
//#region src/client/icon.ts
|
|
43
98
|
/**
|
|
@@ -77,12 +132,18 @@ window.__ModuleLoader__.load({
|
|
|
77
132
|
.dsm-btn-primary:hover:not(:disabled){opacity:.9}
|
|
78
133
|
.dsm-trae-usage{display:flex;flex-direction:column;gap:16px;margin:0;padding:16px 0 4px}
|
|
79
134
|
.dsm-trae-tabs{display:flex;gap:6px;padding:4px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:10px;background:var(--dsw-alias-bg-layer-3,#2a2c33)}
|
|
80
|
-
.dsm-trae-tab{
|
|
135
|
+
.dsm-trae-tab-cell{display:flex;align-items:center;gap:2px;flex:1;min-width:0}
|
|
136
|
+
.dsm-trae-tab{appearance:none;font:inherit;cursor:pointer;flex:1;min-width:0;border:0;border-radius:7px;padding:7px 10px;color:var(--dsw-alias-label-tertiary,#999);font-size:13px;font-weight:500;line-height:18px;background:transparent;transition:color .15s,background .15s;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
81
137
|
.dsm-trae-tab:hover:not(:disabled):not(.dsm-trae-tab-active){color:var(--dsw-alias-label-primary,#e6e6e6)}
|
|
82
138
|
.dsm-trae-tab:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:1px}
|
|
83
139
|
.dsm-trae-tab-active{color:var(--dsw-alias-label-primary,#e6e6e6);background:var(--dsw-alias-bg-layer-2,#232529);box-shadow:inset 0 0 0 1px var(--dsw-alias-border-l2,#3a3d45)}
|
|
140
|
+
.dsm-trae-tab-off{opacity:.55}
|
|
141
|
+
.dsm-trae-tab-switch{display:inline-flex;align-items:center;flex:none;padding:0 8px 0 2px;cursor:pointer}
|
|
142
|
+
.dsm-trae-tab-switch input{margin:0;cursor:pointer;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
|
|
143
|
+
.dsm-trae-tab-switch input:disabled{cursor:not-allowed}
|
|
84
144
|
.dsm-trae-tab-dot{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:6px;vertical-align:baseline}
|
|
85
145
|
.dsm-trae-tab-hint{margin:0;color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
|
|
146
|
+
.dsm-trae-tab-off-notice{margin:0;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:10px;color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:12px;line-height:18px;background:var(--dsw-alias-bg-layer-3,#2a2c33)}
|
|
86
147
|
.dsm-trae-usage-account{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:14px;background:var(--dsw-alias-bg-layer-2,#24262c)}
|
|
87
148
|
.dsm-trae-usage-account-copy{display:flex;flex-direction:column;gap:3px;min-width:0}
|
|
88
149
|
.dsm-trae-usage-expiry{padding-left:19px;color:var(--dsw-alias-label-tertiary,#9aa0a8);font-size:12px;line-height:18px}
|
|
@@ -223,6 +284,8 @@ window.__ModuleLoader__.load({
|
|
|
223
284
|
const [drafts, setDrafts] = (0, react.useState)({});
|
|
224
285
|
const [saving, setSaving] = (0, react.useState)(false);
|
|
225
286
|
const [switchingAccount, setSwitchingAccount] = (0, react.useState)(false);
|
|
287
|
+
/** Region whose on/off checkbox write is in flight, so its box can't race. */
|
|
288
|
+
const [togglingRegion, setTogglingRegion] = (0, react.useState)(void 0);
|
|
226
289
|
const mounted = (0, react.useRef)(true);
|
|
227
290
|
(0, react.useEffect)(() => {
|
|
228
291
|
mounted.current = true;
|
|
@@ -233,6 +296,21 @@ window.__ModuleLoader__.load({
|
|
|
233
296
|
(0, react.useEffect)(() => settingsScope?.subscribe(() => {
|
|
234
297
|
setSettingsRevision((value) => value + 1);
|
|
235
298
|
}), [settingsScope]);
|
|
299
|
+
/**
|
|
300
|
+
* Whether one region's provider is switched on, read off the SAME committed
|
|
301
|
+
* settings document the Host reads (`regionEnabledOf` mirrors the Host's
|
|
302
|
+
* `regionStateOf` opt-out rule: only an explicit `false` disables). Reading
|
|
303
|
+
* the stored value rather than echoing local state means a rejected write,
|
|
304
|
+
* another window's change, or a restart all converge on the truth.
|
|
305
|
+
*
|
|
306
|
+
* The whole settings section is passed deliberately: `regionEnabledOf`
|
|
307
|
+
* accepts either it or the bare `regions` map, because passing the section
|
|
308
|
+
* where the map was expected was a shipped bug (the lookup read
|
|
309
|
+
* `section['cn']`, found nothing, and reported `true` forever — the checkbox
|
|
310
|
+
* stayed checked and clicking it appeared dead while the write succeeded).
|
|
311
|
+
*/
|
|
312
|
+
const regionOn = (item) => regionEnabledOf(settingsScope?.getSnapshot().value, item);
|
|
313
|
+
const activeRegionOn = regionOn(activeRegion);
|
|
236
314
|
const refreshUsage = (0, react.useCallback)(async (region, signal) => {
|
|
237
315
|
try {
|
|
238
316
|
const response = await fetch(withTraeRegion(TRAE_USAGE_PATH, region), {
|
|
@@ -260,7 +338,7 @@ window.__ModuleLoader__.load({
|
|
|
260
338
|
}
|
|
261
339
|
}, [t]);
|
|
262
340
|
(0, react.useEffect)(() => {
|
|
263
|
-
if (!open) return;
|
|
341
|
+
if (!open || !activeRegionOn) return;
|
|
264
342
|
const controller = new AbortController();
|
|
265
343
|
refreshUsage(activeRegion, controller.signal);
|
|
266
344
|
return () => {
|
|
@@ -269,6 +347,7 @@ window.__ModuleLoader__.load({
|
|
|
269
347
|
}, [
|
|
270
348
|
open,
|
|
271
349
|
activeRegion,
|
|
350
|
+
activeRegionOn,
|
|
272
351
|
refreshUsage
|
|
273
352
|
]);
|
|
274
353
|
const status = statusByRegion[activeRegion] ?? {
|
|
@@ -276,7 +355,7 @@ window.__ModuleLoader__.load({
|
|
|
276
355
|
accounts: []
|
|
277
356
|
};
|
|
278
357
|
(0, react.useEffect)(() => {
|
|
279
|
-
if (!open || status.status !== "signed-in") return;
|
|
358
|
+
if (!open || !activeRegionOn || status.status !== "signed-in") return;
|
|
280
359
|
const controller = new AbortController();
|
|
281
360
|
const timer = window.setInterval(() => {
|
|
282
361
|
refreshUsage(activeRegion, controller.signal);
|
|
@@ -288,6 +367,7 @@ window.__ModuleLoader__.load({
|
|
|
288
367
|
}, [
|
|
289
368
|
open,
|
|
290
369
|
activeRegion,
|
|
370
|
+
activeRegionOn,
|
|
291
371
|
refreshUsage,
|
|
292
372
|
status.status
|
|
293
373
|
]);
|
|
@@ -326,6 +406,22 @@ window.__ModuleLoader__.load({
|
|
|
326
406
|
if (mounted.current) setSwitchingAccount(false);
|
|
327
407
|
}
|
|
328
408
|
};
|
|
409
|
+
/**
|
|
410
|
+
* Switch one region's provider off or on (issue #11). The write carries the
|
|
411
|
+
* region's whole slot through untouched — only `enabled` changes — so the
|
|
412
|
+
* user's directory, model picks, image opt-ins and budgets survive a
|
|
413
|
+
* round trip. The Host withdraws or restores the provider route on the next
|
|
414
|
+
* `onChange`, which is what actually removes it from DSH's model picker.
|
|
415
|
+
*/
|
|
416
|
+
const toggleRegion = async (item, enabled) => {
|
|
417
|
+
if (settingsScope === void 0 || settingsScope.getSnapshot().writable !== true) return;
|
|
418
|
+
setTogglingRegion(item);
|
|
419
|
+
try {
|
|
420
|
+
await settingsScope.set("regions", nextRegionEnabled(settingsScope.getSnapshot().value, item, enabled));
|
|
421
|
+
} finally {
|
|
422
|
+
if (mounted.current) setTogglingRegion(void 0);
|
|
423
|
+
}
|
|
424
|
+
};
|
|
329
425
|
const refreshModels = async () => {
|
|
330
426
|
setBusy(true);
|
|
331
427
|
try {
|
|
@@ -443,6 +539,7 @@ window.__ModuleLoader__.load({
|
|
|
443
539
|
try {
|
|
444
540
|
if (status.status !== "signed-in") return;
|
|
445
541
|
await settingsScope.set("regions", nextRegionSlots(configuredRegions, activeRegion, {
|
|
542
|
+
enabled: regionOn(activeRegion),
|
|
446
543
|
lastCatalog: visibleModels.map((model) => ({
|
|
447
544
|
...model,
|
|
448
545
|
input: ["text"]
|
|
@@ -503,19 +600,36 @@ window.__ModuleLoader__.load({
|
|
|
503
600
|
"aria-label": title,
|
|
504
601
|
children: TRAE_REGIONS.map((item) => {
|
|
505
602
|
const regionStatus = statusByRegion[item];
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
"
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
603
|
+
const on = regionOn(item);
|
|
604
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
605
|
+
className: "dsm-trae-tab-cell",
|
|
606
|
+
role: "presentation",
|
|
607
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
608
|
+
type: "button",
|
|
609
|
+
role: "tab",
|
|
610
|
+
"aria-selected": item === activeRegion,
|
|
611
|
+
className: `dsm-trae-tab${item === activeRegion ? " dsm-trae-tab-active" : ""}${on ? "" : " dsm-trae-tab-off"}`,
|
|
612
|
+
onClick: () => {
|
|
613
|
+
setActiveRegion(item);
|
|
614
|
+
},
|
|
615
|
+
children: [regionStatus === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
616
|
+
"aria-hidden": "true",
|
|
617
|
+
className: "dsm-trae-tab-dot",
|
|
618
|
+
style: dotStyle(regionStatus.status)
|
|
619
|
+
}), item === "cn" ? t("row.tabCn") : t("row.tabAi")]
|
|
620
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
621
|
+
className: "dsm-trae-tab-switch",
|
|
622
|
+
title: t("row.tabSwitchHint"),
|
|
623
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
624
|
+
type: "checkbox",
|
|
625
|
+
checked: on,
|
|
626
|
+
disabled: settingsScope?.getSnapshot().writable !== true || togglingRegion !== void 0,
|
|
627
|
+
"aria-label": t("row.tabSwitchAria", { region: item === "cn" ? t("row.tabCn") : t("row.tabAi") }),
|
|
628
|
+
onChange: (event) => {
|
|
629
|
+
toggleRegion(item, event.currentTarget.checked);
|
|
630
|
+
}
|
|
631
|
+
})
|
|
632
|
+
})]
|
|
519
633
|
}, item);
|
|
520
634
|
})
|
|
521
635
|
}),
|
|
@@ -523,6 +637,11 @@ window.__ModuleLoader__.load({
|
|
|
523
637
|
className: "dsm-trae-tab-hint",
|
|
524
638
|
children: t("row.tabHint")
|
|
525
639
|
}),
|
|
640
|
+
regionOn(activeRegion) ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
641
|
+
className: "dsm-trae-tab-off-notice",
|
|
642
|
+
role: "status",
|
|
643
|
+
children: t("row.tabOffNotice")
|
|
644
|
+
}),
|
|
526
645
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
527
646
|
className: "dsm-trae-usage-account",
|
|
528
647
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -850,6 +969,9 @@ window.__ModuleLoader__.load({
|
|
|
850
969
|
"row.tabCn": "Domestic",
|
|
851
970
|
"row.tabAi": "Global",
|
|
852
971
|
"row.tabHint": "Each tab is a separate provider (trae / trae-global) with its own account, usage, and models. Both sides are live at once: different sessions can pick from either side, and changes on one tab never touch the other.",
|
|
972
|
+
"row.tabSwitchHint": "Uncheck to switch this provider off. Its models disappear from the DSH model picker; its account, usage, and model settings are kept and return when you check it again.",
|
|
973
|
+
"row.tabSwitchAria": "Offer the {region} provider to DSH",
|
|
974
|
+
"row.tabOffNotice": "This provider is switched off: none of its models are offered to DSH. Your account, usage, and model settings are kept — check the box above the tab to bring it back.",
|
|
853
975
|
"row.signedOut": "Not signed in",
|
|
854
976
|
"row.signedOutHint": "Sign in once in the Trae desktop app; this plugin follows that sign-in automatically.",
|
|
855
977
|
"row.searchedTitle": "Paths checked",
|
|
@@ -915,6 +1037,9 @@ window.__ModuleLoader__.load({
|
|
|
915
1037
|
"row.tabCn": "国内版",
|
|
916
1038
|
"row.tabAi": "国际版",
|
|
917
1039
|
"row.tabHint": "每个 tab 是一个独立供应商(trae / trae-global),各有自己的账号、用量与模型。两边同时生效:不同会话可各选一边,一侧的改动不影响另一侧。",
|
|
1040
|
+
"row.tabSwitchHint": "取消勾选即关闭该供应商:它的模型不再出现在 DSH 模型选择器里;账号、用量与模型设置都会保留,重新勾选即恢复。",
|
|
1041
|
+
"row.tabSwitchAria": "是否向 DSH 提供{region}供应商",
|
|
1042
|
+
"row.tabOffNotice": "该供应商已关闭:它的模型不会提供给 DSH。账号、用量与模型设置均已保留——勾选上方复选框即可恢复。",
|
|
918
1043
|
"row.signedOut": "未登录",
|
|
919
1044
|
"row.signedOutHint": "在 Trae 桌面 App 里登录一次即可,插件会自动跟随当前登录的账号。",
|
|
920
1045
|
"row.searchedTitle": "已检查的路径",
|
package/lib/index.d.ts
CHANGED
|
@@ -760,6 +760,10 @@ type RawChatDelta = {
|
|
|
760
760
|
inputTokens?: number;
|
|
761
761
|
outputTokens?: number;
|
|
762
762
|
totalTokens?: number;
|
|
763
|
+
/** Cache-read tokens (`prompt_tokens_details.cached_tokens` / `cached_tokens`). */
|
|
764
|
+
cacheReadTokens?: number;
|
|
765
|
+
/** Cache-write tokens (`prompt_tokens_details.cache_write_tokens`). */
|
|
766
|
+
cacheWriteTokens?: number;
|
|
763
767
|
} | {
|
|
764
768
|
type: 'done';
|
|
765
769
|
finishReason: string;
|
|
@@ -1018,6 +1022,17 @@ type TraeStreamEvent = {
|
|
|
1018
1022
|
outputTokens?: number;
|
|
1019
1023
|
totalTokens?: number;
|
|
1020
1024
|
reasoningTokens?: number;
|
|
1025
|
+
/**
|
|
1026
|
+
* Trae's prompt-cache accounting (`cache_read_input_tokens` /
|
|
1027
|
+
* `cache_creation_input_tokens` on the `token_usage` event). Verified
|
|
1028
|
+
* 2026-09-22 (docs/ISSUE10_DIAGNOSIS.md): a byte-identical 9224-token
|
|
1029
|
+
* prompt reports `prompt_tokens: 9224` while `cache_read_input_tokens`
|
|
1030
|
+
* rises to 9216, so these are a *subset* of `prompt_tokens` — the OpenAI
|
|
1031
|
+
* convention, not Anthropic's exclusive one. Forwarding them as
|
|
1032
|
+
* `cached_tokens` is therefore arithmetically correct.
|
|
1033
|
+
*/
|
|
1034
|
+
cacheReadTokens?: number;
|
|
1035
|
+
cacheWriteTokens?: number;
|
|
1021
1036
|
} | {
|
|
1022
1037
|
type: 'done';
|
|
1023
1038
|
finishReason: string;
|
|
@@ -1331,6 +1346,42 @@ interface TraeWebSearchPath {
|
|
|
1331
1346
|
reason: 'missing' | 'unreadable' | 'invalid';
|
|
1332
1347
|
message?: string;
|
|
1333
1348
|
}
|
|
1349
|
+
/**
|
|
1350
|
+
* Build the next `regions` settings value for the card's save. The write
|
|
1351
|
+
* targets ONLY the signed-in account's region slot; every other region's slot
|
|
1352
|
+
* is carried over untouched, so switching accounts never clobbers the other
|
|
1353
|
+
* region's picks. Tolerates any stored shape (absent, non-object) by starting
|
|
1354
|
+
* from an empty document.
|
|
1355
|
+
*/
|
|
1356
|
+
declare function nextRegionSlots<Slot extends object>(regions: unknown, region: TraeRegion, slot: Slot): Record<string, unknown>;
|
|
1357
|
+
/**
|
|
1358
|
+
* Whether one region's provider is switched on. Opt-out semantics: only an
|
|
1359
|
+
* explicit `false` disables it, so a config written before this switch existed
|
|
1360
|
+
* (and the pre-region-split flat fields, which never carry `enabled`) keep both
|
|
1361
|
+
* providers running exactly as before. The Host reads the same rule through
|
|
1362
|
+
* `regionStateOf`, so card and Host can never disagree about a region's state.
|
|
1363
|
+
*
|
|
1364
|
+
* `value` may be the whole settings section or the `regions` map (see
|
|
1365
|
+
* {@link regionsMapOf}).
|
|
1366
|
+
*/
|
|
1367
|
+
declare function regionEnabledOf(value: unknown, region: TraeRegion): boolean;
|
|
1368
|
+
/**
|
|
1369
|
+
* Build the next `regions` settings value for a provider on/off toggle. ONLY
|
|
1370
|
+
* the target region's `enabled` flag changes: every other field of that slot
|
|
1371
|
+
* (its directory, selection, image opt-ins, context budgets) and every other
|
|
1372
|
+
* region's slot are carried over verbatim, so switching a provider off never
|
|
1373
|
+
* discards the user's model picks and switching it back on restores them.
|
|
1374
|
+
*
|
|
1375
|
+
* This is deliberately separate from {@link nextRegionSlots}: that helper
|
|
1376
|
+
* writes a whole slot from a signed-in tab's draft, while this one must work
|
|
1377
|
+
* for a region that is signed OUT — which is precisely the region a user wants
|
|
1378
|
+
* to switch off (no international install, no international account).
|
|
1379
|
+
*
|
|
1380
|
+
* `value` may be the whole settings section or the `regions` map; the RETURN
|
|
1381
|
+
* value is always the `regions` map, i.e. exactly what `settingsScope.set(
|
|
1382
|
+
* 'regions', ...)` needs.
|
|
1383
|
+
*/
|
|
1384
|
+
declare function nextRegionEnabled(value: unknown, region: TraeRegion, enabled: boolean): Record<string, unknown>;
|
|
1334
1385
|
/** Subscription status of an international (ai) account, rendered instead of the CN credit packs. */
|
|
1335
1386
|
interface TraeWebPayStatus {
|
|
1336
1387
|
isDollarUsageBilling: boolean;
|
|
@@ -1354,6 +1405,7 @@ type TraeWebUsage = {
|
|
|
1354
1405
|
accounts: readonly TraeWebAccount[];
|
|
1355
1406
|
message?: string;
|
|
1356
1407
|
searched?: readonly TraeWebSearchPath[];
|
|
1408
|
+
enabled?: boolean;
|
|
1357
1409
|
} | {
|
|
1358
1410
|
status: 'signed-in';
|
|
1359
1411
|
accountId: string;
|
|
@@ -1361,6 +1413,8 @@ type TraeWebUsage = {
|
|
|
1361
1413
|
tokenExpiresAtMs: number;
|
|
1362
1414
|
/** Which per-region model directory and selection this account owns. */
|
|
1363
1415
|
region: TraeRegion;
|
|
1416
|
+
/** Whether this region's provider is currently offered to DSH. */
|
|
1417
|
+
enabled?: boolean;
|
|
1364
1418
|
accounts: readonly TraeWebAccount[];
|
|
1365
1419
|
models: readonly TraeWebModel[];
|
|
1366
1420
|
enabledModelIds: readonly string[];
|
|
@@ -1395,6 +1449,12 @@ interface TraeUsageRouteOptions {
|
|
|
1395
1449
|
displayModels(region: TraeRegion): readonly TraeModelInfo[];
|
|
1396
1450
|
/** The user's model selection in the requested region, stored as model id (= Trae name). */
|
|
1397
1451
|
enabledModelIds(region: TraeRegion): readonly string[];
|
|
1452
|
+
/**
|
|
1453
|
+
* Whether the requested region's provider is currently offered to DSH. The
|
|
1454
|
+
* card renders this as the tab's on/off checkbox, so the switch reflects the
|
|
1455
|
+
* committed settings value rather than a local guess.
|
|
1456
|
+
*/
|
|
1457
|
+
regionEnabled(region: TraeRegion): boolean;
|
|
1398
1458
|
/** Re-read one region's live directory from the upstream. */
|
|
1399
1459
|
discoverModels?(region: TraeRegion, signal?: AbortSignal): Promise<readonly TraeModelInfo[]>;
|
|
1400
1460
|
/** Raw-Chat capability state of the requested region. */
|
|
@@ -1425,6 +1485,15 @@ declare const inject: string[];
|
|
|
1425
1485
|
declare const TRAE_SETTINGS_NS = "trae";
|
|
1426
1486
|
/** One region's saved model state: its own directory and the user's selection within it. */
|
|
1427
1487
|
interface TraeRegionState {
|
|
1488
|
+
/**
|
|
1489
|
+
* Whether this region's provider is switched on. Opt-out: only an explicit
|
|
1490
|
+
* `false` disables it, so a config predating this switch keeps both providers
|
|
1491
|
+
* running. A disabled region is fully withdrawn from the harness — its
|
|
1492
|
+
* adapter route and its configurable-provider entry both hold zero routes, so
|
|
1493
|
+
* it disappears from DSH's model picker instead of lingering as an
|
|
1494
|
+
* unselectable row (see `syncRegionRegistration`).
|
|
1495
|
+
*/
|
|
1496
|
+
enabled?: boolean;
|
|
1428
1497
|
/** The last-refreshed raw directory for this region; what the card displays. */
|
|
1429
1498
|
lastCatalog?: TraeModelInfo[];
|
|
1430
1499
|
/** The user's selection in this region, as model ids (= Trae name). */
|
|
@@ -1480,7 +1549,16 @@ interface Config {
|
|
|
1480
1549
|
* picks (the same failure workbuddy fixed with its region split).
|
|
1481
1550
|
*/
|
|
1482
1551
|
declare function regionStateOf(config: Config, region: TraeRegion): TraeRegionState;
|
|
1552
|
+
/**
|
|
1553
|
+
* Whether one region's provider is switched on (issue #11). Opt-out semantics:
|
|
1554
|
+
* only an explicit `false` disables it, so every config written before this
|
|
1555
|
+
* switch existed — including the pre-region-split flat fields, which never
|
|
1556
|
+
* carry `enabled` — keeps both providers running exactly as before. The card
|
|
1557
|
+
* reads the same rule through `regionEnabledOf`, so the two halves can never
|
|
1558
|
+
* disagree about a region's state.
|
|
1559
|
+
*/
|
|
1560
|
+
declare function regionEnabled(config: Config, region: TraeRegion): boolean;
|
|
1483
1561
|
declare const Config: z<Config>;
|
|
1484
1562
|
declare function apply(ctx: Context, config: Config): void;
|
|
1485
1563
|
//#endregion
|
|
1486
|
-
export { Config, FALLBACK_TRAE_MODELS, FALLBACK_TRAE_MODELS_AI, REGION_GATEWAYS, type RawChatDelta, type RawChatMessage, type RawChatTool, SseDecoder, type SseEvent, TRAE_AI_PROVIDER, TRAE_CN_AGENT_TASK_PATH, TRAE_CN_TITLE_PATH, TRAE_MODEL_DETAIL_FUNCTIONS, TRAE_MODEL_DETAIL_PATH, TRAE_PAY_BASE, TRAE_PROVIDER, TRAE_PROVIDERS, TRAE_PROVIDER_DISPLAY_NAMES, TRAE_RAW_CHAT_V1_PATH, TRAE_RAW_CHAT_V2_PATH, TRAE_REASONING_EFFORTS, TRAE_REGIONS, TRAE_REGION_PARAM, TRAE_SETTINGS_NS, TRAE_SOLO_CHAT_PATH, TRAE_SOLO_FUNCTION, TRAE_SOLO_MODELS_PATH, TRAE_SOLO_REMOTE_BASE, TRAE_STREAM_IDLE_TIMEOUT_MS, TRAE_USAGE_PATH, TRAE_VERSION_CODE_FALLBACK, type TraeActivityRule, type TraeAdapter, type TraeCachedModelConfig, TraeCatalog, type TraeChatResult, type TraeCheckinStatus, type TraeContextBudget, type TraeCredential, TraeCredentialStore, TraeDelegatingUpstreamClient, type TraeDiscoveredModel, type TraeDiscoveredReasoning, type TraeEdition, TraeFallbackUpstreamClient, type TraeFallbackUpstreamOptions, type TraeFusionRawChatEnvelope, TraeGatedUpstreamClient, type TraeGatedUpstreamOptions, type TraeIdentity, type TraeInputModality, type TraeModelDetailRequest, type TraeModelInfo, type TraeObservedModelConfig, TraeRawCapabilityController, type TraeRawCapabilityControllerOptions, type TraeRawCapabilitySnapshot, TraeRawCapabilityState, type TraeRawChatBehaviorConfig, type TraeRawChatCapability, type TraeRawChatClientOptions, type TraeRawChatConfig, type TraeRawChatFailureReason, type TraeRawChatRuntimeConfig, TraeRawChatUpstreamClient, type TraeRawDiagnostic, type TraeRawDiagnosticState, type TraeRawGateway, type TraeRawGatewayOptions, type TraeRawResolverResult, type TraeReasoningCapability, type TraeReasoningEffort, type TraeRefreshDevice, type TraeRegion, type TraeRegionGateways, TraeRegionState, type TraeShim, TraeSoloBridge, type TraeSoloClientOptions, TraeSoloRemoteCatalogClient, type TraeSoloRemoteCatalogOptions, TraeSoloUpstreamClient, type TraeStorageCandidate, type TraeStreamEvent, type TraeUpstreamClient, TraeUsageClient, type TraeUsageOptions, type TraeUsagePack, type TraeUsageRouteOptions, type TraeUsageSnapshot, type TraeUsageSummary, type TraeUsageView, type TraeWebActivity, type TraeWebCheckin, type TraeWebCredits, type TraeWebUsage, type TraeWireModel, UnconfiguredTraeUpstreamClient, apply, applyContextBudgets, applyImageSelection, applyReasoningEffort, bridgeTraeSoloStream, buildTraeAgentTaskBody, buildTraeCnHeaders, buildTraeFusionRawChatEnvelope, buildTraeModelDetailRequest, buildTraeRawChatDraft, buildTraeRawChatRuntimeConfig, classifyTraeRawChatFailure, createTraeAdapter, createTraeRawGateway, createTraeShim, decodeRawChatChunk, decodeTraeEvent, decryptTraeStorageValue, deriveCatalog, discoveredCatalog, fallbackModelsFor, hashTraeRawChatArg, identityHeaders, inject, legacyTraeOwnAuthPath, mergeTraeModelSources, name, normalizeTraeCredential, normalizeTraeVersionCode, parseObservedModelConfig, parseReasoningCapability, parseTraeAuthValue, parseTraeCachedModel, parseTraeModelExtraConfigLogLine, parseTraeRawChatBehaviorConfig, parseTraeRemoteModel, parseTraeStorageDocument, pickTraeStorageIdentity, prepareSoloBody, probeTraeRawChatCapability, rawCapabilityDiagnostic, rawCapabilityFingerprint, readTraeCachedModel, readTraeCliIdentity, readTraeIdentity, refreshTraeCredential, regionOfCredential, regionOfEdition, regionOfHost, regionOfTraeProvider, regionOfTraeStatusUrl, regionOfUserRegion, regionStateOf, registerTraeUsageRoute, resolveTraeIdentity, resolveTraeRawRuntime, sanitizeCatalog, traeEndpoint, traeInputModalities, traeModelDisplayName, traeOwnAuthPath, traeRawChatExtraInfo, traeStorageCandidates, traeWebUsage, withTraeRegion };
|
|
1564
|
+
export { Config, FALLBACK_TRAE_MODELS, FALLBACK_TRAE_MODELS_AI, REGION_GATEWAYS, type RawChatDelta, type RawChatMessage, type RawChatTool, SseDecoder, type SseEvent, TRAE_AI_PROVIDER, TRAE_CN_AGENT_TASK_PATH, TRAE_CN_TITLE_PATH, TRAE_MODEL_DETAIL_FUNCTIONS, TRAE_MODEL_DETAIL_PATH, TRAE_PAY_BASE, TRAE_PROVIDER, TRAE_PROVIDERS, TRAE_PROVIDER_DISPLAY_NAMES, TRAE_RAW_CHAT_V1_PATH, TRAE_RAW_CHAT_V2_PATH, TRAE_REASONING_EFFORTS, TRAE_REGIONS, TRAE_REGION_PARAM, TRAE_SETTINGS_NS, TRAE_SOLO_CHAT_PATH, TRAE_SOLO_FUNCTION, TRAE_SOLO_MODELS_PATH, TRAE_SOLO_REMOTE_BASE, TRAE_STREAM_IDLE_TIMEOUT_MS, TRAE_USAGE_PATH, TRAE_VERSION_CODE_FALLBACK, type TraeActivityRule, type TraeAdapter, type TraeCachedModelConfig, TraeCatalog, type TraeChatResult, type TraeCheckinStatus, type TraeContextBudget, type TraeCredential, TraeCredentialStore, TraeDelegatingUpstreamClient, type TraeDiscoveredModel, type TraeDiscoveredReasoning, type TraeEdition, TraeFallbackUpstreamClient, type TraeFallbackUpstreamOptions, type TraeFusionRawChatEnvelope, TraeGatedUpstreamClient, type TraeGatedUpstreamOptions, type TraeIdentity, type TraeInputModality, type TraeModelDetailRequest, type TraeModelInfo, type TraeObservedModelConfig, TraeRawCapabilityController, type TraeRawCapabilityControllerOptions, type TraeRawCapabilitySnapshot, TraeRawCapabilityState, type TraeRawChatBehaviorConfig, type TraeRawChatCapability, type TraeRawChatClientOptions, type TraeRawChatConfig, type TraeRawChatFailureReason, type TraeRawChatRuntimeConfig, TraeRawChatUpstreamClient, type TraeRawDiagnostic, type TraeRawDiagnosticState, type TraeRawGateway, type TraeRawGatewayOptions, type TraeRawResolverResult, type TraeReasoningCapability, type TraeReasoningEffort, type TraeRefreshDevice, type TraeRegion, type TraeRegionGateways, TraeRegionState, type TraeShim, TraeSoloBridge, type TraeSoloClientOptions, TraeSoloRemoteCatalogClient, type TraeSoloRemoteCatalogOptions, TraeSoloUpstreamClient, type TraeStorageCandidate, type TraeStreamEvent, type TraeUpstreamClient, TraeUsageClient, type TraeUsageOptions, type TraeUsagePack, type TraeUsageRouteOptions, type TraeUsageSnapshot, type TraeUsageSummary, type TraeUsageView, type TraeWebActivity, type TraeWebCheckin, type TraeWebCredits, type TraeWebUsage, type TraeWireModel, UnconfiguredTraeUpstreamClient, apply, applyContextBudgets, applyImageSelection, applyReasoningEffort, bridgeTraeSoloStream, buildTraeAgentTaskBody, buildTraeCnHeaders, buildTraeFusionRawChatEnvelope, buildTraeModelDetailRequest, buildTraeRawChatDraft, buildTraeRawChatRuntimeConfig, classifyTraeRawChatFailure, createTraeAdapter, createTraeRawGateway, createTraeShim, decodeRawChatChunk, decodeTraeEvent, decryptTraeStorageValue, deriveCatalog, discoveredCatalog, fallbackModelsFor, hashTraeRawChatArg, identityHeaders, inject, legacyTraeOwnAuthPath, mergeTraeModelSources, name, nextRegionEnabled, nextRegionSlots, normalizeTraeCredential, normalizeTraeVersionCode, parseObservedModelConfig, parseReasoningCapability, parseTraeAuthValue, parseTraeCachedModel, parseTraeModelExtraConfigLogLine, parseTraeRawChatBehaviorConfig, parseTraeRemoteModel, parseTraeStorageDocument, pickTraeStorageIdentity, prepareSoloBody, probeTraeRawChatCapability, rawCapabilityDiagnostic, rawCapabilityFingerprint, readTraeCachedModel, readTraeCliIdentity, readTraeIdentity, refreshTraeCredential, regionEnabled, regionEnabledOf, regionOfCredential, regionOfEdition, regionOfHost, regionOfTraeProvider, regionOfTraeStatusUrl, regionOfUserRegion, regionStateOf, registerTraeUsageRoute, resolveTraeIdentity, resolveTraeRawRuntime, sanitizeCatalog, traeEndpoint, traeInputModalities, traeModelDisplayName, traeOwnAuthPath, traeRawChatExtraInfo, traeStorageCandidates, traeWebUsage, withTraeRegion };
|
package/lib/index.js
CHANGED
|
@@ -2166,7 +2166,9 @@ function decodeTraeEvent(event) {
|
|
|
2166
2166
|
...typeof record["prompt_tokens"] === "number" ? { inputTokens: record["prompt_tokens"] } : {},
|
|
2167
2167
|
...typeof record["completion_tokens"] === "number" ? { outputTokens: record["completion_tokens"] } : {},
|
|
2168
2168
|
...typeof record["total_tokens"] === "number" ? { totalTokens: record["total_tokens"] } : {},
|
|
2169
|
-
...typeof record["reasoning_tokens"] === "number" ? { reasoningTokens: record["reasoning_tokens"] } : {}
|
|
2169
|
+
...typeof record["reasoning_tokens"] === "number" ? { reasoningTokens: record["reasoning_tokens"] } : {},
|
|
2170
|
+
...typeof record["cache_read_input_tokens"] === "number" ? { cacheReadTokens: record["cache_read_input_tokens"] } : {},
|
|
2171
|
+
...typeof record["cache_creation_input_tokens"] === "number" ? { cacheWriteTokens: record["cache_creation_input_tokens"] } : {}
|
|
2170
2172
|
};
|
|
2171
2173
|
if (event.event === "done" || typeof record["finish_reason"] === "string" && record["response"] === void 0) return {
|
|
2172
2174
|
type: "done",
|
|
@@ -2253,12 +2255,20 @@ function bridgeTraeSoloStream(response, model) {
|
|
|
2253
2255
|
delta["tool_calls"] = toolCalls;
|
|
2254
2256
|
}
|
|
2255
2257
|
if (Object.keys(delta).length > 0) controller.enqueue(chunk(delta));
|
|
2256
|
-
} else if (decoded.type === "usage")
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2258
|
+
} else if (decoded.type === "usage") {
|
|
2259
|
+
const cacheRead = decoded.cacheReadTokens;
|
|
2260
|
+
const cacheWrite = decoded.cacheWriteTokens;
|
|
2261
|
+
const details = {
|
|
2262
|
+
...cacheRead === void 0 ? {} : { cached_tokens: cacheRead },
|
|
2263
|
+
...cacheWrite === void 0 ? {} : { cache_write_tokens: cacheWrite }
|
|
2264
|
+
};
|
|
2265
|
+
usage = {
|
|
2266
|
+
...decoded.inputTokens === void 0 ? {} : { prompt_tokens: decoded.inputTokens },
|
|
2267
|
+
...decoded.outputTokens === void 0 ? {} : { completion_tokens: decoded.outputTokens },
|
|
2268
|
+
...decoded.totalTokens === void 0 ? {} : { total_tokens: decoded.totalTokens },
|
|
2269
|
+
...Object.keys(details).length === 0 ? {} : { prompt_tokens_details: details }
|
|
2270
|
+
};
|
|
2271
|
+
} else if (decoded.type === "done") {
|
|
2262
2272
|
upstreamEnded = true;
|
|
2263
2273
|
if (!emittedFinishReason) {
|
|
2264
2274
|
emittedFinishReason = true;
|
|
@@ -2549,11 +2559,16 @@ function decodeRawChatChunk(value) {
|
|
|
2549
2559
|
}
|
|
2550
2560
|
if (typeof record["usage"] === "object" && record["usage"] !== null) {
|
|
2551
2561
|
const usage = record["usage"];
|
|
2562
|
+
const details = typeof usage["prompt_tokens_details"] === "object" && usage["prompt_tokens_details"] !== null ? usage["prompt_tokens_details"] : {};
|
|
2563
|
+
const cacheRead = details["cached_tokens"] ?? usage["prompt_cache_hit_tokens"] ?? usage["cached_tokens"];
|
|
2564
|
+
const cacheWrite = details["cache_write_tokens"];
|
|
2552
2565
|
result.push({
|
|
2553
2566
|
type: "usage",
|
|
2554
2567
|
...typeof usage["prompt_tokens"] === "number" ? { inputTokens: usage["prompt_tokens"] } : {},
|
|
2555
2568
|
...typeof usage["completion_tokens"] === "number" ? { outputTokens: usage["completion_tokens"] } : {},
|
|
2556
|
-
...typeof usage["total_tokens"] === "number" ? { totalTokens: usage["total_tokens"] } : {}
|
|
2569
|
+
...typeof usage["total_tokens"] === "number" ? { totalTokens: usage["total_tokens"] } : {},
|
|
2570
|
+
...typeof cacheRead === "number" ? { cacheReadTokens: cacheRead } : {},
|
|
2571
|
+
...typeof cacheWrite === "number" ? { cacheWriteTokens: cacheWrite } : {}
|
|
2557
2572
|
});
|
|
2558
2573
|
}
|
|
2559
2574
|
return result.length === 0 ? [{
|
|
@@ -3196,6 +3211,74 @@ function regionOfTraeStatusUrl(url) {
|
|
|
3196
3211
|
if (value === null || value === "") return "cn";
|
|
3197
3212
|
return TRAE_REGIONS.includes(value) ? value : void 0;
|
|
3198
3213
|
}
|
|
3214
|
+
/**
|
|
3215
|
+
* Build the next `regions` settings value for the card's save. The write
|
|
3216
|
+
* targets ONLY the signed-in account's region slot; every other region's slot
|
|
3217
|
+
* is carried over untouched, so switching accounts never clobbers the other
|
|
3218
|
+
* region's picks. Tolerates any stored shape (absent, non-object) by starting
|
|
3219
|
+
* from an empty document.
|
|
3220
|
+
*/
|
|
3221
|
+
function nextRegionSlots(regions, region, slot) {
|
|
3222
|
+
return {
|
|
3223
|
+
...typeof regions === "object" && regions !== null && !Array.isArray(regions) ? regions : {},
|
|
3224
|
+
[region]: slot
|
|
3225
|
+
};
|
|
3226
|
+
}
|
|
3227
|
+
/**
|
|
3228
|
+
* Narrow a settings value to the `regions` map. Accepts EITHER the whole
|
|
3229
|
+
* settings section (`{ regions: {...}, ... }`) or the `regions` map itself, and
|
|
3230
|
+
* unwraps the former. This tolerance is deliberate: passing the whole section
|
|
3231
|
+
* where the map was expected was a real shipped bug — the lookup then read
|
|
3232
|
+
* `section['cn']` (absent), so the card's checkbox reported `true` forever and
|
|
3233
|
+
* clicking it appeared to do nothing even though the write succeeded.
|
|
3234
|
+
*/
|
|
3235
|
+
function regionsMapOf(value) {
|
|
3236
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return {};
|
|
3237
|
+
const record = value;
|
|
3238
|
+
const nested = record["regions"];
|
|
3239
|
+
if (typeof nested === "object" && nested !== null && !Array.isArray(nested)) return nested;
|
|
3240
|
+
return record;
|
|
3241
|
+
}
|
|
3242
|
+
/** One region's stored slot as a plain object; any other shape reads as empty. */
|
|
3243
|
+
function regionSlotOf(value, region) {
|
|
3244
|
+
const slot = regionsMapOf(value)[region];
|
|
3245
|
+
return typeof slot === "object" && slot !== null && !Array.isArray(slot) ? slot : {};
|
|
3246
|
+
}
|
|
3247
|
+
/**
|
|
3248
|
+
* Whether one region's provider is switched on. Opt-out semantics: only an
|
|
3249
|
+
* explicit `false` disables it, so a config written before this switch existed
|
|
3250
|
+
* (and the pre-region-split flat fields, which never carry `enabled`) keep both
|
|
3251
|
+
* providers running exactly as before. The Host reads the same rule through
|
|
3252
|
+
* `regionStateOf`, so card and Host can never disagree about a region's state.
|
|
3253
|
+
*
|
|
3254
|
+
* `value` may be the whole settings section or the `regions` map (see
|
|
3255
|
+
* {@link regionsMapOf}).
|
|
3256
|
+
*/
|
|
3257
|
+
function regionEnabledOf(value, region) {
|
|
3258
|
+
return regionSlotOf(value, region)["enabled"] !== false;
|
|
3259
|
+
}
|
|
3260
|
+
/**
|
|
3261
|
+
* Build the next `regions` settings value for a provider on/off toggle. ONLY
|
|
3262
|
+
* the target region's `enabled` flag changes: every other field of that slot
|
|
3263
|
+
* (its directory, selection, image opt-ins, context budgets) and every other
|
|
3264
|
+
* region's slot are carried over verbatim, so switching a provider off never
|
|
3265
|
+
* discards the user's model picks and switching it back on restores them.
|
|
3266
|
+
*
|
|
3267
|
+
* This is deliberately separate from {@link nextRegionSlots}: that helper
|
|
3268
|
+
* writes a whole slot from a signed-in tab's draft, while this one must work
|
|
3269
|
+
* for a region that is signed OUT — which is precisely the region a user wants
|
|
3270
|
+
* to switch off (no international install, no international account).
|
|
3271
|
+
*
|
|
3272
|
+
* `value` may be the whole settings section or the `regions` map; the RETURN
|
|
3273
|
+
* value is always the `regions` map, i.e. exactly what `settingsScope.set(
|
|
3274
|
+
* 'regions', ...)` needs.
|
|
3275
|
+
*/
|
|
3276
|
+
function nextRegionEnabled(value, region, enabled) {
|
|
3277
|
+
return nextRegionSlots(regionsMapOf(value), region, {
|
|
3278
|
+
...regionSlotOf(value, region),
|
|
3279
|
+
enabled
|
|
3280
|
+
});
|
|
3281
|
+
}
|
|
3199
3282
|
//#endregion
|
|
3200
3283
|
//#region src/web-status.ts
|
|
3201
3284
|
/** Redact token-like content before it crosses to the browser. */
|
|
@@ -3249,12 +3332,14 @@ function toCredits(snapshot) {
|
|
|
3249
3332
|
*/
|
|
3250
3333
|
async function traeWebUsage(deps, region) {
|
|
3251
3334
|
const store = deps.store(region);
|
|
3335
|
+
const enabled = deps.regionEnabled(region);
|
|
3252
3336
|
const accounts = await store.accounts();
|
|
3253
3337
|
if ((await store.status()).state !== "signed-in") {
|
|
3254
3338
|
const { failures } = await store.diagnose();
|
|
3255
3339
|
return {
|
|
3256
3340
|
status: "signed-out",
|
|
3257
3341
|
accounts,
|
|
3342
|
+
enabled,
|
|
3258
3343
|
searched: failures.map((failure) => ({
|
|
3259
3344
|
path: failure.path,
|
|
3260
3345
|
edition: failure.edition,
|
|
@@ -3271,6 +3356,7 @@ async function traeWebUsage(deps, region) {
|
|
|
3271
3356
|
return {
|
|
3272
3357
|
status: "signed-out",
|
|
3273
3358
|
accounts,
|
|
3359
|
+
enabled,
|
|
3274
3360
|
message: safeMessage(error)
|
|
3275
3361
|
};
|
|
3276
3362
|
}
|
|
@@ -3279,6 +3365,7 @@ async function traeWebUsage(deps, region) {
|
|
|
3279
3365
|
accountName: credential.accountName ?? credential.userId,
|
|
3280
3366
|
tokenExpiresAtMs: credential.expiresAtMs,
|
|
3281
3367
|
region,
|
|
3368
|
+
enabled,
|
|
3282
3369
|
accounts,
|
|
3283
3370
|
models: deps.displayModels(region).map((model) => ({
|
|
3284
3371
|
...model,
|
|
@@ -3556,6 +3643,17 @@ function regionStateOf(config, region) {
|
|
|
3556
3643
|
...config.contextBudgets === void 0 ? {} : { contextBudgets: config.contextBudgets }
|
|
3557
3644
|
};
|
|
3558
3645
|
}
|
|
3646
|
+
/**
|
|
3647
|
+
* Whether one region's provider is switched on (issue #11). Opt-out semantics:
|
|
3648
|
+
* only an explicit `false` disables it, so every config written before this
|
|
3649
|
+
* switch existed — including the pre-region-split flat fields, which never
|
|
3650
|
+
* carry `enabled` — keeps both providers running exactly as before. The card
|
|
3651
|
+
* reads the same rule through `regionEnabledOf`, so the two halves can never
|
|
3652
|
+
* disagree about a region's state.
|
|
3653
|
+
*/
|
|
3654
|
+
function regionEnabled(config, region) {
|
|
3655
|
+
return regionStateOf(config, region).enabled !== false;
|
|
3656
|
+
}
|
|
3559
3657
|
const modelConfig = z.object({
|
|
3560
3658
|
id: z.string().required(),
|
|
3561
3659
|
name: z.string().required(),
|
|
@@ -3566,6 +3664,7 @@ const modelConfig = z.object({
|
|
|
3566
3664
|
wireFunction: z.string()
|
|
3567
3665
|
});
|
|
3568
3666
|
const regionStateConfig = z.object({
|
|
3667
|
+
enabled: z.boolean().default(true).description("Whether this region's provider is offered to DSH (opt-out; false withdraws it entirely)"),
|
|
3569
3668
|
lastCatalog: z.array(modelConfig).default([]),
|
|
3570
3669
|
enabledModelIds: z.array(z.string()).default([]),
|
|
3571
3670
|
imageModelIds: z.array(z.string()).default([]),
|
|
@@ -3797,9 +3896,42 @@ function apply(ctx, config) {
|
|
|
3797
3896
|
client: (region) => stacks[region].usageClient,
|
|
3798
3897
|
displayModels: (region) => displayModels(current(), region),
|
|
3799
3898
|
enabledModelIds: (region) => regionStateOf(current(), region).enabledModelIds ?? [],
|
|
3899
|
+
regionEnabled: (region) => regionEnabled(current(), region),
|
|
3800
3900
|
discoverModels: (region, signal) => stacks[region].discoverModels(signal),
|
|
3801
3901
|
rawDiagnostic: (region) => stacks[region].rawDiagnostic()
|
|
3802
3902
|
}));
|
|
3903
|
+
/**
|
|
3904
|
+
* Live registration handles per region, filled once the shim is listening.
|
|
3905
|
+
* A disabled region holds ZERO routes while staying registered: DSH allows
|
|
3906
|
+
* `replace([])` for exactly this case ("a settings section that emptied holds
|
|
3907
|
+
* zero routes while staying registered"), which is what makes the on/off
|
|
3908
|
+
* switch reversible without a restart. Withdrawing the adapter route is what
|
|
3909
|
+
* actually removes the region's models from DSH's model picker — hiding the
|
|
3910
|
+
* card tab alone would leave every model selectable.
|
|
3911
|
+
*/
|
|
3912
|
+
const registration = {
|
|
3913
|
+
cn: {},
|
|
3914
|
+
ai: {}
|
|
3915
|
+
};
|
|
3916
|
+
/**
|
|
3917
|
+
* Publish each region's on/off state to the harness (issue #11). Both swaps
|
|
3918
|
+
* are single synchronous sections, so no request can observe a half-applied
|
|
3919
|
+
* state, and `replace` announces itself through `llm/adapters-updated`, which
|
|
3920
|
+
* is what makes third-party consumers (e.g. a vision router deriving
|
|
3921
|
+
* `<provider>-vision` variants) drop the region too. No-op until the shim has
|
|
3922
|
+
* registered; `applySelection` runs again on every card write, so a toggle
|
|
3923
|
+
* lands immediately.
|
|
3924
|
+
*/
|
|
3925
|
+
const syncRegionRegistration = (value) => {
|
|
3926
|
+
for (const region of REGION_KEYS) registration[region].adapter?.replace(regionEnabled(value, region) ? [TRAE_PROVIDERS[region]] : []);
|
|
3927
|
+
registration.cn.directory?.replace(REGION_KEYS.filter((region) => regionEnabled(value, region)).map((region) => ({
|
|
3928
|
+
provider: TRAE_PROVIDERS[region],
|
|
3929
|
+
displayName: TRAE_PROVIDER_DISPLAY_NAMES[region],
|
|
3930
|
+
settingsNs: TRAE_SETTINGS_NS,
|
|
3931
|
+
settingsPath: [],
|
|
3932
|
+
declared: false
|
|
3933
|
+
})));
|
|
3934
|
+
};
|
|
3803
3935
|
/** Push the current config into every region's store selection and catalog. */
|
|
3804
3936
|
const applySelection = (value) => {
|
|
3805
3937
|
for (const region of REGION_KEYS) {
|
|
@@ -3808,6 +3940,7 @@ function apply(ctx, config) {
|
|
|
3808
3940
|
stack.catalog.set(configuredModels(value, region));
|
|
3809
3941
|
stack.invalidateAdapter();
|
|
3810
3942
|
}
|
|
3943
|
+
syncRegionRegistration(value);
|
|
3811
3944
|
};
|
|
3812
3945
|
const sectionHooks = {
|
|
3813
3946
|
setSource(source) {
|
|
@@ -3841,9 +3974,6 @@ function apply(ctx, config) {
|
|
|
3841
3974
|
});
|
|
3842
3975
|
Promise.all(REGION_KEYS.map((region) => stacks[region].shim.ready)).then(async () => {
|
|
3843
3976
|
if (stopped) return;
|
|
3844
|
-
let releaseAdapterCn;
|
|
3845
|
-
let releaseAdapterAi;
|
|
3846
|
-
let releaseDirectory;
|
|
3847
3977
|
try {
|
|
3848
3978
|
for (const region of REGION_KEYS) {
|
|
3849
3979
|
const stack = stacks[region];
|
|
@@ -3857,12 +3987,12 @@ function apply(ctx, config) {
|
|
|
3857
3987
|
stack.invalidateAdapter = () => {
|
|
3858
3988
|
trae.invalidate();
|
|
3859
3989
|
};
|
|
3860
|
-
|
|
3861
|
-
else releaseAdapterAi = ctx.llm.registerAdapter([TRAE_AI_PROVIDER], trae.adapter);
|
|
3990
|
+
registration[region].adapter = region === "cn" ? ctx.llm.registerAdapter([TRAE_PROVIDER], trae.adapter) : ctx.llm.registerAdapter([TRAE_AI_PROVIDER], trae.adapter);
|
|
3862
3991
|
}
|
|
3863
3992
|
ctx.llm.registerModelDiscovery(TRAE_SETTINGS_NS, async (request, signal) => {
|
|
3864
3993
|
const region = regionOfTraeProvider(request.provider ?? "");
|
|
3865
3994
|
if (region === void 0) return [];
|
|
3995
|
+
if (!regionEnabled(current(), region)) return [];
|
|
3866
3996
|
const cancellation = signal ?? request.signal;
|
|
3867
3997
|
return applyImageSelection(await stacks[region].discoverModels(cancellation), imageSet(current(), region)).map((model) => ({
|
|
3868
3998
|
id: model.id,
|
|
@@ -3872,46 +4002,50 @@ function apply(ctx, config) {
|
|
|
3872
4002
|
inputModalities: traeInputModalities(model)
|
|
3873
4003
|
}));
|
|
3874
4004
|
});
|
|
3875
|
-
|
|
4005
|
+
registration.cn.directory = ctx.llm.registerConfigurableProviders(REGION_KEYS.map((region) => ({
|
|
3876
4006
|
provider: TRAE_PROVIDERS[region],
|
|
3877
4007
|
displayName: TRAE_PROVIDER_DISPLAY_NAMES[region],
|
|
3878
4008
|
settingsNs: TRAE_SETTINGS_NS,
|
|
3879
4009
|
settingsPath: [],
|
|
3880
4010
|
declared: false
|
|
3881
4011
|
})));
|
|
4012
|
+
syncRegionRegistration(current());
|
|
3882
4013
|
} finally {
|
|
3883
|
-
if (
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
4014
|
+
if (registration.cn.adapter === void 0 || registration.ai.adapter === void 0 || registration.cn.directory === void 0) {
|
|
4015
|
+
registration.cn.adapter?.();
|
|
4016
|
+
registration.ai.adapter?.();
|
|
4017
|
+
registration.cn.directory?.();
|
|
3887
4018
|
}
|
|
3888
4019
|
}
|
|
3889
4020
|
try {
|
|
3890
4021
|
ctx.effect(() => () => {
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
4022
|
+
registration.cn.adapter?.();
|
|
4023
|
+
registration.ai.adapter?.();
|
|
4024
|
+
registration.cn.directory?.();
|
|
3894
4025
|
});
|
|
3895
4026
|
} catch {
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
4027
|
+
registration.cn.adapter?.();
|
|
4028
|
+
registration.ai.adapter?.();
|
|
4029
|
+
registration.cn.directory?.();
|
|
4030
|
+
}
|
|
4031
|
+
for (const region of REGION_KEYS) {
|
|
4032
|
+
if (!regionEnabled(current(), region)) continue;
|
|
4033
|
+
(async () => {
|
|
4034
|
+
const stack = stacks[region];
|
|
4035
|
+
try {
|
|
4036
|
+
const models = await stack.discoverModels();
|
|
4037
|
+
if (stopped) return;
|
|
4038
|
+
stack.catalog.set(derive(current(), models, region));
|
|
4039
|
+
} catch (error) {
|
|
4040
|
+
ctx.logger.warn(`dsh-connect-trae: live ${region} model directory unavailable; serving the configured catalog`, error);
|
|
4041
|
+
stack.catalog.set(configuredModels(current(), region));
|
|
4042
|
+
}
|
|
4043
|
+
stack.invalidateAdapter();
|
|
4044
|
+
})();
|
|
3899
4045
|
}
|
|
3900
|
-
for (const region of REGION_KEYS) (async () => {
|
|
3901
|
-
const stack = stacks[region];
|
|
3902
|
-
try {
|
|
3903
|
-
const models = await stack.discoverModels();
|
|
3904
|
-
if (stopped) return;
|
|
3905
|
-
stack.catalog.set(derive(current(), models, region));
|
|
3906
|
-
} catch (error) {
|
|
3907
|
-
ctx.logger.warn(`dsh-connect-trae: live ${region} model directory unavailable; serving the configured catalog`, error);
|
|
3908
|
-
stack.catalog.set(configuredModels(current(), region));
|
|
3909
|
-
}
|
|
3910
|
-
stack.invalidateAdapter();
|
|
3911
|
-
})();
|
|
3912
4046
|
}).catch((error) => {
|
|
3913
4047
|
ctx.logger.error("dsh-connect-trae: loopback shim failed; providers not registered", error);
|
|
3914
4048
|
});
|
|
3915
4049
|
}
|
|
3916
4050
|
//#endregion
|
|
3917
|
-
export { Config, FALLBACK_TRAE_MODELS, FALLBACK_TRAE_MODELS_AI, REGION_GATEWAYS, SseDecoder, TRAE_AI_PROVIDER, TRAE_CN_AGENT_TASK_PATH, TRAE_CN_TITLE_PATH, TRAE_MODEL_DETAIL_FUNCTIONS, TRAE_MODEL_DETAIL_PATH, TRAE_PAY_BASE, TRAE_PROVIDER, TRAE_PROVIDERS, TRAE_PROVIDER_DISPLAY_NAMES, TRAE_RAW_CHAT_V1_PATH, TRAE_RAW_CHAT_V2_PATH, TRAE_REASONING_EFFORTS, TRAE_REGIONS, TRAE_REGION_PARAM, TRAE_SETTINGS_NS, TRAE_SOLO_CHAT_PATH, TRAE_SOLO_FUNCTION, TRAE_SOLO_MODELS_PATH, TRAE_SOLO_REMOTE_BASE, TRAE_STREAM_IDLE_TIMEOUT_MS, TRAE_USAGE_PATH, TRAE_VERSION_CODE_FALLBACK, TraeCatalog, TraeCredentialStore, TraeDelegatingUpstreamClient, TraeFallbackUpstreamClient, TraeGatedUpstreamClient, TraeRawCapabilityController, TraeRawCapabilityState, TraeRawChatUpstreamClient, TraeSoloBridge, TraeSoloRemoteCatalogClient, TraeSoloUpstreamClient, TraeUsageClient, UnconfiguredTraeUpstreamClient, apply, applyContextBudgets, applyImageSelection, applyReasoningEffort, bridgeTraeSoloStream, buildTraeAgentTaskBody, buildTraeCnHeaders, buildTraeFusionRawChatEnvelope, buildTraeModelDetailRequest, buildTraeRawChatDraft, buildTraeRawChatRuntimeConfig, classifyTraeRawChatFailure, createTraeAdapter, createTraeRawGateway, createTraeShim, decodeRawChatChunk, decodeTraeEvent, decryptTraeStorageValue, deriveCatalog, discoveredCatalog, fallbackModelsFor, hashTraeRawChatArg, identityHeaders, inject, legacyTraeOwnAuthPath, mergeTraeModelSources, name, normalizeTraeCredential, normalizeTraeVersionCode, parseObservedModelConfig, parseReasoningCapability, parseTraeAuthValue, parseTraeCachedModel, parseTraeModelExtraConfigLogLine, parseTraeRawChatBehaviorConfig, parseTraeRemoteModel, parseTraeStorageDocument, pickTraeStorageIdentity, prepareSoloBody, probeTraeRawChatCapability, rawCapabilityDiagnostic, rawCapabilityFingerprint, readTraeCachedModel, readTraeCliIdentity, readTraeIdentity, refreshTraeCredential, regionOfCredential, regionOfEdition, regionOfHost, regionOfTraeProvider, regionOfTraeStatusUrl, regionOfUserRegion, regionStateOf, registerTraeUsageRoute, resolveTraeIdentity, resolveTraeRawRuntime, sanitizeCatalog, traeEndpoint, traeInputModalities, traeModelDisplayName, traeOwnAuthPath, traeRawChatExtraInfo, traeStorageCandidates, traeWebUsage, withTraeRegion };
|
|
4051
|
+
export { Config, FALLBACK_TRAE_MODELS, FALLBACK_TRAE_MODELS_AI, REGION_GATEWAYS, SseDecoder, TRAE_AI_PROVIDER, TRAE_CN_AGENT_TASK_PATH, TRAE_CN_TITLE_PATH, TRAE_MODEL_DETAIL_FUNCTIONS, TRAE_MODEL_DETAIL_PATH, TRAE_PAY_BASE, TRAE_PROVIDER, TRAE_PROVIDERS, TRAE_PROVIDER_DISPLAY_NAMES, TRAE_RAW_CHAT_V1_PATH, TRAE_RAW_CHAT_V2_PATH, TRAE_REASONING_EFFORTS, TRAE_REGIONS, TRAE_REGION_PARAM, TRAE_SETTINGS_NS, TRAE_SOLO_CHAT_PATH, TRAE_SOLO_FUNCTION, TRAE_SOLO_MODELS_PATH, TRAE_SOLO_REMOTE_BASE, TRAE_STREAM_IDLE_TIMEOUT_MS, TRAE_USAGE_PATH, TRAE_VERSION_CODE_FALLBACK, TraeCatalog, TraeCredentialStore, TraeDelegatingUpstreamClient, TraeFallbackUpstreamClient, TraeGatedUpstreamClient, TraeRawCapabilityController, TraeRawCapabilityState, TraeRawChatUpstreamClient, TraeSoloBridge, TraeSoloRemoteCatalogClient, TraeSoloUpstreamClient, TraeUsageClient, UnconfiguredTraeUpstreamClient, apply, applyContextBudgets, applyImageSelection, applyReasoningEffort, bridgeTraeSoloStream, buildTraeAgentTaskBody, buildTraeCnHeaders, buildTraeFusionRawChatEnvelope, buildTraeModelDetailRequest, buildTraeRawChatDraft, buildTraeRawChatRuntimeConfig, classifyTraeRawChatFailure, createTraeAdapter, createTraeRawGateway, createTraeShim, decodeRawChatChunk, decodeTraeEvent, decryptTraeStorageValue, deriveCatalog, discoveredCatalog, fallbackModelsFor, hashTraeRawChatArg, identityHeaders, inject, legacyTraeOwnAuthPath, mergeTraeModelSources, name, nextRegionEnabled, nextRegionSlots, normalizeTraeCredential, normalizeTraeVersionCode, parseObservedModelConfig, parseReasoningCapability, parseTraeAuthValue, parseTraeCachedModel, parseTraeModelExtraConfigLogLine, parseTraeRawChatBehaviorConfig, parseTraeRemoteModel, parseTraeStorageDocument, pickTraeStorageIdentity, prepareSoloBody, probeTraeRawChatCapability, rawCapabilityDiagnostic, rawCapabilityFingerprint, readTraeCachedModel, readTraeCliIdentity, readTraeIdentity, refreshTraeCredential, regionEnabled, regionEnabledOf, regionOfCredential, regionOfEdition, regionOfHost, regionOfTraeProvider, regionOfTraeStatusUrl, regionOfUserRegion, regionStateOf, registerTraeUsageRoute, resolveTraeIdentity, resolveTraeRawRuntime, sanitizeCatalog, traeEndpoint, traeInputModalities, traeModelDisplayName, traeOwnAuthPath, traeRawChatExtraInfo, traeStorageCandidates, traeWebUsage, withTraeRegion };
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-connect-trae",
|
|
3
3
|
"displayName": "DSH Connect Trae",
|
|
4
4
|
"description": "Connect locally signed-in Trae models to DeepSeek Harness, plus a read-only usage/credits summary.",
|
|
5
|
-
"version": "2.0.
|
|
5
|
+
"version": "2.0.6",
|
|
6
6
|
"author": "dingminhua",
|
|
7
7
|
"copyright": "Copyright (c) 2026 LaoDing",
|
|
8
8
|
"license": "MIT",
|
|
@@ -75,12 +75,12 @@
|
|
|
75
75
|
"@deepseek-ai/cordis": "^4.0.1-rc.1",
|
|
76
76
|
"@deepseek-ai/dsh-atomic-write": ">=0.1.5-0 <0.2.0-0",
|
|
77
77
|
"@deepseek-ai/dsh-attachment": ">=0.1.5-0 <0.2.0-0",
|
|
78
|
+
"@deepseek-ai/dsh-client-ui-primitives": ">=0.1.5-0 <0.2.0-0",
|
|
78
79
|
"@deepseek-ai/dsh-home-paths": ">=0.1.5-0 <0.2.0-0",
|
|
79
80
|
"@deepseek-ai/dsh-host-webserver": ">=0.1.5-0 <0.2.0-0",
|
|
80
81
|
"@deepseek-ai/dsh-llm": ">=0.1.5-0 <0.2.0-0",
|
|
81
82
|
"@deepseek-ai/dsh-llm-pi-ai": ">=0.1.5-0 <0.2.0-0",
|
|
82
83
|
"@deepseek-ai/dsh-settings": ">=0.1.5-0 <0.2.0-0",
|
|
83
|
-
"@deepseek-ai/dsh-client-ui-primitives": ">=0.1.5-0 <0.2.0-0",
|
|
84
84
|
"@deepseek-ai/schemastery": "^3.18.1-rc.1",
|
|
85
85
|
"@earendil-works/pi-ai": ">=0.85.1",
|
|
86
86
|
"react": "^18.2.0"
|
|
@@ -90,10 +90,10 @@
|
|
|
90
90
|
"@deepseek-ai/dsh-atomic-write": "0.1.5-rc.2",
|
|
91
91
|
"@deepseek-ai/dsh-attachment": "0.1.5-rc.2",
|
|
92
92
|
"@deepseek-ai/dsh-client-locale": "0.1.5-rc.2",
|
|
93
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.1.5-rc.2",
|
|
93
94
|
"@deepseek-ai/dsh-client-ui-renderer": "0.1.5-rc.2",
|
|
94
95
|
"@deepseek-ai/dsh-client-ui-settings": "0.1.5-rc.2",
|
|
95
96
|
"@deepseek-ai/dsh-client-ui-settings-plugins": "0.1.5-rc.2",
|
|
96
|
-
"@deepseek-ai/dsh-client-ui-primitives": "0.1.5-rc.2",
|
|
97
97
|
"@deepseek-ai/dsh-client-ui-slots": "0.1.5-rc.2",
|
|
98
98
|
"@deepseek-ai/dsh-home-paths": "0.1.5-rc.2",
|
|
99
99
|
"@deepseek-ai/dsh-host-webserver": "0.1.5-rc.2",
|
|
@@ -102,9 +102,13 @@
|
|
|
102
102
|
"@deepseek-ai/dsh-settings": "0.1.5-rc.2",
|
|
103
103
|
"@deepseek-ai/schemastery": "3.18.2",
|
|
104
104
|
"@earendil-works/pi-ai": "^0.85.1",
|
|
105
|
+
"@testing-library/dom": "^10.4.2",
|
|
106
|
+
"@testing-library/react": "^16.3.3",
|
|
105
107
|
"@types/node": "^22.20.0",
|
|
106
108
|
"@types/react": "~18.3.31",
|
|
109
|
+
"jsdom": "^25.0.1",
|
|
107
110
|
"react": "^18.3.1",
|
|
111
|
+
"react-dom": "18.3.1",
|
|
108
112
|
"tsdown": "^0.22.2",
|
|
109
113
|
"typescript": "^5.9.3",
|
|
110
114
|
"vitest": "^4.1.8"
|