# dsh-herdr 架构说明书 ## 1. 架构摘要 `dsh-herdr` 是单模块、无状态、host-side 的 DSH toolkit 插件。它把经过 DSH schema 校验的工具参数确定性映射为 `herdr` CLI argv,并将 CLI 结果归一化为 DSH 可持久化的 lossless JSON。 完整可交互图: - [dsh-herdr 系统架构 HTML](./architecture/dsh-herdr-architecture.html) - [Archify 架构规格](./architecture/dsh-herdr.architecture.json) ```text DSH Web 用户 | v DSH 智能体 -> Tools Registry -> dsh-herdr 工具定义 | v CLI 安全适配 runHerdr | execFile("herdr", argv) | v herdr CLI -> local socket server | v workspace / tab / pane / agent ``` ## 2. 系统边界 ### DSH 进程内 - DSH 智能体决定何时调用工具。 - `@deepseek-ai/dsh-tools` 负责 schema、参数校验和工具结果规范。 - `dsh-herdr` 注册 24 个工具并执行 CLI 适配。 - 插件不启动客户端 UI,不注册 Web route,不维护后台 timer。 ### Herdr 运行环境 - `herdr` CLI 是插件使用的唯一集成入口。 - CLI 连接 Herdr server 的本地 socket API。 - Herdr server 持有 workspace、tab、pane、agent 和 PTY 状态。 - Herdr TUI 与 DSH 是两个独立客户端;插件不能依赖 TUI 当前焦点。 ### 包与安装 - `package.json` 的 `dsh.bundle.patch` 指向 `cordis.patch.yml`。 - DSH profile 把包加入 bundle stack 后加载 `lib/index.js`。 - `lib/` 提交到仓库,因此 GitHub 安装不需要现场编译。 ## 3. 运行时组件 ### `apply(ctx)` 位置:`src/index.ts` 职责: - 注册全部工具。 - 用 `ctx.effect` 挂载注销函数,保证热重载或卸载时清理工具。 - 不执行 Herdr 探测,不在加载阶段访问 socket。 不应承担: - 产品工作流编排。 - 缓存 Herdr 状态。 - 自动启动 server。 - 读取用户配置并改变安全默认值。 ### `tool(...)` 职责: - 包装 `defineTool`。 - 复用统一输出 schema 和渲染器。 - 保持工具描述短而明确,避免膨胀模型首轮工具目录。 ### `register(...)` 职责: - 集中执行 `ctx.tools.register`。 - 为 Cordis effect 提供可读 label。 - 确保资源随 plugin fiber 生命周期释放。 ### `runtime.ts`(`runHerdr` / `runHerdrWith`) 职责: - 使用 `execFile('herdr', args)`,避免 shell 参数插值。 - 限制 stdout/stderr 缓冲为 4 MiB。 - 支持 `DSH_HERDR_CLI_TIMEOUT_MS` 作为子进程硬超时兜底。 - 成功时尝试把 stdout 解析为 JSON;解析成功只返回 `data`,纯文本输出(如 pane/agent read)保留为 `stdout`。 - 失败时同时尝试解析 stdout 与 stderr 的 JSON;Herdr 把 `server_not_running` 等错误写在 stderr,因此错误码会进入 `data`,原始 `stderr` 仍保留。 - 把成功和失败归一化为同一个结果结构。 `command` 仅记录 `herdr `,不回显 prompt、命令正文或发送文本,避免敏感内容进入工具摘要。 `src/argv.ts` 是每个工具到 `herdr` argv 的确定性映射,`src/index.ts` 只负责 schema 注册与装配。 ### 工具定义区 职责: - 每个工具声明模型可见名称、简短描述和参数 schema。 - 将参数映射为固定 Herdr 子命令和 flags。 - 创建工具固定追加 `--no-focus`。 - 控制工具要求 pane ID 或 agent target。 - doctor 是纯只读组合检查;agent explain 也是只读诊断。 ## 4. 数据与控制流 ### 查询路径 1. 用户要求列出或读取 Herdr 状态。 2. DSH 智能体选择 list/get/read 工具。 3. DSH tools 校验参数。 4. 插件构造 CLI argv。 5. Herdr CLI 从 server 读取权威状态。 6. JSON stdout 进入 `data`;纯文本输出(如 read 工具)保留为 `stdout`。 7. DSH 智能体解释结果。 ### 创建路径 1. 用户明确 cwd、workspace 或 pane 目标。 2. create/split 工具追加 `--no-focus`。 3. Herdr 返回新对象和真实公开 ID。 4. 后续调用必须使用返回 ID,不能推测 `w1:p2` 等编号。 ### Agent 工作流 ```text pane_split -> 读取返回 pane ID agent_start -> 读取命名 agent 状态 agent_prompt -> --wait agent_wait(可选)-> idle/done/blocked agent_read -> 汇总结果 ``` `agent_start` 不创建 pane;目标必须是已存在且可用的 shell pane。 ### 命令运行路径 `pane_run` 调用 Herdr 的 `pane run`,由 Herdr 在目标 pane 输入命令并发送 Enter。插件不在自身 Node.js 进程执行该命令。安全影响发生在目标终端,因此必须显式 pane ID。 ## 5. 返回数据模型 统一结果: ```ts interface HerdrResult { ok?: boolean command?: string exitCode?: number errorCode?: string data?: JsonValue stdout?: string stderr?: string } ``` 字段约定: - `ok`:CLI 是否以 0 退出。 - `command`:去敏后的命令类别,例如 `herdr agent prompt`。 - `exitCode`:CLI 或进程启动失败的退出码。 - `errorCode`:失败时从 Herdr 错误 JSON 提取的标准错误码(如 `server_not_running`);缺失则省略。 - `data`:stdout(或失败时的 stderr)能解析为 JSON 时的结构化内容。 - `stdout`:非空原始标准输出。 - `stderr`:非空错误输出或 Node 进程错误。 结果对象不能包含 `undefined` 值;DSH lossless JSON 校验会拒绝它们。因此实现使用条件展开,只添加实际存在字段。 ## 6. 安全模型 ### 已实施控制 - 命令白名单:没有通用 CLI 透传。 - 无 shell:`execFile` 接受 argv 数组。 - 显式目标:pane/agent 控制不依赖焦点。 - 不抢焦点:create/split 固定 `--no-focus`。 - 不暴露破坏性工具:没有 close/move/swap/focus。 - R2 工具支持 `preview`;设置 `DSH_HERDR_PREVIEW_REQUIRED` 后未预览直接执行会被拒绝,并返回 `preview_required`。 - 有界等待:wait 工具支持 timeout,并可用 `DSH_HERDR_CLI_TIMEOUT_MS` 兜底。 - 敏感参数不出现在 `command` 摘要。 - CLI stderr JSON 会解析进 `data`,便于读取标准 `error.code`。 ### 信任边界 - DSH 模型可能生成错误参数,但 schema 和 Herdr CLI 会二次校验。 - 用户需要信任正在运行的 DSH profile 插件代码。 - 插件信任 PATH 中解析到的 `herdr` 可执行文件。 - Herdr server 决定实际权限和终端状态。 - `pane_run` 执行任意用户/模型提供命令,是当前最高风险工具。 ### 新增高影响工具的要求 下列能力必须先写 ADR,并设计显式确认或预览: - close、move、swap、send-keys、focus。 - server stop/start/restart。 - 任意 CLI 参数透传。 - 自动批准 agent 权限请求。 - 跨 workspace 批量操作。 ## 7. 故障模型 | 故障 | 发生位置 | 结果 | 处理 | | --- | --- | --- | --- | | `herdr` 不在 PATH | Node `execFile` | `ok: false`,进程错误 | 修复 DSH 启动环境 PATH | | server 未运行 | Herdr CLI/socket | `server_not_running` | 在真实终端启动 `herdr` | | pane/agent 不存在 | Herdr server | CLI JSON error | 重新 list,使用真实 ID | | 等待超时 | Herdr wait | 非零或 timeout 结果 | 读取状态,决定继续或停止 | | stdout 非 JSON | CLI/版本差异 | 仅 `stdout` | 仍可诊断,必要时适配新契约 | | 输出超过 4 MiB | Node buffer | 执行失败 | 使用 lines 限制或分段读取 | | 插件 schema 无效 | DSH loader | bundle 加载失败 | 构建后做 loader/schema 验证 | | GitHub 包缺 `lib/` | 安装包 | import 失败 | 构建并提交 `lib/` | ## 8. 构建与装配 ```text src/index.ts | | scripts/build.sh + DSH checkout tsc/types v lib/index.js + lib/types/index.d.ts | | package files + dsh.bundle.patch v GitHub source package | | dsh plugin --profile web add github:... v DSH web profile bundle stack ``` 构建脚本会为编译创建指向 DSH checkout 的 `node_modules` 符号链接。这些目录被 `.gitignore` 忽略;不能提交本机绝对链接。 ## 9. 架构约束 后续实现必须保持: 1. Herdr CLI 是唯一运行集成边界,除非另有 ADR 决定改用稳定 API client。 2. 插件保持无状态,不复制 Herdr session 状态。 3. 每项能力有独立工具 schema,不开放通用透传。 4. 创建默认不抢焦点。 5. 编译产物随源码提交。 6. 错误返回结构化数据,不把可预期 CLI 错误升级为 DSH 工具崩溃。 7. README 面向用户;详细设计放在 `docs/`。