dsh-m 0.9.6 → 0.9.7

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/README.en.md CHANGED
@@ -48,6 +48,11 @@ The default registry includes DSH Skins, ModSearch, the Lark / QQ / Weixin / WeC
48
48
 
49
49
  The restart chain below applies to **Web only** (Desktop takes effect via the official app lifecycle: after installing, quit and reopen the Desktop app — see the 0.9.0 section). After any mutation, the already-open Market and Installed views refresh the profile state together, keeping badges and cards in sync without closing and reopening the marketplace; a "⚡ Restart" banner appears — under systemd, the DSH launcher's `appExit` hook hands the restart back to a unit configured with `Restart=on-failure` or `Restart=always`, avoiding a `systemctl` helper inside the unit cgroup that is about to stop; if `appExit` is unavailable, the fallback uses a manager-owned transient `systemd-run` service and only then a detached helper. The client confirms the replacement by boot id and dismisses the banner, leaving DSH Web's own background connection recovery in control; it does not force a full-page reload during the auth/route handoff. The restart chain has been live-verified on the current DSH Web `0.2.0-rc.2` (2026-10-01): dsh-m probes the unit's `Restart=` policy, hands the stop back via `appExit`, the `status=75/TEMPFAIL` exit is caught by the Restart policy and auto-started, and after recovery the page reconnects in the background with the panel fully functional. Historical scope: the `0.1.5-rc.1` era covered live `/dshm` ping and authenticated `303 → 200` checks; `0.1.2-rc.1` is covered by contract checks plus runtime shape-detection fallback (see the verified array) — with the deployment moved on, no live E2E is chased on retired generations; the transient `systemd-run` fallback only applies to hosts without `appExit` (all supported generations provide it) and stays a documented fallback rather than a release gate; repeated install/uninstall is covered by the profile-transaction test surface (compensation, post-install guard, 813 cases) plus the 0.4.0→0.4.2 consecutive-release evidence. Installs stream live pnpm progress (resolve → download → link → build).
50
50
 
51
+ ### Fixed in 0.9.7 — Upgrade chip on Desktop showed a red "upgrade failed" banner (capability refusal is guidance, not an error)
52
+
53
+ - **Root cause**: the 0.9.1 upgrade chip always calls `self-upgrade`; the Desktop capability table refuses by design with a structured 409, but the client rendered that 409 like any other failure — a red "upgrade failed" banner. Per the capability table this is not a failure, it's a pointer to the official entry.
54
+ - **Fix**: `api()` now forwards the structured refusal fields (code/action/profile/guidance); on a 409 the chip click shows a **neutral info banner** with the official-entry guidance (the guidance text's single source stays server-side in `active-profile.ts`, zero duplication in the client); info banners stay for 12s. Real failures on Web still use the red error banner — unchanged.
55
+
51
56
  ### Fixed in 0.9.6 — "Clear finished" silently no-opped on failed records
52
57
 
53
58
  - **Root cause**: the 0.7.0 review consensus excluded failed/superseded records from clearing (kept for review), so clicking the button against a failed record did nothing with zero feedback — reported as a bug from the real Windows host (2026-10-01). An explicit clear click is not a "silent wipe"; the old consensus is overruled.
package/README.md CHANGED
@@ -48,6 +48,11 @@
48
48
 
49
49
  以下重启链路仅适用于 **Web**(Desktop 的生效走官方应用生命周期:安装完成后退出并重新打开 Desktop 应用即可,见 0.9.0 节)。安装 / 卸载 / 升级完成后,当前已打开的市场页与已装页会一起重新读取 profile 状态并同步徽标/卡片,不需要关闭后重新打开插件市场;随后出现「⚡ 一键重启」横幅——受 systemd 管理时通过 DSH launcher 的 `appExit` 交给服务的 `Restart` 策略,避免在待停止 unit 的 cgroup 内启动 `systemctl` helper;无 `appExit` 的 systemd 兜底改用 manager-owned transient `systemd-run`,最后才退回 detached-helper。客户端按 boot id 确认新进程已恢复后关闭横幅,交由 DSH Web 自身的后台连接重试恢复页面,不强制整页刷新,避免认证/路由切换期间白屏。重启链路已在当前 DSH Web `0.2.0-rc.2` 实机核验(2026-10-01):dsh-m 探测 unit `Restart=` 策略后经 `appExit` 交还 systemd,`status=75/TEMPFAIL` 退出由 Restart 策略接住自动拉起,服务恢复后页面后台重连、面板全功能可用。历史口径:`0.1.5-rc.1` 时代已核验 `/dshm` ping 与带认证 `303 → 200`;`0.1.2-rc.1` 按契约核对 + 形态探测兜底收录(verified 数组),部署面演进后不再追旧代 live E2E;transient `systemd-run` 兜底仅适用于无 `appExit` 的宿主(受支持代际均提供 `appExit`),保持设计兜底而非发布门槛;连续安装/卸载由 profile 事务测试面(补偿事务、装后守卫等 813 用例)与 0.4.0→0.4.2 连续发布实证覆盖。安装过程实时显示 pnpm 进度(解析 → 下载 → 链接 → 构建)。
50
50
 
51
+ ### 0.9.7 修复:Desktop 下点升级角标弹红色「升级失败」(能力表拒绝应为指导而非报错)
52
+
53
+ - **根因**:0.9.1 的升级角标点击后一律调 `self-upgrade`;Desktop 能力表按设计 409 结构化拒绝,但客户端把 409 当普通失败渲染成红色「升级失败」横幅——按能力表这根本不是失败,是「该走官方入口」的指引。
54
+ - **修复**:`api()` 透传能力表拒绝的结构化字段(code/action/profile/guidance),角标点击收到 409 时改出**中性 info 横幅**展示官方入口指引(guidance 单一事实源仍在服务端 `active-profile.ts`,客户端零复制);info 横幅停留 12s。Web 端真实失败仍走红色 err 横幅,行为不变。
55
+
51
56
  ### 0.9.6 修复:「清除已完成」对失败记录无声 no-op
52
57
 
53
58
  - **根因**:0.7.0 评审共识把「失败/已跳过」排除在清除范围外(保留供回看),于是按钮对着一条失败记录点击毫无反应、也无任何反馈——Windows 实机被当成 bug 上报(2026-10-01)。显式点击清除不是「静默抹掉」,旧共识被推翻。
package/lib/client.js CHANGED
@@ -1845,6 +1845,9 @@ async function api(method, params, signal) {
1845
1845
  if (!res.ok || data.ok === false) {
1846
1846
  const err = new Error(data.error || `API ${res.status}`);
1847
1847
  if (data && typeof data === "object" && data.issue) err.issue = data.issue;
1848
+ if (data && typeof data === "object" && data.code === "unsupported-on-profile") {
1849
+ err.unsupported = { action: data.action, profile: data.profile, guidance: typeof data.guidance === "string" ? data.guidance : "" };
1850
+ }
1848
1851
  if (data && typeof data === "object" && data.kind && Array.isArray(data.violations)) {
1849
1852
  err.guard = { kind: data.kind, violations: data.violations, compensation: data.compensation, needsRestart: data.needsRestart === true, restartSafe: data.restartSafe === true, repairBasis: data.repairBasis };
1850
1853
  }
@@ -3392,7 +3395,11 @@ function DshmVersionChip({ version, notify, initialCheck }) {
3392
3395
  text: lookup("notify.selfupgraded", { from: version, to: res.version || "latest" }) + (res.buildApprovals && res.buildApprovals.length ? lookup("notify.builds", { names: res.buildApprovals.join(", ") }) : res.fallbackAllBuilds ? lookup("notify.builds.fallback") : "")
3393
3396
  });
3394
3397
  } catch (e) {
3395
- notify({ kind: "err", text: lookup("notify.selfupgraded.failed", { err: e && e.message || e }) });
3398
+ if (e && e.unsupported) {
3399
+ notify({ kind: "info", text: e.unsupported.guidance || e && e.message || String(e) });
3400
+ } else {
3401
+ notify({ kind: "err", text: lookup("notify.selfupgraded.failed", { err: e && e.message || e }) });
3402
+ }
3396
3403
  } finally {
3397
3404
  setBusy(false);
3398
3405
  }
@@ -3586,7 +3593,7 @@ function MarketPanel({ onClose }) {
3586
3593
  }, [onClose]);
3587
3594
  useEffect(() => {
3588
3595
  if (!toast) return;
3589
- const t = setTimeout(() => setToast(null), toast.kind === "err" ? 15e3 : 6e3);
3596
+ const t = setTimeout(() => setToast(null), toast.kind === "err" ? 15e3 : toast.kind === "info" ? 12e3 : 6e3);
3590
3597
  return () => clearTimeout(t);
3591
3598
  }, [toast]);
3592
3599
  const notify = useCallback(({ kind, text, needsRestart }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-m",
3
- "version": "0.9.6",
3
+ "version": "0.9.7",
4
4
  "description": "可自定义收录清单的 DSH 插件市场:收录、安装、卸载、升级一站完成,附 agent 工具与 dshm CLI。",
5
5
  "license": "MIT",
6
6
  "type": "module",