dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DeepSeek Harness 的技能(Gem)管理器:把可复用的指令与本地知识文档打包成模型可调用的技能,并提供设置面板里的 Dsh-Gen 页面。
给 DeepSeek Harness 用的「技能」管理器,思路类似 Gemini 的 Gems: 把一段可复用的角色设定与若干本地文档打包成一个技能,模型在对话里能直接调用它, 并能按技能正文里的绝对路径读取挂载的知识文件。
我的技能:卡片列表,每张卡显示头像、名称、说明、知识文件数与更新时间。
新建技能 / 编辑技能:整页两栏编辑器。左栏 名称 / 说明 / 指令 / 知识,右栏实时预览。
预览里的「指令」按 Markdown 渲染,标题、列表、代码块、表格都与对话里一致。
知识挂载:可以在首次保存之前就添加文件,随保存一起落盘,不必先存一次再加。
每张卡片的四个操作(图标来自 QingIcon,MIT):
| 图标 | 作用 |
|---|---|
| 对话气泡 | 在 dsh-gen 工作区新建会话,并直接绑定该技能的角色 |
| 铅笔 | 打开编辑器修改 |
| 垃圾桶 | 删除技能(连同其知识文件) |
| 眼睛 | 控制该技能是否出现在会话内的技能选择器里 |
输入框工具行上的胶囊按钮。点开后是扇形卡组(每页 5 张,可翻页),支持三种选法:
弹出层锚定在胶囊按钮的正中央,带升起动效;静置 60 秒无操作会自动收纳,
下次点击重新展开。若技能较多,底部分页条显示 ← ●● → 共 N 第 x/y 组。
选中的技能会以系统提示的形式注入会话,而不是塞进输入框,因此不会被后续提问稀释。 注入内容包含技能名称、说明、指令,以及知识文件的绝对路径清单。
dsh CLI)^22.19.0 || >=24.0.0# 1. 解压到任意目录,例如
# Windows: C:\Users\<你>\.dsh\plugins\dsh-gen
# macOS/Linux: ~/.dsh/plugins/dsh-gen
# 2. 进入目标 profile 目录
cd ~/.dsh/profiles/desktop
# 3. 用 DSH 自带的 pnpm 安装(注意用 file: 前缀指向解压目录)
node ~/.dsh/dsh-runtimes/dsh-primary-runtime/dependencies/pnpm/bin/pnpm.cjs \
add file:C:/Users/<你>/.dsh/plugins/dsh-gen --offline
把项目目录放到 ~/.dsh/profiles/desktop/node_modules/dsh-gen
(Windows 下建议用目录联接,便于改代码即时生效):
mklink /J "%USERPROFILE%\.dsh\profiles\desktop\node_modules\dsh-gen" "C:\path\to\dsh-gen"
编辑 ~/.dsh/profiles/desktop/package.json,在两处登记:
{
"dependencies": {
"dsh-gen": "file:C:/path/to/dsh-gen"
},
"dsh": {
"profile": {
"bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-gen"]
}
}
}
重启 DSH。
启动后打开 设置 → Dsh-Gen,能看到「我的技能」页面即安装成功。
插件加载痕迹写在 $DSH_HOME/dsh-gen/load-trace.log,排查问题时先看它。
插件加载时读取技能存储,为每个启用的技能调用:
ctx.skills.register({
name, // kebab-case,中文名会退化成 skill-<id 后缀>
description, // 以「技能名」开头,便于模型匹配中文标题
content, // Markdown 正文:## 指令 + ## 知识库(含绝对路径)
resourceBase: { kind: 'directory', path },
metadata,
});
模型通过 DSH 的 skill 工具按需加载这些条目。
插件注册了一个全局 system prompt 区段,其正文是一个按会话 id 查表的函数:
会话 id → 技能 id → 该技能的名称/说明/指令/知识路径
这样做的两个好处:
绑定关系存在 gens.json 的 bindings 字段里,删除技能时会一并清理。
宿主端(lib/index.js)通过 TypertRemoteService 暴露 9 个 RPC 方法:
| 方法 | 用途 |
|---|---|
listGens | 读取全部技能与工作区信息 |
saveGen | 新建或更新(同一事务里可附带知识文件与删除项) |
deleteGen | 删除技能及其知识文件 |
addKnowledge / removeKnowledge | 单独增删知识文件 |
openWorkspace | 取回 dsh-gen 工作区的 id 与路径 |
bindSession / unbindSession / sessionBinding | 会话与技能的绑定关系 |
浏览器端(lib/client.js)用 ctx.remote.$mount() 挂载这些描述符,
再通过 ctx.get('remote.dshGen') 调用。
dsh-gen/
├── package.json 插件清单:入口、导出、DSH 集成声明、peer 依赖
├── cordis.patch.yml 向 Cordis 插件树插入本插件的补丁行
├── README.md 本文档
└── lib/
├── index.js 宿主端:存储、RPC、技能注册、提示注入、工作区
└── client.js 浏览器端:设置页 UI、会话内选择器、RPC 描述符
package.json — 插件身份的来源,DSH 靠它发现插件:
{
"main": "lib/index.js", // 宿主端入口
"exports": {
"./client": "./lib/client.js" // 浏览器端入口
},
"dsh": {
"bundle": { "patch": "./cordis.patch.yml" }, // 自动注册进插件树
"client": { "platform": "web", "inject": [...] } // 让前端 bundle 被发现
}
}
dsh.bundle.patch 指向 cordis.patch.yml,DSH 启动时据此把插件插入 Cordis 树。dsh.client 声明这是一个 Web 端插件,并列出它依赖的前端服务。peerDependencies 只声明宿主会提供的包(cordis、typert-protocol、react),
因此插件本身不需要打包 node_modules。cordis.patch.yml — 三行插件树补丁:
- insert:
- id: dsh-gen
name: dsh-gen
lib/index.js(宿主端) — 模块顶层 export const name = 'dsh-gen',apply(ctx) 里完成:
$DSH_HOME/dsh-gen 目录并注册为工作区(标题 dsh-gen)gens.json(按 mtime:size 检测外部改动并自动重读)TypertRemoteService 暴露 RPCload-trace.log 记录加载过程lib/client.js(浏览器端) — 用 window.__ModuleLoader__.load({ id, factory }) 包装,
在 apply(ctx) 里挂两个 UI 槽位:
| 槽位 | 组件 | 说明 |
|---|---|---|
settings.section | DshGenSection | 设置页的 Dsh-Gen 选项卡 |
conversation.input.left | SkillPicker | 输入框工具行的技能选择器 |
同时用 ctx.remote.$mount() 挂载与宿主端对应的 RPC 描述符。
关联链路(一次「保存技能」的完整路径):
设置页点保存
→ client.js 调 api.saveGen({...})
→ RPC 序列化,走 connection 到宿主
→ index.js 的 DshGenService.saveGen
→ runtime.save():写 gens.json + 拷贝知识文件 + syncSkills()
→ 返回最新 view
→ client.js 用返回值刷新界面
所有用户数据都在 $DSH_HOME/dsh-gen/(DSH_HOME 默认是 ~/.dsh):
$DSH_HOME/dsh-gen/
├── gens.json 技能与绑定关系
├── knowledge/<技能 id>/... 挂载的知识文件副本
└── load-trace.log 加载痕迹(有 256KB 上限)
gens.json 结构:
{
"version": 1,
"gens": [
{
"id": "gen_xxx",
"name": "技能名",
"description": "说明",
"instructions": "指令(Markdown)",
"disableKnowledge": false, // 停用知识引用
"enabled": true, // 是否注册为 runtime skill
"pickerHidden": false, // 是否在会话内选择器中隐藏
"knowledge": [
{ "id": "k_xxx", "name": "文档名", "fileName": "磁盘文件名",
"path": "绝对路径", "bytes": 1234, "addedAt": "ISO 时间" }
],
"createdAt": "ISO 时间",
"updatedAt": "ISO 时间"
}
],
"bindings": {
"session-xxxx": "gen_xxx" // 会话 → 技能
}
}
注意:
gens.json是用户数据,不在本项目仓库里,也不应该提交到 Git。 换机器时备份整个$DSH_HOME/dsh-gen/目录即可。
推荐用目录联接(Windows)或符号链接(macOS/Linux)把项目挂进 profile, 这样改完代码刷新窗口即可看到效果,不必反复安装:
# macOS / Linux
ln -s /path/to/dsh-gen ~/.dsh/profiles/desktop/node_modules/dsh-gen
:: Windows
mklink /J "%USERPROFILE%\.dsh\profiles\desktop\node_modules\dsh-gen" "C:\path\to\dsh-gen"
lib/index.js(宿主端):DSH 的 HMR 会重新加载插件行。lib/client.js(浏览器端):需要刷新页面。node --check lib/index.js
node --check lib/client.js
# 只打包 npm files 白名单里的文件
npm pack
stroke: currentColor 以继承按钮状态色。装完设置页看不到 Dsh-Gen 选项卡?
先看 $DSH_HOME/dsh-gen/load-trace.log 有没有 apply() entered。
没有说明宿主端没加载,通常是 profile 的 bundles 里没加 dsh-gen,或 node_modules 里没有这个包。
技能注册名为什么是 skill-xxxxxx 而不是我填的中文名?
DSH 的 skill 名必须是 kebab-case ASCII(^[a-z0-9]+(-[a-z0-9]+)*$)。
中文名无法直接转成合法名字,因此退回 skill-<id 后缀>。
展示名始终是你填的名字,技能描述里也以「你的名字」开头,方便模型匹配。
知识文件放在哪?原文件删了会怎样?
选中时会把文件复制到 $DSH_HOME/dsh-gen/knowledge/<技能 id>/。
原文件之后移动、改名、删除都不影响已挂载的副本。删除技能时副本一起删除。
给会话切换技能要重新开对话吗?
不需要。角色注入是每次组装系统提示时按会话 id 实时查表,切换后下一轮就生效。
会话内选择器里的技能不见了?
可能被卡片上的眼睛图标关掉了(卡片元信息会显示「会话内已隐藏」)。 到设置页用眼睛图标重新打开即可。
图标来自 QingIcon(asorn/qingicon-open,MIT), 仅以内联 SVG path 形式使用,不引入运行时依赖。