# 架构与生命周期 > 源码一级目录已从 13 个收敛为 10 个,当前目录和迁移映射见[解析器解耦重构说明](<解析器解耦重构说明.md>)。本轮仅调整目录归属,Plugin/Source 模型版本仍为 `5.0.0`,各内置协议实现版本仍为 `1.1.0`,运行逻辑、执行 profile 和用户 profile 均不变;本文保留完整存储及生命周期背景。 ## 1. 五层边界 ```text 第一层:Source 获取 Git / Directory / Archive │ ▼ 第二层:parser/catalog + parser/registry Marketplace、单插件识别、paths 兜底、Plugin 位置 │ ▼ 第三层:protocols 独立模块 + parser/plugin 归一化 Claude Code / Codex / Agent Plugins 的组件与元数据 │ ▼ 第四层:Plugin manager/runtime 安装、启停、运行状态、更新、卸载、父生命周期 │ ▼ 第五层:runtime/adapters Compat* → DSH Skill/MCP/Hook ``` 职责不能倒置:Source 获取层不理解 Skill;[parser/catalog.ts](<../src/parser/catalog.ts>) 编排来源发现,不解析具体能力;[runtime/adapters](<../src/runtime/adapters/>) 不读取 Marketplace 或旧格式;runtime 不根据生态分支执行。加载器归 [manager/plugin-loader.ts](<../src/manager/plugin-loader.ts>),继续负责按显式引擎端口加载与重扫。 ## 2. Source、Plugin 与能力 ```text 一个 Source │ └── 1..N Plugin │ ├── 0..N Skill ├── 0..N MCP ├── 0..N Hook ├── 0..N Agent └── 0..N Command ``` Source 是内容获取与更新单位。Plugin 是安装、启用、禁用、运行和卸载单位。内部能力永远不成为独立安装项。 ### 2.1 Source 类型 - Git:记录 URL、ref 和最终实际 commit。 - Archive:记录本地路径和 SHA-256。 - Directory:保存目录快照和内容 revision。 Source 的 `ownership`: - `explicit`:由 `sourceAdd()` 创建,引用归零仍保留。 - `implicit`:由 `install(input)` / `prepareInstall(input)` 创建,引用归零自动回收。 ### 2.2 Plugin 位置 `CompatPluginLocation` 支持: - `source-path`:Source `package/` 内的相对路径; - `git`:Marketplace 指向外部 Git; - `git-subdir`:外部 Git 的子目录; - `npm`、`archive`、`unsupported`:当前可被识别并诊断,不会伪装成已支持。 外部 Git Plugin 放在 `external//`,仍属于 Source 的版本和更新事务。 ## 3. 存储模型 包身份已改为 `@rvaim/dsh-plugin-bridge`,数据 namespace 仍为 `dsh-compat`,存储根目录保持如下布局;本次更名不迁移用户数据或修改 profile,详见[包更名说明](<包更名说明.md>)。 ```text $DSH_HOME/dsh-compat/ ├── sources// │ ├── source.json │ ├── scan.json │ ├── package/ │ └── cache/ ├── installed// │ ├── install.json │ ├── state.json │ ├── scan.json │ ├── data/ │ └── cache/ ├── external// ├── temp/ └── locks/ ``` ### 3.1 Source 事实真源 `sources//package/` 保存完整原始 Source。多 Plugin 共享同一份内容,避免每个安装项复制源码,也保证共享 Source 的所有 Plugin 使用同一个版本。 ### 3.2 Plugin 安装记录 `install.json` 只保存: ```ts { pluginId, sourceId, sourcePluginId, location, materialization?, installedAt, updatedAt? } ``` `state.json` 只保存整个 Plugin: ```ts { enabled: boolean, priority: number } ``` 禁止出现按 Skill/MCP/Hook 分拆的启停开关。 ### 3.3 缓存失效 - Source `scan.json` 带 `parserVersion`。 - Plugin `scan.json` 同时带 `parserVersion` 与 `sourceRevision`。 - 任一版本不匹配即从原始 Source 重新扫描。 - `scan.json` 从来不是事实真源。 ## 4. Source 定位流程 ```text 取得 Source 目录 │ ├─ 发现 Claude Marketplace ─┐ ├─ 发现 Codex Marketplace ──┼─ 合并候选 │ │ └─ 均不存在 ┘ │ ├─ 有 paths:逐项验证 └─ 无 paths:只检查根目录是否为单插件 ``` 存在 Marketplace 时不会递归猜测未列出的目录,也不会自动安装全部候选。混合 Marketplace 只有在同一规范化 ID、同一真实目录且关键语义一致时才能合并。 ## 5. Plugin parser 传统规则基线为 Codex 0.163.0 与 CC 2.1.304 / 2026-10-20 官方文档。Agent Plugins 1.0.0 独立接入阶段的 Plugin parser / Source parser 为 `3.1.0`;后续解析器解耦已将统一模型升级为 `5.0.0`,各内置协议实现版本为 `1.1.0`,本次目录搬迁保持这些版本。规范校验、固定组件发现和失败隔离与传统方言分开;根清单优先,不合并旧清单/目录的核心能力。详见[独立格式说明]()。平台先完成显式配置替换/追加、Marketplace 事件覆盖和同平台 MCP 覆盖,再进入跨方言等价合并与跨插件冲突判断;不能直接用一套公共去重替代上游覆盖规则。完整传统规则见[传统插件兼容矩阵](传统插件兼容矩阵.md)。 Hook IR 记录合法但运行未适配声明、源 JSON Pointer、执行方式、失败策略及 matcher 分类;MCP/Skill IR 记录不能兑现的运行限制,由 manager 和 adapter 双重拒绝。Marketplace 内联 JSON 引用使用 `base:source`,当前运行布局提供 Source 根,避免插件嵌套、暂存提交或更新后文件定位改变;其余引用默认以插件根为基准。运行接入使用当前 `agent/created`、Shell `execute().result()` 和 session projection;exec-form Hook 经 subprocess 直接传 argv,不能回退 Shell。 Plugin parser 是兼容编译层,负责: - 识别 Claude Code/Codex 清单和默认目录; - 验证字段、路径、事件、handler、transport; - 将路径变量编译成结构化运行值; - 保留凭据引用与延迟 JSON 指针; - 产生统一 `CompatPlugin` 与结构化诊断; - 合并等价能力,拒绝语义冲突。 `CompatPlugin` 是 adapters 的唯一 Plugin 输入: ```ts interface CompatPlugin { id: string name: string version?: string defaultEnabled?: boolean sourceId: string sourcePluginId: string formats: Array<'claude-code' | 'codex'> skills: CompatSkill[] mcpServers: CompatMcpServer[] hooks: CompatHook[] commands: CompatCommand[] agents: CompatAgent[] diagnostics: CompatDiagnostic[] manifestFiles: string[] } ``` ## 6. 安装事务 ```text 仓库锁 + Source/Plugin 锁 │ ▼ 创建 temp/ │ ▼ 获取并安全检查完整 Source │ ▼ parser/catalog 定位候选 │ ▼ 用户/调用方选择 1..N Plugin │ ▼ 物化外部 Git、逐个 Plugin parser │ ▼ 兼容与跨 Plugin 名称冲突检查 │ ▼ 一次性提交 Source 与全部 installed 记录 │ ▼ 按 enabled 策略启动父实例 ``` 一次安装多个 Plugin 时,静态阶段整体成功或整体失败。不会产生只安装了一部分的状态。 ### 6.1 Web 两阶段选择 Web 无法在一次同步调用中阻塞等待用户勾选,因此使用分步但不重复拉取的流程: ```text beginAdd(source) ↓ 获取、解析并固定 implicit Source 快照 ├─ 单插件:直接 install(sourceId) └─ 多插件:返回 requestId + candidates ↓ 用户勾选 / 取消 ├─ completeAdd:从同一 sourceId 安装 └─ cancelAdd:回收无引用临时 Source ``` 请求 ID 只保存在 Host 内存中,默认十分钟过期;页面卸载和插件停止也会触发清理。同一 Source 出现并发选择时,临时 Source 的清理责任会转交给最后一个请求,避免一个页面取消后破坏另一个页面仍在使用的快照。 ## 7. Web 设置与 Remote 边界 Web 页面是 DSH Client 插件。Host 网关归 [web/host/gateway.ts](<../src/web/host/gateway.ts>);浏览器入口、设置页、样式、locale 和类型声明统一归 [web/client/](<../src/web/client/>): - `package.json#dsh.client` 把浏览器 bundle 加入 DSH Client 启动图; - `settings.section` Slot 的 section ID 为 `plugin-bridge`,中文页名“插件桥”、英文页名“Plugin Bridge”,沿用设置 Shell 的导航与内容容器; - Client locale namespace 为 `settings.pluginBridge`; - `Modal`、`Input`、`Button`、`Toast`、图标、locale 与 `--dsw-*` Token 全部来自 DSH Client; - Host 侧 `CompatWebGateway` 继承 `TypertRemoteService`,以 `pluginBridgeAdmin` RPC namespace 生成 `./typert` 与 `./remote` 严格 schema 产物; - Client 动态挂载本包 Remote contribution,通过 `/api` 调用当前进程中 `ctx.pluginBridge` 提供的 `CompatService`;公开服务与 RPC 均不保留旧 namespace 别名。 `@rvaim/dsh-plugin-bridge` 本身的安装、升级和删除会改变 Host/Client 插件图,按 DSH Client Modules 的缓存边界需要重启 profile。设置页中添加、启停和卸载的旧 Plugin 只改变 `CompatRuntimeManager` 的子生命周期,不改变 Client 插件图,因此在当前进程中立即生效。 ## 8. 共享 Source 更新 共享 Source 更新是单个事务: ```text 锁 Source + 全部受影响 Plugin │ ▼ 新版 Source 获取到 temp │ ▼ 重新读取 Marketplace │ ▼ 确认每个已安装 Plugin 仍存在 │ ▼ 重新解析全部受影响 Plugin │ ▼ 检查全局能力冲突 │ ▼ 停止旧父实例 │ ▼ 原子切换 Source 与外部物化内容 │ ▼ 写新扫描记录并启动新版 │ ├─ 成功:清理备份 └─ 失败:恢复旧 Source、旧记录、旧运行实例 ``` 新 Marketplace 出现的 Plugin 只进入可安装列表,不会自动安装。已安装 Plugin 消失时默认拒绝更新,不猜测改名或迁移。 `update(pluginId)` 对独占隐式 Source 可直接执行;对共享 Source 必须由调用方显式确认影响范围。 ## 9. 运行实例与 adapters 每个启用 Plugin 创建一个父 Cordis 运行实例: ```text Plugin 父实例 ├── Skill Provider ├── MCP 子插件 1..N ├── Hook 监听与运行集合 ├── Command adapter(当前版本不执行) └── Agent adapter(当前版本不执行) ``` 父实例停止时: - Skill Provider 撤销; - MCP 子 Fiber 递归释放,工具注销并断开连接; - Hook 监听撤销,未完成运行收到取消并等待清理; - 所有 effect/disposer 一起释放。 如果部分能力失败,状态可为 `degraded`;如果全部可运行能力失败,父实例被立即 dispose,避免部分注册残留。 ## 10. 冲突与稳定顺序 启用前检查活动 Plugin 的全局能力名,包括 MCP `serverName`、Skill 名称及已实现的其他可见名称。冲突产生 `error`,不会自动加前缀或覆盖。 恢复顺序固定为: ```text priority 升序 → pluginId 字典序 ``` ## 11. 卸载与 Source 回收 ```text 锁 Plugin ↓ 阻止新运行请求 ↓ dispose 父实例并等待子资源 ↓ 事务删除 installed/ ↓ 统计 Source 引用 ├─ 仍被引用:保留 Source ├─ 引用为 0 且 explicit:保留 Source └─ 引用为 0 且 implicit:删除 Source 与 external 内容 ``` DSH 已经持久化的历史会话、工具调用和 Hook 结果属于历史事实,卸载不会回删。