# Deepseek-Harness-Desktop

> DSH 一次性任务运行组合包：把命令行任务交给 Agent 执行后，把最后一段 assistant 文本写到 stdout 并退出，不开端口、不带 Web UI。

## Metadata

- Author: [@ChisaAlter](https://github.com/ChisaAlter)
- Repo: <https://github.com/ChisaAlter/Deepseek-Harness-Desktop.git>
- GitHub: [ChisaAlter/Deepseek-Harness-Desktop](https://github.com/ChisaAlter/Deepseek-Harness-Desktop)
- Stars: 127
- Language: JavaScript
- License: [MIT](https://spdx.org/licenses/MIT.html)
- Topics: `deepseek`, `deepseek-harness`, `dsh-plugin`
- Forks: 9
- Open Issues: 0
- Last push: 2026-08-20T11:02:01.000Z
- Added: 2026-08-20T00:00:00.000Z

## Install

```bash
dsh plugin --profile web add github:ChisaAlter/Deepseek-Harness-Desktop/vendor/deepseek-harness/packages/bundle/headless
```

## Wiki

> 本文档对应 vendor 仓中 `vendor/deepseek-harness/packages/bundle/headless` 子路径，是落地页 `/plugins/{owner}/{repo}` 中**插件百科**模块的内容来源。

## 一句话定位
DSH 一次性任务运行组合包：在命令行提交一段任务文本，由内置 Agent 执行完成后，把最后一段 assistant 回复打印到 stdout 并退出。它不开端口、不带 Web UI、也不依赖 host 层，只是叠在 dsh-base 之上的一层薄补丁。

## 核心能力
- 解析 `dsh --profile headless "<task>"` 命令行，把多词位置参数拼成一条 task；缺失或只有空白任务在启动前就被拒绝，runner 不会被激活（src/startup.ts:31-55 / tests/startup.spec.ts）
- 通过 `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 / 区间内无 turn）退出 1；error 时还会把 code 与 message 写到 stderr（src/index.ts:129-133 / tests/headless.spec.ts:146-179）
- 通过 launcher 提供的 `ctx.appExit` 主进程钩子请求退出，并保证不开任何监听端口（src/index.ts:144-149 / README.md:5-7）
- 作为 dsh-base 之上的薄叠加：覆盖 system-prompt 的 persona、关闭 HMR、设置 tools mode、追加 Code Mode 的 worker 运行时与 headless-startup / headless-runner 两个插件（cordis.patch.yml:7-35）

## 技术实现
- **语言**: TypeScript（ESM 源码，发布为 `lib/index.js` / `lib/types/index.d.ts`）
- **关键依赖**: `@deepseek-ai/dsh-cmdline`（命令行与 appExit）、`@deepseek-ai/dsh-code-runtime-worker-thread`（Code Mode worker 运行时）、`commander`（命令行解析）、`@deepseek-ai/schemastery`（Config 校验）
- **架构模式**: 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。本条目收录的是 Deepseek-Harness-Desktop 把上游 headless 组合 vendored 进 dsh-desktop 的同一份上游实现，本身不是 Desktop 端 UI 的承载者。

## 前置依赖与兼容性
| 依赖 | 最低版本 | 说明 |
|---|---|---|
| DSH（launcher） | 0.1.0-rc.7+ | 自身声明 `0.1.0-rc.7`（package.json:4）；必须通过 `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 | 由 vendored harness 根 `engines.node` 约束；bundle/headless 自身未单独声明（vendor/deepseek-harness/package.json:8-10） |
| 平台 | 跨平台 | macOS / Windows / Linux 均可；不依赖任何原生模块或平台特定二进制（package.json 中无 os/cpu 字段） |
| 原生模块 | 无 | 本包未引入新的原生模块；下层 sqlite 走 `:memory:` 且默认不打开 |

## 安装方式
```bash
dsh plugin --profile web add github:ChisaAlter/Deepseek-Harness-Desktop/vendor/deepseek-harness/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 叠加使用同一变量（cordis.patch.yml:18-21） | 未设置（沿用 tools 插件 schema 默认） |

## 常见问题

**Q: 这个 bundle 跟 dsh-base 是什么关系？**

A: headless 是叠在 dsh-base 之上的薄补丁（patch over），不是替代品。它本身不重复注册任何 host、HTTP、Web 或浏览器能力，只覆写 dsh-base 中 system-prompt 的 persona、关闭 HMR、转发 tools mode、追加 Code Mode 的 worker 运行时，再额外插入 `headless-startup`（命令行提供者）与 `headless-runner`（任务执行者）两个插件（cordis.patch.yml:7-35）。

**Q: 必须用 `--profile headless` 启动吗？**

A: 是的。`headless-startup` 的 commander 名是 `dsh --profile headless`，且 runner 显式要求 launcher 提供 `ctx.appExit`；直接用其他 profile 启动会缺 host 钩子，在挂载阶段立即抛错（src/index.ts:144-147 / src/startup.ts:31-41）。

**Q: 任务里需要引号怎么办？**

A: shell 的引号规则即可，多词位置参数会被 startup 解析器用空格拼成单个 task（src/startup.ts:51-55）。

**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-217）。

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

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

**Q: 模型 key 怎么提供？**

A: 不在 headless 包里配。模型选择走 dsh-base 的 `agent-default-model`，key 通过 `$DSH_HOME/settings.yaml` 的 `llm-deepseek:` 段或对应环境变量提供；headless 只是叠加在最上层（cordis.patch.yml）。

## 上手难度
入门 — 只调一次 `dsh --profile headless "<task>"` 就能跑，本包没有额外的配置面、也没有持久 UI 状态。

## 已知问题与限制
- 只能提交一次任务；runner 没有交互式后续入口，等 Agent 自然回到 idle 就退出，不会再读 stdin 或 followup（README.md:19 / src/index.ts:120-129）
- `ctx.appExit` 必须由启动器提供；在 `dsh` 启动器之外激活 headless-runner 会在挂载阶段立即抛 `the launcher must provide ctx.appExit before the tree mounts`（src/index.ts:144-147 / tests/headless.spec.ts:243-245）
- 自身不持有运行时不变量检查 —— `./invariant` 注册的是空安装器，由 launcher 端到端测试兜底覆盖，进程内不审计（src/invariant.ts:18-22）
- Loader 结算期间若 tree 被 dispose，runner 会直接放弃本次运行、不再请求退出（src/index.ts:99-104 / tests/headless.spec.ts:220-241）

---

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