dsh-connect-trae 3.10.1 → 4.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 +72 -0
- package/README.md +0 -1
- package/lib/client.js +148 -184
- package/lib/index.d.ts +52 -81
- package/lib/index.js +117 -155
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,77 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.0.0 (2026-10-19)
|
|
4
|
+
|
|
5
|
+
> 移除 issue #29 的「跟随 Trae 桌面端当前登录」整套机制,连同它派生的两处 UI。
|
|
6
|
+
>
|
|
7
|
+
> **原因:账号池功能无法兼容原需求。** #29 要的是「插件始终跟着 App 当前的登录走」,而账号池要的是
|
|
8
|
+
> 「每个账号都能被长期保留、随时选中、按可用性换号」——池里选中的账号往往正是那个已经不在 App 里
|
|
9
|
+
> 登录的账号,它必须照常可用。「跟随当前登录」与「账号池」是互斥的两条规则,只能是后者。
|
|
10
|
+
>
|
|
11
|
+
> 这是**大版本**:一个配置字段与两处界面被移除。
|
|
12
|
+
|
|
13
|
+
### 移除
|
|
14
|
+
|
|
15
|
+
- **设置卡片里的「跟随 Trae 当前登录的账号」开关**。连带删除 `Config.followAppLogin` 字段、每次设置
|
|
16
|
+
生效时的自动重绑、以及启动时的那次跟随。插件不再有「不点也会自己换绑计费账号」的路径。
|
|
17
|
+
该开关默认关闭,所以从未打开过它的用户没有任何行为变化;存过 `true` 的旧配置里的这个键会被忽略。
|
|
18
|
+
- **账号区上方的「Trae 已登录 X,但本插件绑定的是 Y。改用 Y」提示条**。连带删除 Host 侧
|
|
19
|
+
`TraeCredentialStore.liveLogin()`、usage 文档的 `appLogin` 字段及其读取路径、`TraeWebAppLogin` 类型。
|
|
20
|
+
usage 文档不再携带这个字段。
|
|
21
|
+
- **账号池设置里的「每登录一个账号,就刷新一次」黄色警告框**。账号副本是每次读取就补齐一次的,
|
|
22
|
+
这句「没刷新就换号,上一个账号不会被保存」与实现不符。
|
|
23
|
+
- 顺带清掉只服务于跟随开关的 Host 侧 `writeSettings` 写入通道(`settings.update` + revision 冲突重试),
|
|
24
|
+
它的唯一消费者就是那个开关。
|
|
25
|
+
|
|
26
|
+
### 不变
|
|
27
|
+
|
|
28
|
+
- **没有显式选择账号时,仍然默认使用 App 当前登录的账号**(`preferred()` 对 `source === 'desktop'`
|
|
29
|
+
的凭据优先)。这是本次移除后仍然保留的「跟随」行为,且不需要任何开关。
|
|
30
|
+
- **账号副本机制不变**:本机每个 Trae 登录都会被复制成自己的一份(`~/.dsh/.trae-auth.<region>.json`),
|
|
31
|
+
换号不会顶掉任何账号,之前的账号仍可被重新选回来。
|
|
32
|
+
|
|
33
|
+
### 测试
|
|
34
|
+
|
|
35
|
+
- 删掉随功能退役的用例:`auth.spec.ts` 的 `liveLogin()` 三态、`web-status.spec.ts` 整个
|
|
36
|
+
`app sign-in follow hint (issue #29)` describe、`card-accounts-table` 的提示条两条、`card-pool` 的
|
|
37
|
+
警告框一条。`card-styles` 的遮罩用例改用 `showPointsInMainUi` 开关驱动。
|
|
38
|
+
- 全仓 **895** 例通过,0 skipped;`tsc` 两个工程干净;`build` 通过。
|
|
39
|
+
|
|
40
|
+
## 3.10.2 (2026-10-19)
|
|
41
|
+
|
|
42
|
+
> 设置页的账号列表**先出、积分后补**。打开卡片原本要等**一整份**用量文档组装完才画第一笔:那份文档的
|
|
43
|
+
> 尾巴上挂着「逐账号读上游余额」和「逐账号读签到状态」,每个账号一次网络往返,单次超时 30 秒、没有重试,
|
|
44
|
+
> 上游一慢就层层等待——这正是兄弟插件 dsh-connect-workbuddy 的 issue #77(实测 30–60 秒才看到账号)。
|
|
45
|
+
> Trae 这边同一份文档里 `poolMembersOf` 甚至被调了 **4 次**(`members`、`effectiveMemberAccountIds`、
|
|
46
|
+
> `memberNames`、`otherAccounts` 各一次),签到那条路还要再触发一次,所以同样的机器上比 workbuddy 更慢。
|
|
47
|
+
> 本版把首屏与「测量」拆开:首屏只用本机已存的东西,测量结果随后补上。
|
|
48
|
+
|
|
49
|
+
### 修复
|
|
50
|
+
|
|
51
|
+
- **账号列表不再等上游余额**。`GET /usage?region=…&detail=fast` 返回一份只用本机状态的「首屏文档」:
|
|
52
|
+
`store.current()` 读已存凭据(不做网络刷新)、账号行来自新增的 `localMembers`(就是请求路径本来就在用的
|
|
53
|
+
`localPoolMembers`,零上游调用)、签到状态来自本进程已测过的缓存。**实测 0 次上游请求**,本机 1.3 秒的
|
|
54
|
+
完整文档变成毫秒级首屏。
|
|
55
|
+
- **卡片分两笔画**:先请求 `detail=fast` 上屏,再请求完整文档补齐积分/签到/模型探测结果。首屏文档**永不
|
|
56
|
+
覆盖**已经在屏的完整文档——否则每次轮询都会把积分闪回「—」。
|
|
57
|
+
- **首屏之前不再谎称「未登录」**。旧代码在 `statusByRegion` 里预置了一份 `signed-out` 占位文档并直接渲染,
|
|
58
|
+
于是整段等待时间里已登录的用户看到的是「去登录」引导;现在状态表初始为空,未拿到任何文档时显示新增的
|
|
59
|
+
「正在加载账号…」,真正的 `signed-out` 文档到了才显示登录引导。
|
|
60
|
+
- **完整文档故意保持不变**:`detail` 缺省、或值不是精确的 `fast`(比如大小写写错、拼成 `fastest`)时一律
|
|
61
|
+
退回完整文档——宁可慢一会儿,也不能给出一份更空的卡片。
|
|
62
|
+
- Host 侧配套:`TraePoolDeps` 新增可选 `localMembers` / `cachedCheckinState`,`src/index.ts` 里分别接到
|
|
63
|
+
`localPoolMembers` 与新增的签到状态缓存(`CHECKIN_CACHE_MS = 10 * 60_000`)。老版本 Host 没有这两个
|
|
64
|
+
dep 时,首屏文档**省略** pool 段而不是回头去付那次昂贵读取。
|
|
65
|
+
|
|
66
|
+
### 测试
|
|
67
|
+
|
|
68
|
+
- 新增 `tests/issue77-fast-first-paint.spec.ts`(11 例):首屏文档的上游调用数为 0、用的是 `current()`
|
|
69
|
+
而不是 `resolve()`、缺 dep 时省略 pool 段、`detail` 缺省/拼错一律退回完整文档。
|
|
70
|
+
- 新增 `tests/issue77-card-first-paint.spec.tsx`(3 例):完整文档还在路上时账号行已经上屏、未拿到任何
|
|
71
|
+
文档时显示「正在加载账号」而不是「未登录」引导、迟到的首屏文档不会覆盖已在屏的完整文档。
|
|
72
|
+
- 全仓 **902** 例通过,0 skipped。
|
|
73
|
+
|
|
74
|
+
|
|
3
75
|
## 3.10.1 (2026-10-19)
|
|
4
76
|
|
|
5
77
|
> 「一键签到所有账号」下线。Trae 限制**每台设备每天只能签到一次**:本机任一账号签过之后,这个按钮
|
package/README.md
CHANGED
|
@@ -46,7 +46,6 @@
|
|
|
46
46
|
- **目录与勾选按面板隔离** —— 三个面板各一套模型目录、勾选、图片开关与上下文预算,各自读各自的槽位。名录从**该面板所钉的那条网关**读取——原本钉在企业池的客户端去读消费池名录,会永远发现不了自己池的独占模型。
|
|
47
47
|
- **识图能力跟随 Trae 自身的标注** —— Trae 把某个模型标为 `multimodal` 时,插件直接按多模态对外声明,无需你在卡片上逐一手勾(issue #16)。判据是「**你手动勾选的 ∪ Trae 标注多模态的**」:Trae 明确标 `false` 或未标注的模型保持纯文本,你可以照旧手动为它们打开图片输入。卡片上被自动开启的那一行显示为已勾选且只读,并说明原因。详见下方「图片输入(识图)」。
|
|
48
48
|
- **多账号切换** —— 支持重新读取 Token 列表并选择账号;Token 不写入 DSH 设置。**你的选择会被记住并长期生效**:插件不会因为某个账号额度用尽、或检测到另一个可用账号就自作主张替你切换(换账号意味着换人计费)——唯一的例外是你**亲自打开**的账号池(见下一条),那是一个显式选择,而不是插件的自作主张。所以当绑定的账号额度耗尽时,卡片会**明确告诉你**,并把本机其他还有额度的账号连同其可用积分数列出来,由你一键切换——而不是让面板一直空着、也不自动把你切走。「刷新账号与 Token」每次都会回报结果(找到几个账号、当前仍绑定谁),不会再「点了没反应」。下拉里显示的是产品名(`Trae` / `TRAE SOLO` / 国际版),便于分辨凭据来自哪个安装。
|
|
49
|
-
- **跟随 Trae 当前登录的账号** —— 你在 Trae 桌面端**退出并换登录**后,卡片会检测到「插件绑定的账号」与「Trae 现在登录的账号」不一致,并给出一行提示与**一键切换**(复用账号下拉的同一次写入,不引入第二条切换路径)。想让插件**自动跟随**,可打开设置卡片里的「跟随 Trae 当前登录的账号」开关:此后每次配置生效都会把 `accounts.<region>` 重绑到 App 当前登录的账号。**该开关默认关闭**——已有配置保持原来的严格绑定,自动换绑意味着换人计费,必须由你主动勾选;未勾选时上面那行提示就是你的手动通道。(issue #29)
|
|
50
49
|
- **账号导入 / 导出** —— 不用再为了用第二个账号而把凭据手工写进默认路径、顶掉上一个。在卡片账号区的「导入 / 导出账号」里:**导出**勾选若干账号下载一份 JSON;**导入**选择文件后先看**脱敏预览**(只显示账号标识与「是否有 Token」,不含凭据本身)再勾选导入。文件格式与同族 `dsh-connect-workbuddy` **互通**,两边导出的文件可以互相导入。导入按账号去重(同一账号再导入即覆盖更新),**认不出所属区域或因缺 Token 的记录会被跳过并计数**——国内版与国际版是两条并行计费通道,跨区导入会导致跨区计费,所以这里只认同能**正向识别**的区域(企业版共用 CN 凭据池,不构成第三条导入通道)。导出文件里是**有效 Token,请当作密码保管**。(issue #37)
|
|
51
50
|
- **只读用量与模型管理** —— 国内版查看 Work 积分与通用积分,国际版查看订阅/试用状态;刷新/启用 Trae 模型;只读查询不消耗额度。
|
|
52
51
|
- **积分临近到期会提醒** —— 剩余额度只回答「还有多少」,回答不了「**哪一笔先到期**」;提醒条按到期紧迫程度逐级加重(**7 天 → 24 小时 → 已过期**),写明金额、套餐数和最早到期时间,避免只盯总额时悄悄丢掉最先到期的额度。国内版在用量页与输入框积分面板两处都显示(国际版是订阅制,没有逐包到期数据)。
|
package/lib/client.js
CHANGED
|
@@ -136,6 +136,22 @@ window.__ModuleLoader__.load({
|
|
|
136
136
|
function withTraeTransferAction(path, panel, action) {
|
|
137
137
|
return `${path}?${TRAE_REGION_PARAM}=${panel}&action=${action}`;
|
|
138
138
|
}
|
|
139
|
+
/** Query parameter asking for a thinner first-paint document (issue #77). */
|
|
140
|
+
const TRAE_DETAIL_PARAM = "detail";
|
|
141
|
+
/** The only recognised `detail` value: local state only, no upstream read. */
|
|
142
|
+
const TRAE_DETAIL_FAST = "fast";
|
|
143
|
+
/**
|
|
144
|
+
* Address one panel's usage route asking for the FAST document, e.g.
|
|
145
|
+
* `/plugins/dsh-connect-trae/usage?region=cn&detail=fast`.
|
|
146
|
+
*
|
|
147
|
+
* Kept beside {@link withTraeRegion} for the same reason as
|
|
148
|
+
* {@link withTraeTransferAction}: the region is already a parameter, so a caller
|
|
149
|
+
* appending `&detail=` by hand is one typo away from a request that silently
|
|
150
|
+
* asks for the wrong document.
|
|
151
|
+
*/
|
|
152
|
+
function withTraeRegionFastDetail(path, panel) {
|
|
153
|
+
return `${withTraeRegion(path, panel)}&${TRAE_DETAIL_PARAM}=${TRAE_DETAIL_FAST}`;
|
|
154
|
+
}
|
|
139
155
|
/**
|
|
140
156
|
* Peel ONE `{get(): T}` live reference, the shape DSH 0.1.7 delivers a
|
|
141
157
|
* volatile-marked settings field as (see {@link unwrapVolatileDeep}).
|
|
@@ -191,20 +207,6 @@ window.__ModuleLoader__.load({
|
|
|
191
207
|
return typeof accounts === "object" && accounts !== null ? accounts : {};
|
|
192
208
|
}
|
|
193
209
|
/**
|
|
194
|
-
* Whether the host-side "follow the Trae app's sign-in" switch is on (issue
|
|
195
|
-
* #29). Absent reads as OFF, so an existing configuration keeps the strict
|
|
196
|
-
* bind it shipped with and the switch is an opt-in — the same opt-out
|
|
197
|
-
* semantics as {@link regionEnabledOf}, for the same reason: a config written
|
|
198
|
-
* before this field existed cannot ask for behavior it never declared.
|
|
199
|
-
*
|
|
200
|
-
* Reads the whole settings section (or the bare field); deep-unwraps first
|
|
201
|
-
* because on DSH 0.1.7 a volatile field arrives as a `{get(): T}` live
|
|
202
|
-
* reference, whose `typeof === 'object'` would otherwise pass a naive check.
|
|
203
|
-
*/
|
|
204
|
-
function followAppLoginEnabled(configured) {
|
|
205
|
-
return unwrapVolatileDeep(configured)?.followAppLogin === true;
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
210
|
* Build the next `regions` settings value for the card's save. The write
|
|
209
211
|
* targets ONLY the signed-in account's region slot; every other region's slot
|
|
210
212
|
* is carried over untouched, so switching accounts never clobbers the other
|
|
@@ -465,7 +467,7 @@ window.__ModuleLoader__.load({
|
|
|
465
467
|
.dsm-trae-pref input{flex:none;cursor:pointer;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
|
|
466
468
|
.dsm-trae-pref-check{inline-size:15px;block-size:15px;margin:3px 0 0}
|
|
467
469
|
/* The label and its explanation used to run together as one wrapped blob
|
|
468
|
-
("
|
|
470
|
+
("在输入框工具栏显示积分读数 在输入框工具栏显示可点击读数…"), which is what made
|
|
469
471
|
this block read as a wall of text. They are two lines now: what the switch
|
|
470
472
|
does, then what it means. */
|
|
471
473
|
.dsm-trae-pref-copy{display:flex;flex-direction:column;gap:3px;min-width:0}
|
|
@@ -899,9 +901,6 @@ window.__ModuleLoader__.load({
|
|
|
899
901
|
/* "Every member is unusable" is the one case that IS the pool's fault, so it
|
|
900
902
|
keeps the warn tone the base class sets. */
|
|
901
903
|
.dsm-trae-pool-warning-unusable{border-color:var(--dsw-alias-state-warn-secondary,#fbbf24);background:var(--dsw-alias-state-warn-tertiary,#fef3c7);color:var(--dsw-alias-state-warn-primary,#b45309)}
|
|
902
|
-
/* The sign-in/refresh workflow sits in the settings block: it keeps the warn
|
|
903
|
-
tone (getting it wrong loses an account) but is not a fault of the pool. */
|
|
904
|
-
.dsm-trae-pool-warning-workflow{margin:0 0 2px}
|
|
905
904
|
.dsm-trae-pool-save-row{display:flex;align-items:center;justify-content:space-between;gap:12px;border-top:1px solid var(--dsw-alias-border-l2,#36373b);padding-top:12px}
|
|
906
905
|
.dsm-trae-pool-save-buttons{display:flex;align-items:center;justify-content:flex-end;gap:8px}
|
|
907
906
|
.dsm-trae-pool-save-row .dsm-btn{padding:5px 14px;font-size:13px;line-height:1.5}
|
|
@@ -1922,17 +1921,6 @@ window.__ModuleLoader__.load({
|
|
|
1922
1921
|
className: "dsm-trae-pool-tools dsm-trae-pool-tools-dashed",
|
|
1923
1922
|
children: tools
|
|
1924
1923
|
}),
|
|
1925
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1926
|
-
className: "dsm-trae-pool-warning dsm-trae-pool-warning-workflow",
|
|
1927
|
-
role: "note",
|
|
1928
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1929
|
-
className: "dsm-trae-pool-warning-title",
|
|
1930
|
-
children: t("row.poolMirrorWorkflowTitle")
|
|
1931
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1932
|
-
className: "dsm-trae-pool-warning-hint",
|
|
1933
|
-
children: t("row.poolMirrorWorkflowHint")
|
|
1934
|
-
})]
|
|
1935
|
-
}),
|
|
1936
1924
|
pool.copies === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1937
1925
|
className: "dsm-trae-pool-note dsm-trae-pool-copyfile",
|
|
1938
1926
|
role: "note",
|
|
@@ -3037,21 +3025,18 @@ window.__ModuleLoader__.load({
|
|
|
3037
3025
|
const [open, setOpen] = (0, react.useState)(view === "page");
|
|
3038
3026
|
/** The region whose tab is on screen; each tab is its own provider stack. */
|
|
3039
3027
|
const [activeRegion, setActiveRegion] = (0, react.useState)("cn");
|
|
3040
|
-
/**
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
accounts: []
|
|
3053
|
-
}
|
|
3054
|
-
});
|
|
3028
|
+
/**
|
|
3029
|
+
* Last-known usage per region, so tab dots survive tab switches.
|
|
3030
|
+
*
|
|
3031
|
+
* A region is ABSENT until something has been read for it (issue #77). It used
|
|
3032
|
+
* to be seeded with a signed-out document, which meant the whole wait for the
|
|
3033
|
+
* first paint rendered as "not signed in — sign in again / scan for accounts"
|
|
3034
|
+
* to a user who was perfectly signed in. The difference between "nothing known
|
|
3035
|
+
* yet" and "known to be signed out" is the difference between a loading line
|
|
3036
|
+
* and a wrong instruction, so the map starts empty and the render below says
|
|
3037
|
+
* which one it is.
|
|
3038
|
+
*/
|
|
3039
|
+
const [statusByRegion, setStatusByRegion] = (0, react.useState)({});
|
|
3055
3040
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
3056
3041
|
/**
|
|
3057
3042
|
* Availability verdicts from the one-click test, per region, keyed by model
|
|
@@ -3166,10 +3151,7 @@ window.__ModuleLoader__.load({
|
|
|
3166
3151
|
* local state: a write made elsewhere (the DSH settings form, another window)
|
|
3167
3152
|
* then shows up here instead of being shadowed by a stale copy.
|
|
3168
3153
|
*/
|
|
3169
|
-
const committedSettings = {
|
|
3170
|
-
followAppLogin: followAppLoginEnabled(settingsScope?.getSnapshot().value),
|
|
3171
|
-
showPointsInMainUi: unwrapVolatileDeep(settingsScope?.getSnapshot().value)?.showPointsInMainUi === true
|
|
3172
|
-
};
|
|
3154
|
+
const committedSettings = { showPointsInMainUi: unwrapVolatileDeep(settingsScope?.getSnapshot().value)?.showPointsInMainUi === true };
|
|
3173
3155
|
/**
|
|
3174
3156
|
* Ids for the block's label/hint wiring. `useId`, not constants: DSH can mount
|
|
3175
3157
|
* this card twice at once (the settings ROW and the expanded PAGE), and two
|
|
@@ -3181,13 +3163,8 @@ window.__ModuleLoader__.load({
|
|
|
3181
3163
|
* over it. Reading BOTH sources here is what lets the write above stay simple —
|
|
3182
3164
|
* it never has to write into component state that a Host refresh would fight.
|
|
3183
3165
|
*/
|
|
3184
|
-
const settingsEdits = {
|
|
3185
|
-
followAppLogin: optimisticSettings.current.followAppLogin ?? committedSettings.followAppLogin,
|
|
3186
|
-
showPointsInMainUi: optimisticSettings.current.showPointsInMainUi ?? committedSettings.showPointsInMainUi
|
|
3187
|
-
};
|
|
3166
|
+
const settingsEdits = { showPointsInMainUi: optimisticSettings.current.showPointsInMainUi ?? committedSettings.showPointsInMainUi };
|
|
3188
3167
|
const settingsIds = (0, react.useId)();
|
|
3189
|
-
const followLabelId = `${settingsIds}-follow`;
|
|
3190
|
-
const followHintId = `${settingsIds}-follow-hint`;
|
|
3191
3168
|
const creditsLabelId = `${settingsIds}-credits`;
|
|
3192
3169
|
const creditsHintId = `${settingsIds}-credits-hint`;
|
|
3193
3170
|
/**
|
|
@@ -3213,7 +3190,7 @@ window.__ModuleLoader__.load({
|
|
|
3213
3190
|
};
|
|
3214
3191
|
setSettingsRevision((revision) => revision + 1);
|
|
3215
3192
|
try {
|
|
3216
|
-
await writeSettingsField(settingsScope, field, value, (readBack) =>
|
|
3193
|
+
await writeSettingsField(settingsScope, field, value, (readBack) => unwrapVolatileDeep(readBack)?.showPointsInMainUi === value);
|
|
3217
3194
|
} catch (error) {
|
|
3218
3195
|
if (mounted.current) setWriteError(error instanceof Error ? error.message : t("row.requestFailed"));
|
|
3219
3196
|
} finally {
|
|
@@ -3324,10 +3301,54 @@ window.__ModuleLoader__.load({
|
|
|
3324
3301
|
return;
|
|
3325
3302
|
}
|
|
3326
3303
|
}, [t]);
|
|
3304
|
+
/**
|
|
3305
|
+
* Paint the fast document (issue #77).
|
|
3306
|
+
*
|
|
3307
|
+
* The full document above cannot answer before the Host has read every member's
|
|
3308
|
+
* balance — one upstream round-trip per account, and this panel's pool assembly
|
|
3309
|
+
* happens to walk the membership more than once. That is the wait the issue
|
|
3310
|
+
* reports: tens of seconds in which the card used to show nothing at all.
|
|
3311
|
+
*
|
|
3312
|
+
* The fast document is built from local state only (no upstream read at all), so
|
|
3313
|
+
* it answers in milliseconds and carries everything needed to DRAW: the bound
|
|
3314
|
+
* account, its expiry, the model list and the pool rows. The live figures it
|
|
3315
|
+
* omits (credits, check-in column) are exactly the values that need the round
|
|
3316
|
+
* trip, and the card already renders their absence as `—` / unknown.
|
|
3317
|
+
*
|
|
3318
|
+
* Deliberately separate from {@link refreshUsage} rather than a parameter of it:
|
|
3319
|
+
* a first paint never writes an error state (a slow upstream is not the user's
|
|
3320
|
+
* problem until the full document says so), and it must never downgrade a region
|
|
3321
|
+
* that has already been painted in full.
|
|
3322
|
+
*/
|
|
3323
|
+
const refreshUsageFast = (0, react.useCallback)(async (region, signal) => {
|
|
3324
|
+
try {
|
|
3325
|
+
const response = await fetch(withTraeRegionFastDetail(TRAE_USAGE_PATH, region), {
|
|
3326
|
+
headers: { accept: "application/json" },
|
|
3327
|
+
credentials: "same-origin",
|
|
3328
|
+
...signal === void 0 ? {} : { signal }
|
|
3329
|
+
});
|
|
3330
|
+
if (!response.ok) return;
|
|
3331
|
+
const usage = await response.json().catch(() => void 0);
|
|
3332
|
+
if (usage === void 0) return;
|
|
3333
|
+
if (!mounted.current || signal?.aborted === true) return;
|
|
3334
|
+
setStatusByRegion((prev) => {
|
|
3335
|
+
const previous = prev[region];
|
|
3336
|
+
if (previous !== void 0 && previous.status === "signed-in") return prev;
|
|
3337
|
+
return {
|
|
3338
|
+
...prev,
|
|
3339
|
+
[region]: usage
|
|
3340
|
+
};
|
|
3341
|
+
});
|
|
3342
|
+
} catch {}
|
|
3343
|
+
}, []);
|
|
3327
3344
|
(0, react.useEffect)(() => {
|
|
3328
3345
|
if (!open || !activeRegionOn) return;
|
|
3329
3346
|
const controller = new AbortController();
|
|
3330
|
-
|
|
3347
|
+
(async () => {
|
|
3348
|
+
await refreshUsageFast(activeRegion, controller.signal);
|
|
3349
|
+
if (controller.signal.aborted) return;
|
|
3350
|
+
refreshUsage(activeRegion, controller.signal);
|
|
3351
|
+
})();
|
|
3331
3352
|
return () => {
|
|
3332
3353
|
controller.abort();
|
|
3333
3354
|
};
|
|
@@ -3335,9 +3356,19 @@ window.__ModuleLoader__.load({
|
|
|
3335
3356
|
open,
|
|
3336
3357
|
activeRegion,
|
|
3337
3358
|
activeRegionOn,
|
|
3338
|
-
refreshUsage
|
|
3359
|
+
refreshUsage,
|
|
3360
|
+
refreshUsageFast
|
|
3339
3361
|
]);
|
|
3340
|
-
|
|
3362
|
+
/**
|
|
3363
|
+
* The document actually on screen for this region, and whether one has arrived.
|
|
3364
|
+
*
|
|
3365
|
+
* `loaded === undefined` is the only state that may render the loading line: a
|
|
3366
|
+
* signed-out document is a real answer (the user must sign in), never a stand-in
|
|
3367
|
+
* for "still fetching".
|
|
3368
|
+
*/
|
|
3369
|
+
const loaded = statusByRegion[activeRegion];
|
|
3370
|
+
const loading = loaded === void 0;
|
|
3371
|
+
const status = loaded ?? {
|
|
3341
3372
|
status: "signed-out",
|
|
3342
3373
|
accounts: []
|
|
3343
3374
|
};
|
|
@@ -3442,21 +3473,6 @@ window.__ModuleLoader__.load({
|
|
|
3442
3473
|
}
|
|
3443
3474
|
};
|
|
3444
3475
|
/**
|
|
3445
|
-
* Follow the Trae app's current sign-in from the hint row (issue #29).
|
|
3446
|
-
*
|
|
3447
|
-
* One click, and it goes through the SAME `switchAccount` write the account
|
|
3448
|
-
* dropdown uses — so the card has exactly one writer for `accounts`, the Host
|
|
3449
|
-
* sees the change through `loader/volatile-update` like every other card
|
|
3450
|
-
* write, and there is no second settings path to keep in sync. The app's
|
|
3451
|
-
* sign-in is `status.appLogin`, which the Host only reports when it differs
|
|
3452
|
-
* from the bound account, so a click here always moves the binding to what
|
|
3453
|
-
* the app is actually logged into.
|
|
3454
|
-
*/
|
|
3455
|
-
const followAppLogin = async () => {
|
|
3456
|
-
if (status.status !== "signed-in" || status.appLogin === void 0) return;
|
|
3457
|
-
await switchAccount(status.appLogin.id);
|
|
3458
|
-
};
|
|
3459
|
-
/**
|
|
3460
3476
|
* Load one account row's per-account state (check-in / balances /
|
|
3461
3477
|
* subscription) and cache it for the row. Called when the table renders and
|
|
3462
3478
|
* again after a claim so the reward/refusal lands on the row that owns it.
|
|
@@ -4023,7 +4039,7 @@ window.__ModuleLoader__.load({
|
|
|
4023
4039
|
children: view.label
|
|
4024
4040
|
});
|
|
4025
4041
|
};
|
|
4026
|
-
const label = status.status === "signed-in" ? t("row.signedIn", { accountName: status.accountName }) : status.status === "error" ? t("row.requestFailed") : t("row.signedOut");
|
|
4042
|
+
const label = loading ? t("row.accountsLoading") : status.status === "signed-in" ? t("row.signedIn", { accountName: status.accountName }) : status.status === "error" ? t("row.requestFailed") : t("row.signedOut");
|
|
4027
4043
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
4028
4044
|
className: `dsm-plugin-card${open ? " dsm-plugin-card-open" : ""}`,
|
|
4029
4045
|
children: [
|
|
@@ -4118,90 +4134,62 @@ window.__ModuleLoader__.load({
|
|
|
4118
4134
|
}),
|
|
4119
4135
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4120
4136
|
className: "dsm-trae-prefs",
|
|
4121
|
-
children: [
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
children: t("row.followAppLoginHint")
|
|
4144
|
-
})]
|
|
4137
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4138
|
+
className: "dsm-trae-pref",
|
|
4139
|
+
title: t(isCnPool ? "row.showPointsInMainUiHint" : "row.showPointsInMainUiHintAi"),
|
|
4140
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4141
|
+
type: "checkbox",
|
|
4142
|
+
className: "dsm-trae-pref-check",
|
|
4143
|
+
checked: settingsEdits.showPointsInMainUi,
|
|
4144
|
+
disabled: settingsScope?.getSnapshot().writable !== true || savingSettings,
|
|
4145
|
+
"aria-describedby": creditsHintId,
|
|
4146
|
+
onChange: (event) => {
|
|
4147
|
+
writePreference("showPointsInMainUi", event.currentTarget.checked);
|
|
4148
|
+
}
|
|
4149
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4150
|
+
className: "dsm-trae-pref-copy",
|
|
4151
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4152
|
+
className: "dsm-trae-pref-label",
|
|
4153
|
+
id: creditsLabelId,
|
|
4154
|
+
children: t("row.showPointsInMainUi")
|
|
4155
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4156
|
+
className: "dsm-trae-pref-hint",
|
|
4157
|
+
id: creditsHintId,
|
|
4158
|
+
children: t(isCnPool ? "row.showPointsInMainUiHint" : "row.showPointsInMainUiHintAi")
|
|
4145
4159
|
})]
|
|
4146
|
-
})
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
disabled: settingsScope?.getSnapshot().writable !== true || savingSettings,
|
|
4155
|
-
"aria-describedby": creditsHintId,
|
|
4156
|
-
onChange: (event) => {
|
|
4157
|
-
writePreference("showPointsInMainUi", event.currentTarget.checked);
|
|
4158
|
-
}
|
|
4159
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4160
|
-
className: "dsm-trae-pref-copy",
|
|
4160
|
+
})]
|
|
4161
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
4162
|
+
className: "dsm-trae-pref dsm-trae-credit-position-pref",
|
|
4163
|
+
title: t("row.creditPositionHint"),
|
|
4164
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4165
|
+
className: "dsm-trae-pref-copy",
|
|
4166
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4167
|
+
className: "dsm-trae-pref-lead",
|
|
4161
4168
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4162
4169
|
className: "dsm-trae-pref-label",
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
className: "dsm-trae-pref-lead",
|
|
4179
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4180
|
-
className: "dsm-trae-pref-label",
|
|
4181
|
-
children: t("row.creditPositionLabel")
|
|
4182
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
4183
|
-
className: "dsm-trae-credit-position",
|
|
4184
|
-
value: activeRegionOn ? creditPositionOn(activeRegion) : "composer",
|
|
4185
|
-
disabled: !activeRegionOn || settingsScope?.getSnapshot().writable !== true || savingSettings,
|
|
4186
|
-
"aria-label": t("row.creditPositionAria", { region: activeRegion === "cn" ? t("row.tabCn") : activeRegion === "enterprise" ? t("row.tabEnterprise") : t("row.tabAi") }),
|
|
4187
|
-
onChange: (event) => {
|
|
4188
|
-
changeCreditPosition(activeRegion, event.currentTarget.value === "stats" ? "stats" : "composer");
|
|
4189
|
-
},
|
|
4190
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4191
|
-
value: "composer",
|
|
4192
|
-
children: t("row.creditPositionComposer")
|
|
4193
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4194
|
-
value: "stats",
|
|
4195
|
-
children: t("row.creditPositionStats")
|
|
4196
|
-
})]
|
|
4170
|
+
children: t("row.creditPositionLabel")
|
|
4171
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
4172
|
+
className: "dsm-trae-credit-position",
|
|
4173
|
+
value: activeRegionOn ? creditPositionOn(activeRegion) : "composer",
|
|
4174
|
+
disabled: !activeRegionOn || settingsScope?.getSnapshot().writable !== true || savingSettings,
|
|
4175
|
+
"aria-label": t("row.creditPositionAria", { region: activeRegion === "cn" ? t("row.tabCn") : activeRegion === "enterprise" ? t("row.tabEnterprise") : t("row.tabAi") }),
|
|
4176
|
+
onChange: (event) => {
|
|
4177
|
+
changeCreditPosition(activeRegion, event.currentTarget.value === "stats" ? "stats" : "composer");
|
|
4178
|
+
},
|
|
4179
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4180
|
+
value: "composer",
|
|
4181
|
+
children: t("row.creditPositionComposer")
|
|
4182
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4183
|
+
value: "stats",
|
|
4184
|
+
children: t("row.creditPositionStats")
|
|
4197
4185
|
})]
|
|
4198
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4199
|
-
className: "dsm-trae-pref-hint",
|
|
4200
|
-
children: t("row.creditPositionHint")
|
|
4201
4186
|
})]
|
|
4202
|
-
})
|
|
4187
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4188
|
+
className: "dsm-trae-pref-hint",
|
|
4189
|
+
children: t("row.creditPositionHint")
|
|
4190
|
+
})]
|
|
4203
4191
|
})
|
|
4204
|
-
]
|
|
4192
|
+
})]
|
|
4205
4193
|
}),
|
|
4206
4194
|
status.status === "signed-in" && status.pool !== void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4207
4195
|
className: "dsm-trae-accounts",
|
|
@@ -4376,25 +4364,6 @@ window.__ModuleLoader__.load({
|
|
|
4376
4364
|
formatNumber,
|
|
4377
4365
|
formatDateTime
|
|
4378
4366
|
}) : null,
|
|
4379
|
-
status.status === "signed-in" && status.appLogin !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4380
|
-
className: "dsm-trae-usage-alternative",
|
|
4381
|
-
role: "status",
|
|
4382
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4383
|
-
className: "dsm-trae-usage-alternative-text",
|
|
4384
|
-
children: t("row.appLoginMismatch", {
|
|
4385
|
-
account: status.appLogin.accountName,
|
|
4386
|
-
bound: status.accountName
|
|
4387
|
-
})
|
|
4388
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4389
|
-
type: "button",
|
|
4390
|
-
className: "dsm-trae-usage-switch-button",
|
|
4391
|
-
disabled: switchingAccount || settingsScope?.getSnapshot()?.writable !== true,
|
|
4392
|
-
onClick: () => {
|
|
4393
|
-
followAppLogin();
|
|
4394
|
-
},
|
|
4395
|
-
children: t("row.appLoginFollow", { account: status.appLogin.accountName })
|
|
4396
|
-
})]
|
|
4397
|
-
}) : null,
|
|
4398
4367
|
rescanNote === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4399
4368
|
className: "dsm-trae-usage-note",
|
|
4400
4369
|
role: "status",
|
|
@@ -4658,7 +4627,12 @@ window.__ModuleLoader__.load({
|
|
|
4658
4627
|
]
|
|
4659
4628
|
})
|
|
4660
4629
|
] }) : null,
|
|
4661
|
-
status.status === "signed-out" ? /* @__PURE__ */ (0, react_jsx_runtime.
|
|
4630
|
+
status.status === "signed-out" && loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4631
|
+
className: "dsm-trae-usage-text",
|
|
4632
|
+
role: "status",
|
|
4633
|
+
children: t("row.accountsLoading")
|
|
4634
|
+
}) }) : null,
|
|
4635
|
+
status.status === "signed-out" && !loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4662
4636
|
className: "dsm-trae-usage-text",
|
|
4663
4637
|
children: status.message ?? t("row.signedOutHint")
|
|
4664
4638
|
}), status.searched && status.searched.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
@@ -5472,6 +5446,7 @@ window.__ModuleLoader__.load({
|
|
|
5472
5446
|
"row.transferNoToken": "no token, will be skipped",
|
|
5473
5447
|
"row.accountsRescan": "Detect accounts again",
|
|
5474
5448
|
"row.accountsScanning": "Detecting…",
|
|
5449
|
+
"row.accountsLoading": "Loading your accounts…",
|
|
5475
5450
|
"row.accountsNone": "This region reports no usable account.",
|
|
5476
5451
|
"row.accountCurrentBadge": "in use",
|
|
5477
5452
|
"row.accountSwitch": "Switch",
|
|
@@ -5530,10 +5505,6 @@ window.__ModuleLoader__.load({
|
|
|
5530
5505
|
"row.expiryWarningWeek": "{total} credit lapses within 7 days ({packs} packs)",
|
|
5531
5506
|
"row.expiryWarningLapsed": "{total} credit has already lapsed but is still counted in the balance ({packs} packs)",
|
|
5532
5507
|
"row.expiryEarliest": "Earliest: {time}",
|
|
5533
|
-
"row.appLoginMismatch": "Trae is signed in to {account}, but this plugin is bound to {bound}.",
|
|
5534
|
-
"row.appLoginFollow": "Use {account}",
|
|
5535
|
-
"row.followAppLogin": "Follow the account Trae is signed in to",
|
|
5536
|
-
"row.followAppLoginHint": "Rebind automatically when you switch accounts in the Trae desktop app. Off by default, so the current binding is kept.",
|
|
5537
5508
|
"row.settingsDirty": "Unsaved changes",
|
|
5538
5509
|
"row.modelsTestEmpty": "Enable at least one model first.",
|
|
5539
5510
|
"row.modelsTestError": "Test failed: {message}",
|
|
@@ -5581,8 +5552,6 @@ window.__ModuleLoader__.load({
|
|
|
5581
5552
|
"row.poolCreditExpiry": "expires {when}",
|
|
5582
5553
|
"row.poolProbeMeasured": "measured {when}",
|
|
5583
5554
|
"row.poolProbeAt": "tested {when}",
|
|
5584
|
-
"row.poolMirrorWorkflowTitle": "Sign in one account, then refresh",
|
|
5585
|
-
"row.poolMirrorWorkflowHint": "The Trae app holds only one sign-in. After signing in to an account, refresh this card (or click “Detect accounts again”) so the plugin saves that login as its own copy. Switch accounts without refreshing and the previous login is not saved — the new one overwrites it in Trae.",
|
|
5586
5555
|
"row.poolSettingsNote": "Preferences below take effect when you save; test results are written by the plugin and are not affected by that button.",
|
|
5587
5556
|
"row.poolSelectedCount": "Selected {count} / {total}",
|
|
5588
5557
|
"row.poolNoCopyTag": "no copy here",
|
|
@@ -5793,6 +5762,7 @@ window.__ModuleLoader__.load({
|
|
|
5793
5762
|
"row.transferNoToken": "无 Token,将被跳过",
|
|
5794
5763
|
"row.accountsRescan": "重新检测账号",
|
|
5795
5764
|
"row.accountsScanning": "正在检测…",
|
|
5765
|
+
"row.accountsLoading": "正在加载账号…",
|
|
5796
5766
|
"row.accountsNone": "该区域没有可列出的账号。",
|
|
5797
5767
|
"row.accountCurrentBadge": "当前使用",
|
|
5798
5768
|
"row.accountSwitch": "切换",
|
|
@@ -5851,10 +5821,6 @@ window.__ModuleLoader__.load({
|
|
|
5851
5821
|
"row.expiryWarningWeek": "有 {total} 积分将在 7 天内过期({packs} 个套餐)",
|
|
5852
5822
|
"row.expiryWarningLapsed": "有 {total} 积分已过期但仍计入余额({packs} 个套餐)",
|
|
5853
5823
|
"row.expiryEarliest": "最早到期:{time}",
|
|
5854
|
-
"row.appLoginMismatch": "Trae 已登录 {account},但本插件绑定的是 {bound}。",
|
|
5855
|
-
"row.appLoginFollow": "改用 {account}",
|
|
5856
|
-
"row.followAppLogin": "跟随 Trae 当前登录的账号",
|
|
5857
|
-
"row.followAppLoginHint": "在 Trae 桌面端切换登录后自动换绑到新账号。默认关闭,已有绑定保持不变。",
|
|
5858
5824
|
"row.settingsDirty": "有未保存的修改",
|
|
5859
5825
|
"row.modelsTestEmpty": "请先启用至少一个模型。",
|
|
5860
5826
|
"row.modelsTestError": "测试失败:{message}",
|
|
@@ -5902,8 +5868,6 @@ window.__ModuleLoader__.load({
|
|
|
5902
5868
|
"row.poolCreditExpiry": "{when} 过期",
|
|
5903
5869
|
"row.poolProbeMeasured": "测得 {when}",
|
|
5904
5870
|
"row.poolProbeAt": "测试于 {when}",
|
|
5905
|
-
"row.poolMirrorWorkflowTitle": "每登录一个账号,就刷新一次",
|
|
5906
|
-
"row.poolMirrorWorkflowHint": "Trae 应用只有一个登录位:每登录一个账号,就回到这里刷新一次(打开卡片或点「重新检测账号」),插件会把这次登录保存成一份自己的副本。没刷新就换号,上一个账号不会被保存——它会被 Trae 里的新登录覆盖。",
|
|
5907
5871
|
"row.poolSettingsNote": "下面是偏好设置:点保存才生效;测试结果由插件自动写入,不受这个保存按钮影响。",
|
|
5908
5872
|
"row.poolSelectedCount": "已选 {count} / {total}",
|
|
5909
5873
|
"row.poolNoCopyTag": "本机无副本",
|
package/lib/index.d.ts
CHANGED
|
@@ -833,24 +833,6 @@ interface TraeWebAccount {
|
|
|
833
833
|
refreshExpiresAtMs?: number;
|
|
834
834
|
selected: boolean;
|
|
835
835
|
}
|
|
836
|
-
/**
|
|
837
|
-
* The account the Trae desktop app is signed in to right now, when it differs
|
|
838
|
-
* from the one this plugin is bound to (issue #29).
|
|
839
|
-
*
|
|
840
|
-
* Reported on the usage document so the card can offer one-click follow without
|
|
841
|
-
* an extra round trip; also written back on a POST to the accounts refresh
|
|
842
|
-
* route when the client confirms it saw the hint. Absent whenever the two
|
|
843
|
-
* agree or the app is not readable — a healthy document carries no extra
|
|
844
|
-
* fields and the plugin never bills from this value directly: the FOLLOW is a
|
|
845
|
-
* settings write of `accounts.<region>`, made by the card or by the host's
|
|
846
|
-
* follow switch, never a silent rebind here.
|
|
847
|
-
*/
|
|
848
|
-
interface TraeWebAppLogin {
|
|
849
|
-
id: string;
|
|
850
|
-
accountName: string;
|
|
851
|
-
edition: 'cn' | 'sg' | 'solo' | 'solo-sg';
|
|
852
|
-
region: TraeRegion;
|
|
853
|
-
}
|
|
854
836
|
/**
|
|
855
837
|
* The per-account state shown inside an account row of the card's table.
|
|
856
838
|
*
|
|
@@ -931,18 +913,6 @@ declare function unwrapVolatileDeep<T>(value: T): T;
|
|
|
931
913
|
* function into the settings document.
|
|
932
914
|
*/
|
|
933
915
|
declare function configuredAccountsOf(configured: unknown): Record<string, string>;
|
|
934
|
-
/**
|
|
935
|
-
* Whether the host-side "follow the Trae app's sign-in" switch is on (issue
|
|
936
|
-
* #29). Absent reads as OFF, so an existing configuration keeps the strict
|
|
937
|
-
* bind it shipped with and the switch is an opt-in — the same opt-out
|
|
938
|
-
* semantics as {@link regionEnabledOf}, for the same reason: a config written
|
|
939
|
-
* before this field existed cannot ask for behavior it never declared.
|
|
940
|
-
*
|
|
941
|
-
* Reads the whole settings section (or the bare field); deep-unwraps first
|
|
942
|
-
* because on DSH 0.1.7 a volatile field arrives as a `{get(): T}` live
|
|
943
|
-
* reference, whose `typeof === 'object'` would otherwise pass a naive check.
|
|
944
|
-
*/
|
|
945
|
-
declare function followAppLoginEnabled(configured: unknown): boolean;
|
|
946
916
|
/**
|
|
947
917
|
* Build the next `regions` settings value for the card's save. The write
|
|
948
918
|
* targets ONLY the signed-in account's region slot; every other region's slot
|
|
@@ -1094,15 +1064,6 @@ type TraeWebUsage = {
|
|
|
1094
1064
|
* other case so a healthy account never pays for the extra read.
|
|
1095
1065
|
*/
|
|
1096
1066
|
alternatives?: readonly TraeWebCreditAlternative[];
|
|
1097
|
-
/**
|
|
1098
|
-
* Present only when the Trae app's CURRENT sign-in differs from the
|
|
1099
|
-
* account this plugin is bound to (issue #29): the card then offers a
|
|
1100
|
-
* one-click follow instead of making the user hunt the account dropdown
|
|
1101
|
-
* after every app-side sign-out. Absent in every other case — a healthy
|
|
1102
|
-
* document costs no extra read beyond the accounts scan the document
|
|
1103
|
-
* already runs.
|
|
1104
|
-
*/
|
|
1105
|
-
appLogin?: TraeWebAppLogin;
|
|
1106
1067
|
/**
|
|
1107
1068
|
* Daily check-in state (CN only). Absent on the international region,
|
|
1108
1069
|
* whose check-in surface does not exist — probed 2026-09-24: the
|
|
@@ -1759,18 +1720,6 @@ declare class TraeCredentialStore {
|
|
|
1759
1720
|
* the plugin performed is never discarded by this preference.
|
|
1760
1721
|
*/
|
|
1761
1722
|
private preferred;
|
|
1762
|
-
/**
|
|
1763
|
-
* The account the Trae app is signed in to right now, when that differs from
|
|
1764
|
-
* the account this store is bound to (issue #29).
|
|
1765
|
-
*
|
|
1766
|
-
* This is a READ, not a rebind: `current()` keeps resolving the bound
|
|
1767
|
-
* account verbatim so billing never moves without a decision — the store
|
|
1768
|
-
* only reports the mismatch, and the card (hint row) or the host (follow
|
|
1769
|
-
* switch) turns it into a settings write the user can see and undo. Absent
|
|
1770
|
-
* when nothing is live, when the live account IS the bound one, or when the
|
|
1771
|
-
* binding itself already fell through to the live account.
|
|
1772
|
-
*/
|
|
1773
|
-
liveLogin(): Promise<TraeAccountChoice | undefined>;
|
|
1774
1723
|
/**
|
|
1775
1724
|
* The accounts this store is willing to OFFER.
|
|
1776
1725
|
*
|
|
@@ -3398,14 +3347,6 @@ interface TraeUsageRouteOptions {
|
|
|
3398
3347
|
claim: TraeCheckinClaim;
|
|
3399
3348
|
checkin: TraeCheckinStatus;
|
|
3400
3349
|
} | undefined>;
|
|
3401
|
-
/**
|
|
3402
|
-
* The account the Trae app is signed in to right now, when it differs from
|
|
3403
|
-
* the bound one (issue #29). Optional: absent means the card's follow hint
|
|
3404
|
-
* simply is not offered (a test harness or a host without the store's scan).
|
|
3405
|
-
* Read-only — the HOST never rebinds from this value; the follow is a
|
|
3406
|
-
* settings write, made by the card or by the host's follow switch.
|
|
3407
|
-
*/
|
|
3408
|
-
liveLogin?(panel: TraePanel): Promise<TraeWebAppLogin | undefined>;
|
|
3409
3350
|
/** The requested region's last-refreshed raw directory (one entry per upstream model). */
|
|
3410
3351
|
displayModels(panel: TraePanel): readonly TraeModelInfo[];
|
|
3411
3352
|
/** The user's model selection in the requested panel, stored as model id (= Trae name). */
|
|
@@ -3459,6 +3400,31 @@ interface TraePoolDeps {
|
|
|
3459
3400
|
* Only the status document's own assembly and an explicit batch call it.
|
|
3460
3401
|
*/
|
|
3461
3402
|
members(panel: TraePanel): Promise<readonly TraePoolMember[]>;
|
|
3403
|
+
/**
|
|
3404
|
+
* One panel's pool membership from LOCAL state only — the first-paint source
|
|
3405
|
+
* (issue #77).
|
|
3406
|
+
*
|
|
3407
|
+
* Cheap by contract in the strongest sense: no upstream read at all, so the
|
|
3408
|
+
* account table can be painted before any billing endpoint answers. A member
|
|
3409
|
+
* whose credit total has never been measured carries no `credits`, which the
|
|
3410
|
+
* card already renders as `—` rather than as a wrong figure; the follow-up
|
|
3411
|
+
* full document fills the real numbers in.
|
|
3412
|
+
*
|
|
3413
|
+
* A Host built without it degrades to omitting the pool block from a
|
|
3414
|
+
* first-paint document rather than paying {@link members} there — the card
|
|
3415
|
+
* then shows its single-account view for one refresh cycle.
|
|
3416
|
+
*/
|
|
3417
|
+
localMembers?(panel: TraePanel): Promise<readonly TraePoolMember[]>;
|
|
3418
|
+
/**
|
|
3419
|
+
* The panel's saved member names, from a local scan only.
|
|
3420
|
+
*
|
|
3421
|
+
* The first-paint counterpart of {@link memberNames}. The full document builds
|
|
3422
|
+
* these by walking {@link members} (which reads every balance), which is a
|
|
3423
|
+
* second full membership pass for a map of names; the fast document must not
|
|
3424
|
+
* pay that, and it does not need to: the saved-list rows it renders are the
|
|
3425
|
+
* ones {@link localMembers} already names.
|
|
3426
|
+
*/
|
|
3427
|
+
localMemberNames?(panel: TraePanel): Promise<Record<string, string>>;
|
|
3462
3428
|
/**
|
|
3463
3429
|
* The checked ids that still resolve to a local sign-in, from a local scan only.
|
|
3464
3430
|
* The batch route's empty-pool guard runs this on every request, so it must not
|
|
@@ -3482,6 +3448,16 @@ interface TraePoolDeps {
|
|
|
3482
3448
|
* TODAY because this machine already spent its claim on another account.
|
|
3483
3449
|
*/
|
|
3484
3450
|
checkinState?(panel: TraePanel): Promise<Record<string, TraePoolCheckinState>>;
|
|
3451
|
+
/**
|
|
3452
|
+
* Today's check-in state from the cells already measured this process — the
|
|
3453
|
+
* first-paint counterpart (issue #77) of {@link checkinState}.
|
|
3454
|
+
*
|
|
3455
|
+
* Synchronous by contract: it reads the Host's measurement cache and nothing
|
|
3456
|
+
* else, so it must never start a request. A Host without it degrades to an
|
|
3457
|
+
* unknown check-in column in the first paint, which the card already renders
|
|
3458
|
+
* as unknown; the full document fills the column in.
|
|
3459
|
+
*/
|
|
3460
|
+
cachedCheckinState?(panel: TraePanel): Readonly<Record<string, TraePoolCheckinState>>;
|
|
3485
3461
|
/** Run the check-in batch. Persists nothing to settings; drops its own caches. */
|
|
3486
3462
|
checkin?(panel: TraePanel): Promise<readonly TraePoolCheckinRow[]>;
|
|
3487
3463
|
/** Run the model-availability batch and persist its measurements. */
|
|
@@ -3500,6 +3476,19 @@ interface TraePoolCheckinState {
|
|
|
3500
3476
|
/** Whether this account is offered the reward at all; false renders `disabled`. */
|
|
3501
3477
|
enabled?: boolean;
|
|
3502
3478
|
}
|
|
3479
|
+
/**
|
|
3480
|
+
* Which usage document a request asked for (issue #77).
|
|
3481
|
+
*
|
|
3482
|
+
* `full` is the document every earlier caller has always received: live credit
|
|
3483
|
+
* figures, fresh check-in state, the ranked pool. `fast` is the same document
|
|
3484
|
+
* built from LOCAL state only — no upstream read at all — so the card can paint
|
|
3485
|
+
* the account table immediately and then upgrade it with the full document.
|
|
3486
|
+
*
|
|
3487
|
+
* The distinction lives in ONE optional query parameter (`detail`) so an old
|
|
3488
|
+
* caller, a hand-written curl and a browser refresh all keep working: an absent
|
|
3489
|
+
* or unrecognised value is `full`.
|
|
3490
|
+
*/
|
|
3491
|
+
type TraeStatusDetail = 'full' | 'fast';
|
|
3503
3492
|
/**
|
|
3504
3493
|
* Assemble one region's card document. `panel` is the tab the card is on; the
|
|
3505
3494
|
* panel-scoped store already answers with only that region's accounts, so the
|
|
@@ -3507,7 +3496,9 @@ interface TraePoolCheckinState {
|
|
|
3507
3496
|
* Sign-in state is read-only; credit is a live billing answer whose failure
|
|
3508
3497
|
* degrades to `creditsError` rather than failing the whole document.
|
|
3509
3498
|
*/
|
|
3510
|
-
declare function traeWebUsage(deps: TraeUsageRouteOptions, panel: TraePanel
|
|
3499
|
+
declare function traeWebUsage(deps: TraeUsageRouteOptions, panel: TraePanel, options?: {
|
|
3500
|
+
detail?: TraeStatusDetail;
|
|
3501
|
+
}): Promise<TraeWebUsage>;
|
|
3511
3502
|
/** Mount the GET usage route on an optional webServer context. */
|
|
3512
3503
|
declare function registerTraeUsageRoute(ctx: Context$1, deps: TraeUsageRouteOptions): void;
|
|
3513
3504
|
//#endregion
|
|
@@ -3624,26 +3615,6 @@ interface Config {
|
|
|
3624
3615
|
* settings form can both read and write it.
|
|
3625
3616
|
*/
|
|
3626
3617
|
showPointsInMainUi?: boolean;
|
|
3627
|
-
/**
|
|
3628
|
-
* Follow the Trae desktop app's CURRENT sign-in instead of staying bound to
|
|
3629
|
-
* the account the user (or the default) picked earlier (issue #29).
|
|
3630
|
-
*
|
|
3631
|
-
* When the app switches accounts, the old binding kept billing the previous
|
|
3632
|
-
* account forever while `dsh-connect-workbuddy` followed the app
|
|
3633
|
-
* automatically — every Trae sign-out then needed a manual switch here. With
|
|
3634
|
-
* this switch ON, each settings re-apply rebinds `accounts.<region>` to
|
|
3635
|
-
* whatever the app is signed in to right now (the live login outranks the
|
|
3636
|
-
* plugin's own refreshed copies, see `TraeCredentialStore.preferred`).
|
|
3637
|
-
*
|
|
3638
|
-
* OFF by default: an existing configuration keeps the strict bind it
|
|
3639
|
-
* shipped with, because silently switching billing accounts is exactly what
|
|
3640
|
-
* `current()`'s no-fallback rule exists to prevent — the user opts in. When
|
|
3641
|
-
* OFF, the card still shows a one-click hint (its own settings write), so
|
|
3642
|
-
* the report in #29 is solved on both paths without any behavior change
|
|
3643
|
-
* nobody asked for. Declared volatile so the card's switch can read and
|
|
3644
|
-
* write it on the 0.1.7 settings form.
|
|
3645
|
-
*/
|
|
3646
|
-
followAppLogin?: boolean;
|
|
3647
3618
|
/**
|
|
3648
3619
|
* Per-region model state, keyed `cn` | `ai`. The CN and international apps
|
|
3649
3620
|
* expose different rosters, so each keeps its own directory and selection
|
|
@@ -3726,4 +3697,4 @@ declare function asVolatile<S>(schema: S): S;
|
|
|
3726
3697
|
declare const Config: z<Config>;
|
|
3727
3698
|
declare function apply(ctx: Context$1, config: Config): void;
|
|
3728
3699
|
//#endregion
|
|
3729
|
-
export { Config, FALLBACK_TRAE_MODELS, FALLBACK_TRAE_MODELS_AI, MAX_PROBE_INPUT_TOKENS, type PiAiGeneration, type PiAiRuntimeInfo, REGION_GATEWAYS, type RawChatDelta, type RawChatMessage, type RawChatTool, SseDecoder, type SseEvent, TRAE_ACCOUNT_STATUS_PATH, TRAE_AI_PROVIDER, TRAE_CHECKIN_PATH, TRAE_CN_AGENT_TASK_PATH, TRAE_CN_TITLE_PATH, TRAE_ENTERPRISE_PROVIDER, TRAE_MODELS_TEST_PATH, TRAE_MODEL_DETAIL_FUNCTIONS, TRAE_MODEL_DETAIL_PATH, TRAE_PANELS, TRAE_PAY_BASE, TRAE_PROBE_TIMEOUT_MS, TRAE_PROVIDER, TRAE_PROVIDERS, TRAE_PROVIDER_DISPLAY_NAMES, TRAE_PROVIDER_IDS, TRAE_PROVIDER_ID_DISPLAY_NAMES, TRAE_PROVIDER_ROUTES, 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_STATE_DB_FILENAME, TRAE_STREAM_IDLE_TIMEOUT_MS, TRAE_USAGE_PATH, TRAE_VERSION_CODE_FALLBACK, type TraeActivityRule, type TraeAdapter, type TraeCachedModelConfig, TraeCatalog, type TraeChatResult, type TraeCheckinClaim, type TraeCheckinStatus, type TraeContextBudget, type TraeCredential, TraeCredentialStore, TraeDelegatingUpstreamClient, type TraeDiscoveredModel, type TraeDiscoveredReasoning, type TraeEdition, type TraeFailure, TraeFallbackUpstreamClient, type TraeFallbackUpstreamOptions, type TraeFusionRawChatEnvelope, TraeGatedUpstreamClient, type TraeGatedUpstreamOptions, type TraeIdentity, type TraeInputModality, type TraeModelDetailRequest, type TraeModelInfo, type TraeModelProbeVerdict, type TraeModelsTestResponse, type TraeObservedModelConfig, type TraePanel, type TraeProbeResult, type TraeProbeVerdict, type TraeProviderRoute, 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 TraeWebAccountState, type TraeWebActivity, type
|
|
3700
|
+
export { Config, FALLBACK_TRAE_MODELS, FALLBACK_TRAE_MODELS_AI, MAX_PROBE_INPUT_TOKENS, type PiAiGeneration, type PiAiRuntimeInfo, REGION_GATEWAYS, type RawChatDelta, type RawChatMessage, type RawChatTool, SseDecoder, type SseEvent, TRAE_ACCOUNT_STATUS_PATH, TRAE_AI_PROVIDER, TRAE_CHECKIN_PATH, TRAE_CN_AGENT_TASK_PATH, TRAE_CN_TITLE_PATH, TRAE_ENTERPRISE_PROVIDER, TRAE_MODELS_TEST_PATH, TRAE_MODEL_DETAIL_FUNCTIONS, TRAE_MODEL_DETAIL_PATH, TRAE_PANELS, TRAE_PAY_BASE, TRAE_PROBE_TIMEOUT_MS, TRAE_PROVIDER, TRAE_PROVIDERS, TRAE_PROVIDER_DISPLAY_NAMES, TRAE_PROVIDER_IDS, TRAE_PROVIDER_ID_DISPLAY_NAMES, TRAE_PROVIDER_ROUTES, 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_STATE_DB_FILENAME, TRAE_STREAM_IDLE_TIMEOUT_MS, TRAE_USAGE_PATH, TRAE_VERSION_CODE_FALLBACK, type TraeActivityRule, type TraeAdapter, type TraeCachedModelConfig, TraeCatalog, type TraeChatResult, type TraeCheckinClaim, type TraeCheckinStatus, type TraeContextBudget, type TraeCredential, TraeCredentialStore, TraeDelegatingUpstreamClient, type TraeDiscoveredModel, type TraeDiscoveredReasoning, type TraeEdition, type TraeFailure, TraeFallbackUpstreamClient, type TraeFallbackUpstreamOptions, type TraeFusionRawChatEnvelope, TraeGatedUpstreamClient, type TraeGatedUpstreamOptions, type TraeIdentity, type TraeInputModality, type TraeModelDetailRequest, type TraeModelInfo, type TraeModelProbeVerdict, type TraeModelsTestResponse, type TraeObservedModelConfig, type TraePanel, type TraeProbeResult, type TraeProbeVerdict, type TraeProviderRoute, 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 TraeWebAccountState, type TraeWebActivity, type TraeWebCheckin, type TraeWebCheckinClaim, type TraeWebCredits, type TraeWebUsage, type TraeWireModel, type TraeWireTarget, UnconfiguredTraeUpstreamClient, adaptLegacyPiAiContext, adaptModernPiAiContext, apply, applyContextBudgets, applyImageSelection, applyReasoningEffort, asVolatile, bridgeTraeSoloStream, buildTraeAgentTaskBody, buildTraeCnHeaders, buildTraeFusionRawChatEnvelope, buildTraeModelDetailRequest, buildTraeRawChatDraft, buildTraeRawChatRuntimeConfig, classifyProbeError, classifyProbeResult, classifyTraeRawChatFailure, configuredAccountsOf, createTraeAdapter, createTraeRawGateway, createTraeShim, creditPosition, decidePiAiGeneration, decodeRawChatChunk, decodeTraeEvent, decryptTraeStorageValue, deriveCatalog, describeIdShape, describeNameShape, describeTraeFailure, discoveredCatalog, displayNameOfTraeProvider, fallbackModelsFor, generationOf, hashTraeRawChatArg, identityHeaders, inject, isEnterpriseAccount, legacyTraeOwnAuthPath, maskUserPath, mergeTraeModelSources, name, nextRegionCreditPosition, nextRegionEnabled, nextRegionSlots, normalizeTraeCredential, normalizeTraeVersionCode, panelOfKey, panelOfProvider, parseObservedModelConfig, parseReasoningCapability, parseTraeAuthValue, parseTraeCachedModel, parseTraeModelExtraConfigLogLine, parseTraeRawChatBehaviorConfig, parseTraeRemoteModel, parseTraeStorageDocument, piAiRuntimeInfo, pickTraeStorageIdentity, prepareSoloBody, probeBody, probeModelsSequentially, probeOneModel, probeTraeRawChatCapability, providerOfPanel, rawCapabilityDiagnostic, rawCapabilityFingerprint, readTraeCachedModel, readTraeCliIdentity, readTraeIdentity, refreshTraeCredential, regionCreditPositionOf, regionEnabled, regionEnabledOf, regionOfCredential, regionOfEdition, regionOfHost, regionOfPanel, regionOfTraeProvider, regionOfTraeStatusUrl, regionOfUserRegion, regionStateOf, registerTraeUsageRoute, resolveTraeIdentity, resolveTraeRawRuntime, routeOfTraeProvider, sanitizeCatalog, settingsNamespaceOf, traeEndpoint, traeInputModalities, traeModelDisplayName, traeOwnAuthPath, traeRawChatExtraInfo, traeStateDatabaseCandidates, traeStorageCandidates, traeWebUsage, traeWindowsAppNames, unwrapVolatile, unwrapVolatileDeep, wireTargetsOf, withContextAdaptation, withTraeRegion };
|
package/lib/index.js
CHANGED
|
@@ -1089,6 +1089,23 @@ function regionOfTraeStatusUrl(url) {
|
|
|
1089
1089
|
if (value === null || value === "") return "cn";
|
|
1090
1090
|
return TRAE_PANELS.includes(value) ? value : void 0;
|
|
1091
1091
|
}
|
|
1092
|
+
/** Query parameter asking for a thinner first-paint document (issue #77). */
|
|
1093
|
+
const TRAE_DETAIL_PARAM = "detail";
|
|
1094
|
+
/** The only recognised `detail` value: local state only, no upstream read. */
|
|
1095
|
+
const TRAE_DETAIL_FAST = "fast";
|
|
1096
|
+
/**
|
|
1097
|
+
* Whether a request URL asks for the fast document.
|
|
1098
|
+
*
|
|
1099
|
+
* Only the exact value counts — a typo (`fastest`, `FAST`) degrades to the FULL
|
|
1100
|
+
* document, because a thinner panel is a worse failure than a slower one: the
|
|
1101
|
+
* caller that made the mistake is asking about an optimisation, so the safe
|
|
1102
|
+
* answer is the truthful one.
|
|
1103
|
+
*/
|
|
1104
|
+
function fastDetailOf(url) {
|
|
1105
|
+
const at = url.indexOf("?");
|
|
1106
|
+
if (at === -1) return false;
|
|
1107
|
+
return new URLSearchParams(url.slice(at + 1)).get(TRAE_DETAIL_PARAM) === TRAE_DETAIL_FAST;
|
|
1108
|
+
}
|
|
1092
1109
|
/**
|
|
1093
1110
|
* Peel ONE `{get(): T}` live reference, the shape DSH 0.1.7 delivers a
|
|
1094
1111
|
* volatile-marked settings field as (see {@link unwrapVolatileDeep}).
|
|
@@ -1144,20 +1161,6 @@ function configuredAccountsOf(configured) {
|
|
|
1144
1161
|
return typeof accounts === "object" && accounts !== null ? accounts : {};
|
|
1145
1162
|
}
|
|
1146
1163
|
/**
|
|
1147
|
-
* Whether the host-side "follow the Trae app's sign-in" switch is on (issue
|
|
1148
|
-
* #29). Absent reads as OFF, so an existing configuration keeps the strict
|
|
1149
|
-
* bind it shipped with and the switch is an opt-in — the same opt-out
|
|
1150
|
-
* semantics as {@link regionEnabledOf}, for the same reason: a config written
|
|
1151
|
-
* before this field existed cannot ask for behavior it never declared.
|
|
1152
|
-
*
|
|
1153
|
-
* Reads the whole settings section (or the bare field); deep-unwraps first
|
|
1154
|
-
* because on DSH 0.1.7 a volatile field arrives as a `{get(): T}` live
|
|
1155
|
-
* reference, whose `typeof === 'object'` would otherwise pass a naive check.
|
|
1156
|
-
*/
|
|
1157
|
-
function followAppLoginEnabled(configured) {
|
|
1158
|
-
return unwrapVolatileDeep(configured)?.followAppLogin === true;
|
|
1159
|
-
}
|
|
1160
|
-
/**
|
|
1161
1164
|
* Build the next `regions` settings value for the card's save. The write
|
|
1162
1165
|
* targets ONLY the signed-in account's region slot; every other region's slot
|
|
1163
1166
|
* is carried over untouched, so switching accounts never clobbers the other
|
|
@@ -2347,36 +2350,6 @@ var TraeCredentialStore = class {
|
|
|
2347
2350
|
return eligible.find((credential) => credential.source === "desktop") ?? eligible[0];
|
|
2348
2351
|
}
|
|
2349
2352
|
/**
|
|
2350
|
-
* The account the Trae app is signed in to right now, when that differs from
|
|
2351
|
-
* the account this store is bound to (issue #29).
|
|
2352
|
-
*
|
|
2353
|
-
* This is a READ, not a rebind: `current()` keeps resolving the bound
|
|
2354
|
-
* account verbatim so billing never moves without a decision — the store
|
|
2355
|
-
* only reports the mismatch, and the card (hint row) or the host (follow
|
|
2356
|
-
* switch) turns it into a settings write the user can see and undo. Absent
|
|
2357
|
-
* when nothing is live, when the live account IS the bound one, or when the
|
|
2358
|
-
* binding itself already fell through to the live account.
|
|
2359
|
-
*/
|
|
2360
|
-
async liveLogin() {
|
|
2361
|
-
if (this.accountId === void 0 || this.accountId === "") return void 0;
|
|
2362
|
-
const credentials = await this.readAll();
|
|
2363
|
-
const live = this.preferred(credentials);
|
|
2364
|
-
if (live === void 0) return void 0;
|
|
2365
|
-
const liveId = traeAccountId(live);
|
|
2366
|
-
if (liveId === this.accountId) return void 0;
|
|
2367
|
-
return {
|
|
2368
|
-
id: liveId,
|
|
2369
|
-
accountName: live.accountName ?? (live.userId || `${live.edition} account`),
|
|
2370
|
-
edition: live.edition,
|
|
2371
|
-
enterprise: isEnterpriseAccount(live),
|
|
2372
|
-
region: regionOfCredential(live),
|
|
2373
|
-
source: live.source,
|
|
2374
|
-
tokenExpiresAtMs: live.expiresAtMs,
|
|
2375
|
-
...live.refreshExpiresAtMs === void 0 ? {} : { refreshExpiresAtMs: live.refreshExpiresAtMs },
|
|
2376
|
-
selected: false
|
|
2377
|
-
};
|
|
2378
|
-
}
|
|
2379
|
-
/**
|
|
2380
2353
|
* The accounts this store is willing to OFFER.
|
|
2381
2354
|
*
|
|
2382
2355
|
* Scoped to who this store may bill:
|
|
@@ -6856,7 +6829,8 @@ function toCheckin(status) {
|
|
|
6856
6829
|
* Sign-in state is read-only; credit is a live billing answer whose failure
|
|
6857
6830
|
* degrades to `creditsError` rather than failing the whole document.
|
|
6858
6831
|
*/
|
|
6859
|
-
async function traeWebUsage(deps, panel) {
|
|
6832
|
+
async function traeWebUsage(deps, panel, options = {}) {
|
|
6833
|
+
const fast = options.detail === "fast";
|
|
6860
6834
|
const store = deps.store(panel);
|
|
6861
6835
|
const enabled = deps.regionEnabled(panel);
|
|
6862
6836
|
/**
|
|
@@ -6887,7 +6861,8 @@ async function traeWebUsage(deps, panel) {
|
|
|
6887
6861
|
}
|
|
6888
6862
|
let credential;
|
|
6889
6863
|
try {
|
|
6890
|
-
credential = await store.resolve();
|
|
6864
|
+
credential = fast ? await store.current() : await store.resolve();
|
|
6865
|
+
if (credential === void 0) credential = await store.resolve();
|
|
6891
6866
|
} catch (error) {
|
|
6892
6867
|
return {
|
|
6893
6868
|
status: "signed-out",
|
|
@@ -6896,10 +6871,6 @@ async function traeWebUsage(deps, panel) {
|
|
|
6896
6871
|
message: safeMessage(error)
|
|
6897
6872
|
};
|
|
6898
6873
|
}
|
|
6899
|
-
let appLogin;
|
|
6900
|
-
try {
|
|
6901
|
-
appLogin = await deps.liveLogin?.(panel);
|
|
6902
|
-
} catch {}
|
|
6903
6874
|
const account = {
|
|
6904
6875
|
accountId: accounts.find((item) => item.selected)?.id ?? "",
|
|
6905
6876
|
accountName: credential.accountName ?? credential.userId,
|
|
@@ -6908,7 +6879,6 @@ async function traeWebUsage(deps, panel) {
|
|
|
6908
6879
|
region: regionOfPanel(panel),
|
|
6909
6880
|
enabled,
|
|
6910
6881
|
accounts,
|
|
6911
|
-
...appLogin === void 0 ? {} : { appLogin },
|
|
6912
6882
|
models: deps.displayModels(panel).map((model) => ({
|
|
6913
6883
|
...model,
|
|
6914
6884
|
...model.input === void 0 ? {} : { input: [...model.input] }
|
|
@@ -6916,7 +6886,16 @@ async function traeWebUsage(deps, panel) {
|
|
|
6916
6886
|
enabledModelIds: [...deps.enabledModelIds(panel)],
|
|
6917
6887
|
...deps.rawDiagnostic === void 0 ? {} : { rawChat: deps.rawDiagnostic(panel) }
|
|
6918
6888
|
};
|
|
6919
|
-
|
|
6889
|
+
if (fast) {
|
|
6890
|
+
const poolSection = await poolSectionOf(deps, panel, true);
|
|
6891
|
+
return {
|
|
6892
|
+
status: "signed-in",
|
|
6893
|
+
...account,
|
|
6894
|
+
...poolSection,
|
|
6895
|
+
...panel === "enterprise" ? { creditsUnavailable: "enterprise" } : {}
|
|
6896
|
+
};
|
|
6897
|
+
}
|
|
6898
|
+
const poolSection = await poolSectionOf(deps, panel, false);
|
|
6920
6899
|
const client = deps.client(panel);
|
|
6921
6900
|
if (regionOfPanel(panel) === "ai") try {
|
|
6922
6901
|
const payStatus = await client.payStatus();
|
|
@@ -7009,10 +6988,10 @@ async function creditAlternatives(deps, panel, accounts) {
|
|
|
7009
6988
|
* BOUND account, which `traeWebUsage` reports as `status: error` before the pool
|
|
7010
6989
|
* is even assembled.
|
|
7011
6990
|
*/
|
|
7012
|
-
async function poolSectionOf(deps, panel) {
|
|
6991
|
+
async function poolSectionOf(deps, panel, fast = false) {
|
|
7013
6992
|
if (deps.pool === void 0) return {};
|
|
7014
6993
|
try {
|
|
7015
|
-
const pool = await traeWebPool(deps, panel);
|
|
6994
|
+
const pool = await traeWebPool(deps, panel, fast);
|
|
7016
6995
|
return pool === void 0 ? {} : { pool };
|
|
7017
6996
|
} catch {
|
|
7018
6997
|
return {};
|
|
@@ -7030,20 +7009,35 @@ async function poolSectionOf(deps, panel) {
|
|
|
7030
7009
|
* Every per-row read is failure-tolerant on purpose: one account whose balance
|
|
7031
7010
|
* read throws must not remove the other members from the table.
|
|
7032
7011
|
*/
|
|
7033
|
-
async function traeWebPool(deps, panel) {
|
|
7012
|
+
async function traeWebPool(deps, panel, fast = false) {
|
|
7034
7013
|
const pool = deps.pool;
|
|
7035
7014
|
if (pool === void 0) return void 0;
|
|
7036
7015
|
const preferences = pool.preferences(panel);
|
|
7037
|
-
const members = await pool.members(panel);
|
|
7016
|
+
const members = fast ? await pool.localMembers?.(panel) : await pool.members(panel);
|
|
7017
|
+
if (members === void 0) return void 0;
|
|
7038
7018
|
const nowMs = Date.now();
|
|
7039
7019
|
const ranked = rankPool(members, nowMs);
|
|
7040
7020
|
const target = resolveTargetModel(pool.catalog?.(panel) ?? [], preferences.targetModelId);
|
|
7041
7021
|
const byId = new Map(members.map((member) => [member.account.id, member]));
|
|
7042
7022
|
const excludedById = new Map(ranked.flatMap((row) => row.excludedBy === void 0 ? [] : [[row.account.id, row.excludedBy]]));
|
|
7043
7023
|
const rankedIds = new Set(ranked.map((row) => row.account.id));
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7024
|
+
/**
|
|
7025
|
+
* ISSUE #77: every dep below is shaped so the first paint can avoid the
|
|
7026
|
+
* upstream. `otherAccounts`, `memberNames` and `checkinState` are all derived
|
|
7027
|
+
* from a FULL membership pass on this Host (one balance read per account), and
|
|
7028
|
+
* three of them in a row is most of the reported wait. The fast document
|
|
7029
|
+
* answers the same questions from the roster it already has:
|
|
7030
|
+
*
|
|
7031
|
+
* - `otherAccounts`: the local roster minus the members already ranked. The
|
|
7032
|
+
* saved-but-unreachable ghosts are added below from `preferences` anyway, so
|
|
7033
|
+
* nothing a row is built from goes missing.
|
|
7034
|
+
* - `memberNames`: only consulted for ghosts, whose rows the fast document
|
|
7035
|
+
* carries from `localMemberNames` when the Host offers it.
|
|
7036
|
+
* - `checkinState`: the measurement cache, read synchronously.
|
|
7037
|
+
*/
|
|
7038
|
+
const others = fast ? members.filter((member) => !rankedIds.has(member.account.id)).map((member) => member.account) : (await pool.otherAccounts?.(panel) ?? []).filter((account) => !rankedIds.has(account.id));
|
|
7039
|
+
const checkinState = fast ? { ...pool.cachedCheckinState?.(panel) ?? {} } : await pool.checkinState?.(panel).catch(() => ({})) ?? {};
|
|
7040
|
+
const memberNames = fast ? await pool.localMemberNames?.(panel).catch(() => ({})) ?? {} : await pool.memberNames?.(panel).catch(() => ({})) ?? {};
|
|
7047
7041
|
const currentAccountId = await pool.currentAccountId?.(panel).catch(() => void 0);
|
|
7048
7042
|
/**
|
|
7049
7043
|
* A row per listed account, members first.
|
|
@@ -7239,8 +7233,9 @@ function registerTraeUsageRoute(ctx, deps) {
|
|
|
7239
7233
|
}
|
|
7240
7234
|
const panel = requestRegion(req, res);
|
|
7241
7235
|
if (panel === void 0) return;
|
|
7236
|
+
const detail = fastDetailOf(req.url ?? "") ? "fast" : "full";
|
|
7242
7237
|
try {
|
|
7243
|
-
json(res, 200, await traeWebUsage(deps, panel));
|
|
7238
|
+
json(res, 200, await traeWebUsage(deps, panel, { detail }));
|
|
7244
7239
|
} catch (error) {
|
|
7245
7240
|
json(res, 500, { error: safeMessage(error) });
|
|
7246
7241
|
}
|
|
@@ -8531,7 +8526,6 @@ const Config = z.object({
|
|
|
8531
8526
|
]).default("auto").description("Trae edition hint")),
|
|
8532
8527
|
accountId: z.string().description("Deprecated: pre-split account selector, attributed to its own region"),
|
|
8533
8528
|
showPointsInMainUi: asVolatile(z.boolean().default(false).description("在输入框工具栏显示积分读数「Trae CN · <数值>」(仅当选中的模型属于本插件时出现,可点击查看账号与积分明细)")),
|
|
8534
|
-
followAppLogin: asVolatile(z.boolean().default(false).description("跟随 Trae 桌面端当前登录账号换绑(打开后每次配置生效时重绑到 App 现在登录的账号)")),
|
|
8535
8529
|
accounts: asVolatile(accountSelectionConfig.description("Per-panel account selections, keyed cn | enterprise | ai")),
|
|
8536
8530
|
regions: asVolatile(z.dict(regionStateConfig).default({}).description("Per-panel model directory and selection, keyed cn | enterprise | ai")),
|
|
8537
8531
|
lastCatalog: z.array(modelConfig).description("Deprecated: pre-region-split CN model directory"),
|
|
@@ -8543,6 +8537,15 @@ const Config = z.object({
|
|
|
8543
8537
|
/** Every region, in card tab order. */
|
|
8544
8538
|
const REGION_KEYS = ["cn", "ai"];
|
|
8545
8539
|
/**
|
|
8540
|
+
* How long a measured check-in state stays usable (issue #77).
|
|
8541
|
+
*
|
|
8542
|
+
* The upstream rewards ONE check-in per device per day, so a figure measured
|
|
8543
|
+
* minutes ago cannot have changed; re-reading it is a pure upstream round-trip.
|
|
8544
|
+
* Ten minutes matches the sibling plugin's cache and is comfortably shorter than
|
|
8545
|
+
* a day, so a genuine second check-in still shows up on the next poll after it.
|
|
8546
|
+
*/
|
|
8547
|
+
const CHECKIN_CACHE_MS = 6e5;
|
|
8548
|
+
/**
|
|
8546
8549
|
* What ONE failed real request means for the account that served it (issue #44).
|
|
8547
8550
|
*
|
|
8548
8551
|
* The shim only reports account-scoped kinds here, so this table never has to
|
|
@@ -8992,6 +8995,24 @@ function apply(ctx, config) {
|
|
|
8992
8995
|
enterprise: /* @__PURE__ */ new Map(),
|
|
8993
8996
|
ai: /* @__PURE__ */ new Map()
|
|
8994
8997
|
};
|
|
8998
|
+
/**
|
|
8999
|
+
* Today's check-in state per account, measured by the LAST full read.
|
|
9000
|
+
*
|
|
9001
|
+
* Issue #77: the check-in column used to be an upstream call per account with
|
|
9002
|
+
* no memory at all, so every open of the card paid for every member twice (the
|
|
9003
|
+
* pool's own membership pass plus one `checkinStatus()` each). The first-paint
|
|
9004
|
+
* document may not wait for any of that, and re-measuring what was measured
|
|
9005
|
+
* minutes ago buys nothing: a check-in happens at most once per device per day.
|
|
9006
|
+
*
|
|
9007
|
+
* Entries older than {@link CHECKIN_CACHE_MS} are treated as absent rather than
|
|
9008
|
+
* as stale truth, so a document never claims "already checked in" off yesterday's
|
|
9009
|
+
* measurement. A cold cache means "not read", which the card renders as unknown.
|
|
9010
|
+
*/
|
|
9011
|
+
const checkinStateCache = {
|
|
9012
|
+
cn: {},
|
|
9013
|
+
enterprise: {},
|
|
9014
|
+
ai: {}
|
|
9015
|
+
};
|
|
8995
9016
|
const poolCreditsOfAccount = async (panel, accountId) => {
|
|
8996
9017
|
const credits = await stacks[panel].creditsOfAccount(accountId);
|
|
8997
9018
|
if (credits === void 0) return void 0;
|
|
@@ -9067,6 +9088,24 @@ function apply(ctx, config) {
|
|
|
9067
9088
|
}));
|
|
9068
9089
|
};
|
|
9069
9090
|
/**
|
|
9091
|
+
* Today's check-in state from the cells already measured — the first-paint read
|
|
9092
|
+
* (issue #77).
|
|
9093
|
+
*
|
|
9094
|
+
* Synchronous and local by contract: it never starts a request, so the card's
|
|
9095
|
+
* first paint cannot be held by the upstream. Only FRESH cells are returned; an
|
|
9096
|
+
* expired entry is dropped rather than reported, because "checked in" from four
|
|
9097
|
+
* hours ago is a claim about a day that may already be over.
|
|
9098
|
+
*/
|
|
9099
|
+
function cachedCheckinStateOf(panel) {
|
|
9100
|
+
const nowMs = Date.now();
|
|
9101
|
+
const state = {};
|
|
9102
|
+
for (const [accountId, entry] of Object.entries(checkinStateCache[panel])) {
|
|
9103
|
+
if (nowMs - entry.atMs >= CHECKIN_CACHE_MS) continue;
|
|
9104
|
+
state[accountId] = entry.state;
|
|
9105
|
+
}
|
|
9106
|
+
return state;
|
|
9107
|
+
}
|
|
9108
|
+
/**
|
|
9070
9109
|
* Pool members from LOCAL state only: the store's logins, the saved probes and
|
|
9071
9110
|
* the cached credit totals. No upstream call, so it is safe on the request
|
|
9072
9111
|
* path (see {@link poolCreditCache} for why the cache exists).
|
|
@@ -9291,7 +9330,8 @@ function apply(ctx, config) {
|
|
|
9291
9330
|
checkinSupported: (stored) => regionOfPanel(stored) === "cn",
|
|
9292
9331
|
checkin: async (stored) => checkinAllAccounts(await poolTargetsOf(stored), probeDeps()),
|
|
9293
9332
|
checkinState: async (stored) => {
|
|
9294
|
-
const state =
|
|
9333
|
+
const state = cachedCheckinStateOf(stored);
|
|
9334
|
+
const nowMs = Date.now();
|
|
9295
9335
|
for (const target of await poolTargetsOf(stored)) {
|
|
9296
9336
|
const credential = await stacks[stored].store.resolveCredentialOf(target.accountId);
|
|
9297
9337
|
if (credential === void 0) continue;
|
|
@@ -9301,15 +9341,21 @@ function apply(ctx, config) {
|
|
|
9301
9341
|
});
|
|
9302
9342
|
try {
|
|
9303
9343
|
const status = await client.checkinStatus();
|
|
9304
|
-
|
|
9344
|
+
const measured = {
|
|
9305
9345
|
checkedIn: status.checkedIn,
|
|
9306
9346
|
deviceCheckedIn: status.didCheckedIn,
|
|
9307
9347
|
enabled: status.enabled
|
|
9308
9348
|
};
|
|
9349
|
+
state[target.accountId] = measured;
|
|
9350
|
+
checkinStateCache[stored][target.accountId] = {
|
|
9351
|
+
state: measured,
|
|
9352
|
+
atMs: nowMs
|
|
9353
|
+
};
|
|
9309
9354
|
} catch {}
|
|
9310
9355
|
}
|
|
9311
9356
|
return state;
|
|
9312
9357
|
},
|
|
9358
|
+
cachedCheckinState: (stored) => cachedCheckinStateOf(stored),
|
|
9313
9359
|
test: async (stored, modelId, onRow) => {
|
|
9314
9360
|
await warmWireMap(stored);
|
|
9315
9361
|
const probeInputTokens = probeInputTokensOf(stored);
|
|
@@ -9341,50 +9387,6 @@ function apply(ctx, config) {
|
|
|
9341
9387
|
}
|
|
9342
9388
|
};
|
|
9343
9389
|
};
|
|
9344
|
-
/**
|
|
9345
|
-
* The app's current sign-in when it differs from the bound account (issue
|
|
9346
|
-
* #29). Pure read: the store resolves the bound account verbatim, so billing
|
|
9347
|
-
* never moves from here — the card's hint and the follow switch below are
|
|
9348
|
-
* the only writers.
|
|
9349
|
-
*/
|
|
9350
|
-
const liveLoginOf = async (panel) => {
|
|
9351
|
-
const region = regionOfPanel(panel);
|
|
9352
|
-
try {
|
|
9353
|
-
const live = await stacks[panel].store.liveLogin();
|
|
9354
|
-
if (live === void 0 || live.region !== region) return void 0;
|
|
9355
|
-
return {
|
|
9356
|
-
id: live.id,
|
|
9357
|
-
accountName: live.accountName,
|
|
9358
|
-
edition: live.edition,
|
|
9359
|
-
region: live.region
|
|
9360
|
-
};
|
|
9361
|
-
} catch {
|
|
9362
|
-
return;
|
|
9363
|
-
}
|
|
9364
|
-
};
|
|
9365
|
-
/**
|
|
9366
|
-
* Persist `accounts.<region>` = the app's live sign-in and rebind the store
|
|
9367
|
-
* (issue #29). The ONE Host-side path that moves the binding, driven by the
|
|
9368
|
-
* follow switch in {@link maybeFollowAppLogin}; the card's one-click hint
|
|
9369
|
-
* goes through its own settings write instead (one writer per path). Returns
|
|
9370
|
-
* the applied login so the caller can report what moved.
|
|
9371
|
-
*
|
|
9372
|
-
* The write goes through `settings.update` (the volatile write gate), not a
|
|
9373
|
-
* direct config mutation: `apply()` receives a snapshot, and the settings
|
|
9374
|
-
* event (`loader/volatile-update`) is what re-runs `applySelection` with the
|
|
9375
|
-
* updated document — the same channel every card write already uses.
|
|
9376
|
-
*/
|
|
9377
|
-
const followAppLogin = async (panel) => {
|
|
9378
|
-
const live = await liveLoginOf(panel);
|
|
9379
|
-
if (live === void 0 || writeSettings === void 0) return void 0;
|
|
9380
|
-
const configured = configuredAccountsOf(current());
|
|
9381
|
-
if (configured[panel] === live.id) return live;
|
|
9382
|
-
await writeSettings({ accounts: {
|
|
9383
|
-
...configured,
|
|
9384
|
-
[panel]: live.id
|
|
9385
|
-
} });
|
|
9386
|
-
return live;
|
|
9387
|
-
};
|
|
9388
9390
|
ctx.inject(["webServer"], (webCtx) => registerTraeUsageRoute(webCtx, {
|
|
9389
9391
|
store: (panel) => stacks[panel].store,
|
|
9390
9392
|
client: (panel) => stacks[panel].usageClient,
|
|
@@ -9405,10 +9407,15 @@ function apply(ctx, config) {
|
|
|
9405
9407
|
}),
|
|
9406
9408
|
accountStateOf: (panel, accountId) => stacks[panel].accountStateOf(accountId),
|
|
9407
9409
|
claimCheckinOf: (panel, accountId) => stacks[panel].claimCheckinOf(accountId),
|
|
9408
|
-
liveLogin: (panel) => liveLoginOf(panel),
|
|
9409
9410
|
pool: {
|
|
9410
9411
|
preferences: (panel) => poolPreferencesOf(panel),
|
|
9411
9412
|
members: (panel) => poolMembersOf(panel),
|
|
9413
|
+
localMembers: (panel) => localPoolMembers(panel),
|
|
9414
|
+
localMemberNames: async (panel) => {
|
|
9415
|
+
const names = {};
|
|
9416
|
+
for (const member of await localPoolMembers(panel)) names[member.account.id] = member.account.accountName;
|
|
9417
|
+
return names;
|
|
9418
|
+
},
|
|
9412
9419
|
effectiveMemberAccountIds: async (panel) => {
|
|
9413
9420
|
return effectiveMembersOf(poolPreferencesOf(panel).memberAccountIds ?? [], new Set((await poolMembersOf(panel)).map((member) => member.account.id)));
|
|
9414
9421
|
},
|
|
@@ -9423,6 +9430,7 @@ function apply(ctx, config) {
|
|
|
9423
9430
|
checkinSupported: (panel) => regionOfPanel(panel) === "cn",
|
|
9424
9431
|
checkin: (panel) => poolDepsOf(panel).checkin?.(panel) ?? Promise.resolve([]),
|
|
9425
9432
|
checkinState: (panel) => poolDepsOf(panel).checkinState?.(panel) ?? Promise.resolve({}),
|
|
9433
|
+
cachedCheckinState: (panel) => cachedCheckinStateOf(panel),
|
|
9426
9434
|
test: (panel, modelId, onRow) => poolDepsOf(panel).test?.(panel, modelId, onRow) ?? Promise.resolve([]),
|
|
9427
9435
|
testOne: (panel, accountId) => poolDepsOf(panel).testOne?.(panel, accountId) ?? Promise.resolve(void 0),
|
|
9428
9436
|
removeOne: (panel, accountId) => poolDepsOf(panel).removeOne?.(panel, accountId) ?? Promise.resolve(false)
|
|
@@ -9524,59 +9532,13 @@ function apply(ctx, config) {
|
|
|
9524
9532
|
}
|
|
9525
9533
|
} catch {}
|
|
9526
9534
|
})();
|
|
9527
|
-
let writeSettings;
|
|
9528
9535
|
ctx.inject(["settings"], (settingsCtx) => {
|
|
9529
9536
|
const settings = settingsCtx.settings;
|
|
9530
9537
|
ctx.effect(() => settings.configure({ auto: true }, ctx.fiber));
|
|
9531
|
-
writeSettings = async (patch) => {
|
|
9532
|
-
const revision = () => settings.describe().find((entry) => entry.ns === settingsNs)?.revision;
|
|
9533
|
-
const conflict = (error) => typeof error === "object" && error !== null && error.code === "SETTINGS_CONFLICT";
|
|
9534
|
-
try {
|
|
9535
|
-
await settings.update(settingsNs, patch, revision());
|
|
9536
|
-
} catch (error) {
|
|
9537
|
-
if (!conflict(error)) throw error;
|
|
9538
|
-
await settings.update(settingsNs, patch, revision());
|
|
9539
|
-
}
|
|
9540
|
-
};
|
|
9541
|
-
ctx.effect(() => () => {
|
|
9542
|
-
writeSettings = void 0;
|
|
9543
|
-
});
|
|
9544
9538
|
});
|
|
9545
9539
|
ctx.on("loader/volatile-update", () => {
|
|
9546
9540
|
applySelection(current());
|
|
9547
|
-
maybeFollowAppLogin();
|
|
9548
9541
|
});
|
|
9549
|
-
/**
|
|
9550
|
-
* Follow the app's sign-in on every settings change when the switch is ON
|
|
9551
|
-
* (issue #29). Runs AFTER `applySelection` so this pass binds the previous
|
|
9552
|
-
* document, reads the app's live login against it, and — if they differ —
|
|
9553
|
-
* writes `accounts.<region>` once; that write emits `volatile-update` again,
|
|
9554
|
-
* which re-runs `applySelection` on the NEW document. The loop terminates on
|
|
9555
|
-
* its own the moment `liveLogin()` reports no mismatch (the second pass binds
|
|
9556
|
-
* the live account, so `preferred` and `current` agree). The guard below
|
|
9557
|
-
* additionally forbids two writes in flight, so a stuck mismatch can never
|
|
9558
|
-
* turn into a write storm: the next settings change retries from scratch.
|
|
9559
|
-
*
|
|
9560
|
-
* The switch reads FALSE for an existing config (see {@link Config}), so a
|
|
9561
|
-
* user who never opted in never gets a rebind — the card's one-click hint is
|
|
9562
|
-
* their path instead.
|
|
9563
|
-
*/
|
|
9564
|
-
let followInFlight = false;
|
|
9565
|
-
const maybeFollowAppLogin = async () => {
|
|
9566
|
-
if (followInFlight || !followAppLoginEnabled(current())) return;
|
|
9567
|
-
followInFlight = true;
|
|
9568
|
-
try {
|
|
9569
|
-
for (const panel of TRAE_PANELS) {
|
|
9570
|
-
if (!regionEnabled(current(), panel)) continue;
|
|
9571
|
-
await followAppLogin(panel);
|
|
9572
|
-
}
|
|
9573
|
-
} catch (error) {
|
|
9574
|
-
ctx.logger.warn("dsh-connect-trae: follow-app-login rebind skipped", error);
|
|
9575
|
-
} finally {
|
|
9576
|
-
followInFlight = false;
|
|
9577
|
-
}
|
|
9578
|
-
};
|
|
9579
|
-
maybeFollowAppLogin();
|
|
9580
9542
|
let stopped = false;
|
|
9581
9543
|
ctx.effect(() => () => {
|
|
9582
9544
|
stopped = true;
|
|
@@ -9661,4 +9623,4 @@ function apply(ctx, config) {
|
|
|
9661
9623
|
});
|
|
9662
9624
|
}
|
|
9663
9625
|
//#endregion
|
|
9664
|
-
export { Config, FALLBACK_TRAE_MODELS, FALLBACK_TRAE_MODELS_AI, MAX_PROBE_INPUT_TOKENS, REGION_GATEWAYS, SseDecoder, TRAE_ACCOUNT_STATUS_PATH, TRAE_AI_PROVIDER, TRAE_CHECKIN_PATH, TRAE_CN_AGENT_TASK_PATH, TRAE_CN_TITLE_PATH, TRAE_ENTERPRISE_PROVIDER, TRAE_MODELS_TEST_PATH, TRAE_MODEL_DETAIL_FUNCTIONS, TRAE_MODEL_DETAIL_PATH, TRAE_PANELS, TRAE_PAY_BASE, TRAE_PROBE_TIMEOUT_MS, TRAE_PROVIDER, TRAE_PROVIDERS, TRAE_PROVIDER_DISPLAY_NAMES, TRAE_PROVIDER_IDS, TRAE_PROVIDER_ID_DISPLAY_NAMES, TRAE_PROVIDER_ROUTES, 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_STATE_DB_FILENAME, 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, adaptLegacyPiAiContext, adaptModernPiAiContext, apply, applyContextBudgets, applyImageSelection, applyReasoningEffort, asVolatile, bridgeTraeSoloStream, buildTraeAgentTaskBody, buildTraeCnHeaders, buildTraeFusionRawChatEnvelope, buildTraeModelDetailRequest, buildTraeRawChatDraft, buildTraeRawChatRuntimeConfig, classifyProbeError, classifyProbeResult, classifyTraeRawChatFailure, configuredAccountsOf, createTraeAdapter, createTraeRawGateway, createTraeShim, creditPosition, decidePiAiGeneration, decodeRawChatChunk, decodeTraeEvent, decryptTraeStorageValue, deriveCatalog, describeIdShape, describeNameShape, describeTraeFailure, discoveredCatalog, displayNameOfTraeProvider, fallbackModelsFor,
|
|
9626
|
+
export { Config, FALLBACK_TRAE_MODELS, FALLBACK_TRAE_MODELS_AI, MAX_PROBE_INPUT_TOKENS, REGION_GATEWAYS, SseDecoder, TRAE_ACCOUNT_STATUS_PATH, TRAE_AI_PROVIDER, TRAE_CHECKIN_PATH, TRAE_CN_AGENT_TASK_PATH, TRAE_CN_TITLE_PATH, TRAE_ENTERPRISE_PROVIDER, TRAE_MODELS_TEST_PATH, TRAE_MODEL_DETAIL_FUNCTIONS, TRAE_MODEL_DETAIL_PATH, TRAE_PANELS, TRAE_PAY_BASE, TRAE_PROBE_TIMEOUT_MS, TRAE_PROVIDER, TRAE_PROVIDERS, TRAE_PROVIDER_DISPLAY_NAMES, TRAE_PROVIDER_IDS, TRAE_PROVIDER_ID_DISPLAY_NAMES, TRAE_PROVIDER_ROUTES, 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_STATE_DB_FILENAME, 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, adaptLegacyPiAiContext, adaptModernPiAiContext, apply, applyContextBudgets, applyImageSelection, applyReasoningEffort, asVolatile, bridgeTraeSoloStream, buildTraeAgentTaskBody, buildTraeCnHeaders, buildTraeFusionRawChatEnvelope, buildTraeModelDetailRequest, buildTraeRawChatDraft, buildTraeRawChatRuntimeConfig, classifyProbeError, classifyProbeResult, classifyTraeRawChatFailure, configuredAccountsOf, createTraeAdapter, createTraeRawGateway, createTraeShim, creditPosition, decidePiAiGeneration, decodeRawChatChunk, decodeTraeEvent, decryptTraeStorageValue, deriveCatalog, describeIdShape, describeNameShape, describeTraeFailure, discoveredCatalog, displayNameOfTraeProvider, fallbackModelsFor, generationOf, hashTraeRawChatArg, identityHeaders, inject, isEnterpriseAccount, legacyTraeOwnAuthPath, maskUserPath, mergeTraeModelSources, name, nextRegionCreditPosition, nextRegionEnabled, nextRegionSlots, normalizeTraeCredential, normalizeTraeVersionCode, panelOfKey, panelOfProvider, parseObservedModelConfig, parseReasoningCapability, parseTraeAuthValue, parseTraeCachedModel, parseTraeModelExtraConfigLogLine, parseTraeRawChatBehaviorConfig, parseTraeRemoteModel, parseTraeStorageDocument, piAiRuntimeInfo, pickTraeStorageIdentity, prepareSoloBody, probeBody, probeModelsSequentially, probeOneModel, probeTraeRawChatCapability, providerOfPanel, rawCapabilityDiagnostic, rawCapabilityFingerprint, readTraeCachedModel, readTraeCliIdentity, readTraeIdentity, refreshTraeCredential, regionCreditPositionOf, regionEnabled, regionEnabledOf, regionOfCredential, regionOfEdition, regionOfHost, regionOfPanel, regionOfTraeProvider, regionOfTraeStatusUrl, regionOfUserRegion, regionStateOf, registerTraeUsageRoute, resolveTraeIdentity, resolveTraeRawRuntime, routeOfTraeProvider, sanitizeCatalog, settingsNamespaceOf, traeEndpoint, traeInputModalities, traeModelDisplayName, traeOwnAuthPath, traeRawChatExtraInfo, traeStateDatabaseCandidates, traeStorageCandidates, traeWebUsage, traeWindowsAppNames, unwrapVolatile, unwrapVolatileDeep, wireTargetsOf, withContextAdaptation, withTraeRegion };
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-connect-trae",
|
|
3
3
|
"displayName": "DSH Connect Trae",
|
|
4
4
|
"description": "把本机登录的 Trae 模型接入 DeepSeek Harness:国内版与国际版双供应商并行,提供用量/积分概览与每日签到领取。",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "4.0.0",
|
|
6
6
|
"author": "dingminhua",
|
|
7
7
|
"copyright": "Copyright (c) 2026 LaoDing",
|
|
8
8
|
"license": "MIT",
|