# deepseek-harness-studio

> DSH 一次性任务运行包：在命令行提交一句任务，启动 Agent 执行后把最终答案打印到 stdout 退出。

## Metadata

- Author: [@fufankeji](https://github.com/fufankeji)
- Repo: <https://github.com/fufankeji/deepseek-harness-studio.git>
- GitHub: [fufankeji/deepseek-harness-studio](https://github.com/fufankeji/deepseek-harness-studio)
- Stars: 403
- Language: TypeScript
- License: [MIT](https://spdx.org/licenses/MIT.html)
- Homepage: <https://www.beyondata.com/>
- Topics: `ai-agent`, `deepseek`, `deepseek-harness`, `deepseek-harness-studio`, `desktop-app`, `developer-tools`, `dsh`, `dsh-plugin`, `electron`, `macos`, `plugin-manager`, `windows`
- Forks: 43
- Open Issues: 2
- Last push: 2026-08-20T10:56:44.000Z
- Added: 2026-08-20T00:00:00.000Z

## Install

```bash
dsh plugin --profile web add github:fufankeji/deepseek-harness-studio/packages/bundle/headless
```

## Wiki

## 一句话定位
DSH 一次性任务运行包：在命令行提交一段任务文本，由内置 Agent 执行完成后，把最后一段 assistant 回复打印到 stdout 并退出。它不开端口、不带 Web UI，只复用 dsh-base 的核心能力。

## 核心能力
- 解析 `dsh --profile headless "<task>"` 命令行，把多词位置参数拼成一条 task，缺失或空白任务在启动前就被拒绝（src/startup.ts:31-55）。
- 通过 `ctx.agents` 创建一个全新的持久化 Agent，把 task 当作普通用户消息提交，等待 Agent 自然回到 idle（src/index.ts:111-126）。
- 把会话 flush 到磁盘（复用 dsh-base 的 JSONL 会话持久化），汇总本次执行区间内的所有事件，取最后一段非空 assistant 文本写到 stdout（src/index.ts:127-129）。
- 根据最终 turn/end 的 reason 决定退出码：completed 退出 0，其它（含 aborted / error）退出 1；error 时还会把错误 code 与 message 写到 stderr（src/index.ts:129-133）。
- 通过 launcher 提供的 `ctx.appExit` 主进程钩子请求退出，并保证没有监听端口（src/index.ts:144-149 / README.md:5-7）。
- 作为 dsh-base 之上的薄叠加：覆盖 system-prompt 的 persona、关闭 HMR、设置 tools mode、挂载 Code Mode 的 worker 运行时（cordis.patch.yml:7-26）。

## 技术实现
- **语言**: TypeScript（ESM 源码，发布为 `lib/index.js` / `lib/types/index.d.ts`，package.json:13-19）。
- **关键依赖**: `@deepseek-ai/dsh-cmdline`（命令行与 appExit）、`@deepseek-ai/dsh-code-runtime-worker-thread`（Code Mode worker 运行时）、`commander`（命令行解析）、`@deepseek-ai/schemastery`（Config 校验）（package.json:46-51）。
- **架构模式**: cordis bundle patch——`cordis.patch.yml` 在 dsh-base 之上插入若干行并改写 system-prompt / hmr / tools 的 config，再插入 `headless-startup`（命令行提供者）与 `headless-runner`（任务执行者）两个插件；runner 通过 `inject: [headlessStartup]` 拿到 task 文本，启动器通过 `provideCmdline` 注入 `cmdlineArgs` 与 `appExit`（cordis.patch.yml:7-35 / src/startup.ts:49-56 / src/index.ts:141-149）。
- **入口文件**: `src/index.ts`（headless-runner）、`src/startup.ts`（headless-startup）、`src/invariant.ts`（包内不变量占位）。

## 适用场景
想在 CI、脚本或一次性调试里跑一句自然语言任务并拿到文本结论的用户，比如批量跑回归提示词、把 Agent 当命令行工具嵌入到现有工作流、或在没有浏览器的服务器环境里验证一段 prompt。不适合需要多轮对话、网页交互或多人协作的场景——这类需求请用 dsh 的 Web/Desktop/TUI 包。

## 前置依赖与兼容性
| 依赖 | 最低版本 | 说明 |
|---|---|---|
| DSH（launcher） | 0.1.0-rc.8+ | 必须通过 `dsh --profile headless` launcher 调用，runner 强依赖 launcher 的 `ctx.appExit` 与 `ctx.cmdlineArgs`（src/index.ts:144-147 / src/startup.ts:13-16）。 |
| Node.js | ^22.19.0 \|\| >=24.0.0 | 由仓库根 `engines.node` 约束，bundle/headless 自身未声明独立版本要求（package.json:4 / 仓库根 package.json:8-10）。 |
| 平台 | 跨平台 | macOS / Windows / Linux 均可；不依赖任何原生模块或平台特定二进制（package.json 中无 os/cpu 字段）。 |
| 原生模块 | 无 | 本包未引入新的原生模块；下层 sqlite 走 `:memory:` 且默认不打开（packages/bundle/base/cordis.patch.yml:117-121）。 |

## 安装方式
```bash
dsh plugin --profile web add github:fufankeji/deepseek-harness-studio/packages/bundle/headless
```

## 配置项
| 配置 | 类型 | 说明 | 默认值 |
|---|---|---|---|
| `task` | 字符串（必填） | 一次性任务的提示文本，由命令行提供方注入到 `ctx.headlessStartup.task` 后传入 runner；运行时整段作为用户消息提交给 Agent（src/index.ts:31-38, 122-125 / cordis.patch.yml:31-35）。 | 由调用方提供，无内建默认值 |
| `DSH_TOOLS_MODE` 环境变量 | `native` / `code` / `both` | 通过 cordis.patch.yml 写到 tools 行的 `mode` 字段，控制 Code Mode 与原生工具的开关；在 Web bundle 中也使用同一变量（cordis.patch.yml:18-21 / packages/bundle/web-app/cordis.patch.yml:44-48）。 | 未设置（沿用 tools 插件 schema 默认） |

## 常见问题
**Q: 跟 dsh 自带的 web 或 TUI 模式冲突吗？**

A: 不冲突。`dsh --profile headless` 是独立入口，只挂上 dsh-base + headless-runner；它不启动 host、HTTP server 或 Web 运行时，跟 `--profile web` / `--profile tui` 是并列的运行时档位（README.md:5 / cordis.patch.yml:1-5）。

**Q: 任务需要付费模型 key 怎么办？**

A: 不在 headless 包里配。模型选择走 dsh-base 的 `agent-default-model`（默认 `deepseek-official` / `deepseek-v4-flash`），key 通过 `$DSH_HOME/settings.yaml` 的 `llm-deepseek:` 段或对应环境变量提供，Web Models 页面写的就是这份文档（packages/bundle/base/cordis.patch.yml:63-67, 78-79）。

**Q: 能一次调用连续追问吗？**

A: 不能。runner 是真正的“一次性”——它只 followup 一次任务消息，然后等 Agent 自然回到 idle 就退出；没有任何交互式后续入口。要做多轮请用 Web/TUI profile（README.md:18-19 / src/index.ts:120-129）。

**Q: 失败时如何排查？**

A: 看退出码与 stderr。退出码 1 通常对应任务未完成（aborted / error / 区间内无 turn）或启动期异常；error 时 stderr 会打印 `dsh: <code>: <message>`。直接创建 Agent 失败、序列化失败、loader 期被释放等场景也会走 stderr → 退出 1 的链路（src/index.ts:85-88, 129-133 / tests/headless.spec.ts:146-196）。

**Q: 卸载会影响其它 profile 吗？**

A: headless 是独立 bundle，只在 `--profile headless` 启动时被加载；其它 profile（web、tui、desktop 等）都依赖自己的 bundle 补丁，删除本包不会影响它们的运行（README.md:5 / cordis.patch.yml）。

**Q: 任务文本里需要 quote 怎么办？**

A: 直接用 shell 引号包裹位置参数；解析器用 `program.args.join(' ')` 拼接，不会再做引号剥离，因此外层引号由 shell 处理后传入的是纯文本（src/startup.ts:31-41, 51-53）。

**Q: DSH_TOOLS_MODE 不设置会怎样？**

A: tools 行 schema 没有显式默认值时走默认配置（保持原生工具可用）。`code` 切到 Code Mode，`both` 同时启用，注释把它定位为“临时过渡选项”（cordis.patch.yml:17-21 / packages/bundle/web-app/cordis.patch.yml:44-48）。

## 上手难度
入门 — 命令只有一行位置参数；需要 dsh launcher 已经能跑起来，模型 key 通过 settings 文档配置，不需要写 cordis.yml。

## 已知问题与限制
- **只能提交一次任务**：runner 没有交互式追问 surface；它等 Agent 在返回 idle 前完成的所有工作，并打印该区间内最后一条非空 assistant 消息（README.md:18-19）。
- **退出钩子由 launcher 持有**：在 dsh 启动器之外直接挂载 headless-runner 会立刻报错直到宿主提供 `ctx.appExit`；这是 by-design 的硬约束（README.md:20 / src/index.ts:144-147）。
- **Loader 结算期间被释放会静默放弃**：早期进程关闭可能在 `ctx.loader.await()` 期间触发 fiber 释放；runner 通过检查 `agents / agentDefaultModel / sessions` 是否还在来决定是否提前返回，不会再请求退出（src/index.ts:99-104 / tests/headless.spec.ts:220-241）。
- **区间内没有任何 turn 也退出 1**：仅 followup 没产生 turn/end 时，runner 仍走“reason.kind 非 completed → 退出 1”的路径，stdout 只输出换行（src/index.ts:129-133 / tests/headless.spec.ts:175-179）。

---

This document is auto-generated by [deepseek-plugin.org](https://deepseek-plugin.org). HTML page: [deepseek-harness-studio](https://deepseek-plugin.org/plugins/fufankeji/deepseek-harness-studio/packages/bundle/headless)
Wiki generated by AI (model: `MiniMax-M3`)
