dsh-m 0.9.92 → 0.9.93
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 +12 -0
- package/lib/cli.js +3 -3
- package/lib/core/host-api.js +3 -7
- package/lib/core/market.js +15 -0
- package/lib/tools.js +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,12 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
|
|
|
8
8
|
|
|
9
9
|
## 中文
|
|
10
10
|
|
|
11
|
+
### 0.9.93 重构:装后守卫线格式收敛至 InstallGuardError.toWire——加守卫字段编辑点 8 → 2(纯重构,线格式语义零变化)
|
|
12
|
+
|
|
13
|
+
- **需求(2026-10-16 架构巡查摩擦点 3,巡查首推)**:`InstallGuardError` → 线格式的投影在手写 5 处逐字重复(host-api 409 payload / tools install+upgrade 两臂 / cli install+upgrade 两臂)+ client 再拼一次——加一个守卫字段要碰 8 处(类 + 5 投影 + client 读取 + 测试),`repairBasis` 后加时各点手补已实证过一次复发。
|
|
14
|
+
- **方案**:产地方法 `toWire(): InstallGuardWire`(随类导出接口 + `GuardViolation` re-export)成为 6 字段共享线格式单一事实源;五处投影改一行展开(信封差异留在各端:host-api `ok/error`、tools `ok/guard/message`、cli 裸 JSON),`repairBasis` undefined 时键不存在的语义移入方法本体。加字段编辑点降为 2(类+toWire 一处、client 读取一处——浏览器边界不能 import core,防御性拼装留守,如实注记)。自觉差异注记:tools 信封键序 kind/message 交换(JSON 键序对消费方无语义,全 tests/ 无原始 JSON 键序钉)。
|
|
15
|
+
- **验证**:全量 1530 测试全绿(新增 toWire 三钉例:六字段齐/repairBasis 键缺席/键序;新增 host-api dispatcher 409 钉例补 host-api 投影零测试覆盖的缺口;tools 两臂 execute 钉例零迁移)、typecheck 零错误;收敛锚 `repairBasis ? { repairBasis: … }` 三文件 5 → 0(兼证无第 6 处投影);实施计划经两轮独立评审四项收敛 + 执行评审两轮(含 A1 钉例独立成 describe)记录在 `docs/plans/2026-10-17-install-guard-towire-implementation-plan.md`。
|
|
16
|
+
|
|
11
17
|
### 0.9.92 修复+重构:三端市场查询文法统一(market-query 单一入口)——CLI 非法 category 吞错修复
|
|
12
18
|
|
|
13
19
|
- **需求(2026-10-16 实测)**:`dshm search --category <非法>` 静默返回「没有匹配的收录条目」+ exit 0,而同输入 GUI 得 400、agent 工具显式报错——「合并市场」词条承诺的「三端同源同语义」被 CLI 打破;根因是分类 slug 正则、source/offset/limit 解析、精选标签表在三端各持一份拷贝(正则 ×3、解析 ×3、标签表 ×2、deadline 常量 ×2),CLI 的拷贝漏了 category 校验。
|
|
@@ -649,6 +655,12 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
|
|
|
649
655
|
|
|
650
656
|
## English
|
|
651
657
|
|
|
658
|
+
### Refactor in 0.9.93 — post-install guard wire format converged onto InstallGuardError.toWire; adding a guard field now touches 8 → 2 points (pure refactor, zero wire-format semantic change)
|
|
659
|
+
|
|
660
|
+
- **Need (2026-10-16 architecture walk, friction point 3, the walk's top pick)**: the `InstallGuardError` → wire-format projection was hand-written verbatim in 5 places (host-api 409 payload / tools install+upgrade arms / cli install+upgrade arms) plus one more assembly in the client — adding a guard field meant touching 8 points (class + 5 projections + client read + tests); the later addition of `repairBasis` had already demonstrated the recurrence once.
|
|
661
|
+
- **Approach**: a source method `toWire(): InstallGuardWire` (interface exported alongside the class, plus a `GuardViolation` re-export) becomes the single source of the shared 6-field wire format; the five projections become one-line spreads (envelope differences stay per-end: host-api `ok/error`, tools `ok/guard/message`, cli bare JSON), and the "repairBasis key absent when undefined" semantics move into the method body. Adding a field now touches 2 points (class+toWire, and the client read — the browser bundle cannot import core modules, so its defensive assembly stays, honestly noted). Deliberate delta: the tools envelope swaps key order between kind/message (JSON key order carries no consumer semantics; no raw-JSON key-order pins exist across tests/).
|
|
662
|
+
- **Verification**: full suite 1530 green (three new toWire pins: six fields / repairBasis key absence / key order; a new host-api dispatcher 409 pin closing the "host-api projection had zero test coverage" gap; tools-arm execute pins migrated zero), typecheck clean; the convergence anchor `repairBasis ? { repairBasis: … }` went 5 → 0 across the three files (also proving there is no 6th projection); plan review (two rounds, four findings) and execution review (two rounds, including A1 extracting the pin into its own describe) recorded in `docs/plans/2026-10-17-install-guard-towire-implementation-plan.md`.
|
|
663
|
+
|
|
652
664
|
### Fix + refactor in 0.9.92 — unified three-end market query intake (market-query single entry); CLI invalid-category silent swallow fixed
|
|
653
665
|
|
|
654
666
|
- **Need (2026-10-16, reproduced live)**: `dshm search --category <invalid>` silently printed "no matching entries" and exited 0, while the same input gets a 400 from the panel API and an explicit error from the agent tool — breaking the "same source, same semantics across GUI / tools / CLI" promise. Root cause: the category slug regex, source/offset/limit parsing, and the curated label map each had per-end copies (regex ×3, parsing ×3, labels ×2, deadline constants ×2), and the CLI copy lacked category validation.
|
package/lib/cli.js
CHANGED
|
@@ -193,7 +193,7 @@ async function runCliDispatch(argv, deps, io) {
|
|
|
193
193
|
// metadata-only:不构造全量 latest;0.9.92 三端查询文法:category/source/limit/offset 经
|
|
194
194
|
// market-query 单一入口归一(与 host-api/工具同源同语义);文法违规 throw 直飞 dispatch 统一
|
|
195
195
|
// catch(`错误:${msg}` + exit 1,与此前 source 内联报错输出逐字节同款)——非法 category
|
|
196
|
-
//
|
|
196
|
+
// 自此显式报错(≤0.9.91 静默空结果吞错,回归钉例在 cli-search.test.mjs)。
|
|
197
197
|
// 双源判定:主清单 unavailable 但社区有条目 → 照常出页(Q42);两层皆不可用 → exit 1
|
|
198
198
|
const intake = parseMarketQueryInput({ category: flags.category, source: flags.source, limit: flags.limit, offset: flags.offset }, { limitMax: 80, limitDefault: 10 });
|
|
199
199
|
const result = await d.listMarket(cfg, {
|
|
@@ -332,7 +332,7 @@ async function runCliDispatch(argv, deps, io) {
|
|
|
332
332
|
}
|
|
333
333
|
catch (e) {
|
|
334
334
|
if (e instanceof InstallGuardError) {
|
|
335
|
-
err('[⛔ 装后守卫拦截] ' + JSON.stringify(
|
|
335
|
+
err('[⛔ 装后守卫拦截] ' + JSON.stringify(e.toWire()));
|
|
336
336
|
err(e.message);
|
|
337
337
|
err(e.restartSafe ? '可以重启 DSH Web:dshm restart --yes' : '修复后再重启(不要现在重启)。');
|
|
338
338
|
return 1;
|
|
@@ -357,7 +357,7 @@ async function runCliDispatch(argv, deps, io) {
|
|
|
357
357
|
}
|
|
358
358
|
catch (e) {
|
|
359
359
|
if (e instanceof InstallGuardError) {
|
|
360
|
-
err('[⛔ 装后守卫拦截] ' + JSON.stringify(
|
|
360
|
+
err('[⛔ 装后守卫拦截] ' + JSON.stringify(e.toWire()));
|
|
361
361
|
err(e.message);
|
|
362
362
|
err(e.restartSafe ? '可以重启 DSH Web:dshm restart --yes' : '修复后再重启(不要现在重启)。');
|
|
363
363
|
return 1;
|
package/lib/core/host-api.js
CHANGED
|
@@ -166,18 +166,14 @@ function errorStatus(err) {
|
|
|
166
166
|
return { status, payload: { ok: false, error: err.message, code: err.code } };
|
|
167
167
|
}
|
|
168
168
|
if (err instanceof InstallGuardError) {
|
|
169
|
-
// 装后守卫拦截(M2 Task 3):结构化投影含 needsRestart/restartSafe/kind——GUI 一键重启只读 restartSafe
|
|
169
|
+
// 装后守卫拦截(M2 Task 3):结构化投影含 needsRestart/restartSafe/kind——GUI 一键重启只读 restartSafe;
|
|
170
|
+
// 0.9.93 起线格式单源于 toWire(信封 ok/error 本端自拼)
|
|
170
171
|
return {
|
|
171
172
|
status: 409,
|
|
172
173
|
payload: {
|
|
173
174
|
ok: false,
|
|
174
175
|
error: err.message,
|
|
175
|
-
|
|
176
|
-
violations: err.violations,
|
|
177
|
-
compensation: err.compensation,
|
|
178
|
-
needsRestart: err.needsRestart,
|
|
179
|
-
restartSafe: err.restartSafe,
|
|
180
|
-
...(err.repairBasis ? { repairBasis: err.repairBasis } : {}),
|
|
176
|
+
...err.toWire(),
|
|
181
177
|
},
|
|
182
178
|
};
|
|
183
179
|
}
|
package/lib/core/market.js
CHANGED
|
@@ -822,6 +822,21 @@ export class InstallGuardError extends Error {
|
|
|
822
822
|
this.compensation = fields.compensation;
|
|
823
823
|
this.repairBasis = fields.repairBasis;
|
|
824
824
|
}
|
|
825
|
+
/**
|
|
826
|
+
* 线格式单一事实源(0.9.93):三端共享的 6 字段守卫投影。各端信封自拼(host-api 409 payload 的
|
|
827
|
+
* ok/error、tools 的 ok/guard/message、cli 的裸 JSON),共享部分一律经此方法——加守卫字段只改
|
|
828
|
+
* 本方法 + client 读取一处。repairBasis undefined 时键不存在(今日 5 处投影同语义)。
|
|
829
|
+
*/
|
|
830
|
+
toWire() {
|
|
831
|
+
return {
|
|
832
|
+
kind: this.kind,
|
|
833
|
+
violations: this.violations,
|
|
834
|
+
compensation: this.compensation,
|
|
835
|
+
needsRestart: this.needsRestart,
|
|
836
|
+
restartSafe: this.restartSafe,
|
|
837
|
+
...(this.repairBasis ? { repairBasis: this.repairBasis } : {}),
|
|
838
|
+
};
|
|
839
|
+
}
|
|
825
840
|
}
|
|
826
841
|
/** lockfile packages 段的目标包 resolution 提取(行扫描:键行 `pkg@ver:` → 收集至下一键行;引号/嵌套键兼容)。 */
|
|
827
842
|
function lockResolutionOf(lockText, pkg) {
|
package/lib/tools.js
CHANGED
|
@@ -241,7 +241,7 @@ export function registerTools(ctx, cfg, deps = {}, profile = resolveActiveProfil
|
|
|
241
241
|
catch (err) {
|
|
242
242
|
if (err instanceof InstallGuardError) {
|
|
243
243
|
// 结构化 error result(M2 Task 3 ㉑-㉜):不渲染「已安装」,无 force 通道;restartSafe 随结果携带
|
|
244
|
-
return { ok: false, guard: true,
|
|
244
|
+
return { ok: false, guard: true, message: err.message, ...err.toWire() };
|
|
245
245
|
}
|
|
246
246
|
throw err;
|
|
247
247
|
}
|
|
@@ -403,7 +403,7 @@ export function registerTools(ctx, cfg, deps = {}, profile = resolveActiveProfil
|
|
|
403
403
|
}
|
|
404
404
|
catch (err) {
|
|
405
405
|
if (err instanceof InstallGuardError) {
|
|
406
|
-
return { ok: false, guard: true,
|
|
406
|
+
return { ok: false, guard: true, message: err.message, ...err.toWire() };
|
|
407
407
|
}
|
|
408
408
|
throw err;
|
|
409
409
|
}
|