为 DeepSeek Harness 提供按 profile 分层的设置覆盖:全局 settings.yaml 仍为基线,每个 profile 可用自己的 profiles/<name>/settings.patch.yml 覆盖任意设置命名空间——对象段递归合并,数组与标量整体替换,!unset 显式屏蔽继承值。覆盖层对现有插件透明(照常读 ctx.settings),写入只落在 profile 覆盖层;官方 schema 语义、revision、expectedRevision 冲突检测、watcher 与事件均不改动。附带 settings 命令族(get/set/unset/mask/unmask/promote/demote/migrate/diff/layers),并在 Web 设置面板经 loopback RPC 通道提供 Profile Settings 区块。
安装
# npm 包(预构建)
dsh plugin --profile web add @xmoon76/dsh-profile-settings
# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)
dsh plugin --profile web add github:XMoon/dsh-profile-settings
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED 或 ERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yaml 的 allowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。
README
English | 中文
一个 DSH profile bundle 插件:在官方用户设置 seam 之上增加按 profile 隔离的
Settings 覆盖层。$DSH_HOME/settings.yaml 继续作为全局基线;每个 profile 可以
通过自己的 $DSH_HOME/profiles/<name>/settings.patch.yml 覆盖任意 Settings
namespace —— object 递归合并,array/scalar 整体替换,!unset 显式删除继承值。
Schema Defaults
↓
Composition Base
↓
~/.dsh/settings.yaml (全局用户层)
↓
~/.dsh/profiles/<name>/settings.patch.yml (profile 用户层)
↓
Effective Settings
已有插件仍然只使用 ctx.settings,完全感知不到覆盖层的存在。写操作
(update / replace / mutate)只落在 profile 覆盖层;replace({})
回退到 global 层而不是 composition 默认。官方 schema 校验、revision 语义、
expectedRevision 冲突检测、watcher 与事件全部保持不变。
安装
要求 profile 的 bundles 包含 @deepseek-ai/dsh-base(官方 web / headless
模板都满足)。为每个需要独立覆盖层的 profile 安装:
dsh plugin --profile web add @xmoon76/dsh-profile-settings
dsh plugin --profile pi-tui add @xmoon76/dsh-profile-settings
dsh plugin --profile <name> add 会自动把声明了 dsh.bundle 的包 reconcile
进该 profile 的 dsh.profile.bundles。下次启动时 bundle patch
(cordis.patch.yml)会:
- 禁用 base 的
settings行(@deepseek-ai/dsh-settings-file); - 插入
profile-settings行成为ctx.settings的唯一 owner。
一个 composition 只能有一个 ctx.settings owner:如果 base 行仍然活跃,
启动会 fail loud(Cordis 重复服务注册错误),绝不静默覆盖。
配置
插入的行不需要配置(下方为默认值);profile 自己的 cordis.patch.yml 可以按
行 id 覆盖 config:
| key | 默认值 | 含义 |
|---|---|---|
profile |
自动检测 | 显式指定 profile 名 |
globalPath |
$DSH_HOME/settings.yaml |
全局 settings 文档 |
profileFile |
settings.patch.yml |
profile 目录内的覆盖文件名 |
dshHome |
$DSH_HOME 或 ~/.dsh |
harness home |
watch |
true |
热加载两个文档 |
debounceMs |
100 |
watcher 写稳定窗口 |
writable |
true |
允许进程内写入覆盖层 |
当前 profile 按以下顺序解析(绝不通过 cwd 猜测):显式 profile 配置 →
安装位置(profiles/<name>/node_modules/…,即 dsh plugin 布局)→ launcher
--profile <name> / --profile=<name> → DSH_PROFILE 环境变量。无法确认时
启动 fail loud。
覆盖文件
# $DSH_HOME/profiles/web/settings.patch.yml
agent-default-model:
provider: pi-ai
model: gpt-5.6
permission:
mode: danger-full-access
some-plugin:
endpoint: !unset
普通 YAML 值 = override;!unset = 显式 mask(删除下层继承值,回退 schema
默认,除非覆盖层自己提供值)。mask 永不进入解析后的 JSON 文档 —— UI 不会把
它当作普通值。文件可以手工编辑,也可以通过 settings 命令修改;进程内写入
只落在这个文件,绝不写 settings.yaml。
settings 命令
命令运行时挂载后,ctx.profileSettings 与 settings 命令族可用:
settings layers [ns [path]] 每个叶子值的来源链
settings get <ns.path> 读取生效值
settings set <ns.path> <value> 写入 profile 覆盖层
settings unset <ns.path> 删除覆盖值(重新继承)
settings mask <ns.path> 写入 !unset
settings unmask <ns.path> 移除 mask
settings reset <ns> 对覆盖层执行 replace({})
settings promote <ns.path> 把值提升到全局文档
settings demote <ns.path> 把值降级到 profile 覆盖层
settings migrate <ns.path> [--copy] 把全局值迁入 profile(先备份 .bak.<时间戳>;--copy 保留全局)
settings diff [ns] 全局与覆盖层的叶子级差异
settings ui [ns] 机器可读 JSON 快照(供 Web 页面预览)
settings profile 当前 profile 与文档路径
Web UI(Profile Settings 页面)
bundle 自带浏览器半端(client/):Web 设置面板新增 Profile Settings
页面——每个字段带来源徽标(默认 / 组合 / 全局 / 本 profile / 已屏蔽)、生效值,
以及 set / unset / mask / unmask / promote / reset 操作。页面通过 host 半端注册的
/profile-settings loopback RPC 通道通信,不依赖会话上下文、不写命令日志。
配置链与设计说明
Provider 保持官方基类的 document 为 profile raw section,因此
update/replace/mutate/revision/expectedRevision/describe 的语义
完全不变;global 层被折进每个注册的合成 base
(applyMasks(merge(composition, global), masks))。对基类 TS-private 成员
只通过一个窄类型 facade 访问 —— 不 fork 任何 Settings 机制。完整 M0 调研见
docs/research.md。
Fail-loud(启动):无法解析 profile、覆盖文件根非 map、namespace section
非 object、数组内出现 !unset、不支持的 YAML tag(!!js/*、!!python/*、
自定义 tag)、覆盖路径逃逸 profile 目录、global 与覆盖文件同路径、重复
ctx.settings owner。
Warn + 保留 last good(热加载):任一文档临时无效 YAML、未注册 namespace (原样保留,等待对应插件稍后加载)。
并发边界与限制
| 范围 | 保证 |
|---|---|
同进程普通 set/replace/mutate |
官方 Settings 语义(per-namespace 串行写队列、expectedRevision 冲突检测) |
| 跨进程文件完整性(read-modify-write) | 双文档写锁 + 原子 rename |
跨进程自定义层操作(promote/demote/migrate/mask/unmask) |
真事务:固定顺序双锁、锁内 fence 校验、layer fence 随每次写入推进 |
跨进程对同一 profile、同一 namespace 的普通 update/mutate |
继承官方 seam 限制:官方 revision 是进程内写队列的,跨进程 CAS 尽力而为(文件锁下后写者胜) |
开发
npm run typecheck # tsc(src + tests)
npm test # vitest
npm run build # tsc 构建 + copy-lib(lib/)
npm pack # prepack 构建 + postpack tarball 冒烟(防泄漏检查)
需要 Node ≥ 22.6(type stripping)与 DSH harness(0.1.1-rc.2 系列)提供 peer 依赖 —— 运行时从安装环境解析,包自身不打包 harness 副本,避免 module twin。
License
MIT
链接
同类插件
Tencent/WeKnora#dsh-weknora★ 32683
把 WeKnora 知识库接入 dsh 的四个只读工具:列出知识库、混合检索原文片段、按顺序还原单篇文档,以及直接取用 WeKnora 自己带引用的 RAG 或 ReAct agent 回答(含可续聊的 session id)。
superdesigndev/treg★ 4865
给 Agent 的工具目录:按「要做的事」检索约 2,600 个外部接口(SEO 与 SERP、外链、社交、人物与公司信息补全、广告库、抓取),查看参数与单次调用价格后直接调用,凭据由服务端注入。附带技能,MCP 行在未设置 TREG_TOKEN 前保持禁用。
TencentCloudBase/CloudBase-AI-Toolkit#dsh-plugin★ 1134
把腾讯云 CloudBase 后端接入 DeepSeek Harness——在对话里搭好并部署全栈应用,查询结果渲染为表格卡片(分页、排序、导出 CSV),部署后可预览真实域名,并提供 CloudBase MCP 工具集(`mcp__cloudbase__*`),登录走 device-code 流程。
gitroomhq/postiz-agent#dsh-postiz★ 508
通过 MCP 将 DeepSeek Harness 连接到 Postiz:列出已连接的社交媒体渠道、获取各平台发帖规则,并向 X、LinkedIn、Instagram、Facebook、Threads、TikTok、YouTube、Reddit、Bluesky、Mastodon、Discord、Slack、Telegram 等平台排期、存草稿或发布帖子;附带 postiz 工作流技能。
EthanYoQ/Invoice-Downloader#dsh-invoice-downloader★ 502
面向 DeepSeek Harness 的本地 IMAP 发票下载、OCR 识别、归档与 Excel 报销汇总。
anysearch-team/anysearch-dsh★ 452
基于 AnySearch 的实时网页与垂直搜索插件,为 DeepSeek Harness 提供搜索工具。
社区评论
评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。