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

分步计划执行器 MathieuPortmann/dsh-plan-runner

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

把长任务拆成步骤,逐步在独立会话中执行并校验结果

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

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

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add MathieuPortmann/dsh-plan-runner
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:仅索引
是什么
dsh 原生插件 · chat
装得上吗
本站尚未做安装检查
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 6 天前
档位由哪些信号合成

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

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

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 28 处,展示前 28 条)
  • 读取本地文件
    index.js:48
    for (const d of (() => { try { return readdirSync(root); } catch { return []; } })()) {
  • 读取本地文件
    index.js:49
    const read = f => { try { return readFileSync(join(root, d, f), 'utf8').trim(); } catch { return ''; } };
  • 发起网络请求
    index.js:155
    const r = await fetch(`${cfg.ollamaUrl}/api/tags`, { signal: AbortSignal.timeout(4000) });
  • 删除本地文件
    index.js:173
    if (out) { try { unlinkSync(resolve(state.cwd, out)); } catch { } }
  • 读取本地文件
    index.js:225
    if (!(p in state.preexisting)) state.preexisting[p] = existsSync(abs);
  • 读取本地文件
    index.js:227
    if (rel.startsWith('..') || !existsSync(abs) || !statSync(abs).isFile()) continue;
  • 读取本地文件
    index.js:227
    if (rel.startsWith('..') || !existsSync(abs) || !statSync(abs).isFile()) continue;
  • 写入本地文件
    index.js:228
    try { mkdirSync(dirname(join(dest, rel)), { recursive: true }); copyFileSync(abs, join(dest, rel)); } catch { continue; }
  • 写入本地文件
    index.js:228
    try { mkdirSync(dirname(join(dest, rel)), { recursive: true }); copyFileSync(abs, join(dest, rel)); } catch { continue; }
  • 删除本地文件
    index.js:232
    if (!step.forEach && state.preexisting?.[p] === false && !inputs.has(p) && !keptByDoneStep) { try { unlinkSync(abs); } catch { } }
  • 读取本地文件
    index.js:238
    unchanged: (cwd, paths, since) => paths.filter(p => { try { return statSync(resolve(cwd, p)).mtimeMs < since - 1000; } catch { return false; } }),
  • 读取本地文件
    lib/plan-tools.js:71
    if (!session || !cwd || !existsSync(cwd)) return 'REFUSED: this conversation has no working folder.';
  • 读取本地文件
    lib/plan-file.js:36
    if (typeof plan === 'string' && plan.trim() && cwd && existsSync(cwd)) {
  • 写入本地文件
    lib/plan-file.js:37
    try { writeFileSync(join(cwd, `PLAN-${planSlug(plan)}.md`), plan.endsWith('\n') ? plan : `${plan}\n`); }
  • 读取本地文件
    lib/checks.js:83
    try { return readdirSync(dir, { withFileTypes: true }); } catch { return []; }
  • 读取本地文件
    lib/checks.js:86
    const isFile = f => { try { return statSync(f).isFile(); } catch { return false; } };
  • 读取本地文件
    lib/checks.js:108
    try { statSync(next); } catch { return; }
  • 执行外部命令固定命令
    lib/checks.js:162
    child = spawn('sh', ['-c', command], { cwd, stdio: ['ignore', 'pipe', 'pipe'], detached: process.platform !== 'win32' });
  • 读取本地文件
    lib/checks.js:193
    try { const st = statSync(abs); size = st.isFile() ? st.size : undefined; } catch { }
  • 读取本地文件
    lib/checks.js:197
    try { text = readFileSync(abs).toString('utf8'); } catch { }
  • 读取本地文件
    lib/checks.js:234
    try { const st = statSync(abs); return st.isFile() ? st.size : undefined; } catch { return undefined; }
  • 写入本地文件
    lib/store.js:22
    mkdirSync(dirname(file), { recursive: true });
  • 写入本地文件
    lib/store.js:24
    writeFileSync(tmp, content);
  • 写入本地文件
    lib/store.js:25
    renameSync(tmp, file);
  • 读取本地文件
    lib/store.js:36
    const s = JSON.parse(new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(readFileSync(String(file))));
  • 读取本地文件
    lib/store.js:45
    const parsed = JSON.parse(readFileSync(join(dataDir(), 'index.json'), 'utf8'));
  • 读取本地文件
    lib/store.js:64
    return list.filter(f => typeof f === 'string' && existsSync(f)).map(loadState).filter(Boolean);
  • 读取本地文件
    lib/store.js:68
    return Object.values(readIndex()).flatMap(l => (Array.isArray(l) ? l : [])).filter(f => typeof f === 'string' && existsSync(f));
扫描于 2026/11/1(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

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

依赖的 DSH / Cordis 模块
@deepseek-ai/dsh-llm@deepseek-ai/dsh-tools
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

为 DeepSeek Harness(DSH)逐步执行计划的运行器,专为小型本地模型打造:每一步都开全新子会话,做机械校验,可选审查者。

README

由 DeepSeek 最新模型翻译生成
dsh-plan-runner 为 DeepSeek Harness(DSH)打造的分步计划执行器,专为本地小模型设计——这类模型在长任务中容易跑偏。Plan 智能体负责写步骤;执行器随后在全新的子会话里逐步执行,每个子会话只能看到目标、自己的指令、前面步骤的简短摘要和需要用到的文件,并用 harness 自己运行的检查项验证结果。 维护状态: 尽力而为。issue 可能无人回复;欢迎 fork。 工作原理 1. 选择 Plan 预设。智能体先做只读探索,然后用 plan_begin、plan_add_step(每步一次调用)和 plan_show 记录步骤。某一步如果超过其模型上下文窗口的 60 %,会被拒绝,必须拆分。计划同时保存为 PLAN-<name>.md。 2. 批准计划,然后按 Run step by step(计划审核卡片,或输入框上方的条带)。每一步都在自己的会话里运行(原生 DSH 子代理),带有: - 自己的模型(见「配置」), - 自己的访问级别:read、read-shell(仅只读 shell 命令)、write(写文件,限于该步骤声明的输出)或 full, - 一个 worker 人设和固定的最终报告(Summary / Files / Decisions / Open questions)。
展开完整文档(README 剩余 1 节)
dsh-plan-runner

为 DeepSeek Harness(DSH)打造的分步计划执行器,专为本地小模型设计——这类模型在长任务中容易跑偏。Plan 智能体负责写步骤;执行器随后在全新的子会话里逐步执行,每个子会话只能看到目标、自己的指令、前面步骤的简短摘要和需要用到的文件,并用 harness 自己运行的检查项验证结果。

维护状态: 尽力而为。issue 可能无人回复;欢迎 fork。

工作原理

1. 选择 Plan 预设。智能体先做只读探索,然后用 plan_begin、plan_add_step(每步一次调用)和 plan_show 记录步骤。某一步如果超过其模型上下文窗口的 60 %,会被拒绝,必须拆分。计划同时保存为 PLAN-<name>.md。
2. 批准计划,然后按 Run step by step(计划审核卡片,或输入框上方的条带)。每一步都在自己的会话里运行(原生 DSH 子代理),带有:
- 自己的模型(见「配置」),
- 自己的访问级别:read、read-shell(仅只读 shell 命令)、write(写文件,限于该步骤声明的输出)或 full,
- 一个 worker 人设和固定的最终报告(Summary / Files / Decisions / Open questions)。
3. 每步结束后,harness 运行该步骤的检查项:exists: <path>、contains: <path> :: <text>、words: <path> >= <n>、count: <glob> >= <n>、command: <shell command>(退出码 0 即通过)。重要步骤还会配一个只读审核者,以 VERDICT: OK 或 VERDICT: PROBLEMS - ... 结尾。
4. 失败的步骤会重试一次,附带失败原因和一条经验教训;第二次失败后,会提议更细的拆分,在你接受之前不做任何改动。
5. for_each 步骤按文件或条目逐个运行,每个都在简短会话里执行,并为每个条目写一个结果文件。标记为 stop 的步骤会暂停运行,方便你检查结果。
6. 循环(同一工具调用反复出现)会中止该步骤;因重启而中断的计划会暂停,并从当前步骤继续。

状态保存在 <working folder>/.plan-<name>/state.json(人类可读),每个会话的计划索引保存在 <DSH_HOME>/plan-runner/index.json。步骤覆盖的文件会备份到 .plan-<name>/backups/ 下。

步骤无法请求批准(DSH 会拒绝子代理中的批准请求):会话的权限模式不允许免询问执行的操作会被拒绝,该步骤随之失败。对于要写文件的步骤,请把会话设为自动模式或 workspace-write 模式。

安装

dsh plugin --profile web add dsh-plan-runner
or straight from GitHub:
dsh plugin --profile web add github:MathieuPortmann/dsh-plan-runner

之后重启配置档。该 bundle 自带 Plan 预设(id 为 plan);不要与其他定义了相同 id 预设的 bundle 一起使用。

配置

plan-runner 这一行写在你的配置档的 cordis.patch.yml 里;每个键都是可选的。

- id: plan-runner
config:
models: # models a step may use; empty = the plan conversation's own model
my-local-model:
provider: ollama
window: 32768 # context window in tokens
description: good reasoning, slow # shown to the Plan agent
defaultModel: my-local-model
splitModel: my-local-model # proposes the finer split after two failures
auditModel: '' # reviewer of important steps ('' = the step's own model)
pauseOnBattery: false # pause the run on battery power
ollamaUrl: http://127.0.0.1:11434
commandTimeoutMs: 120000 # timeout of command: checks
loopRepeats: 3 # identical tool calls in a step that count as a loop

当某个模型的服务商是 ollama 时,只要 ollamaUrl 不再响应,运行就会暂停。token 预算由 basePromptTokens、outputReserve、unknownFileTokens、itemOutputTokens 和 summaryTokens 控制(默认值按 32k 窗口调校)。

兼容性

| 组件 | 支持范围 |
| Node.js | ^22.19.0 \|\| >=24.0.0(engines.node) |
| DSH | >=0.2.0-rc.2 <0.3.0;以可选 peerDependencies 的形式声明在 @deepseek-ai/dsh-llm 和 @deepseek-ai/dsh-tools 上,这也是插件唯一导入的两个 DSH 包 |
| 平台 | Linux(已测试);纯 JavaScript,不含原生扩展。command: 检查需要 POSIX sh |

已在 Node 22.23 上配合 DSH 0.2.0-rc.2 测试(见验证)。

权限与外部服务

插件能触及的一切,以及失败时的表现:

| 能力 | 位置 | 用途 | 失败时 |
| 文件(读) | lib/checks.js、index.js | exists / contains / words / count 检查、用于 token 预算的文件大小、/sys/class/power_supply(仅在开启 pauseOnBattery 时);每个路径都在工作目录内解析 | 检查失败,或该文件按缺失处理 |
| 文件(写) | lib/store.js、lib/plan-file.js、index.js | <cwd>/.plan-<slug>/state.json 和 backups/、<DSH_HOME>/plan-runner/index.json、工作目录里的 PLAN-<name>.md;步骤自己的结果文件由该步骤的子会话写入,且限于它声明的 outputs | 计划保持之前的状态 |
| 命令 | lib/checks.js(spawn('sh', ['-c', …])) | command: 检查,由你或 Plan 智能体编写,在工作目录中运行并带超时(commandTimeoutMs);Plan 智能体会被告知保持它们只读 | 超时会杀掉命令及其子进程;检查失败 |
| 网络 | index.js(fetch) | 仅当某步骤使用 ollama 模型时,发起 GET <ollamaUrl>/api/tags | 服务器不可达时暂停运行 |
| 动态模块加载 | index.js、lib/plan-tools.js | 从运行插件的 DSH 安装中加载 @deepseek-ai/dsh-llm 和 @deepseek-ai/dsh-tools(在 Node 22 上,对 ES 模块做同步 require 会失败);不下载任何内容,也不从文本中求值任何代码 | 两者都是必需的:缺少任何一个,插件都无法加载 |
| 子代理 | index.js(ctx.subagents) | 每个步骤一个子会话,按访问级别(read、read-shell、write、full)配置工具过滤器 | 该步骤失败,并按重试规则处理 |

计划状态是 .plan-<slug>/ 下的纯 JSON;删除该文件夹即可忘记一个计划。

验证

在一次性配置档上验证过(DSH_HOME 指向临时文件夹,配置档用 dsh <name> --from-default-profile web 克隆,因此真实配置档从未被触碰):

npm pack
dsh plugin --profile t add file:$PWD/dsh-plan-runner-0.1.1.tgz # install
dsh --profile t --no-open --port 3095 # start: plugin loads, no error

npm test 运行 42 个独立测试(状态、解析、检查、存储、运行器、作用域守卫、只读 shell、配置),无需安装 DSH。用 9B 本地模型做了一次真实运行,通过 plan_begin / plan_add_step 创建计划,在子会话中运行其步骤,并用 words: 检查验证了结果。

开发

npm test

许可证

MIT
预览分享卡片长什么样 ↓MathieuPortmann/dsh-plan-runner
分享 · DeepSeek Harness Hub
MathieuPortmann/dsh-plan-runner

把长任务拆成步骤,逐步在独立会话中执行并校验结果

回到顶部