Back to home@0x1a27

dsh-pwsh-direct

DSH Desktop(Windows)内置 pwsh/shell 空结果 bug 修复 + pwsh_direct 直启兜底工具

Stars
0
Language
JavaScript
Created
Aug 19, 2026
Updated
Aug 19, 2026
GitHub repo

Introduction

dsh-pwsh-direct

DSH Desktop(Windows)内置 pwsh/shell 工具"空结果"bug 的修复插件,提供两层能力:

  1. 自愈(根治):每次 DSH 启动时把已安装的 dsh-subprocess-local(安装目录副本 + 全部 profile 副本)打上补丁,在沙箱 runner 的 spawn 环境注入 ELECTRON_RUN_AS_NODE=1。重启 DSH Desktop 后内置 pwsh/shell 即恢复。
  2. pwsh_direct 兜底工具(绕路):在重启生效前的窗口期、或 DSH 升级冲掉补丁后,可直接用 pwsh_direct spawn PowerShell 可执行文件,完全绕过 DSH 子进程服务层。

版本 0.2.0(2026-08-19,全链路验证通过)。可分发 zip 由 release/dsh-pwsh-direct 源码重建(zip 不入库,重建步骤见 release/README.md),包已具备 npm 发布条件(见"npm 发布")。


症状(本插件修复的对象)

DSH Desktop(服务器内嵌于 Electron 主进程)中调用内置 pwsh/shell 时返回完全空结果:命令没有执行、没有任何副作用(文件不创建、进程不启动),GUI 显示空白或 "(no output)"。

需求 / 兼容性

要求
平台Windows(自愈的安装副本目标是 Program Files 路径;pwsh_direct 的 PATH 解析与进程树杀法面向 Windows)
DSH Desktop服务器内嵌 Electron 主进程、bundle 解包到 resources\app.asar.unpacked 的当前标准安装形态
Node>= 20(见 package.jsonengines
PowerShellpwsh 7+ 优先;缺失时回退 Windows PowerShell 5.1(含 System32 规范路径)
写权限DSH Desktop 安装目录 + ~/.dsh/profiles/*(实测安装目录可写;若出现 write-failed 需以管理员身份操作一次)
必需 peer(声明为 optional)@deepseek-ai/dsh-tools —— 由 DSH 宿主环境(dsh CLI 树 / profile)提供,不随本插件捆绑;npm 已发布 0.0.1-rc.1,但声明为 optional peer 后 pnpm/npm 不自动安装(避免注入 shadow copy 遮蔽宿主捆绑版本);所有锚点均解析失败时插件自动禁用,不阻塞 DSH 启动
可选 peer(声明为 optional)@chaggle/dsh-powershell-check —— 同 profile 安装时为 pwsh_direct 启用"PowerShell 坑位"执行门禁;缺失时门禁降级为透传并告警(当前未发布到 npm,经 github / 本地路径安装)

安装

dsh plugin --profile web add <插件目录路径>
# 然后重启 DSH Desktop(必须)
  • --profile 必填。本地路径生成 link: 依赖;声明 dsh.bundle.patch 的包由 reconcilePlugins 追加进 dsh.profile.bundles 层栈。
  • 必须重启:自愈是文件补丁,运行中的服务器模块缓存仍持有旧代码,重启后内置工具才恢复。
  • 发布 zip 解压后可直接使用(内含 dsh-pwsh-direct 目录)。
  • 发布到 npm 后也可按包名直接安装(无 scope 公共包):
    dsh plugin --profile web add dsh-pwsh-direct
    
  • 或直接按 GitHub 仓库安装(免克隆):
    dsh plugin --profile web add github:0x1a27/dsh-pwsh-direct
    

卸载:

dsh plugin --profile web remove dsh-pwsh-direct

npm 发布

  • 依赖建模@deepseek-ai/dsh-tools(npm 已发布 0.0.1-rc.1)与 @chaggle/dsh-powershell-check(当前未发布到 npm)均声明为 optional peerDependenciespeerDependenciesMeta.optional: true)。两者都由 DSH 宿主环境提供(见"依赖解析"),本插件不捆绑;pnpm/npm 不自动安装 optional peer,避免向 profile 的 node_modules 注入独立副本、遮蔽宿主捆绑版本(否则锚点①/②会先命中注入副本,产生版本漂移)。缺失时由 4 锚点回落 + 自动禁用机制兜底,安装不会失败。
  • tarball 内容files 字段控制):lib/cordis.patch.yml + npm 自动包含的 package.jsonREADME.mdLICENSE(共 6 个文件,~16 kB)。调试 / 验证脚本(smoke-test.mjsverify/release/)不发布。
  • 发布流程
    npm login
    npm pack          # 预览 tarball(应为 6 个文件)
    npm publish       # 无 scope 包,默认 public
    
  • 发布后用户可按包名安装(见"安装");本地路径 / zip 安装方式继续可用,互不影响。

生产验收清单

依次执行,全部通过即达到生产条件:

  1. 自愈状态(任意机器,无需重启):
    node lib\electron-runner-heal.mjs
    
    预期:JSON results 中每一项(install 副本 + 各 profile 副本)均为 patchedalready-patched。出现 write-failed → 见排障。
  2. 重启 DSH Desktop,在任意会话调用内置 pwsh(如 Write-Output ping): 预期:返回非空结果。
  3. pwsh_direct 兜底路径:新会话调用 pwsh_direct { "command": "Write-Output ok" }: 预期:ok
  4. 深度验证(可选;脚本内嵌本机路径):
    node smoke-test.mjs              # 3 场景冒烟:预期 SMOKE TEST PASSED
    node verify\verify-spawn.mjs     # S1/S2 A/B:env 有/无 ELECTRON_RUN_AS_NODE
    node verify\verify-runner.mjs    # 1:1 生产链:预期 R1 exit=0 out=[RUNNER-OK-42]
    
    注意:smoke-test.mjs 内嵌本机路径常量(开头 PLUGIN_DIR / CLI_BIN / DSH_ROOT 默认值),换机器运行前须改成对应路径;verify\*.mjs 依赖本机 C:\Program Files\DSH Desktop\DSH Desktop.exe 路径。

工作原理

根因链(2026-08-19 本机实测)

  1. DSH 服务器内嵌于 DSH Desktop(Electron)主进程,无独立 node 进程。生产链实际加载的是安装目录的 C:\Program Files\DSH Desktop\resources\app.asar.unpacked\node_modules\@deepseek-ai\dsh-subprocess-local——profile 副本与捆绑 @deepseek-ai/dsh 包内的 trampoline adapter 均为死代码。
  2. windows-acl 沙箱链以 process.execPath(DSH Desktop Electron exe)作为 runner 的 argv[0]。
  3. dsh-subprocess-localspawnSubprocessconst env = childEnv(spec.env); 处)从不注入 ELECTRON_RUN_AS_NODE=1
  4. 结果:每次沙箱化命令都启动一个第二个桌面应用实例;第二实例丢失单例锁后静默自杀(exit 0 / 零输出 / 零副作用)→ 内置 pwsh/shell 表现为"完全空结果"。

验证证据(verify/ 下脚本):

  • S1/S2 A/Bverify-spawn.mjs):node spawn exe + ELECTRON_RUN_AS_NODE=1 → 探针以 node 语义运行(exit 0、输出正常、写入 marker);同条件去掉 env → 无输出、无副作用(第二 GUI 实例自杀)。
  • 1:1 生产链verify-runner.mjs):exe-as-node + 生产 windows-acl runner(app.asar.unpacked 里的 dsh-sandbox-windows-acl)+ 捆绑 koffi + 真实 pwsh → exit 0、输出 RUNNER-OK-42,与重启后生产链一致。

自愈细节

  • 目标lib/electron-runner-heal.mjshealAll):

    • 安装副本:C:\Program Files\DSH Desktop\resources\app.asar.unpacked\node_modules\@deepseek-ai\dsh-subprocess-local\lib\index.jsC:\Program Files (x86)\... 同路径;
    • 各 profile:~/.dsh/profiles/<name>/node_modules/@deepseek-ai/dsh-subprocess-local/lib/index.js
  • 补丁点spawnSubprocessconst env = childEnv(spec.env); 一行之后插入(带 marker 注释、可检测可重入;路径比较不区分大小写):

    /* dsh-desktop-powershell-fix: ...(5 行注释)... */
    if (process.versions.electron !== void 0 && program.toLowerCase() === process.execPath.toLowerCase()) env.ELECTRON_RUN_AS_NODE = "1";
    
  • 幂等与安全:已含 marker dsh-desktop-powershell-fix 的文件跳过(already-patched);任一步失败只告警、不抛错、绝不阻塞 DSH 启动。

  • 手动 / 独立执行node lib/electron-runner-heal.mjs —— 打印各副本状态(JSON)。

  • 状态值

    status含义
    patched本次运行新打上补丁
    already-patched已含 marker
    anchor-not-found未找到 spawnSubprocess 锚点(上游布局变了?)
    unreadable / write-failed无读 / 写权限
    not-found(install 副本)两个标准安装路径都不存在
  • 升级冲掉:DSH Desktop 升级会重写 app.asar.unpacked;插件下次启动时重新自愈,无需人工干预。窗口期(升级后至下次启动前)内置工具仍为空——用 pwsh_direct

pwsh_direct 工具

说明
工具名pwsh_direct(不能与内置 pwsh 同名,重名注册会抛错)
参数command(必填)/ description / workdir / timeoutMs(默认 120s,上限 600s)
可执行解析pwsh.exe(PS 7+,PATH 优先)→ powershell.exe(PATH)→ System32\WindowsPowerShell\v1.0\powershell.exe
启动参数-NoLogo -NoProfile -NonInteractive -Command,stdin 忽略,stdout/stderr 分路捕获(每路保留最后 256KB)
编码执行前注入 [Console]::OutputEncoding/InputEncoding = UTF-8(try 包裹),修复 PS 5.1 zh-CN 宿主的 GBK 中文乱码(R1 坑位)
超时到点 taskkill /F /T 杀进程树(非 Windows 回落 SIGKILL),输出 [timeout after Xms: process tree killed]
输出格式stdout(空则 (no output))→ [stderr] 段(非空时)→ [exit code: N](非零时),与内置 pwsh 同格式
坑位门禁可解析到 @chaggle/dsh-powershell-check(任一锚点)时,复用其 checker 作 tools/pre-execute deny 门禁(与内置 pwsh 同一规则);不可解析时门禁降级透传并告警
系统提示注入 tool:dsh-pwsh-direct 段,提示模型内置 pwsh 失败时切换 pwsh_direct

依赖解析(4 锚点回落,绝不阻塞 DSH 启动)

lib/index.jsrequire 链按以下锚点顺序回落解析 @deepseek-ai/dsh-tools,任一命中即继续;全部失败 → 告警并跳过注册(插件禁用,DSH 照常启动):

  1. 当前模块自身锚点(插件所在 profile 的 node_modules);
  2. ~/.dsh/profiles/<active>/node_modules(活动 profile 锚点);
  3. process.execPath 所在 Electron 安装树的 node_modules(捆绑依赖锚点);
  4. 内置 require(CommonJS 默认解析,兜底)。

0.1.x 在 link: 安装形态下 import 即抛错、整个 DSH 无法启动的问题由此根治。

安全与副作用

  • 修改的文件:仅安装目录与各 profile 下的 dsh-subprocess-local/lib/index.js——各插入 6 行(5 行注释 + 1 行 if),带 marker dsh-desktop-powershell-fix;不修改其他任何文件,无网络访问,无遥测。
  • 写入安装目录:自愈会写 C:\Program Files\DSH Desktop\...(实测可写;不可写时对应副本报 write-failed,需管理员权限运行一次)。
  • pwsh_direct 无沙箱:它以 DSH 进程的环境与权限直接 spawn PowerShell 可执行文件,不受内置工具的文件沙箱约束。命令以 DSH 进程权限执行——高危操作请与对待内置 pwsh 同等谨慎。
  • 系统提示注入:一段 tool:dsh-pwsh-direct(指导模型选择工具,无其他行为)。

卸载与回滚

dsh plugin --profile web remove dsh-pwsh-direct
  • 卸载并重启后:pwsh_direct 工具、门禁与系统提示段消失。
  • 文件补丁保留:已补丁的 ELECTRON_RUN_AS_NODE 注入仅在"argv[0] 是 Electron 宿主二进制"时生效,对普通 node runner 无害,且幂等可检测。
  • 要彻底回滚:在目标文件中删除 marker 注释块与 if (process.versions.electron ...) 一行,或重装 DSH Desktop(升级会自然覆盖)。

排障

症状可能原因处理
重启后内置 pwsh 仍空安装副本未打上(write-failed),或重启未真正生效node lib\electron-runner-heal.mjs 看 JSON;write-failed 则以管理员身份启动 DSH Desktop 一次并重启;全部 already-patched 时再重启一次
宿主日志 cannot resolve "@deepseek-ai/dsh-tools"插件未装到提供该 peer 的 profiledsh plugin --profile <name> add <path> 装到正确 profile
宿主日志 pitfall gate disabledprofile 未装 @chaggle/dsh-powershell-check同 profile 安装 checker;或接受透传(不阻断)
heal 结果 anchor-not-found上游 dsh-subprocess-local 布局变更人工检查目标文件 spawnSubprocess 函数,或上报
heal 结果 not-found(install)DSH Desktop 安装目录非标准(两个 Program Files 路径都不命中)确认实际安装路径;profile 副本自愈不受影响
DSH 升级后补丁被冲掉预期行为(升级重写 app.asar.unpacked下次启动自愈;窗口期用 pwsh_direct
GUI 显示 "No result provided" 且会话查询类工具(search/recall 等)失败会话索引引擎(dsh-session-query-sqlite)的独立故障,与本 bug 无关重启通常恢复;与 DSH Desktop 的服务器重启瞬态故障窗口有关

已知限制

  • workdir 默认取 DSH 宿主进程 cwd,不是会话工作目录——操作会话文件时请显式传 workdir
  • 命令由解析到的 PowerShell 版本解释:回落 5.1 时 &&/||/??/三元 ?: 等语法不可用(坑位门禁会拦截)。
  • 安装副本自愈只覆盖 C:\Program Files\DSH Desktop 两个标准路径,非标准安装目录需人工处理(profile 副本不受影响)。
  • 自愈是本地文件补丁、非上游修复:DSH Desktop 升级后补丁被冲掉属预期,插件下次启动重新自愈;若上游将来官方修复此 bug,可停用本插件的自愈(pwsh_direct 工具可保留作绕路)。
  • pwsh_direct 命令运行在 DSH 沙箱之外(见"安全与副作用")。

文件

文件说明
lib/index.js插件入口(启动自愈 / pwsh_direct 注册 / 4 锚点依赖解析 / 门禁 / 系统提示)
lib/electron-runner-heal.mjs自愈模块(healProfileFile / healAllProfiles / healInstallFile / healAll,可独立执行)
cordis.patch.ymlbundle patch 声明(dsh.plugin 安装契约)
LICENSEMIT 许可(随 npm tarball 自动包含)
smoke-test.mjs3 场景冒烟测试(进程内功能 / 启动模拟 / 坏环境不抛;内嵌本机路径)
verify/verify-spawn.mjsS1/S2 A/B(node spawn exe,env 有/无)
verify/verify-runner.mjs1:1 生产链(exe-as-node + 生产 windows-acl runner + 捆绑 koffi + pwsh)

变更日志

  • 0.2.0(2026-08-19)
    • 新增启动自愈 electron-runner-heal(覆盖安装副本 + 全部 profile 副本;幂等、绝不阻塞启动、可独立执行)
    • 依赖解析改为 4 锚点回落(旧版在 link: 安装下 import 即抛错、阻塞 DSH 启动;现降级为告警并跳过注册)
    • 自愈补丁行的锚点比较改为不区分大小写
    • 新增"PowerShell 坑位"执行门禁(复用 @chaggle/dsh-powershell-check,可选)
    • npm 发布就绪:两个 peer 声明为 optional peerDependencies(不捆绑、不自动安装、缺失自动禁用),补 LICENSE,移除未使用的 esbuild devDependency
  • 0.1.0:初版——pwsh_direct 兜底执行工具。

License

MIT