# 架构说明 (architecture.md)

## 1. 为什么需要这个插件

DSH 的插件系统是 Cordis 插件 + Agent preset/session 组合。对新手来说，插件"看不到、摸不着"：
只能靠反复对话迭代。工作室把运行时反成**可见状态**（事件、插件、装载表、签名）并补上
**低代码开发**与**持久化管理**。

## 2. 依赖的 Cordis / DSH 机制（全部来自运行时真实契约）

| 机制 | 用途 | 位置 |
| --- | --- | --- |
| `internal/dispatch` (mode, name, args, thisArg) | 捕获**所有**非 internal 事件分发（emit/waterfall/serial/parallel/bail） | `@deepseek-ai/cordis` EventsService.dispatch |
| `internal/listener` | 统计每个事件的监听器注册数 | EventsService.on |
| `internal/plugin` / `internal/status` | 插件生命周期监控（pending/loading/active/failed/unloading/disposed） | Fiber |
| `typert.listPackages()` | 反射所有包的服务/事件模型（签名/JSDoc）——**一览表与下拉的反射源** | dsh-typert-registry |
| `loader.entries()/update()/create()/remove()` | 系统分区装载表 + 启停/注册/卸载（持久化到部署配置） | cordis-plugin-loader |
| `pluginInventory.list()` | 只读装载表快照 | dsh-host-plugin-inventory |
| `settings.prepareDocument()` | 定位 DSH home（`<home>/settings.yaml` 的目录 = DSH home） | dsh-settings-file |
| `fs` (resolve/readText/writeText) | 持久化（原子写 + 自动建目录） | dsh-fs-local |
| `shell` (resolve/run) | `git clone` 安装 GitHub 插件 | dsh-shell |
| `timer` | 触发器定时器/监控窗口 | cordis-plugin-timer |
| 客户端 `slots` (sidebar.footer.action / shell.overlay) | 拼图图标入口 + 全屏面板 | dsh-client-ui-* |

## 3. 动态插件沙箱约束（Host）

动态 Host 半部运行在 `node:vm` 沙箱并拿到**受限 ctx 门面**：

- 只能 `ctx.on / ctx.once / ctx.provide / ctx.effect` + 注入服务属性 + `ctx.get(name)` + `ctx.tools.*`
- **没有** `ctx.emit`、`ctx.plugin`、`ctx.registry` —— 因此工作室"凭空产生事件"通过
  **自有 bus**（`api.bus.emit` → 走进事件流 ring，标记 mode=bus）实现；对外"触发"则调用
  真实服务（web/timer 等）。这是一个有意的工程折衷：宁可 bus 事件可观测，也不绕过沙箱。
- `new Function` 在沙箱内可用（vm realm 标准内建）→ 监听器脚本/触发器脚本按此编译执行，
  全程 try/catch + 错误记录（rt.errors），waterfall 出错自动回落到 `next()`（默认行为）。

## 4. 数据模型（持久化，均为 JSON，原子写）

```
<storeRoot>/
├── catalog.json      { version, plugins: [ { id, name, kind: listener|trigger|external,
│                       hooks: [{ event, mode, params:[{name,desc}], body, enabled }],
│                       trigger: { everySeconds, body }, autoStart, source, entryId } ] }
├── sets.json         { version, sets: [ { id, name, members: [{kind: plugin|set, id}] } ] }
├── state.json        { version, cap, lastSnapshot: { at, loader:{id:enabled}, catalog:{id:bool} },
│                       restorePrompt, autoSnapshot }
├── annotations.json  [ [event, "a, b, c", "a: 说明; b: 说明"], ... ]   ← 需求中的"表头"格式
├── ctx-templates.json [ { label, code } ]                              ← 需求中的 ctx.get(...) 模板
├── dev-packages.json { version, packages: [...] }                      ← 开发中插件包（权威副本）
└── dev-packages/<id>/manifest.json + listeners/<hookId>.js             ← 与本地文件对应（镜像）
```

`storeRoot` 解析顺序：workspace 相对 `dsh-plugin-studio-data/`（尊重部署 fs 写策略）
→ DSH home `~/.dsh/dsh-plugin-studio/`（由 settings 文档路径推导）→ 兜底。

## 5. 监听器编译与 waterfall 安全规则

```
编译: new Function('ctx','emit','console','next',
       'return async function __hook(' + params.join(',') + ') { ' + body + ' }')
包裹(waterfall): 传入 wrappedNext 记录 called；body 未调用 next 且返回 undefined → 自动 next()；
                 body 抛错 → 记录 + next()（绝不破坏 DSH 默认行为）
其他模式: try/catch 记录错误，返回值透传
```

## 6. 插件一览表的"反射"语义

- **事件目录**：typert 反射（包 → 事件名/mode/签名/说明）∪ 持久化 annotations（覆盖/补充说明），
  下拉列表同源。
- **插件 → 事件边**：工作室自己的 catalog 插件是精确已知的；**外部插件监听关系无法静态反射**
  （Cordis `internal/listener` 回调不携带注册者纤维，`hook.ctx` 恒为事件总线所有者）。
  当前实现为：精确边 = studio 插件；系统插件显示装载状态+接口（package 反射），
  精确"谁在监听"留作后续（typert 贡献式声明 API）。
- **插件生命周期**：`internal/plugin`/`internal/status` → 每个 fiber 的 uid/name/state。

## 7. loader 与"系统/动态分区"

- 系统分区 = loader entries：`loader.update(id, {disabled})` 切换（写回部署配置，UI 有提示）。
- 动态分区 = studio catalog：启停=`ctx.on` 注册/注销；GitHub 安装=`shell git clone` 到
  `installed/<slug>`，再 `loader.create({name: path})` 装载（**该步骤执行真实模块导入**，
  UI 明确标注风险）。
- 组合嵌套：set.members 支持 `{kind:'set'}`，flatten 带环检测；状态 = 全部启动绿 / 全停红 / 部分黄。

## 8. 状态恢复

`recordTool 当前启停` → `state.json.lastSnapshot`。工作室每次启动时对比当前 loader+catalog
状态，不一致→面板顶部横幅"是否恢复？"（恢复/忽略），恢复=批量 apply。

## 9. 限制与后续

- 客户端生产打包需要 DSH monorepo 的 web 构建管线（`dsh.client` 扫描/umd 化）；
  仓库内以"动态插件引导 + dist 单文件"形式交付完整功能，loader 生产入口为 `dist/host.mjs`。
- Git 安装依赖环境存在 `git` 与 shell 后端（Windows 下为 bash/pwsh 之一）。
- 代码编辑器为「带行感的 textarea + tab 插入 + 模板插入」，未内嵌 Monaco（体积/产物约束）。
