dsh-connect-trae 1.0.0 → 1.0.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 CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 1.0.1 (2026-08-31)
6
+
7
+ ### Changes
8
+
9
+ - 将 Cordis 插件运行 ID 从 `llm-trae` 统一调整为 `dsh-connect-trae`,与 npm 包名保持一致。已有 1.0.0 本地安装升级后如保留旧配置条目,需移除旧的 `llm-trae` 实例,避免重复加载。
10
+
11
+ ### Windows support
12
+
13
+ - `src/identity.ts` 现按平台读取 `product.json`:macOS 保持原路径,新增 Windows 路径 `<LOCALAPPDATA>\Programs\<AppName>\resources\app\product.json`(缺失时回退 `<home>\AppData\Local`),Windows 上 `appVersion` 不再恒为 `undefined`,会随请求头发送 `x-app-version` / `x-ide-version`。
14
+ - `src/identity.ts` 的 `osVersion` 在 Windows 上由 `win32 <release>` 规范化为 `Windows <release>`。
15
+ - `src/identity.ts` / `src/model-cache.ts` 路径推导参数化(`platform` / `home` / `env` 可选注入,默认取真实环境),便于跨平台测试;所有平台判断统一使用同一平台源,避免 `x-device-type` 与 `x-os-version` 自相矛盾。
16
+ - `src/model-cache.ts` 的 `state.vscdb` 路径按平台推导;该模块依赖 `sqlite3` 命令行,Windows 默认未安装,Raw Chat 探测会失败并安全回退(Raw Chat 默认关闭,不影响主流程),详见 README「Windows 说明」。
17
+ - 新增 Windows / Linux / `sg` / `solo-sg` 的 identity 测试用例;修复 `tests/identity.spec.ts` 中依赖测试机平台类型的断言(现显式注入平台,Windows 真机与 CI 均可通过)。
18
+
3
19
  ## 1.0.0 (2026-08-30)
4
20
 
5
21
  ### Features
package/README.en.md CHANGED
@@ -1,16 +1,29 @@
1
- # dsh-connect-trae
1
+ <p align="center">
2
+ <img src="docs/assets/dsh-connect-trae-usage-card.png" width="860" alt="dsh-connect-trae settings panel" />
3
+ </p>
2
4
 
3
- [English](README.en.md) | 中文
5
+ <h1 align="center">dsh-connect-trae</h1>
4
6
 
5
- A [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) bundle plugin that connects locally signed-in Trae CN models to the DSH model picker. Trae generates structured tool calls while DSH executes its own local tools, with a read-only Work/general credits and model-management panel.
7
+ <p align="center"><b>Connect locally signed-in Trae models to DeepSeek Harness with local DSH tools and a read-only credits overview.</b></p>
6
8
 
7
- ## Preview
9
+ <p align="center">
10
+ <a href="README.md">中文</a> ·
11
+ <a href="#install">Install</a> ·
12
+ <a href="#how-it-works">How it works</a> ·
13
+ <a href="CHANGELOG.md">Changelog</a> ·
14
+ <a href="https://github.com/dingminhua/dsh-connect-trae/issues">Issues</a>
15
+ </p>
8
16
 
9
17
  <p align="center">
10
- <img src="docs/assets/dsh-connect-trae-usage-card.png" alt="DSH Connect Trae plugin card showing account selection, Work and general credits, and model management" width="900">
18
+ <a href="https://www.npmjs.com/package/dsh-connect-trae"><img src="https://img.shields.io/npm/v/dsh-connect-trae?style=flat-square&label=npm&color=cb3837" alt="npm version"></a>
19
+ <a href="https://www.npmjs.com/package/dsh-connect-trae"><img src="https://img.shields.io/npm/dm/dsh-connect-trae?style=flat-square&label=downloads" alt="npm downloads"></a>
20
+ <a href="https://github.com/dingminhua/dsh-connect-trae/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/dingminhua/dsh-connect-trae/ci.yml?branch=main&style=flat-square&label=tests" alt="test status"></a>
21
+ <a href="LICENSE"><img src="https://img.shields.io/github/license/dingminhua/dsh-connect-trae?style=flat-square" alt="MIT license"></a>
22
+ <a href="https://github.com/dingminhua/dsh-connect-trae/stargazers"><img src="https://img.shields.io/github/stars/dingminhua/dsh-connect-trae?style=flat-square" alt="GitHub stars"></a>
23
+ <a href="https://dshfind.com/plugins/dingminhua/dsh-connect-trae"><img src="https://dshfind.com/api/badge/dingminhua/dsh-connect-trae" alt="dshfind plugin"></a>
11
24
  </p>
12
25
 
13
- <p align="center"><sub>Settings → Plugins → DSH Connect Trae: switch local Trae accounts, view Work/general credits, and manage the Trae models enabled in DSH.</sub></p>
26
+ A [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) bundle plugin that connects locally signed-in Trae CN models to the DSH model picker. Trae generates structured tool calls while DSH executes its own local tools, with a read-only Work/general credits and model-management panel.
14
27
 
15
28
  ## Features
16
29
 
@@ -50,6 +63,13 @@ npm install dsh-connect-trae
50
63
 
51
64
  Restart the DSH process after install/update/uninstall.
52
65
 
66
+ ## Windows notes
67
+
68
+ - **Account data directory**: the plugin reads `%APPDATA%\Trae CN` / `%APPDATA%\TRAE SOLO CN` → `User\globalStorage\storage.json` (unrelated to the install directory). The folder names match macOS, so no extra config is needed; if the name does not match, point the `authFile` + `edition` plugin options at the exact path.
69
+ - **App version headers**: the plugin reads `appVersion` from `<LOCALAPPDATA>\Programs\<AppName>\resources\app\product.json` and sends `x-app-version` / `x-ide-version`; if unreadable, those headers are omitted (same as the previous behavior).
70
+ - **Raw Chat probing (known limitation)**: `model-cache` depends on the `sqlite3` command-line tool, which is not installed by default on Windows; Raw Chat capability probing fails and falls back safely. Raw Chat is off by default and does not affect the main flow.
71
+ - See `docs/WINDOWS_TOKEN_PROBE.md` for troubleshooting.
72
+
53
73
  ## Development
54
74
 
55
75
  ```sh
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <b>dsh-connect-trae</b>
2
+ <img src="docs/assets/dsh-connect-trae-usage-card.png" width="860" alt="dsh-connect-trae settings panel" />
3
3
  </p>
4
4
 
5
5
  <h1 align="center">dsh-connect-trae</h1>
@@ -16,19 +16,15 @@
16
16
 
17
17
  <p align="center">
18
18
  <a href="https://www.npmjs.com/package/dsh-connect-trae"><img src="https://img.shields.io/npm/v/dsh-connect-trae?style=flat-square&label=npm&color=cb3837" alt="npm version"></a>
19
+ <a href="https://www.npmjs.com/package/dsh-connect-trae"><img src="https://img.shields.io/npm/dm/dsh-connect-trae?style=flat-square&label=downloads" alt="npm downloads"></a>
20
+ <a href="https://github.com/dingminhua/dsh-connect-trae/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/dingminhua/dsh-connect-trae/ci.yml?branch=main&style=flat-square&label=tests" alt="test status"></a>
19
21
  <a href="LICENSE"><img src="https://img.shields.io/github/license/dingminhua/dsh-connect-trae?style=flat-square" alt="MIT license"></a>
22
+ <a href="https://github.com/dingminhua/dsh-connect-trae/stargazers"><img src="https://img.shields.io/github/stars/dingminhua/dsh-connect-trae?style=flat-square" alt="GitHub stars"></a>
23
+ <a href="https://dshfind.com/plugins/dingminhua/dsh-connect-trae"><img src="https://dshfind.com/api/badge/dingminhua/dsh-connect-trae" alt="dshfind plugin"></a>
20
24
  </p>
21
25
 
22
26
  一个独立的 [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) bundle 插件。它把本机已登录的 Trae 中国区账号接到 DSH 的模型选择器:模型负责生成结构化工具调用,`bash` / `read` / `write` / `edit` 等工具由 DSH 本地执行;同时提供**只读**的 Work/通用积分与模型管理界面。
23
27
 
24
- ## 效果预览
25
-
26
- <p align="center">
27
- <img src="docs/assets/dsh-connect-trae-usage-card.png" alt="DSH Connect Trae 插件卡片:Trae 账号、Work 与通用积分以及模型管理" width="900">
28
- </p>
29
-
30
- <p align="center"><sub>设置 → 插件配置 → DSH Connect Trae:切换本机 Trae 账号、查看 Work/通用积分,并管理 DSH 中启用的 Trae 模型。</sub></p>
31
-
32
28
  ## 功能特性
33
29
 
34
30
  - **Trae 模型接入** —— 把本机登录的 Trae 模型注册为 DSH 的 `trae` provider,模型选择器出现 `DeepSeek-V4-Flash`、`DeepSeek-V4-Pro` 等。
@@ -69,6 +65,13 @@ npm install dsh-connect-trae
69
65
 
70
66
  安装、更新或卸载 bundle 后,需要重启对应的 DSH 进程。
71
67
 
68
+ ## Windows 说明
69
+
70
+ - **账号数据目录**:插件读取 `%APPDATA%\Trae CN` / `%APPDATA%\TRAE SOLO CN` 下的 `User\globalStorage\storage.json`(与安装目录无关)。目录名与 macOS 一致,无需额外配置;若目录名对不上,可用插件配置项 `authFile` + `edition` 直接指定完整路径。
71
+ - **应用版本头**:插件从安装目录 `<LOCALAPPDATA>\Programs\<AppName>\resources\app\product.json` 读取 `appVersion`,随请求发送 `x-app-version` / `x-ide-version`;读不到时这些头不发送(与旧版行为一致)。
72
+ - **Raw Chat 探测(已知限制)**:`model-cache` 依赖 `sqlite3` 命令行,Windows 默认未安装,Raw Chat 能力探测会失败并安全回退。Raw Chat 默认关闭,不影响主流程。
73
+ - 排查指引见 `docs/WINDOWS_TOKEN_PROBE.md`。
74
+
72
75
  ## 开发
73
76
 
74
77
  ```sh
package/cordis.patch.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  # Register the Trae provider without changing the profile's default model.
2
2
  - insert:
3
- - id: llm-trae
3
+ - id: dsh-connect-trae
4
4
  name: dsh-connect-trae
package/lib/index.d.ts CHANGED
@@ -213,8 +213,16 @@ interface TraeIdentity {
213
213
  osVersion?: string;
214
214
  platform: NodeJS.Platform;
215
215
  }
216
+ interface TraeIdentityReadOptions {
217
+ /** Platform override for testing; defaults to process.platform. */
218
+ platform?: NodeJS.Platform;
219
+ /** Home-directory override for testing; defaults to homedir(). */
220
+ home?: string;
221
+ /** Environment override for testing; defaults to process.env. */
222
+ env?: NodeJS.ProcessEnv;
223
+ }
216
224
  /** Read stable identity from Trae-owned files without generating impersonated IDs. */
217
- declare function readTraeIdentity(candidate: TraeStorageCandidate): Promise<TraeIdentity>;
225
+ declare function readTraeIdentity(candidate: TraeStorageCandidate, options?: TraeIdentityReadOptions): Promise<TraeIdentity>;
218
226
  /** Headers derived from actual persisted identity, never a new random identity per request. */
219
227
  declare function identityHeaders(identity: TraeIdentity): Record<string, string>;
220
228
  //#endregion
@@ -244,8 +252,20 @@ interface TraeCachedModelConfig {
244
252
  }
245
253
  /** Parse a safe subset of one cached model entry; credentials and endpoints are intentionally omitted. */
246
254
  declare function parseTraeCachedModel(value: unknown): TraeCachedModelConfig | undefined;
247
- /** Read Trae's own current user's model map via sqlite3 without exposing secrets. */
248
- declare function readTraeCachedModel(functionName: string, modelName: string, userId: string): Promise<TraeCachedModelConfig | undefined>;
255
+ interface TraeCachedModelReadOptions {
256
+ /** Platform override for testing; defaults to process.platform. */
257
+ platform?: NodeJS.Platform;
258
+ /** Home-directory override for testing; defaults to homedir(). */
259
+ home?: string;
260
+ /** Environment override for testing; defaults to process.env. */
261
+ env?: NodeJS.ProcessEnv;
262
+ }
263
+ /**
264
+ * Read Trae's own current user's model map via sqlite3 without exposing
265
+ * secrets. The sqlite3 command line is a macOS prerequisite; on Windows it is
266
+ * typically absent, so the call fails and callers fall back gracefully.
267
+ */
268
+ declare function readTraeCachedModel(functionName: string, modelName: string, userId: string, options?: TraeCachedModelReadOptions): Promise<TraeCachedModelConfig | undefined>;
249
269
  //#endregion
250
270
  //#region src/model-extra-config.d.ts
251
271
  interface TraeRawChatBehaviorConfig {
@@ -952,7 +972,7 @@ declare function traeWebUsage(deps: TraeUsageRouteOptions): Promise<TraeWebUsage
952
972
  declare function registerTraeUsageRoute(ctx: Context, deps: TraeUsageRouteOptions): void;
953
973
  //#endregion
954
974
  //#region src/index.d.ts
955
- declare const name = "llm-trae";
975
+ declare const name = "dsh-connect-trae";
956
976
  declare const inject: string[];
957
977
  declare const TRAE_SETTINGS_NS: import("@deepseek-ai/dsh-settings").SettingsNamespace;
958
978
  interface Config {
package/lib/index.js CHANGED
@@ -834,7 +834,10 @@ function deviceCenterId(storage) {
834
834
  return ids.length === 1 ? ids[0] : void 0;
835
835
  }
836
836
  /** Read stable identity from Trae-owned files without generating impersonated IDs. */
837
- async function readTraeIdentity(candidate) {
837
+ async function readTraeIdentity(candidate, options = {}) {
838
+ const platform = options.platform ?? process.platform;
839
+ const home = options.home ?? homedir();
840
+ const env = options.env ?? process.env;
838
841
  const storage = JSON.parse(await readFile(candidate.path, "utf8"));
839
842
  const appRoot = dirname(dirname(dirname(candidate.path)));
840
843
  const machineFile = nonEmpty(await readFile(join(appRoot, "machineid"), "utf8").catch(() => ""));
@@ -845,16 +848,24 @@ async function readTraeIdentity(candidate) {
845
848
  if (machineId === void 0) throw new Error(`Trae ${candidate.edition} has no stable machine identity`);
846
849
  const deviceId = dcDevice ?? devDevice ?? createHash("sha256").update(machineId).digest("hex").slice(0, 32);
847
850
  const buildVersion = nonEmpty(storage["iCubeLastVersion"]);
848
- const productPaths = candidate.edition === "cn" || candidate.edition === "solo" ? [candidate.edition === "cn" ? "/Applications/Trae CN.app/Contents/Resources/app/product.json" : "/Applications/TRAE SOLO CN.app/Contents/Resources/app/product.json"] : [];
851
+ const appName = candidate.edition === "cn" ? "Trae CN" : candidate.edition === "solo" ? "TRAE SOLO CN" : void 0;
852
+ const productPaths = [];
853
+ if (appName !== void 0 && (platform === "darwin" || platform === "win32")) {
854
+ if (platform === "darwin") productPaths.push(join("/Applications", `${appName}.app`, "Contents", "Resources", "app", "product.json"));
855
+ else {
856
+ const localRoots = [env.LOCALAPPDATA, join(home, "AppData", "Local")].filter((value) => typeof value === "string" && value !== "").filter((value, index, all) => all.indexOf(value) === index);
857
+ for (const root of localRoots) productPaths.push(join(root, "Programs", appName, "resources", "app", "product.json"));
858
+ }
859
+ }
849
860
  let product = {};
850
861
  for (const path of productPaths) try {
851
862
  product = JSON.parse(await readFile(path, "utf8"));
852
863
  break;
853
864
  } catch {}
854
865
  const appVersion = nonEmpty(product["appVersion"]);
855
- const deviceBrand = process.platform === "darwin" ? nonEmpty(process.env["TRAE_DEVICE_BRAND"]) : void 0;
866
+ const deviceBrand = platform === "darwin" ? nonEmpty(env["TRAE_DEVICE_BRAND"]) : void 0;
856
867
  const deviceCpu = cpus()[0]?.model.split(" ")[0];
857
- const osVersion = `${process.platform === "darwin" ? "macOS" : process.platform} ${release()}`;
868
+ const osVersion = `${platform === "darwin" ? "macOS" : platform === "win32" ? "Windows" : platform} ${release()}`;
858
869
  return {
859
870
  edition: candidate.edition,
860
871
  machineId,
@@ -864,7 +875,7 @@ async function readTraeIdentity(candidate) {
864
875
  ...deviceBrand === void 0 ? {} : { deviceBrand },
865
876
  ...deviceCpu === void 0 ? {} : { deviceCpu },
866
877
  osVersion,
867
- platform: process.platform
878
+ platform
868
879
  };
869
880
  }
870
881
  /** Headers derived from actual persisted identity, never a new random identity per request. */
@@ -2320,9 +2331,16 @@ function parseTraeCachedModel(value) {
2320
2331
  ...typeof raw["model_type"] === "string" ? { modelType: raw["model_type"] } : {}
2321
2332
  };
2322
2333
  }
2323
- /** Read Trae's own current user's model map via sqlite3 without exposing secrets. */
2324
- async function readTraeCachedModel(functionName, modelName, userId) {
2325
- const database = join(homedir(), "Library", "Application Support", "Trae CN", "User", "globalStorage", "state.vscdb");
2334
+ /**
2335
+ * Read Trae's own current user's model map via sqlite3 without exposing
2336
+ * secrets. The sqlite3 command line is a macOS prerequisite; on Windows it is
2337
+ * typically absent, so the call fails and callers fall back gracefully.
2338
+ */
2339
+ async function readTraeCachedModel(functionName, modelName, userId, options = {}) {
2340
+ const platform = options.platform ?? process.platform;
2341
+ const home = options.home ?? homedir();
2342
+ const env = options.env ?? process.env;
2343
+ const database = platform === "win32" ? join(env.APPDATA ?? join(home, "AppData", "Roaming"), "Trae CN", "User", "globalStorage", "state.vscdb") : join(home, "Library", "Application Support", "Trae CN", "User", "globalStorage", "state.vscdb");
2326
2344
  const key = `${userId}_AI.agent.model.model_list_map`;
2327
2345
  const sql = `select value from ItemTable where key=${JSON.stringify(key)} limit 1;`;
2328
2346
  const { stdout } = await execFileAsync("sqlite3", [database, sql], { maxBuffer: 8388608 });
@@ -2915,7 +2933,7 @@ var UnconfiguredTraeUpstreamClient = class {
2915
2933
  };
2916
2934
  //#endregion
2917
2935
  //#region src/index.ts
2918
- const name = "llm-trae";
2936
+ const name = "dsh-connect-trae";
2919
2937
  const inject = ["llm"];
2920
2938
  const TRAE_SETTINGS_NS = settingsNamespace("trae");
2921
2939
  const modelConfig = z.object({
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "dsh-connect-trae",
3
3
  "displayName": "DSH Connect Trae",
4
4
  "description": "Connect locally signed-in Trae models to DeepSeek Harness, plus a read-only usage/credits summary.",
5
- "version": "1.0.0",
5
+ "version": "1.0.1",
6
6
  "author": "dingminhua",
7
7
  "copyright": "Copyright (c) 2026 LaoDing",
8
8
  "license": "MIT",