DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Custom Tool Studio — DeepSeek Harness 插件(DSH Plugin)
← Plugins
C

dsh-custom-tool-studio

Custom Tool Studio

自定义工具工作室:在 Web 页面中或通过 agent tools 编写 Python 工具,然后将其作为一等 DSH 工具运行。危险工具在每次执行前都需要内置批准。可选的按对话解锁模式(延迟加载工具),支持始终解锁的工具包以及未分组工具的单个工具解锁。

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:xtt-xt/dsh-custom-tool-studio#1664ed55fda1cecc0438179c856a0cd85436e2eb
README兼容性版本

兼容性与来源证明

Custom Tool Studio 以 dsh-custom-tool-studio 发布,当前版本为 1.2.0。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
github
Registry 更新时间
2026/10/18

版本

1.2.0stable
2026/10/18

相关插件

正在加载相关插件…

最新版
1.2.0
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
web
许可证
MIT
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/10/18
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录

相关插件

继续浏览 developer-tools 分类下经过校验的插件。

Web App@deepseek-ai/dsh-web-appdsh 浏览器界面捆绑包:位于 dsh-base 之上的 Web 补丁层,加上运行时粘合插件(提供前端 dist、Web 界面提示符、bash 运行时变量和 URL 行)Sdk Minimal@deepseek-ai/dsh-sdk-minimal独立的最小 SDK 配置包:JSON-RPC、一个 DeepSeek 适配器、持久化 Shell 和 JSONL 会话Sdk App@deepseek-ai/dsh-sdk-appdsh SDK 配置包:基于 dsh-base 提供 stdio JSON-RPC 服务和进程生命周期管理Experimental Inspector@deepseek-ai/dsh-experimental-inspector用于 Host 调试和 Client Runtime 检查的实验性跨 realm CDP hub

README

DSH 自定义工具工作室(dsh-custom-tool-studio)

在 DeepSeek Harness(DSH)里用 Python 写工具,保存即生效。

两条入口都能用:

  • 网页:左侧导航「自定义工具」→ 新建 / 编辑 / 测试 / 删除 / 启停;
  • 对话:直接让模型调用内置的 custom_tool_manage 工具。

保存后的工具会立刻注册成真正的 DSH 工具(不需要重启 DSH),模型下一轮就能调用。所有数据只存在本机 %DSH_HOME%\custom-tools,不会上传。

仓库(公开):https://github.com/xtt-xt/dsh-custom-tool-studio · MIT · 当前版本 1.1.0


目录

  1. 这个插件做什么
  2. 界面入口
  3. Python 工具约定
  4. 危险工具与内置审批 / 自动审查
  5. 工具包(把几个工具放在一起)
  6. AI 侧工具 custom_tool_manage
  7. 数据存放位置
  8. 安装 / 验证 / 卸载
  9. 一键恢复脚本
  10. 故障排查
  11. 文件清单

1. 这个插件做什么

能力说明
创建工具起一个模型可见的工具名,写描述、参数 JSON Schema、Python 代码
即时注册保存/启用/改名/删除后,host 立即对账并注册或注销,无需重启 DSH
试运行页面「测试」或 AI 的 custom_tool_manage → action="test",直接传参数跑一次
危险工具标记后每次执行前都走 DSH 内置审批,和内置的自动审查兼容
工具包把几个工具归到一个有名称、有描述的分组里,一键启停整包;工具仍各自注册
AI 主动解锁开启后每个对话只看到管理/解锁两个工具,AI 解锁工具包后才出现包内工具;支持「始终解锁」与未分组工具单独解锁
统一存储所有工具存在一个 tools.json,可整体备份、迁移、删除
一键恢复restore.ps1 把插件从 profile 里干净卸载,可选清空工具数据

不是 Skill、不是 MCP:工具是同进程注册的真正 DSH 工具,执行时由 host 启动本机 Python 进程运行你的脚本。


2. 界面入口

安装并重启 DSH 后,左侧导航栏会出现「自定义工具」(与「插件」「插件市场」「自动化任务」「技能中心」同一排)。点进去是工具列表页:

  • 每行显示工具名(等宽字体)、标题、一句话描述,以及 Python、危险、已禁用 徽标;
  • 列表按工具包分组:每个包一张卡片,卡片头是包名、包描述、N 个工具 · 启用 M,右侧是整包开关和 编辑/删除;没有归包的工具放在最后一张「未分组工具」卡片里;
  • 每行右侧:测试、编辑、删除,以及该工具自己的启用/停用开关;
  • 顶部按钮:设置、刷新、新建工具包、新建工具。新建工具 打开编辑对话框:名称、标题、描述、所属工具包、参数 JSON Schema、Python 代码、危险工具开关、启用开关;
  • 没有工具也没有工具包时显示空状态和 def run(args): ... 约定;
  • 如果 host 探测不到 Python,页面顶部会显示「未检测到 Python,创建的工具无法执行」的警告条,并带上具体原因。

「测试」对话框里填 args JSON(就是传给 run(args) 的对象),点「运行」后显示成功/失败、结果、stdout、stderr 和耗时。测试失败不会弄坏已保存的工具。


3. Python 工具约定

3.1 最小例子

def run(args):
    text = args["text"]
    return {
        "chars": len(text),
        "upper": text.upper(),
    }
  • 脚本必须定义 def run(args):(页面和 AI 侧都会校验代码里有没有这个入口);
  • args 是模型按参数 JSON Schema 传进来的参数对象(普通 Python dict);
  • 返回值必须是可 JSON 序列化的值(dict / list / str / 数字 / bool / None)。host 会把它 json.dumps(..., ensure_ascii=False) 后作为工具结果给模型看;
  • 返回值是结果通道,print() 是日志通道,两者互不干扰:
    • print() / sys.stdout 的内容作为 stdout 展示(最多保留末尾 8,000 字符);
    • 脚本写到 sys.stderr 的内容进 stderr;抛出的异常被抓成 error(含 traceback),不会混进 stderr;
    • 结果 JSON 超过 20,000 字符会被截断。
  • 脚本在独立 Python 进程里执行,工作目录是 DSH 进程的当前目录;可以用 Python 标准库,第三方库需要装在那个解释器里;
  • 默认超时 60 秒,超时或取消会终止进程。可用环境变量 DSH_CUSTOM_TOOL_TIMEOUT_MS 调整(毫秒),改完重启 DSH;
  • 每次执行都会在 runs\ 下建一个临时目录(tool.py、args.json、result.json、stdout.txt、stderr.txt),只保留最新 30 个;
  • 解释器探测顺序:DSH_CUSTOM_TOOL_PYTHON(可指定完整路径)→ python → python3 → py -3。

3.2 参数 JSON Schema 怎么填

参数根节点必须是 JSON 对象。常用写法:

{
  "type": "object",
  "properties": {
    "text": { "type": "string", "description": "要统计的文本" },
    "limit": { "type": "integer", "description": "最多返回几行" }
  },
  "required": ["text"]
}
  • description 写给模型看,说明每个参数的含义;
  • required 里列出必填参数;
  • 没有参数就填 {"type": "object", "properties": {}};
  • 非法 JSON 或非对象会被 host 容忍并退化成「无参数」,但那样模型就传不进参数了——以页面「测试」的结果为准。

3.3 名称与描述

  • 工具名规则:^[a-z][a-z0-9_]{1,47}$(小写字母开头,只含小写字母/数字/下划线,长度 2–48);
  • run_code 是 DSH 保留名,不能使用;
  • 不能和内置工具、其它插件已经注册的工具重名(例如 read_file),冲突时保存会报「已被现有工具占用」;
  • description 是模型选工具的主要依据,写清「做什么 / 什么时候用 / 返回什么」;
  • 标题(title)只给人看,可以写中文;不填时默认等于工具名。

4. 危险工具与内置审批 / 自动审查

  • 编辑对话框里的「危险工具」开关,或 AI 侧 dangerous: true,会把工具标记为危险;
  • 标为危险后,模型每一次调用它都会先产生一个 ask 审批决定,由 DSH 内置审批界面问你「是否运行危险自定义工具 xxx?」,点拒绝就不会执行;AI 通过 custom_tool_manage 的 action="test" 试跑危险工具同样要审批;
  • 页面上的「测试」按钮是你本人直接点的一次运行,直接执行、不弹审批(否则等于自己审批自己);
  • 这与 desktop profile 默认加载的内置自动审查(@deepseek-ai/dsh-experimental-auto-review)兼容:自动审查策略接管 ask 的时候同样按它的判断走,不需要额外配置;
  • 非危险工具不会弹审批;
  • 建议把会写文件、删文件、执行系统命令、访问网络、读取敏感目录的工具都标成危险。

4.1 管理操作审批(顶部「设置」)

除「危险工具执行」之外,还可以让 AI 的管理操作也要你确认:

开关默认作用
创建工具需要审批关AI 用 custom_tool_manage 的 create 新建工具时先弹审批
修改工具需要审批关AI 用 update 改名称/描述/参数/代码/危险与启用状态时先弹审批
删除工具需要审批开AI 用 delete 删除工具时先弹审批(删除不可恢复,所以默认开)

工具包的操作复用同样三个开关:

AI action走哪个开关
pack_create创建工具需要审批
pack_update(改描述/改成员/改名)修改工具需要审批
pack_delete删除工具需要审批
pack_set_enabled不拦(和 set_enabled 一样,只是批量改启用状态)
  • 三个开关存在 %DSH_HOME%\custom-tools\settings.json,点「保存设置」写入;
  • 只拦截 AI 侧 的 custom_tool_manage;你在页面上点按钮 / 用表单操作不会被自己拦;
  • test(试跑)不在三个开关里:它按「危险工具」规则单独判定;
  • 「危险工具执行审批」不受这里影响,始终生效。

危险开关只是「每次都问」,不是沙箱。脚本仍以当前用户权限运行,写代码时要自己注意边界。


5. 工具包(把几个工具放在一起)

工具包(pack)是分组,不是新的工具类型:

  • 一个包有名称(1–48 字符,可写中文)和描述;
  • 包里的工具仍然是各自独立的 DSH 工具,注册方式、工具名、参数、代码、危险标记全部不变;
  • 包只做两件事:把工具在页面里归到一起、以及一键启停(等价于逐个改它们的启用标记)。

5.1 在页面里用

  1. 点顶部 新建工具包,填名称和描述,在「包里的工具」里勾选要放进去的工具;
  2. 保存后列表里出现这个工具包的卡片,勾选的工具显示在它下面;
  3. 卡片头的开关一键启用/停用整包:全部启用时开、全部停用时空、部分启用时显示「部分启用」并可一键全部打开;
  4. 工具要换包:编辑工具 → 「所属工具包」下拉选另一个包(或选「不放入工具包」);
  5. 删除工具包只是取消分组:包里的工具全部保留,变回「未分组工具」。

包和工具是「一个工具最多属于一个包」的关系(成员关系记在工具记录上),所以改包名不会影响任何工具。

5.2 什么时候用

  • 工具多起来以后按用途分类:文本处理、数据处理、运维脚本……;
  • 调试某批工具时整包停掉,避免它们出现在模型的工具列表里;需要时整包打开。

5.3 AI 主动解锁模式(deferred tool loading)

通用叫法是 deferred tool loading(工具延迟加载)/ progressive tool disclosure(渐进式工具披露);Anthropic 侧叫 Tool Search / defer_loading,OpenAI Responses 侧叫 deferred tools。插件用的是 DSH 内核自带的「会话中途增加工具」通道:解锁只改变当前 agent 的可见性,不重新注册工具。

开启方式:页面顶部「设置」→ 打开「AI 主动解锁模式」。

开启后:

  1. 每个对话初始只看到两个自定义工具:
    • custom_tool_manage(管理工具/工具包)
    • custom_tool_unlock(解锁工具包)
  2. 包里的工具默认不可见、不可调用;AI 调 custom_tool_unlock 的 action="unlock"、pack="包名" 后,包内启用的工具在下一轮请求出现。
  3. 不同对话单独解锁:解锁状态按 session id 记在 unlocks.json;新对话 / 子代理不会继承。DSH 重启或插件重挂载后会按文件恢复。
  4. 始终解锁:编辑工具包 → 打开「始终解锁(所有对话可见)」。这个包在任何对话里都无需解锁,适合常用工具。也可以让 AI 用 custom_tool_manage 的 pack_update 传 alwaysUnlocked=true。
  5. 未分组工具:编辑工具 → 把它放进某个工具包是推荐做法。若确实要保留未分组,可在「设置」里打开「允许单独解锁未分组工具」,然后 AI 用 custom_tool_unlock 的 tool="工具名" 单独解锁;关闭这个开关时,未分组工具不能单独解锁,只能放进工具包。
  6. 关闭解锁模式:所有启用工具立即恢复可见;unlocks.json 里的记录保留,下次开启继续生效。
  7. 工具包清单会注入上下文:插件注册了一段动态运行上下文(ctx.systemPrompt.context),把当前工具包、成员名与未分组工具写进每个模型步骤的提示里,AI 不用先调 action="list" 就知道有什么可解锁。文本每次组装时重建,改包/改开关下一轮即生效;解锁模式关闭时不注入,避免无意义 token。

注意:

  • 这是可见性/上下文管理,不是权限边界。危险工具即使被解锁,每次执行仍然走 DSH 内置审批。
  • 解锁/锁定不会改变工具的启用状态;要停用整包请用工具包开关或 pack_set_enabled。
  • 可见工具集变化会让该对话的提示前缀缓存失效一次,这是解锁的固有成本。
  • 插件重挂载(bundle 禁用→启用)时,旧掩码会先释放、再按 unlocks.json 重建,不会出现「解锁了却仍不可见」的叠加。

6. AI 侧工具 custom_tool_manage

模型可以直接调用 custom_tool_manage 完成全部管理操作,和网页共用同一套逻辑。

6.1 action 列表

action作用必填参数
list列出所有自定义工具(含启用/危险状态,以及工具包名)–
get查看某个工具的完整定义(含 Python 代码、所属工具包)name
create新建并注册一个工具name、description、code
update修改已有工具(只改传入的字段)name + 至少一个字段
delete删除工具(同时从 DSH 注销)name
set_enabled启用 / 停用name、enabled
test用给定参数试运行一次name(args 可选)
pack_list列出工具包及各自成员–
pack_get查看一个工具包的成员与描述pack(或 name)
pack_create新建工具包,可同时指定成员name(description/tools/alwaysUnlocked 可选)
pack_update改包描述 / 改成员 / 改名 / 改始终解锁pack + 至少一项(description/tools/alwaysUnlocked/name)
pack_delete删除工具包(工具保留,变未分组)pack
pack_set_enabled一键启停整包pack、enabled

6.2 参数

参数类型说明
actionstring上面 13 个之一(必填)
namestring工具名;create 必填,其余工具类 action 用它定位工具。pack_create 时是包名,pack_update 时是包的新名字
titlestring给人看的标题,可空
descriptionstring给模型看的说明;create 必填。工具包类 action 里是包的描述
parametersobject参数 JSON Schema 对象;省略则无参数
codestringPython 源码,必须含 def run(args):;create 必填
dangerousboolean是否危险(每次执行需审批),默认 false
enabledboolean是否启用,默认 true;停用的工具不注册
packstring包名或包 id:pack_get/update/delete/set_enabled 用它定位包;create/update 用它把这个工具放进该包,传空字符串表示移出
toolsstring[]包成员(工具名数组):pack_create 可选;pack_update 给了就整体替换成员(没列出的工具移出该包但保留)
alwaysUnlockedboolean工具包是否「始终解锁」:true 时任何对话都无需 custom_tool_unlock 即可见。仅在解锁模式开启时有意义
argsobject仅 test 使用:传给 run(args) 的参数对象

6.3 对话示例

帮我新建一个自定义工具 python_word_count,统计文本字符数。
参数:text(字符串,必填)。代码用 def run(args): return {"chars": len(args["text"])}

模型会调用 custom_tool_manage:action="create",然后建议 action="test" 验证。

也可以直接说:

列出我现在所有的自定义工具,并测试 python_word_count,参数 text="hello"

工具包相关:

把 python_word_count 和 python_clean_text 放进一个叫「文本处理」的工具包,
描述写「处理文本统计与清洗」。然后把整个包停用。

模型会依次调用 pack_create(name="文本处理", description=..., tools=["python_word_count","python_clean_text"])和 pack_set_enabled(pack="文本处理", enabled=false)。

6.4 解锁工具 custom_tool_unlock

只在开启「AI 主动解锁模式」后有意义。参数:

参数类型说明
actionstringlist / unlock / lock(必填)
packstring工具包名称或 id;与 tool 二选一
toolstring未分组工具名;与 pack 二选一,需要设置开启「允许单独解锁未分组工具」

action 行为:

action作用
list列出所有工具包、未分组工具及各自状态(未解锁 / 当前对话已解锁 / 始终解锁 / 已停用),并说明当前设置
unlock解锁整个包(pack=...)或一个未分组工具(tool=...);下一轮请求生效
lock反向收起;alwaysUnlocked 的包不能单独锁定,要去页面关闭「始终解锁」

示例:

我需要用 mcdev-docs 里的工具查文档。

模型会先 custom_tool_unlock action="list",再 action="unlock"、pack="mcdev-docs";包内工具在下一轮出现。属于某个包的工具不能用 tool=... 绕过包,必须整包解锁。


7. 数据存放位置

所有数据都在:

%DSH_HOME%\custom-tools

本机默认是:

C:\Users\acer\.dsh\custom-tools

如果设置过 DSH_HOME,以它为准;插件也支持用 DSH_CUSTOM_TOOLS_DIR 指向别的目录(需重启 DSH)。

路径内容说明
tools.json工具记录(唯一真源)名称、标题、描述、参数、代码、dangerous、enabled、packId(所属工具包,空=未分组)、时间戳
packs.json工具包记录id、name、description、alwaysUnlocked(是否始终解锁)、时间戳;成员关系不在这个文件里,而在各工具的 packId 上
settings.json全局开关unlockMode(AI 主动解锁模式)、allowUngroupedUnlock(允许单独解锁未分组工具)、approveCreate / approveUpdate / approveDelete;文件不存在时用默认值(解锁模式关、删除审批开)
unlocks.json按对话的解锁状态sessions.<sessionId>.packs(已解锁的工具包 id)+ .tools(已单独解锁的未分组工具 id);最多保留最近 200 个对话
scripts\<工具名>.py每个工具的代码副本方便直接阅读/备份;直接改这里不会生效,执行用的是 tools.json 里的 code
runs\r_<...>\每次执行的临时目录含 tool.py、args.json、result.json、stdout.txt、stderr.txt;自动只留最新 30 个
runner.py自动生成的 Python 执行器不要手改,会被覆盖

备份/迁移:直接复制整个 custom-tools 目录即可(tools.json + packs.json + settings.json + unlocks.json 一起复制才算完整)。恢复时把目录放回原位(或设 DSH_CUSTOM_TOOLS_DIR),重启 DSH。

只把 packs.json 删掉不会丢工具:每个工具的 packId 找不到对应的包时,它会作为「未分组工具」照常列出来。


8. 安装 / 验证 / 卸载

下面命令都在 PowerShell 里执行。用 dsh.cmd,不要直接敲 dsh(PowerShell 执行策略常禁止执行 .ps1 垫片);本地开发用 link: 绝对路径,避免相对路径被锚定到当前目录。

8.1 安装

dsh.cmd plugin --profile desktop add link:F:\xtt\workspace\dsh-custom-tool-studio

dsh plugin 内部会跑一次 pnpm,并在成功后把声明了 dsh.bundle.patch 的本包自动写进 profile 的 dsh.profile.bundles。

如果 PATH 上没有 dsh.cmd,用 node 直接调启动器:

node "$env:APPDATA\npm\node_modules\@deepseek-ai\dsh\lib\bin.js" plugin --profile desktop add link:F:\xtt\workspace\dsh-custom-tool-studio

8.2 从 Git 仓库安装(本仓库)

本仓库本身就是一个可直接安装的 DSH bundle 包:根目录 package.json 声明了 dsh.bundle.patch(指向 cordis.patch.yml)和 dsh.client(Web 面板),纯 ESM + 纯脚本,没有构建步骤。安装后 profile 的 dsh.profile.bundles 会自动增加 dsh-custom-tool-studio。

dsh.cmd plugin --profile desktop add git+https://github.com/xtt-xt/dsh-custom-tool-studio.git

仓库是公开的,直接装即可,不需要 token:

dsh.cmd plugin --profile desktop add git+https://github.com/xtt-xt/dsh-custom-tool-studio.git

也可以装 Release 上的预构建包(dsh-custom-tool-studio.tgz,免源码构建):

dsh.cmd plugin --profile desktop add https://github.com/xtt-xt/dsh-custom-tool-studio/releases/latest/download/dsh-custom-tool-studio.tgz

装完同样要完全退出 DSH 再启动。在本机做开发时继续用 8.1 的 link: 方式(改代码不用重装)。

8.3 验证

# 1) 确认 profile 清单里有这个 bundle(desktop 版的实验 bundle 在 app.asar 里,
#    所以 `dsh --dump-config` 在 desktop 上会因解析不到实验 bundle 而报错,别用它验证本插件)
node -e "const p=require('C:/Users/acer/.dsh/profiles/desktop/package.json');console.log(p.dsh.profile.bundles.includes('dsh-custom-tool-studio')?'bundle 已登记':'未登记')"

# 2) 完全退出 DSH 再启动(host 只在启动时读取 profile 清单)

重启后:

  • 左侧导航出现「自定义工具」;
  • 页面能打开且不提示缺少 Python;
  • 新会话里模型可以调用 custom_tool_manage。

不启动 DSH 的离线自检(检查记录校验、工具定义构建、工具包生命周期):

node F:\xtt\workspace\dsh-custom-tool-studio\selfcheck.mjs

期望最后一行是 SELF-CHECK OK(退出码 0)。它会检查:工具名校验、参数 schema 归一化、输入校验、buildToolDefinition 的输出结构 / render 文本块 / execute 返回键与声明 schema 一致、custom_tool_manage 名称与 pack_* 动作、工具包的分组 / 整包开关 / 删除后保留工具、alwaysUnlocked、custom_tool_unlock 的 list/unlock/lock、computeDeny 的基线/包/单工具规则,以及 unlocks.json 往返。

完整离线套件(8 个):

node F:\xtt\workspace\dsh-custom-tool-studio\test\run.mjs

期望 ALL SUITES PASS (8)。

线上验收解锁模式(建议按顺序):

  1. 页面「设置」打开「AI 主动解锁模式」和「允许单独解锁未分组工具」,保存;
  2. 新对话里模型只应看到 custom_tool_manage + custom_tool_unlock;内置工具不受影响;
  3. 让 AI 执行 custom_tool_unlock action="list",再 action="unlock"、pack="<包名>";下一轮包内工具应出现;
  4. 另开一个对话 / 子代理:它应显示「当前对话 0 个已解锁」,看不到上一个对话解锁的工具;
  5. 给某个包打开「始终解锁」后,新对话无需解锁即可看到它的成员;
  6. 关闭解锁模式:所有启用工具立即恢复可见;再打开,unlocks.json 里的记录继续生效。
  7. 打开解锁模式时,模型上下文里应出现一段「自定义工具包(解锁模式已开启)」并列出包名与成员;关掉后该段消失。

完整离线测试(宿主 store / HTTP 路由 / 审批门 / 页面字典 / 页面渲染):

node F:\xtt\workspace\dsh-custom-tool-studio\test\run.mjs   # 或 npm test

期望最后一行是 ALL SUITES PASS (8)。

8.4 卸载

dsh.cmd plugin --profile desktop remove dsh-custom-tool-studio

重启 DSH 后条目和工具都会消失。工具数据会保留在 %DSH_HOME%\custom-tools,需要一并清空时用下面的 restore.ps1 -PurgeData。


9. 一键恢复脚本

位置:

F:\xtt\workspace\dsh-custom-tool-studio\restore.ps1

用途:把本插件从 profile 里干净卸载;自动卸载失败时也能靠备份完成回退。它只处理 dsh-custom-tool-studio 这一个 bundle,不会碰其它插件或其它 profile 文件。

9.1 基本用法

powershell -NoProfile -ExecutionPolicy Bypass -File "F:\xtt\workspace\dsh-custom-tool-studio\restore.ps1"

装了 PowerShell 7 也可以:

pwsh -NoProfile -ExecutionPolicy Bypass -File "F:\xtt\workspace\dsh-custom-tool-studio\restore.ps1"

9.2 同时清空工具数据(不可恢复)

powershell -NoProfile -ExecutionPolicy Bypass -File "F:\xtt\workspace\dsh-custom-tool-studio\restore.ps1" -PurgeData

-PurgeData 会删除 %DSH_HOME%\custom-tools(本机 C:\Users\acer\.dsh\custom-tools),包括 tools.json、scripts\、runs\。脚本会校验目标必须是 %DSH_HOME%\custom-tools 这一确切路径,否则拒绝删除。

9.3 可选参数

参数默认值说明
-ProfiledesktopDSH profile 名
-BundleNamedsh-custom-tool-studio要卸载的 bundle 包名
-PurgeData关同时删除工具数据目录

9.4 脚本每一步做什么

  1. 备份:把 <profile>\package.json 和 <profile>\cordis.patch.yml(如果存在)复制到 C:\Users\acer\.dsh\profiles\desktop\.restore-backup-<时间戳>\;

  2. 官方卸载:执行 dsh.cmd plugin --profile desktop remove dsh-custom-tool-studio, 并打印退出码;PATH 上找不到 dsh.cmd 时自动回退到 node "$env:APPDATA\npm\node_modules\@deepseek-ai\dsh\lib\bin.js" plugin --profile desktop remove ...;

  3. 独立校验(不信 CLI 的返回值,自己查):

    • package.json 的 dsh.profile.bundles 里不再有 dsh-custom-tool-studio;
    • <profile>\node_modules\dsh-custom-tool-studio 不存在。

    校验失败时进入手动回退:以备份为底重建 package.json(只摘掉本插件一条,其它 bundle 原样保留)、用备份恢复 cordis.patch.yml、删除 node_modules 里的链接(只删链接,不删它指向的 F:\xtt\workspace\dsh-custom-tool-studio 源码目录),然后再校验一次;

  4. 可选清理数据:仅当传入 -PurgeData 时删除 %DSH_HOME%\custom-tools。

最后打印汇总,并提示:重启 DSH 使更改生效。脚本是幂等的,重复运行不会报错(第二次会提示「bundle 已不在 profile 中,跳过卸载」)。

退出码:0 = 校验通过且(如要求)数据已清理;1 = 有失败项,按输出里的 [restore] x 行排查。

脚本不会删除备份目录;需要彻底清理时手动删除 .restore-backup-* 即可。


10. 故障排查

现象原因 / 处理
页面顶部提示「未检测到 Python」安装 Python 3 并勾选 “Add python.exe to PATH”,在 PowerShell 里确认 python --version 可用;或设置环境变量 DSH_CUSTOM_TOOL_PYTHON=完整路径\python.exe。探测结果在 DSH 进程内缓存,改完要重启 DSH。
保存时报「工具名已被占用 / 已存在」自定义工具名不能和内置工具、其它插件工具重名;只能用 ^[a-z][a-z0-9_]{1,47}$;run_code 是保留名。换一个名字。
保存后模型看不到新工具检查「启用」开关;停用的工具不注册。新建的工具在下一轮请求可见(不需要重启 DSH)。
左侧没有「自定义工具」入口插件没装成功或装完没重启。先确认 profile 的 dsh.profile.bundles 含包名(见 8.3),再完全退出 DSH 重新启动。
工具包里的工具没变灰/没停用包开关改的是每个成员自己的启用标记;如果某个工具是后来才加进包的,重开一次页面即可看到最新状态。包本身没有独立开关。
开启解锁模式后工具突然消失预期行为:每个对话初始只保留 custom_tool_manage + custom_tool_unlock。让 AI 解锁对应工具包,或在页面把该包设为「始终解锁」;也可以在「设置」里关掉解锁模式立即恢复。
AI 说未分组工具不能单独解锁「设置」里的「允许单独解锁未分组工具」没开。开启后 AI 可用 custom_tool_unlock 的 tool=...;或者直接把工具放进某个工具包。
解锁成功但模型还是看不到工具解锁在下一轮请求生效;同一轮不会立刻变。若重启过 DSH 仍不可见,检查 unlocks.json 里的 session id 是否是当前对话,以及 [custom-tool-studio] 日志里有没有 install unlock mask ... failed。
子代理看不到父对话解锁的工具预期行为:每个 agent / 对话单独解锁。子代理自己调用 custom_tool_unlock,或把包设为始终解锁。
插件重挂载后解锁状态错乱正常流程会先释放旧掩码、再按 unlocks.json 重建;若仍异常,查看日志里的 install unlock mask / dispose,必要时删除 unlocks.json 让该对话重新解锁。
页面能开但一操作就报错看 DSH 启动日志里的 [custom-tool-studio] 警告(host 挂载失败会被记录);也看页面上的错误文字。
改了 scripts\*.py 不生效正常:tools.json 才是真源,scripts\ 只是代码副本。请在页面/让 AI 改,或改完用 custom_tool_manage 的 update 写回。
执行超时默认 60 秒;设 DSH_CUSTOM_TOOL_TIMEOUT_MS(毫秒)后重启 DSH,或让脚本更快返回。
危险工具没弹审批确认保存时「危险工具」是开着的;确认 profile 里审批/自动审查插件正常;改完重启 DSH。
中文输出乱码执行器已设置 PYTHONUTF8=1;仍乱码时把脚本文件存成 UTF-8(无 BOM),并在脚本里显式 encoding="utf-8" 读写文件。
dsh 命令找不到 / 报执行策略错误

11. 文件清单

F:\xtt\workspace\dsh-custom-tool-studio\
├─ package.json          # bundle 声明(dsh.bundle.patch / dsh.client)、入口
├─ cordis.patch.yml      # bundle 层:往 loader 树插入 custom-tool-studio
├─ index.js              # host 入口:存储、注册、HTTP 路由、dangerous 审批门
├─ lib\
│  ├─ store.js           # tools.json / packs.json / settings.json / unlocks.json 读写 + 输入校验
│  ├─ access.js          # 按 agent 计算 deny 掩码;会话解锁状态与 bundle 重挂清理
│  ├─ python.js          # Python 探测与执行(stdout/stderr/超时/取消)
│  ├─ tools.js           # 每个记录 → ToolDefinition;custom_tool_manage + custom_tool_unlock
│  ├─ prompt.js          # 解锁模式下把工具包清单写进模型上下文
│  └─ routes.js          # /api/custom-tool-studio/* 路由
├─ client.js             # Web 页面(侧边栏 + 分组列表/编辑/测试/设置/始终解锁)
├─ test\                 # 离线测试(node test/run.mjs)
│  ├─ run.mjs            # 汇总入口
│  ├─ store.test.mjs     # 记录校验 / 持久化 / 工具包 / 解锁状态
│  ├─ access.test.mjs    # deny 掩码、每对话隔离、模式开关、持久化
│  ├─ unlock-tool.test.mjs # custom_tool_unlock 的 list/unlock/lock
│  ├─ prompt.test.mjs    # 上下文里的工具包清单(模式开关、成员、始终解锁)
│  ├─ routes.test.mjs    # HTTP 路由与错误码
│  ├─ gate.test.mjs      # 危险工具与审批开关
│  ├─ client-dict.test.mjs   # 字典 zh/en 对齐、用到的 key 都存在
│  └─ client-render.test.mjs # 纯组件渲染(对话框/分组/编辑器/开关)
├─ .github\workflows\ci.yml  # 推送后自动跑 selfcheck + 测试
├─ scripts\push-api.mjs  # 本机 git push 走不通时,用 GitHub API 提交
├─ locale\               # zh / en 文案
├─ icon.svg
├─ restore.ps1           # 一键恢复(本文件第 9 节)
├─ selfcheck.mjs         # 离线自检
└─ README.md             # 本文件

开发循环:

改 index.js / lib\*.js / client.js
  → node F:\xtt\workspace\dsh-custom-tool-studio\selfcheck.mjs
  → node F:\xtt\workspace\dsh-custom-tool-studio\test\run.mjs
  → 重启 DSH(link: 安装后改代码不需要重新 add)

11.1 推送到 GitHub

仓库地址(公开):https://github.com/xtt-xt/dsh-custom-tool-studio

origin 走 SSH(git@github.com:xtt-xt/dsh-custom-tool-studio.git):本机 github.com:22 可通、github.com:443 被拦,所以 SSH 是这台机器上可用的 git 传输方式。公钥 ~/.ssh/id_ed25519 已加到 GitHub 账号 xtt-xt。

git add -A
git commit -m "feat: ..."
git push               # 已设置 upstream = origin/main

如果换到只有 api.github.com 可通、22/443 都不通的环境,用仓库里的 API 脚本提交同样的树(不需要 git 协议):

$env:GH_TOKEN = '<GitHub PAT>'          # 需要 Contents: write
"feat: ..." | Set-Content -Path "$env:TEMP\msg.txt" -Encoding utf8
node scripts\push-api.mjs xtt-xt/dsh-custom-tool-studio main "$env:TEMP\msg.txt"

脚本会为每个已跟踪文件建 blob → 建 tree → 建 commit → 移动 refs/heads/main,输出的最后一行是 commit 链接。用它推送后,本机执行 git fetch origin && git reset --hard origin/main 把本地历史对齐到远端。


适用:DSH desktop profile,Node.js 20+,Windows。最后更新:2026-10-18。

用 dsh.cmd;或 node "$env:APPDATA\npm\node_modules\@deepseek-ai\dsh\lib\bin.js" ...。
卸载后数据还在属于预期:custom-tools 独立于插件。要清空用 restore.ps1 -PurgeData。
插件卸载/安装后行为异常装、卸插件都必须完全重启 DSH;GUI 当前进程不会热加载新 bundle。