# dsh-plugins

> 为 DeepSeek Harness 提供 Codex 风格 shell 工具集，支持 exec_command 启动进程、write_stdin 持续喂入，专门解决需要 stdin 交互的 CLI 场景。

## Metadata

- Author: [@Ephemeral-AI-Lab](https://github.com/Ephemeral-AI-Lab)
- Repo: <https://github.com/Ephemeral-AI-Lab/dsh-plugins.git>
- GitHub: [Ephemeral-AI-Lab/dsh-plugins](https://github.com/Ephemeral-AI-Lab/dsh-plugins)
- Stars: 44
- Language: Python
- License: [MIT](https://spdx.org/licenses/MIT.html)
- Topics: `dsh-plugin`, `dsh-plugin-market`, `dsh-plugins`
- Forks: 4
- Open Issues: 1
- Last push: 2026-08-20T20:04:56.000Z
- Added: 2026-08-20T00:00:00.000Z

## Install

```bash
dsh plugin --profile web add github:Ephemeral-AI-Lab/dsh-plugins/codex-shell
```

## Wiki

> 落地页对应的仓库子路径是 `codex-shell`，但仓库中实际承载该插件代码的目录是 `codex-terminal`，npm 包名 `dsh-codex-terminal`。该插件是 `Ephemeral-AI-Lab/dsh-plugins` 这个 monorepo 下的子包之一。本百科聚焦 `codex-shell` 路径对应的能力。

## 一句话定位
为 DeepSeek Harness 增加 Codex 风格的双工具 shell：用 `exec_command` 启动进程并自动接管超时，再用 `write_stdin` 给同一会话持续喂输入，专门处理普通 shell 工具搞不定的交互式 CLI、REPL、OAuth 流程等场景。

## 核心能力
- 启动命令并按超时返回后台会话：执行命令超过 `defaultYieldTimeMs`（默认 10 秒）时自动转为 `codex-terminal-N` job，返回的 `job_id` 同时被 `write_stdin`、`job_list`、`job_kill` 共享。
- 持续向同一会话喂输入：用 `write_stdin(job_id, chars)` 发送一行 stdin，等于空字符串就是只轮询未读输出，结果会和 `exit_code` 一起返回。
- 接 DSH 任务系统：自动注册到宿主的 `ctx.jobs`，让原生 dsh-tools 任务工具统一管理长期运行 session。
- 进程自然结束时主动通知：通过 `ctx.steer` 通知 owner 在下一步拉一次输出，避免 owner 被 `followup` 误唤醒。
- 跨平台 shell 解析：Windows 自动走 PowerShell，macOS 优先 `/bin/zsh`，Linux 默认 bash/sh；可用 `windowsShell` / `posixShell` 自定义。
- 输出有上限且自动清理：单次响应受 `max_output_tokens` 限制（默认 10000 tokens），单会话保留不超过 1 MiB；收集完毕后进程句柄、输出缓存、监听器都释放。

## 技术实现
- **语言**: TypeScript（`tsc` 编译到 `lib/`，`package.json:5` 声明 `"type": "module"`）
- **关键依赖**:
  - `node-pty`（原生，PTY 后端的可选 transport，pipe fallback 默认不走）
  - `@deepseek-ai/cordis`（>=4.0.0）：声明 `inject: ['tools', 'systemPrompt', 'jobs']`，插件通过 Cordis 注册到宿主
  - `@deepseek-ai/dsh-tools`：用 `defineTool` 注册 `exec_command` / `write_stdin`
  - `@deepseek-ai/schemastery`（>=3.18.0）：工具入参 schema 校验
- **架构模式**: 标准 Cordis 插件 + `cordis.patch.yml` 给宿主注入一行 `codex-terminal`；`apply(ctx, config)` 内部用 PTY-first + 可配置 pipe fallback 工厂，三类后端（node-pty / pipe / pty-first 工厂）都在 `src/backend/` 下；shell 适配器按 `process.platform` 自动切换。
- **入口文件**: `codex-terminal/src/index.ts`（同时 `export name = 'codex-terminal'`、`inject = ['tools','systemPrompt','jobs']`，`src/index.ts:10-11`）

## 适用场景
当 DSH agent 跑的任务必须跟一个需要持续 stdin 喂入的 CLI 进程对话时（设备码登录、OAuth 回调、Python/Node REPL、SSH 会话、数据库客户端 prompt、需要回答"N Y/n" 的安装脚本等），普通 bash 工具只能启动+一次性取输出，没法继续喂输入；codex-terminal 通过 job_id 留出"再说话"的通道，让 agent 能完整跑完一轮交互。

## 前置依赖与兼容性
| 依赖 | 最低版本 | 说明 |
|---|---|---|
| `@deepseek-ai/dsh-tools` | `>=0.1.0-rc.5` | peerDependencies，没有它注册不了工具 |
| `@deepseek-ai/cordis` | `>=4.0.0` | peerDependencies，宿主插件框架 |
| `@deepseek-ai/schemastery` | `>=3.18.0` | peerDependencies，工具参数校验 |
| `@deepseek-ai/dsh-jobs-local` + `@deepseek-ai/dsh-tool-jobs` | 任意可用版本 | 未在 package.json 声明，但 README 和 `src/index.ts:32-35` 写明必须配合加载，否则插件启动即抛错 |
| `node-pty` | `^1.1.0` | 原生模块，启用 PTY 传输时需要；pipe fallback（默认）编译失败也能跑 |
| 平台 | 跨平台 | Windows / macOS / Linux 全部支持，分别走 PowerShell、zsh、bash/sh |
| Node.js | 未声明 | package.json 没声明 `engines`，devDependency 是 `@types/node ^22.20.0` |

## 安装方式
```bash
dsh plugin --profile web add github:Ephemeral-AI-Lab/dsh-plugins/codex-shell
```

## 配置项
| 配置 | 类型 | 说明 | 默认值 |
|---|---|---|---|
| `executionMode` | `'trusted'` \| `'host-policy'` | 部署级执行策略；`'host-policy'` 当前会主动抛错（`src/policy/execution-policy.ts:10-13`），只能留 `'trusted'` | `trusted` |
| `ptyFallback` | `'pipe'` \| `'error'` | PTY 后端分配失败时回退到 pipe 还是直接报错 | `pipe` |
| `maxSessions` | 整数 | 同时存在的会话上限，超过即拒绝新 job | `64` |
| `defaultYieldTimeMs` | 正整数（毫秒） | `exec_command` 默认等待时长；超时则转为后台 job 返回 `job_id` | `10000` |
| `pollYieldTimeMs` | 正整数（毫秒） | `write_stdin` 空轮询时的默认等输出时长 | `250` |
| `maxOutputBytes` | 正整数 | 单个会话保留的输出字节上限（内存） | `1048576`（1 MiB） |
| `defaultMaxOutputTokens` | 正整数 | 单次工具响应里允许返回的最大 token 数；超过会分页 | `10000` |
| `rows` | 正整数 | 伪终端行数（仅 PTY 模式生效） | `24` |
| `cols` | 正整数 | 伪终端列数（仅 PTY 模式生效） | `80` |
| `windowsPtyStartupGraceMs` | 非负整数（毫秒） | Windows 上 ConPTY 启动后保护首次 stdin 不被吞掉的等待时间 | `2000` |
| `windowsShell` | 字符串 | 自定义 Windows shell 路径 | 不配置则用 PowerShell |
| `posixShell` | 字符串 | 自定义 macOS/Linux shell 路径，留空时按 `$SHELL` → `/bin/zsh` → `/bin/bash` → `/bin/sh` 顺序探测 | 自动探测 |

## 常见问题
**Q: 装完之后怎么启用？**

A: 不是装完就能用，必须到 DSH Web UI 进入 Settings → Agent presets，用 Creator mode 新建一个名为 "Codex Whale"、id 为 `codex-whale` 的 preset（duplicate Standard preset，加入一行 `id: codex-terminal` 并禁用 `tool-bash`/`tool-pwsh`），设置成默认 preset、重启 DSH、新建会话后才生效。

**Q: 原生 `node-pty` 编译失败怎么办？**

A: 默认 `ptyFallback: 'pipe'` 已经让 pipe 后端接管 `exec_command` + `write_stdin` 整条链路（`README.md:101-103`），所以本地编译失败不影响主要功能；想用真 PTY 的话，去目标 profile 的 `pnpm-workspace.yaml` 加 `allowBuilds: { node-pty: true }` 后重新装。

**Q: 现有会话还在用旧的 shell 工具？**

A: 故意为之。`README.md:165-166` 明确说"existing sessions keep their old tools"，所以装完插件必须新建会话才能体验新工具。

**Q: 报错 "background jobs unavailable"？**

A: 意味着当前 DSH profile 没把 `@deepseek-ai/dsh-jobs-local` 和 `@deepseek-ai/dsh-tool-jobs` 加载进来——`src/index.ts:32-35` 在拿不到 `ctx.jobs` 时直接抛错，这俩插件不打开 `exec_command` 没意义。

**Q: 长输出会被截断吗？输到一半内容丢了怎么办？**

A: 单次响应按 `defaultMaxOutputTokens` 分页，已缓冲的输出不会丢（`changelog/0.1.2.md` 修复了"截断就释放 session"的回归 bug）；继续用 `write_stdin(job_id)` 空轮询就能拉下一页，直到响应里不再带 `job_id` 为止。

**Q: 进程自然结束了，怎么知道？**

A: 通过 owner-level `steer` 发一次内部通知（`README.md:120-123`），agent 下一步会收到"用空 chars 调一次 write_stdin 拉收尾"的提示；如果 `exec_command`/`write_stdin` 在自己的 yield 内就探到了 exit code，背景 notify 会抑制，避免重复打扰。

**Q: 能和原生的 `bash`/`pwsh` 工具同时开吗？**

A: Codex Whale preset 专门把 `tool-bash` 和 `tool-pwsh` 禁了（`README.md:54`），所以正常使用下不会撞车；如果硬要同时启用，两套工具会同时出现在 agent tool 列表里，可能造成同一会话被两条路径操作导致状态错乱。

## 上手难度
**进阶** — 玩家必须先理解 DSH Agent preset / profile 加载机制，再手工建 Codex Whale preset、配套打开 jobs 插件、决定要不要编译 node-pty；按 README 走一遍需要六七步，但每步有具体命令和验证方式，不算难。

## 已知问题与限制
- `executionMode: 'host-policy'` 已声明但未实现，选这个值会在启动时主动抛 `unsupported until an explicit DHS policy adapter is supplied`（`src/policy/execution-policy.ts:10-13`），只能保持 `trusted`。
- 必须依赖 `@deepseek-ai/dsh-jobs-local` 和 `@deepseek-ai/dsh-tool-jobs`，否则 `src/index.ts:33-35` 直接 throw；插件不会自己加载兄弟插件。
- 老的会话保持旧工具集，要新开会话才能用到 codex-terminal（`README.md:165-166`）。
- `job_output` 必须从 agent 视角隐藏——codex-terminal 的完成通知走专门的 `steer` 通道，不通过 `job_output`，混用会丢输出。
- 原生 `node-pty` 在某些受限环境（容器、CI）会被 pnpm 构建守护拦下，需要在 profile 的 `pnpm-workspace.yaml` 加 `allowBuilds` 白名单（`README.md:201-208`）。
- 单会话最多保留 1 MiB 输出，超大数据流型任务需要持续轮询把内容取出，否则超限会被 `maxOutputBytes` 截断。

---

This document is auto-generated by [deepseek-plugin.org](https://deepseek-plugin.org). HTML page: [dsh-plugins](https://deepseek-plugin.org/plugins/Ephemeral-AI-Lab/dsh-plugins/codex-shell)
Wiki generated by AI (model: `MiniMax-M3`)
