dsh-m 0.2.11 → 0.2.12

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
@@ -40,7 +40,7 @@ The default registry includes DSH Skins, ModSearch, the Lark / QQ / Weixin / WeC
40
40
  | **Installed** | What your web profile actually has, annotated "via market / non-market"; update badges, upgrade, two-step confirm uninstall; 📖 README preview (64KB cap) |
41
41
  | **Settings** | Registry address draft with "Validate & apply / Restore default / Download default registry.json / Check entries reachability"; configured vs active address and status at a glance; force refresh; dsh-m self-update |
42
42
 
43
- After any mutation 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 current DSH Web `0.1.5-rc.1` runtime has now live-loaded dsh-m `0.2.11`; `/dshm` ping returned version `0.2.11`, and an authenticated page request completed `303 → 200`. The reported 404 was not reproducible once the service stabilized; journal evidence showed repeated `status=75/TEMPFAIL` restarts during the incident. Live DSH `0.1.2-rc.1`, transient-fallback, and repeated install/uninstall E2E experiments remain pre-release gaps. Installs stream live pnpm progress (resolve → download → link → build).
43
+ 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 current DSH Web `0.1.5-rc.1` runtime has now live-loaded dsh-m `0.2.11`; `/dshm` ping returned version `0.2.11`, and an authenticated page request completed `303 → 200`. The reported 404 was not reproducible once the service stabilized; journal evidence showed repeated `status=75/TEMPFAIL` restarts during the incident. Live DSH `0.1.2-rc.1`, transient-fallback, and repeated install/uninstall E2E experiments remain pre-release gaps. Installs stream live pnpm progress (resolve → download → link → build).
44
44
 
45
45
  ## Agent tools (7)
46
46
 
package/README.md CHANGED
@@ -40,7 +40,7 @@
40
40
  | **已装** | web profile 实装列表,标注「市场安装 / 非市场安装」;可升级徽标、升级、两段式确认卸载;📖 README 预览(64KB 截断) |
41
41
  | **设置** | registry 地址草稿 +「校验并应用 / 恢复默认 / 下载默认 registry.json / 检查条目可达性」;配置地址、生效来源与状态一目了然;强制刷新;dsh-m 自更新 |
42
42
 
43
- 安装 / 卸载 / 升级完成后出现「⚡ 一键重启」横幅——受 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.1.5-rc.1` 运行时的 live 核验:web profile 已加载 dsh-m `0.2.11`,重启后 `/dshm` ping 返回 `version: 0.2.11`,携带当前认证 token 的页面请求流程为 `303 → 200`;无认证请求会被拒绝。此次白屏截图对应的 404 在服务稳定后未复现;journal 显示截图时段发生多次 `status=75/TEMPFAIL` 重启,当前暂判定为重启/认证过渡窗口现象,未发现 dsh-m Host 路由崩溃。DSH `0.1.2-rc.1` live E2E、transient fallback live E2E,以及连续安装/卸载实验仍是正式发布前的验证 gap。安装过程实时显示 pnpm 进度(解析 → 下载 → 链接 → 构建)。
43
+ 安装 / 卸载 / 升级完成后,当前已打开的市场页与已装页会一起重新读取 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.1.5-rc.1` 运行时的 live 核验:web profile 已加载 dsh-m `0.2.11`,重启后 `/dshm` ping 返回 `version: 0.2.11`,携带当前认证 token 的页面请求流程为 `303 → 200`;无认证请求会被拒绝。此次白屏截图对应的 404 在服务稳定后未复现;journal 显示截图时段发生多次 `status=75/TEMPFAIL` 重启,当前暂判定为重启/认证过渡窗口现象,未发现 dsh-m Host 路由崩溃。DSH `0.1.2-rc.1` live E2E、transient fallback live E2E,以及连续安装/卸载实验仍是正式发布前的验证 gap。安装过程实时显示 pnpm 进度(解析 → 下载 → 链接 → 构建)。
44
44
 
45
45
  ## Agent 工具(7 个)
46
46
 
package/docs/DESIGN.md CHANGED
@@ -95,7 +95,7 @@ npm 安装 / GitHub 安装 / 升级 / 自升级 / 卸载是**同一个事务模
95
95
 
96
96
  3 个视图,卡片展开式详情(不做独立详情页),**中文优先**,跟随 DSH Web 深色主题:
97
97
 
98
- 1. **市场页**(默认):registry 卡片流;搜索/分类为**服务端过滤**(Host 强制 `withLatest=true`、每页 50、1,000 条清单第一页只探测当前页);`MarketPanel` 是市场/已装数据唯一 owner(请求 generation + AbortController 丢弃旧响应);分页控件 + 超过 200 条性能提示 + 默认/自定义/缓存/不可用短提示(不含本地路径);卡片详情保留 README markdown 预览与 npm/GitHub 官方外链。
98
+ 1. **市场页**(默认):registry 卡片流;搜索/分类为**服务端过滤**(Host 强制 `withLatest=true`、每页 50、1,000 条清单第一页只探测当前页);`MarketPanel` 是市场/已装数据唯一 owner(请求 generation + AbortController 丢弃旧响应);安装/卸载/升级完成后通过统一协调器同时刷新市场与已装快照,不要求关闭并重新打开面板;分页控件 + 超过 200 条性能提示 + 默认/自定义/缓存/不可用短提示(不含本地路径);卡片详情保留 README markdown 预览与 npm/GitHub 官方外链。
99
99
  2. **已装页**:profile 实际安装列表,标注来源(市场/非市场/未知);registry 不可用时仍列出已装并标记;「可升级 → x.y.z」徽标 +「升级」;「卸载」。
100
100
  3. **设置页**:registry 地址草稿 +「校验并应用」(先校验候选再写 settings,失败不落盘)/「恢复默认」/「下载默认 registry.json」(不改当前配置)/「检查条目可达性」(probe 统计 + 最多 100 条 issue,只读不改配置);同时展示配置地址、当前生效配置、生效来源与 configStatus(含 rejected/回滚原因与维护性 warnings);完整本地路径仅在此页显示。registry 配置为 **live 生效,不出现安装类重启横幅**;安装/卸载/升级仍保留重启横幅。
101
101
 
package/lib/client.js CHANGED
@@ -410,6 +410,23 @@ var init_restart_wait = __esm({
410
410
  }
411
411
  });
412
412
 
413
+ // src/client/view-refresh.js
414
+ var view_refresh_exports = {};
415
+ __export(view_refresh_exports, {
416
+ refreshAfterMutation: () => refreshAfterMutation
417
+ });
418
+ async function refreshAfterMutation({ marketReload, installedReload }) {
419
+ await Promise.all([
420
+ marketReload(false),
421
+ installedReload()
422
+ ]);
423
+ }
424
+ var init_view_refresh = __esm({
425
+ "src/client/view-refresh.js"() {
426
+ "use strict";
427
+ }
428
+ });
429
+
413
430
  // src/client/main.jsx
414
431
  var React = require("react");
415
432
  var rd = require("react-dom");
@@ -423,6 +440,7 @@ var { ExtLink, MdImg, renderMarkdown } = createMarkdown2(h);
423
440
  var { installedViewModel: installedViewModel2, registrySourceKey: registrySourceKey2 } = (init_installed_view(), __toCommonJS(installed_view_exports));
424
441
  var { pickPayload: pickPayload2, parseToolArgs: parseToolArgs2 } = (init_tool_view(), __toCommonJS(tool_view_exports));
425
442
  var { RESTART_POLL_MS: RESTART_POLL_MS2, RESTART_DEADLINE_MS: RESTART_DEADLINE_MS2, nextRestartWait: nextRestartWait2, isAmbiguousRestartRequestError: isAmbiguousRestartRequestError2 } = (init_restart_wait(), __toCommonJS(restart_wait_exports));
443
+ var { refreshAfterMutation: refreshAfterMutation2 } = (init_view_refresh(), __toCommonJS(view_refresh_exports));
426
444
  var ZH = {
427
445
  "market.title": "\u63D2\u4EF6\u5E02\u573A",
428
446
  "tab.market": "\u5E02\u573A",
@@ -1056,7 +1074,7 @@ function RestartBanner({ note, onDone }) {
1056
1074
  phase === "idle" && !err ? h("button", { className: "dshm-btn sm", onClick: () => onDone(false) }, lookup("common.later")) : null
1057
1075
  );
1058
1076
  }
1059
- function MarketTab({ notify, market }) {
1077
+ function MarketTab({ notify, market, onMutation }) {
1060
1078
  const { data, loading, error, reload, query, updateQuery } = market;
1061
1079
  const [openId, setOpenId] = useState(null);
1062
1080
  const [busyId, setBusyId] = useState(null);
@@ -1084,7 +1102,7 @@ function MarketTab({ notify, market }) {
1084
1102
  needsRestart: true,
1085
1103
  text: lookup("notify.installed", { pkg: res.pkg, version: res.version ? ` v${res.version}` : "" }) + (res.usedAllowAllBuilds ? lookup("notify.allowbuilds") : "")
1086
1104
  });
1087
- await reload(false);
1105
+ await (onMutation ? onMutation() : reload(false));
1088
1106
  } catch (e) {
1089
1107
  notify({ kind: "err", text: lookup("failed.install", { err: e && e.message || e }) });
1090
1108
  } finally {
@@ -1242,7 +1260,7 @@ function ReadmeBlock({ pkg }) {
1242
1260
  state.truncated ? h("div", { className: "dshm-md-note" }, lookup("readme.truncated")) : null
1243
1261
  );
1244
1262
  }
1245
- function InstalledTab({ notify, installed }) {
1263
+ function InstalledTab({ notify, installed, onMutation }) {
1246
1264
  const { loading, data, error, reload } = installed;
1247
1265
  const [openPkg, setOpenPkg] = useState(null);
1248
1266
  const [readmePkg, setReadmePkg] = useState(null);
@@ -1256,7 +1274,7 @@ function InstalledTab({ notify, installed }) {
1256
1274
  needsRestart: true,
1257
1275
  text: lookup("notify.uninstalled", { pkg: res.pkg }) + (res.liveDisabled ? lookup("notify.livedisabled") : "") + (res.leftovers && res.leftovers.length ? lookup("notify.leftovers", { paths: res.leftovers.join(", ") }) : "")
1258
1276
  });
1259
- await reload();
1277
+ await (onMutation ? onMutation() : reload());
1260
1278
  } catch (e) {
1261
1279
  notify({ kind: "err", text: lookup("failed.uninstall", { err: e && e.message || e }) });
1262
1280
  } finally {
@@ -1276,7 +1294,7 @@ function InstalledTab({ notify, installed }) {
1276
1294
  to: res.version ? `v${res.version}` : res.sha ? res.sha.slice(0, 7) : "latest"
1277
1295
  }) + (res.usedAllowAllBuilds ? lookup("notify.upgradehint") : "")
1278
1296
  });
1279
- await reload();
1297
+ await (onMutation ? onMutation() : reload());
1280
1298
  } catch (e) {
1281
1299
  notify({ kind: "err", text: lookup("failed.upgrade", { err: e && e.message || e }) });
1282
1300
  } finally {
@@ -1632,10 +1650,11 @@ function MarketPanel({ onClose }) {
1632
1650
  const [tab, setTab] = useState("market");
1633
1651
  const market = useMarketData();
1634
1652
  const installed = useAsync(() => api("installed"), []);
1635
- const onRegistryChanged = useCallback(async () => {
1636
- await market.reload(false);
1637
- await installed.reload().catch(() => void 0);
1638
- }, [market, installed]);
1653
+ const refreshViews = useCallback(
1654
+ () => refreshAfterMutation2({ marketReload: market.reload, installedReload: installed.reload }),
1655
+ [market.reload, installed.reload]
1656
+ );
1657
+ const onRegistryChanged = refreshViews;
1639
1658
  const counts = {
1640
1659
  market: market.data ? market.data.total : null,
1641
1660
  installed: installed.data ? installed.data.items.length : null
@@ -1693,8 +1712,8 @@ function MarketPanel({ onClose }) {
1693
1712
  h(
1694
1713
  "div",
1695
1714
  { className: "dshm-body" },
1696
- tab === "market" ? h(MarketTab, { notify, market }) : null,
1697
- tab === "installed" ? h(InstalledTab, { notify, installed }) : null,
1715
+ tab === "market" ? h(MarketTab, { notify, market, onMutation: refreshViews }) : null,
1716
+ tab === "installed" ? h(InstalledTab, { notify, installed, onMutation: refreshViews }) : null,
1698
1717
  tab === "settings" ? h(SettingsTab, { notify, onRegistryChanged }) : null
1699
1718
  ),
1700
1719
  toast ? h(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-m",
3
- "version": "0.2.11",
3
+ "version": "0.2.12",
4
4
  "description": "DSH Marketplace — 个人自用的 DeepSeek Harness 插件市场:收录、安装、卸载、升级 DSH 插件",
5
5
  "license": "MIT",
6
6
  "type": "module",