Bundle
@tsqurt/dsh-plugin-studio
DSH 插件工作室 — 事件流 / 插件一览 / 插件管理 / 插件开发的 webUI 工作台
- Source
- Tsqurt
- stars
- 3 stars
- License
- MIT
- Updated
- Updated 4 hours ago
Readme
# 🧩 dsh-plugin-studio · DSH 插件工作室
**让 Cordis 运行时从"看不见、摸不着"变成看得见、可操作、可开发的 Web 工作台。**
`v0.1.0-alpha` · Demo · MIT · 平台:[DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness)
> **状态声明**:这是一个功能完整的 **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 插件一样安装:
```powershell
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 行:
```powershell
# 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
```
3. **注册 loader 行**(用户 patch 层,不动部署自带配置——追加而非覆盖):
```yaml
# $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
```
4. 重启 DSH web 进程。宿主日志出现 `loader: load plugin …/dist/host.mjs` 即装载成功。
5. **验证**:面板打开后「事件流」应立即出现滚动事件(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 调用直接装载,不写任何配置:
1. 克隆并构建(同上);
2. 对 agent 说:
> 请用 cordis_define 把 `<工作区>/dsh-plugin-studio/dist/bootstrap-host.js` 定义为新插件(host 半部),再用 cordis_define(kind:"existing") 把 `dist/bootstrap-client.js` 追加为 client 半部,然后 cordis_run 激活。
3. 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 等。**事件流本身只在内存**——重启进程即清空。
## 五分钟上手
1. 打开 🧩 面板 → **事件流**:发一条消息,看事件实时滚过;点开一行,展开参数看函数源码。
2. **插件一览**:在事件目录里找 `tools/pre-execute`,看它的签名;在关系图里找你认识的插件。
3. **插件管理**:点「制作快照」;停用一个系统插件再恢复它(注意黄色警告——这会写回部署装载表)。
4. **无状态插件开发**:新建包 → 选一个分区示例插入 → 测试 → 推送为插件 → 回到管理页启动它。
5. **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](docs/architecture.md)**(Cordis 机制、沙箱约束、数据模型、waterfall 安全规则),UI 手册见 **[docs/user-guide.md](docs/user-guide.md)**,需求逐条映射见 **[docs/requirements-trace.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)
- [x] **静态 client 打包**:已在官方双面 bundle 渠道落地(`dsh.client` + `exports["./client"]` + HTTP RPC)。官方渠道安装即获得完整 UI。
- [ ] 外部插件的精确监听边:Cordis 的监听回调不携带注册者纤维,关系图中系统插件暂显示装载状态与接口,精确"谁在听谁"等待 typert 贡献式声明 API。
- [ ] 代码编辑器为带行感的增强 textarea(Tab 缩进、模板插入),未内嵌 Monaco。
- [ ] GitHub 安装依赖环境存在 `git` 与 shell 后端。
- [ ] 事件流轮询刷新(Host→Client 无公开推送通道),高负载下有秒级延迟。
## 开发与测试
```powershell
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
MITInstall
dsh plugin --profile web add github:Tsqurt/dsh-plugin-studio
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install tsqurt-dsh-plugin-studio from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.