dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
在一个设置分区里新建与配置 Agent 预设——人格提示词、勾选后注入系统提示的知识库条目清单、四个知识库工具的独立开关、按 Agent 收窄 MCP 连接,以及知识库批量导入。
多 Agent 管理与配置插件(DeepSeek Harness)。与 dsh-eli-mode 并存,不改动它的任何文件。
dsh plugin --profile web add dsh-agent-manager
装完重启 dsh web,设置里会出现 Agent 管理 分区。
dsh 0.1.0-rc.7(见 package.json 的 dsh.engines.dsh);Node ^22.19.0 || ^24.0.0。dsh-eli-mode 的 eliKb 服务。
没装 eli-mode 时它们显示为禁用态,其余配置照常可用。lib/client.js 已提交),安装时不需要构建。kb_read 按需取)、四个知识库工具的独立开关、可用哪些 MCP 连接。- <条目路径> — <标题>(与 eli-mode 生成的目录页同格式):标题让 Agent 光看清单就知道该读哪一篇,中文文件名下标题自然就是中文。| 平面 | 文件 | 挂载方式 |
|---|---|---|
| host | lib/index.js | 本插件 cordis.patch.yml 的 insert |
| preset | lib/preset.js | 每个生成的 Agent 预设在自己的 agent.cordis.yml 里按 id 挂载 |
| 浏览器 | lib/client.js | package.json 的 dsh.client |
preset 平面由预设自己挂载,是为了让它天然绑定到「自己服务哪个 Agent」——挂载行里带 config.presetId。
~/.dsh/settings.yaml 的 agent-manager 段,形状 agents: { <presetId>: { ... } }。由插件注册的设置命名空间读写,不写进预设的组合文本。.agent-manager.json,记录 managedBy / blueprintId / createdAt。删除只允许删带这个标记、且位于 ~/.dsh/.agent-presets/ 之下的目录,避免误删 eli-mode、wechat 等既有预设。复用 dsh-eli-mode 的 eliKb 服务读取 ~/.dsh/eli-knowledge(同一份 wiki),不重复实现一套。因此:dsh-eli-mode 被停用或升级导致 eliKb 不可用时,知识库清单会显示禁用态,其余配置不受影响。
「Agent 管理」头部的「导入知识库」:填源目录(绝对路径)→ 扫描 出预览(+ 新增 / = 已存在、并列出被跳过的文件及原因)→ 导入 N 项。
C:\...\docs 导成 tripartite/...。.md / .markdown;跳过 index.md(各级目录页)、隐藏目录、node_modules 等;默认跳过已存在的同名条目(可勾选覆盖)。# 标题 时,导入时会在最前面补一行 # <文件名>(原内容一行不删)。原因见下。eliKb.regenerateAllIndexes()。kb.js 的 _readRaw 是这么取正文的:
let title = norm.split('/').pop()
if (lines[0].startsWith('# ')) title = lines[0].slice(2).trim() // 标题
const body = lines.slice(1).join('\n') // ← 首行被丢掉
return { id, title, content: '# ' + title + '\n\n' + body }
首行永远被丢弃:是 # 标题 时"丢得正好"(标题另外存了),不是时那一行正文就永久读不到 —— Agents 读不到、知识库网页看不到、search 也搜不到(search 基于同一份裁过的 content)。上游自己因此约定「首行即标题」(write() 强制写 # title,自带种子 wiki 的文件首行全是 # 标题)。
已有条目如果踩了这个坑(例如手拷进 wiki/ 的文件),用脚本就地补,不必删了重导(在包目录里执行):
node scripts/fix-title-lines.mjs # 预览(不写文件)
node scripts/fix-title-lines.mjs --apply # 执行:先备份,再补行,最后重建索引
# 个别条目的标题取自自身小标题(如 `# 1. 概述`)时,可用文件名重写:
node scripts/fix-title-lines.mjs --apply --force=6
备份落在 ~/.dsh/eli-knowledge/.titleline-backup-<时间戳>/(在 wiki/ 之外,不会被当成条目)。
会话清单读的是知识库根索引 ~/.dsh/eli-knowledge/index.md,而它只在 eli-kb 自己 write() / remove() / init()(且根索引不存在)时重建,知识库没有任何路由或按钮能手动触发。所以直接往 wiki/ 拷文件会出现「文件在、list() 看得见、Agent 却完全不知道」的哑谜。本插件为此提供两处入口:
# 面板内:导入知识库 → 仅重建索引
# 命令行(DSH 没跑起来时,在包目录里执行):
node scripts/reindex-kb.mjs
# 调 prompt 模板/清单格式时,先看看 Agent 到底会收到什么:
node scripts/preview-injection.mjs [presetId]
三个脚本都用 scripts/eli-kb.mjs 定位 dsh-eli-mode 的 lib/kb.js:先看环境变量 ELI_KB_MODULE,再按 Node 解析规则从当前目录找 dsh-eli-mode,最后试仓库内 vendored 快照。找不到时会直接告诉你该装什么或该设哪个环境变量。
中文 / 下划线 / 空格命名的条目能否被读取,取决于 dsh-eli-mode 的 lib/kb.js 里 normalizeId 的字符集限制:
[a-z0-9-] 段名 → 这类条目「能列出但读不到」(list()/tree()/search() 不走校验,read()/write()/remove()/双链解析才走),表现为知识库网页点不开、kb_read 返回空、kb_write 还会把中文标题 slugify 成 entry-xxxx。. / ..」并拒绝 NUL:字符集任意,仍拒绝路径穿越,id 永远落在 wiki/ 之内。补丁的判定只影响读取。要放中文条目,请让 eli-mode 使用打了补丁的版本(差异就是 kb.js 里 normalizeId 那一个函数)。
mcp__<serverName>__<toolName>,所以「装了哪些、有哪些工具」以 ctx.tools.schemas() 为准。实测连接器自己的 status 接口报 0 个连接时,工具表里仍有 1 个真实连接(5 个工具)——按 status 做会误判成「没有 MCP」。createScope(selfCtx, { agentPreset: <id> })),因此在 preset 平面调用官方 ctx.tools.restrict({ deny }),未勾选连接的工具会同时从 schema、lookup 与 dispatch 中消失。这与 MCP 连接器收窄工作区用的是同一套机制。mcpRestrict 默认 false = 沿用「全部已连接的 MCP 都能用」,避免升级后静默砍功能。开启后才按 mcpServers 精确放行(勾选为空 = 全禁)。tools/registered,连接增删后按新工具表重新计算(否则重连之后仍被旧限制挡住)。重算前会先撤销自己的限制再读工具表,避免「读到被自己遮蔽后的子集、越算越少」。restrict() 若抛错(例如名字刚好失效、或该 scope 不允许限制),只记一条 warn 并保持不限制——不会让整个预设挂载失败。settings.section 这类通用插槽)。选择器只用本地名后缀匹配([class*="_card"] 等),官方卡片结构变化时按钮会自动不注入并记一条 warn——独立分区仍然可用,功能不丢。execute 里按开关拒绝并返回说明。代价是禁用中的工具仍会出现在工具清单里。~/.dsh/.agent-presets/<id>/,绕过了官方 AgentPresets 服务「只允许整目录复制」的授权限制;因此改写只做按行文本修改 + YAML 校验 + 失败回滚,尽量保留蓝本里的注释。eli-kb 自己的 remove() 删条目时,若该目录已生成了 index.md(导入必然生成),空目录不会被自底向上清掉,会留一个只有 index.md 的空壳目录;条目列表不受影响(index.md 不算条目),需要时手动删目录。npm install # 需要 devDependencies(tsdown / typescript / @types/*)
npm run build # 产出 lib/client.js(已随仓库提交,运行时无需构建)
npm run typecheck
npm test # node --test tests/*.test.mjs(不依赖 dsh-eli-mode)
host 半与 preset 半是免构建的 lib/*.js;只有浏览器半需要 tsdown 打包,再由 scripts/wrap-client.ts
包成 DSH 的 window.__ModuleLoader__.load 形状。npm publish 前 prepack 会自动跑一次构建。
需要真实 eliKb 实例的那部分测试(含中文 id 补丁与索引重建的验证)在开发用的单体仓库里维护,不随包发布。
插件市场里的列表来自精选列表仓库 awesome-dsh-plugin, 收录方式是往那边提 PR 新增一个条目文件(不是往 dsh-market 提):
contrib/let-mi-think__agent-manager.yml 复制成 data/plugins/let-mi-think__agent-manager.yml(文件名必须与仓库一致);dsh.bundle 且根目录有 cordis.patch.yml(两个都已具备);dsh-plugin topic,并确保仓库创建已满 1 天(CI 会检查);npm 包不是收录的必要条件,但发了才能显示下载量:npm publish 后映射会被自动采集
(条目里不要写 npm: 键,写了会被校验拒绝)。本仓库的 repository 已指向自己,
prepack 会在发布前自动跑一次构建,lib/ 也已随仓库提交,因此从 npm 安装无需构建。
可选增强:在仓库根放 screenshots.json(1–8 张图,路径相对该文件、不能跳出仓库)
可以让市场显示应用商店式截图;不放也行,市场会从 README 自动抽取。
MIT