dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
在 DeepSeek Harness 运行时注册与管理 Agent 预设的插件。它不修改任何 DSH 配置文件:预设通过 ctx.agentPresets.register() 动态注册,数据只保存在插件自己的目录里。
@rvaim/dsh-preset-manager0.5.0$DSH_HOME/dsh-preset-manager/启用插件后提供三个只读预设,可在预设管理器中查看、复制配置或派生为可编辑的自定义预设。
ID · 中文名称 · English name · 描述 standard-efficient · 标准增强 · Standard Efficient · 基于 DSH Standard 的自适应高效编码 Agent codex-original · Codex 原版 · Codex Original · 使用 OpenAI Codex 官方公开基础提示词原文的实验预设 redteam-mode · 红队模式 · Red Team Mode · 红队对抗演练模式 —— 意图归一化 + 25 条域规则路由 + 确定性输出槽 + 破甲自检
标准增强和 Codex 原版在插件每次启用时,通过宿主公开的 agentPresets.readDocument('standard') 读取当前 Standard 的完整实际插件组合,并保留注册表中的排序元数据。除上述身份信息及 Persona 的 prefix 外,插件树、工具、嵌套分组、条件表达式、隔离配置、Persona 的 suffix/complete/includeRuntimeContext 和未知字段均保持原样。启用后不会持续改写已注册定义;重新启用时才重新读取 Standard。已有同名用户预设或第三方预设会保留,并报告冲突。
自带预设不写入用户的 presets/ 或修订历史;清除用户数据不会删除它们,停用插件会撤销本插件持有的全部自带预设注册。Standard 缺失、异常或其 Persona 结构无法安全识别时,会显示诊断,不回退到一份猜测的精简配置。管理器按当前语言显示中英文名称;宿主注册表使用上述中文名称。
order: 10、22 个根条目及三个嵌套分组中的完整插件配置;直接读取本地定义,不从 Standard 合并或改写。{{model}} 模板变量。970111f6cb62cc02fe36a82ea7d71b37a2889c97,SHA-256 为 ac8ae107a0d72fe3476b430afb161ea4e67da2e446d778aefc44828160559807。原始文件共 20,903 字节,未经总结、改写、翻译、工具名替换或添加前后文。{{ 模板标记,不会被 DSH Persona 意外插值;以后更换上游原文时若出现该标记,将拒绝注册而不改写原文。Codex 原版只替换 Persona Prefix,仍使用 DSH Standard 的工具、运行环境和其他系统提示段。它用于比较官方公开提示词的效果,不等同于复现完整 Codex CLI 的行为。
npm run verify 包含 verify:bundled:实际生成 npm tarball、解包,比较原文字节与 SHA-256,并从解包后的 Host 模块验证断网时的本地资源读取。
预设以一行一项的横向列表展示:名称与 ID、完整用途说明、操作和启停开关保持对齐。
自动化可通过 preset_manager_set_enabled 使用相同的启停服务,参数为预设 id 和布尔值 enabled。
DSH 的 Agent 预设原本只能在 Cordis 组合(cordis.patch.yml 或 Bundle Patch)里声明。想调整 Persona、复制一份内置预设、试用一个别人分享的配置,都得改 DSH 的配置文件再重启。
本插件换了一条路径:DSH 的 AgentPresetRegistry 服务提供了一个公开的运行时注册接口,插件在启动时读取自己的预设文件,逐个注册进 Registry,并在停用时逐个注销。DSH 的配置文件从头到尾没有变化。
以下项目都在本机真实的 DSH V0.2.1-rc.2 运行时上验证过,不是推断。
能力 · 验证方式 · 结果 第三方 Host 插件访问 AgentPresetRegistry · 真实 DSH 进程内 inject: ['agentPresets'] · ✅ 程序化注册预设 · register() 返回 disposer,list() 由 5 项变 6 项 · ✅ 读取真实激活健康状态 · compositionInventory() 显示 fiberState: 2(ACTIVE)、无 broken · ✅ 停用时注销 · 停用后再启用,list-before 中已无该预设 · ✅ 不修改 DSH 配置文件 · cordis.patch.yml 哈希前后一致(6d5d0e8e…),语义解析无任何预设内容 · ✅ 设置页出现在官方设置中 · Slot 检查器显示 settings.section 占用项 dsh-preset-manager,order: 25,active: true · ✅ 复制内置预设 · 通过真实服务复制,组合逐字节一致 · ✅ 保存并注册 · 编辑 Persona 后保存,Registry 中定义已更新 · ✅ 修订号冲突检测 · 用过期 revision 保存被拒绝 · ✅ 导出 → 导入 · 分享文本往返,语义一致 · ✅ 删除并注销 · 删除后 Registry 中消失 · ✅ 停用注销全部自身注册 · teardown 后托管预设消失,内置预设保留 · ✅ 重启自动恢复 · 第二个进程从同一目录恢复,且只注册一次 · ✅ 停用不删数据 · 停用后 presets/、history/、metadata.json 全部保留 · ✅
自动化测试:210 个单元/集成测试全部通过,覆盖动态注册生命周期、持久化、领域规则、导入导出、非侵入性、注册管理器与插件自带预设;另有 17 项真实 Gateway/插件注入检查和 57 项完整 Client/Host 入口检查。
这些功能代码完整、类型检查与构建通过,但无法在无人值守环境下点击验证:
未经人工点击确认的部分,我不宣称已通过视觉验收。
YAML 区统一使用 CodeMirror,提供语法高亮、行号、折叠和缩进引导线; 左侧插件树使用对齐的图标/名称/包名列,并显示父子分支线。 编辑窗口缩为有尺寸上限的居中弹窗,前置/后置提示词改为 Tab 切换。 派生移到查看弹窗;删除、放弃未保存修改和保存确认使用独立确认弹窗, 编辑器底栏的删除、取消、保存始终保持位置和含义不变。
预设由 id/name/description/order 元数据和 plugins 组合树构成,不等于单一前缀。
设计依据及语义边界见 DSH 预设编辑模型。
「复制」只创建副本并刷新网格,不自动打开编辑器;需要修改时由用户点击「编辑」。 「导出」直接将配置复制到剪贴板,不打开窗口;剪贴板拒绝访问时明确报告失败。 编辑弹窗使用接近整个视口的宽高,压缩名称和描述区域,将主要空间用于提示词。 移除「替换内置提示词」开关;直接编辑原文并保存,同时保留源配置原有的组合模式。 提示词和配置 YAML 来回切换保持同一草稿,无法解析的 YAML 保留原文。
双列网格作为主界面始终保留,只在极窄内容区折成单列。 卡片「查看」直接打开只读弹窗;自定义卡片「编辑」直接打开编辑弹窗,无须先查看。 「复制并编辑」直接进入新副本的编辑弹窗。保存、取消和关闭都回到原网格位置。 弹窗有固定标题/操作栏与独立滚动,支持 Escape、焦点归还和未保存修改保护。
列表采用与 DSH 原生 Agent 预设页相近的分组卡片:宽内容区两列、窄内容区一列, 按内置、自定义和第三方区分。用途说明完整展示;内置预设缺少注册元数据时, 使用与宿主相同的中英文短说明,自定义内容始终优先。
点击卡片的「复制并编辑」直接复制该项,自动生成名称和不冲突的 ID,随后进入新副本编辑。 不再弹出来源选择和 ID 表单,也不会默认复制列表第一项。复制保留完整插件组合和用途说明。 主按钮的背景与文字都使用随宿主主题变化的成对令牌,移除了固定白字与亮度滤镜。
remote 与 remote.presetManager 是两个独立的 Cordis Service。 0.1.4 虽已完成命名空间挂载,却从只声明 remote 的插件上下文读取另一个服务, 因此出现 cannot get property "remote.presetManager" without inject。 现在由外层负责挂载,再由显式声明该命名空间的子 fiber 消费服务;服务撤销会清除页面 API, 晚到的列表响应不会恢复旧绑定,网关的对象型错误详情也能正确显示。
npm run verify:client 加载实际打包的 Client 与 Host 入口,使用真实 Cordis 注入与 Gateway, 在隔离数据目录中检查列表就绪、复制、打开编辑器、删除、断连恢复、停用再启用及取消。 原来的 root Context 通信验证保留,但不再作为插件入口可用的唯一证据。 当前浏览器还可通过可选的 @rvaim/dsh-preset-manager Client Inspect provider 查询 版本、挂载状态、列表状态、预设数量和实际挂载页面数量;该查询只读。
0.1.3 的远程方法 presetManager/remove 与当前 DSH 的 RemoteNamespaceService.remove() 重名。宿主在挂载前拒绝整组 descriptor, 因此即使 Host 插件已启用,页面仍无法读取列表,并显示 method "presetManager/remove" conflicts with its namespace service。
删除端点现为 deletePreset,通过协议的 implementation: 'remove' 映射到 原有 Host 删除逻辑;页面的业务 API 保持不变。
npm run verify:binding 现在默认验证本次构建的 dist,使用与目标 DSH 一致的真实 Cordis、Gateway 和 Typert Registry,验证旧端点确实被拒绝、 完整接口挂载、列表读取、删除 RPC、参数传递、YAML 返回值、非法输入和卸载后重挂载。 也可以传入明确的产物目录,例如 node scripts/verify-binding.mjs /path/to/package/dist。 这些自动化检查不等同于真实页面的视觉验收。升级已安装包后,若安装器提示需要重启, 请完整退出并重开 DSH 后再进入「设置 → 预设管理」。
上一版在真实页面上有两个缺陷,均已修复:
根因是 gateway 的绑定校验要求 typertRemote.service === 被提供的对象本身。 原实现把 face 展开成第二个对象,service 却指向第一个,身份校验失败, 每次调用都得到 gateway/binding-invalid。现在绑定直接挂在被提供的对象上, 并有回归测试锁定这一约束(scripts/verify-binding.mjs、src/tests/unit/remote-binding.test.ts)。
根因是 DSH 的 locale 服务用扁平的 dict[key] 查表,不会遍历嵌套对象。 原字典是嵌套树,只有顶层键能解析,带点的键全部回退成键名本身。 现在注册前会扁平化,并有测试断言中英文键集合一致、占位符齐全 (src/tests/unit/locale.test.ts)。
DSH 把「默认预设」存在自己的 settings 里(agent-preset-registry.config.selectedDefault)。本插件不写它。若用户把自定义预设设为默认后又删除它,DSH 仍指向那个 ID。在「绝对禁止修改 DSH 设置」的前提下,这无法自动修复——插件会在删除前明确警告,并提示用户在 DSH 设置里改回内置预设。
DSH 的卸载流程没有提供能可靠区分「卸载」与「热重载 / 临时停用」的扩展点。因此卸载后 $DSH_HOME/dsh-preset-manager/ 会保留(不再被读取,但占用磁盘)。这是刻意的选择:宁可留下无效目录,也不冒一次 HMR 就删光用户预设的风险。清理方式见 §8。
会话日志里记录的是预设 ID。插件停用后该 ID 不再存在,恢复能力取决于 DSH 自身的处理,本插件不修改会话日志。
DSH 没有公开「预设列表已变化」的通知接口。选择器在组件挂载时重新读取(useEffect → load()),因此新预设会在下次打开新建会话或重新挂载选择器时出现,而不是即时推送。本插件不使用 DOM 注入或猴子补丁去强制刷新。
轻量 Clean Architecture + Ports & Adapters。
DSH Web Settings
│
Client UI (React)
│
Client State (store)
│
Remote API (typert)
│
Application Service
╱ │ ╲
Use Cases Ports Sharing
│
Infrastructure
╱ ╲
FilePresetRepository DynamicPresetRegistryAdapter
│ │
Plugin-owned files AgentPresetRegistrydsh-preset-manager/
├── package.json # Bundle 清单 + dsh.client 声明
├── cordis.patch.yml # 只加载本插件一行,不含任何预设
├── icon.svg # 显示图标
├── locale/{zh,en}.json # 显示元数据 + 页面文案
├── tsconfig*.json
├── src/
│ ├── shared/ # 跨 Host/Client 的线类型与错误码
│ │ ├── types.ts
│ │ ├── contracts.ts
│ │ └── errors.ts
│ ├── domain/ # 纯逻辑,无 IO
│ │ ├── preset-model.ts # YAML 方言、canonical、展平
│ │ ├── preset-revision.ts # 内容哈希(Host-only,用 node:crypto)
│ │ ├── preset-validator.ts # 结构与 ID 校验
│ │ ├── clone-preset.ts # 完整复制
│ │ ├── persona-editor.ts # Persona 投影读写
│ │ └── preset-diff.ts # 语义差异
│ ├── sharing/ # 版本化分享格式
│ │ ├── share-format.ts
│ │ ├── preset-exporter.ts
│ │ ├── preset-importer.ts
│ │ ├── compatibility-checker.ts
│ │ └── sensitive-data-checker.ts
│ ├── host/
│ │ ├── index.ts # 组合根:注入、路径、注册、生命周期
│ │ ├── application/
│ │ │ ├── preset-manager-service.ts
│ │ │ ├── deps.ts
│ │ │ └── use-cases/*.ts
│ │ ├── ports/ # 三个端口接口
│ │ ├── adapters/
│ │ │ ├── file-preset-repository.ts
│ │ │ ├── dynamic-preset-registry.ts
│ │ │ ├── dsh-preset-source.ts
│ │ │ └── cordis-context.ts
│ │ ├── lifecycle/
│ │ │ ├── registration-manager.ts
│ │ │ └── startup-restore.ts
│ │ └── remote/
│ │ ├── descriptors.ts # Host 与 Client 共用的端点声明
│ │ └── preset-manager-remote.ts
│ ├── client/
│ │ ├── index.ts # 注册设置页与 Remote 命名空间
│ │ ├── api/remote-api.ts
│ │ ├── state/store.ts
│ │ ├── pages/PresetManagerPage.tsx
│ │ ├── components/primitives.ts
│ │ ├── features/… # list / clone / persona-editor /
│ │ │ # history / export / import
│ │ └── locales/
│ └── tests/unit/ # 20 个测试文件 + 假 Registry
└── scripts/
├── build.mjs # Host: tsc → ESM
├── run-tests.mjs # 编译并运行测试
├── e2e-verify.mjs # 完整生命周期端到端
├── e2e-restart.mjs # 重启恢复
├── e2e-installed.mjs # 针对已安装产物验证
└── asar-extract.mjs # 读取 app.asar 内源码的辅助工具模块职责不混淆: domain/ 与 sharing/ 无 IO;host/ports/ 只有接口;host/adapters/ 才碰文件与 Registry;application/ 只做编排;client/ 不直接调用文件系统或 Registry。
全部位于 $DSH_HOME/dsh-preset-manager/(通过 DSH 官方 dshHomePath() 解析,不硬编码用户目录)。
$DSH_HOME/dsh-preset-manager/
├── presets/
│ └── <id>.yml # 当前有效定义,唯一权威来源
├── history/
│ └── <id>/
│ └── <sha256>.yml # 被替换/删除的历史版本
├── backups/
│ ├── quarantine/ # 解析失败的文件(原样保留,供手工修复)
│ └── purge-<时间>.json # 「清除全部数据」前的快照
└── metadata.json # 仅索引:revision / savedAt / reason约定:
presets/ 是唯一权威。历史版本永远不会自动覆盖当前配置。metadata.json 只存索引,不存第二份预设定义(测试断言了这一点)。!!js 表达式以 { __jsExpr: "…" } 形式保留,解析期永不求值。fsync → rename 覆盖。中断不会留下半个预设。backups/quarantine/ 而不是删除,其余预设不受影响。id: my-preset
name: 我的预设
plugins:
- id: persona
name: '@deepseek-ai/dsh-persona'
config:
prefix: |
You are a coding agent.
complete: false@deepseek-ai/dsh-agent-preset-registry 的实现有三条性质,决定了整个设计:
Duplicate agent preset: )。所以「替换」只能是「先注销,再注册」,没有 upsert。broken 字段上。因此不能把「没抛异常」当成成功——每次注册后都回读 list() 判断健康。读 presets/ → 结构校验 → 冲突检查 → register() → 回读 list() 判定健康
├─ healthy → 写入当前版本(已是新内容)
└─ broken → 补偿回滚,不落盘