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

并行会话调度 nicremo/dsh-cli

DeepSeek Harness
nicremo25 天前有提交已提供中文文档扫描:含敏感能力

让 Claude Code、Codex、OpenCode、Pi 或任何其他智能体并行运行 DeepSeek…

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

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

自动兼容检查有告警:未发布到 npm registry,仅可从源码安装。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add nicremo/dsh-cli
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · ui
装得上吗
本站已在 dsh 0.2.1-rc.2 下真实安装成功
非静态推断
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 25 天前
档位由哪些信号合成

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查⚠ 装前注意

未发布到 npm registry,仅可从源码安装

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✗npm 包dsh-cli(未发布到 npm,仅可源码安装)
✓Node 引擎要求 >=22.19 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/27 23:41:36

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

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

可执行外部命令读写/删除本地文件读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 114 处,展示前 94 条)
  • 读取本地文件
    src/cli.mjs:25
    const VERSION = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version
  • 读取本地文件
    src/cli.mjs:90
    return readFileSync(0, 'utf8')
  • 读取本地文件
    src/cli.mjs:97
    if (file) return readFileSync(resolve(file), 'utf8')
  • 读取本地文件
    src/cli.mjs:116
    if (existsSync(join(current, WORKSPACE_MARKER))) return true
  • 写入本地文件
    src/cli.mjs:126
    mkdirSync(abs, { recursive: true })
  • 写入本地文件
    src/cli.mjs:128
    writeFileSync(join(abs, WORKSPACE_MARKER), JSON.stringify({ kind, createdAt: new Date().toISOString() }) + '\n')
  • 读取本地文件
    src/cli.mjs:130
    if (force || !existsSync(agents)) writeFileSync(agents, workspaceAgentsMd({ name: basename(abs), kind, description }))
  • 写入本地文件
    src/cli.mjs:130
    if (force || !existsSync(agents)) writeFileSync(agents, workspaceAgentsMd({ name: basename(abs), kind, description }))
  • 读取本地文件
    src/cli.mjs:138
    if (!existsSync(abs)) ensureWorkspace(abs, { kind })
  • 执行外部命令来自配置/变量
    src/cli.mjs:789
    const res = spawnSync(python, [CAPTURE_PY, ...argv.filter((a) => a !== '--json')], { stdio: 'inherit' })
  • 执行外部命令来自配置/变量
    src/cli.mjs:796
    spawnSync(opener, [url], { stdio: 'ignore' })
  • 读取本地文件
    src/collect.mjs:17
    entries = readdirSync(current, { withFileTypes: true })
  • 读取本地文件
    src/collect.mjs:26
    const st = statSync(full)
  • 读取本地文件
    src/collect.mjs:31
    if (existsSync(dir)) walk(dir)
  • 读取本地文件
    src/collect.mjs:45
    return existsSync(file) ? readFileSync(file, 'utf8') : null
  • 读取本地文件
    src/collect.mjs:45
    return existsSync(file) ? readFileSync(file, 'utf8') : null
  • 写入本地文件
    src/collect.mjs:106
    writeFileSync(indexPath, index)
  • 写入本地文件
    src/collect.mjs:111
    writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n')
  • 写入本地文件
    src/collect.mjs:115
    writeFileSync(sourcesPath, JSON.stringify([...sources.values()], null, 2) + '\n')
  • 读取本地文件
    src/dsh.mjs:40
    accessSync(candidate, constants.X_OK)
  • 读取本地文件
    src/dsh.mjs:59
    const isCheckout = (repo) => existsSync(join(repo, 'apps', 'cli', 'src', 'bin.ts')) && existsSync(join(repo, 'node_modules', 'tsx'))
  • 读取本地文件
    src/dsh.mjs:89
    if (!existsSync(file)) return {}
  • 读取本地文件
    src/dsh.mjs:90
    return yaml.load(readFileSync(file, 'utf8')) ?? {}
  • 读取本地文件
    src/dsh.mjs:94
    return existsSync(join(profileDir(PROFILE), 'package.json')) ? PROFILE : 'headless'
  • 写入本地文件
    src/dsh.mjs:112
    writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', { mode: 0o600 })
  • 写入本地文件
    src/dsh.mjs:114
    writeFileSync(patchPath, `# dsh-cli: per-job settings with a model override\n- id: settings\n config:\n path: ${JSON.stringify(settingsPath)}\n
  • 写入本地文件
    src/profile.mjs:85
    mkdirSync(dir, { recursive: true })
  • 读取本地文件
    src/profile.mjs:90
    if (!existsSync(source)) continue
  • 读取本地文件
    src/profile.mjs:93
    lstatSync(target)
  • 写入本地文件
    src/profile.mjs:115
    mkdirSync(home(), { recursive: true })
  • 读取本地文件
    src/profile.mjs:116
    if (!existsSync(join(dir, 'package.json'))) {
  • 执行外部命令来自配置/变量
    src/profile.mjs:119
    const res = spawnSync(c.cmd, c.args, { env: c.env, cwd: home(), encoding: 'utf8', timeout: 180000 })
  • 读取本地文件
    src/profile.mjs:120
    if (res.status !== 0 || !existsSync(join(dir, 'package.json'))) {
  • 读取本地文件
    src/profile.mjs:134
    if (existsSync(sourcePatch)) {
  • 读取本地文件
    src/profile.mjs:135
    ;({ rows, dump } = extractRows(readFileSync(sourcePatch, 'utf8'), mcpFilter(mcp)))
  • 写入本地文件
    src/profile.mjs:142
    writeFileSync(patchFile, renderPatch({ rows, dump, skillsDir, sourceProfile: from }), { mode: 0o600 })
  • 写入本地文件
    src/profile.mjs:144
    chmodSync(patchFile, 0o600)
  • 执行外部命令来自配置/变量
    src/profile.mjs:148
    const check = spawnSync(c.cmd, c.args, { env: c.env, cwd: home(), encoding: 'utf8', timeout: 180000 })
  • 执行外部命令来自配置/变量
    src/profile.mjs:166
    const res = spawnSync(python, ['-c', 'import importlib.metadata as m; print(m.version("playwright"))'], { encoding: 'utf8', timeout: 15000 })
  • 读取密钥环境变量
    src/profile.mjs:195
    const hasCreds = existsSync(join(dshHome(), '.credentials.yaml')) || Boolean(process.env.DEEPSEEK_API_KEY)
  • 执行外部命令来自配置/变量
    src/profile.mjs:201
    const res = spawnSync(c.cmd, c.args, { env: c.env, cwd: home(), encoding: 'utf8', timeout: 120000 })
  • 写入本地文件
    src/profile.mjs:212
    mkdirSync(home(), { recursive: true })
  • 写入本地文件
    src/runner.mjs:29
    writeFileSync(taskFile(job.id), taskText)
  • 读取本地文件
    src/runner.mjs:34
    return existsSync(taskFile(id)) ? readFileSync(taskFile(id), 'utf8') : ''
  • 读取本地文件
    src/runner.mjs:34
    return existsSync(taskFile(id)) ? readFileSync(taskFile(id), 'utf8') : ''
  • 读取本地文件
    src/runner.mjs:51
    if (!existsSync(file)) return ''
  • 读取本地文件
    src/runner.mjs:52
    return readFileSync(file, 'utf8')
  • 写入本地文件
    src/runner.mjs:65
    writeFileSync(cancelMarker(id), nowIso())
  • 读取本地文件
    src/runner.mjs:84
    if (existsSync(cancelMarker(id))) return updateJob(id, { status: 'cancelled', endedAt: nowIso() })
  • 写入本地文件
    src/runner.mjs:89
    mkdirSync(job.cwd, { recursive: true })
  • 写入本地文件
    src/runner.mjs:90
    if (job.outDir) mkdirSync(job.outDir, { recursive: true })
  • 执行外部命令来自配置/变量
    src/runner.mjs:100
    const child = spawn(spec.cmd, spec.args, {
  • 写入本地文件
    src/runner.mjs:135
    appendFileSync(eventsPath, chunk)
  • 读取本地文件
    src/runner.mjs:162
    if (existsSync(cancelMarker(id))) {
  • 写入本地文件
    src/runner.mjs:170
    appendFileSync(stderrPath, `dsho: spawn failed: ${err.message}\n`)
  • 执行外部命令来自配置/变量
    src/runner.mjs:365
    const child = spawn(process.execPath, [BIN, ...args], {
  • 读取本地文件
    src/skills.mjs:15
    const VERSION = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version
  • 读取本地文件
    src/skills.mjs:30
    const st = lstatSync(path)
  • 读取本地文件
    src/skills.mjs:32
    return existsSync(join(path, MARKER)) ? 'ours' : 'foreign'
  • 读取本地文件
    src/skills.mjs:45
    if (!existsSync(join(BUNDLED, 'SKILL.md'))) throw new DshoError(`bundled skill missing at ${BUNDLED}`, 5)
  • 删除本地文件
    src/skills.mjs:51
    if (kind === 'link') rmSync(canonical)
  • 删除本地文件递归
    src/skills.mjs:52
    if (kind === 'ours') rmSync(canonical, { recursive: true, force: true })
  • 写入本地文件
    src/skills.mjs:53
    mkdirSync(agentsSkillsDir(), { recursive: true })
  • 写入本地文件
    src/skills.mjs:55
    writeFileSync(join(canonical, MARKER), `dsh-cli ${VERSION}\n`)
  • 读取本地文件
    src/skills.mjs:61
    if (!wanted && !existsSync(t.parent)) {
  • 删除本地文件
    src/skills.mjs:71
    if (existing === 'link') rmSync(target)
  • 删除本地文件递归
    src/skills.mjs:72
    if (existing === 'ours') rmSync(target, { recursive: true, force: true })
  • 写入本地文件
    src/skills.mjs:73
    mkdirSync(t.dir, { recursive: true })
  • 写入本地文件
    src/store.mjs:24
    mkdirSync(jobDir(id), { recursive: true })
  • 读取本地文件
    src/store.mjs:73
    if (!existsSync(dir)) return []
  • 读取本地文件
    src/store.mjs:74
    return readdirSync(dir).filter((name) => !name.startsWith('.')).sort().reverse()
  • 写入本地文件
    src/store.mjs:87
    mkdirSync(batchDir(id), { recursive: true })
  • 读取本地文件
    src/supervisor.mjs:26
    if (existsSync(batchCancelMarker(id)) && !existsSync(cancelMarker(jid))) writeFileSync(cancelMarker(jid), nowIso())
  • 写入本地文件
    src/supervisor.mjs:26
    if (existsSync(batchCancelMarker(id)) && !existsSync(cancelMarker(jid))) writeFileSync(cancelMarker(jid), nowIso())
  • 写入本地文件
    src/supervisor.mjs:42
    writeFileSync(batchCancelMarker(id), nowIso())
  • 写入本地文件
    src/util.mjs:44
    mkdirSync(dirname(file), { recursive: true })
  • 写入本地文件
    src/util.mjs:46
    writeFileSync(tmp, JSON.stringify(value, null, 2) + '\n')
  • 写入本地文件
    src/util.mjs:47
    renameSync(tmp, file)
  • 读取本地文件
    src/util.mjs:53
    return JSON.parse(readFileSync(file, 'utf8'))
  • 读取密钥环境变量
    src/web.mjs:27
    process.env.DSHO_WEB_TOKEN_FILE,
  • 读取密钥环境变量
    src/web.mjs:32
    const urls = process.env.DSHO_WEB_TOKEN_URL ? [process.env.DSHO_WEB_TOKEN_URL] : []
  • 读取本地文件
    src/web.mjs:34
    if (!existsSync(file)) continue
  • 读取本地文件
    src/web.mjs:35
    const raw = readFileSync(file, 'utf8').trim()
  • 写入本地文件
    src/web.mjs:52
    mkdirSync(home(), { recursive: true })
  • 写入本地文件
    src/web.mjs:53
    writeFileSync(tokenUrlFile(), parsed.href + '\n', { mode: 0o600 })
  • 发起网络请求
    src/web.mjs:71
    res = await fetch(t, { redirect: 'manual', signal: AbortSignal.timeout(5000) })
  • 写入本地文件
    src/web.mjs:78
    mkdirSync(home(), { recursive: true })
  • 写入本地文件
    src/web.mjs:79
    writeFileSync(authFile(), JSON.stringify({ origin: baseUrl(), cookie, savedAt: new Date().toISOString() }) + '\n', { mode: 0o600 })
  • 发起网络请求
    src/web.mjs:111
    res = await fetch(`${baseUrl()}/api/${method}`, {
  • 发起网络请求
    src/web.mjs:135
    let res = await fetch(`${baseUrl()}/`, { headers: { cookie: c }, redirect: 'manual', signal: AbortSignal.timeout(2500) })
  • 发起网络请求
    src/web.mjs:139
    res = await fetch(`${baseUrl()}/`, { headers: { cookie: fresh }, redirect: 'manual', signal: AbortSignal.timeout(2500) })
  • 写入本地文件
    src/web.mjs:278
    mkdirSync(logsDir(), { recursive: true })
  • 执行外部命令来自配置/变量
    src/web.mjs:281
    const child = spawn(cmd, args, { detached: true, stdio: ['ignore', fd, fd], env, cwd: homedir() })
  • 读取本地文件
    src/web.mjs:287
    const text = readFileSync(logFile, 'utf8')
扫描于 2026/11/2(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

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

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

界面预览

1 张 · 点击查看大图

功能亮点

  • 批量派发会话
  • 并行运行智能体
  • 实时显示网页界面

让 Claude Code、Codex、OpenCode、Pi 等智能体并行跑 DeepSeek Harness(dsh)会话:批量调研、素材收集,实时显示在 DSH 网页界面里。非官方社区工具。

README

由 DeepSeek 最新模型翻译生成
dsh-cli 让 Claude Code、Codex、OpenCode、Pi 或任何其他智能体并行运行 DeepSeek Harness 会话。 deepseekharnesscli.nicremo.de 非官方社区项目。 dsh-cli 与 DeepSeek 没有关联,也未获其认可或赞助。「DeepSeek Harness」是 DeepSeek 的商标,此处仅用于说明兼容性。 为什么做这个 我用 DeepSeek Harness(dsh)主要做研究,也用来跑大量并行会话:一次问十个研究问题、收集文章截图、博客长文的滚动视频、素材视频。DeepSeek 又快又便宜,很适合当工作池。缺的是让主编程智能体驱动它的办法:启动会话、等它跑完、读结果、继续追问。dsh-cli 就是干这个的。 dsh-cli 以智能体为先。每条命令都非交互、立即返回,输出紧凑文本或 --json,退出码稳定。它附带一份 Agent Skill,教 Claude Code、Codex、OpenCode、Pi 等 harness 什么时候、怎样把活交给 DeepSeek Harness。
展开完整文档(README 剩余 1 节)
dsh-cli

让 Claude Code、Codex、OpenCode、Pi 或任何其他智能体并行运行 DeepSeek Harness 会话。

deepseekharnesscli.nicremo.de

非官方社区项目。 dsh-cli 与 DeepSeek 没有关联,也未获其认可或赞助。「DeepSeek Harness」是 DeepSeek 的商标,此处仅用于说明兼容性。

为什么做这个

我用 DeepSeek Harness(dsh)主要做研究,也用来跑大量并行会话:一次问十个研究问题、收集文章截图、博客长文的滚动视频、素材视频。DeepSeek 又快又便宜,很适合当工作池。缺的是让主编程智能体驱动它的办法:启动会话、等它跑完、读结果、继续追问。dsh-cli 就是干这个的。

dsh-cli 以智能体为先。每条命令都非交互、立即返回,输出紧凑文本或 --json,退出码稳定。它附带一份 Agent Skill,教 Claude Code、Codex、OpenCode、Pi 等 harness 什么时候、怎样把活交给 DeepSeek Harness。

功能

- 并行研究批次:dsho research "q1" "q2" ... 每个问题跑一个 worker;各自写出 report.md 和 sources.json,dsho collect 把它们合并成一个 INDEX.md。
- 素材收集:dsho materials "brief" 每种素材跑一个 worker(截图、滚动视频、新闻文章、素材视频、素材照片),并用 manifest.json 记录每个文件的来源和许可。
- 在 DSH web UI 里实时可见:dsh web 运行时,每个任务都是该 UI 里的真实会话。一个批次自成一个工作区,每个会话都有可读的标题,你可以围观,也可以随时介入。
- 无界面兜底:没有 UI 时,每个任务在独立的 headless dsh 进程里跑,使用精简配置档(orchestra),复用你的研究 MCP 服务器。
- 像人一样操控:wait、status、logs -f、result、files、continue(同一会话、同一文件夹)、cancel。
- 确定性捕获:dsho capture shot|scroll <url> 用 Playwright 拍干净的截图和流畅的 1080p 滚动视频(关掉 cookie 横幅、隐藏浮层、拦截广告网络、看门狗处理卡死页面)。
- 无守护进程:状态就是 ~/.dsho/ 里的普通 JSON;分离的 runner 进程让任务在调用它的 shell 退出后继续存活。

工作原理

flowchart LR
A["Your agent(Claude Code, Codex, OpenCode, Pi)"] -- "dsho research ... --json" --> C[dsh-cli]
C -- "DSH web UI running" --> W["dsh websessions in one workspace"]
C -- "otherwise" --> H["headless dshone process per job"]
W --> F[("workspace folderreport.md, manifest.json, ...")]
H --> F
C -- "dsho wait / collect" --> F
F -- "INDEX.md" --> A

1. 你的智能体调用 dsho(通常通过内置技能)。
2. dsh-cli 创建工作区文件夹,每个任务对应一个 job,然后把每个 job 交给分离的 runner,并立即返回 id。
3. runner 要么在运行中的 DSH web UI 里创建会话(通过它的 HTTP API),要么启动 dsh --profile orchestra --json -,把任务放到 stdin。
4. 事件记录在 ~/.dsho/jobs/<id>/events.jsonl,进度记录在 job.json,最终答案记录在 result.md。
5. 你的智能体用 dsho wait 等待,读取 dsho result 或 dsho collect,再用 dsho continue 继续会话。

环境要求

- Node.js >=22.19
- DeepSeek Harness,用 npm install -g @deepseek-ai/dsh 安装(或从源码检出),并登录一次
- 可选,供 dsho capture 使用:Python 3 加 Playwright(pip install playwright && python3 -m playwright install chromium)和 ffmpeg
- macOS 或 Linux

安装

npm install -g dsh-cli && dsho setup

dsho setup 会创建无头 dsh 配置档 orchestra,并把 dsh-orchestration 技能安装到 ~/.agents/skills(OpenCode 和其他 harness 会读取这个目录),同时为已安装的 Claude Code、Codex 和 Pi 建立链接。运行 dsho doctor 检查一切是否就绪。

只想要技能?npx skills add nicremo/dsh-cli 也可以,但技能需要命令行。

安装后会得到两个命令:dsh-cli 和短别名 dsho。

快速开始

dsho ui start # optional: start the DSH web UI to watch jobs live
dsho research "AI agent pricing 2026" "State of MCP servers" "Prompt caching strategies" \
--depth standard -C ~/dsh-workspaces/ai-2026
dsho wait last-batch --timeout 9m # exit code 4: still running, wait again
dsho collect last-batch # writes ~/dsh-workspaces/ai-2026/INDEX.md

dsho run -w "Summarize the release notes of Astro 6" # one task, print the answer
dsho materials "Electric cars in Europe, neutral tone" --kinds screenshots,articles,stock-video
dsho continue last "Add three sources from 2026 and update report.md"
dsho capture scroll https://example.com/blog/post -o post.mp4 --duration 12

在你的智能体里使用

运行 dsho setup 之后,你的智能体就认识这个技能了。用自然语言提问即可:

Use DeepSeek Harness to research these 10 topics in parallel and give me a summary.
帮我找一些近期新闻文章的截图,再找一些关于太阳能的素材视频,用在我的视频里。

技能会告诉智能体:先启动一个批次,再按 shell 超时时间分步等待,读取 INDEX.md,抽查结果,并在需要的地方跟进。dsho guide 会打印同一份手册,供不支持技能的智能体使用。

命令

| 命令 | 作用 |
| dsho run <task> | 在后台跑一个任务(-w 等待并打印答案) |
| dsho batch -f tasks.txt | 并行跑多个任务,每个任务一个文件夹(.txt、按 --- 分块、.json、.jsonl) |
| dsho research ... | 并行调研工作进程(--depth quick\|standard\|deep、--focus) |
| dsho materials <brief> | 每种素材一个工作进程(--kinds、--count、--url) |
| dsho status [ref] | 查看总览,或某个任务、批次的详情 |
| dsho ls [--batches] | 列出任务或批次 |
| dsho wait <ref>... | 阻塞直到完成(--timeout、--any) |
| dsho result <ref> | 最终答案(--files) |
| dsho logs <job> | 工具调用时间线(-f、--full、--raw、--stderr) |
| dsho files <ref> | 任务创建的文件 |
| dsho continue <job> <text> | 继续同一个会话 |
| dsho cancel <ref> | 停止一个任务或整个批次 |
| dsho collect <batch> | 写入 INDEX.md,合并 manifest.json 和 sources.json |
| dsho capture shot\|scroll <url> | 截图和滚动视频 |
| dsho ws init <dir> | 创建工作区,并在 AGENTS.md 中写入约定 |
| dsho ui [start\|login] | 打开、启动或登录 DSH web UI |
| dsho skill install | 安装或刷新智能体技能 |
| dsho setup / dsho doctor | 一次性设置 / 环境检查 |
| dsho config [set\|unset] | 持久化设置 |
| dsho guide | 给智能体的手册 |

引用方式:任务 id、批次 id、唯一前缀、名称、last、last-batch。常用选项:-C <dir> 工作区文件夹,-m flash|pro|v4-flash|vision,--effort off|low|high|max,-t <duration> 时间限制,-p <n> 并行度,-b auto|web|headless 后端。每个命令都有 --help。

JSON 与退出码

每个命令都接受 --json,并打印一个对象:{ "ok": true, ... } 或 { "ok": false, "error", "code", "hint" }。

| 退出码 | 含义 |
| 0 | 成功 |
| 1 | 任务失败、超时或被取消 |
| 2 | 用法错误 |
| 3 | 找不到任务或批次 |
| 4 | 等待超时,或任务尚未完成 |
| 5 | 环境问题(缺少 dsh、UI 无法访问、缺少 Playwright) |

DSH web UI 后端

web UI 可访问时,dsh-cli 会与它的 HTTP API 通信(POST /api/<namespace>/<method>):注册工作区文件夹,在其中创建会话,给会话命名,可选地选择模型,发送任务,然后轮询会话日志,直到回答该任务的那一轮结束。会话日志会转换成 headless 模式产生的同一种事件格式,因此 logs、status 和 result 在任何地方行为一致。
- dsho ui start 在后台启动 dsh web,抓取它打印的 token URL 并登录。
- 如果你自己启动 dsh web,把打印的 URL 传一次:dsho ui login "http://127.0.0.1:3080/?token=..."。得到的 cookie 存在 ~/.dsho/web-auth.json(权限 0600),30 天内有效,重启后也仍然有效。
- -b web 让缺失的 UI 直接报错,而不是回退到 headless。

headless 后端与 dsho setup

headless 任务在任务文件夹里运行 dsh --profile orchestra --json -。dsho setup 用随附的 headless 模板构建这个配置档,再加上:

- 从另一个配置档(默认 web)复制的 MCP 服务器:--mcp research(firecrawl、Brave、context7、Exa、Tavily、Perplexity、Jina 等;默认)、all、none,或逗号分隔的 id 列表。复制的行可能含 API key,所以配置档文件以 0600 权限写入,且不打印任何内容。
- 精选的技能文件夹(~/.dsh/skills-orchestra,符号链接到 ~/.agents/skills),而不是你的整个技能池,这样 worker 提示词大约小三分之一。--skills a,b,c 选其他技能,--skills all 保留 dsh 默认值。
- .dsho-workspace 作为额外的项目根标记,这样批量子文件夹里的 worker 仍会加载工作区的 AGENTS.md。

配置

dsho config set <key> <value> 把设置存到 ~/.dsho/config.json。环境变量优先于文件。

| 键 | 环境变量 | 含义 |
| dshRepo | DSHO_DSH_REPO | 使用 dsh 源码检出(优先于 dshBin) |
| dshBin | DSHO_DSH_BIN | dsh 可执行文件路径(默认:PATH 上的 dsh) |
| webUrl | DSHO_WEB_URL | DSH web UI 的源(默认 http://127.0.0.1:3080) |
| backend | DSHO_BACKEND | auto、web 或 headless |
| workspaces | DSHO_WORKSPACES | 新工作区的根目录(默认 ~/dsh-workspaces) |
| python | DSHO_PYTHON | 带 Playwright 的 Python,用于截图 |

其他变量:DSHO_HOME(状态文件夹,默认 ~/.dsho)、DSH_HOME(dsh 主目录,默认 ~/.dsh)、DSHO_WEB_TOKEN_URL / DSHO_WEB_TOKEN_FILE(web UI 的 token URL)。

文件

~/.dsho/
config.json settings
web-auth.json cookie for the DSH web UI (0600)
jobs/<id>/ job.json, task.md, events.jsonl, stderr.log, result.md
batches/<id>/ batch.json
logs/ runner logs
<workspace>/
AGENTS.md, .dsho-workspace
<nn-job>/ one folder per batch job
INDEX.md after dsho collect

故障排查

| 症状 | 修复 |
| dsh not found | npm install -g @deepseek-ai/dsh,或 dsho config set dshBin <path> |
| 任务不在 UI 里显示 | dsho doctor;dsho ui start 或 dsho ui login <url>;用 -b web 启动任务 |
| 任务状态为 lost | 它的 runner 进程已死(重启、被杀);重新启动它 |
| headless worker 卡住 | 把 dsh 的权限预设设为完全访问,用于无人值守运行(dsho doctor 会显示) |
| dsho capture 失败 | pip install playwright && python3 -m playwright install chromium;滚动视频需要 ffmpeg |
| 抓取时页面卡住 | 看门狗在 --max-time 后终止它(截图默认 90 秒) |

安全

dsh worker 能运行 shell 命令;很多环境里 dsh 以完整权限运行,没有沙箱。把任务限制在工作区内,绝不要委派删除、部署、git push 或任何涉及密钥的操作。worker 找到的素材不自动归你发布:在 manifest.json 里核对许可和来源。报告漏洞见 SECURITY.md。

开发

git clone https://github.com/nicremo/dsh-cli && cd dsh-cli
npm install
npm test # node:test with a fake dsh and a fake web UI; never touches your real setup
node bin/dsho.mjs help

架构说明:docs/architecture.md。欢迎贡献,见 CONTRIBUTING.md。

许可

MIT。演示动画里的 logo 归各自所有者(Anthropic、OpenAI、OpenCode、Pi、DeepSeek),仅用于标识兼容工具。
预览分享卡片长什么样 ↓nicremo/dsh-cli
分享 · DeepSeek Harness Hub
nicremo/dsh-cli

让 Claude Code、Codex、OpenCode、Pi 或任何其他智能体并行运行 DeepSeek…

这是你的仓库?认领条目

回到顶部