每次 edit 与 bash 调用前都会询问:编辑前展示红绿行级 diff,命令前展示描述与命令面板——同意一次或拒绝,两个 gate 各带独立总开关(Settings → General)。
安装
# npm 包(预构建)
dsh plugin --profile web add dsh-edit-approval
# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)
dsh plugin --profile web add github:SiriLee/dsh-edit-approval
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。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
为 DeepSeek Harness 提供先询问后执行的审批:每次 write / edit 调用都在文件真正落盘前先询问——弹出红绿行级 diff,同意一次 / 拒绝——每次 bash 命令执行前也先询问,两者各自的总开关位于侧栏「插件」面板里本插件的页面上。
English | 中文
刻意保持聚焦,只做一件事:两扇镜像对称的审批门——编辑与命令——让 agent 未经你同意就无法改动文件或执行命令。
| 审批门 | 拦截目标 | 默认 | 面板 |
|---|---|---|---|
| 编辑审批 | write / edit(str_replace_editor 需自行选入) |
开 | 红绿行级 diff——同意一次 / 拒绝 |
| 命令审批 | bash |
关 | 描述 headline + 原生命令行 |
两扇门共用 harness 自带的 serviceAsk seam:插件在 tools/pre-execute 返回 { kind: 'ask', reason },harness 将其路由进 Web 审批面板——host 端零 UI 改动——allowed-once 继续执行、rejected 拒绝调用;在 never 策略下插件直接委托,全权会话照常工作。
效果预览
所有写类调用先弹出红绿 diff 面板;开启命令审批后,每条命令先弹出面板:白色 headline 是 agent 的描述,灰色行是命令原文。两个总开关位于侧栏「插件」面板里本插件的页面上,走 harness 自带的表单暂存(截图为 v0.4.0)。
安装
dsh plugin --profile web add dsh-edit-approval
装完重启 dsh web(--profile web)生效。
给贡献者:可从本地 checkout、pin 的 commit 或离线 tarball 安装——dsh plugin --profile web add /path/to/dsh-edit-approval、dsh plugin --profile web add github:SiriLee/dsh-edit-approval#<sha> 或 npm pack 后 dsh plugin --profile web add ./dsh-edit-approval-<version>.tgz。git 安装首次会失败:pnpm 默认禁止 git 依赖执行构建脚本,需先在 profile 的 pnpm-workspace.yaml 加 allowBuilds;之后 pnpm 会执行插件的 prepare 并装入 profile。npm pack 同样运行 prepare,tarball 内始终包含预构建 lib/(含 .d.ts)与 LICENSE。
使用
- 编辑审批默认开启。 任何
write/edit调用都在文件被触碰前先询问。面板只显示改动行——删除红色、新增绿色——带右对齐NN|行号 gutter,被跳过的上下文与 hunk 间隔以…省略号标记。 - 同意一次 / 拒绝。
allowed-once放行该调用;rejected拒绝并反馈给模型。 - 命令审批默认关闭——在本插件页面上或通过下面的命令开启。面板白色 headline 是描述(如
bash · push to remote);下方灰色行是命令原文,由 harness 原生渲染。 - 命令行入口:
/approval-edit on|off|status与/approval-bash on|off|status——与该面板写同一份文档,两者不可能不一致。 - 白名单(仅配置文件):
bashAllow存放始终放行的命令前缀。匹配做了空白归一化(git push命中git push),无法用多余空格绕过。暂不提供 UI。
原理
插件监听 tools/pre-execute 瀑布(harness 在工具执行前的 seam),按工具名分派到两扇门之一——名称重叠时编辑门优先。
1. 编辑审批
对每个被拦截的写类调用:
- 解析目标文件:经
ctx.fs解析路径,使用 fs 工具所写的会话 cwd 原文(DSH 0.1.7 起 fs 工具不再对含..的 cwd 做规范化,本预览同样不做)。 - 读取当前内容,并按各工具自身的语义从参数重建"拟写入内容":
write——全文;edit——单次唯一替换(或replace_all);str_replace_editor——str_replace唯一替换、insert按行插入、create取file_text。 - 计算行级 diff:用 jsdiff 的
structuredPatch(Myers),与 harness 写类结果卡片同一个参考实现、同一个 3 行上下文窗口——因此审批预览与批准后的结果卡片同源,大文件里改一行就只显示一行。 - 返回
{ kind: 'ask', reason }:首行是摘要(tool · file (op): N insertions, M deletions),其后是 diff 文本。harness 的serviceAsk将其经ctx.approval路由进 Web 审批面板。
2. 命令审批
纯判定,完全不碰 fs——既不读也不写:
- 以下情况直接放行:门已关闭、工具不在
bashTools中、命令为空、或该调用是沙箱提权(带sandbox_permissions+justification,它们自带审批,不能重复询问)。 - 白名单优先:空白归一化后的前缀命中即放行。
- 否则返回
{ kind: 'ask', reason },只有一行 headline——bash · <描述>(描述为空时就是bash)。命令原文不写进 reason:harness 会在面板的命令行里原生渲染,避免重复。
3. 共享策略处理
会话审批策略(ask / never)始终生效。在 never 下(如 danger-full-access),本插件发出的每个 ask 都会被审批服务确定性拒绝,从而静默打断全权会话里的所有编辑与命令——所以两扇门都改为经 next() 委托,交由沙箱执行。插件绝不扩大权限,也不改动沙箱模式。
4. 审批面板
浏览器半边(dsh.client)在面板出现时增强它(按动画帧合并的 MutationObserver,所有副作用都在单个 ctx.effect 内,卸载 / HMR 时拆除):
- 编辑面板:从纯文本 headline 重建为只有改动行——删除红色、新增绿色、右对齐
NN|gutter——并附white-space: pre-wrap补偿,多行 diff 再加一个折叠按钮。 - 命令面板:保持 harness 原生外观,只加一个
dsh-ea-kind-command标记,不重排样式、不重建——白色描述 headline 与灰色命令行就是 harness 渲染的样子。
配置
本插件的全部配置就是一个 profile 条目——bundle patch 插入的 dsh-edit-approval 行——分层为 schema 默认值 < 行配置 < 持久化用户层。patch 刻意不带 config:src/index.ts 里的 schema 默认值是唯一真源,profile patch 只需重述它要改的键:
# profile 的 cordis.patch.yml
- id: dsh-edit-approval
name: dsh-edit-approval
config:
editMinDiffLines: 2
editIncludeCreate: false
bashEnabled: true
| 键 | 默认 | 位置 | 说明 |
|---|---|---|---|
editEnabled |
true |
「插件」面板 | 编辑审批总开关 |
bashEnabled |
false |
「插件」面板 | 命令审批总开关 |
editTools |
['write','edit'] |
profile 文件 | 被拦截的写类工具名 |
editMinDiffLines |
0 |
profile 文件 | 改动达到至少这么多行才询问;更小的改动静默放行 |
editIncludeCreate |
true |
profile 文件 | 新建文件是否询问 |
editIncludeDelete |
true |
profile 文件 | 清空 / 置空文件是否询问 |
bashTools |
['bash'] |
profile 文件 | 被拦截的命令类工具名 |
bashAllow |
[] |
profile 文件 | 始终放行的命令前缀(空白归一化) |
只有两个总开关是 live 字段——这正是它们成为"「插件」面板唯一展示、也是设置写入唯一可寻址"字段的原因:改动立即生效,无需重启。其余都是普通配置,在条目加载时读取,因此放在 profile 文件里。
开关被拨回 schema 默认值时是清除而非固化,所以 profile patch 只保留你真正改过的东西,日后的默认值变更仍能送达你。
str_replace_editor 默认不被拦截——它自 DSH 0.1.3 起不再是默认工具。针对它的 guard 分支仍然随包发布并有单测;把 str_replace_editor 加进 editTools 即可启用。
从 0.3.x 升级。
edit-approval与bash-approval两个设置命名空间已不存在,键名改为上面的扁平字段。DSH 的设置服务会把旧settings.yaml的 section 导入同名条目,而这两个旧名字都不是 profile 条目 id,因此不会迁移。若你此前开着命令审批,请在 profile patch 里写上bashEnabled: true(或在该面板拨一次)。
明确不做的事
- 不绕过也不扩大沙箱——从不改动沙箱模式或授予权限;提权调用交给沙箱自己的审批。
- 不拦截命令内部——在
bash命令里发生的文件改动不由编辑门管辖(开启命令审批后由它覆盖)。 - 不做部分应用——diff 是只读预览(
+/-行标记),不支持"只应用其中几行"。 - 不对工具本身会失败的调用提问——例如对已存在文件执行
str_replace_editor create、old_str/old_string缺失或不唯一;这些直接放行,由工具自己报错。空的old_string编辑预览与工具行为有偏差(按"未找到"处理)——这是安全的,绝不会误拦。 - 不做快捷键(Enter 批准 / Esc 拒绝)——已拆到独立的 dsh-approval-hotkeys 插件。
- 不做改后复查 / 回滚——见社区插件 dsh-change-review。
- 不扩展权限层级——见社区插件 dsh-auto-approval-plugin。
兼容性
- Node.js
^22.19.0 || >=24.0.0。 - DeepSeek Harness web profile(
dsh --profile web);@deepseek-ai/*由 harness 在运行时解析,本包不会自行拉取。 - 仅支持 DSH
0.2.0这一条线——验证目标是0.2.0-rc.1,也是本次发布devDependencies钉住的版本。声明为每个 harness 包一个^0.2.0-rc.1peer 元组,以及engines.dsh = ">=0.2.0-rc.1"。本插件一次只追一条 DSH 线,线一移动就丢掉上一条:该范围放行0.2.0-rc.1至0.2.x(同元组的预发布滚动无需改动 peer),而0.1.7-rc.2与0.3.0-rc.1会在启动时被跳过并给出 peer 诊断,而不是加载成半坏状态。 node scripts/check-dsh-version.mjs盯发布节奏:校验声明自洽(各 harness peer 同一元组、engine floor 同步),并在 DSH 发布了声明窗口之上的版本时报告。- 为什么是单线。 DSH 0.1.7 把本插件两扇门共用的设置命名空间注册表换成了"每个 profile 条目一个 live
Config",同时删除了浏览器侧dsh-client-runtime包与settingsScope服务。旧模型没有 1:1 后继,因此同时兼容更早的线意味着维护两套设置模型、两条客户端写路径与两种会话读取形状。逐 seam 的完整记录(包括哪些失效是静默的)见 docs/compat/0.1.7-audit.md。 - 注册的工具名是
str_replace_editor(下划线),与 npm 包名@deepseek-ai/dsh-tool-str-replace-editor不同。
[!WARNING] 本项目与 DSH 均处于开发者预览阶段。请在可复现环境中 pin 精确版本,并留意上述行为说明。
安全
插件只在 tools/pre-execute 拦截点读取目标文件以计算编辑预览;命令门完全不碰文件。它自己从不写文件——工具主体只在你批准后才执行写入。它不发起网络请求,也不访问任何凭据。
开发
npm install # 装 devDeps(harness 包精确钉到目标线)
npm run typecheck # 对两套编译面做 tsc,跑在真实 DSH 类型上
npm test # vitest:diff / guards / config / integration / client / package-layout
npm run build # scripts/build.mjs:声明 + 两个产物 + 冒烟检查
npm run verify:host # 在真实 cordis Context 上驱动**已构建**的 host 产物
npm run check # typecheck + tests + build + verify:host + npm pack --dry-run
prepare 会跑构建,因此 git 安装与 npm pack / npm publish 始终产出完整的 lib/(含 .d.ts)与 LICENSE。
其中两步的存在,是因为让本插件整整报废一条 DSH 线的两种失效都是静默的:
- 构建会被自己的冒烟检查拦住:客户端产物不允许内联任何
node_modules输入(经 esbuild 的 metafile 检查),所以 externals 列表漏掉一个平台模块会让构建失败,而不是悄悄打进第二份 React;同时两个产物都不得引用目标线已删除的包。 verify:host把 settings 替身双向锚定到真实的SettingsForms.prototype。此前那个替身只实现了一个方法——settings.register,恰好是 0.1.7 删掉的那一个——于是套件与插件互相印证、与真实世界无关,绿着穿过了它本该拦住的那次断裂。
发布
发布走 GitHub Actions Trusted Publishing(OIDC,不存 NPM_TOKEN)。见 docs/npm-trusted-publishing-guide.md。
npm version patch && git push origin main --tags # 触发 .github/workflows/publish.yml
工作流会校验 tag 与 package.json 一致,跑 typecheck + 测试 + 完整构建 + 产物校验,带 Sigstore provenance 发布,并创建 GitHub Release。CI(.github/workflows/ci.yml)在每次 push / PR 上跑同样的检查。发布步骤是幂等的——npm 上已存在的版本会被跳过。
许可
链接
同类插件
Tencent/WeKnora#dsh-weknora★ 31680
把 WeKnora 知识库接入 dsh 的四个只读工具:列出知识库、混合检索原文片段、按顺序还原单篇文档,以及直接取用 WeKnora 自己带引用的 RAG 或 ReAct agent 回答(含可续聊的 session id)。
superdesigndev/treg★ 3995
给 Agent 的工具目录:按「要做的事」检索约 2,600 个外部接口(SEO 与 SERP、外链、社交、人物与公司信息补全、广告库、抓取),查看参数与单次调用价格后直接调用,凭据由服务端注入。附带技能,MCP 行在未设置 TREG_TOKEN 前保持禁用。
TencentCloudBase/CloudBase-AI-Toolkit#dsh-plugin★ 1130
把腾讯云 CloudBase 后端接入 DeepSeek Harness——在对话里搭好并部署全栈应用,查询结果渲染为表格卡片(分页、排序、导出 CSV),部署后可预览真实域名,并提供 CloudBase MCP 工具集(`mcp__cloudbase__*`),登录走 device-code 流程。
gitroomhq/postiz-agent#dsh-postiz★ 499
通过 MCP 将 DeepSeek Harness 连接到 Postiz:列出已连接的社交媒体渠道、获取各平台发帖规则,并向 X、LinkedIn、Instagram、Facebook、Threads、TikTok、YouTube、Reddit、Bluesky、Mastodon、Discord、Slack、Telegram 等平台排期、存草稿或发布帖子;附带 postiz 工作流技能。
EthanYoQ/Invoice-Downloader#dsh-invoice-downloader★ 477
面向 DeepSeek Harness 的本地 IMAP 发票下载、OCR 识别、归档与 Excel 报销汇总。
anysearch-team/anysearch-dsh★ 443
基于 AnySearch 的实时网页与垂直搜索插件,为 DeepSeek Harness 提供搜索工具。
社区评论
评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。