dsh-connect-trae 2.8.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.d.ts CHANGED
@@ -1689,15 +1689,6 @@ interface TraeWebAccount {
1689
1689
  tokenExpiresAtMs: number;
1690
1690
  selected: boolean;
1691
1691
  }
1692
- /**
1693
- * Another account on this machine that still has credit, surfaced only while
1694
- * the BOUND account is exhausted (issue #25).
1695
- *
1696
- * The account switch is persisted, so a user whose bound account ran out sees
1697
- * a permanently zeroed card with no explanation. This carries just enough to
1698
- * name the alternative in the UI — identity plus the two CN credit buckets —
1699
- * and is only ever produced by a read-only upstream call.
1700
- */
1701
1692
  interface TraeWebCreditAlternative {
1702
1693
  id: string;
1703
1694
  accountName: string;
@@ -2022,6 +2013,23 @@ interface Config {
2022
2013
  * its own slot; tokens remain outside settings.
2023
2014
  */
2024
2015
  accounts?: Partial<Record<TraeRegion, string>>;
2016
+ /**
2017
+ * Show the credit readout in the composer tool row: `Trae CN · <n>`, rendered
2018
+ * ONLY while the session's selected model belongs to this plugin, clickable
2019
+ * for the account/balances panel.
2020
+ *
2021
+ * This field first gated the sidebar credit line (issue #26, contributed by
2022
+ * JiewiW); when that line was removed in 2.13.0 the composer readout replaced
2023
+ * it and rendered unconditionally; 2.13.3 restores the switch, because the
2024
+ * user went looking for it and a readout inside the SHARED composer row
2025
+ * deserves the same opt-in the sidebar line had. The field is REUSED so an
2026
+ * existing `true` keeps the readout on across the upgrade.
2027
+ *
2028
+ * OFF by default. The card's checkbox is the only writer, so exactly one path
2029
+ * can change it. Declared volatile alongside `accounts`/`regions` so the
2030
+ * settings form can both read and write it.
2031
+ */
2032
+ showPointsInMainUi?: boolean;
2025
2033
  /**
2026
2034
  * Per-region model state, keyed `cn` | `ai`. The CN and international apps
2027
2035
  * expose different rosters, so each keeps its own directory and selection
package/lib/index.js CHANGED
@@ -1266,13 +1266,16 @@ var TraeCredentialStore = class {
1266
1266
  */
1267
1267
  async readAll() {
1268
1268
  const { credentials: desktop } = await this.readDesktopAll();
1269
- const credentials = [...desktop.filter((credential) => this.matchesRegion(credential))];
1270
- for (const own of await this.readOwns()) {
1271
- if (!this.matchesRegion(own)) continue;
1272
- if (credentials.some((credential) => traeAccountId(credential) === traeAccountId(own))) continue;
1273
- credentials.push(own);
1274
- }
1275
- return credentials;
1269
+ const byAccount = /* @__PURE__ */ new Map();
1270
+ const consider = (credential) => {
1271
+ if (!this.matchesRegion(credential)) return;
1272
+ const id = traeAccountId(credential);
1273
+ const incumbent = byAccount.get(id);
1274
+ if (incumbent === void 0 || credential.expiresAtMs > incumbent.expiresAtMs) byAccount.set(id, credential);
1275
+ };
1276
+ for (const credential of desktop) consider(credential);
1277
+ for (const own of await this.readOwns()) consider(own);
1278
+ return [...byAccount.values()];
1276
1279
  }
1277
1280
  /**
1278
1281
  * Which paths were tried and why each one failed. Read-only and token-free:
@@ -3939,6 +3942,17 @@ const TRAE_MODELS_TEST_PATH = "/plugins/dsh-connect-trae/models/test";
3939
3942
  /** Plugin-owned local account rescan endpoint. */
3940
3943
  const TRAE_ACCOUNTS_REFRESH_PATH = "/plugins/dsh-connect-trae/accounts/refresh";
3941
3944
  /**
3945
+ * General balance of EVERY account in a region, for the composer panel's table.
3946
+ *
3947
+ * Separate from the usage route on purpose: the usage document backs the
3948
+ * 5-minute readout, and multiplying its upstream reads by the account count
3949
+ * would charge a healthy account for something it never looks at — the same
3950
+ * principle that keeps `creditAlternatives` firing only when a balance hits
3951
+ * zero. This one is paid when the panel is actually opened, which is the only
3952
+ * place the table is shown.
3953
+ */
3954
+ const TRAE_ACCOUNT_CREDITS_PATH = "/plugins/dsh-connect-trae/account-credits";
3955
+ /**
3942
3956
  * Plugin-owned daily check-in claim endpoint. POST, loopback-only, and the only
3943
3957
  * route in this plugin that changes upstream account state.
3944
3958
  */
@@ -4297,6 +4311,37 @@ async function creditAlternatives(deps, region, accounts) {
4297
4311
  * The region a request addresses, or a 400 answer. Absent parameter means the
4298
4312
  * domestic tab; an unknown value is refused rather than guessed.
4299
4313
  */
4314
+ /**
4315
+ * The general balance of every account in one region.
4316
+ *
4317
+ * One upstream read per account — the same call `creditsOfAccount` already
4318
+ * makes for the exhausted-balance alternatives, widened to every account and to
4319
+ * zero balances. Costs nothing until the composer panel is opened, so the
4320
+ * 5-minute readout never pays for it (see {@link TRAE_ACCOUNT_CREDITS_PATH}).
4321
+ *
4322
+ * An account whose read fails or is unavailable still appears, without a
4323
+ * figure: the table lists how many accounts exist even when one cannot be
4324
+ * quoted, rather than dropping it and making the switch unavailable.
4325
+ */
4326
+ async function traeAccountCredits(deps, region) {
4327
+ const accounts = (await deps.store(region).accounts()).filter((account) => account.region === region);
4328
+ if (accounts.length === 0) return [];
4329
+ return Promise.all(accounts.map(async (account) => {
4330
+ let generalAvailable;
4331
+ if (account.selected) try {
4332
+ generalAvailable = toCredits(await deps.client(region).snapshot()).generalAvailable;
4333
+ } catch {}
4334
+ else if (deps.creditsOfAccount !== void 0) try {
4335
+ generalAvailable = (await deps.creditsOfAccount(region, account.id))?.generalAvailable;
4336
+ } catch {}
4337
+ return {
4338
+ id: account.id,
4339
+ accountName: account.accountName,
4340
+ selected: account.selected,
4341
+ ...generalAvailable === void 0 ? {} : { generalAvailable }
4342
+ };
4343
+ }));
4344
+ }
4300
4345
  function requestRegion(req, res) {
4301
4346
  const region = regionOfTraeStatusUrl(req.url ?? "/");
4302
4347
  if (region === void 0) {
@@ -4329,6 +4374,27 @@ function registerTraeUsageRoute(ctx, deps) {
4329
4374
  }
4330
4375
  }
4331
4376
  });
4377
+ /**
4378
+ * General balance of every account in the region — read only when the
4379
+ * composer panel is opened. One upstream read per account, paid once per
4380
+ * open rather than on the 5-minute cycle (see
4381
+ * {@link TRAE_ACCOUNT_CREDITS_PATH}).
4382
+ */
4383
+ const disposeAccountCredits = ctx.webServer.register({
4384
+ kind: "exact",
4385
+ path: TRAE_ACCOUNT_CREDITS_PATH,
4386
+ handler: async (req, res) => {
4387
+ if (req.method !== "GET") return json(res, 405, { error: "method not allowed" });
4388
+ if (!loopbackOrigin(req)) return json(res, 403, { error: "origin-not-trusted" });
4389
+ const region = requestRegion(req, res);
4390
+ if (region === void 0) return;
4391
+ try {
4392
+ json(res, 200, { accounts: await traeAccountCredits(deps, region) });
4393
+ } catch (error) {
4394
+ json(res, 500, { error: safeMessage(error) });
4395
+ }
4396
+ }
4397
+ });
4332
4398
  const disposeAccounts = ctx.webServer.register({
4333
4399
  kind: "exact",
4334
4400
  path: TRAE_ACCOUNTS_REFRESH_PATH,
@@ -4462,6 +4528,7 @@ function registerTraeUsageRoute(ctx, deps) {
4462
4528
  disposeRefresh();
4463
4529
  disposeModelsTest();
4464
4530
  disposeAccounts();
4531
+ disposeAccountCredits();
4465
4532
  disposeUsage();
4466
4533
  };
4467
4534
  }, "dsh-connect-trae: Web usage route");
@@ -4769,6 +4836,7 @@ const Config = z.object({
4769
4836
  "solo-sg"
4770
4837
  ]).default("auto").description("Trae edition hint")),
4771
4838
  accountId: z.string().description("Deprecated: pre-split account selector, attributed to its own region"),
4839
+ showPointsInMainUi: asVolatile(z.boolean().default(false).description("在输入框工具栏显示积分读数「Trae CN · <数值>」(仅当选中的模型属于本插件时出现,可点击查看账号与积分明细)")),
4772
4840
  accounts: asVolatile(accountSelectionConfig.description("Per-region account selections, keyed cn | ai")),
4773
4841
  regions: asVolatile(z.dict(regionStateConfig).default({}).description("Per-region model directory and selection, keyed cn | ai")),
4774
4842
  lastCatalog: z.array(modelConfig).description("Deprecated: pre-region-split CN model directory"),
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": "2.8.0",
5
+ "version": "3.0.0",
6
6
  "author": "dingminhua",
7
7
  "copyright": "Copyright (c) 2026 LaoDing",
8
8
  "license": "MIT",
@@ -109,6 +109,7 @@
109
109
  "@testing-library/react": "^16.3.3",
110
110
  "@types/node": "^22.20.0",
111
111
  "@types/react": "~18.3.31",
112
+ "@types/react-dom": "^18.3.7",
112
113
  "@types/semver": "^7.7.0",
113
114
  "jsdom": "^25.0.1",
114
115
  "react": "^18.3.1",