🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

Codex 任务桥 HazenSun/dsh-codex-bridge

MCP
HazenSun24 天前有提交已提供中文文档扫描:含敏感能力兼容 / 相关生态

把 Codex 拆解的任务交给 DSH 隔离执行并回收证据

30.5/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/12 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

相关生态插件(非 dsh 原生,按其对应运行时安装)
git clone https://github.com/HazenSun/dsh-codex-bridge.git

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:待复验
是什么
生态应用
桌面端 / Web 外壳,不以 dsh plugin add 安装
装得上吗
曾在 dsh 0.2.0-rc.2 下真实安装成功,但当前 dsh 已更新至 0.2.1-rc.5,本站尚未在新版下复验
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 24 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件
查看扫描证据(共 124 处,展示前 110 条)
  • 读取本地文件
    packages/artifacts/src/index.test.ts:48
    expect(await readFile(join(root, 'tasks', 'task-001', 'objects', digest), 'utf8')).toContain(
  • 读取本地文件
    packages/artifacts/src/index.test.ts:51
    expect(await readFile(join(root, 'tasks', 'task-001', 'manifest.json'), 'utf8')).toContain(
  • 写入本地文件
    packages/artifacts/src/index.test.ts:238
    await writeFile(join(root, 'tasks', 'task-001', 'objects', descriptor.artifactId), 'tampered');
  • 读取本地文件
    packages/artifacts/src/index.ts:556
    const sourceStats = await stat(source);
  • 读取本地文件
    packages/artifacts/src/index.ts:566
    return this.put(taskId, artifactPath, await readFile(source), options);
  • 读取本地文件
    packages/artifacts/src/index.ts:610
    const bytes = await readFile(join(taskPaths.objectsDir, artifact.artifactId));
  • 删除本地文件递归
    packages/artifacts/src/index.ts:696
    await rm(taskPaths.taskDir, { recursive: true, force: true });
  • 写入本地文件
    packages/artifacts/src/index.ts:717
    await mkdir(this.#rootDir, { recursive: true, mode: 0o700 });
  • 写入本地文件
    packages/artifacts/src/index.ts:720
    await mkdir(this.#tasksDir, { recursive: true, mode: 0o700 });
  • 写入本地文件
    packages/artifacts/src/index.ts:751
    await mkdir(taskDir, { recursive: true, mode: 0o700 });
  • 写入本地文件
    packages/artifacts/src/index.ts:754
    await mkdir(objectsDir, { recursive: true, mode: 0o700 });
  • 读取本地文件
    packages/artifacts/src/index.ts:767
    const entry = await lstat(path);
  • 读取本地文件
    packages/artifacts/src/index.ts:779
    const entry = await lstat(path);
  • 读取本地文件
    packages/artifacts/src/index.ts:794
    const existing = await lstat(objectPath);
  • 写入本地文件
    packages/artifacts/src/index.ts:825
    await rename(temporaryPath, objectPath);
  • 删除本地文件
    packages/artifacts/src/index.ts:828
    await rm(temporaryPath, { force: true });
  • 写入本地文件
    packages/artifacts/src/index.ts:847
    await rename(temporaryPath, manifestPath);
  • 删除本地文件
    packages/artifacts/src/index.ts:849
    await rm(temporaryPath, { force: true });
  • 读取本地文件
    packages/cli/src/index.ts:67
    const value: unknown = JSON.parse(await readFile(launcherConfigPath(), 'utf8'));
  • 读取本地文件
    packages/cli/src/index.ts:136
    await access(path, constants.F_OK);
  • 写入本地文件
    packages/cli/src/index.ts:157
    await mkdir(dirname(path), { recursive: true });
  • 写入本地文件
    packages/cli/src/index.ts:168
    await rename(temporaryPath, path);
  • 删除本地文件
    packages/cli/src/index.ts:171
    await rm(temporaryPath);
  • 删除本地文件
    packages/cli/src/index.ts:174
    await rm(temporaryPath, { force: true });
  • 写入本地文件
    packages/cli/src/index.ts:264
    await mkdir(resolve(projectRoot, '.codex', 'agents'), { recursive: true });
  • 读取本地文件
    packages/cli/src/index.ts:267
    const current = await readFile(path, 'utf8');
  • 读取本地文件
    packages/cli/src/index.ts:289
    const root = await realpath(resolve(path));
  • 读取本地文件
    packages/cli/src/index.ts:315
    if ((await realpath(stdout.trim())) !== root) {
  • 读取本地文件
    packages/cli/src/index.ts:345
    const current = await readFile(target, 'utf8');
  • 写入本地文件
    packages/cli/src/index.ts:349
    await mkdir(dirname(backup), { recursive: true, mode: 0o700 });
  • 写入本地文件
    packages/cli/src/index.ts:350
    await copyFile(target, backup, constants.COPYFILE_EXCL);
  • 写入本地文件
    packages/cli/src/index.ts:358
    await mkdir(profileRoot, { recursive: true });
  • 删除本地文件
    packages/cli/src/index.ts:1307
    await rm(lockPath, { force: true });
  • 写入本地文件
    packages/cli/src/launch-config.test.ts:21
    await writeFile(
  • 写入本地文件
    packages/cli/src/launch-config.test.ts:31
    await writeFile(
  • 读取本地文件
    packages/cli/src/launch-config.test.ts:68
    const calls: Array<{ home: string; args: string[] }> = (await readFile(callsPath, 'utf8'))
  • 删除本地文件递归
    packages/cli/src/launch-config.test.ts:75
    await rm(root, { recursive: true, force: true });
  • 写入本地文件
    packages/cli/src/launch-config.test.ts:84
    await mkdir(dshHome);
  • 写入本地文件
    packages/cli/src/launch-config.test.ts:85
    await writeFile(binary, '#!/usr/bin/env node\nconsole.log("0.1.6-alpha.1");\n', {
  • 读取本地文件
    packages/cli/src/launch-config.test.ts:114
    readFile(join(dshHome, 'profiles/codex-bridge/package.json')),
  • 删除本地文件递归
    packages/cli/src/launch-config.test.ts:117
    await rm(root, { recursive: true, force: true });
  • 写入本地文件
    packages/cli/src/launch-config.test.ts:125
    await writeFile(
  • 写入本地文件
    packages/cli/src/launch-config.test.ts:132
    await writeFile(config, baseline);
  • 读取本地文件
    packages/cli/src/launch-config.test.ts:156
    expect(await readFile(config, 'utf8')).toBe(baseline);
  • 删除本地文件递归
    packages/cli/src/launch-config.test.ts:158
    await rm(root, { recursive: true, force: true });
  • 写入本地文件
    packages/config/src/index.test.ts:64
    await writeFile(path, stringifyYaml(config), { mode: 0o600 });
  • 写入本地文件
    packages/config/src/index.test.ts:82
    await mkdir(join(root, 'project'));
  • 写入本地文件
    packages/config/src/index.test.ts:83
    await writeFile(
  • 读取本地文件
    packages/config/src/index.test.ts:119
    const canonicalProject = await realpath(join(root, 'project'));
  • 读取本地文件
    packages/config/src/index.test.ts:156
    const beforeBytes = await readFile(configPath);
  • 读取本地文件
    packages/config/src/index.test.ts:170
    expect(await readFile(configPath)).toEqual(beforeBytes);
  • 读取本地文件
    packages/config/src/index.test.ts:188
    const fileMode = (await stat(configPath)).mode & 0o777;
  • 读取本地文件
    packages/config/src/index.test.ts:196
    expect(await readFile(backups[0] as string, 'utf8')).toBe(initial.raw);
  • 写入本地文件
    packages/config/src/index.test.ts:206
    await writeFile(configPath, source, { mode: 0o600 });
  • 读取本地文件
    packages/config/src/index.test.ts:219
    const updatedRaw = await readFile(configPath, 'utf8');
  • 写入本地文件
    packages/config/src/index.test.ts:245
    await writeFile(lockPath, '{"pid":99999}\n', { mode: 0o600 });
  • 删除本地文件
    packages/config/src/index.test.ts:258
    await rm(lockPath);
  • 读取本地文件
    packages/config/src/index.ts:134
    const raw = parseYaml(await readFile(configPath, 'utf8')) as unknown;
  • 读取本地文件
    packages/config/src/index.ts:143
    root: await realpath(absoluteFrom(base, project.root)),
  • 读取本地文件
    packages/config/src/index.ts:152
    profile.workspace.allowed_roots.map(async (root) => realpath(absoluteFrom(base, root))),
  • 读取本地文件
    packages/config/src/index.ts:861
    const bytes = await readFile(resolvedPath);
  • 读取本地文件
    packages/config/src/index.ts:875
    return calculateConfigRevision(await readFile(resolve(configPath)));
  • 写入本地文件
    packages/config/src/index.ts:948
    await rename(temporaryPath, filePath);
  • 删除本地文件
    packages/config/src/index.ts:952
    await rm(temporaryPath, { force: true }).catch(() => undefined);
  • 写入本地文件
    packages/config/src/index.ts:971
    await mkdir(directory, { recursive: true, mode: 0o700 });
  • 写入本地文件
    packages/config/src/index.ts:972
    await chmod(directory, 0o700);
  • 写入本地文件
    packages/config/src/index.ts:978
    await chmod(backupPath, 0o600);
  • 读取本地文件
    packages/config/src/index.ts:986
    names = await readdir(directory);
  • 删除本地文件
    packages/config/src/index.ts:1006
    for (const path of paths.slice(maxBackups)) await rm(path, { force: true });
  • 删除本地文件
    packages/config/src/index.ts:1022
    if (handle !== undefined) await rm(lockPath, { force: true }).catch(() => undefined);
  • 删除本地文件
    packages/config/src/index.ts:1033
    await rm(lockPath, { force: true });
  • 写入本地文件
    packages/task-engine/src/index.test.ts:107
    await writeFile(join(request.cwd, 'generated.txt'), `run-${this.calls.length}\n`);
  • 写入本地文件
    packages/task-engine/src/index.test.ts:195
    await writeFile(join(projectRoot, 'README.md'), 'fixture\n');
  • 读取本地文件
    packages/task-engine/src/index.test.ts:198
    projectRoot = await realpath(projectRoot);
  • 删除本地文件递归
    packages/task-engine/src/index.test.ts:273
    await rm(fixtureRoot, { recursive: true, force: true });
  • 写入本地文件
    packages/task-engine/src/index.test.ts:412
    await writeFile(
  • 写入本地文件
    packages/task-engine/src/index.test.ts:490
    await writeFile(join(dataRoot, 'tasks', record.task.task_id, 'task.lease.json'), '{');
  • 读取本地文件
    packages/task-engine/src/index.test.ts:582
    expect(await readFile(join(runtime.calls[0]!.cwd, 'generated.txt'), 'utf8')).toBe('run-1\n');
  • 读取本地文件
    packages/task-engine/src/index.test.ts:583
    expect(await readFile(join(projectRoot, 'README.md'), 'utf8')).toBe('fixture\n');
  • 删除本地文件递归
    packages/task-engine/src/lease.test.ts:18
    await rm(root, { recursive: true, force: true });
  • 写入本地文件
    packages/task-engine/src/lease.test.ts:35
    await mkdir(join(root, taskId));
  • 执行外部命令来自配置/变量
    packages/task-engine/src/lease.test.ts:38
    const child = spawn(
  • 读取本地文件
    packages/task-engine/src/lease.test.ts:101
    expect(await readdir(join(root, 'task_shared'))).toEqual([]);
  • 读取本地文件
    packages/task-engine/src/lease.test.ts:122
    expect(await readdir(join(root, 'task_child'))).toEqual([]);
  • 写入本地文件
    packages/task-engine/src/lease.test.ts:132
    await writeFile(
  • 读取本地文件
    packages/task-engine/src/lease.test.ts:139
    expect(await readdir(join(root, 'task_guard'))).toEqual(['task.lease.json']);
  • 写入本地文件
    packages/task-engine/src/lease.test.ts:150
    await mkdir(join(root, 'task_corrupt'));
  • 写入本地文件
    packages/task-engine/src/lease.test.ts:151
    await writeFile(path, content);
  • 读取本地文件
    packages/task-engine/src/lease.test.ts:155
    expect(await readFile(path, 'utf8')).toBe(content);
  • 写入本地文件
    packages/task-engine/src/lease.test.ts:163
    await writeFile(path, JSON.stringify(replacement));
  • 读取本地文件
    packages/task-engine/src/lease.test.ts:166
    expect(JSON.parse(await readFile(path, 'utf8')) as unknown).toEqual(replacement);
  • 读取本地文件
    packages/task-engine/src/lease.ts:42
    const stat = await lstat(path);
  • 读取本地文件
    packages/task-engine/src/lease.ts:46
    const content = await readFile(path, 'utf8');
  • 删除本地文件
    packages/task-engine/src/lease.ts:108
    await unlink(path);
  • 删除本地文件
    packages/task-engine/src/lease.ts:144
    await unlink(path);
  • 读取本地文件
    packages/task-engine/src/store.ts:120
    const content = await readFile(join(this.#root, taskId, 'task.json'), 'utf8');
  • 读取本地文件
    packages/task-engine/src/store.ts:130
    const entries = await readdir(this.#root, { withFileTypes: true });
  • 写入本地文件
    packages/task-engine/src/store.ts:144
    await mkdir(dirname(path), { recursive: true });
  • 写入本地文件
    packages/task-engine/src/store.ts:156
    await rename(temporary, path);
  • 删除本地文件
    packages/task-engine/src/store.ts:158
    if (created) await rm(temporary, { force: true });
  • 写入本地文件
    packages/task-engine/src/store.ts:165
    await mkdir(directory, { recursive: true });
  • 写入本地文件
    packages/workspace/src/index.test.ts:19
    await writeFile(join(root, 'README.md'), 'base\n');
  • 写入本地文件
    packages/workspace/src/index.test.ts:23
    await mkdir(worktrees);
  • 写入本地文件
    packages/workspace/src/index.test.ts:39
    await writeFile(join(handle.path, 'README.md'), 'changed\n');
  • 写入本地文件
    packages/workspace/src/index.test.ts:40
    await writeFile(join(handle.path, 'new-file.txt'), 'new\n');
  • 读取本地文件
    packages/workspace/src/index.test.ts:46
    expect(await readFile(join(root, 'README.md'), 'utf8')).toBe('base\n');
  • 读取本地文件
    packages/workspace/src/index.ts:83
    this.#allowedRoots = Promise.all(options.allowedRoots.map((root) => realpath(resolve(root))));
  • 读取本地文件
    packages/workspace/src/index.ts:89
    const projectRoot = await realpath(resolve(input));
  • 读取本地文件
    packages/workspace/src/index.ts:96
    const gitRoot = await realpath(stdout.trim());
  • 写入本地文件
    packages/workspace/src/index.ts:121
    await mkdir(dirname(worktreePath), { recursive: true });
扫描于 2026/11/2(0 天前) · 范围 全包扫描 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 委派任务给智能体
  • 隔离执行任务
  • 回收执行证据

本地优先的桥接,把 Codex 任务经 MCP 委派给 DeepSeek Harness 智能体。

README

由 DeepSeek 最新模型翻译生成
DSH × Codex Bridge 把 Codex 的决策力与 DSH 的执行力连接起来。 中文 · English · 安装 · 使用 · 架构 dsh-codex-bridge 是面向开发者的开源 Agent 基础设施:Codex 负责拆解目标、选择委派策略和验收证据;DeepSeek Harness(DSH)负责在指定 Profile 中执行代码、测试和内部 Sub-Agent 工作;Bridge 负责协议、隔离工作区、任务生命周期和可审查产物。 项目状态 源码安装|0.1.0-alpha.3|目标 DSH 0.2.0-rc.2 Bridge 提供源码构建、项目设置、本地 DSH Profile/Codex Plugin 安装、模型管理和隔离 Worktree 任务。当前版本的实际验证范围见兼容矩阵。tests/e2e/evidence 中的 rc.8 模型调用是历史证据,不能作为新版 DSH 的验证结果。 Bridge npm 包和 Marketplace 公共发布尚未进行。Alpha 3 对接官方 npm 的 DSH 0.2.0-rc.2,使用精确版本,并可与已有 DSH 安装并存。 它解决什么问题?
展开完整文档(README 剩余 1 节)
DSH × Codex Bridge

把 Codex 的决策力与 DSH 的执行力连接起来。

中文 · English · 安装 · 使用 · 架构

dsh-codex-bridge 是面向开发者的开源 Agent 基础设施:Codex 负责拆解目标、选择委派策略和验收证据;DeepSeek Harness(DSH)负责在指定 Profile 中执行代码、测试和内部 Sub-Agent 工作;Bridge 负责协议、隔离工作区、任务生命周期和可审查产物。

项目状态

源码安装|0.1.0-alpha.3|目标 DSH 0.2.0-rc.2

Bridge 提供源码构建、项目设置、本地 DSH Profile/Codex Plugin 安装、模型管理和隔离 Worktree 任务。当前版本的实际验证范围见兼容矩阵。tests/e2e/evidence 中的 rc.8 模型调用是历史证据,不能作为新版 DSH 的验证结果。

Bridge npm 包和 Marketplace 公共发布尚未进行。Alpha 3 对接官方 npm 的 DSH 0.2.0-rc.2,使用精确版本,并可与已有 DSH 安装并存。

它解决什么问题?

当一个任务不值得消耗主控模型的完整上下文时,Codex 可以把它交给 DSH:

Codex(计划 / 委派 / 审查) │ │ Direct Mode:默认、低额外成本 ▼ 本地 MCP Gateway + Task Engine │ ▼ DSH Plugin(Profile / 模型 / 工具 / Sub-Agent) │ ▼ 隔离 Worktree → Diff、测试、摘要、Artifact

Bridge 不把 DSH 宣称为 Codex 原生模型或原生 Sub-Agent。默认的 Direct Mode 是 Codex 主线程直接调用 Bridge MCP;可选的 Native Shell Mode 会生成一个窄职责的 Codex 自定义 Agent 作为调度外壳,以获得更接近原生线程的体验,但会额外消耗 Codex 用量。

让 Codex 完成安装和首次配置

在有文件和终端权限的 Codex 任务中,替换下面三个绝对路径后直接发送:

请帮我从 https://github.com/HazenSun/dsh-codex-bridge 安装 DSH × Codex Bridge。
目标 Git 项目:/absolute/path/to/your-project Bridge 源码目录:/absolute/path/to/dsh-codex-bridge Bridge 专用 DSH 安装目录:/absolute/path/to/dsh-bridge-runtime

先检查环境并展示安装计划,再构建 Bridge、安装官方 DSH 0.2.0-rc.2 和本地插件。
安装时使用 --dsh-bin 指向专用 DSH,保存启动配置并注册本地 MCP。
首次使用旧 DSH Home 前备份,或先用独立 DSH_HOME 验证;保留现有 DSH 和其他项目。
从 codex-bridge Profile 的实时模型目录选择精确 Provider/Model,展示配置 Diff 后完成 default-code。
如果缺少凭据,引导我在 DSH 中配置;最后运行 doctor 和配置校验,报告是否需要新建 Codex 任务。
安装期间不要运行真实模型任务。

当前安装需要 Node.js、pnpm、Git 和支持本地 Plugin/STDIO MCP 的 Codex CLI。凭据由 DSH 管理。Codex 可以完成命令执行和模型 Profile 配置;账号登录或缺失的 Provider 凭据仍需你在 DSH 中设置。

手动安装(源码版)

前置条件:Node.js ^22.19.0 或 >=24.0.0、pnpm 11.19.0、Git 和 Codex CLI/Desktop。Provider 凭据只在 DSH 中配置,不要粘贴给 Codex 或写入 bridge.yaml。

git clone https://github.com/HazenSun/dsh-codex-bridge.git
cd dsh-codex-bridge
corepack enable
pnpm install --frozen-lockfile
pnpm build

BRIDGE_RUNTIME=/absolute/path/to/dsh-bridge-runtime
npm install --prefix "$BRIDGE_RUNTIME" --no-audit --no-fund @deepseek-ai/dsh@0.2.0-rc.2
export DSH_BRIDGE_DSH_BIN="$BRIDGE_RUNTIME/node_modules/.bin/dsh"
"$DSH_BRIDGE_DSH_BIN" --version

BRIDGE_PROJECT=/absolute/path/to/your-project
pnpm dsh-bridge setup --project "$BRIDGE_PROJECT" --source . \
--dsh-bin "$DSH_BRIDGE_DSH_BIN" --dry-runbash
pnpm dsh-bridge setup --project "$BRIDGE_PROJECT" --source . \
--dsh-bin "$DSH_BRIDGE_DSH_BIN"

setup 会安装本地 DSH Profile 与 Codex Plugin,并把选中的可执行路径及 DSH Home 保存到 Codex Home 的 dsh-codex-bridge/runtime.json。Desktop 后续 MCP 从该文件启动,无需继承终端的 export。首次使用已有 DSH Home 时,DSH 0.2 可能迁移旧 settings.yaml;先备份,或使用独立 Home 验证,详见安装手册。

安装器同时通过官方 codex mcp add 注册明确的 Node、启动脚本和配置路径;不依赖插件 JSON 的路径变量展开。只安装 Marketplace 中的 Skill 插件还不等于完成本机运行时设置,首次使用应运行上述 setup。

保留 Bridge 源码和专用 DSH 目录,注册的启动路径会引用它们。使用自定义 CODEX_HOME 时,在该 Home 下安装和检查 MCP。后续若要让 Desktop 切换 DSH 路径或 Home,重新执行 setup 保存新选择;只在另一个终端 export 不会更新 Desktop。停用 Skill 插件也不会移除已注册的 MCP,完整停用步骤见安装手册。

没有 bridge.yaml 时,安装停在 needs_execution_profile。通过插件的 Setup 入口,或新建 Codex 任务后直接说: text 帮我完成 DSH Bridge 初次设置。所有配置先预览,确认后再写入。

Codex 会检查状态、读取 codex-bridge DSH Profile 可见的 Provider/Model、建议一个命名 Profile,并在首次创建前展示精确的 setup 计划。bridge.yaml 存在后,Profile 变更先展示语义 Diff。如果模型配置已在同一 DSH Home 的 web Profile 中,可以先用 models sync --from-profile web 预览同步 Provider 配置和安全凭据引用;没有配置时在 DSH 中完成。具体步骤见安装手册。

也可以完全使用 CLI。先发现 DSH 模型,再用精确 ID 完成首次配置: bash
pnpm dsh-bridge models list --config "$BRIDGE_PROJECT/bridge.yaml" --details

将这两个值替换为上一步返回的精确 ID

DSH_PROVIDER_ID='returned-provider-id'
DSH_MODEL_ID='returned-model-id'
pnpm dsh-bridge setup \
--project "$BRIDGE_PROJECT" \
--source . \
--provider "$DSH_PROVIDER_ID" \
--model "$DSH_MODEL_ID" \
--profile-id default-code

首次设置完成后,在新 Codex 任务中直接说: text 使用 DSH 处理这个任务,自己判断单 Agent 还是多 Agent。

delegate-to-dsh Skill 会发现可用 Profile,根据独立工作流和审查收益选择 single 或 multi,并把理由和角色写入任务。显式说“单 Agent”或“多 Agent”始终覆盖自动判断。多 Agent 只有在 DSH Session 中观察到足够的子 Agent 调用和完成事件后才算通过。

用 Codex 新增、切换和回滚模型

先让新模型在 codex-bridge DSH Profile 中可见,再将它映射成 Bridge 的命名 Profile。已有 web 模型配置可通过受控同步复用;新增 Provider 协议仍需 DSH Adapter 支持。完成 DSH 配置后,可以直接告诉 Codex: text 显示 DSH 当前可用模型。
把目录中我选中的 Provider/Model 加成 fast-code Profile,先预览。
把 fast-code 设为当前项目默认 Profile,确认刚才的 Diff 后写入。
回滚上一次 Bridge 模型配置。

Codex 只能选择 discover_dsh_models 实际返回的路由。每次写入都必须先预览,使用 SHA-256 Revision 防止覆盖并发修改,成功前会创建 0600 权限的有界备份。等价 CLI: bash

预览新增;输出 before_revision

pnpm dsh-bridge profiles add fast-code \
--config "$BRIDGE_PROJECT/bridge.yaml" \
--provider "$DSH_PROVIDER_ID" \
--model "$DSH_MODEL_ID"

审查后写入

pnpm dsh-bridge profiles add fast-code \
--config "$BRIDGE_PROJECT/bridge.yaml" \
--provider "$DSH_PROVIDER_ID" \
--model "$DSH_MODEL_ID" \
--apply --expected-revision 'before_revision-from-preview'

预览把已有 Profile 切到另一个 DSH 模型

先把 DSH_PROVIDER_ID/DSH_MODEL_ID 换成新路由的精确值

pnpm dsh-bridge profiles update fast-code \
--config "$BRIDGE_PROJECT/bridge.yaml" \
--provider "$DSH_PROVIDER_ID" \
--model "$DSH_MODEL_ID"

设置默认同样先预览,再带 Revision 重复执行并加 --apply

pnpm dsh-bridge profiles set-default fast-code \
--config "$BRIDGE_PROJECT/bridge.yaml" \
--project-id 'project-id-from-config'
配置变更不改变正在执行的 Turn 或已有历史。工具返回 restart_required: true 时,新建 Codex 任务加载新配置;新任务和重启后继续的 Run 会使用当前 Profile。已有 Session 续接时必须保留原 Agent Preset,不能在继续时更换。完整说明见安装手册和配置参考。

只有目录明确返回所选模型的推理档位时,才添加 --reasoning-effort 'advertised-effort-id'。发现模型和通过配置校验,不等于账号已经完成一次真实调用。

切换已有 Profile 的模型时,省略推理参数会保留旧值。需要使用新模型默认值时,在 profiles update 中加入 --clear-reasoning-effort,先预览再写入;它不能与 --reasoning-effort 同时使用。也可以告诉 Codex:“切换到我选中的模型,并清除旧推理档位,先预览。”

安装处理专用 DSH 目录、DSH codex-bridge Profile、本地 Codex Plugin 和启动配置;项目设置处理 bridge.yaml,后续变更的备份位于 .dsh-codex-bridge/config-backups/。模型管理只改变该项目的 Bridge Profile。启动配置则由同一 Codex Home 下的 Bridge 任务共用,切换运行时或 DSH Home 时需要一并核对。

真实 DSH 闭环验证

安装检查不调用生成模型。需要验证模型权限和任务执行时,再选择一个小任务或运行 E2E;以下命令会产生真实 Provider 请求,并可能产生费用: bash
pnpm test:e2e:dsh
pnpm test:e2e:model-matrix
pnpm test:e2e:setup

E2E 通过 DSH_BRIDGE_E2E_PROVIDER、DSH_BRIDGE_E2E_MODEL 和可选 DSH_BRIDGE_E2E_EFFORT 选择精确路由;多模型测试另有第二路参数。运行日志默认位于仓库外,也可设置 DSH_BRIDGE_EVIDENCE_DIR。参数示例见入门页。

测试目标包括任务状态、模型路由、子 Agent 事件、Artifact Hash、继续、取消和 Worktree 隔离。脚本需要它所测试的路由仍存在于当前 DSH 目录;历史 Flash/Kimi ID 不能直接当作新版本默认值。历史 rc.8 证据保留在 tests/e2e/evidence;本版实际执行结果以兼容矩阵为准。

未来发布安装(npm / Marketplace)

npm 包和 Codex Plugin Marketplace 包仍未发布。未来的包名、审核状态、Node 运行时和 DSH 版本约束,以正式 Release Notes 为准;当前版本不要执行未经发布说明确认的全局安装命令。

当前连接使用本地 STDIO MCP。源码安装无需部署公网 HTTPS 服务;公共目录上架是独立的发布步骤。

两种运行模式

| 模式 | 调用链 | 适合场景 | 代价 |
| Direct Mode(默认) | Codex → MCP → DSH | 批量执行、低额外 Codex 用量、清晰的结构化结果 | DSH 任务不是 Codex 原生 Agent Thread |
| Native Shell Mode(可选) | Codex → dsh_orchestrator → MCP → DSH | 需要线程可见性和显式调度外壳 | 增加一层 Codex Agent 和相应用量 |

两种模式共用协议、Profile、任务状态、Worktree 和 Artifact;切换模式不应改变任务结果格式。

文档导航

- 完整安装手册:环境、源码构建、Direct/Native Shell、升级和卸载边界。
- 完整使用手册:自然语言触发、单/多 Agent、Profile、结果审查、继续和取消。
- 10 分钟入门:从源码安装到第一个可审查任务。
- 配置参考:Profile、模型路由、工作区、并发与策略。
- 技术可行性报告:公开 API 证据、边界和 M0 PoC。
- 软件架构规划:Gateway、Task Engine、DSH Adapter 与协议。
- 施工与发布规划:里程碑、测试和 Definition of Done。
- 安全模型:密钥、路径、网络、Shell、递归和产物边界。
- 故障排查:doctor、MCP、DSH、Provider、Worktree 和恢复。
- 贡献指南:本地开发、测试、变更和 Pull Request 规范。
- 安全漏洞披露:不要在公开 Issue 中提交敏感漏洞细节。
- 行为准则:参与项目时共同维护的协作标准。
- 支持渠道:Bug、功能建议和安全问题应该去哪里。
- 变更记录:当前 Alpha 的功能与已知限制。

当前限制

- 仅支持源码安装;npm 包和公共 Codex Plugin Directory 尚未发布。
- 当前写入运行时只支持 Git isolated Worktree。
- Bridge 不自动提交、合并、部署或发布 DSH 结果。
- Profile 中部分网络、拒绝路径、文件数和磁盘策略仍是协议声明,尚未全部映射为 Bridge 侧执行器。
- 确定性的跨模型执行应使用多个命名 Profile;角色级子模型强制路由仍需 DSH Child Setup Contract。

设计承诺

- Protocol first:Codex、DSH 和未来的其他 Host 只交换版本化 JSON,不暴露 Cordis 私有类型。
- Profile over model:用户选择语义化 Profile;Provider、Model 和 Reasoning 是可验证的解析结果。
- Safety by default:默认隔离 Worktree、最小权限、资源上限、可取消和秘密脱敏。
- Codex owns the decision:DSH 返回证据,Codex 决定接受、继续、取消或放弃。
- Release as one unit:Codex Plugin、DSH Plugin 和共享协议固定版本同步发布。

开源许可

代码采用 Apache-2.0。第三方依赖、示例和 DSH 本身保留各自的版权与许可证;正式发布前会生成依赖许可证清单、SBOM 和构建来源证明。

贡献与反馈

提交 Issue 前请先阅读 贡献指南 与 故障排查;涉及安全问题请遵循 SECURITY.md,不要把密钥、Cookie、完整日志或私有代码贴到公开 Issue。
预览分享卡片长什么样 ↓HazenSun/dsh-codex-bridge
分享 · DeepSeek Harness Hub
HazenSun/dsh-codex-bridge

把 Codex 拆解的任务交给 DSH 隔离执行并回收证据

回到顶部