dsh-m 0.1.1 → 0.2.1

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
@@ -8,26 +8,32 @@
8
8
 
9
9
  English · [中文](./README.md)
10
10
 
11
- Your personal plugin marketplace for DeepSeek Harness (DSH): **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 seven `dshm_*` agent tools and the `dshm` CLI.
12
12
 
13
13
  ## Quick start
14
14
 
15
- ```sh
16
- npm install -g dsh-m # or: dsh plugin --profile web add dsh-m@<version>
15
+ **Paste this whole block to your agent to install:**
16
+
17
+ ```text
18
+ Install and enable the DSH plugin dsh-m:
19
+ 1. Run `dsh plugin --profile web add dsh-m`
20
+ 2. Restart DSH Web to load the plugin (on this machine: `systemctl --user restart dsh-web.service`; or restart it your usual way)
21
+ 3. Poll `curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3080` until it returns 200 again
22
+ 4. Run `curl -s -X POST http://127.0.0.1:3080/dshm -H 'content-type: application/json' -d '{"method":"ping"}'` and confirm the reply has `plugin: dsh-m`
23
+ 5. Then remind me to refresh the page and click "Plugin Marketplace" at the bottom of the sidebar
17
24
  ```
18
25
 
19
- 1. **Restart DSH Web** after installing (one-click restart works too).
20
- 2. Refresh the page and click "**Plugin Marketplace**" at the bottom of the sidebar.
26
+ > `npm install -g dsh-m` installs only the `dshm` CLI (terminal management) it does **not** register the plugin into the web profile; it can coexist with the plugin install above.
21
27
 
22
- First listings include: DSH Skins, DSH Web Search, the Lark / QQ / Weixin / WeCom / DingTalk channel bridges, and dsh-m itself.
28
+ The default registry includes DSH Skins, ModSearch, the Lark / QQ / Weixin / WeCom / DingTalk channel bridges, and dsh-m itself.
23
29
 
24
30
  ## Panel
25
31
 
26
32
  | View | Capabilities |
27
33
  |---|---|
28
- | **Market** | Card flow with search, category filters and tags; expandable details; "Install" — npm sources pin the latest exact version, GitHub sources pin the release/tag commit |
34
+ | **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 |
29
35
  | **Installed** | What your web profile actually has, annotated "via market / non-market"; update badges, upgrade, two-step confirm uninstall; 📖 README preview (64KB cap) |
30
- | **Settings** | Registry cache status + force refresh, registry source override, dsh-m self-update |
36
+ | **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 |
31
37
 
32
38
  After any mutation a "⚡ Restart" banner appears — restart goes through the systemd unit (same-origin checked, detached-helper fallback) and polls by boot id until the service is back. Installs stream live pnpm progress (resolve → download → link → build).
33
39
 
@@ -54,11 +60,23 @@ dshm uninstall --pkg dsh-web-search --yes
54
60
  dshm restart --yes
55
61
  ```
56
62
 
63
+ When the registry is unavailable, `registry` / `search` / `outdated` print the configured vs active address and exit 1; `list` still shows installed plugins. The CLI uses its own cache namespace and never touches the Web side's.
64
+
57
65
  ## Registry
58
66
 
59
- `registry.json` is hand-curated and served at runtime via **raw.githubusercontent / jsDelivr `@main`** (local 60-min TTL cache + bundled snapshot fallback) — listing updates are decoupled from plugin releases. To add or amend a listing, edit `registry.json` and open a PR; CI validates schema, npm/GitHub existence, duplicate ids and URL reachability.
67
+ `registry.json` is hand-curated and fetched at runtime in order: **GitHub raw (`@main`) → GitHub mirror (jsDelivr CDN, backup line) → local 60-min TTL cache bundled snapshot** — listing updates are decoupled from plugin releases. To add or amend a listing, edit `registry.json` and open a PR; CI validates the strict schema, npm/GitHub existence, duplicate ids and URL reachability.
68
+
69
+ **Custom registry (overrides the official one)**: the Settings tab supports a single custom registry address that **replaces** the default registry (no merging):
60
70
 
61
- Security baseline: HTTPS-only fetches with size caps and timeouts; npm installs verified by lock integrity + post-install version match; GitHub installs pinned to commit SHA; pnpm build scripts are allowed-by-policy with an explicit report when unblocked.
71
+ 1. "Download default registry.json" gives you a copy of the official listing;
72
+ 2. edit the copy yourself (add/remove entries);
73
+ 3. paste its address in Settings and hit "Validate & apply" — an **HTTPS URL**, a local **absolute path / `file://`** (HTTP only for 127.0.0.1/localhost debugging);
74
+ 4. validation failures (bad fields, missing path, over 2 MiB / 1,000 entries, …) are never saved — the currently active registry stays; a successful apply takes effect **immediately, no restart** (only the first deploy of a new dsh-m version needs one restart);
75
+ 5. "Restore default" switches back to the official registry in one click.
76
+
77
+ Rules and limits: strict v1 schema (unknown fields / invalid ids / oversized values / duplicates reject the whole file, never truncated); the copy is a standalone snapshot and does **not** auto-sync with the official listing; a failing custom source keeps its own last good cache and never silently falls back to the official registry; old custom-source caches are cleaned after switching (the default cache is kept); custom registries are not validated by official CI — install only from sources you trust; full local paths appear only on the Settings tab, tools and cards show short statuses.
78
+
79
+ Security baseline: HTTPS-only fetches (loopback HTTP excepted) with per-hop redirect checks, size caps and timeouts; npm installs verify the exact version's dist integrity against the pnpm lockfile — mismatches fail closed and roll back; GitHub installs pinned to commit SHA; pnpm build scripts are allowed-by-policy with an explicit report when unblocked.
62
80
 
63
81
  ## Development
64
82
 
@@ -75,14 +93,17 @@ Release: `npm version patch|minor|major && git push --tags` → OIDC trusted pub
75
93
 
76
94
  ## FAQ
77
95
 
78
- **Why don't GitHub-sourced update hints follow main?**
96
+ **1. Why don't GitHub-sourced update hints follow main?**
79
97
  Intermediate commits on main can be unstable. dsh-m tracks **releases / tags** only (`releases/latest` first, tags list as fallback) and pins the commit SHA the tag points to.
80
98
 
81
- **Does uninstalling delete my data?**
99
+ **2. Does uninstalling dsh-m delete my data?**
82
100
  No. Only the package reference in the profile is removed (live UI disabled first), and suspected leftover paths are reported to you.
83
101
 
84
- **Does it work with third-party skins?**
85
- Yes. Every color comes from DSH theme semantic tokens (`state-*` / `brand-*` / `bg-overlay`), verified against the official light/dark themes and third-party skins.
102
+ **3. Will a custom registry slow the market down?**
103
+ 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.
104
+
105
+ **4. What if my custom source goes down?**
106
+ 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.
86
107
 
87
108
  ## License
88
109
 
package/README.md CHANGED
@@ -8,26 +8,32 @@
8
8
 
9
9
  [English](./README.en.md) · 中文
10
10
 
11
- 个人自用的 DeepSeek Harness (DSH) 插件市场:**收录 · 安装 · 卸载 · 升级**,全部本机完成。以 DSH web 插件形态运行——侧栏「插件市场」打开三视图面板,同时提供 `dshm_*` agent 工具与 `dshm` CLI。
11
+ 可自定义收录清单(Registry)的 DeepSeek Harness (DSH) 插件市场:**收录 · 安装 · 卸载 · 升级**,全部本机完成。以 DSH web 插件形态运行——侧栏「插件市场」打开三视图面板,同时提供 `dshm_*` agent 工具与 `dshm` CLI。
12
12
 
13
13
  ## 30 秒上手
14
14
 
15
- ```sh
16
- npm install -g dsh-m # 或 dsh plugin --profile web add dsh-m@<version>
15
+ **把下面整段贴给 agent 即可完成安装**:
16
+
17
+ ```text
18
+ 安装并启用 DSH 插件 dsh-m:
19
+ 1. 执行 `dsh plugin --profile web add dsh-m`
20
+ 2. 重启 DSH Web 使插件加载(本机:`systemctl --user restart dsh-web.service`;或按你的部署方式重启)
21
+ 3. 轮询 `curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3080`,直到恢复 200
22
+ 4. 执行 `curl -s -X POST http://127.0.0.1:3080/dshm -H 'content-type: application/json' -d '{"method":"ping"}'`,确认返回 `plugin: dsh-m`
23
+ 5. 完成后提醒我刷新页面,点击侧栏底部的「插件市场」
17
24
  ```
18
25
 
19
- 1. 安装后**重启 DSH Web**(一键重启亦可)。
20
- 2. 刷新页面,点击侧栏底部的「**插件市场**」。
26
+ > `npm install -g dsh-m` 只安装 `dshm` CLI(终端管理用),**不会**把插件注册进 web profile;与上面插件安装不冲突,可并存。
21
27
 
22
- 收录清单里的首批插件:DSH Skins、DSH Web Search、Lark / QQ / 微信 / 企业微信 / 钉钉通道桥,以及 dsh-m 自身。
28
+ 默认收录清单里的插件包括:DSH Skins、ModSearch、Lark / QQ / 微信 / 企业微信 / 钉钉通道桥,以及 dsh-m 自身。
23
29
 
24
30
  ## 界面(侧栏「插件市场」)
25
31
 
26
32
  | 视图 | 能力 |
27
33
  |---|---|
28
- | **市场** | 收录卡片流;关键词搜索、分类筛选、标签;卡片展开详情与「安装」;npm 源锁定最新精确版本,GitHub 源锁定 release/tag 指向的 commit |
34
+ | **市场** | 收录卡片流;关键词搜索、分类筛选(服务端过滤 + 分页,每页 50 条,1,000 条清单也只探测当前页);卡片展开详情与「安装」;npm 源锁定最新精确版本,GitHub 源锁定 release/tag 指向的 commit |
29
35
  | **已装** | web profile 实装列表,标注「市场安装 / 非市场安装」;可升级徽标、升级、两段式确认卸载;📖 README 预览(64KB 截断) |
30
- | **设置** | 收录清单缓存状态与强制刷新、registry 源覆盖、dsh-m 自更新 |
36
+ | **设置** | registry 地址草稿 +「校验并应用 / 恢复默认 / 下载默认 registry.json / 检查条目可达性」;配置地址、生效来源与状态一目了然;强制刷新;dsh-m 自更新 |
31
37
 
32
38
  安装 / 卸载 / 升级完成后出现「⚡ 一键重启」横幅——重启走 systemd 单元(同源校验,兜底 detached helper),按 boot id 轮询直至服务恢复。安装过程实时显示 pnpm 进度(解析 → 下载 → 链接 → 构建)。
33
39
 
@@ -54,11 +60,23 @@ dshm uninstall --pkg dsh-web-search --yes
54
60
  dshm restart --yes
55
61
  ```
56
62
 
63
+ 清单不可用时 `registry` / `search` / `outdated` 打印配置与实际生效地址并退出码 1;`list` 仍列出已装插件。CLI 固定独立缓存命名空间,不影响 Web 端。
64
+
57
65
  ## 收录清单(registry)
58
66
 
59
- `registry.json` 手工 curated,运行时经 **raw.githubusercontent / jsDelivr `@main`** 分发(本地 60 分钟 TTL 缓存 + 包内快照兜底)——收录更新与插件发版**解耦**。收录 / 修订直接改 `registry.json` 发 PR,CI 自动校验:schema、npm 包与 GitHub 仓库存在性、重复 id、URL 可达性。
67
+ `registry.json` 手工 curated,运行时按 **GitHub 原始文件(raw @main)→ GitHub 镜像(jsDelivr CDN,备用线路)→ 本地 60 分钟 TTL 缓存 包内快照** 的顺序获取——收录更新与插件发版**解耦**,push 后最多等一个缓存周期(可在设置页强制刷新)。收录 / 修订直接改 `registry.json` 发 PR,CI 自动校验:严格 schema、npm 包与 GitHub 仓库存在性、重复 id、URL 可达性。
68
+
69
+ **自定义收录清单(可覆盖官方清单)**:设置页支持单一自定义 registry 地址,**整体覆盖**默认清单(不合并):
60
70
 
61
- 安全基线:拉取仅 HTTPS + 体积上限 + 超时;npm 安装按 lock integrity 校验 + 装后版本核对;GitHub 安装强制锁定 commit SHA;pnpm 构建脚本被拦时按策略放行并明确报告。
71
+ 1. 「下载默认 registry.json」得到一份官方清单副本;
72
+ 2. 自行编辑副本(增删条目);
73
+ 3. 在设置页填入副本地址并「校验并应用」——支持 **HTTPS URL**、本机**绝对路径 / `file://`**(HTTP 仅限 127.0.0.1/localhost 本机调试);
74
+ 4. 校验失败(字段错误、路径不存在、超过 2 MiB / 1,000 条等)不会保存配置,当前生效清单保持不变;应用成功**即时生效,无需重启**(仅首次部署新版本 dsh-m 需要一次重启);
75
+ 5. 「恢复默认」一键回到官方清单。
76
+
77
+ 规则与边界:严格 v1 schema(未知字段 / 非法 ID / 超限 / 重复一律拒绝,不截断);副本是独立快照,**不会自动同步**官方新条目;自定义源失败时保留其最近一次成功缓存,绝不静默回退官方清单;切换后旧自定义源缓存会被清理(默认缓存保留);自定义清单未经官方 CI 校验,请确认来源可信再安装;完整本地路径只在设置页显示,工具与卡片只显示短状态。
78
+
79
+ 安全基线:拉取仅 HTTPS(loopback HTTP 除外)+ 重定向逐跳校验 + 体积上限 + 超时;npm 安装按精确版本 dist integrity 对照 pnpm lockfile 校验,不一致 fail closed 并回滚;GitHub 安装强制锁定 commit SHA;pnpm 构建脚本被拦时按策略放行并明确报告。
62
80
 
63
81
  ## 开发
64
82
 
@@ -75,14 +93,17 @@ node scripts/validate-registry.mjs
75
93
 
76
94
  ## FAQ
77
95
 
78
- **为什么 GitHub 来源的更新提示不走 main HEAD?**
96
+ **1. 为什么 GitHub 来源的更新提示不走 main HEAD?**
79
97
  main 上的中间提交可能不稳定。dsh-m 只跟踪 **release / tag**(优先 `releases/latest`,无 release 回退 tags 列表),安装时锁定 tag 指向的 commit SHA。
80
98
 
81
- **卸载会删我的数据吗?**
99
+ **2. 卸载 dsh-m 会删我的数据吗?**
82
100
  不会。只移除 profile 中的包引用(卸载前先下线运行中的界面),并把疑似残留路径报告给你。
83
101
 
84
- **支持第三方皮肤吗?**
85
- 支持。全部配色来自 DSH 主题语义 token(`state-*` / `brand-*` / `bg-overlay` 等),官方深浅色与第三方皮肤均已验证。
102
+ **3. 自定义清单会让市场变慢吗?**
103
+ 收录超过 200 条时会提示性能边界。市场列表是服务端分页(每页 50 条),1,000 条清单第一页也只查询当前页的最新版本,浏览仍然流畅。
104
+
105
+ **4. 自定义源挂了怎么办?**
106
+ 优先使用该源最近一次成功的缓存并标记「缓存来源」;完全没有缓存时市场显示「收录清单不可用」,已安装插件仍可正常管理。修正地址或恢复默认即可。
86
107
 
87
108
  ## License
88
109
 
package/docs/DESIGN.md ADDED
@@ -0,0 +1,172 @@
1
+ # dsh-m v1 设计共识(grilling 定稿)
2
+
3
+ > 2026-09-04 与 owner 三轮对齐的完整共识。实现以本文档为准;与本文冲突的实现细节以本文档为准。
4
+
5
+ ## 1. 定位与形态
6
+
7
+ - **dsh-m** = 个人自用的 DSH(DeepSeek Harness)插件市场。
8
+ - 形态:**一个 DSH web 插件**(旗舰是 Web GUI)+ **7 个 agent 工具** + **薄 CLI `dshm`**,全部在同一个 npm 包里,不搞 monorepo。
9
+ - npm 包名 `dsh-m`(已占位发布 0.0.x),插件 id `dsh-m`,显示名 **DSH Marketplace**。
10
+ - 管理对象:**只管 DSH 插件**,不管 Agent Skills(skills 归 skillhub)。
11
+ - 与 `@cocofhu/skillhub` **完全独立并存**:不读不写它的数据与配置,仅在实现机制上借鉴(其源码镜像见 §10)。
12
+ - 单机自用:无服务端、无账号、无提交入口;收录变更 = 改本仓库的 registry(他人可发 PR)。
13
+
14
+ ## 2. Registry(收录清单)
15
+
16
+ ### 2.1 载体与分发(v1.1 可自定义 registry 定稿)
17
+ - repo 内**单文件 `registry.json`**,手工 curated,版本号**不写死**(运行时实查 npm/GitHub)。
18
+ - **默认源**获取顺序:raw.githubusercontent `@main` → jsDelivr `@main` → 默认 TTL 缓存 → npm 包内快照兜底。jsDelivr 是 GitHub 内容的免费 CDN 镜像,仅作 raw 拉取失败时的**备用线路**(覆盖大陆可达性与 GitHub 故障;CDN 缓存可能滞后数小时,可用 purge.jsdelivr.net 手动清理)。收录更新与插件发版**解耦**。
19
+ - **自定义覆盖源(单一地址,整体覆盖,不合并)**:`registryUrl` 为空 = 官方默认清单;非空 = 一个 HTTPS URL(或 loopback HTTP,仅本机管理员信任边界,不承诺 DNS rebinding 防护)或 DSH Web 主机上的本地普通文件(绝对路径 / `file://`,`realpath` + `O_NOFOLLOW` 同 fd 读取与复核,严格 UTF-8,2 MiB 原始字节上限)。自定义源失败只回退**该源自己的缓存**,绝不静默改用官方清单;无可缓存数据时返回空清单 + 不可用状态。
20
+ - **「新增插件」流程**:设置页下载默认 `registry.json` → 用户自行编辑副本 → 填入副本地址「校验并应用」。副本是独立快照,不自动同步官方新条目。
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
+ - **缓存模型**: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
+ - **统一安全 HTTP**:所有 JSON/text/HEAD 请求共享同一 primitive——手动重定向(每跳校验协议、最多 3 跳、循环检测、signal 传播、返回最终 URL)+ 响应大小上限 + 超时。
24
+ - **live 语义**:registry 地址是 live 设置,应用成功即时生效(无需重启);只有**首次部署新 Host 代码**需要一次用户确认后的 DSH Web 重启。`timeoutMs`/`cacheTtlMin` 亦为 live 读取;agent 框架注册时的静态工具 deadline 不承诺热更新。
25
+
26
+ ### 2.2 Schema(v1 定稿,严格校验)
27
+
28
+ 规则补充(与 `scripts/validate-registry.mjs`、`tests/registry.test.mjs` 同源实现):
29
+ - `id`:小写字母/数字开头,仅小写字母、数字、`.`、`_`、`-`,≤64 字符且清单内唯一;
30
+ - `name` ≤100、`description` ≤500、`tags` ≤10 个且单个 ≤30 字符、tag 不重复;超限拒绝,不截断;
31
+ - npm 包名:标准 scoped/unscoped 形状(≤214 字符,不接受版本/range/URL/空白);GitHub:`owner/repo`(owner ≤39、repo ≤100);
32
+ - `homepage`/`icon`:HTTPS、≤2,048 字符、无 userinfo;
33
+ - registry 地址规范化:trim 外层空白、拒控制字符与 userinfo、去 fragment 留 query、拒绝已知凭据 query key(`token`/`access_token`/`api_key`/`password`/`secret`)。
34
+ ```jsonc
35
+ {
36
+ "version": 1,
37
+ "plugins": [
38
+ {
39
+ "id": "dsh-skins", // slug,唯一
40
+ "name": "DSH Skins",
41
+ "description": "中文描述", // v1 只有中文
42
+ "category": "ui", // market|tools|ui|search|media|other 六选一
43
+ "tags": ["主题"],
44
+ "source": "github", // npm|github
45
+ "npm": "可选;source=npm 时必填",
46
+ "github": "owner/repo", // source=github 时必填;npm 条目也可附
47
+ "homepage": "https://...",
48
+ "icon": "可选;覆盖自动头像"
49
+ }
50
+ ]
51
+ }
52
+ ```
53
+
54
+ ### 2.3 CI 校验(`.github/workflows/registry.yml`)
55
+ `registry.json`、`src/**`、`tests/**`、`scripts/**`、`package.json`、lockfile 或 workflow 本身变更时触发:
56
+ 1. `npm run build`(TypeScript + esbuild + bundle marker,含 `lib/cli.js`);
57
+ 2. `npm test`(Node 内置 test runner 全量契约测试);
58
+ 3. schema 合法(复用 `lib/core/registry.js` 的严格 `validateRegistry`,与运行时同一套规则);
59
+ 4. npm 条目可查;GitHub 条目 repo 存在;`icon`/`homepage` URL 可达(icon 允许为空)。
60
+ 自定义 registry 不经过官方 CI——设置页对自定义源展示未校验信任提示。
61
+
62
+ ## 3. 安装 / 卸载 / 升级 / 重启
63
+
64
+ 底层原语(本机实证):`dsh plugin --profile web add|remove|update`(转发 pnpm,作用于 `$DSH_HOME/profiles/web`)。**profile 的 `package.json` 就是唯一事实源**——不引入任何额外状态文件。
65
+
66
+ - **安装(npm 源)**:装最新版并以**精确版本锁定**(不用 `^` 范围;用户指定版本必须为精确 semver,经该精确版本 endpoint 查询)。安装前对 profile 的 `package.json` / `pnpm-lock.yaml` / `pnpm-workspace.yaml` 做字节快照;安装后核验 importer 依赖为该精确版本,并在 lockfile `packages` 条目中比对与 npm dist 一致的 `resolution.integrity`——缺失或不一致 **fail closed** 并执行 **best-effort dependency rollback**(原子恢复快照 + `pnpm install --frozen-lockfile`;恢复失败同时报告两类错误并提示人工修复)。不声称 node_modules 与间接依赖已字节级回滚。
67
+ - **安装(GitHub 源)**:解析并**锁定 commit SHA**(`github:owner/repo#sha`),skillhub 同款。
68
+ - **已装识别**:读 profile `package.json` dependencies,与 registry 匹配 → 标注「市场安装」;不匹配的也列出,标注「非市场安装 / 来源未知」。卸载/升级对两类都可用。
69
+ - **卸载**:live-disable(先让 client bundle 下线,避免 404)→ `dsh plugin remove`。**不清理插件产生的数据/配置**,但把检测到的疑似残留路径(如 `~/.dsh/<plugin>.json`)列出报告。
70
+ - **升级**:**按需检查**(`dshm_outdated` / `dshm_list` 时实时比对本地版本 vs npm latest / GitHub main),半自动——展示升级计划,确认后执行。**不做后台定时器**。
71
+ - **自更新**:dsh-m 对自己同样做版本比对 + 提示升级(设置页呈现)。
72
+ - **重启**:内置**一键重启**,复用 skillhub 验证过的重启路径(本机 `dsh-web.service` 是转发 shim,不新建 systemd 单元、不监听 3080)。安装/卸载/升级完成后 GUI 弹「需重启生效 [一键重启]」横幅,工具返回重启提示。
73
+ - **安全基线(5 条)**:
74
+ 1. 所有拉取仅 HTTPS + 响应大小上限 + 超时;
75
+ 2. npm 安装校验 integrity;
76
+ 3. GitHub 安装强制 pinned SHA;
77
+ 4. pnpm 构建脚本被拦时,沿用 skillhub 的 `dangerouslyAllowAllBuilds` 重试,但**必须明确报告**「该插件需要执行构建脚本」;
78
+ 5. 不做签名/验签体系(自用,明确不做)。
79
+
80
+ ## 4. GUI(旗舰,v1 必须做好)
81
+
82
+ 3 个视图,卡片展开式详情(不做独立详情页),**中文优先**,跟随 DSH Web 深色主题:
83
+
84
+ 1. **市场页**(默认):registry 卡片流;搜索/分类为**服务端过滤**(Host 强制 `withLatest=true`、每页 50、1,000 条清单第一页只探测当前页);`MarketPanel` 是市场/已装数据唯一 owner(请求 generation + AbortController 丢弃旧响应);分页控件 + 超过 200 条性能提示 + 默认/自定义/缓存/不可用短提示(不含本地路径);卡片详情保留 README markdown 预览与 npm/GitHub 官方外链。
85
+ 2. **已装页**:profile 实际安装列表,标注来源(市场/非市场/未知);registry 不可用时仍列出已装并标记;「可升级 → x.y.z」徽标 +「升级」;「卸载」。
86
+ 3. **设置页**:registry 地址草稿 +「校验并应用」(先校验候选再写 settings,失败不落盘)/「恢复默认」/「下载默认 registry.json」(不改当前配置)/「检查条目可达性」(probe 统计 + 最多 100 条 issue,只读不改配置);同时展示配置地址、当前生效配置、生效来源与 configStatus(含 rejected/回滚原因与维护性 warnings);完整本地路径仅在此页显示。registry 配置为 **live 生效,不出现安装类重启横幅**;安装/卸载/升级仍保留重启横幅。
87
+
88
+ 技术:`src/client.js` 经 **esbuild** 打包为 `lib/client.js`;`window.__ModuleLoader__.load({ id: "dsh-m", factory })` 注册;**React 从 module loader require**(零额外运行时依赖);manifest 注入 `@deepseek-ai/dsh-client-runtime`、`@deepseek-ai/dsh-client-ui-slots`、`@deepseek-ai/dsh-client-ui-settings`。
89
+ 图标:GitHub 来源自动用 `https://github.com/<owner>.png?size=64`;`icon` 字段可覆盖;npm-only 条目首字母色块回退。
90
+
91
+ ## 5. Agent 工具(host)与 CLI
92
+
93
+ - 工具前缀 `dshm_`,共 7 个:`dshm_search` / `dshm_list` / `dshm_install` / `dshm_uninstall` / `dshm_outdated` / `dshm_upgrade` / `dshm_restart`。Agent tools 运行于 Host 进程,与 Web GUI **共用 `host` namespace 缓存与 active config**;`dshm_search` 走服务端过滤(metadata-only,`withLatest=false`、limit ≤80),返回不含本地路径的短 summary。
94
+ - CLI bin `dshm`:同名同义命令集(`dshm list|search|install|uninstall|outdated|upgrade|restart`),固定 `cli` namespace。`registry`/`search`/`outdated` 在清单不可用时打印配置/实际生效地址并 **exit 1**;`list` 仍列出已装并标记不可用;本地终端可显示完整路径。
95
+ - 本地 API:`POST /dshm` 单路由 method 分发;除 `ping` 外全部要求 JSON Content-Type + `trustedRestartRequest` host 等价同源防护;typed 错误映射 400/403/404/405/413/415/422/500;`registry-config-apply` 校验失败 422;清单不可用的 `registry`/`market` 仍返回 200 + 结构化状态。
96
+ - 实现顺序:**GUI 先行,CLI 收尾**(核心逻辑同一层,CLI 是薄封装)。
97
+
98
+ ## 6. 仓库与发版
99
+
100
+ - 单包结构;现有 `.github/workflows/publish.yml`(tag `v*` → OIDC trusted publishing)**一字不改**。
101
+ - 发版 = `npm version patch|minor|major && git push --tags`。
102
+ - npm 包 `dsh-m` 的 `files` 覆盖:`lib/`(`host.js` + `client.js` + `cli.js`)、`registry.json`(离线快照)、`cordis.patch.yml`、双语 README 与 `DESIGN.md`;`scripts/assert-pack.mjs` 对 `npm pack --dry-run --json` 做机器断言。
103
+
104
+ ## 7. 首批收录
105
+
106
+ - `dsh-skins`(github 源)、`dsh-web-search`(实现时核实其 npm/GitHub 身份后录入)。
107
+ - **skillhub 不进首批**。
108
+ - 核心 `deepseek-harness-*` 包永不收录。
109
+
110
+ ## 8. 里程碑(GUI 先行)
111
+
112
+ - **M0 脚手架**:单包结构(TS + esbuild)、manifest(`dsh.client` + `cordis.patch.yml` + `dsh.bundle.patch`)、构建脚本。
113
+ - **M1 core 层**:registry 拉取/缓存/覆盖、profile 读取、install/uninstall/upgrade/outdated 封装(spawn `dsh` CLI,非 shell 拼接,超时 + SIGTERM,保留末 256KB 输出)。
114
+ - **M2 GUI**:3 视图 + 卡片展开 + 重启横幅。
115
+ - **M3 host 工具**:7 个 `dshm_*` + cordis patch 注册。
116
+ - **M4 CLI**:`dshm` bin。
117
+ - **M5 收尾**:registry CI workflow、首批收录两条、自更新、发 `0.1.0`。
118
+
119
+ ## 9. 决策记录(三轮 19 条)
120
+
121
+ | # | 决策 | 结论 |
122
+ |---|------|------|
123
+ | Q1 | 形态 | DSH 插件主体;**GUI 旗舰(Q9 修订:比 CLI 重要,v1 必须做好)**+ 工具 + 薄 CLI |
124
+ | Q2 | 管理范围 | 只管插件,skills 归 skillhub |
125
+ | Q3 | registry 维护 | repo 内手工 curated;版本不写死 |
126
+ | Q4 | 安装来源 | npm 优先 + GitHub 兜底,schema 留 `source` |
127
+ | Q5 | 自用边界 | 单机自用,收录靠 repo/PR,无服务端 |
128
+ | Q6 | 升级提示 | v1 包含,半自动;全自动后台升级不做 |
129
+ | Q7 | 与 skillhub 关系 | 完全独立并存 |
130
+ | Q8 | 重启 | 内置一键重启,复用已验证路径 |
131
+ | Q9 | v1 边界 | ✏️ 修订:GUI 必须进 v1 且优先级最高 |
132
+ | Q10 | registry 分发 | 运行时拉 `@main` + TTL 缓存 + 包内快照兜底 |
133
+ | Q11 | schema | 精简中文 schema(icon 经 Q22 修订为可选覆盖) |
134
+ | Q12 | pin 策略 | npm 精确版本;GitHub 锁 SHA |
135
+ | Q13 | 已装识别 | 读 profile package.json,零额外状态 |
136
+ | Q14 | 卸载语义 | 删包不删数据,报告残留 |
137
+ | Q15 | 命名 | `dshm_` 前缀 / `dshm` bin |
138
+ | Q16 | 升级检查时机 | 纯按需 + 自更新检查进 v1 |
139
+ | Q17 | 安全基线 | §3 的 5 条 |
140
+ | Q18 | 仓库发版 | 单包;publish.yml 不改;tag 发版 |
141
+ | Q19 | 首批收录 | ✏️ 修订:dsh-skins + dsh-web-search(不含 skillhub) |
142
+ | Q20–24 | GUI 细节 | 3 视图 / esbuild+React / 自动头像 / CLI 薄封装 / 设置页三件事 |
143
+
144
+ ### 9.1 可自定义 Registry v1 追加决策(2026-09-04 grilling + 评审)
145
+
146
+ | # | 决策 | 结论 |
147
+ |---|------|------|
148
+ | Q25 | 覆盖模型 | 单一 registry 地址**整体覆盖**默认清单;不做运行时多源合并、不做逐条 UI 编辑器 |
149
+ | Q26 | 新增插件 | 下载默认 registry.json → 自行编辑副本 → 填地址应用;副本为独立快照不同步官方 |
150
+ | Q27 | schema | 严格 v1:未知字段/非法 ID/超限/重复拒绝整份清单;2 MiB / 1,000 条上限;>200 条提示性能 |
151
+ | Q28 | 地址类型 | HTTPS URL(外网);HTTP 仅 loopback(本机管理员信任边界,不做 DNS rebinding 防护声明);本地普通文件(fd 级安全读取) |
152
+ | Q29 | 失败语义 | 自定义源失败只回退该源自身缓存,绝不伪装成官方清单;无数据返回空清单 + 不可用状态 |
153
+ | Q30 | 缓存 | host/cli 双 namespace、CacheFile v2 原子写、candidate 不 prune、commitActiveSource 先 metadata 后 prune、失败降级 warning |
154
+ | Q31 | live | registry 地址 live 生效;仅首次部署新 Host 代码需一次用户确认的重启 |
155
+ | Q32 | 性能 | 服务端分页:GUI withLatest 最多 50/页,Agent/CLI metadata-only 最多 80;latest probe 并发 ≤8 + TTL cache + 全局 deadline |
156
+ | Q33 | 诊断 | 用户主动触发的 probe(npm/GitHub/homepage/icon),统计 + 稳定排序 + 100 条截断;不改配置不写缓存 |
157
+ | Q34 | API 防护 | 除 ping 外全部 POST 要求 JSON + host 等价同源 guard;typed 错误 400/413/415/422/500 |
158
+ | Q35 | integrity | npm 精确版本 dist integrity 对照 pnpm lockfile v9,fail closed + best-effort 快照回滚(Q12 的落地实现) |
159
+ | Q36 | namespace | Agent tools 与 Host GUI 共用 host namespace 及 active config;仅独立 CLI 用 cli namespace(Q48-A 修正) |
160
+ | Q37 | CLI | registry/search/outdated 不可用 exit 1 并输出配置/实际生效地址;agent 输出不泄露本地路径 |
161
+
162
+ ## 10. 实现参考(本地镜像)
163
+
164
+ - **skillhub 完整源码**:`~/.research-skillhub/all`(工作区内)。重点借鉴:`src/install.ts`(zip 原子写入、防穿越)、`src/plugin-market.ts`(install-plan、pinned SHA、spawn dsh CLI、dangerouslyAllowAllBuilds 重试)、`src/installed-plugins.js`(卸载前 live-disable)、`src/live-plugin.js`、`src/restart.ts`(重启路径)、`src/client.js`(GUI 模式范本)、`src/self-update.ts`。
165
+ - **dshmarketplace 源码**:`~/research-dshmarketplace/`。借鉴:registry 校验两段式(无密钥校验 + 受信写入)、HMAC 常量时间比对(若将来需要)、收录启发式(topic + 最少 commits + marker 文件,备将来自动发现)。
166
+
167
+ ## 11. 已知事实约束(本机)
168
+
169
+ - profile 根:`$DSH_HOME/profiles/web`(默认 `~/.dsh/profiles/web`);已装插件 = 其 `package.json` dependencies + `dsh.profile.bundles`。
170
+ - 新装插件需**重启 dsh web** 才加载;HMR 仅在 `pnpm run dev:web` watcher 存活时有效。
171
+ - 禁止创建监听 3080 的进程;`dsh-web.service` 为 shim,重启走其转发路径。
172
+ - 插件包协议要点:`type: module`、`main` host 入口、`exports["./client"]` 指向打包产物、`dsh.client.platform: "web"`、`dsh.bundle.patch: ./cordis.patch.yml`(`- insert: - id: dsh-m; name: dsh-m`)。