dsh-bridge
Officialby SoftDefender
README.md
# dsh-bridge
本机 **DSH ↔ ChatGPT/Codex 会话** MCP 双工桥:让 DeepSeek Harness(DSH)会话与 ChatGPT 桌面应用 / Codex CLI 中的本地 Codex 会话之间实现**双向消息、协同开发与对方能力调用**。
- 零外部依赖(Node ≥ 22.5 内置 `node:sqlite`、`WebSocket`、`fetch`)
- 单进程常驻 coordinator:SQLite 持久队列、会话所有权状态机、统一投递决策(steer / new_turn / queue / auto)
- **writer 租约**:桥仅在执行 turn 期间持有会话 writer,结束自动交还(archive→unarchive,实测唯一 per-thread 释放手段)——桌面应用可无感打开桥会话
- **临时介入循环**:DSH 托管的长任务可随时 `session_pause` 让用户原生接管,操作完 DSH 下一条指令自动收回托管
- 对桌面当前持有的活跃会话**不抢占**:自动降级排队(mailbox),桌面释放后经 `session_claim` 切换
- 同一 MCP server 同时供 ChatGPT 桌面应用与 Codex CLI 加载(共享 `~/.codex/config.toml` 的 `[mcp_servers.*]` 段)
设计基线见仓库内文档:
- [`dsh-codex-mcp-bridge.md`](./dsh-codex-mcp-bridge.md) —— 方案(评审稿,含实测勘误)
- [`dsh-codex-mcp-bridge-implementation.md`](./dsh-codex-mcp-bridge-implementation.md) —— 实施细节(协议实测值、通知/RPC 目录、边界矩阵)
- [`docs/chatgpt-usage-prompt.md`](./docs/chatgpt-usage-prompt.md) —— 粘贴给 ChatGPT 会话的使用说明 prompt
## 架构
```text
ChatGPT 桌面任务 Codex CLI / bridge 控制台
│ MCP stdio │ CLI / unix socket
▼ ▼
┌──────────────────────────────────────────────────────────┐
│ dsh-bridge Coordinator │
│ - 统一 session interface(steer/start/queue/auto) │
│ - 会话注册表 / writer 租约 / active execution │
│ - SQLite mailbox、回执、事件游标、熔断 │
└───────────────┬──────────────────────────┬───────────────┘
│ │
DshSessionAdapter CodexHostAdapter
HTTP RPC(事件流 WS) JSON-RPC over WS
│ │
▼ ▼
DSH Web :3080 bridge 管理的 Codex App Server
```
核心模块:
| 组件 | 路径 | 说明 |
|---|---|---|
| Coordinator | `src/coordinator.mjs` | 单例进程:DB、registry、mailbox、双 adapter、unix socket IPC、`/healthz`、KILLSWITCH |
| DshSessionAdapter | `src/adapters/dsh_http.mjs` | DSH Web HTTP RPC(`session.list/history/prompt/cancel/create`;`events.mux` 为 WS 流) |
| CodexHostAdapter | `src/adapters/codex_ws.mjs` | 独立 Codex App Server(`thread/*`、`turn/*`、settings/name/release) |
| Delivery 决策 | `src/adapters/host.mjs` | steer / new_turn / queue / auto 与 writer 租约、介入循环的单一实现 |
| MCP stdio 前端 | `src/frontends/mcp_stdio.mjs` | 供 ChatGPT 桌面 / Codex CLI 加载(15 个工具) |
| DSH CLI | `src/frontends/cli.mjs` | `dsh-bridge status/send/guide/…`(与 MCP 同构) |
| 运维 | `src/ops/backup.mjs` | SQLite 每日快照(VACUUM INTO,保留 7 份) |
## MCP 工具面(15 个)
| 工具 | 作用 |
|---|---|
| `bridge_status` / `capabilities` | 桥与双 host 状态、能力与限制 |
| `session_list` / `session_history` | 枚举会话(dsh/codex)、读历史(dsh 带 seq 游标) |
| `session_send` | 普通消息:目标空闲→新 turn,运行中→排队 |
| `session_guide` | 引导/指令:`auto`(默认)/ `steer`(追加到执行中 turn,不打断工具)/ `new_turn` / `queue` |
| `session_cancel` | 取消:dsh=session.cancel;codex=turn/interrupt |
| `session_pause` | 临时介入:中断当前 turn + 立即交还 writer(桌面原生接管),DSH 下条指令自动收回托管 |
| `session_settings` | codex:切换模型 / 思考程度(影响后续 turn) |
| `session_create` | 新建会话:codex=桥会话(`threadSource=user`,桌面侧栏可见);dsh=新建空白 DSH 会话(queue 消息自动启动) |
| `session_ownership` | codex:claim / release / status(不抢占桌面活跃会话) |
| `session_receive` / `session_ack` | mailbox 收件箱轮询与回执 |
| `task_submit` / `task_report` | 结构化任务提交(`--from` 溯源)与结果回传 |
## 快速开始(Linux)
```bash
git clone https://github.com/SoftDefender/dsh-bridge.git
cd dsh-bridge
node --version # 需要 ≥ 22.5
# 1) 启动 coordinator(首次启动生成默认配置 ~/.codex-bridge/)
node src/coordinator.mjs &
curl http://127.0.0.1:45170/healthz # 健康检查:双 host up
# 2) CLI 冒烟
node src/frontends/cli.mjs status
```
> 默认 `codex.cli_path = /usr/lib/chatgpt/resources/codex`(ChatGPT 桌面内置二进制);
> `code_mode_host = true`(false 时桥会话无法执行 shell,勿改回)。
> 纯 Codex CLI 环境可将 cli_path 改为 `codex`。其他参数在 `~/.codex-bridge/bridge.toml` 调整。
### 生产运行(systemd user unit)
```bash
cp systemd/dsh-bridge.service ~/.config/systemd/user/ # 把 ExecStart 的 node 换成绝对路径
systemctl --user daemon-reload && systemctl --user enable --now dsh-bridge
cp systemd/dsh-bridge-backup.{service,timer} ~/.config/systemd/user/
systemctl --user enable --now dsh-bridge-backup.timer # 每日 03:00 SQLite 快照(保留 7 份)
```
- 熔断:`touch ~/.codex-bridge/KILLSWITCH` 即拒绝全部投递(`quota-exceeded`),删除即恢复。
- 日志:`~/.codex-bridge/log/bridge.log`(jsonl,含 App Server 通知流)。
## 接入 ChatGPT 桌面应用 / Codex CLI
两者共用同一配置段(追加到 `~/.codex/config.toml`):
```toml
[mcp_servers.dsh-bridge]
command = "node"
args = ["/home/<user>/dsh-bridge/src/frontends/mcp_stdio.mjs"]
startup_timeout_sec = 30
[mcp_servers.dsh-bridge.env]
DSH_BRIDGE_SOCKET = "/home/<user>/.codex-bridge/run/coordinator.sock"
```
- **ChatGPT 桌面应用**:重启后自动加载;每个会话独立 spawn 一个 `mcp_stdio` 前端,**工具面是会话创建时刻的桥版本快照**——升级桥后需**新开会话**(或重启应用)获取最新工具集(当前 15 个)。给会话粘贴 [`docs/chatgpt-usage-prompt.md`](./docs/chatgpt-usage-prompt.md) 即可让它按约定协同 DSH 会话。
- **Codex CLI**:同段自动生效;也可 `codex mcp add dsh-bridge -- node <abs-path>/src/frontends/mcp_stdio.mjs`。
- coordinator 必须先于任一端运行(`mcp_stdio.mjs` 是无状态薄前端,仅转发 coordinator.sock)。
## 协同操作总览(实测矩阵)
**ChatGPT → DSH**(DSH 会话执行长任务期间全程可操作):
| 操作 | 方式 | 状态 |
|---|---|---|
| 发送/启动 | `session_send`、`session_guide new_turn`;空白会话 queue 消息即自动启动 | ✅ 实测 |
| 执行中引导 | `session_guide delivery=steer`(注入执行中回合,不打断工具) | ✅ 实测 |
| 排队 | `session_guide delivery=queue` | ✅ 实测 |
| 取消 | `session_cancel`(`session.cancel`,turn/end aborted) | ✅ 实测 |
| 历史/列表/新建 | `session_history` / `session_list` / `session_create` | ✅ 实测 |
| 切换模型/思考程度 | 无会话级 RPC(仅创建时 `agent_preset`) | ❌ 协议限制 |
**DSH → ChatGPT(桥托管 codex 会话)**:
| 操作 | 方式 | 状态 |
|---|---|---|
| 执行中 steer | `session_guide delivery=steer`(同 turn 注入) | ✅ 实测 |
| 取消/暂停 | `session_cancel`、`session_pause` | ✅ 实测 |
| 排队 | mailbox 自动降级(`desktop-owned`/busy) | ✅ 实测 |
| 模型/思考程度 | `session_settings`(影响后续 turn) | ✅ 实测 |
| 桌面原生接管 | `session_pause` → 用户操作 → DSH 下条指令自动收回 | ✅ 实测 |
**执行模型(混合)**:DSH 发起的自主长任务由桥宿主执行(配合暂停介入);用户在场任务经 `task_submit` 投递,由桌面会话原生执行。两者共用同一 mailbox/任务协议,可随时切换。
## CLI 用法
```bash
alias dsh-bridge='node /home/<user>/dsh-bridge/src/frontends/cli.mjs'
dsh-bridge status # coordinator + 双 host + 队列
dsh-bridge session create --host codex|dsh --cwd /path [--title 任务名] [--agent-preset p]
dsh-bridge session claim <threadId> | release <threadId> | pause <threadId>
dsh-bridge session cancel <dsh:|codex:><id> # 取消运行中会话
dsh-bridge session list --host dsh|codex # 枚举会话
dsh-bridge session settings --to codex:<id> [--model m] [--effort e]
dsh-bridge history --to <dsh:|codex:><id> [--limit N]
dsh-bridge guide --to codex:<threadId> --message "优先处理测试失败" --delivery auto
dsh-bridge send --to dsh:<sessionId> --text "hello"
dsh-bridge receive --to codex:<threadId> --status all # 收件箱(含排队消息)
dsh-bridge ack --message-id <id> --reply "收到"
dsh-bridge task submit --to codex:<threadId> [--from dsh:<sessionId>] --kind task --title T --brief B
dsh-bridge task report <taskId> --status done --summary S
dsh-bridge capabilities
```
目标格式:`dsh:<DSH sessionId>` 或 `codex:<threadId>`。
## 测试
```bash
node --no-warnings --test test/unit.test.mjs # 单测(无需外部依赖)
node --no-warnings --test test/integration_codex.test.mjs # 真实 App Server 集成(慢,少量模型配额)
```
已覆盖(实测断言):`thread/start(threadSource=user) → turn/start(sleep) → steer → STEER_OK` 且 rollout 真实执行过 exec(防假绿);已存在会话再次引导;**确定性模拟桌面宿主**(第二 App Server 持有活跃 turn)→ 降级排队 + claim 拒绝;writer 租约交还与第二宿主无感接管;`session_meta.thread_source=user` 侧栏可见性断言。
## 与 ChatGPT 原生协同机制的定位差异
| | dsh-bridge | ChatGPT 桌面 collab agent |
|---|---|---|
| 范围 | 跨应用:DSH ↔ ChatGPT/Codex 会话 | 应用内:ChatGPT 会话之间 |
| 触发 | MCP 工具 / CLI / 桥队列 | 应用内任务分派 |
| 外部可编程性 | 完全可编程(协议/CLI/库) | 不可外部编程接入 |
| 适用 | 跨模型(DeepSeek ↔ GPT)协同开发 | 同模型多会话分工 |
两者互补:dsh-bridge 面向"DSH 与 ChatGPT/Codex 的跨应用双工协同"。
## 已知限制
- 桌面应用当前持有的**活跃**会话不可被外部直接 steer(单写者);排队 + `session_claim`(桌面释放后)切换。桌面仅加载未运行时 claim 即可成功。
- 桥新建的 Codex 会话**跑过第一个 turn 后**出现在桌面侧栏(线程列表/状态库在首 turn 才落库;命名在建会话时已写入)。
- `turn/steer` 影响后续模型决策、不强制中断正在执行的 shell/MCP 工具;立即停止用 `session_cancel`。
- 桥执行 turn 期间桌面原生 UI 无法接管(协议级单写者,跨宿主 steer/interrupt 均 `thread not found`);轻量控制经任意桌面会话的桥 MCP 工具完成,原生接管用 `session_pause`。DSH 方向无此限制;DSH 无会话级模型/思考程度切换 RPC。
- App Server WebSocket transport 为实验性(codex 官方标注);升级 Codex 后需重跑集成测试。
- DSH HTTP schema 处于 developer preview;coordinator 启动做 capability probe,失败即拒绝相关操作(fail-closed)。`events.mux` 是 WebSocket 流(非 SSE)。
## License
MIT
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues