Skip to content
dsh.fish
Bundle

codex-dsh-bridge

Codex to DeepSeek Harness bridge: an MCP endpoint hosted inside the running DSH web profile that creates and drives real, visible DSH sessions.

Source
journ10
License
MIT
Updated
Updated 8 days ago

Readme

# Codex DSH Bridge

让 Codex 通过 MCP 在正在运行的 DeepSeek Harness(DSH)Web 中创建任务、继续已有会话、查询进度和获取结果。会话由同一个 DSH Host 管理,可在 DSH Web 中查看。

正式版本:`0.1.0`。通过 [GitHub Releases](https://github.com/journ10/codex-dsh-bridge/releases) 分发,可直接安装 GitHub tag 或下载的 tarball;当前不发布 npm。

```text
Codex ── HTTP MCP + bearer token ──▶ DSH Web 中的插件
                                      └─ sessionController → DSH 会话
                                                               └─ Web 显示
```

## 开发

主插件仅使用 Node 内置模块和仓库内 JavaScript 文件,无第三方运行依赖,无编译步骤。

```sh
npm test
npm run test:legacy
npm run test:package
npm pack --dry-run --ignore-scripts
```

`npm test` 使用模拟 DSH 服务和临时 HTTP 端口;`test:legacy` 使用 stub 子进程。两者不会提交真实模型任务。Node 要求为 `^22.19.0 || >=24.0.0`;DSH peer 固定为 `0.2.1-alpha.2`,其他 DSH 版本需要重新验证。peer 标为 optional 是为了让安装复用已有 DSH,不自动下载第二份 Host。

## 安装发布版本

前提:已有 DSH `0.2.1-alpha.2`、支持的 Node 和 pnpm,Web profile 可正常启动。

```sh
dsh plugin --profile web add github:journ10/codex-dsh-bridge#v0.1.0
```

也可从 [v0.1.0 Release](https://github.com/journ10/codex-dsh-bridge/releases/tag/v0.1.0) 下载 `codex-dsh-bridge-0.1.0.tgz` 和 `SHA256SUMS`,在下载目录校验并安装:

```sh
shasum -a 256 -c SHA256SUMS
dsh plugin --profile web add ./codex-dsh-bridge-0.1.0.tgz
```

安装完成后重启 DSH Web。包直接分发 JavaScript,不运行 `prepare` 或其他安装构建脚本。

## 安装本地开发版本

前提:已经安装 DSH 和 pnpm,并且 Web profile 能正常启动、模型与权限配置可用。建议先使用隔离的 `DSH_HOME` 验证你的配置。

在本仓库根目录执行:

```sh
dsh plugin --profile web add "$PWD"
```

如果使用 DSH 源码 checkout,从该 checkout 调用 `pnpm dsh plugin --profile web add /absolute/path/to/codex-dsh-bridge`。安装后用你原来的方式重启 DSH Web;插件不会负责启动第二个 DSH。

默认端点示例:

```sh
curl --fail 'http://127.0.0.1:3080/codex-dsh?probe=tools'
```

此探针无需令牌,只返回服务标识和工具名称。地址、端口取决于你的 DSH Web 配置;探针成功不代表任务执行成功。

## 连接 Codex

将 [配置示例](examples/codex.config.toml) 中的 MCP 段合并到自己的 Codex 配置,保留其他已有设置,并在新会话中验证工具加载。

```toml
[mcp_servers.dsh]
url = "http://127.0.0.1:3080/codex-dsh"
bearer_token_env_var = "DSH_BRIDGE_TOKEN"
tool_timeout_sec = 3600
```

`DSH_BRIDGE_TOKEN` 必须在启动 Codex 的进程环境中可见。桥默认从 `$DSH_HOME/codex-dsh-bridge.token` 读取令牌;未设置 `DSH_HOME` 时目录为 `~/.dsh`,首次加载会以排他创建方式生成令牌。已有文件必须非空且为普通文件;POSIX 权限不能向组或其他用户开放(可用 `chmod 600` 修复)。读取或权限错误会阻止插件加载,避免覆盖已有令牌。令牌仅在用户机器上配置,不写进本仓库。

对于不继承终端环境的桌面进程,可以在个人 Codex 配置中使用 `[mcp_servers.dsh.http_headers]` 下的 `Authorization = "Bearer <本机令牌>"`,并移除 `bearer_token_env_var`。该凭据配置不应分享或加入版本控制。可用 `codex mcp get dsh` 检查配置是否被识别。

### 非交互 Codex 调用

`codex exec` 的退出码为 0 不代表 MCP 调用成功:在已验证的 Codex CLI `0.136.0` 中,如果工具需要审批而 `approval_policy = "never"`,调用会返回 `MCP tool call requires approval`,Codex 仍可能正常结束并报告失败。

交互使用时按提示批准工具。只有在你信任该 DSH 端点、并明确允许自动执行时,才在对应 MCP 配置段中加入:

```toml
[mcp_servers.dsh]
url = "http://127.0.0.1:3080/codex-dsh"
bearer_token_env_var = "DSH_BRIDGE_TOKEN"
tool_timeout_sec = 3600
required = true
default_tools_approval_mode = "approve"
```

这会预先允许该端点的工具调用,包括创建和继续 DSH 任务。自动化验证应使用独立 `CODEX_HOME`、隔离 DSH 和临时工作区;不要为测试修改正在使用的全局配置。使用 `codex exec --json` 保存事件,并检查真实 `mcp_tool_call` 结果。详见 [Codex 非交互模式](https://learn.chatgpt.com/docs/non-interactive-mode) 和 [MCP 配置](https://learn.chatgpt.com/docs/extend/mcp?surface=cli)。

本仓库尚未提供主方案的 Codex marketplace 安装包。`legacy/headless/codex-marketplace/` 是旧方案的参考文件,不能代替上述 Web MCP 配置。

## 工具

| 工具 | 用途 |
|---|---|
| `dsh_workspaces` | 列出 DSH 已注册工作区,获取 `workspace_id` |
| `dsh_sessions` | 列出会话,可按工作区或目录过滤 |
| `dsh_task` | 在指定工作区或目录新建会话并发送 `prompt` |
| `dsh_send` | 给已有会话发送消息;`mode` 为 `queue` 或 `steer` |
| `dsh_wait` | 等待会话结果,传入异步提交返回的 `from_seq` 和 `request_id` |
| `dsh_status` | 查询当前状态、最近答案与工具调用 |

`dsh_task` 和 `dsh_send` 默认等待结果;`wait: false` 返回 `session_id`、`accepted`、`from_seq` 和 `request_id`,之后将 `session_id`、`from_seq`、`request_id` 一起传给 `dsh_wait`,按请求定位其回答;不传 `request_id` 时只查询区间的最近状态。等待结果包含 `turn_completed`、`stop_reason`、`answer`、`tool_calls` 和 `timed_out`;列表与状态工具返回各自的数据。超时结束等待,不等于停止 DSH 任务。客户端断连或插件卸载会取消等待并清理等待计时器,不保证中止已提交任务。`steer` 共享当前 turn,返回该 turn 的后续答案,并非独立消息的隔离执行。

新任务参数使用 `prompt`。旧 headless 方案的 `task`、`dsh_reply` 和文本尾标不适用于本方案。会话自身的文件权限、审批和模型配置由 DSH 管理。

## 插件配置

默认配置在 [cordis.patch.yml](cordis.patch.yml)。用户可在自己的 Web profile patch 中覆盖同一插件行,保留所需配置字段;DSH patch 会替换整段 `config`,不是深度合并。

| 字段 | 默认值 | 含义 |
|---|---|---|
| `path` | `/codex-dsh` | MCP 路由,以 `/` 开头且不以 `/` 结尾 |
| `token` | 未指定 | 显式 bearer token;省略则读取 `tokenFile` |
| `tokenFile` | `$DSH_HOME/codex-dsh-bridge.token` | 本机令牌文件 |
| `allowedHosts` | `[]` | 除回环名称外额外允许的 HTTP Host 值 |
| `agentPreset` | DSH 默认 | 新建会话使用的 agent preset |
| `defaultTimeoutMs` | `600000` | 工具未指定超时时的等待时长 |

路由挂在 DSH Web server 上,位于 `/api` 浏览器鉴权路径之外,独立检查 Host 和 bearer token。Host 校验针对请求头,不是 TCP 来源地址限制;不要把它当作防火墙或远程部署方案。令牌持有者可以调用桥暴露的会话工具。

## 卸载

```sh
dsh plugin --profile web remove codex-dsh-bridge
```

重启 DSH Web,并移除个人 Codex 配置中的对应 MCP 段。是否保留令牌文件由用户决定;卸载不会删除 DSH 会话。

## 验证与限制

[完整验证记录与页面证据](https://github.com/journ10/codex-dsh-bridge/blob/v0.1.0/docs/validation-0.1.0.md)。

发布验证使用 macOS、Node `25.4.0`、pnpm `12.3.4`、DSH `0.2.1-alpha.2`(源码 commit `d743267388641bc76f17c45ce8b4c231aed1d32c`)。在隔离 `DSH_HOME` 和回环端口中,使用真实 DSH Web Host 与本地模拟模型端点,验证 profile 安装、MCP 握手和六工具、鉴权、会话创建、续聊、重启后读取及卸载。单元/HTTP 回归另覆盖排队结果归属、steer 转发、长冷会话、超时、卸载取消、输入校验和令牌保护。

正式版还通过真实 DeepSeek-V4.1-Flash(`deepseek-flash`)和 Codex CLI `0.136.0`(`gpt-6-astra`)端到端测试:Codex 实际调用全部六个工具,DSH 读取随机测试文件、计算结果、继续同一会话并返回状态。Chrome 中已核对任务、答案、工具轨迹,完成 Web 续聊、刷新持久化及 MCP 读回。

- 验证范围为 Codex CLI 与 DSH Web;Codex Desktop 的配置发现与重载流程未单独验证。测试使用独立 home 和端口,未重启正在使用的 DSH。
- 面向 Web profile;未验证 Desktop、TUI 或 headless profile 安装。
- DSH 服务和事件仍在变化,因此使用精确 DSH peer;相同版本号的源码修改也可能影响兼容性。
- 配置由插件在加载时校验;尚未导出 DSH 配置表单使用的标准 `Config` schema,请使用 profile patch 配置。
- MCP 提供 JSON POST 响应和工具调用,没有服务器 SSE 推送;未声明完整协议一致性认证。
- 等待按请求区间查询;超长会话的查询成本随事件数量增长。`dsh_status` 默认仅折取末尾 200 个事件。
- `private: true` 保留以防意外 npm 发布,不影响 GitHub 或 tarball 安装。旧 headless 方案只在 `legacy/` 中保留,不进入安装包。

许可证沿用原插件的 MIT 声明,见 [LICENSE](LICENSE)。

Install

dsh plugin --profile web add github:journ10/codex-dsh-bridge

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source