dsh-connect-trae 3.6.0 → 3.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/lib/client.js +53 -0
- package/lib/index.d.ts +26 -0
- package/lib/index.js +3 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.6.1 (2026-10-16)
|
|
4
|
+
|
|
5
|
+
> **「自动续期」不再是一句没有期限的承诺。** 卡片现在会告诉你**刷新令牌**何时到期 ——
|
|
6
|
+
> 在那之前访问令牌会自动续期,在那之后自动续期就彻底停了,必须重新登录一次 Trae。
|
|
7
|
+
|
|
8
|
+
### 修复
|
|
9
|
+
|
|
10
|
+
- **刷新令牌的到期时间对用户完全不可见(issue #37 追问引出)**:
|
|
11
|
+
- **问题**:卡片一直只写「访问令牌 {时间} 过期(自动续期)」。这句话是对的,
|
|
12
|
+
而且一直对 —— 直到**刷新令牌**到期的那个瞬间,自动续期就永久停止了。
|
|
13
|
+
一个读了几个月「自动续期」的用户,没有任何理由预期这件事。
|
|
14
|
+
- **根因**:刷新令牌的到期时间**被解析了、也被存下来了,但从来没送到浏览器** ——
|
|
15
|
+
状态接口里根本没有这个字段,所以卡片无从显示。
|
|
16
|
+
- **现在**:状态接口暴露刷新令牌到期时间,卡片**按账号**在临近到期时给出一行提示:
|
|
17
|
+
- 剩余 **30 天**以内 → 提醒(警告色),写明**到期时间**与**还剩几天**;
|
|
18
|
+
- **已到期** → 明确说明「自动续期已停止」,并给出恢复方式(重新登录一次 Trae);
|
|
19
|
+
- **还很久 / 没有该字段**(CLI 凭据本身不带)→ **不显示任何东西**。
|
|
20
|
+
健康的令牌不该在每一行都占一行文字,伪造的期限比不显示更糟。
|
|
21
|
+
|
|
22
|
+
> 这条不是理论问题:我们在排查 `#38` 时**亲身遇到过** —— 某区域插件自存的凭据访问令牌已过期,
|
|
23
|
+
刷新也返回 401,而卡片上一直写着「自动续期」。当时只能靠人肉发现。
|
|
24
|
+
|
|
25
|
+
### 测试
|
|
26
|
+
|
|
27
|
+
- 新增 5 例覆盖三种状态与两个边界(健康不显示、临近显示截止日与剩余天数、已过期显示错误态、
|
|
28
|
+
无该字段不伪造、最后一天仍算「未过期」)。
|
|
29
|
+
- **变异验证**:去掉 30 天提醒窗口 → 2 例变红;在缺少字段时伪造一个期限 → 1 例变红。
|
|
30
|
+
- 全仓 **660** 例测试通过,类型检查通过。
|
|
31
|
+
|
|
3
32
|
## 3.6.0 (2026-10-16)
|
|
4
33
|
|
|
5
34
|
> **多个账号可以并存了。** 本版新增**账号导入 / 导出**:不再需要每次把某台机器的登录凭据
|
package/lib/client.js
CHANGED
|
@@ -335,6 +335,13 @@ window.__ModuleLoader__.load({
|
|
|
335
335
|
.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)}
|
|
336
336
|
.dsm-trae-usage-account-copy{display:flex;flex-direction:column;gap:3px;min-width:0}
|
|
337
337
|
.dsm-trae-usage-expiry{padding-left:19px;color:var(--dsw-alias-label-tertiary,#9aa0a8);font-size:12px;line-height:18px}
|
|
338
|
+
/* Refresh-token countdown (issue #37 follow-up). Sits under the access-token
|
|
339
|
+
line and aligns with it, so the two read as one stack: "token renews" then
|
|
340
|
+
"but this is when renewal itself ends". Only rendered when it is close or
|
|
341
|
+
already past, so a healthy account is unchanged. */
|
|
342
|
+
.dsm-trae-refresh-warn{padding-left:19px;font-size:12px;line-height:18px}
|
|
343
|
+
.dsm-trae-refresh-warn-soon{color:var(--dsw-alias-state-warn-primary,#b45309)}
|
|
344
|
+
.dsm-trae-refresh-warn-lapsed{color:var(--dsw-alias-state-error-primary,#dc2626)}
|
|
338
345
|
.dsm-trae-account-picker{display:block}
|
|
339
346
|
/* The 璁剧疆 block: label + hint on the left, control on the right, one save for
|
|
340
347
|
the whole block. It replaces the bare checkbox rows that used to write on
|
|
@@ -1306,6 +1313,40 @@ window.__ModuleLoader__.load({
|
|
|
1306
1313
|
minute: "2-digit"
|
|
1307
1314
|
}).format(new Date(value));
|
|
1308
1315
|
}
|
|
1316
|
+
/** How close a refresh token must be to lapsing before the card warns, in days. */
|
|
1317
|
+
const REFRESH_WARN_DAYS = 30;
|
|
1318
|
+
/**
|
|
1319
|
+
* The refresh-token warning for one account, or undefined when there is nothing
|
|
1320
|
+
* worth saying.
|
|
1321
|
+
*
|
|
1322
|
+
* The access token renews itself every few minutes, so its own expiry answers
|
|
1323
|
+
* "when does this need attention" with "never, until the refresh token lapses".
|
|
1324
|
+
* The refresh token is therefore the only honest countdown on this card, and it
|
|
1325
|
+
* has to be shown BEFORE it runs out: once it lapses the plugin cannot renew at
|
|
1326
|
+
* all and the only recovery is signing in to Trae again, which a user who has
|
|
1327
|
+
* been reading "auto-renewal" for months has no reason to expect.
|
|
1328
|
+
*
|
|
1329
|
+
* Three tones, matching the credit-expiry notice: `lapsed` (renewal already
|
|
1330
|
+
* dead), `soon` (inside the warning window), and nothing otherwise — a healthy
|
|
1331
|
+
* token must not add a line to every account row forever.
|
|
1332
|
+
*/
|
|
1333
|
+
function refreshTokenWarning(expiresAtMs, nowMs, t, formatTime) {
|
|
1334
|
+
if (expiresAtMs === void 0) return void 0;
|
|
1335
|
+
const text = formatTime(expiresAtMs);
|
|
1336
|
+
if (expiresAtMs <= nowMs) return {
|
|
1337
|
+
tone: "lapsed",
|
|
1338
|
+
text: t("row.refreshTokenLapsed", { expiresAt: text })
|
|
1339
|
+
};
|
|
1340
|
+
const days = Math.ceil((expiresAtMs - nowMs) / 864e5);
|
|
1341
|
+
if (days > REFRESH_WARN_DAYS) return void 0;
|
|
1342
|
+
return {
|
|
1343
|
+
tone: "soon",
|
|
1344
|
+
text: t("row.refreshTokenExpiring", {
|
|
1345
|
+
expiresAt: text,
|
|
1346
|
+
days
|
|
1347
|
+
})
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1309
1350
|
function formatCapacity(value, unknown) {
|
|
1310
1351
|
if (value === void 0) return unknown;
|
|
1311
1352
|
if (value >= 1e6 && value % 1e6 === 0) return `${value / 1e6}M`;
|
|
@@ -2575,6 +2616,14 @@ window.__ModuleLoader__.load({
|
|
|
2575
2616
|
sourceLabel(account.source)
|
|
2576
2617
|
]
|
|
2577
2618
|
}),
|
|
2619
|
+
(() => {
|
|
2620
|
+
const warning = refreshTokenWarning(account.refreshExpiresAtMs, Date.now(), t, formatDateTime);
|
|
2621
|
+
return warning === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2622
|
+
className: `dsm-trae-refresh-warn dsm-trae-refresh-warn-${warning.tone}`,
|
|
2623
|
+
role: "status",
|
|
2624
|
+
children: warning.text
|
|
2625
|
+
});
|
|
2626
|
+
})(),
|
|
2578
2627
|
error === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2579
2628
|
className: "dsm-trae-account-note",
|
|
2580
2629
|
role: "alert",
|
|
@@ -3617,6 +3666,8 @@ window.__ModuleLoader__.load({
|
|
|
3617
3666
|
"row.sourceCli": "Trae CLI",
|
|
3618
3667
|
"row.signedIn": "Signed in: {accountName}",
|
|
3619
3668
|
"row.tokenExpiry": "Access token expires {expiresAt} (auto-renewal)",
|
|
3669
|
+
"row.refreshTokenExpiring": "Sign in to Trae again by {expiresAt} ({days} day(s) left) — automatic renewal stops after that.",
|
|
3670
|
+
"row.refreshTokenLapsed": "Automatic renewal has stopped: the Trae sign-in expired {expiresAt}. Sign in to Trae again to restore it.",
|
|
3620
3671
|
"row.requestFailed": "Request failed",
|
|
3621
3672
|
"row.creditsAvailableLabel": "Available",
|
|
3622
3673
|
"row.workCreditsLabel": "Work credits",
|
|
@@ -3783,6 +3834,8 @@ window.__ModuleLoader__.load({
|
|
|
3783
3834
|
"row.sourceCli": "Trae CLI",
|
|
3784
3835
|
"row.signedIn": "已登录:{accountName}",
|
|
3785
3836
|
"row.tokenExpiry": "访问令牌 {expiresAt} 过期(自动续期)",
|
|
3837
|
+
"row.refreshTokenExpiring": "请在 {expiresAt} 前重新登录一次 Trae(还剩 {days} 天),此后将无法自动续期。",
|
|
3838
|
+
"row.refreshTokenLapsed": "自动续期已停止:Trae 登录凭据已于 {expiresAt} 过期。请重新登录一次 Trae 以恢复。",
|
|
3786
3839
|
"row.requestFailed": "请求失败",
|
|
3787
3840
|
"row.creditsAvailableLabel": "可用",
|
|
3788
3841
|
"row.workCreditsLabel": "Work 积分",
|
package/lib/index.d.ts
CHANGED
|
@@ -590,6 +590,13 @@ interface TraeAccountChoice {
|
|
|
590
590
|
region: TraeRegion;
|
|
591
591
|
source: 'desktop' | 'dsh' | 'cli';
|
|
592
592
|
tokenExpiresAtMs: number;
|
|
593
|
+
/**
|
|
594
|
+
* When this account's refresh token lapses, when the credential carries one.
|
|
595
|
+
* The access token above renews itself automatically until this expires; see
|
|
596
|
+
* the mirror field on `TraeWebAccount` in status-paths.ts for why the card
|
|
597
|
+
* needs it.
|
|
598
|
+
*/
|
|
599
|
+
refreshExpiresAtMs?: number;
|
|
593
600
|
selected: boolean;
|
|
594
601
|
}
|
|
595
602
|
/**
|
|
@@ -2002,6 +2009,18 @@ interface TraeWebAccount {
|
|
|
2002
2009
|
region: TraeRegion;
|
|
2003
2010
|
source: 'desktop' | 'dsh' | 'cli';
|
|
2004
2011
|
tokenExpiresAtMs: number;
|
|
2012
|
+
/**
|
|
2013
|
+
* When this account's REFRESH token expires, when the credential carries one.
|
|
2014
|
+
*
|
|
2015
|
+
* The access token above is renewed automatically — the plugin refreshes it
|
|
2016
|
+
* five minutes before it lapses — so by itself it answers "when does this
|
|
2017
|
+
* need attention", which is "never, while the refresh token lives". The
|
|
2018
|
+
* refresh token is the real horizon: once IT expires the automatic renewal
|
|
2019
|
+
* stops working and the user must sign in to Trae again. Before this field the
|
|
2020
|
+
* card could only say "auto-renewal", which stays true right up to the moment
|
|
2021
|
+
* it silently stops being true.
|
|
2022
|
+
*/
|
|
2023
|
+
refreshExpiresAtMs?: number;
|
|
2005
2024
|
selected: boolean;
|
|
2006
2025
|
}
|
|
2007
2026
|
/**
|
|
@@ -2228,6 +2247,13 @@ type TraeWebUsage = {
|
|
|
2228
2247
|
accountId: string;
|
|
2229
2248
|
accountName: string;
|
|
2230
2249
|
tokenExpiresAtMs: number;
|
|
2250
|
+
/**
|
|
2251
|
+
* When the BOUND account's refresh token expires (absent when the credential
|
|
2252
|
+
* carries none). See {@link TraeWebAccount.refreshExpiresAtMs} — this is the
|
|
2253
|
+
* horizon that actually matters, because the access token above renews
|
|
2254
|
+
* itself until this one lapses.
|
|
2255
|
+
*/
|
|
2256
|
+
refreshExpiresAtMs?: number;
|
|
2231
2257
|
/** Which per-region model directory and selection this account owns. */
|
|
2232
2258
|
region: TraeRegion;
|
|
2233
2259
|
/** Whether this region's provider is currently offered to DSH. */
|
package/lib/index.js
CHANGED
|
@@ -1867,6 +1867,7 @@ var TraeCredentialStore = class {
|
|
|
1867
1867
|
region: regionOfCredential(live),
|
|
1868
1868
|
source: live.source,
|
|
1869
1869
|
tokenExpiresAtMs: live.expiresAtMs,
|
|
1870
|
+
...live.refreshExpiresAtMs === void 0 ? {} : { refreshExpiresAtMs: live.refreshExpiresAtMs },
|
|
1870
1871
|
selected: false
|
|
1871
1872
|
};
|
|
1872
1873
|
}
|
|
@@ -1881,6 +1882,7 @@ var TraeCredentialStore = class {
|
|
|
1881
1882
|
region: regionOfCredential(credential),
|
|
1882
1883
|
source: credential.source,
|
|
1883
1884
|
tokenExpiresAtMs: credential.expiresAtMs,
|
|
1885
|
+
...credential.refreshExpiresAtMs === void 0 ? {} : { refreshExpiresAtMs: credential.refreshExpiresAtMs },
|
|
1884
1886
|
selected: selectedExists ? traeAccountId(credential) === this.accountId : credential === defaultSelected
|
|
1885
1887
|
}));
|
|
1886
1888
|
}
|
|
@@ -5484,6 +5486,7 @@ async function traeWebUsage(deps, region) {
|
|
|
5484
5486
|
accountId: accounts.find((item) => item.selected)?.id ?? "",
|
|
5485
5487
|
accountName: credential.accountName ?? credential.userId,
|
|
5486
5488
|
tokenExpiresAtMs: credential.expiresAtMs,
|
|
5489
|
+
...credential.refreshExpiresAtMs === void 0 ? {} : { refreshExpiresAtMs: credential.refreshExpiresAtMs },
|
|
5487
5490
|
region,
|
|
5488
5491
|
enabled,
|
|
5489
5492
|
accounts,
|
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": "3.6.
|
|
5
|
+
"version": "3.6.1",
|
|
6
6
|
"author": "dingminhua",
|
|
7
7
|
"copyright": "Copyright (c) 2026 LaoDing",
|
|
8
8
|
"license": "MIT",
|