dsh-plugin-studio
为了开发插件,开发了一个开发插件的插件。通过可视化的事件流、插件管理、工具管理、技能管理、预设管理,简化插件的开发流程,方便开发者理解插件的作用。
- Stars
- 1
- Language
- JavaScript
- Created
- Aug 27, 2026
- Updated
- Aug 27, 2026
Introduction
🧩 dsh-plugin-studio · DSH 插件工作室
让 Cordis 运行时从"看不见、摸不着"变成看得见、可操作、可开发的 Web 工作台。
v0.1.0-alpha · Demo · MIT · 平台:DeepSeek Harness (DSH)
状态声明:这是一个功能完整的 demo 版本。它已在真实 DSH 部署上跑通全部七个页签;官方双面 bundle 渠道(经
dsh plugin add)同时交付 Host 半部与浏览器 UI。API 与数据格式可能在小版本间变化。
为什么做它:五个真实的问题
DSH 的理念是用户自行开发插件、随插随用。但现实是:插件系统(Cordis)对用户完全不可见。 下面的五个问题,每一个都来自真实使用中的困境——不是"有了锤子找钉子",而是先有钉子。
问题一:插件运行时是一个黑箱
你在 DSH 里装了插件、写了预设,然后呢?你什么都看不到。
- 插件注册了哪些监听?此刻有没有事件正在分发?分发给了谁?参数是什么?
- 一个 waterfall 事件被三个插件接力处理,现在进行到哪一棒?谁改了载荷?谁断了链?
- LLM 流式调用、工具执行、会话投影……这些系统内部每秒发生的事,对用户是黑箱。
不了解 Cordis 底层架构的用户只有一个手段:用对话一遍一遍迭代——"帮我看看插件有没有被触发""好像没生效,你再试试"。这不仅低效,而且每一次盲改都可能把插件改崩,崩了还不知道崩在哪:没有任何报错可见,没有任何状态可查。
一个好的解决方案必须让用户看见:发生了什么事件、组件与状态长什么样。这需要一个可视化 UI 视图 + IDE,而不是又一段文档。
工作室的回答:「事件流」页签。 侧边栏底部的 🧩 拼图按钮打开一个专属子界面:滚动的实时事件树——每一次事件分发一行(seq / 时间 / 模式徽标 / 事件名 / 参数摘要),覆盖 emit、waterfall、serial、parallel、bail 全部分发模式;按命名空间着色,可搜索、按模式过滤、按分类(agent / tools / llm / session / workflow…)过滤;点击行展开参数,每个
[Object]/[Function]/[Array(n)]芯片可逐层展开到真实内容(函数能看到源码)。默认 500 条内存环形缓存(50–10000 可调、持久化),重启即清空——它是观测镜,不是日志系统。
问题二:能力地图不存在——"我到底能组合什么?"
就算你能看见事件在流动,下一个问题立刻出现:有哪些事件可以监听?签名是什么?哪个插件在听哪个事件?
- 没有任何界面告诉你"当前运行时里存在哪些事件、参数是什么";
- 想让插件 A 响应插件 B 产生的事件?你只能去翻源码、猜字符串、试错;
- "事件与插件"是单点关系,而用户真正想要的是一套可持久维护的组合:把若干插件打成一个具名的 set,甚至 set 套 set。
没有地图,扩展就是探险;每次增加一个节点、一个功能,都要重新摸索一遍。
工作室的回答:「插件一览」页签。 通过运行时真实反射(
typert)给出全部已注册包的事件/服务签名目录(而非字符串猜测);支持持久化的注释层,为事件参数补上人类可读的表头;SVG 关系图把"事件 ↔ 插件"连线画出来;「插件组合」允许把插件(或嵌套的组合)定义成具名集合,持久化保存、一键启停、带环检测。
问题三:插件生命周期只能靠"嘴"管理
插件是动态的:用户想随时加载、卸载、启停。但系统里没有一个地方以"动态可用插件"的视角管理它们:
- 装了什么、停了什么,散落在对话和配置文件里;
- 上一轮调试到一半的启停状态,重启 webUI 后全部丢失,又要从头来;
- 想批量恢复一组插件?"逐个手动点"是唯一选项。
以"安装与否"界定插件是静态思维;用户需要的是一个可持久化、可监控、可恢复的动态目录。
工作室的回答:「插件管理」页签。 系统插件(loader 分区,绿/红监控,可手动启停)、外来插件(GitHub 一键 clone 入库 / 本地注册)、动态插件(工作室目录)三个分区;具名组合支持嵌套,全启绿 / 全停红 / 部分黄;快照记录当前三类插件的启停情形,下次 webUI 启动时若发现状态不一致,横幅询问"是否恢复"——并支持把任意快照设为侧栏快捷启停按钮。
问题四:写插件的 engineer tax 太高
监听一个事件、改一个状态,本应是十行代码的事。但现在:你得知道事件叫什么、参数有哪些、waterfall 要不要调 next()、怎么测试、怎么持久化、怎么变成真正可启停的插件。每一项都是门槛,叠加起来就是一道墙。
用户需要的不是文档,是一个低代码工作台:选事件像点菜单,写逻辑像填空,剩下的交给平台。
工作室的回答:「无状态插件开发」页签。 新建插件包 → 在
apply(ctx)函数体编辑器里写逻辑(编辑器上下方有只读的 apply 代码块提示,你的代码落在哪一目了然)→ 两级下拉插入分区示例(每个示例自带官方手册链接 + 典型用途)→ 监听器可用下拉选事件(反射 ∪ 注释双源)、自动推断参数名与说明、一键切换到等价代码视图(回转失败会提示风险而不是崩溃)→ JSON payload 单次测试 → N 秒被动监控窗口验证触发 → **「推送为插件」**直接进入问题三的动态插件目录。全部内容即时持久化,并与本地开发文件一一对应。
问题五:三类输入面(tool / skill / 预设)没有管理界面
光能监听事件还不够。Agent 的行为由三样东西塑形:工具(模型能调用什么)、技能(加载后注入的指令包)、预设(整个 agent 的组合方式)。它们目前散落在注册表、文件系统和 YAML 里,没有统一的查看、创建、启停入口——更不用说"把现有预设复制一份改成自己的"这种最基本的定制诉求。
工作室的回答:tool管理 / skill管理 / 预设管理 三个页签。 工具与技能按 agent 域分层取并集(系统内置的也会出现),支持以持久化方式新建(HTTP 工具 / async 代码工具 / Markdown 技能),启停即注册/注销;预设管理列出全部预设,可从现有预设副本式创建(与"创造模式"同构)、结构化模板编辑(逐行组合、实时 YAML 预览、不落盘、一键复制完整模板)、设默认、删除本地预设。
一个贯穿的设计立场:安全边界优先
工作室大量触达运行时内核(事件总线、装载表、反射)。它给自己划了明确的红线:工作室自身运行在动态插件沙箱里——没有 ctx.emit,想"凭空产生事件"只能走自己的内部 bus(事件流中标记为 bus 模式,可观测);监听插件在工作室自己的纤维上注册,waterfall 未调用 next() 时自动续链、出错自动回落,绝不破坏 DSH 默认行为;所有涉及部署配置的写操作(如停用系统插件)在 UI 上明确标注。
一屏速览
| 页签 | 你能做什么 |
|---|---|
| 事件流 | 实时事件树 · 搜索/模式/分类过滤 · 参数逐层展开 · 缓存上限可调(仅内存) |
| 插件一览 | 事件/服务反射目录 · 事件↔插件关系图 · 注释层 · 插件矩阵 |
| 插件管理 | 系统/外来/动态三分区启停 · GitHub 安装 · 嵌套组合 · 快照与恢复 · 侧栏快捷启停 |
| 无状态插件开发 | apply 函数体编辑器 · 事件下拉+参数推断 · 模板库 · 测试 · 被动监控 · 推送为插件 |
| tool管理 | 内置+自建工具并集 · HTTP/代码工具创建 · 启停/删除 · 持久化 |
| skill管理 | 内置+自建技能并集 · Markdown 技能创建 · 启停/删除 · 查看内容 |
| 预设管理 | 预设列表 · 从现有预设副本式创建 · 结构化模板编辑(不落盘) · 设默认 |
打开方式:侧边栏底部(设置旁)的 🧩 插件工作室 按钮 → 全屏面板,右上 × 关闭。
安装指南
前置条件
- 一个可运行的 DSH 部署(web profile),侧边栏与设置面板工作正常;
- 需要本仓库以 npm 包形式发布后,由
dsh plugin安装(见下)。
方式一:官方渠道安装(推荐 · 完整功能)
本项目是双面 bundle 插件:一个 loader 条目同时提供 Host 半部(事件捕获、反射、RPC、持久化、外部插件装载)与浏览器半部(七个页签的 Web UI)。发布后可像其它 DSH 插件一样安装:
dsh plugin --profile web add @tsqurt/dsh-plugin-studio
安装后重启 dsh web,侧边栏底部出现 🧩 插件工作室 按钮,打开即为完整 UI。
无需手动改
cordis.patch.yml:本包声明了dsh.bundle.patch(见包内cordis.patch.yml)。dsh plugin add把它识别为 bundle,自动追加到dsh.profile.bundles,启动时由 DSH 将该 patch 作为一层叠入配置树——HOST 与 CLIENT 两半都自动就位。若你的部署未启用该 bundle 机制,可退回到下方的 git 安装方式。
方式二:从源码克隆并注册 loader 行(自托管)
不想用 npm 时,可 clone 源码,构建后手动注册 loader 行:
# 1. 克隆(或放到任意目录)
cd $env:DSH_HOME # 例如 C:\Users\<you>\.dsh
git clone https://github.com/Tsqurt/dsh-plugin-studio.git
# 2. 构建单文件动态包
cd dsh-plugin-studio
node src/build.js # 产出 dist/host.js / dist/client.js / dist/host.mjs / bootstrap-*.js
- 注册 loader 行(用户 patch 层,不动部署自带配置——追加而非覆盖):
# $DSH_HOME/profiles/web/cordis.patch.yml (或 $DSH_HOME/cordis.patch.yml,对全部 profile 生效)
- insert:
- id: plugin-studio
name: ../../dsh-plugin-studio/dist/host.mjs # 相对 profile 配置文件解析;也可用绝对 file:/// URL
- 重启 DSH web 进程。宿主日志出现
loader: load plugin …/dist/host.mjs即装载成功。 - 验证:面板打开后「事件流」应立即出现滚动事件(Host 半部已挂上事件总线)。
注意:官方渠道安装时以包名
@tsqurt/dsh-plugin-studio作为 loader 条目name,此时客户端半部也会被 DSH 客户端模块系统扫入window.__DSH_BOOT__(经dsh.client声明)。仅用path直接注册dist/host.mjs的方式则只加载 Host 半部,需配合动态引导(dist/bootstrap-client.js)才出现完整 UI——详见路线图。
方式三:动态加载(零安装,完整功能 · 老 demo 推荐)
任意开启了 Cordis 预设的 DSH 会话里,让 agent 用两个动态 Cordis 调用直接装载,不写任何配置:
- 克隆并构建(同上);
- 对 agent 说:
请用 cordis_define 把
<工作区>/dsh-plugin-studio/dist/bootstrap-host.js定义为新插件(host 半部),再用 cordis_define(kind:"existing") 把dist/bootstrap-client.js追加为 client 半部,然后 cordis_run 激活。
- UI 批准动态插件运行 → 刷新页面 → 侧边栏出现 🧩 按钮。
引导体只做一件事:从磁盘读入 dist/host.js / dist/client.js(client 经宿主 RPC 拉取)。因此改完源码只需重新 node src/build.js 并刷新页面,无需重新激活。
卸载
- 方式一(官方渠道):
dsh plugin --profile web remove @tsqurt/dsh-plugin-studio,重启进程;数据目录可一并删除。 - 方式二:从 patch 层删除
insert行,重启进程。 - 方式三:让 agent
cordis_undefine,会话级即时清除,不留任何痕迹。
数据落在哪
工作室自身数据(JSON,原子写)优先落 <当前工作区>/dsh-plugin-studio-data/,回退 ~/.dsh/dsh-plugin-studio/:事件缓存上限、注释表头、插件目录、组合、快照、开发包、自建 tools/skills 等。事件流本身只在内存——重启进程即清空。
五分钟上手
- 打开 🧩 面板 → 事件流:发一条消息,看事件实时滚过;点开一行,展开参数看函数源码。
- 插件一览:在事件目录里找
tools/pre-execute,看它的签名;在关系图里找你认识的插件。 - 插件管理:点「制作快照」;停用一个系统插件再恢复它(注意黄色警告——这会写回部署装载表)。
- 无状态插件开发:新建包 → 选一个分区示例插入 → 测试 → 推送为插件 → 回到管理页启动它。
- tool管理:新建一个 HTTP 工具,然后回对话框让模型调用它。
工作原理(一段话版)
Host 半部挂在 Cordis 事件总线上:internal/dispatch 捕获每一次业务事件分发(含 waterfall/serial/parallel/bail 与触发器 bus),internal/plugin / internal/status 监控插件纤维生命周期,typert 服务反射出全部服务/事件签名,loader 给出系统装载表与启停控制;自身数据以 JSON 持久化。Studio 开发的监听插件以 ctx.on(event, handler) 注册在工作室自己的纤维上,启停即注册/注销。细节见 docs/architecture.md(Cordis 机制、沙箱约束、数据模型、waterfall 安全规则),UI 手册见 docs/user-guide.md,需求逐条映射见 docs/requirements-trace.md。
安全与边界
- 沙箱:工作室 Host 半部运行在
node:vm动态插件沙箱,只有白名单能力;监听器脚本经new Function编译,全程 try/catch,waterfall 出错自动回落next()。 - 慎重操作:启停系统插件会写回部署装载表(UI 有提示);GitHub 安装会执行真实
git clone与模块导入(UI 标注风险);卸载插件会连带清理组合引用(UI 有确认框)。 - 不做的事:不持久化事件流;不绕过沙箱直接 emit;系统(shipped)预设只读展示,模板编辑不落盘。
已知限制与路线图(0.1.0-alpha)
- 静态 client 打包:已在官方双面 bundle 渠道落地(
dsh.client+exports["./client"]+ HTTP RPC)。官方渠道安装即获得完整 UI。 - 外部插件的精确监听边:Cordis 的监听回调不携带注册者纤维,关系图中系统插件暂显示装载状态与接口,精确"谁在听谁"等待 typert 贡献式声明 API。
- 代码编辑器为带行感的增强 textarea(Tab 缩进、模板插入),未内嵌 Monaco。
- GitHub 安装依赖环境存在
git与 shell 后端。 - 事件流轮询刷新(Host→Client 无公开推送通道),高负载下有秒级延迟。
开发与测试
node src/build.js # 重新打包 dist(host.js / client.js / bootstrap-*.js / host.mjs)
源码是 src/ 下的平面 JS 片段(build.js 按序拼接),dist/ 全部为生成物。运行时改码:改 src/ → node src/build.js → 刷新页面。
版本化的自动测试脚本位于仓库的
dev/(本包发布不包含,见.gitignore);本地克隆后可按需恢复或自行编写。
目录结构
dsh-plugin-studio/
├── package.json # 包元数据(name / main / exports ./client / dsh.client),即 npm 发布清单
├── cordis.yml # 部署示例(loader 装载)
├── LICENSE # MIT
├── docs/ # architecture / user-guide / requirements-trace
├── src/
│ ├── build.js # 打包器:src 平面片段 → dist 单文件 bundle(host+client)
│ ├── host/ # Host 半部(事件捕获/反射/持久化/RPC/处理器)
│ └── client/ # Client 半部(React UI:七个页签)
├── dist/ # 构建产物(host.mjs loader 入口 + host.js + client.js)
└── examples/ # 外部插件注册样例
License
MIT