第 4 章 命令行与无头模式:把 Harness 变成可脚本化的工具
Web UI 适合人机交互。但真正让 dsh 值钱的,是它可以被脚本和 CI 调用。这一章讲 dsh 命令本身。
4.1 命令语法:先搞清"谁的参数"
dsh 是启动器,它只解析自己的 flag,把剩下的参数交给 profile 里的应用插件解析。一句话:launcher 的参数在前,应用的参数在后,遇到第一个启动器不认识的部分,就开始算应用的。
代码块
Plain Text
dsh --profile web --port 8080 # --port 属于 web 应用
dsh --profile headless "run the tests"
dsh --profile web --help # 打印 web 应用帮助
dsh --help # 打印启动器自己的帮助
4.2 入口模式一览
#hntcIRpCXUlt text { white-space: pre; }#hntcIRpCXUlt .fb0 { stroke: var(--N900-FG, 0.15); }#hntcIRpCXUlt .fb0 {stroke: rgba(31,35,41, 0.15)}#hntcIRpCXUlt .fx0 { stroke-miterlimit: 10; }#hntcIRpCXUlt .fx0 {stroke-miterlimit: 10}#hntcIRpCXUlt .fa1 { fill: rgb(187, 191, 196); }#hntcIRpCXUlt .fa1 {fill: rgb(187, 191, 196)}#hntcIRpCXUlt .fx1 { font: 400 12px MonospacedNumber, LarkHackSafariFont, LarkEmojiFont, LarkChineseQuote, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Tahoma, "PingFang SC", "Microsoft Yahei", Arial, "Hiragino Sans GB", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; }#hntcIRpCXUlt .fx1 {font-style: normal}#hntcIRpCXUlt .fx2 { text-anchor: middle; }#hntcIRpCXUlt .fx2 {text-anchor: middle}#hntcIRpCXUlt .fx3 { dominant-baseline: central; }#hntcIRpCXUlt .fx3 {dominant-baseline: central}#hntcIRpCXUlt .fx4 { stroke: none; }#hntcIRpCXUlt .fx4 {stroke: none}#hntcIRpCXUlt .fc0 { fill: var(--text-caption); }#hntcIRpCXUlt .fc0 {fill: #646a73}#hntcIRpCXUlt .fa2 { fill: var(--text-link-disabled); }#hntcIRpCXUlt .fa2 {fill: rgb(187, 191, 196)}#hntcIRpCXUlt .fa3 { fill: rgb(232, 239, 250); }#hntcIRpCXUlt .fa3 {fill: rgb(232, 239, 250)}#hntcIRpCXUlt .fx5 { font: 700 12px MonospacedNumber, LarkHackSafariFont, LarkEmojiFont, LarkChineseQuote, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Tahoma, "PingFang SC", "Microsoft Yahei", Arial, "Hiragino Sans GB", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; }#hntcIRpCXUlt .fx5 {font-style: normal}#hntcIRpCXUlt .fx6 { text-anchor: start; }#hntcIRpCXUlt .fx6 {text-anchor: start}#hntcIRpCXUlt .fx7 { dominant-baseline: alphabetic; }#hntcIRpCXUlt .fx7 {dominant-baseline: alphabetic}#hntcIRpCXUlt .fc1 { fill: rgb(30, 42, 58); }#hntcIRpCXUlt .fc1 {fill: rgb(30, 42, 58)}#hntcIRpCXUlt .fx8 { font: 400 12px LarkHackSafariFont, LarkEmojiFont, Consolas, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "Noto Sans", Tahoma, "Nimbus Sans L", Arial, "Liberation Sans", "PingFang SC", "Hiragino Sans GB", "Noto Sans CJK SC", "Source Han Sans SC", "Source Han Sans CN", "Microsoft YaHei", "Wenquanyi Micro Hei", "WenQuanYi Zen Hei", STHeiti, SimHei, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", system-ui; }#hntcIRpCXUlt .fx8 {font-style: normal}#hntcIRpCXUlt .fc2 { fill: rgb(26, 58, 122); }#hntcIRpCXUlt .fc2 {fill: rgb(26, 58, 122)}#hntcIRpCXUlt .fx9 { font: 400 12px MonospacedNumber, LarkHackSafariFont, LarkEmojiFont, LarkChineseQuote, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Tahoma, "PingFang SC", "Microsoft Yahei", Arial, "Hiragino Sans GB", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; }#hntcIRpCXUlt .fx9 {font-style: normal}#hntcIRpCXUlt .fx10 { font: 400 11px LarkHackSafariFont, LarkEmojiFont, Consolas, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "Noto Sans", Tahoma, "Nimbus Sans L", Arial, "Liberation Sans", "PingFang SC", "Hiragino Sans GB", "Noto Sans CJK SC", "Source Han Sans SC", "Source Han Sans CN", "Microsoft YaHei", "Wenquanyi Micro Hei", "WenQuanYi Zen Hei", STHeiti, SimHei, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", system-ui; }#hntcIRpCXUlt .fx10 {font-style: normal}#hntcIRpCXUlt .fb1 { fill: var(--N900-FG, 0.15); }#hntcIRpCXUlt .fb1 {fill: rgba(31,35,41, 0.15)}#hntcIRpCXUlt .fb2 { fill: rgb(201, 212, 230); }#hntcIRpCXUlt .fb2 {fill: rgb(201, 212, 230)}#hntcIRpCXUlt .fa4 { fill: var(--bg-body); }#hntcIRpCXUlt .fa4 {fill: #fff}AB1234567命令作用dsh webdsh --profile headless "任务"dsh --profile
4.3 无头模式:一行命令跑一个任务
无头模式是脚本化的核心。它接受一个任务文本,创建并持久化一个全新会话,跑完后打印 agent 的最终文本回复并退出:
代码块
Plain Text
dsh --profile headless "fix the failing test in this workspace"
注意:
•
调用目录是默认工作区根;
•
web 和 headless 两个 profile 首次使用会自动初始化;
•
会话是持久化的——跑过的任务会留下日志,这对批量任务和审计非常有价值。
4.4 用 --patch 加载你自己的配置
这是开发插件的日常命令。你写了一个插件和对应的 patch 文件,不想装进 profile,只想临时试一下:
代码块
Plain Text
dsh web --patch ./scratch-plugin/cordis.yml
多个 patch 可以叠加,按参数顺序应用。用 --dump-config 可以先看组合结果再启动。
4.5 dsh plugin:管理 Profile 的插件
前面说过,profile 的 manifest 不需要手写,dsh plugin 帮你维护:
代码块
Plain Text
dsh plugin --profile demo add ./hello-plugin # 安装本地插件
dsh plugin --profile demo add github:you/hello-plugin # 从 GitHub 安装
dsh plugin --profile demo remove dsh-hello-plugin # 移除
dsh plugin 本质上是在 profile 目录里转发 pnpm 命令,所以 add、remove、install 等 pnpm 子命令都可用。注意它只管理 profile 的依赖和 bundle 层,不会动你的全局环境。
4.6 实际场景:把它接进 CI
无头模式 + 退出码 + 会话日志,这三样组合起来就是一个完整的 agent 流水线:
代码块
Plain Text
# 示例:让 dsh 在 CI 里修测试并留档
进阶玩法:
•
用 --patch 按环境切换配置(比如 CI 里换更严格的沙箱策略);
•
把会话日志目录接进你的日志系统;
•
用 Python SDK(下一章)在代码里编排多任务。
4.7 本章小结
•
dsh 是启动器,先写 launcher flag,再写应用参数;
•
dsh --profile headless "任务" 是无头执行的核心命令;
•
--patch 让你临时叠加配置,--dump-config 让你预览配置树;
•
dsh plugin 管理 profile 的插件,底层转发 pnpm;
•
无头模式天然适合 CI 和批量自动化。
下一章:Python SDK——不通过命令行,直接在程序里驱动 Harness。