# DESIGN — 设计溯源
本插件是一次有明确出处的组合式设计,不是闭门造车。三个来源、三种角色:
## 1. [codex-bridge-chatgpt](https://github.com/anightmonarch/codex-bridge-chatgpt)(MIT)— 机制蓝本
职责拆分哲学原样保留:**网页 AI 提方案,本地 Agent 验证、修改、测试**。以下机制自该项目移植并适配 dsh(代码为本项目原创 MIT 实现,字段命名按 dsh 语境调整):
| 机制 | 原项目 | 本项目 |
| --- | --- | --- |
| Context Packet 契约(标记 + 六章节 + 3K token 上限) | `validate-handoff.mjs` | `validate.mjs`(同构移植,新增 gitlab/slack token 模式) |
| Reasoning Result 契约(七章节 + packet_id 绑定) | 同上 | 同上 |
| 本地采纳门(accepted / rejected / deferred + 本地证据) | references/ + SKILL.md | `references/adoption-gate.md` |
| 运行回执(schema_version 2 + 哈希绑定 + 隐私审查字段) | `codex_model` / `chatgpt_model` | `local_model` / `web_model`,新增 `target` 字段 |
| complete 闸门(全字段 passed + 产物哈希重算) | 同上 | 同上 |
| 版本化风险授权状态机(disclosure_version + 精确确认令牌 + 原子写) | `automation-consent.mjs` | `consent.mjs`(确认令牌与状态路径改为 dsh 约定) |
| Doctor 分层预检(安装层 → 授权层 → 浏览器层) | `doctor.mjs` + references/doctor.md | `doctor.mjs` + `references/doctor.md` |
| 传输纪律:只发一次、不确定即停、可见取回、不碰私有端点 | browser-transport.md | `references/transport.md` + 各目标适配器 |
核心差异是**传输层**:原项目依赖 Codex 桌面 App 的 in-app Browser;dsh 本身内置
CDP 浏览器工具(`browser_launch` 复用日常 Chrome 登录态),登录问题天然缓解,
因此预检简化为 `NEEDS_BROWSER / NEEDS_LOGIN / NEEDS_MODEL_SELECTION / NEEDS_SITE_PERMISSION`,
并把"只许 in-app Browser"改写为"只许 dsh 内置调试实例、只做用户可见等价操作"。
## 2. [ShunCode](https://github.com/ZS520L/shuncode) — 设计理念参照
ShunCode(README-only 理念仓库)对本项目的影响是取向而非代码:
- **桥接复用网页订阅**:不把网页 AI 当复制粘贴对象,而是受控、契约化的协作方;
ShunCode 用 MCP Bridge 把本地工作区暴露给网页 AI,本项目选择反方向——
浏览器把 Packet 推给网页 AI、把结论带回本地验证(无需公网隧道,攻击面更小)。
- **权限分层**:Ask / Plan / Code 的分层思想体现在 consent → preflight → adoption
三道门上:未授权不碰浏览器,未预检不发 Packet,未过采纳门不动仓库。
- **按需上下文**:Packet 只装"会改变决策的信息",默认摘要、必要时最小摘录,
与 ShunCode 的上下文经济原则一致。
- **安全边界白纸黑字**:环回/最小化/脱敏/免责声明不弱化,回执可审计但明确
"哈希只绑定本地产物,不是远端模型证明"。
## 3. dsh 插件体系 — 宿主事实
- 技能注册:`ctx.skills.register(...)`(`@deepseek-ai/dsh-skill` 运行时技能,
`resourceBase: { kind: 'directory' }` 让 references/scripts 随技能目录解析)
- 提示注入:`ctx.systemPrompt.section(...)`;生命周期:`ctx.effect(fn, label)`
(与 `dsh-vision` 插件同款用法)
- 打包分发:`dsh.plugin.json` + `dsh plugin --profile web add
`
- 零依赖、零构建:`lib/` + `skills/` 即产物(`dsh-versions` 先例),全部确定性逻辑
用 node:test 覆盖
## 4. 传输层 v2(0.2.0)— 从散文到可执行代码
0.1.0 的传输只有 `transport-chatgpt.md` 里给 agent 照抄的 `browser_eval` 片段。
对照真实工具面(`dsh-browser-control@0.1.0`)发现两处硬伤,片段根本跑不通:
1. **每次求值都重新导航**:`browser_eval {url, expression}` 底层 `drive.mjs` 先
`Page.navigate` 再求值——发送后的标签页会被后续调用清掉,无法分步操作同一页面。
2. **异步结果丢失**:`Runtime.evaluate` 未设 `awaitPromise`,文档里所有
`(async () => …)()` 只返回 `"Promise"` 字符串——composer 等待、流式结束检测、
剪贴板读取全部拿不到值。
3. **启动器仅 macOS**:`launch.sh` 硬编码 `$HOME/Library/...`,Windows 上
`browser_launch` 必然失败,流程永远停在 `NEEDS_BROWSER`。
0.2.0 的对策:插件自带**零依赖 CDP 驱动**(`scripts/lib/cdp.mjs`,Node ≥ 22 原生
WebSocket),附着到 dsh 调试 Chrome 同一个 :9222 实例,一次连接内完成
navigate → `evaluate(awaitPromise)` → 截图;页面阶段(probe/send/fetch)是
`scripts/lib/page-chatgpt.mjs` 里可测试的表达式构建器,选择器多候选、失败映射
`why` 码,禁触 Cookie/存储/网络由单元测试守卫。纪律不降级:仍是一次发送、
可见等价操作、经站点自身 copy 控件 + 剪贴板取回(`Browser.grantPermissions`
授权,失败降级为可见 DOM 文本并如实标注 `extraction: "dom"`)、不碰私有端点。
传输驱动的角色边界:**不接管生命周期**——macOS 上仍优先 dsh `browser_launch`
(登录态复用);Windows 兜底 `transport.mjs launch` 用专用独立 profile 启动,
不拷贝任何凭据文件(登录态拷贝属于 browser-control 的职责,本插件的纪律禁止
触碰 Cookie 等凭据文件),首次在该可见窗口人工登录一次。
## 何时扩展新目标
新增一个网页 AI 目标 = `scripts/targets-table.mjs` 加一行(target.mjs 与
validate.mjs 共享此表,不会漂移)+ `scripts/lib/page-.mjs` 写该站点的
选择器集与阶段表达式 + `references/transport-.md` 按适配器契约写清
登录探测 / 模型校验 / 一次性发送 / 可见取回 / 阻断态 / 证据。
Packet、Result、采纳门、回执契约不需要任何改动。
页面模块需自带 `INPUT_MODE`(`paste-event` 或 `insert-text`)与对应阶段函数:
`paste-event` 导出 `buildSendExpression`,`insert-text` 导出
`buildComposerFocusExpression` + `buildSendAfterInsertExpression`;两者都必须导出
`buildProbeExpression` 与 `buildFetchExpression`。`tests/transport.test.mjs` 会断言
「表里的目标」与「page-*.mjs 文件」一一对应,并逐个检查导出契约——少写一边 CI 就红。
## 5. 0.4.0 审计:把「宣称」与「机制」分开
0.4.0 之前,README 的多条承诺(采纳门、发送前脱敏、consent 闸门)**只有散文、
没有代码**。根因是结构性的:`dsh.plugin.json` 声明 `"tools": []`,插件不注册任何
工具,因此**无法拦截**模型对 `pwsh`/`edit`/`write` 的调用——采纳门在设计上就只能
是「请求模型遵守」。
审计(一次 README-vs-实现 走查 + 两次独立子审计:采纳门绕过、敏感外发)的处置原则:
- **能变机制的立刻变机制**:consent 前置闸门(`open|probe|send`)、发送前凭据扫描
(`send` 命中即拒)、`complete` 复扫产物内容、采纳记录非空要求、面板端口解析修复。
- **变不成机制的诚实标注**:README 增「强制执行 vs. 纪律约定」表,
`adoption-gate.md`、`reasoning-result.md` 明写「校验通过=结构合规,不等于安全」。
- **顺手修掉的确定性缺陷**:扫描器从 7 条扩到 20 类(JWT/云密钥/连接串/webhook/.env
原先全漏,且 `sk_live_` 与 `\b` 两处正则本身写错);测试守卫从 5 字符串扩到覆盖
`sendBeacon`/`cookieStore`/`WebSocket`/`import()` 等绕过面;`checkNodeRuntime`
从死配置变成真闸门;`.gitignore` 补上运行产物。
教训记在这里:**任何「我们把 X 关在门外」的表述,都要能指向一行拒绝执行的代码**,
否则就写成纪律,并说明它依赖遵守。