dsh-m 0.6.1 → 0.8.4

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
@@ -4,11 +4,11 @@
4
4
  [![npm](https://img.shields.io/npm/v/dsh-m?label=npm)](https://www.npmjs.com/package/dsh-m)
5
5
  [![Registry Check](https://img.shields.io/github/actions/workflow/status/iasiv5/dsh-m/registry.yml?branch=main&label=Registry%20Check)](../../actions/workflows/registry.yml)
6
6
  [![License](https://img.shields.io/github/license/iasiv5/dsh-m?label=License)](./LICENSE)
7
- [![DSH Web](https://img.shields.io/badge/DSH%20Web-0.1.2--rc.1%20%E2%86%92%200.1.7--rc.2%20dual--API-2563eb)](#faq)
7
+ [![DSH Web](https://img.shields.io/badge/DSH%20Web-0.1.2--rc.1%20%E2%86%92%200.2.0--rc.2%20dual--API-2563eb)](#faq)
8
8
 
9
9
  English · [中文](./README.md)
10
10
 
11
- A DeepSeek Harness (DSH) plugin marketplace with a customizable registry: **browse, install, uninstall, upgrade** — all local. Ships as a DSH web plugin: the "Plugin Marketplace" sidebar entry opens a three-view panel, backed by seven `dshm_*` agent tools and the `dshm` CLI.
11
+ A DeepSeek Harness (DSH) plugin marketplace with a customizable registry: **browse, install, uninstall, upgrade** — all local. Ships as a DSH web plugin: the "Plugin Marketplace" sidebar entry opens a three-view panel, backed by eight `dshm_*` agent tools and the `dshm` CLI.
12
12
 
13
13
  <div align="center">
14
14
  <img src="https://raw.githubusercontent.com/iasiv5/dsh-m/main/docs/images/marketplace.webp" alt="Plugin Marketplace panel — Market view: curated card flow with search and category filters" width="100%">
@@ -36,11 +36,80 @@ The default registry includes DSH Skins, ModSearch, the Lark / QQ / Weixin / WeC
36
36
 
37
37
  | View | Capabilities |
38
38
  |---|---|
39
- | **Market** | Card flow with search and category filters (server-side filtering + pagination, 50 per page — even a 1,000-entry registry probes the current page only); expandable details; "Install" — 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); npm sources pin the latest exact version, GitHub sources pin the release/tag commit |
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 |
40
41
  | **Installed** | What your web profile 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) |
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
+ | **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 |
42
43
 
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
+ 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, 763 cases) plus the 0.4.0→0.4.2 consecutive-release evidence. Installs stream live pnpm progress (resolve → download → link → build).
45
+
46
+ ### Changed in 0.8.4 — Category labels now follow the UI language
47
+
48
+ - **Community categories**: English names for the 23 known categories come straight from the upstream catalog's `categories.en`; under the English UI, category chips and the category row in detail/favorite modals show English. Upstream categories without an English name fall back to Chinese and keep rendering as raw slugs in the temporary group until labels land in a release. The Chinese UI is unchanged.
49
+ - **Curated categories**: the five chips now go through the bilingual dictionary (Market/Tools/UI/Search/Other ⇄ 市场/工具/界面/搜索/其他), consistent with the detail modal.
50
+ - **Implementation**: the summary carries `categoryLabelsEn` (derived from the upstream catalog in `communityOutcome`; ids without English are omitted — no second hand-maintained table), and the client merges per UI language. 0.8.1–0.8.3 were local iteration numbers with no separate changelog surface.
51
+
52
+ ### Changed in 0.8.0 — Settings page redo (aligned with the zoned dual-catalog positioning)
53
+
54
+ - **Information architecture**: Community catalog (from awesome-dsh-plugin) → Curated registry (registry.json) → dsh-m itself → About; the primary registry's user-facing name is now unified as "Curated registry".
55
+ - **Community catalog toggle**: a new GUI switch (live effect, instant, no confirm; when off the whole card collapses to one line); new `set-community` API.
56
+ - **Curated registry slimmed**: status fields consolidated from 7 rows to 4 (merged address, removed the hard-coded "caching" row); config status only appears when abnormal; buttons reduced to "Force refresh / Validate & apply / Restore default / Download default registry"; "Check entries reachability" removed from the GUI (`registry-diagnose` API kept).
57
+ - **Copy fixes**: the custom-source note now says it replaces the whole primary registry and only affects the Curated zone; removed the hard-coded TTL description that didn't match reality (`timeoutMs`/`cacheTtlMin` remain config-file settings).
58
+ - **dsh-m itself**: when the local dev build is ahead of npm, show "local dev build" instead of the misleading old "npm latest" (new `ahead` field).
59
+ - **About**: copy aligned with the current positioning, plus GitHub repo and issue-tracker links.
60
+
61
+ ### Polished in 0.7.10
62
+
63
+ - **Head split into three groups**: a hairline divider between the title and tab navigation — "title | nav | status + window controls" reads clearly.
64
+ - **Version badge unified color**: the v-number no longer uses the bright primary text color; it matches the dsh-m name in secondary gray.
65
+ - **Vertical rhythm consolidated**: window-control cells 26→28px to match tab height; version badge adjusted to 24px; maximize/restore icons unified at 12px; title weight 700→600.
66
+
67
+ ### Fixed in 0.7.9
68
+
69
+ - **Category chips keep a stable order**: retired the "promote active to front" reordering (every click on a clipped category used to jump it to first place). Instead, when the active category falls inside the collapsed clip region, the row auto-expands so the active filter stays visible — same goal, stable order; a manual collapse under the same active category is respected, and picking another clipped category or "All" resets it.
70
+
71
+ ### Changed in 0.7.8
72
+
73
+ - **Root cause fix for "maximize does nothing" — CSS hot-update self-healing**: the panel stylesheet was only injected once (`#dshm-css` present → skip), so after a hot update the stale stylesheet lacked rules for new classes (fullscreen/window controls) — new features appeared dead and buttons rendered as unstyled natives. The injected stylesheet now carries a content hash (djb2); reopening the panel after a bundle update swaps in the fresh styles automatically, no page refresh needed.
74
+ - **Window-control group recolored**: dropped the raised surface for a transparent background + hairline outline (same language as the search clear button); cells widened 34→44px against mis-clicks; close keeps its red hover.
75
+
76
+ ### Changed in 0.7.7
77
+
78
+ - **Fullscreen ported from dsh-market**: a maximize/restore control joins the panel head as a unified window-control group (equal-width maximize + close cells, hairline divider, shared SVG line icons; close tints red on hover). Fullscreen fills the viewport without rounded corners, the state is remembered in localStorage, and Esc still closes the panel.
79
+ - 0.7.6 catch-up: version badge de-bolded and made static (no copy-on-click); sticky category row's top gap fixed (sticky anchor shifted to offset container padding); close/clear buttons moved to the outlined flat style.
80
+
81
+ ### Changed in 0.7.5
82
+
83
+ - **Header version chip now shows dsh-m's own version** (`dsh-m v0.7.5`, click to copy; the DSH runtime version lives in Settings and `dshm ping`).
84
+ - **× close/clear buttons redrawn**: search clear, detail-modal close, and operation-row remove now use an SVG line icon on a dedicated hover-tinted button; the search clear button floats inside the pill's right edge.
85
+ - **Fixed the dsh-market peer warning**: `@deepseek-ai/dsh-tools` peer changed from `*` (strict semver never matches rc prereleases) to the explicit range `^0.1.7-rc.2 || ^0.2.0-rc.1 || >=0.2.0`; newer rc lines (e.g. 0.3.0-rc.x) need another entry.
86
+
87
+ ### Changed in 0.7.4
88
+
89
+ - **Panel tabs reverted to the classic segmented style** (0.7.2 mistakenly introduced underline tabs; the rounded button group with a highlighted active tab is back).
90
+ - **Full-width search**: fixed the search wrapper missing `display:flex`, which kept the input from stretching to the row.
91
+ - **Filter button and page-jump control recolored**: the filter button now uses an elevated surface with squared corners to stand apart from the pill category chips (accent outline when open); the page input is a slim pill with a hairline border and an accent-colored "Go" text button.
92
+ - **Sticky category row fully opaque**: the background now uses the opaque base token directly (the previous color-mix translucency still let card text bleed through on the dark theme); the bottom divider stays.
93
+
94
+ ### Changed in 0.7.3 (includes 0.7.2)
95
+
96
+ - **Home layout modeled after dsh-market**: the market view becomes "zone chips → full-width search row → category chips with a trailing Filter popover"; the popover gets its own look (rounded rectangle + leading chevron) and holds sort field (npm downloads/stars/date added), direction, and page size (the old sort dropdown and pager page-size select are retired).
97
+ - **Favorites cards open the detail modal**: fixed favorites-zone cards not responding to clicks — favorite snapshots lack full fields, so opening resolves the complete entry by id (in-memory zones → market API → snapshot fallback), reusing the detail modal and the full install path (peer confirm included).
98
+ - **Page-number jump**: the pager gains a page input — type a valid page and hit Enter or "Go" to jump.
99
+ - **Frosted sticky category row**: the sticky row now blurs content beneath it (backdrop blur + divider), so card text no longer bleeds through.
100
+ - **Removed by request**: the "discover/request listing" line (dsh-m does not accept listings), the "Tasks" button (operations panel is always visible again), and the "Refresh" button (force refresh lives in Settings).
101
+ - Note: registry entries carry no host-version requirement field, so dsh-market's "host version" filter has no data source here and was not replicated.
102
+
103
+ ### Fixed in 0.7.1
104
+
105
+ - **Community entries are installable again**: fixes a 0.7.0 regression where installing a community listing failed with "registry 中没有该条目" (install-by-id only consulted the primary registry, never the community catalog); the path now mirrors upgrades — on a primary miss the catalog is searched by id.
106
+ - **Compact market header**: search, refresh, and sort (community zone) move into the zone chips row; informational source banners ("official default registry / custom registry / served from local cache") are retired — their "{count} listings" figure was never wired up (always 0); the "registry unavailable" error and community fallback/stale hints remain.
107
+
108
+ ### New in 0.7.0
109
+
110
+ - **Zoned market** ([ADR-0004](./docs/adr/0004-zoned-market-display.md)): the dual-catalog data merge stays, but the display splits into Community (default) / Curated / Favorites zones; `dshm_search` and `dshm search` switch to `--source community|primary|all` + `--offset` real pagination (default 10 cards) — `primary_only` is retired.
111
+ - **Relevance search**: NFKC normalization + CJK↔Latin boundaries + field weighting (name/npm > owner > description > category > tags), multi-term same-field matching; whole-id exact match takes top priority.
112
+ - **Operation log + resume executor**, **local favorites + delisted cleanup**, **detail modal + screenshot lightbox**, community byline/deprecated badges/catalog-snapshot version fallback (never used for outdated).
44
113
 
45
114
  ### New in 0.4.0
46
115
 
@@ -72,7 +141,7 @@ After any mutation, the already-open Market and Installed views refresh the prof
72
141
  ## CLI
73
142
 
74
143
  ```sh
75
- dshm search [--query topic] [--category ui]
144
+ dshm search [--query topic] [--category ui] [--source community|primary|all] [--limit N] [--offset N]
76
145
  dshm list | outdated | registry
77
146
  dshm install --id dsh-web-search [--force] # --force: skip the precheck gate after confirming the risk
78
147
  dshm upgrade --pkg dsh-web-search --yes [--force]
@@ -101,11 +170,11 @@ Security baseline: HTTPS-only fetches (loopback HTTP excepted) with per-hop redi
101
170
 
102
171
  ## Community catalog (awesome-dsh-plugin, 0.5.0)
103
172
 
104
- On top of the hand-curated primary registry, the market layers a **read-only community catalog** anchored to the npm package [`dsh-plugin-catalog`](https://www.npmjs.com/package/dsh-plugin-catalog) (CC0-1.0, the full awesome-dsh-plugin directory, 4,000+ entries), merged into one market with the primary registry **always taking precedence** (duplicate entries displace the community side).
173
+ On top of the hand-curated primary registry, the market layers a **read-only community catalog** anchored to the npm package [`dsh-plugin-catalog`](https://www.npmjs.com/package/dsh-plugin-catalog) (CC0-1.0, the full awesome-dsh-plugin directory, 4,000+ entries). The data layer still merges and dedupes with the primary registry **always taking precedence** (duplicate entries displace the community side); the display layer splits into Community / Curated zones (ADR-0004) — the community zone no longer shows entries duplicated by the primary registry, and the curated zone stays a single curated-order page.
105
174
 
106
175
  - **Fetch chain**: dist-tags probe → jsDelivr pinned fetch → npmmirror → unpkg fallback; unchanged versions are not re-fetched, probes are skipped within TTL. On failure it falls back to the runtime cache under `<cache dir>/awesome/` and **explicitly labels it "cached snapshot"** — stale data never masquerades as fresh.
107
176
  - **Switch & pin**: `communityCatalog` toggle in Settings (default on, live), `communityCatalogPin` to lock the catalog version (exact semver); CLI opt-out with `DSHM_COMMUNITY_CATALOG=0`, pin with `DSHM_COMMUNITY_CATALOG_PIN`.
108
- - **Categories & search**: community categories are an open set (20 known ones with Chinese labels in the filter bar; upstream additions land in a "Community · New" group); search matches both Chinese and English descriptions; primary registry pinned on top, community sorted by 30-day downloads.
177
+ - **Categories & search**: community categories are an open set (23 known categories with bilingual labels — Chinese maintained in-repo as the single source of truth, English from the upstream catalog, served per UI language); search runs a relevance-weighted pipeline (Chinese + English); the community zone offers a sort switch (downloads/stars/added × asc/desc, default downloads-desc; missing downloads ≠ zero downloads).
109
178
  - **Install semantics**: community entries go through the same `installEntry` (npm exact version / GitHub pinned commit SHA); the browse page probes npm entries only — GitHub entries are not probed at page level (anonymous 60 req/h quota is uncontrollable).
110
179
  - **Installed-page budget (best-effort)**: update checks for GitHub-sourced plugins are capped at ≤25 wire requests per run and ≤50 per rolling hour per host process (passive checks only; active install/upgrade unaffected); capped items are honestly labeled "check incomplete" instead of "all up to date". A standalone CLI process does not share the host budget and makes no guarantee under concurrency.
111
180
  - **Capability disclosure**: capabilities/red lines appear only in the detail fold (**absent = not scanned ≠ not detected**), never as card badges; screenshots load only in the detail layer and pass a client-side allowlist.
@@ -133,13 +202,13 @@ Intermediate commits on main can be unstable. dsh-m tracks **releases / tags** o
133
202
  No. Only the package reference in the profile is removed (live UI disabled first), and suspected leftover paths are reported to you.
134
203
 
135
204
  **3. Will a custom registry slow the market down?**
136
- Listings over 200 entries trigger a performance notice. The market list is server-paginated (50 per page); even a 1,000-entry registry queries latest versions for the current page only.
205
+ Listings over 200 entries trigger a performance notice. The market list is server-paginated (24/48/96 per page); even a 1,000-entry registry queries latest versions for the current page only.
137
206
 
138
207
  **4. What if my custom source goes down?**
139
208
  dsh-m serves its last successful cache for that source and marks it as cached; with no cache at all the market shows "registry unavailable" while installed plugins stay manageable. Fix the address or restore the default anytime.
140
209
 
141
210
  **5. Which DSH Web versions are supported?**
142
- Same scope as dsh-skip-browser-auth: the public package contracts for `0.1.2-rc.1`, `0.1.5-rc.1/rc.2`, `0.1.7-rc.1`, and `0.1.7-rc.2` are all checked. DSH 0.1.7 reshaped the settings service into `SettingsForms` (the old `settings.register` is gone); dsh-m stays compatible via runtime shape detection: the whole ≤0.1.5 generation uses the `register()` scope path (0.1.5-rc.2's dsh-settings still ships the old API and is covered), while 0.1.7-rc.1 / rc.2 use Config `.volatile()` fields + `settings.update` + the `loader/volatile-update` event (byte-identical dsh-settings and the same loader across both rcs, so one implementation covers both). Unknown shapes degrade to the cordis config-file path with marketplace features unaffected. All these versions expose the dsh-m `appExit` launcher hook used by the restart button; the 0.1.5-rc.1 runtime has live-loaded dsh-m and passed `/dshm` ping plus an authenticated `303 → 200` page request, while the 0.1.7 adaptation's live E2E (install + settings write persistence) follows after release. Systemd deployments must configure `Restart=on-failure` or `Restart=always`; otherwise use the deployment's manual restart procedure.
211
+ Same scope as dsh-skip-browser-auth: the public package contracts for `0.1.2-rc.1`, `0.1.5-rc.1/rc.2`, `0.1.7-rc.1/rc.2`, and `0.2.0-rc.1/rc.2` are all checked (0.2.0-rc.2 being the current live runtime). DSH 0.1.7 reshaped the settings service into `SettingsForms` (the old `settings.register` is gone); dsh-m stays compatible via runtime shape detection: the whole ≤0.1.5 generation uses the `register()` scope path (0.1.5-rc.2's dsh-settings still ships the old API and is covered), while 0.1.7-rc.1 / rc.2 use Config `.volatile()` fields + `settings.update` + the `loader/volatile-update` event (byte-identical dsh-settings and the same loader across both rcs, so one implementation covers both). Unknown shapes degrade to the cordis config-file path with marketplace features unaffected. All these versions expose the dsh-m `appExit` launcher hook used by the restart button, and the restart chain itself was live-verified on the current `0.2.0-rc.2` runtime (2026-10-01, see the Panel section). The 0.1.7 adaptation's live E2E was written back on 2026-09-28: install and `/dshm` toolchain liveness proven on `0.1.7-rc.2` through the consecutive 0.4.0→0.4.2 releases across two service restarts (`dshm list/upgrade` fully working, registry served normally); settings write persistence remains the only unverified bit (dsh-m's own config is already observed persisted in the profile's `cordis.patch.yml` — a GUI write → restart comparison is left for first real use of the settings panel). Systemd deployments must configure `Restart=on-failure` or `Restart=always`; otherwise use the deployment's manual restart procedure.
143
212
 
144
213
  ## License
145
214
 
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![npm](https://img.shields.io/npm/v/dsh-m?label=npm)](https://www.npmjs.com/package/dsh-m)
5
5
  [![Registry Check](https://img.shields.io/github/actions/workflow/status/iasiv5/dsh-m/registry.yml?branch=main&label=Registry%20Check)](../../actions/workflows/registry.yml)
6
6
  [![License](https://img.shields.io/github/license/iasiv5/dsh-m?label=License)](./LICENSE)
7
- [![DSH Web](https://img.shields.io/badge/DSH%20Web-0.1.2--rc.1%20%E2%86%92%200.1.7--rc.2%20dual--API-2563eb)](#faq)
7
+ [![DSH Web](https://img.shields.io/badge/DSH%20Web-0.1.2--rc.1%20%E2%86%92%200.2.0--rc.2%20dual--API-2563eb)](#faq)
8
8
 
9
9
  [English](./README.en.md) · 中文
10
10
 
@@ -36,11 +36,80 @@
36
36
 
37
37
  | 视图 | 能力 |
38
38
  |---|---|
39
- | **市场** | 收录卡片流;关键词搜索、分类筛选(服务端过滤 + 分页,每页 50 条,1,000 条清单也只探测当前页);卡片展开详情与「安装」;npm 源锁定最新精确版本,GitHub 源锁定 release/tag 指向的 commit |
39
+ | **市场** | **分区制**(0.7.0):**社区**(默认落地页,4,000+ 条全量目录)与**精选**(手工策展主清单,单页直出)两个独立分区,各有自己的分类/搜索/排序/分页状态(切 tab 互不重置);另有**收藏**分区(浏览器本地书签,自动检测下架条目并一键清理)。社区区:分类 chips 两行折叠 + 吸顶收缩、排序(下载量/Star/收录日期 × 升降)、页码窗口化分页(24/48/96 每页)、卡片 byline(作者/下载量/Star);卡片点开**详情 Modal**(卡片信息超集:下载量窗口三要素、截图灯箱、能力披露默认收起、安装命令);npm 源锁定最新精确版本,GitHub 源锁定 release/tag 指向的 commit |
40
+ | **操作记录** | 安装/升级/卸载/开关全部走全局操作记录(0.7.0):状态不挂卡片,翻页/搜索/切 tab 不丢;localStorage 持久化,宿主重载后自动恢复未完成操作(逐条校验「此刻仍成立才执行」);良性前提消失以中性「已跳过」呈现;已装页支持「全部更新 (N)」批量入队 |
40
41
  | **已装** | web profile 实装列表,标注「市场安装 / 非市场安装」;可升级徽标、升级、两段式确认卸载;📖 README 预览(64KB 截断);**运行相位徽标**(●active / ●failed / ○pending)与**一键开关**(0.4.0:委派官方 pluginManager 服务活体生效,服务缺席时文件级编辑 + 重启提示;dsh-m 自身与官方宿主命脉锁定不可开关) |
41
- | **设置** | registry 地址草稿 +「校验并应用 / 恢复默认 / 下载默认 registry.json / 检查条目可达性」;配置地址、生效来源与状态一目了然;强制刷新;dsh-m 自更新 |
42
+ | **设置** | registry 地址草稿 +「强制刷新 / 校验并应用 / 恢复默认 / 下载默认 registry.json」;配置地址、生效来源与状态一目了然(仅异常时提示);社区目录开关;dsh-m 自更新 |
42
43
 
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
+ 安装 / 卸载 / 升级完成后,当前已打开的市场页与已装页会一起重新读取 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 事务测试面(补偿事务、装后守卫等 763 用例)与 0.4.0→0.4.2 连续发布实证覆盖。安装过程实时显示 pnpm 进度(解析 → 下载 → 链接 → 构建)。
45
+
46
+ ### 0.8.4 变更:分类标签随界面语言双语化
47
+
48
+ - **社区分类**:已知 23 个分类的英文名直接取上游目录 `categories.en`,英文界面下分类 chips 与详情/收藏 Modal 的分类行显示英文;缺英文名的上游新分类回退中文,仍按原样渲染进临时组,等发版收录。中文界面不变。
49
+ - **精选分类**:五个分类 chip 改走双语字典(市场/工具/界面/搜索/其他 ⇄ Market/Tools/UI/Search/Other),与详情 Modal 口径一致。
50
+ - **实现**:summary 新增 `categoryLabelsEn`(`communityOutcome` 从上游目录派生,缺 en 的 id 不进映射,不手养第二张表),客户端按界面语言合并取值。0.8.1–0.8.3 为本地迭代号,无独立变更面,不单列。
51
+
52
+ ### 0.8.0 变更:设置页重做(对齐双清单分区定位)
53
+
54
+ - **信息架构**:社区清单(from awesome-dsh-plugin)→ 精选清单(registry.json)→ dsh-m 自身 → 关于;「主清单」的 UI 可见名统一为「精选清单」。
55
+ - **社区目录开关**:新增 GUI 开关(live 生效,即时切换无确认;关闭态整卡收为一行说明);新增 `set-community` API。
56
+ - **精选清单瘦身**:状态字段 7 行收敛为 4 行(配置地址合并、删除写死的「缓存策略」行);「配置状态」仅异常时出现;按钮组精简为「强制刷新 / 校验并应用 / 恢复默认 / 下载默认清单」,「检查条目可达性」从 GUI 移除(`registry-diagnose` API 保留)。
57
+ - **文案修正**:自定义源说明明确「整体替换主清单、仅影响『精选』区」;删除与实际不符的 TTL 写死描述(`timeoutMs`/`cacheTtlMin` 仍走 config 配置)。
58
+ - **dsh-m 自身**:本地 dev 版领先 npm 发布时改显「本地为开发版」而非误导性的旧「npm 最新」(新增 `ahead` 字段)。
59
+ - **关于**:文案对齐当前定位,新增 GitHub 仓库与问题反馈链接。
60
+
61
+ ### 0.7.10 美化
62
+
63
+ - **头部三段分组**:标题与 tab 导航之间加发丝竖分隔线,「标题 | 导航 | 状态+窗口控制」边界清晰。
64
+ - **版本角标同色**:v 版本号不再用主文字亮色,与 dsh-m 名称统一为次级灰。
65
+ - **垂直节奏收敛**:窗口控制组按钮 26→28px 与 tab 按钮等高;版本角标微调至 24px 高;最大化/还原图标统一 12px;标题字重 700→600。
66
+
67
+ ### 0.7.9 修复
68
+
69
+ - **分类 chips 顺序恒定**:退役「激活分类置前」的换序逻辑(此前每点一个被折叠裁掉的分类,它就会跳到首位,顺序随点击不断变化)。替换为:激活分类落在收起态裁剪区时自动展开完整分类行——「当前激活的分类始终可见」目标不变,顺序从此稳定;同一激活分类下手动收起会被尊重,换选其他被裁掉的分类或点「全部」时重置。
70
+
71
+ ### 0.7.8 变更
72
+
73
+ - **修复「最大化没作用」的根因——CSS 热更自愈**:面板样式表此前只在首次注入(`#dshm-css` 存在即跳过),服务热更后旧 bundle 留下的样式表不含新类名规则(全屏/窗口控制组),导致新功能「点了没反应」、按钮裸奔成原生样式。现给注入的样式表带内容哈希(djb2)版本标记,bundle 更新后重新打开面板即自动替换旧样式,无需刷新页面。
74
+ - **窗口控制组重新配色**:去掉浮起底色,改透明底 + 发丝外框(与搜索清除钮同一配色语言),单格加宽 34→44px 防误触;关闭悬停仍为红色警示。
75
+
76
+ ### 0.7.7 变更
77
+
78
+ - **移植 dsh-market 的全屏功能**:面板头部新增「最大化/还原」(连体窗口控制组设计:最大化 + 关闭等宽两格、发丝分隔线、统一 SVG 线条图标;关闭悬停红色警示)。全屏铺满视口、去圆角,状态 localStorage 记忆,Esc 关面板语义不变。
79
+ - 0.7.6 补记:版本角标去粗体、去点击复制(改静态展示);吸顶分类行上沿镂空修复(sticky 锚点上移抵消容器 padding);关闭/清除按钮改框线平面风。
80
+
81
+ ### 0.7.5 变更
82
+
83
+ - **头部版本 chip 改显 dsh-m 自身版本**(`dsh-m v0.7.5`,点击复制;DSH 运行版本看设置页与 `dshm ping`)。
84
+ - **× 关闭/清除按钮统一重绘**:搜索清除、详情 Modal 关闭、操作记录行移除三处改用 SVG 线条图标 + 悬停浅底的专用按钮样式;搜索清除钮悬浮于输入框右缘(胶囊内对齐)。
85
+ - **修复 dsh-market 的 peer 告警**:`@deepseek-ai/dsh-tools` peer 由 `*`(semver 严格口径不匹配 rc 预发布版本)改为显式 range `^0.1.7-rc.2 || ^0.2.0-rc.1 || >=0.2.0`;未来更新的 rc 线(如 0.3.0-rc.x)需再追加。
86
+
87
+ ### 0.7.4 变更
88
+
89
+ - **面板 tab 回退旧版分段按钮风格**(0.7.2 误改下划线样式,按主人要求还原圆角按钮组 + 高亮态)。
90
+ - **搜索框通长**:修复搜索容器缺 `display:flex` 导致输入框未拉伸的问题,恢复整行宽度。
91
+ - **筛选按钮与页号跳转控件重新配色**:筛选按钮改浮起面板底色 + 方角与分类 chips 区分(激活态品牌色描边);页号输入改胶囊形细描边,「跳转」用品牌色文字钮。
92
+ - **吸顶分类行彻底不透**:背景直接使用不透明底色 token(此前的 color-mix 半透明配方在深色主题下仍会透出下方卡片文字),保留底部分隔线。
93
+
94
+ ### 0.7.3 变更(含 0.7.2)
95
+
96
+ - **首页布局复刻 dsh-market**:市场页改为「分区 chips → 整宽搜索行 → 分类 chips + 行尾筛选弹层」结构;筛选弹层独立样式(方角矩形 + 前置 chevron),收纳排序字段(npm 下载量/Star 数/收录日期)、排列方向与每页条数(原排序下拉与分页器条数选择退役)。
97
+ - **收藏卡可点开详情**:修复收藏区卡片点击无响应——收藏快照字段不全,打开时按 id 从两分区内存 → market API → 快照三级解析完整条目,详情 Modal 与安装链路(含兼容确认)全量复用。
98
+ - **翻页页号跳转**:页码行新增页号输入框,输入有效页号回车或点「跳转」直达。
99
+ - **吸顶分类行毛玻璃**:滚动时吸顶的分类行加 backdrop blur 与底部分隔线,下方卡片文字不再透出干扰。
100
+ - **按需求移除**:「发现社区/申请收录」行(dsh-m 不支持收录功能)、「任务」按钮(操作记录面板恢复常驻)、「刷新」按钮(强制刷新在设置页)。
101
+ - 说明:收录条目不携带宿主版本要求字段,dsh-market 的「宿主版本」筛选项无数据源,未复刻。
102
+
103
+ ### 0.7.1 修复
104
+
105
+ - **社区条目可安装**:修复 0.7.0 回归——社区区里的条目点安装报「registry 中没有该条目」(安装按收录 id 只查主清单,未查社区目录);现与升级路径同构,主清单 miss 时按 id 查社区目录再装。
106
+ - **市场页第一行紧凑化**:搜索框、刷新、排序(社区区)并入分区 chips 行右侧;信息性来源横幅(「官方默认收录清单 / 自定义收录清单 / 来源为本地缓存」)退役——其「共 {count} 条」计数从未接线(恒显 0);错误态「收录清单不可用」与社区兜底/陈旧提示保留。
107
+
108
+ ### 0.7.0 新增
109
+
110
+ - **分区制市场**([ADR-0004](./docs/adr/0004-zoned-market-display.md)):数据层双清单合并不变,展示层按「社区(默认)/ 精选 / 收藏」三分区呈现;`dshm_search` 与 `dshm search` 改用 `--source community|primary|all` + `--offset` 真翻页(默认 10 条),`primary_only` 退役。
111
+ - **搜索相关性**:NFKC 归一化 + 中西文边界 + 字段加权(name/npm > owner > 描述 > 分类 > tags),多词同字段全命中;id 整串精确匹配最高优先。
112
+ - **操作记录 + 恢复执行器**、**本地收藏 + 下架清理**、**详情 Modal + 截图灯箱**、社区卡 byline/deprecated 徽章/目录版本快照兜底(不参与 outdated 判定)。
44
113
 
45
114
  ### 0.4.0 新增
46
115
 
@@ -72,7 +141,7 @@
72
141
  ## CLI
73
142
 
74
143
  ```sh
75
- dshm search [--query 主题] [--category ui]
144
+ dshm search [--query 主题] [--category ui] [--source community|primary|all] [--limit N] [--offset N]
76
145
  dshm list | outdated | registry
77
146
  dshm install --id dsh-web-search [--force] # --force:确认兼容风险后跳过预检拦截
78
147
  dshm upgrade --pkg dsh-web-search --yes [--force]
@@ -101,11 +170,11 @@ dshm restart --yes
101
170
 
102
171
  ## 社区清单(awesome-dsh-plugin 目录,0.5.0)
103
172
 
104
- 除手工 curated 的主清单外,市场还会叠加一层**只读的社区清单**:锚定 npm 包 [`dsh-plugin-catalog`](https://www.npmjs.com/package/dsh-plugin-catalog)(CC0-1.0,awesome-dsh-plugin 全量目录,4,000+ 条),与主清单合并为合并市场(**主清单恒优先**,重名条目社区侧让位)。
173
+ 除手工 curated 的主清单外,市场还会叠加一层**只读的社区清单**:锚定 npm 包 [`dsh-plugin-catalog`](https://www.npmjs.com/package/dsh-plugin-catalog)(CC0-1.0,awesome-dsh-plugin 全量目录,4,000+ 条),数据层与主清单合并去重(**主清单恒优先**,重名条目社区侧让位),展示层按「社区 / 精选」分区呈现(ADR-0004)——社区区不再显示与主清单重复的条目,精选区保持策展序单页直出。
105
174
 
106
175
  - **数据获取**:dist-tags 探测最新版本 → jsDelivr 按精确版本直取 → npmmirror → unpkg 三线路兜底;版本未变不重拉正文,TTL 内跳过探测。失败回落 `<缓存目录>/awesome/` 运行时缓存并**显式标注「缓存快照」**——过期数据绝不冒充最新。
107
176
  - **开关与锁定**:设置页 `communityCatalog` 开关(默认开,live 生效)、`communityCatalogPin` 可锁定目录版本(精确 semver);CLI 用 `DSHM_COMMUNITY_CATALOG=0` 退出、`DSHM_COMMUNITY_CATALOG_PIN` 锁版本。
108
- - **分类与搜索**:社区分类是开放集(已知 20 个带中文标签进筛选栏「社区」组,上游新增分类进「社区·新分类」临时组);搜索同时匹配中英文描述;排序主清单置顶 + 社区按 30 天下载量降序。
177
+ - **分类与搜索**:社区分类是开放集(已知 23 个分类带中英双语标签:中文由包内单一事实源维护、英文取上游目录,按界面语言下发);搜索走相关性加权管线(中英文双匹配);社区区排序可切(下载量/Star/收录日期 × 升降,默认下载量降序,无下载量 ≠ 0 下载)。
109
178
  - **安装语义**:社区条目与主清单走同一 `installEntry`(npm 锁精确版本 / GitHub 锁 commit SHA);市场浏览页只对 npm 条目做更新探测,GitHub 条目不做页面级探测(匿名配额 60 次/小时不可控)。
110
179
  - **已装页预算(best-effort)**:已装页对 GitHub 来源插件做更新检查,单次请求 ≤25 个、宿主进程滚动 1 小时 ≤50 个(仅被动检查;主动安装/升级不受限);超限条目如实标注「检查未完成」而不冒充「全部最新」。独立 CLI 进程不共享宿主内预算,并发场景不承诺 60 次/小时绝不耗尽。
111
180
  - **能力披露**:社区条目的 capabilities/红线只出现在详情折叠区(**缺省 = 未扫描 ≠ 未检出**),卡片不打标;截图仅在详情层加载并经客户端白名单校验。
@@ -133,13 +202,13 @@ main 上的中间提交可能不稳定。dsh-m 只跟踪 **release / tag**(优
133
202
  不会。只移除 profile 中的包引用(卸载前先下线运行中的界面),并把疑似残留路径报告给你。
134
203
 
135
204
  **3. 自定义清单会让市场变慢吗?**
136
- 收录超过 200 条时会提示性能边界。市场列表是服务端分页(每页 50 条),1,000 条清单第一页也只查询当前页的最新版本,浏览仍然流畅。
205
+ 收录超过 200 条时会提示性能边界。市场列表是服务端分页(每页 24/48/96 可选),1,000 条清单第一页也只查询当前页的最新版本,浏览仍然流畅。
137
206
 
138
207
  **4. 自定义源挂了怎么办?**
139
208
  优先使用该源最近一次成功的缓存并标记「缓存来源」;完全没有缓存时市场显示「收录清单不可用」,已安装插件仍可正常管理。修正地址或恢复默认即可。
140
209
 
141
210
  **5. 支持哪些 DSH Web 版本?**
142
- 与 dsh-skip-browser-auth 同一口径:`0.1.2-rc.1`、`0.1.5-rc.1/rc.2`、`0.1.7-rc.1`、`0.1.7-rc.2` 的公开包契约均已核对。其中 0.1.7 把 settings 服务重塑为 `SettingsForms`(旧的 `settings.register` 消失),dsh-m 以运行时形态探测双代兼容:≤0.1.5 全代际走 `register()` scope 通路(0.1.5-rc.2 的 dsh-settings 仍为旧 API,已并入核对),0.1.7-rc.1 / rc.2 走 Config `.volatile()` 字段 + `settings.update` + `loader/volatile-update` 通路(经 tarball 逐字节比对,两个 rc 的 dsh-settings 完全相同、loader 同版,一套实现通吃);形态不识别时自动降级 cordis 配置文件通路,市场主功能不受影响。重启方面各版本都提供 dsh-m 使用的 `appExit` launcher hook;0.1.5-rc.1 运行时已实际加载并完成 `/dshm` ping 与带认证页面 `303 → 200` 核验。0.1.7 适配的 live E2E 结论(2026-09-28 回写):装机与 `/dshm` 工具链活性已在 0.1.7-rc.2 实证——0.4.0 → 0.4.2 连续三个版本发布收编,跨两次服务重启后 `dshm list/upgrade` 全链路可用,registry 21 条(自研七件套齐)下发正常;设置页写值持久化未单独核验(0.1.7 已迁移用户层设置存储,旧 `~/.dsh/settings.yaml` 不复存在,核验需 GUI 会话写值后重启对照,留待首次实际使用设置面板时顺手完成)。systemd 部署需要 unit 配置 `Restart=on-failure` 或 `Restart=always`,否则请使用部署方的手动重启方式。
211
+ 与 dsh-skip-browser-auth 同一口径:`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` 的公开包契约均已核对(0.2.0-rc.2 为当前实机运行代际)。其中 0.1.7 把 settings 服务重塑为 `SettingsForms`(旧的 `settings.register` 消失),dsh-m 以运行时形态探测双代兼容:≤0.1.5 全代际走 `register()` scope 通路(0.1.5-rc.2 的 dsh-settings 仍为旧 API,已并入核对),0.1.7-rc.1 / rc.2 走 Config `.volatile()` 字段 + `settings.update` + `loader/volatile-update` 通路(经 tarball 逐字节比对,两个 rc 的 dsh-settings 完全相同、loader 同版,一套实现通吃);形态不识别时自动降级 cordis 配置文件通路,市场主功能不受影响。重启方面各版本都提供 dsh-m 使用的 `appExit` launcher hook;0.1.5-rc.1 运行时已实际加载并完成 `/dshm` ping 与带认证页面 `303 → 200` 核验。0.1.7 适配的 live E2E 结论(2026-09-28 回写):装机与 `/dshm` 工具链活性已在 0.1.7-rc.2 实证——0.4.0 → 0.4.2 连续三个版本发布收编,跨两次服务重启后 `dshm list/upgrade` 全链路可用,registry 21 条(自研七件套齐)下发正常;设置页写值持久化未单独核验(0.1.7 起迁移用户层设置存储,旧 `~/.dsh/settings.yaml` 不复存在;dsh-m 自身 config 已观察到以 `cordis.patch.yml` 持久化形态存在,GUI 写值 → 重启对照留待首次实际使用设置面板时顺手完成)。systemd 部署需要 unit 配置 `Restart=on-failure` 或 `Restart=always`,否则请使用部署方的手动重启方式。
143
212
 
144
213
  ## License
145
214
 
package/docs/DESIGN.md CHANGED
@@ -4,12 +4,12 @@
4
4
 
5
5
  ## 1. 定位与形态
6
6
 
7
- - **dsh-m** = 个人自用的 DSH(DeepSeek Harness)插件市场。
7
+ - **dsh-m** = 面向大众的 DSH(DeepSeek Harness)插件市场(2026-09-29 由「个人自用」升级定位,见 §2.6)。
8
8
  - 形态:**一个 DSH web 插件**(旗舰是 Web GUI)+ **7 个 agent 工具** + **薄 CLI `dshm`**,全部在同一个 npm 包里,不搞 monorepo。
9
9
  - npm 包名 `dsh-m`(已占位发布 0.0.x),插件 id `dsh-m`,显示名 **DSH Marketplace**。
10
10
  - 管理对象:**只管 DSH 插件**,不管 Agent Skills(skills 归 skillhub)。
11
11
  - 与 `@cocofhu/skillhub` **完全独立并存**:不读不写它的数据与配置,仅在实现机制上借鉴(其源码镜像见 §10)。
12
- - 单机自用:无服务端、无账号、无提交入口;收录变更 = 改本仓库的 registry(他人可发 PR)。
12
+ - 单机形态:无服务端、无账号、无提交入口;收录变更 = 改本仓库的 registry(他人可发 PR)。
13
13
 
14
14
  ## 2. Registry(收录清单)
15
15
 
@@ -64,6 +64,8 @@
64
64
 
65
65
  ### 2.5 社区清单与合并市场(0.5.0 grilling 定稿 2026-09-28)
66
66
 
67
+ > **2026-09-29 修订**:本节 Q45(混排 + 主清单置顶 + 「只看主清单」chip)、Q40(ui/tools/market 共享过滤桶)、Q44(能力披露在卡片折叠区)的**展示层**语义被 §2.6 分区制修订;数据层(获取链、适配层、校验、降级、缓存)全部不变。
68
+
67
69
  - **双层模型**:主清单(§2.1–2.4 全部语义不变)之上叠加只读的**社区清单**——awesome-dsh-plugin 维护的全量社区目录(4,377+ 条,日增约 62)。**合并市场 = 主清单 ∪ 社区清单去重(主清单恒优先)**,GUI / agent 工具 / CLI 三端同源同语义。本节修订 §9.1 Q25「不做运行时多源合并」,架构决策见 [ADR-0003](./adr/0003-community-catalog-merge.md)。
68
70
  - **数据锚定 npm 包(Q39)**:`dsh-plugin-catalog`(CC0-1.0,版本 `YYYY.MDD.RUN`)为唯一数据锚。获取链:`registry.npmjs.org` dist-tags 探测最新版本(几 KB)→ **版本未变不重拉正文(版本号即 revalidate 验证器)**→ jsDelivr 按精确版本直取 `plugins.json`(版本 pin 后内容不可变,CDN 缓存无害)→ npmmirror files API → unpkg 按精确版本直取(末位兜底;均为纯 JSON 文件线路,不引入 tar 解包依赖)。npm 精确版本不可变 = 完整性锚;**不做包内快照兜底**(对日增 62 条的目录,过期数据不得冒充最新——陈旧必须显式标注),失败如实报错(发生了什么 / 为什么 / 现在怎么办)。
69
71
  - **目录适配层**:原生条目 → 收录条目。`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,6 +75,26 @@
73
75
  - **开关与缓存**:`communityCatalog`(默认 true,volatile live 生效)+ `communityCatalogPin`(可选锁 npm 版本);CLI 用 `DSHM_COMMUNITY_CATALOG=0` 退出。社区缓存放 `<namespace>/awesome/` 子目录(不参与 `pruneCaches` 的顶层 `*.json` 清理),正文按版本文件缓存,`fetchedAt`/版本号/线路进设置页展示;TTL 复用 `cacheTtlMin`(只约束 dist-tags 探测频率)。
74
76
  - **市场行为(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 图床白名单由上游保证)。
75
77
 
78
+ ### 2.6 双清单分区市场与交互升级(0.7.0 grilling 定稿 2026-09-29)
79
+
80
+ > 本节只动**展示层与三端契约**;数据层(§2.5 获取链、适配层、校验、降级、缓存)与 Profile 变更事务(§3)不变。参考实现:`.dsh-research/dsh-market-clone/`(同一上游目录的原生市场应用,**结构级借鉴其浏览体验,不借鉴其安装链路**——dsh-m 的差异化 = 策展精选层 + 三端同源 + Profile 变更事务)。
81
+
82
+ - **定位升级**:dsh-m 从「个人自用」转向**面向大众发布**(§1 定位句同步改写)。
83
+ - **分区制(修订 §2.5 Q45)**:市场面板改为分区 tab——**「社区」为默认落地页**(全量社区条目)、**「精选」在后**(主清单条目,单页、策展顺序即排序)、**「收藏」第三**(本地收藏)。各区**独立状态实例**:分类、搜索、排序、分页互不重置。「只看主清单」chip 退役;Q40 的共享过滤桶退役(各区分类独立:精选区 5 chips、社区区全量 chips),「分类保留原生值不转译」仍成立。社区区**排除与主清单重复的条目**(displaced 数据现成)。**数据层 `mergeRegistries` 去重不变**(ADR-0003 架构不动)——「合并市场」的合并收敛为数据层语义,展示层分区。
84
+ - **社区区浏览设施**(dsh-market 结构级借鉴):
85
+ - 分类 chips:两行折叠 + 实测裁剪(渲染后逐 chip 测量可容纳数)+ 收起时激活分类置前 + 滚动吸顶自动收缩(不改写用户开合选择);分类标签**单一事实源**(core 提供标签表,杀掉客户端内嵌副本)。
86
+ - 排序:`downloads | stars | added × asc/desc`,默认 `downloads-desc`;**无下载量(github-only)≠ 0 下载**(永远排在有真实计数条目之后,彼此按 stars 排);时间窗过滤明确不做(`added` 排序覆盖「找最新」诉求)。
87
+ - 卡片 byline:`owner / downloads / ★stars`(旁路数据已透传,Tooltip 给精确数);`deprecated` 徽章 + 详情内 replacement 替代链接(适配层补透传原生字段)。
88
+ - 分页:页码窗口化(`1 … n-1 n n+1 … total`,≤7 页全显)+ 页大小 24/48/96 + 筛选变化重置页 1 + 翻页回顶;仅 prev/next 的旧分页退役。
89
+ - 搜索:相关性加权管线——NFKC 归一化 + 中西文边界插空格 + 字段权重(name/npm > owner > 描述 > 分类)+ 命中类型加分 + 按条目缓存归一化结果;输入 250ms debounce + IME composition 全程处理 + draft/已提交 query 分离。
90
+ - 描述 5 行钳制(真实溢出才显示展开钮);**数据层双语**——描述按 UI 语言取 zh/en(数据现成),UI 完整双语字典列 backlog。
91
+ - **详情 = Modal 且为卡片超集**(「detail 显示少于摘要就是倒退」):byline / 分类 / 收录日期 / 下载量窗口三要素(计数 + 窗口 + 核对时间)/ 描述全文 / 截图灯箱(←→/Esc、**禁自动轮播**)/ 能力披露 + 红线(**默认收起**)/ 安装命令折叠行 / deprecated 替代链接;精选条目超集另加 verified 与 tags。**安装确认走 Modal**。卡片瘦身:能力披露、截图、安装命令全部迁出卡片(Q44 精神「只在详情层、默认收起」不变,载体升级)。
92
+ - **全局操作记录**:每个变更操作一条 record(`queued / running / input=冲突待决 / done / warned / failed`),**状态不挂卡片**——翻页、搜索、切 tab 不丢;**localStorage 持久化队列**,宿主重载恢复时逐条校验「此刻仍成立才执行,否则报告」;覆盖安装、升级、卸载、开关。已装侧跟随一致化:「全部更新 (N)」批量入口 + tab 更新红点 + 卡片视觉与发现侧同体系;组管理、个人备注列 backlog。
93
+ - **收藏**:浏览器 localStorage 本地收藏(不进 profile、不进服务端);收藏区 stale 条目(目录中已下架)单独提示 + 一键清理。
94
+ - **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 条。
95
+ - **技术默认件**:图片三层懒加载(IntersectionObserver + `loading=lazy` + `fetchPriority=low`;缩略图本机直连原图,不引第三方代理服务);移除客户端重复排序(服务端单一排序源);`total>200` 性能提示随分区退役;host-api limit 上限对齐新页大小;空状态逐分区定制;错误态带具体原因 + 重试。
96
+ - **Backlog(明确不做)**:UI 完整双语字典、时间窗过滤、浏览层宿主兼容徽章/过滤、组管理、个人备注、giscus 评论、静态官网。
97
+
76
98
  ## 3. 安装 / 卸载 / 升级 / 重启
77
99
 
78
100
  底层原语(本机实证):`dsh plugin --profile web add|remove|update`(转发 pnpm,作用于 `$DSH_HOME/profiles/web`)。**profile 的 `package.json` 就是唯一事实源**——不引入任何额外状态文件。
@@ -120,10 +142,11 @@ npm 安装 / GitHub 安装 / 升级 / 自升级 / 卸载是**同一个事务模
120
142
  0.4.0 增补:已装卡右上 `dshm-switch` 开关(受 `toggleable` 控制,锁因 title 提示);sub 行相位点 `● active · v1.0.8 · npm`(相位点只映射 phase 五值,「已停用」归 Switch,两输入源各管各的);开关结果通知按 `applied` 分流(live → 绿 toast「即时生效」;restart-required → 沿用重启横幅 + 一键重启);安装遇兼容拦截(409 + issue)弹「仍要安装」确认(红字风险 + peers 清单 → `forceIncompatible` 重发)。
121
143
 
122
144
  0.5.0 增补(合并市场,详见 §2.5):筛选栏分组「精选(5)|社区(已知 20 带计数 + 新分类临时组)」,「只看主清单」chip 常驻首位;市场卡与已装卡对社区条目显示「社区收录」徽标;排序主清单置顶 + 社区按 30 天下载量降序;详情折叠区展示能力披露与截图(仅社区条目、缺省=未扫描≠未检出);notice 双源状态(主清单状态 + 社区清单状态互相独立,社区失败静默)。
145
+ > **2026-09-30 修订(0.7.0,详见 §2.6)**:上一段的混排形态(「只看主清单」chip、折叠区详情、50/页)已被分区制取代——市场页为社区(默认)/精选/收藏三分区,详情升级为 Modal 超集;本段仅作历史沿革保留。
123
146
 
124
- 3 个视图,卡片展开式详情(不做独立详情页),**中文优先**,跟随 DSH Web 深色主题:
147
+ 3 个视图,**中文优先**,跟随 DSH Web 深色主题(0.7.0:卡片展开式详情已由 Modal 取代):
125
148
 
126
- 1. **市场页**(默认):registry 卡片流;搜索/分类为**服务端过滤**(Host 强制 `withLatest=true`、每页 50、1,000 条清单第一页只探测当前页);`MarketPanel` 是市场/已装数据唯一 owner(请求 generation + AbortController 丢弃旧响应);安装/卸载/升级完成后通过统一协调器同时刷新市场与已装快照,不要求关闭并重新打开面板;分页控件 + 超过 200 条性能提示 + 默认/自定义/缓存/不可用短提示(不含本地路径);卡片详情保留 README markdown 预览与 npm/GitHub 官方外链。
149
+ 1. **市场页**(默认):**分区制**(0.7.0 §2.6)——社区/精选/收藏三分区,各分区独立分类/搜索/排序/分页状态;搜索/分类/排序为**服务端处理**(Host 强制 `withLatest=true`、limit clamp 1..96 缺省 24,探测仅当前页);`MarketPanel` 是市场/已装数据唯一 owner(请求 generation + AbortController 丢弃旧响应);安装/卸载/升级完成后通过统一协调器同时刷新市场与已装快照,不要求关闭并重新打开面板;页码窗口化分页(24/48/96)+ 默认/自定义/缓存/不可用短提示(不含本地路径;0.5.0 的「超过 200 条性能提示」随分区退役);详情 Modal 保留 README markdown 预览与 npm/GitHub 官方外链。
127
150
  2. **已装页**:profile 实际安装列表,标注来源(市场/非市场/未知);registry 不可用时仍列出已装并标记;「可升级 → x.y.z」徽标 +「升级」;「卸载」。
128
151
  3. **设置页**:registry 地址草稿 +「校验并应用」(先校验候选再写 settings,失败不落盘)/「恢复默认」/「下载默认 registry.json」(不改当前配置)/「检查条目可达性」(probe 统计 + 最多 100 条 issue,只读不改配置);同时展示配置地址、当前生效配置、生效来源与 configStatus(含 rejected/回滚原因与维护性 warnings);完整本地路径仅在此页显示。registry 配置为 **live 生效,不出现安装类重启横幅**;安装/卸载/升级仍保留重启横幅。
129
152
 
package/lib/cli.js CHANGED
@@ -8,9 +8,21 @@
8
8
  import { installFromRegistry, listInstalledWithMeta, listMarket, uninstallPlugin, upgradePlugin, InstallGuardError, } from './core/market.js';
9
9
  import { togglePlugin as coreTogglePlugin } from './core/toggle.js';
10
10
  import { loadRegistry } from './core/registry.js';
11
+ import { COMMUNITY_CATEGORY_LABELS } from './core/community.js';
11
12
  import { scheduleRestart } from './core/restart.js';
12
13
  import { pathToFileURL } from 'node:url';
13
14
  import { resolve } from 'node:path';
15
+ /** 分类中文标签(0.7.0 Task 6):精选 5 类本地表 + 社区已知标签单一事实源;未知 slug 原样。 */
16
+ const CLI_CATEGORY_LABELS = {
17
+ market: '市场',
18
+ tools: '工具',
19
+ ui: '界面',
20
+ search: '搜索',
21
+ other: '其他',
22
+ };
23
+ function categoryLabelOf(category) {
24
+ return CLI_CATEGORY_LABELS[category] ?? COMMUNITY_CATEGORY_LABELS[category] ?? category;
25
+ }
14
26
  function parseArgs(argv) {
15
27
  const cmd = argv[0] || 'help';
16
28
  const flags = {};
@@ -107,12 +119,13 @@ function unavailableLines(state) {
107
119
  state.errors.length ? `错误:${state.errors.join(';')}` : '',
108
120
  ].filter(Boolean);
109
121
  }
110
- const HELP = `dshm — DSH Marketplace(个人自用 DSH 插件市场)
122
+ const HELP = `dshm — DSH Marketplace(DSH 插件市场:精选策展 + 社区目录双清单)
111
123
 
112
124
  用法:dshm <命令> [参数]
113
125
 
114
126
  只读命令:
115
- dshm search [--query 关键词] [--category market|tools|ui|search|other|<社区slug>] [--limit N] [--primary-only]
127
+ dshm search [--query 关键词] [--category market|tools|ui|search|other|<社区slug>] [--source primary|community|all] [--limit N] [--offset N]
128
+ (source 分区:community=社区目录 4000+ 条 / primary=精选策展 / all=默认;limit 默认 10;offset 翻页)
116
129
  dshm list 列出 web profile 已装插件(含市场标注/可升级)
117
130
  dshm outdated 检查已装插件的最新版本
118
131
  dshm registry 查看收录清单来源与条目
@@ -160,15 +173,24 @@ async function runCliDispatch(argv, deps, io) {
160
173
  out(HELP);
161
174
  return 0;
162
175
  case 'search': {
163
- // metadata-only:不构造全量 latest,query/category/limit 直接交给 core;
176
+ // metadata-only:不构造全量 latest;source/offset/limit 直接交给 core(0.7.0 Task 6:与工具同语义);
164
177
  // 双源判定:主清单 unavailable 但社区有条目 → 照常出页(Q42);两层皆不可用 → exit 1
165
- const limit = Number.isFinite(Number(flags.limit)) && Number(flags.limit) > 0 ? Number(flags.limit) : undefined;
178
+ const sourceRaw = typeof flags.source === 'string' ? flags.source : '';
179
+ if (sourceRaw !== '' && sourceRaw !== 'primary' && sourceRaw !== 'community' && sourceRaw !== 'all') {
180
+ err(`错误:非法 source: ${sourceRaw}(需 primary/community/all)`);
181
+ return 1;
182
+ }
183
+ const source = sourceRaw === '' ? 'all' : sourceRaw;
184
+ const limitRaw = Number(flags.limit);
185
+ const limit = Number.isFinite(limitRaw) && limitRaw > 0 ? Math.min(80, Math.max(1, Math.floor(limitRaw))) : 10;
186
+ const offsetRaw = Number(flags.offset);
187
+ const offset = Number.isFinite(offsetRaw) && offsetRaw > 0 ? Math.floor(offsetRaw) : 0;
166
188
  const result = await d.listMarket(cfg, {
167
189
  query: typeof flags.query === 'string' ? flags.query : undefined,
168
190
  category: typeof flags.category === 'string' ? flags.category : null,
169
- offset: 0,
191
+ source,
192
+ offset,
170
193
  limit,
171
- primaryOnly: flags['primary-only'] === true,
172
194
  withLatest: false,
173
195
  namespace: 'cli',
174
196
  deadlineMs: SEARCH_CORE_DEADLINE_MS,
@@ -189,13 +211,22 @@ async function runCliDispatch(argv, deps, io) {
189
211
  }
190
212
  for (const it of result.items) {
191
213
  const inst = it.installed ? ` [已安装 v${it.installedVersion || '?'}]` : '';
192
- out(`• ${it.name} (${it.id}) · ${it.category} · ${it.source}${inst}`);
214
+ const zone = it.community === true ? '[社区] ' : '';
215
+ out(`• ${it.name} (${it.id})${zone}· ${categoryLabelOf(it.category)} · ${it.source}${inst}`);
193
216
  out(` ${it.description}`);
194
217
  }
218
+ const shown = result.items.length;
219
+ const nextOffset = offset + shown < result.total ? offset + shown : null;
195
220
  const s = result.registryState;
196
221
  out(`\n来源:${registrySourceLabel(s.source)}${s.stale ? '(缓存)' : ''} · 更新:${s.fetchedAt ?? '—'} · 共 ${result.total} 条`);
197
222
  const c = result.community;
198
- out(`社区:${c.status === 'ready' ? '就绪' : c.status === 'stale' ? '缓存快照' : c.status} · 收录 ${c.acceptedCount} · 版本 ${c.version ?? '—'} · 线路 ${c.route ?? '—'}`);
223
+ if (c.status !== 'skipped') {
224
+ const cLabel = c.status === 'ready' ? '就绪' : c.status === 'stale' ? '缓存快照' : c.status;
225
+ out(`社区:${cLabel} · 收录 ${c.acceptedCount} · 版本 ${c.version ?? '—'} · 线路 ${c.route ?? '—'}`);
226
+ }
227
+ out(nextOffset !== null
228
+ ? `已显示 ${offset + 1}–${offset + shown} · 翻页:加 --offset ${nextOffset}(其余参数不变)`
229
+ : `已显示 ${offset + 1}–${offset + shown} · 已到末尾`);
199
230
  return 0;
200
231
  }
201
232
  case 'list': {