dsh-m 0.9.14 → 0.9.16

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
@@ -36,7 +36,7 @@ The default registry includes DSH Skins, ModSearch, the Lark / QQ / Weixin / WeC
36
36
 
37
37
  | View | Capabilities |
38
38
  |---|---|
39
- | **Market** | **Zoned** (0.7.0): **Community** (default landing, the 4,000+ full catalog) and **Curated** (hand-picked primary registry, single page) as two independent zones — each keeps its own category/search/sort/pagination state across tab switches; plus a **Favorites** zone (browser-local bookmarks with delisted-entry detection and one-click cleanup). Community zone: two-row collapsible category chips with sticky collapse, sort switch (downloads/stars/added × asc/desc), windowed pagination (24/48/96 per page), card byline (owner/downloads/stars); clicking a card opens a **detail modal** (superset of the card: download window, screenshot lightbox, capabilities collapsed by default, install command — the row is hidden on desktop and for installed entries since 0.9.14); npm sources pin the latest exact version, GitHub sources pin the release/tag commit |
39
+ | **Market** | **Zoned** (0.7.0): **Community** (default landing, the 4,000+ full catalog) and **Curated** (hand-picked primary registry, single page) as two independent zones — each keeps its own category/search/sort/pagination state across tab switches; plus a **Favorites** zone (browser-local bookmarks with delisted-entry detection and one-click cleanup). Community zone: two-row collapsible category chips with sticky collapse, sort switch (downloads/stars/added × asc/desc), windowed pagination (24/48/96 per page), card byline (owner/downloads/stars); clicking a card opens a **detail modal** (superset of the card: download window, screenshot lightbox, capabilities collapsed by default, install command — the row is hidden on desktop and for installed entries since 0.9.14; install progress and result shown in-modal since 0.9.15); npm sources pin the latest exact version, GitHub sources pin the release/tag commit |
40
40
  | **Operations** | Install/upgrade/uninstall/toggle all flow through a global operation log (0.7.0): state lives off cards, surviving paging/searching/tab switches; persisted to localStorage and resumed after host reloads (each restored op re-validated "still applies" before executing); benignly-invalidated ops show as a neutral "skipped"; the Installed page gains "Update all (N)" batch queueing |
41
41
  | **Installed** | What the current profile (web / desktop, 0.9.0+) actually has, annotated "via market / non-market"; update badges, upgrade, two-step confirm uninstall; 📖 README preview (64KB cap); **live phase badge** (●active / ●failed / ○pending) and a **one-click toggle** (0.4.0: delegates to the official pluginManager service for live application; falls back to file-level edits + restart notice when absent; dsh-m itself and official host lifelines are locked) |
42
42
  | **Settings** | Registry address draft with "Force refresh / Validate & apply / Restore default / Download default registry.json"; configured vs active address and status at a glance (shown only when abnormal); community catalog toggle; dsh-m self-update |
@@ -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
+ ### Changed in 0.9.15 — install progress/result now live inside the detail modal
52
+
53
+ - **Scenario**: clicking Install in the detail modal rendered all install feedback (the pnpm phase line, the "changes applied" banner/toast) on the panel layer beneath, half-visible through the modal mask — inside the modal the button just spun, with no sense of progress (live screenshot feedback, 2026-10-02). Uninstall never had this problem: no modal is involved and status already lives on the installed card.
54
+ - **Change**: while an install runs, the modal hosts the progress line itself (same host-status polling: phase / bar / current package); terminal states render an inline result row — success with version + build-script notes and a restart hint (desktop gets the official app lifecycle guide), failure / guard block with the reason and a re-enabled retry button, "skipped" presented neutrally; while the modal is open the underlying twin line steps aside (and returns once the modal closes). Everything still derives from the global operation log and the install result — the "state lives off cards" ownership model is unchanged (DESIGN §2.6).
55
+
51
56
  ### Changed in 0.9.14 (instant-open quartet + context-aware "Install command" row)
52
57
 
53
58
  - **Instant-open quartet** — a fix combo for the marketplace spinner that used to show on every panel open:
package/README.md CHANGED
@@ -36,7 +36,7 @@
36
36
 
37
37
  | 视图 | 能力 |
38
38
  |---|---|
39
- | **市场** | **分区制**(0.7.0):**社区**(默认落地页,4,000+ 条全量目录)与**精选**(手工策展主清单,单页直出)两个独立分区,各有自己的分类/搜索/排序/分页状态(切 tab 互不重置);另有**收藏**分区(浏览器本地书签,自动检测下架条目并一键清理)。社区区:分类 chips 两行折叠 + 吸顶收缩、排序(下载量/Star/收录日期 × 升降)、页码窗口化分页(24/48/96 每页)、卡片 byline(作者/下载量/Star);卡片点开**详情 Modal**(卡片信息超集:下载量窗口三要素、截图灯箱、能力披露默认收起、安装命令(0.9.14 起 desktop 上下文/已安装条目隐藏该折叠行));npm 源锁定最新精确版本,GitHub 源锁定 release/tag 指向的 commit |
39
+ | **市场** | **分区制**(0.7.0):**社区**(默认落地页,4,000+ 条全量目录)与**精选**(手工策展主清单,单页直出)两个独立分区,各有自己的分类/搜索/排序/分页状态(切 tab 互不重置);另有**收藏**分区(浏览器本地书签,自动检测下架条目并一键清理)。社区区:分类 chips 两行折叠 + 吸顶收缩、排序(下载量/Star/收录日期 × 升降)、页码窗口化分页(24/48/96 每页)、卡片 byline(作者/下载量/Star);卡片点开**详情 Modal**(卡片信息超集:下载量窗口三要素、截图灯箱、能力披露默认收起、安装命令(0.9.14 起 desktop 上下文/已安装条目隐藏该折叠行)、安装进度与结果就地显示(0.9.15 起));npm 源锁定最新精确版本,GitHub 源锁定 release/tag 指向的 commit |
40
40
  | **操作记录** | 安装/升级/卸载/开关全部走全局操作记录(0.7.0):状态不挂卡片,翻页/搜索/切 tab 不丢;localStorage 持久化,宿主重载后自动恢复未完成操作(逐条校验「此刻仍成立才执行」);良性前提消失以中性「已跳过」呈现;已装页支持「全部更新 (N)」批量入队 |
41
41
  | **已装** | 当前 profile(web / desktop,0.9.0 起)实装列表,标注「市场安装 / 非市场安装」;可升级徽标、升级、两段式确认卸载;📖 README 预览(64KB 截断);**运行相位徽标**(●active / ●failed / ○pending)与**一键开关**(0.4.0:委派官方 pluginManager 服务活体生效,服务缺席时文件级编辑 + 重启提示;dsh-m 自身与官方宿主命脉锁定不可开关) |
42
42
  | **设置** | registry 地址草稿 +「强制刷新 / 校验并应用 / 恢复默认 / 下载默认 registry.json」;配置地址、生效来源与状态一目了然(仅异常时提示);社区目录开关;dsh-m 自更新 |
@@ -48,6 +48,17 @@
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.16 变更:精选策展五桶分类法 + 严筛收录(23→19)+ 新收录 DSH TUI
52
+
53
+ - **策展分类法**:精选区分类从功能五分类(市场/工具/界面/搜索/其他)改为**策展五桶**——装机必备 / 崔添翼精选 / 我的自研 / 腾讯轻量云专区 / 观察区(chips 按此序,腾讯轻量云垫后)。分类语义从「插件是什么」转为「为什么值得进精选」;重叠归属按 装机必备 > 崔添翼精选 > 自研 > 腾讯轻量云 优先级归桶,功能属性转 tags 保留检索,旧分类值仍可作开放 slug 命中自定义源(ADR-0004 修订)。
54
+ - **严筛收录 23→19**:通道五件套(lark/qqbot/weixin/wecom/dingtalk)退出精选——社区层(4,000+ 条)仍收录可装,只是退出策展位;新增收录 **DSH TUI**(崔添翼 9/26 X 推荐:终端 TUI 客户端,`dsh-tui`,走 DSH 客户端契约 ctx.remote);better-sidebar 因优先级归装机必备,以 tag「崔添翼精选」保留 9/27 推荐出处。
55
+ - **同版本携带**(本地增强随发版收编):desktop 安装 enable 阶段失败自动重试一次(高频装卸/插件树重载竞速实证)+ 失败文案按 packageResult 精确化;市场页「缓存快照」横幅退役——stale 状态由设置页社区卡承接,浏览页不再提示临时缓存状态。
56
+
57
+ ### 0.9.15 变更:安装进度/终态就地进详情 Modal
58
+
59
+ - **场景**:从详情 Modal 点「安装」后,安装信息(pnpm 阶段进度行、「变更完成」横幅/toast)都渲染在面板底层,隔着 Modal 遮罩半透明透出——弹窗内只有按钮转圈,看不出装到哪一步(实机截图反馈 2026-10-02)。卸载无此问题:不弹 Modal,状态本来就挂在已装卡片上。
60
+ - **变更**:安装进行中,Modal 内直接挂进度行(复用 host status 轮询:阶段/进度条/当前包);终态在 Modal 内就地显示结果行——成功附版本与构建脚本说明 + 重启提示(desktop 给官方应用生命周期指引),失败/守卫拦截附原因且按钮回到可重试,「已跳过」中性呈现;Modal 打开时底层同源进度行让位(关闭 Modal 后照常回归)。信息全部派生自全局操作记录与安装结果,「状态不挂卡片」的所有权模型不变(DESIGN §2.6)。
61
+
51
62
  ### 0.9.14 变更(市场秒开四件套 + 详情 Modal 安装命令显隐)
52
63
 
53
64
  - **市场秒开四件套**:针对「每次打开市场必现『加载收录清单中…』」的加速组合拳——
@@ -277,7 +288,7 @@ dshm restart --yes
277
288
 
278
289
  除手工 curated 的主清单外,市场还会叠加一层**只读的社区清单**:锚定 npm 包 [`dsh-plugin-catalog`](https://www.npmjs.com/package/dsh-plugin-catalog)(CC0-1.0,awesome-dsh-plugin 全量目录,4,000+ 条),数据层与主清单合并去重(**主清单恒优先**,重名条目社区侧让位),展示层按「社区 / 精选」分区呈现(ADR-0004)——社区区不再显示与主清单重复的条目,精选区保持策展序单页直出。
279
290
 
280
- - **数据获取**:dist-tags 探测最新版本 → jsDelivr 按精确版本直取 → npmmirror → unpkg 三线路兜底;版本未变不重拉正文,TTL 内跳过探测。失败回落 `<缓存目录>/awesome/` 运行时缓存并**显式标注「缓存快照」**——过期数据绝不冒充最新。
291
+ - **数据获取**:dist-tags 探测最新版本 → jsDelivr 按精确版本直取 → npmmirror → unpkg 三线路兜底;版本未变不重拉正文,TTL 内跳过探测。失败回落 `<缓存目录>/awesome/` 运行时缓存先展示(先回缓存、后台自愈)——过期数据绝不冒充最新;缓存状态(缓存快照/目录版本/获取线路/更新时间)在**设置页**可见,浏览页不打扰。
281
292
  - **开关与锁定**:设置页 `communityCatalog` 开关(默认开,live 生效)、`communityCatalogPin` 可锁定目录版本(精确 semver);CLI 用 `DSHM_COMMUNITY_CATALOG=0` 退出、`DSHM_COMMUNITY_CATALOG_PIN` 锁版本。
282
293
  - **分类与搜索**:社区分类是开放集(已知 23 个分类带中英双语标签:中文由包内单一事实源维护、英文取上游目录,按界面语言下发);搜索走相关性加权管线(中英文双匹配);社区区排序可切(下载量/Star/收录日期 × 升降,默认下载量降序,无下载量 ≠ 0 下载)。
283
294
  - **安装语义**:社区条目与主清单走同一 `installEntry`(npm 锁精确版本 / GitHub 锁 commit SHA);市场浏览页只对 npm 条目做更新探测,GitHub 条目不做页面级探测(匿名配额 60 次/小时不可控)。
package/docs/DESIGN.md CHANGED
@@ -21,7 +21,7 @@
21
21
  - **严格 v1 schema**:顶层只允许 `version/plugins`,条目只允许 `id/name/description/category/tags/source/npm/github/homepage/icon`;未知字段、非法 ID/npm/GitHub/URL、重复 ID/tag、字段超限、`plugins` 超过 1,000 条均拒绝整份清单(不截断、不部分加载);超过 200 条提示性能边界。
22
22
  - **缓存模型**:cache 按 namespace 分目录(Host 与 Agent tools 用 `host/`,独立 CLI 用 `cli/`,互不删除),按带算法版本的 cacheKey 分文件(`CacheFile v2`:原子临时文件 + fsync + rename,0700/0600,symlink 拒写,同 key 进程内写锁)。**候选验证只写候选 cache,绝不 prune 旧源**;settings 写入成功后才 `commitActiveSource`:先原子写 accepted-source metadata(`host/active-source.json`,仅 Host),再清理非当前 custom cache;metadata 失败不 prune、prune 失败保留旧 cache,均只降级为 warning,不撤销已生效的配置。切换后只保留默认缓存与当前 custom 缓存。
23
23
  - **统一安全 HTTP**:所有 JSON/text/HEAD 请求共享同一 primitive——手动重定向(每跳校验协议、最多 3 跳、循环检测、signal 传播、返回最终 URL)+ 响应大小上限 + 超时。
24
- - **秒开缓存语义(0.9.14 SWR)**:`loadRegistry` 与社区获取链的 TTL 过期不再同步等网络——磁盘缓存存在即**先回 stale**(registry `source='default-cache'`、社区 `status='stale'` 既有契约与「缓存快照」横幅),后台**单飞**刷新写回 cache(同 key 进行中复用;不接调用者 signal,后台自愈不被请求 abort 腰斩;`force`/candidate/显式下载三路径不经 SWR)。默认双线路**粘性**:`loadDefaultChain` 读 cache 记录的上次成功 `source` 排序线路(稳定排序,cache 缺失 = 原序,force 同样生效)。社区 bg 自愈是 force flight——**不走同版本短路**(`if (!opts.force)` 守卫),每 TTL 至多全量重拉一次正文(同步路径的短路优化保留)。页条目 latest 探测缓存整体迁入 `latest-cache.ts`:内存 Map + `<cacheRoot>/latest/<ns>.json` 信封 write-through(`latest/` 在 nsDir 之外,规避 `pruneCaches` 顶层 `*.json` 清扫;懒 seed、损坏/版本不符整弃、24h 硬上限淘汰),跨服务重启存活。客户端默认首页响应存 localStorage(10min TTL、双 zone 分键、读侧再过 normalize 收敛)——面板打开先渲染快照后 background 换新。
24
+ - **秒开缓存语义(0.9.14 SWR)**:`loadRegistry` 与社区获取链的 TTL 过期不再同步等网络——磁盘缓存存在即**先回 stale**(registry `source='default-cache'`、社区 `status='stale'` 既有契约;stale 状态由设置页社区卡承接,市场页不提示临时缓存状态——0.9.15 修订),后台**单飞**刷新写回 cache(同 key 进行中复用;不接调用者 signal,后台自愈不被请求 abort 腰斩;`force`/candidate/显式下载三路径不经 SWR)。默认双线路**粘性**:`loadDefaultChain` 读 cache 记录的上次成功 `source` 排序线路(稳定排序,cache 缺失 = 原序,force 同样生效)。社区 bg 自愈是 force flight——**不走同版本短路**(`if (!opts.force)` 守卫),每 TTL 至多全量重拉一次正文(同步路径的短路优化保留)。页条目 latest 探测缓存整体迁入 `latest-cache.ts`:内存 Map + `<cacheRoot>/latest/<ns>.json` 信封 write-through(`latest/` 在 nsDir 之外,规避 `pruneCaches` 顶层 `*.json` 清扫;懒 seed、损坏/版本不符整弃、24h 硬上限淘汰),跨服务重启存活。客户端默认首页响应存 localStorage(10min TTL、双 zone 分键、读侧再过 normalize 收敛)——面板打开先渲染快照后 background 换新。
25
25
  - **live 语义**:registry 地址是 live 设置,应用成功即时生效(无需重启);只有**首次部署新 Host 代码**需要一次用户确认后的 DSH Web 重启。`timeoutMs`/`cacheTtlMin` 亦为 live 读取;agent 框架注册时的静态工具 deadline 不承诺热更新。
26
26
 
27
27
  ### 2.2 Schema(v1 定稿,严格校验)
@@ -40,7 +40,7 @@
40
40
  "id": "dsh-skins", // slug,唯一
41
41
  "name": "DSH Skins",
42
42
  "description": "中文描述", // v1 只有中文
43
- "category": "ui", // market|tools|ui|search|other 五选一
43
+ "category": "essentials", // 策展五桶:essentials|cui-picks|self-dev|tencent-lighthouse|watchlist 五选一(0.9.16 策展分类法;旧功能五分类退役)
44
44
  "tags": ["主题"],
45
45
  "source": "github", // npm|github
46
46
  "npm": "可选;source=npm 时必填",
@@ -72,7 +72,7 @@
72
72
  - **目录适配层**:原生条目 → 收录条目。`description.zh`(缺省回退 `en`;>500 字符截断并计数)→ description;`npm` 非空 → `source: npm`,否则 `source: github`(owner/repo 取自 url);`url` → homepage;tags 置空;**分类保留原生值不转译**(开放集,Q40);`stars/downloads/capabilities/screenshots` 等不进收录条目本体,仅在市场层作为旁路数据透传展示。id 合成:原生 `name` 是裸名(实测 4,377 条中 190 个重名),合成小写 `owner--name` 形态并满足 v1 id 规则(非法字符折叠为 `-`,超 64 字符截断加哈希尾缀),适配层内冲突追加序号。
73
73
  - **校验语义(Q43)**:社区清单独立校验——容器层严格(顶层键白名单、`plugins` 数组、32 MiB / 30,000 条上限,超限**整份拒收**)+ 条目层宽松(单条不合格**跳过 + 计数**,汇入 warnings,不整份拒绝)。主清单严格校验(§2.2)不变。**无 npm 的 monorepo 子包条目跳过并计数**(repo 根不是正确安装目标);有 npm 的子包条目按 npm 安装、正常收录;tarball-only 条目按 github 源收录(dsh-m 不安装 tarball)。
74
74
  - **分类(Q40)**:精选分类(5 个,主清单 schema 不变)∪ 社区分类(开放集)。已知 20 个社区分类带中文标签进筛选栏「社区」组;`ui`/`tools`/`market` 三 id 与精选同名、共享过滤桶;上游新增的未知分类原样渲染进「社区·新分类」临时组,发版收录标签。
75
- - **降级语义(Q42)**:主清单 unavailable + 社区可用 → 显示社区条目 + 顶部错误横幅 + 安装不禁用;社区 unavailable → 主清单照常 + 静默 notice;探测失败时回落 `<ns>/awesome/` **运行时缓存**并显式标注 stale(市场页可见「缓存快照」提示,绝不冒充 ready)——运行时缓存语义与主清单自定义源一致,被禁止的只是**包内快照**;社区**绝不**回退主清单伪装。
75
+ - **降级语义(Q42)**:主清单 unavailable + 社区可用 → 显示社区条目 + 顶部错误横幅 + 安装不禁用;社区 unavailable → 主清单照常 + 静默 notice;探测失败时回落 `<ns>/awesome/` **运行时缓存**并显式标注 stale(绝不冒充 ready)——运行时缓存语义与主清单自定义源一致,被禁止的只是**包内快照**;社区**绝不**回退主清单伪装。(**0.9.15 修订**:SWR 常态化后市场页不再显示「缓存快照」临时横幅——stale 标注移至设置页社区卡,「绝不冒充 ready / 过期可被观察到」的诚实约束不变。)
76
76
  - **开关与缓存**:`communityCatalog`(默认 true,volatile live 生效)+ `communityCatalogPin`(可选锁 npm 版本);CLI 用 `DSHM_COMMUNITY_CATALOG=0` 退出。社区缓存放 `<namespace>/awesome/` 子目录(不参与 `pruneCaches` 的顶层 `*.json` 清理),正文按版本文件缓存,`fetchedAt`/版本号/线路进设置页展示;TTL 复用 `cacheTtlMin`(只约束 dist-tags 探测频率)。
77
77
  - **市场行为(Q45/Q46)**:默认排序主清单置顶(组内维持原顺序)+ 社区按 30 天下载量降序(无数据按名称);搜索同时匹配中英文描述;「只看主清单」chip 常驻筛选栏首位。**探测边界**:市场浏览页对社区 npm 条目做 latest 探测(registry 无配额限制);社区 github 条目**不做**浏览页 REST 探测(GitHub 匿名 60 次/小时在 50 条/页 × 2 调用下不可控),更新检查收敛到详情/安装时的按需解析——配额耗尽时已有可读提示(versions.ts `githubRateLimitMessage`)。**已装页豁免**:探测对象受已装数量天然约束,继续探测;计量在**真实 GitHub HTTP 请求层**(一次 `githubLatestTag` ≈ 1–3 个请求:release 路径 1–2 个、fallback 路径 releases→tags→commits 最多 3 个):单请求 ≤25 次、宿主进程滚动 1 小时 ≤50 次(**仅作用于被动探测**——用户主动 install/upgrade/诊断不经此预算)、同仓库 in-flight single-flight、超限标 `latestError` 不阻塞列表——否则用户装的社区 github 插件永远没有更新徽标。独立 CLI 进程不共享宿主内预算,文档如实标注 best-effort(并发场景不承诺 60/h 绝不耗尽)。能力披露(capabilities/红线)只在详情折叠区展示(缺省 = 未扫描 ≠ 未检出),卡片不打标(Q44,防警告疲劳);截图仅详情层加载(GitHub 图床白名单由上游保证)。
78
78
 
@@ -89,7 +89,7 @@
89
89
  - 分页:页码窗口化(`1 … n-1 n n+1 … total`,≤7 页全显)+ 页大小 24/48/96 + 筛选变化重置页 1 + 翻页回顶;仅 prev/next 的旧分页退役。
90
90
  - 搜索:相关性加权管线——NFKC 归一化 + 中西文边界插空格 + 字段权重(name/npm > owner > 描述 > 分类)+ 命中类型加分 + 按条目缓存归一化结果;输入 250ms debounce + IME composition 全程处理 + draft/已提交 query 分离。
91
91
  - 描述 5 行钳制(真实溢出才显示展开钮);**数据层双语**——描述按 UI 语言取 zh/en(数据现成),UI 完整双语字典列 backlog。
92
- - **详情 = Modal 且为卡片超集**(「detail 显示少于摘要就是倒退」):byline / 分类 / 收录日期 / 下载量窗口三要素(计数 + 窗口 + 核对时间)/ 描述全文 / 截图灯箱(←→/Esc、**禁自动轮播**)/ 能力披露 + 红线(**默认收起**)/ 安装命令折叠行(0.9.14 起按上下文显隐:desktop 上下文与已安装条目整行隐藏——命令两来源恒为 `--profile web` 语义,desktop 装机走弹窗内安装按钮)/ deprecated 替代链接;精选条目超集另加 verified 与 tags。**安装确认走 Modal**。卡片瘦身:能力披露、截图、安装命令全部迁出卡片(Q44 精神「只在详情层、默认收起」不变,载体升级)。
92
+ - **详情 = Modal 且为卡片超集**(「detail 显示少于摘要就是倒退」):byline / 分类 / 收录日期 / 下载量窗口三要素(计数 + 窗口 + 核对时间)/ 描述全文 / 截图灯箱(←→/Esc、**禁自动轮播**)/ 能力披露 + 红线(**默认收起**)/ 安装命令折叠行(0.9.14 起按上下文显隐:desktop 上下文与已安装条目整行隐藏——命令两来源恒为 `--profile web` 语义,desktop 装机走弹窗内安装按钮)/ deprecated 替代链接;精选条目超集另加 verified 与 tags。**安装确认走 Modal**。安装信息就地显示(0.9.15):进行中进度行与终态结果行挂 Modal 内、底层同源进度行让位——展示派生自全局操作记录,§2.6「状态不挂卡片」所有权模型不变。卡片瘦身:能力披露、截图、安装命令全部迁出卡片(Q44 精神「只在详情层、默认收起」不变,载体升级)。
93
93
  - **全局操作记录**:每个变更操作一条 record(`queued / running / input=冲突待决 / done / warned / failed`),**状态不挂卡片**——翻页、搜索、切 tab 不丢;**localStorage 持久化队列**,宿主重载恢复时逐条校验「此刻仍成立才执行,否则报告」;覆盖安装、升级、卸载、开关。已装侧跟随一致化:「全部更新 (N)」批量入口 + tab 更新红点 + 卡片视觉与发现侧同体系;组管理、个人备注列 backlog。
94
94
  - **收藏**:浏览器 localStorage 本地收藏(不进 profile、不进服务端);收藏区 stale 条目(目录中已下架)单独提示 + 一键清理。
95
95
  - **agent/CLI 契约(允许破坏性变更)**:`dshm_search` 参数改为 `query / category / source('primary'|'community'|'all',默认 all,与 GUI 分区对齐) / limit(默认 10,clamp 1–80) / offset(真翻页)`;`primary_only` 删除。输出补 `community` 标记与 `downloads/stars`;社区分类直出中文标签(不再回退英文 slug);工具描述整体重写(真实规模 + 翻页语义,删「registry is curated & small」)。CLI 对齐 `--source / --offset / --limit`,默认 10 条。
@@ -118,6 +118,7 @@ npm 安装 / GitHub 安装 / 升级 / 自升级 / 卸载是**同一个事务模
118
118
  - **安装(GitHub 源)**:解析并**锁定 commit SHA**(`github:owner/repo#sha`)。核验采用**前态比对**:新 spec 必须相对快照是新写入(预置同 spec 旧依赖不误命中),否则 `GITHUB_SPEC_MISMATCH` 回滚。
119
119
  - **已装识别**:读 profile `package.json` dependencies,与 registry 匹配 → 标注「市场安装」;不匹配的也列出,标注「非市场安装 / 来源未知」。卸载/升级对两类都可用。
120
120
  - **卸载**(事务定序写死):validate(严格读取:NOT_INSTALLED / NOT_DSH_PLUGIN / PLUGIN_METADATA_UNREADABLE,全部零写入零快照)→ 快照 → live-disable(先让 client bundle 下线,避免 404;回滚时尽力反向)→ 摘除该包补丁条目(`pnpm-workspace.yaml` 顶层 `patchedDependencies` 与 `package.json#pnpm.patchedDependencies`;残留条目会令 pnpm 以 `ERR_PNPM_UNUSED_PATCH` 整单失败,只精确匹配 `pkg` / `pkg@ver`,补丁文件本体保留并计入残留报告)→ remove → verify gone(仍在则回滚)。**不清理插件产生的数据/配置**,但把检测到的疑似残留路径(如 `~/.dsh/<plugin>.json`)列出报告。
121
+ - **安装(Desktop profile 委派,0.9.0+)**:官方 pluginManager `installBundle(enabled: true)` → ChangeResult 以 application/stage 为准判定:cancelled 拒绝;build-blocked 走结构化审批重试(pending 名单原样回传);minimumReleaseAge 翻译成诚实等待指引(策略正确工作非故障);enable 失败按 `packageResult` 如实区分「包已写入 / 未能写入 / 官方未回传(未知)」——enable 先行失败(pnpm 未跑)时不得断言「包已写入」→ `listBundles` 复读在装(不冒充成功)。**enable 阶段失败自动重试一次**(本地增强 2026-10-02,未发版):应用侧 enable 与高频装卸/插件树重载竞速返回 `operation-error`(实机实证 dsh-copilot-auth 三装卸两败,失败 operation 的 pnpm.log 均为 0 字节),单次退避重试即愈;其余失败形态不重试。
121
122
  - **升级**:**按需检查**(`dshm_outdated` / `dshm_list` 时实时比对本地版本 vs npm latest / GitHub release,半自动——展示升级计划,确认后执行,即重开一次安装事务)。**不做后台定时器**。
122
123
  - **自更新**:dsh-m 对自己同样做版本比对 + 提示升级(设置页呈现);执行即 `install-npm` 事务(integrity 缺失直接拒绝,不进事务)。
123
124
  - **开关(0.4.0,术语见 GLOSSARY.md「开关/行覆盖/Bundle 选择/委派降级」)**:已装插件运行状态的可逆切换,**不经 Profile 变更事务**。粒度自动路由——包内补丁层单 insert 行且无配置补丁行 → 行覆盖(`cordis.patch.yml` 保注释 `disabled` 编辑,官方 `writePluginEnabled` 同款语义,启用写显式 `disabled: false`);多行/不可解析 → Bundle 选择(`dsh.profile.bundles` 数组增删)。写路径委派优先:运行时探测官方 `pluginManager` 服务(`ctx.get`,按存在性不判版本号)→ `setPluginEnabled`/`setBundleEnabled`(白拿保护判定、unaddressable 识别、hmr 活体重组;applied 以复读 `listPlugins()` 为准);服务缺席降级 loader `entry.update()` 直操作 + 锁内文件编辑(bundle 级恒 restart-required)。**必须持官方同款锁**(`withFileLock(<profile>/package.json)`)防与官方 UI 并发丢更新。架构决策见 [ADR-0001](./adr/0001-delegate-with-fallback-for-plugin-manager.md)。
package/lib/cli.js CHANGED
@@ -12,13 +12,13 @@ import { COMMUNITY_CATEGORY_LABELS } from './core/community.js';
12
12
  import { scheduleRestart } from './core/restart.js';
13
13
  import { pathToFileURL } from 'node:url';
14
14
  import { resolve } from 'node:path';
15
- /** 分类中文标签(0.7.0 Task 6):精选 5 类本地表 + 社区已知标签单一事实源;未知 slug 原样。 */
15
+ /** 分类中文标签(0.7.0 Task 6;0.9.16 策展五桶):精选策展桶本地表 + 社区已知标签单一事实源;未知 slug 原样。 */
16
16
  const CLI_CATEGORY_LABELS = {
17
- market: '市场',
18
- tools: '工具',
19
- ui: '界面',
20
- search: '搜索',
21
- other: '其他',
17
+ essentials: '装机必备',
18
+ 'cui-picks': '崔添翼精选',
19
+ 'self-dev': '我的自研',
20
+ 'tencent-lighthouse': '腾讯轻量云专区',
21
+ watchlist: '观察区',
22
22
  };
23
23
  function categoryLabelOf(category) {
24
24
  return CLI_CATEGORY_LABELS[category] ?? COMMUNITY_CATEGORY_LABELS[category] ?? category;
package/lib/client.js CHANGED
@@ -188,7 +188,6 @@ function zoneChips(categoryCounts, categoryLabels, zone) {
188
188
  function marketNotice(registryState, community) {
189
189
  const out = {};
190
190
  const c = community && typeof community === "object" ? community : {};
191
- if (c.status === "stale") out.communityStale = true;
192
191
  if (registryState && registryState.status === "unavailable" && (c.status === "ready" || c.status === "stale")) {
193
192
  out.communityFallback = true;
194
193
  }
@@ -202,8 +201,14 @@ var init_market_state = __esm({
202
201
  "use strict";
203
202
  MARKET_PAGE_SIZES = [24, 48, 96];
204
203
  DEFAULT_PAGE_SIZE = 24;
205
- CURATED_ORDER = ["market", "tools", "ui", "search", "other"];
206
- CURATED_LABELS = { market: "\u5E02\u573A", tools: "\u5DE5\u5177", ui: "\u754C\u9762", search: "\u641C\u7D22", other: "\u5176\u4ED6" };
204
+ CURATED_ORDER = ["essentials", "cui-picks", "self-dev", "tencent-lighthouse", "watchlist"];
205
+ CURATED_LABELS = {
206
+ essentials: "\u88C5\u673A\u5FC5\u5907",
207
+ "cui-picks": "\u5D14\u6DFB\u7FFC\u7CBE\u9009",
208
+ "self-dev": "\u6211\u7684\u81EA\u7814",
209
+ "tencent-lighthouse": "\u817E\u8BAF\u8F7B\u91CF\u4E91\u4E13\u533A",
210
+ watchlist: "\u89C2\u5BDF\u533A"
211
+ };
207
212
  CURATED_IDS = new Set(CURATED_ORDER);
208
213
  SLUG_RE = /^[a-z0-9-]{1,32}$/;
209
214
  SORT_FIELDS = /* @__PURE__ */ new Set(["downloads", "stars", "added"]);
@@ -1168,11 +1173,11 @@ var ZH = {
1168
1173
  "tab.installed": "\u5DF2\u88C5",
1169
1174
  "tab.settings": "\u8BBE\u7F6E",
1170
1175
  "cat.all": "\u5168\u90E8",
1171
- "cat.market": "\u5E02\u573A",
1172
- "cat.tools": "\u5DE5\u5177",
1173
- "cat.ui": "\u754C\u9762",
1174
- "cat.search": "\u641C\u7D22",
1175
- "cat.other": "\u5176\u4ED6",
1176
+ "cat.essentials": "\u88C5\u673A\u5FC5\u5907",
1177
+ "cat.cui-picks": "\u5D14\u6DFB\u7FFC\u7CBE\u9009",
1178
+ "cat.self-dev": "\u6211\u7684\u81EA\u7814",
1179
+ "cat.tencent-lighthouse": "\u817E\u8BAF\u8F7B\u91CF\u4E91\u4E13\u533A",
1180
+ "cat.watchlist": "\u89C2\u5BDF\u533A",
1176
1181
  "zone.community": "\u793E\u533A",
1177
1182
  "zone.primary": "\u7CBE\u9009",
1178
1183
  "zone.favorites": "\u6536\u85CF",
@@ -1329,7 +1334,6 @@ var ZH = {
1329
1334
  "market.page.info": "\u7B2C {page} / {pages} \u9875 \xB7 \u5171 {total} \u6761",
1330
1335
  "notice.toolview.err": "\u6536\u5F55\u6E05\u5355\u6682\u4E0D\u53EF\u7528",
1331
1336
  "badge.community": "\u793E\u533A\u6536\u5F55",
1332
- "community.stale": "\u793E\u533A\u76EE\u5F55\u4E3A\u7F13\u5B58\u5FEB\u7167\uFF08\u663E\u793A\u7684\u4E0D\u662F\u6700\u65B0\u6570\u636E\uFF09",
1333
1337
  "community.fallback": "\u6536\u5F55\u6E05\u5355\u4E0D\u53EF\u7528\uFF0C\u5F53\u524D\u5C55\u793A\u793E\u533A\u6E05\u5355\u6761\u76EE",
1334
1338
  "detail.capabilities": "\u80FD\u529B\u62AB\u9732",
1335
1339
  "detail.capabilities.unscanned": "\u672A\u626B\u63CF \u2260 \u672A\u68C0\u51FA",
@@ -1429,11 +1433,11 @@ var EN = {
1429
1433
  "tab.installed": "Installed",
1430
1434
  "tab.settings": "Settings",
1431
1435
  "cat.all": "All",
1432
- "cat.market": "Market",
1433
- "cat.tools": "Tools",
1434
- "cat.ui": "UI",
1435
- "cat.search": "Search",
1436
- "cat.other": "Other",
1436
+ "cat.essentials": "Essentials",
1437
+ "cat.cui-picks": "Cui Picks",
1438
+ "cat.self-dev": "Self-dev",
1439
+ "cat.tencent-lighthouse": "Tencent Lighthouse",
1440
+ "cat.watchlist": "Watchlist",
1437
1441
  "zone.community": "Community",
1438
1442
  "zone.primary": "Curated",
1439
1443
  "zone.favorites": "Favorites",
@@ -1590,7 +1594,6 @@ var EN = {
1590
1594
  "market.page.info": "Page {page} / {pages} \xB7 {total} listings",
1591
1595
  "notice.toolview.err": "Registry temporarily unavailable",
1592
1596
  "badge.community": "Community",
1593
- "community.stale": "Community catalog served from cache (not the latest data)",
1594
1597
  "community.fallback": "Registry unavailable \u2014 showing community listings",
1595
1598
  "detail.capabilities": "Capabilities",
1596
1599
  "detail.capabilities.unscanned": "Not scanned \u2260 not detected",
@@ -1739,6 +1742,7 @@ var CSS = `
1739
1742
  .dshm-body{flex:1;overflow:auto;padding:14px;display:flex;flex-direction:column;gap:12px}
1740
1743
  .dshm-hint{color:var(--dsw-alias-label-caption,#6b7280);font-size:12px;line-height:18px;margin:0}
1741
1744
  .dshm-err{color:var(--dsw-alias-state-error-primary,#b91c1c);font-size:12px;line-height:18px}
1745
+ .dshm-ok{color:var(--dsw-alias-state-success-primary,#047857);font-size:12px;line-height:18px;word-break:break-word}
1742
1746
  .dshm-btn{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-3,#fff);color:var(--dsw-alias-label-primary,inherit);border-radius:8px;padding:5px 12px;font:inherit;font-size:12px;cursor:pointer;white-space:nowrap}
1743
1747
  .dshm-btn:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}
1744
1748
  .dshm-btn:disabled{opacity:.5;cursor:default}
@@ -2388,7 +2392,7 @@ function useModalDepth(active) {
2388
2392
  };
2389
2393
  }, [active]);
2390
2394
  }
2391
- function DetailModal({ it, labels, busy, onClose, onInstall, profileKind }) {
2395
+ function DetailModal({ it, labels, busy, onClose, onInstall, profileKind, installRec, installNote }) {
2392
2396
  useModalDepth(true);
2393
2397
  const shots = it.community === true ? safeScreenshots(it) : [];
2394
2398
  const [lb, setLb] = useState(null);
@@ -2407,7 +2411,7 @@ function DetailModal({ it, labels, busy, onClose, onInstall, profileKind }) {
2407
2411
  document.addEventListener("keydown", onKey);
2408
2412
  return () => document.removeEventListener("keydown", onKey);
2409
2413
  }, [lb, shots.length, onClose]);
2410
- const catLabel = labels && labels[it.category] || (["market", "tools", "ui", "search", "other"].includes(it.category) ? lookup("cat." + it.category) : it.category);
2414
+ const catLabel = labels && labels[it.category] || (["essentials", "cui-picks", "self-dev", "tencent-lighthouse", "watchlist"].includes(it.category) ? lookup("cat." + it.category) : it.category);
2411
2415
  const installCmd = typeof it.install === "string" && it.install !== "" ? it.install : it.source === "npm" ? `dsh plugin --profile web add ${it.npm}` : `dsh plugin --profile web add github:${it.github}`;
2412
2416
  const copyCmd = async () => {
2413
2417
  try {
@@ -2490,6 +2494,13 @@ function DetailModal({ it, labels, busy, onClose, onInstall, profileKind }) {
2490
2494
  h("button", { className: "dshm-btn sm", onClick: copyCmd }, copied ? lookup("modal.copied") : lookup("modal.copy"))
2491
2495
  )
2492
2496
  ) : null,
2497
+ // 0.9.15:安装信息就地进 Modal(主人反馈 2026-10-02:进度/终态原先只隔遮罩在底层透出)。
2498
+ // 进度行复用 ProgressLine(自轮询 host status);底层同源行由 MarketTab 在本 Modal 打开时让位。
2499
+ // installRec 派生自全局操作记录(DESIGN §2.6「状态不挂卡片」的所有权模型不变,此处只是展示):
2500
+ // 仅 queued/running 挂行,input(兼容待决)走上层 CompatDialog;installNote 为本次终态摘要。
2501
+ installRec && (installRec.status === "running" || installRec.status === "queued") ? h(ProgressLine, { key: "opprog" }) : null,
2502
+ installNote ? h("div", { key: "opnote", className: installNote.kind === "err" ? "dshm-err" : installNote.kind === "hint" ? "dshm-hint" : "dshm-ok" }, installNote.text) : null,
2503
+ installNote && installNote.kind === "ok" ? h("div", { key: "opnote-rh", className: "dshm-hint" }, lookup(profileKind === "desktop" ? "profile.restartHint" : "banner.done")) : null,
2493
2504
  h(
2494
2505
  "div",
2495
2506
  { className: "dsvm-modalactions" },
@@ -2523,7 +2534,9 @@ function MarketTab({ notify, markets, onMutation, ops, favorites, profileKind })
2523
2534
  }, [filterOpen]);
2524
2535
  const [favDetailItem, setFavDetailItem] = useState(null);
2525
2536
  const [pageJump, setPageJump] = useState("");
2526
- const activeInstallTarget = (ops.records.find((r) => r.kind === "install" && (r.status === "running" || r.status === "queued" || r.status === "input")) || {}).target || null;
2537
+ const activeInstallRec = ops.records.find((r) => r.kind === "install" && (r.status === "running" || r.status === "queued" || r.status === "input")) || null;
2538
+ const activeInstallTarget = activeInstallRec ? activeInstallRec.target : null;
2539
+ const [installNote, setInstallNote] = useState(null);
2527
2540
  const items = data && data.items || [];
2528
2541
  const total = data && data.total || 0;
2529
2542
  const limit = data && data.limit || DEFAULT_PAGE_SIZE2;
@@ -2571,14 +2584,13 @@ function MarketTab({ notify, markets, onMutation, ops, favorites, profileKind })
2571
2584
  h(SearchBox, { key: zone, placeholder: lookup("search.ph"), initial: query.query, onCommit: (v) => updateQuery({ query: v }) })
2572
2585
  ) : null;
2573
2586
  const zoneBar = zoneChips3;
2574
- const installDone = (res) => {
2575
- notify({
2576
- kind: "ok",
2577
- needsRestart: true,
2578
- text: lookup("notify.installed", { pkg: res.pkg, version: res.version ? ` v${res.version}` : "" }) + (res.buildApprovals && res.buildApprovals.length ? lookup("notify.builds", { names: res.buildApprovals.join(", ") }) : res.fallbackAllBuilds ? lookup("notify.builds.fallback") : "") + (res.bundleWarning === "no-patch-layer" ? lookup("notify.bundlewarning") : "")
2579
- });
2587
+ const installResultText = (res) => lookup("notify.installed", { pkg: res.pkg, version: res.version ? ` v${res.version}` : "" }) + (res.buildApprovals && res.buildApprovals.length ? lookup("notify.builds", { names: res.buildApprovals.join(", ") }) : res.fallbackAllBuilds ? lookup("notify.builds.fallback") : "") + (res.bundleWarning === "no-patch-layer" ? lookup("notify.bundlewarning") : "");
2588
+ const installDone = (it2, res) => {
2589
+ notify({ kind: "ok", needsRestart: true, text: installResultText(res) });
2590
+ setInstallNote({ id: it2.id, kind: "ok", text: installResultText(res) });
2580
2591
  };
2581
2592
  const doInstall = async (it, version, forceIncompatible, reuseOpId) => {
2593
+ setInstallNote(null);
2582
2594
  try {
2583
2595
  const res = await ops.runOp(
2584
2596
  "install",
@@ -2589,26 +2601,28 @@ function MarketTab({ notify, markets, onMutation, ops, favorites, profileKind })
2589
2601
  reuseOpId
2590
2602
  // 审计 #12:确认/重试复用同一记录,不再新开
2591
2603
  );
2592
- installDone(res);
2604
+ installDone(it, res);
2593
2605
  await (onMutation ? onMutation() : reload(false));
2594
2606
  } catch (e) {
2595
2607
  if (e && e.opSuperseded) {
2596
2608
  notify({ kind: "ok", text: lookup("op.superseded.note", { target: it.name }) });
2609
+ setInstallNote({ id: it.id, kind: "hint", text: lookup("op.superseded.note", { target: it.name }) });
2597
2610
  } else if (e && e.guard) {
2598
- notify({
2599
- kind: "err",
2600
- text: [
2601
- lookup("guard.blocked"),
2602
- `${lookup("guard.compstatus")}: ${e.guard.compensation?.status || "\u2014"}\uFF08${e.guard.compensation?.note || ""}\uFF09`,
2603
- e.guard.repairBasis ? `${lookup("guard.repairbasis")}: ${e.guard.repairBasis}` : null,
2604
- lookup("guard.noforce"),
2605
- e.guard.restartSafe ? lookup("guard.restartsafenow") : lookup("guard.restartunsafe")
2606
- ].filter(Boolean).join(" | ")
2607
- });
2611
+ const guardText = [
2612
+ lookup("guard.blocked"),
2613
+ `${lookup("guard.compstatus")}: ${e.guard.compensation?.status || "\u2014"}\uFF08${e.guard.compensation?.note || ""}\uFF09`,
2614
+ e.guard.repairBasis ? `${lookup("guard.repairbasis")}: ${e.guard.repairBasis}` : null,
2615
+ lookup("guard.noforce"),
2616
+ e.guard.restartSafe ? lookup("guard.restartsafenow") : lookup("guard.restartunsafe")
2617
+ ].filter(Boolean).join(" | ");
2618
+ notify({ kind: "err", text: guardText });
2619
+ setInstallNote({ id: it.id, kind: "err", text: guardText });
2608
2620
  } else if (e && e.issue) {
2609
2621
  setCompatConfirm({ it, version, issue: e.issue, opId: e.opId });
2610
2622
  } else {
2611
- notify({ kind: "err", text: lookup("failed.install", { err: e && e.message || e }) });
2623
+ const failText = lookup("failed.install", { err: e && e.message || e });
2624
+ notify({ kind: "err", text: failText });
2625
+ setInstallNote({ id: it.id, kind: "err", text: failText });
2612
2626
  }
2613
2627
  }
2614
2628
  };
@@ -2683,7 +2697,9 @@ function MarketTab({ notify, markets, onMutation, ops, favorites, profileKind })
2683
2697
  busy: activeInstallTarget === favDetailItem.id,
2684
2698
  onClose: () => setFavDetailItem(null),
2685
2699
  onInstall: (it2) => doInstall(it2),
2686
- profileKind
2700
+ profileKind,
2701
+ installRec: activeInstallRec && activeInstallRec.target === favDetailItem.id ? activeInstallRec : null,
2702
+ installNote: installNote && installNote.id === favDetailItem.id ? installNote : null
2687
2703
  }) : null
2688
2704
  );
2689
2705
  }
@@ -2738,7 +2754,6 @@ function MarketTab({ notify, markets, onMutation, ops, favorites, profileKind })
2738
2754
  searchRow,
2739
2755
  notice && notice.notice ? h("div", { className: "dshm-err" }, lookup(notice.notice.key)) : null,
2740
2756
  notice && notice.communityFallback ? h("div", { className: "dshm-hint" }, lookup("community.fallback")) : null,
2741
- notice && notice.communityStale ? h("div", { className: "dshm-hint" }, lookup("community.stale")) : null,
2742
2757
  h(ZoneChips, {
2743
2758
  zone,
2744
2759
  counts,
@@ -2747,7 +2762,9 @@ function MarketTab({ notify, markets, onMutation, ops, favorites, profileKind })
2747
2762
  onPick: (id) => updateQuery({ category: id, offset: 0 }),
2748
2763
  trailing: filterTrigger
2749
2764
  }),
2750
- activeInstallTarget ? h(ProgressLine, { key: "prog" }) : null,
2765
+ // 0.9.15:安装目标条目的详情 Modal 打开时,进度行入 Modal、底层行让位(避免隔着遮罩双重透出);
2766
+ // Modal 关闭后底层行照常回归(关闭弹窗的安装仍可见)
2767
+ activeInstallTarget && !(detailItem && detailItem.id === activeInstallTarget) ? h(ProgressLine, { key: "prog" }) : null,
2751
2768
  loading && !data ? h("div", { className: "dshm-empty" }, lookup("market.loading"), Spin()) : error ? h("div", { className: "dshm-err" }, lookup("failed.load", { err: error })) : items.length === 0 ? h("div", { className: "dshm-empty" }, lookup("market.empty")) : h(
2752
2769
  React.Fragment,
2753
2770
  null,
@@ -2841,7 +2858,9 @@ function MarketTab({ notify, markets, onMutation, ops, favorites, profileKind })
2841
2858
  busy: activeInstallTarget === detailItem.id,
2842
2859
  onClose: () => setDetailId(null),
2843
2860
  onInstall: (it2) => doInstall(it2),
2844
- profileKind
2861
+ profileKind,
2862
+ installRec: activeInstallRec && activeInstallRec.target === detailItem.id ? activeInstallRec : null,
2863
+ installNote: installNote && installNote.id === detailItem.id ? installNote : null
2845
2864
  }) : null
2846
2865
  );
2847
2866
  }
@@ -329,7 +329,7 @@ export function createApiDispatcher(ctx) {
329
329
  const limit = Number.isFinite(limitRaw) && limitRaw > 0 ? Math.min(96, Math.max(1, Math.floor(limitRaw))) : 24;
330
330
  const offsetRaw = Number(body.offset);
331
331
  const offset = Number.isFinite(offsetRaw) && offsetRaw > 0 ? Math.floor(offsetRaw) : 0;
332
- // category:精选 5 + 社区开放 slug(非法 slug → 400,不静默吞)
332
+ // category:策展五桶 + 社区开放 slug(非法 slug → 400,不静默吞)
333
333
  const categoryRaw = typeof body.category === 'string' ? body.category.trim() : '';
334
334
  let category = null;
335
335
  if (categoryRaw !== '') {
@@ -90,7 +90,7 @@ export async function mapWithConcurrency(items, limit, worker) {
90
90
  return results;
91
91
  }
92
92
  function zeroCounts() {
93
- return { market: 0, tools: 0, ui: 0, search: 0, other: 0 };
93
+ return { essentials: 0, 'cui-picks': 0, 'self-dev': 0, 'tencent-lighthouse': 0, watchlist: 0 };
94
94
  }
95
95
  function clampLimit(raw, max) {
96
96
  const n = typeof raw === 'number' && Number.isFinite(raw) ? Math.floor(raw) : max;
@@ -179,17 +179,42 @@ export async function desktopToggle(pkg, enabled, deps = {}) {
179
179
  * desktopInstall / desktopUpgrade 共用;build-blocked 以 DesktopBuildApprovalNeeded 抛出,
180
180
  * 由调用方映射各自的返回形态。判定纪律沿用文件头「报告 §5.2」(dsh-market #703/#772 实证):
181
181
  * 以 application/stage 为准,绝不只看 packageResult.exitCode;overridden 非失败。
182
+ *
183
+ * 本地增强(2026-10-02,未随计划发版):enable 阶段失败自动重试一次 + 文案按 packageResult 精确化。
184
+ * 背景:desktop 高频装卸/插件树重载窗口下,官方管理器 enable 阶段与应用侧操作竞速,返回
185
+ * application=failed/stage=enable/error=operation-error(实机实证 dsh-copilot-auth 三装卸两败,
186
+ * 失败 operation 的 pnpm.log 均为 0 字节——失败发生在 pnpm 之前的应用侧 enable,重试即愈)。
182
187
  */
188
+ let enableRetryDelayMs = 1_200;
189
+ /** 测试钩子:调整 enable 重试退避毫秒(0 = 立即重试)。 */
190
+ export function _setEnableRetryDelayForTests(ms) {
191
+ enableRetryDelayMs = Math.max(0, ms);
192
+ }
193
+ const sleepMs = (ms) => new Promise((r) => setTimeout(r, ms));
194
+ /** enable 阶段失败文案:按 packageResult 如实区分「已写入 / 未写入 / 未知」——
195
+ * enable 先行失败(pnpm 未跑)时旧文案「包已写入」是错误断言(实机实证)。 */
196
+ function enableStageFailureMessage(pkg, change) {
197
+ const pr = change.packageResult;
198
+ let writtenText;
199
+ if (pr && typeof pr === 'object') {
200
+ const written = pr.exitCode === 0 || (typeof pr.kind === 'string' && /applied|ok|installed|success/i.test(pr.kind));
201
+ writtenText = written ? '包已写入但未能启用' : '包未能写入(启用注册先行失败,安装步骤未执行)';
202
+ }
203
+ else {
204
+ writtenText = '启用阶段失败(官方未回传包写入结果,是否写入未知)';
205
+ }
206
+ return `安装后启用阶段失败(${pkg};application=failed、stage=enable${change.error?.code ? `、error=${change.error.code}` : ''}):${writtenText},请在官方 Desktop 插件页查看状态或重试`;
207
+ }
183
208
  async function runManagedInstall(service, pkg, spec, approvedBuilds) {
184
- const raw = await service.installBundle(spec, {
209
+ const installOnce = async () => changeOf(await service.installBundle(spec, {
185
210
  enabled: true,
186
211
  ...(approvedBuilds && approvedBuilds.length > 0 ? { approvedBuilds } : {}),
187
- });
188
- const change = changeOf(raw);
189
- if (change.application === 'cancelled') {
190
- throw new DesktopOpsError('install-refused', `安装已被取消(${pkg}):desktop profile 文件已由官方管理器恢复,未安装`);
191
- }
192
- if (change.application === 'failed' || change.error) {
212
+ }));
213
+ /** 失败映射(既有语义不变:cancelled / build-blocked 审批 / release-age 等待 / enable / 通用)。 */
214
+ const mapFailure = (change) => {
215
+ if (change.application === 'cancelled') {
216
+ throw new DesktopOpsError('install-refused', `安装已被取消(${pkg}):desktop profile 文件已由官方管理器恢复,未安装`);
217
+ }
193
218
  const pending = Array.isArray(change.pendingBuilds) ? change.pendingBuilds.filter((n) => typeof n === 'string') : [];
194
219
  if (change.packageResult?.kind === 'build-blocked' && pending.length > 0) {
195
220
  throw new DesktopBuildApprovalNeeded(pending);
@@ -200,23 +225,36 @@ async function runManagedInstall(service, pkg, spec, approvedBuilds) {
200
225
  throw new DesktopOpsError('release-age-wait', releaseAgeWaitMessage(diagnosticText));
201
226
  }
202
227
  if (change.stage === 'enable') {
203
- throw new DesktopOpsError('enable-failed', `安装后启用阶段失败(${pkg};application=failed、stage=enable${change.error?.code ? `、error=${change.error.code}` : ''}):包已写入但未能启用,请在官方 Desktop 插件页查看状态或重试`);
228
+ throw new DesktopOpsError('enable-failed', enableStageFailureMessage(pkg, change));
204
229
  }
205
230
  throw new DesktopOpsError('install-refused', `官方管理器安装失败(${pkg};application=${change.application ?? 'failed'}${change.error?.code ? `、error=${change.error.code}` : ''}${change.error?.diagnostic ? `:${change.error.diagnostic}` : ''})`);
206
- }
207
- // 复读校验:listBundles 可用必须见目标在装;不可用(老管理器)至少要求官方宣称产出 bundle
208
- const bundleName = change.bundle ?? pkg;
209
- if (typeof service.listBundles === 'function') {
210
- const bundles = await service.listBundles();
211
- const found = (bundles ?? []).find((b) => b && b.name === bundleName);
212
- if (!found || found.installed === false) {
213
- throw new DesktopOpsError('verify-failed', `安装复读失败(${pkg}):官方结果为 ${change.application ?? 'unknown'} 但 listBundles 未見目标 bundle 在装——不冒充安装成功`);
231
+ };
232
+ /** 复读校验:listBundles 可用必须见目标在装;不可用(老管理器)至少要求官方宣称产出 bundle。 */
233
+ const verifyAndBundle = async (change) => {
234
+ const bundleName = change.bundle ?? pkg;
235
+ if (typeof service.listBundles === 'function') {
236
+ const bundles = await service.listBundles();
237
+ const found = (bundles ?? []).find((b) => b && b.name === bundleName);
238
+ if (!found || found.installed === false) {
239
+ throw new DesktopOpsError('verify-failed', `安装复读失败(${pkg}):官方结果为 ${change.application ?? 'unknown'} 但 listBundles 未見目标 bundle 在装——不冒充安装成功`);
240
+ }
214
241
  }
242
+ else if (!change.bundle) {
243
+ throw new DesktopOpsError('verify-failed', `安装复读不可用(${pkg}):官方结果未携带 bundle 字段且 listBundles 缺席——不冒充安装成功`);
244
+ }
245
+ return { change, bundleName };
246
+ };
247
+ let change = await installOnce();
248
+ // 本地增强:enable 阶段失败自动重试一次(受限单次 + 短退避;与高频装卸/插件树重载的竞速重试即愈;
249
+ // cancelled / build-blocked / release-age 等其他失败形态不重试)
250
+ if (change.stage === 'enable' && (change.application === 'failed' || change.error)) {
251
+ if (enableRetryDelayMs > 0)
252
+ await sleepMs(enableRetryDelayMs);
253
+ change = await installOnce();
215
254
  }
216
- else if (!change.bundle) {
217
- throw new DesktopOpsError('verify-failed', `安装复读不可用(${pkg}):官方结果未携带 bundle 字段且 listBundles 缺席——不冒充安装成功`);
218
- }
219
- return { change, bundleName };
255
+ if (change.application === 'failed' || change.application === 'cancelled' || change.error)
256
+ mapFailure(change);
257
+ return verifyAndBundle(change);
220
258
  }
221
259
  /**
222
260
  * Desktop 卸载(0.9.8,dsh-market 同策略):removeBundle 委派 + ChangeResult 判定 +
@@ -10,7 +10,7 @@ import { constants as fsConstants, mkdirSync } from 'node:fs';
10
10
  import { lstat, mkdir, open, readFile, readdir, realpath, rename, rm } from 'node:fs/promises';
11
11
  import { WEB_PROFILE, cacheRoot } from './env.js';
12
12
  import { decodeUtf8Fatal, fetchJsonLimitedMeta, describeFetchFailure } from './httpx.js';
13
- export const CATEGORIES = ['market', 'tools', 'ui', 'search', 'other'];
13
+ export const CATEGORIES = ['essentials', 'cui-picks', 'self-dev', 'tencent-lighthouse', 'watchlist'];
14
14
  /** 容量上限:2 MiB 是原始 UTF-8 bytes(不是字符数);条目超限拒绝整份清单。 */
15
15
  export const MAX_REGISTRY_BYTES = 2 * 1024 * 1024;
16
16
  export const MAX_PLUGINS = 1000;
package/lib/tools.js CHANGED
@@ -7,11 +7,11 @@ import { togglePlugin as coreTogglePlugin } from './core/toggle.js';
7
7
  import { resolveActiveProfile, assertWriteAllowed } from './core/active-profile.js';
8
8
  import { desktopInstallFromRegistry, desktopToggle, desktopUninstall, desktopUpgradeFromRegistry } from './core/profile-ops.js';
9
9
  export const CATEGORY_LABELS = {
10
- market: '市场',
11
- tools: '工具',
12
- ui: '界面',
13
- search: '搜索',
14
- other: '其他',
10
+ essentials: '装机必备',
11
+ 'cui-picks': '崔添翼精选',
12
+ 'self-dev': '我的自研',
13
+ 'tencent-lighthouse': '腾讯轻量云专区',
14
+ watchlist: '观察区',
15
15
  };
16
16
  /** 分类中文标签(0.7.0 Task 5):精选 5 类 + 社区已知标签(单一事实源 COMMUNITY_CATEGORY_LABELS);未知 slug 原样。 */
17
17
  function categoryLabelOf(category) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-m",
3
- "version": "0.9.14",
3
+ "version": "0.9.16",
4
4
  "description": "可自定义收录清单的 DSH 插件市场:收录、安装、卸载、升级一站完成,附 agent 工具与 dshm CLI。",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/registry.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "id": "dsh-m",
6
6
  "name": "DSH Marketplace",
7
7
  "description": "自定义清单的 DSH 插件市场,收录/安装/卸载/升级;已适配 DSH 0.2.0-rc.2,支持 web 与 desktop 双 profile,详见仓库。",
8
- "category": "market",
8
+ "category": "essentials",
9
9
  "tags": [
10
10
  "市场",
11
11
  "插件管理"
@@ -24,15 +24,79 @@
24
24
  "github": "iasiv5/dsh-m",
25
25
  "homepage": "https://github.com/iasiv5/dsh-m"
26
26
  },
27
+ {
28
+ "id": "dsh-better-sidebar",
29
+ "name": "DSH Better Sidebar",
30
+ "description": "开放的侧边栏底座,支持三方拓展注册侧边栏页面;内置文件编辑、终端、侧边对话、Git 与子代理页。",
31
+ "category": "essentials",
32
+ "tags": [
33
+ "侧边栏",
34
+ "文件编辑",
35
+ "终端",
36
+ "Git",
37
+ "崔添翼精选"
38
+ ],
39
+ "source": "npm",
40
+ "npm": "dsh-better-sidebar",
41
+ "github": "omdsh-dev/DSH-better-sidebar",
42
+ "homepage": "https://github.com/omdsh-dev/DSH-better-sidebar"
43
+ },
44
+ {
45
+ "id": "modsearch",
46
+ "name": "ModSearch",
47
+ "description": "免费网页搜索、X 搜索与页面抓取插件,支持 Firecrawl、Tavily、Exa 等引擎自动故障切换。",
48
+ "category": "essentials",
49
+ "tags": [
50
+ "搜索",
51
+ "网页抓取",
52
+ "X"
53
+ ],
54
+ "source": "npm",
55
+ "npm": "@liustack/modsearch",
56
+ "github": "liustack/modsearch",
57
+ "homepage": "https://github.com/liustack/modsearch"
58
+ },
59
+ {
60
+ "id": "dsh-tui",
61
+ "name": "DSH TUI",
62
+ "description": "终端 TUI 客户端:把 DeepSeek Harness 会话搬进终端,基于 DSH 客户端契约远程连接使用。",
63
+ "category": "cui-picks",
64
+ "tags": [
65
+ "终端",
66
+ "TUI",
67
+ "远程"
68
+ ],
69
+ "source": "npm",
70
+ "npm": "dsh-tui",
71
+ "github": "MashedPotato817/dsh-tui",
72
+ "homepage": "https://github.com/MashedPotato817/dsh-tui"
73
+ },
74
+ {
75
+ "id": "dsh-context",
76
+ "name": "DSH Context",
77
+ "description": "上下文透视:Context 仪表盘、侧栏面板与 /context 命令,查看上下文组成、演进趋势与压缩剪枝等事件,支持跨会话用量统计。",
78
+ "category": "cui-picks",
79
+ "tags": [
80
+ "上下文",
81
+ "Token 统计",
82
+ "可视化",
83
+ "仪表盘"
84
+ ],
85
+ "source": "npm",
86
+ "npm": "dsh-context",
87
+ "github": "bowenliang123/dsh-context",
88
+ "homepage": "https://github.com/bowenliang123/dsh-context"
89
+ },
27
90
  {
28
91
  "id": "dsh-skins",
29
92
  "name": "DSH Skins",
30
93
  "description": "DSH Web 皮肤插件:多套品牌皮肤侧栏热切换,一键回到官方界面。已适配 0.1.7-rc.2 与 0.2.0-rc.1/rc.2,详见仓库。",
31
- "category": "ui",
94
+ "category": "self-dev",
32
95
  "tags": [
33
96
  "皮肤",
34
97
  "主题",
35
- "美化"
98
+ "美化",
99
+ "界面"
36
100
  ],
37
101
  "verified": [
38
102
  "0.1.2-rc.1",
@@ -49,11 +113,12 @@
49
113
  "id": "dsh-copilot-auth",
50
114
  "name": "DSH Copilot Auth",
51
115
  "description": "为内置 Copilot 提供方补登录/注销设置页与开箱路由;模型目录兜底不覆盖定制。适配 0.1.7-rc.2 与 0.2.0-rc.1,旧版用 1.1.x。",
52
- "category": "tools",
116
+ "category": "self-dev",
53
117
  "tags": [
54
118
  "Copilot",
55
119
  "认证",
56
- "模型接入"
120
+ "模型接入",
121
+ "工具"
57
122
  ],
58
123
  "verified": [
59
124
  "0.1.7-rc.2",
@@ -68,11 +133,12 @@
68
133
  "id": "dsh-skip-browser-auth",
69
134
  "name": "DSH Skip Browser Auth",
70
135
  "description": "替换官方 Connection 跳过 BrowserAuth,免 Token;已适配 0.1.2-rc.1、0.1.5-rc.1/rc.2、0.1.7-rc.1/rc.2 与 0.2.0-rc.1/rc.2。",
71
- "category": "tools",
136
+ "category": "self-dev",
72
137
  "tags": [
73
138
  "BrowserAuth",
74
139
  "连接",
75
- "安全"
140
+ "安全",
141
+ "工具"
76
142
  ],
77
143
  "verified": [
78
144
  "0.1.2-rc.1",
@@ -92,7 +158,7 @@
92
158
  "id": "dsh-surf",
93
159
  "name": "DSH Surf",
94
160
  "description": "网络冲浪入口:设置一级菜单一键打开自托管 Selkies/Chrome 远程桌面,视频流直连;需自建容器并挂认证反代。",
95
- "category": "tools",
161
+ "category": "self-dev",
96
162
  "tags": [
97
163
  "浏览器",
98
164
  "远程桌面",
@@ -111,7 +177,7 @@
111
177
  "id": "dsh-obmc-web",
112
178
  "name": "DSH OBMC Web",
113
179
  "description": "在 DSH 里打开 OpenBMC 控制台:同源代理走 SSH 隧道,设置面板一键开启,支持 IP 探测与隧道重定向;需 SSH 跳板可达 BMC。",
114
- "category": "tools",
180
+ "category": "self-dev",
115
181
  "tags": [
116
182
  "OpenBMC",
117
183
  "远程管理",
@@ -131,11 +197,12 @@
131
197
  "id": "dsh-onetree-log",
132
198
  "name": "OneTree Log",
133
199
  "description": "OneTree/dreport 日志包上传解包,14 域清单与预览;可登记服务器本地路径;已适配 0.1.7-rc.2,详见仓库。",
134
- "category": "tools",
200
+ "category": "self-dev",
135
201
  "tags": [
136
202
  "日志",
137
203
  "OneTree",
138
- "诊断"
204
+ "诊断",
205
+ "工具"
139
206
  ],
140
207
  "verified": [
141
208
  "0.1.7-rc.2"
@@ -149,7 +216,7 @@
149
216
  "id": "dsh-quota-watch",
150
217
  "name": "DSH Quota Watch",
151
218
  "description": "GLM 与 Copilot 额度悬浮球:胶囊、详情面板与侧栏卡片,宿主解析凭据,适配 web 与 desktop。",
152
- "category": "tools",
219
+ "category": "self-dev",
153
220
  "tags": [
154
221
  "配额",
155
222
  "悬浮球",
@@ -166,56 +233,56 @@
166
233
  "homepage": "https://github.com/iasiv5/dsh-quota-watch"
167
234
  },
168
235
  {
169
- "id": "dsh-better-sidebar",
170
- "name": "DSH Better Sidebar",
171
- "description": "开放的侧边栏底座,支持三方拓展注册侧边栏页面;内置文件编辑、终端、侧边对话、Git 与子代理页。",
172
- "category": "ui",
236
+ "id": "dsh-auth",
237
+ "name": "DSH Auth",
238
+ "description": "DSH Web 管理员认证:Argon2id 口令与可撤销会话,经 Caddy forward_auth 保护页面、API、下载、SSE 与 WebSocket。",
239
+ "category": "tencent-lighthouse",
173
240
  "tags": [
174
- "侧边栏",
175
- "文件编辑",
176
- "终端",
177
- "Git"
241
+ "认证",
242
+ "安全",
243
+ "工具"
178
244
  ],
179
245
  "source": "npm",
180
- "npm": "dsh-better-sidebar",
181
- "github": "omdsh-dev/DSH-better-sidebar",
182
- "homepage": "https://github.com/omdsh-dev/DSH-better-sidebar"
246
+ "npm": "dsh-auth",
247
+ "github": "hxy91819/dsh-auth",
248
+ "homepage": "https://github.com/hxy91819/dsh-auth"
183
249
  },
184
250
  {
185
- "id": "modsearch",
186
- "name": "ModSearch",
187
- "description": "免费网页搜索、X 搜索与页面抓取插件,支持 Firecrawl、Tavily、Exa 等引擎自动故障切换。",
188
- "category": "search",
251
+ "id": "browser-skill-dsh-plugin",
252
+ "name": "BrowserSkill",
253
+ "description": "浏览器自动化:把 BrowserSkill 的 browser_* 工具暴露给模型,让模型在对话中操作浏览器,详见仓库。",
254
+ "category": "tencent-lighthouse",
189
255
  "tags": [
190
- "搜索",
191
- "网页抓取",
192
- "X"
256
+ "浏览器",
257
+ "自动化",
258
+ "工具调用"
193
259
  ],
194
260
  "source": "npm",
195
- "npm": "@liustack/modsearch",
196
- "github": "liustack/modsearch",
197
- "homepage": "https://github.com/liustack/modsearch"
261
+ "npm": "@wxg-prc-cpg/browser-skill-dsh-plugin",
262
+ "github": "Tencent/BrowserSkill",
263
+ "homepage": "https://github.com/Tencent/BrowserSkill/tree/main/packages/dsh-plugin-browserskill#readme"
198
264
  },
199
265
  {
200
- "id": "dsh-docs-panel",
201
- "name": "DSH Docs Panel",
202
- "description": "侧边栏「全局文档」:全局 Markdown 笔记任何工作区可读,支持大纲跳转与一键转编辑器;需 better-sidebar ≥0.4.0。",
203
- "category": "ui",
266
+ "id": "dsh-weknora",
267
+ "name": "WeKnora",
268
+ "description": "知识库检索:把 WeKnora 接入模型,提供语义搜索、文档阅读与 RAG 问答等工具,可用自有知识库,详见仓库。",
269
+ "category": "tencent-lighthouse",
204
270
  "tags": [
205
- "文档",
206
- "Markdown",
207
- "侧边栏"
271
+ "知识库",
272
+ "RAG",
273
+ "检索",
274
+ "工具"
208
275
  ],
209
276
  "source": "npm",
210
- "npm": "dsh-docs-panel",
211
- "github": "mlosun/dsh-docs-panel",
212
- "homepage": "https://github.com/mlosun/dsh-docs-panel"
277
+ "npm": "@wxg-prc-cpg/dsh-weknora",
278
+ "github": "Tencent/WeKnora",
279
+ "homepage": "https://github.com/Tencent/WeKnora/tree/main/packages/dsh-weknora"
213
280
  },
214
281
  {
215
282
  "id": "dsh-server-deck",
216
283
  "name": "DSH Server Deck",
217
284
  "description": "服务器卡片仪表盘:实时 CPU/内存/磁盘与趋势图(1h~30d),点卡片进 xterm 终端;可选集成 better-sidebar。",
218
- "category": "tools",
285
+ "category": "watchlist",
219
286
  "tags": [
220
287
  "服务器",
221
288
  "监控",
@@ -231,137 +298,23 @@
231
298
  "id": "dsh-flowglass",
232
299
  "name": "DSH Flowglass",
233
300
  "description": "流镜:把当前会话画成三列泳道流程图,展示工具调用、并行分组与子代理分支;可选集成 better-sidebar。",
234
- "category": "ui",
301
+ "category": "watchlist",
235
302
  "tags": [
236
303
  "可视化",
237
304
  "流程图",
238
- "会话"
305
+ "会话",
306
+ "界面"
239
307
  ],
240
308
  "source": "npm",
241
309
  "npm": "dsh-flowglass",
242
310
  "github": "Iwctwbh/dsh-flowglass",
243
311
  "homepage": "https://github.com/Iwctwbh/dsh-flowglass"
244
312
  },
245
- {
246
- "id": "dsh-auth",
247
- "name": "DSH Auth",
248
- "description": "DSH Web 管理员认证:Argon2id 口令与可撤销会话,经 Caddy forward_auth 保护页面、API、下载、SSE 与 WebSocket。",
249
- "category": "tools",
250
- "tags": [
251
- "认证",
252
- "安全"
253
- ],
254
- "source": "npm",
255
- "npm": "dsh-auth",
256
- "github": "hxy91819/dsh-auth",
257
- "homepage": "https://github.com/hxy91819/dsh-auth"
258
- },
259
- {
260
- "id": "deepseek-harness-lark",
261
- "name": "DeepSeek Harness Lark",
262
- "description": "飞书 / Lark 通道插件:把飞书消息桥接进 DeepSeek Harness 对话,支持文本与图片收发。",
263
- "category": "other",
264
- "tags": [
265
- "飞书",
266
- "Lark",
267
- "通道"
268
- ],
269
- "source": "npm",
270
- "npm": "deepseek-harness-lark",
271
- "github": "sliverp/DeepSeek-harness-lark",
272
- "homepage": "https://github.com/sliverp/DeepSeek-harness-lark"
273
- },
274
- {
275
- "id": "deepseek-harness-qqbot",
276
- "name": "DeepSeek Harness QQBot",
277
- "description": "QQ Bot 通道插件:把 QQ 机器人消息桥接进 DeepSeek Harness 对话,支持文本与图片收发。",
278
- "category": "other",
279
- "tags": [
280
- "QQ",
281
- "通道"
282
- ],
283
- "source": "npm",
284
- "npm": "deepseek-harness-qqbot",
285
- "github": "sliverp/DeepSeek-harness-qqbot",
286
- "homepage": "https://github.com/sliverp/DeepSeek-harness-qqbot"
287
- },
288
- {
289
- "id": "deepseek-harness-weixin",
290
- "name": "DeepSeek Harness Weixin",
291
- "description": "微信通道插件:扫码登录,收发文本与图片消息,桥接进 DeepSeek Harness 对话。",
292
- "category": "other",
293
- "tags": [
294
- "微信",
295
- "通道"
296
- ],
297
- "source": "npm",
298
- "npm": "deepseek-harness-weixin",
299
- "github": "sliverp/DeepSeek-harness-weixin",
300
- "homepage": "https://github.com/sliverp/DeepSeek-harness-weixin"
301
- },
302
- {
303
- "id": "deepseek-harness-wecom",
304
- "name": "DeepSeek Harness WeCom",
305
- "description": "企业微信 AI Bot 通道插件:把企业微信消息桥接进 DeepSeek Harness 对话,支持文本与图片收发。",
306
- "category": "other",
307
- "tags": [
308
- "企业微信",
309
- "通道"
310
- ],
311
- "source": "npm",
312
- "npm": "deepseek-harness-wecom",
313
- "github": "sliverp/DeepSeek-harness-wecom",
314
- "homepage": "https://github.com/sliverp/DeepSeek-harness-wecom"
315
- },
316
- {
317
- "id": "deepseek-harness-dingtalk",
318
- "name": "DeepSeek Harness DingTalk",
319
- "description": "钉钉 Stream 通道插件:把钉钉消息桥接进 DeepSeek Harness 对话,支持文本与图片收发。",
320
- "category": "other",
321
- "tags": [
322
- "钉钉",
323
- "通道"
324
- ],
325
- "source": "npm",
326
- "npm": "deepseek-harness-dingtalk",
327
- "github": "sliverp/DeepSeek-harness-dingtalk",
328
- "homepage": "https://github.com/sliverp/DeepSeek-harness-dingtalk"
329
- },
330
- {
331
- "id": "browser-skill-dsh-plugin",
332
- "name": "BrowserSkill",
333
- "description": "浏览器自动化:把 BrowserSkill 的 browser_* 工具暴露给模型,让模型在对话中操作浏览器,详见仓库。",
334
- "category": "tools",
335
- "tags": [
336
- "浏览器",
337
- "自动化",
338
- "工具调用"
339
- ],
340
- "source": "npm",
341
- "npm": "@wxg-prc-cpg/browser-skill-dsh-plugin",
342
- "github": "Tencent/BrowserSkill",
343
- "homepage": "https://github.com/Tencent/BrowserSkill/tree/main/packages/dsh-plugin-browserskill#readme"
344
- },
345
- {
346
- "id": "dsh-weknora",
347
- "name": "WeKnora",
348
- "description": "知识库检索:把 WeKnora 接入模型,提供语义搜索、文档阅读与 RAG 问答等工具,可用自有知识库,详见仓库。",
349
- "category": "tools",
350
- "tags": [
351
- "知识库",
352
- "RAG",
353
- "检索"
354
- ],
355
- "source": "npm",
356
- "npm": "@wxg-prc-cpg/dsh-weknora",
357
- "github": "Tencent/WeKnora",
358
- "homepage": "https://github.com/Tencent/WeKnora/tree/main/packages/dsh-weknora"
359
- },
360
313
  {
361
314
  "id": "dsh-chat-manager",
362
315
  "name": "DSH Chat Manager",
363
316
  "description": "会话管理器:在原生侧边栏搜索归档聊天、恢复会话并安全永久删除;需按内核版本选装,详见仓库。",
364
- "category": "tools",
317
+ "category": "watchlist",
365
318
  "tags": [
366
319
  "会话管理",
367
320
  "归档",
@@ -374,20 +327,20 @@
374
327
  "homepage": "https://github.com/WSL043/dsh-chat-manager"
375
328
  },
376
329
  {
377
- "id": "dsh-context",
378
- "name": "DSH Context",
379
- "description": "上下文透视:Context 仪表盘、侧栏面板与 /context 命令,查看上下文组成、演进趋势与压缩剪枝等事件,支持跨会话用量统计。",
380
- "category": "ui",
330
+ "id": "dsh-docs-panel",
331
+ "name": "DSH Docs Panel",
332
+ "description": "侧边栏「全局文档」:全局 Markdown 笔记任何工作区可读,支持大纲跳转与一键转编辑器;需 better-sidebar ≥0.4.0。",
333
+ "category": "watchlist",
381
334
  "tags": [
382
- "上下文",
383
- "Token 统计",
384
- "可视化",
385
- "仪表盘"
335
+ "文档",
336
+ "Markdown",
337
+ "侧边栏",
338
+ "界面"
386
339
  ],
387
340
  "source": "npm",
388
- "npm": "dsh-context",
389
- "github": "bowenliang123/dsh-context",
390
- "homepage": "https://github.com/bowenliang123/dsh-context"
341
+ "npm": "dsh-docs-panel",
342
+ "github": "mlosun/dsh-docs-panel",
343
+ "homepage": "https://github.com/mlosun/dsh-docs-panel"
391
344
  }
392
345
  ]
393
346
  }