@ljwei-stak/dsh-model-router 0.14.0-beta.2 → 0.14.0-beta.3
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/.dsh-plugin/client.js +38 -14
- package/CHANGELOG.md +11 -0
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/package.json +2 -2
package/.dsh-plugin/client.js
CHANGED
|
@@ -11601,7 +11601,7 @@ var xterm_default = `/**
|
|
|
11601
11601
|
`;
|
|
11602
11602
|
|
|
11603
11603
|
// .dsh-plugin/client/host-version.mjs
|
|
11604
|
-
var ROUTER_CLIENT_VERSION = true ? "0.14.0-beta.
|
|
11604
|
+
var ROUTER_CLIENT_VERSION = true ? "0.14.0-beta.3" : "";
|
|
11605
11605
|
var STALE_HOST_MESSAGE = "\u63D2\u4EF6\u540E\u53F0\u7248\u672C\u8F83\u65E7\uFF0C\u8BF7\u5B8C\u5168\u9000\u51FA\u5E76\u91CD\u542F Harness\uFF08\u5305\u62EC\u6258\u76D8\u56FE\u6807\uFF09\u540E\u518D\u4F7F\u7528\u3002";
|
|
11606
11606
|
function isMissingRemoteMethod(message) {
|
|
11607
11607
|
const value = String(message ?? "");
|
|
@@ -11723,6 +11723,7 @@ function parseTerminalStop(value) {
|
|
|
11723
11723
|
const request = plainObject(value, "terminal stop request");
|
|
11724
11724
|
return { sessionId: sessionIdOf(request.sessionId) };
|
|
11725
11725
|
}
|
|
11726
|
+
var isTerminalSessionId = (value) => typeof value === "string" && SESSION_ID.test(value);
|
|
11726
11727
|
|
|
11727
11728
|
// .dsh-plugin/client/cli-terminal-state.mjs
|
|
11728
11729
|
var text3 = (value) => typeof value === "string" ? value.trim() : "";
|
|
@@ -11779,10 +11780,26 @@ var formatDuration = (ms2) => {
|
|
|
11779
11780
|
const minutes = Math.floor(seconds / 60);
|
|
11780
11781
|
return minutes < 60 ? `${minutes} \u5206 ${seconds % 60} \u79D2` : `${Math.floor(minutes / 60)} \u5C0F\u65F6 ${minutes % 60} \u5206`;
|
|
11781
11782
|
};
|
|
11782
|
-
|
|
11783
|
-
if (response?.ok)
|
|
11784
|
-
|
|
11785
|
-
|
|
11783
|
+
function unwrapTerminal(response, fallback) {
|
|
11784
|
+
if (!response?.ok) throw new Error(remoteErrorText(text3(response?.error?.message) || text3(response?.error), fallback));
|
|
11785
|
+
const inner = response.value;
|
|
11786
|
+
if (inner && typeof inner === "object" && typeof inner.ok === "boolean") {
|
|
11787
|
+
if (!inner.ok) throw new Error(remoteErrorText(text3(inner.error?.message) || text3(inner.error), fallback));
|
|
11788
|
+
return inner.value;
|
|
11789
|
+
}
|
|
11790
|
+
return inner;
|
|
11791
|
+
}
|
|
11792
|
+
var unwrap = unwrapTerminal;
|
|
11793
|
+
async function loadTerminalInfo(api) {
|
|
11794
|
+
const info = unwrap(await api.terminalInfo(), "\u65E0\u6CD5\u8BFB\u53D6\u7EC8\u7AEF\u72B6\u6001\u3002");
|
|
11795
|
+
if (!info || typeof info !== "object" || !["pty", "pipe"].includes(info.backend)) throw new Error("\u7EC8\u7AEF\u72B6\u6001\u683C\u5F0F\u65E0\u6548\u3002");
|
|
11796
|
+
return info;
|
|
11797
|
+
}
|
|
11798
|
+
async function startTerminal(api, request) {
|
|
11799
|
+
const session = unwrap(await api.terminalStart({ ...request, confirmed: true }), "\u7EC8\u7AEF\u542F\u52A8\u5931\u8D25\u3002");
|
|
11800
|
+
if (!isTerminalSessionId(session?.sessionId)) throw new Error("\u7EC8\u7AEF\u542F\u52A8\u7ED3\u679C\u7F3A\u5C11\u6709\u6548\u7684\u4F1A\u8BDD ID\u3002");
|
|
11801
|
+
return session;
|
|
11802
|
+
}
|
|
11786
11803
|
var TerminalConnection = class {
|
|
11787
11804
|
constructor({ api, sessionId, onData, onExit, onError, waitMs = 800, retryMs = 1e3, resizeDelayMs = 120, setTimer = setTimeout, clearTimer = clearTimeout }) {
|
|
11788
11805
|
Object.assign(this, { api, sessionId, onData, onExit, onError, waitMs, retryMs, resizeDelayMs });
|
|
@@ -11796,6 +11813,13 @@ var TerminalConnection = class {
|
|
|
11796
11813
|
this.failures = 0;
|
|
11797
11814
|
}
|
|
11798
11815
|
start() {
|
|
11816
|
+
if (!isTerminalSessionId(this.sessionId)) {
|
|
11817
|
+
this.closed = true;
|
|
11818
|
+
this.onError?.(new Error("\u7EC8\u7AEF\u4F1A\u8BDD ID \u65E0\u6548\uFF0C\u65E0\u6CD5\u8BFB\u53D6\u8F93\u51FA\u3002"));
|
|
11819
|
+
this.onExit?.({ endReason: "lost", exitCode: null });
|
|
11820
|
+
this.loop = Promise.resolve();
|
|
11821
|
+
return this.loop;
|
|
11822
|
+
}
|
|
11799
11823
|
this.loop = this.readLoop();
|
|
11800
11824
|
return this.loop;
|
|
11801
11825
|
}
|
|
@@ -11849,7 +11873,9 @@ var TerminalConnection = class {
|
|
|
11849
11873
|
}
|
|
11850
11874
|
}
|
|
11851
11875
|
resize(cols, rows) {
|
|
11852
|
-
if (this.closed) return;
|
|
11876
|
+
if (this.closed || !Number.isFinite(cols) || !Number.isFinite(rows)) return;
|
|
11877
|
+
cols = Math.min(TERMINAL_LIMITS.maxCols, Math.max(TERMINAL_LIMITS.minCols, Math.round(cols)));
|
|
11878
|
+
rows = Math.min(TERMINAL_LIMITS.maxRows, Math.max(TERMINAL_LIMITS.minRows, Math.round(rows)));
|
|
11853
11879
|
if (this.resizeTimer) this.clearTimer(this.resizeTimer);
|
|
11854
11880
|
this.resizeTimer = this.setTimer(() => {
|
|
11855
11881
|
this.resizeTimer = null;
|
|
@@ -11975,10 +12001,9 @@ function CliTerminalCard({ api, health }) {
|
|
|
11975
12001
|
const mounted = import_react4.default.useRef(true);
|
|
11976
12002
|
const loadInfo = import_react4.default.useCallback(async () => {
|
|
11977
12003
|
try {
|
|
11978
|
-
const
|
|
12004
|
+
const value = await loadTerminalInfo(api);
|
|
11979
12005
|
if (!mounted.current) return;
|
|
11980
|
-
|
|
11981
|
-
setInfo({ status: "ready", value: response.value, error: "" });
|
|
12006
|
+
setInfo({ status: "ready", value, error: "" });
|
|
11982
12007
|
} catch (error) {
|
|
11983
12008
|
if (mounted.current) setInfo({ status: "error", value: null, error: errorText(error, "\u65E0\u6CD5\u8BFB\u53D6\u7EC8\u7AEF\u72B6\u6001\u3002") });
|
|
11984
12009
|
}
|
|
@@ -12007,15 +12032,14 @@ function CliTerminalCard({ api, health }) {
|
|
|
12007
12032
|
setStarting(true);
|
|
12008
12033
|
setStartError("");
|
|
12009
12034
|
try {
|
|
12010
|
-
const
|
|
12011
|
-
if (!response?.ok) throw new Error(remoteErrorText(response?.error?.message, "\u7EC8\u7AEF\u542F\u52A8\u5931\u8D25\u3002"));
|
|
12035
|
+
const session = await startTerminal(api, { target: selected.id, mode, cwd: text4(cwd), cols: 100, rows: 30 });
|
|
12012
12036
|
storeCwd(text4(cwd));
|
|
12013
12037
|
if (!mounted.current) {
|
|
12014
|
-
void api.terminalStop({ sessionId:
|
|
12038
|
+
void api.terminalStop({ sessionId: session.sessionId });
|
|
12015
12039
|
return;
|
|
12016
12040
|
}
|
|
12017
|
-
setSessions((previous) => [...previous, { ...
|
|
12018
|
-
setActiveId(
|
|
12041
|
+
setSessions((previous) => [...previous, { ...session, ended: null }]);
|
|
12042
|
+
setActiveId(session.sessionId);
|
|
12019
12043
|
setConfirming(false);
|
|
12020
12044
|
} catch (error) {
|
|
12021
12045
|
if (mounted.current) setStartError(errorText(error, "\u7EC8\u7AEF\u542F\u52A8\u5931\u8D25\u3002"));
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.14.0-beta.3 — 2026-10-03
|
|
4
|
+
|
|
5
|
+
Prerelease under the npm `next` tag only (`latest` stays 0.13.3). **Quit Harness completely (including the tray icon) and start it again after updating**, or the Host keeps running the 0.14.0-beta.2 code.
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- 官方工具终端: a session (e.g. Kimi Code 登录) opened a black terminal and then failed with `typert gateway: modelRouterOfficialTools/terminalRead: wire field "request" failed boundary validation`; the footer read `undefined · undefined`. The Host methods answer with their own `{ ok, value }` envelope and the Typert gateway wraps that again, so the client receives `{ ok: true, value: { ok: true, value: result } }`. The terminal card read only the outer level, so the session id, command and directory were `undefined` and every `terminalRead` carried no `sessionId`, which the strict request codec rejects at the gateway. Host-side start failures (such as a missing CLI) were hidden the same way. The card now unwraps both levels (`unwrapTerminal`, `loadTerminalInfo`, `startTerminal`) and reports inner errors. The schemas were correct, and the gateway does not strip response fields.
|
|
9
|
+
- The client never sends a read or write for an invalid session id, and it clamps resize requests from a collapsed panel into the accepted 10–500 × 3–300 range instead of failing validation.
|
|
10
|
+
|
|
11
|
+
### Tests
|
|
12
|
+
- `tests/helpers/typert-gateway.mjs` copies Harness's gateway boundary (`assertExactArguments`, `decode`, `assertJsonValue`, `encodeRpcResult`, JSON wire). `tests/cli-terminal-gateway.test.mjs` sends the card's real payloads for all six terminal methods through it, through the real `OfficialToolsRemoteService` envelope and the real Host services.
|
|
13
|
+
|
|
3
14
|
## 0.14.0-beta.2 — 2026-10-03
|
|
4
15
|
|
|
5
16
|
Prerelease under the npm `next` tag only (`latest` stays 0.13.3).
|
package/README.md
CHANGED
|
@@ -402,7 +402,7 @@ Claude Code, Codex, MiMo Code, and Grok Build support read-only and approved edi
|
|
|
402
402
|
|
|
403
403
|
The planned Harness model ID is not necessarily the vendor CLI's model name. A saved `cliModel`, or temporary `cliModelsJson` keyed by tool or work-package ID, can provide a known vendor CLI name. A package-specific temporary mapping takes priority over a tool mapping, then the saved profile. ZCode uses its configured default model. Most vendor CLIs do not provide a verifiable actual model ID in their results, so inspect vendor run records to confirm which model and price applied. The plugin's sequential CLI team runner is separate from Harness's built-in Agent Teams lifecycle.
|
|
404
404
|
|
|
405
|
-
### Interactive terminal (官方工具终端, 0.14.0-beta.
|
|
405
|
+
### Interactive terminal (官方工具终端, 0.14.0-beta.3, npm `next`)
|
|
406
406
|
|
|
407
407
|
The workbench has an **官方工具终端** card below the tool health check. It runs your system shell (PowerShell on Windows, your login `$SHELL` elsewhere) or one installed official CLI (`codex`, `claude`, `kimi`, `mcode`, `mimo`, `grok`, `gemini`) interactively, in tabs, with live output: multi-turn conversations, full-screen TUIs, and logins such as `codex login`, `claude auth login`, `kimi login`, `mcode login`, `mimo auth login`, `grok login` (choose **登录**).
|
|
408
408
|
|
package/README.zh.md
CHANGED
|
@@ -391,7 +391,7 @@ Claude 与 Codex 若本机已有经核验的签名入口,仍优先走原有沙
|
|
|
391
391
|
|
|
392
392
|
Harness 目录中的模型 ID 未必是厂商 CLI 接受的名字。逐模型设置可填写 `cliModel`;团队执行时临时的“工作包映射 > 工具映射 > 保存映射”。ZCode 3.14.3 不能逐次切换模型。多数 CLI 不回报可核验的实际模型 ID,执行后要对照厂商运行记录、权限和账单。Windows Harness 沙箱的 ACL 文件效果报告为部分隔离,涉及敏感仓库时应先用测试环境验证。
|
|
393
393
|
|
|
394
|
-
### 官方工具终端(0.14.0-beta.
|
|
394
|
+
### 官方工具终端(0.14.0-beta.3,npm `next`)
|
|
395
395
|
|
|
396
396
|
工作台“官方工具 · 体检”下方新增 **官方工具终端** 卡片:以标签页形式交互运行系统终端(Windows 为 PowerShell,其他系统为登录 `$SHELL`)或已安装的官方 CLI(`codex`、`claude`、`kimi`、`mcode`、`mimo`、`grok`、`gemini`),实时输出,可多轮对话、显示全屏界面,也可选 **登录** 直接运行 `codex login`、`claude auth login`、`kimi login`、`mcode login`、`mimo auth login`、`grok login`。
|
|
397
397
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ljwei-stak/dsh-model-router",
|
|
3
|
-
"version": "0.14.0-beta.
|
|
3
|
+
"version": "0.14.0-beta.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Model Router for official DeepSeek Harness Desktop: cost-aware task routing, dependent work packages and official vendor CLI discovery, installation and execution. GAL is available separately as @ljwei-stak/dsh-galgame.",
|
|
6
6
|
"repository": {
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"scripts": {
|
|
80
80
|
"build:client": "node scripts/build-client.mjs",
|
|
81
81
|
"check:client": "node scripts/build-client.mjs --check",
|
|
82
|
-
"test": "node --test tests/router.test.mjs tests/harness-*.test.mjs tests/official-tools.test.mjs tests/official-team-runtime.test.mjs tests/model-profiles.test.mjs tests/model-profile-editor.test.mjs tests/tool-install-state.test.mjs tests/vendor-adapters.test.mjs tests/desktop-compat.test.mjs tests/plugin-split.test.mjs tests/task-executors.test.mjs tests/roadmap.test.mjs tests/insights-state.test.mjs tests/billing-team-ledger.test.mjs tests/subscription-billing.test.mjs tests/subscription-host.test.mjs tests/test-pass-regressions.test.mjs tests/roadmap-gaps.test.mjs tests/cordis-inject.test.mjs tests/cli-terminal.test.mjs",
|
|
82
|
+
"test": "node --test tests/router.test.mjs tests/harness-*.test.mjs tests/official-tools.test.mjs tests/official-team-runtime.test.mjs tests/model-profiles.test.mjs tests/model-profile-editor.test.mjs tests/tool-install-state.test.mjs tests/vendor-adapters.test.mjs tests/desktop-compat.test.mjs tests/plugin-split.test.mjs tests/task-executors.test.mjs tests/roadmap.test.mjs tests/insights-state.test.mjs tests/billing-team-ledger.test.mjs tests/subscription-billing.test.mjs tests/subscription-host.test.mjs tests/test-pass-regressions.test.mjs tests/roadmap-gaps.test.mjs tests/cordis-inject.test.mjs tests/cli-terminal.test.mjs tests/cli-terminal-gateway.test.mjs",
|
|
83
83
|
"prepack": "node scripts/prepack.mjs",
|
|
84
84
|
"pack:local": "npm pack --pack-destination dist"
|
|
85
85
|
},
|