Fixes the Windows workspace-open action. The shipped opener runs `explorer.exe "<file:// URI>"`, which on some hosts reports success without raising a window — the exit-code verdict cannot tell an opened window from nothing happening. This routes that one action to `explorer.exe /e,<dir>`, which raises a window reliably, and leaves every other application in the menu on the shipped path.
Install
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:Martlet-Tech/dsh-open-folder-fix
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
This plugin publishes its README in Chinese only.
修正 DSH Web GUI 里「打开工作区」按钮在 Windows 上的行为。
问题
DSH 的 Session 头部有一个「在应用中打开工作目录」的分裂按钮。在 Windows 上,
它走 shell-open 通道,最终由 @deepseek-ai/dsh-native-command 执行:
explorer.exe "<目录的 file:// URI>" ← 0.1.7-rc.1 的写法
更早的版本走的是另一条命令(powershell.exe -NoProfile -Command "Invoke-Item -LiteralPath '<目录>'");两者症状相同,见下。
上游的 explorer 调用在某些 Windows 主机上不起作用,而且不报错。 这是上游
自己记录在案的已知限制(dsh-native-command/README.md,Known Limitations):
非交互会话(服务、或无交互登录的计划任务)里,
explorer.exe不调用任何文件 关联,并在约 1 秒后仍然以退出码 1 返回……于是「打开」报告成功却什么都没打开。
上游的成功判据就是退出码:runExplorer 把退出码 1 当作"已交接给桌面进程"而
吞掉,runShellOpen 再用 launchWatchMs 看门狗兜底。于是"窗口开了"和"什么都
没发生"在 DSH 看来完全一样,按钮照常变回空闲、不报错。
实测:上游写法在本机失败
Windows 10 19045.7663,直接用上游模块的真实代码路径调用:
| 调用 | 报告结果 | Explorer 窗口数变化 |
|---|---|---|
openNativePath('D:\Projects\dshell')(上游 0.1.7-rc.1) |
成功 | 5 → 5(Δ0) |
explorer.exe /e,D:\Projects\dshell(本插件) |
— | 6 → 7(Δ1) |
两者都用 execFile 调用 explorer.exe,唯一差别是参数。上游那次报告成功、
一个窗口都没开——正是上面那段 Known Limitation 描述的情形。
修法
改用 explorer.exe /e,<目录>,实测能稳定新开窗口。explorer.exe 交出控制权后
一律返回退出码 1,这是委派交接而非失败(上游 runExplorer 也按此处理):
| 命令 | 行为 |
|---|---|
explorer.exe <file:// URI>(上游现状) |
本机不弹窗,但报告成功 |
explorer.exe /e,<目录>(本插件) |
连续 6 次均新建窗口;目录已开着时同样新建 |
explorer.exe <目录> |
也新建窗口 |
explorer.exe /select,<父目录> |
新建并选中目标 |
Invoke-Item(更早版本的上游写法)的表现记录在此备查:它执行该目录的默认
shell 动词,把请求交给 shell 后立刻返回退出码 0,多数情况新建、偶尔复用,且
成功与否没有信号。
结构
package.json bundle 清单:声明 dsh.bundle.patch 与 dsh.client
cordis.patch.yml 禁用上游 ui-open-in-app,插入本插件的 Host 行
index.js Host 半边:注册 POST /open-folder-fix/open
client.js 浏览器半边:接管 Session 头部的分裂按钮
两半的分工:
- Host 只加一个路由,路径刻意不与上游
/open-in-app/*重叠。webServer.register对重复的 (kind, path) 会直接抛错,所以覆盖上游路由 会让插件在加载时炸掉。 - 浏览器 自己从
/open-in-app/apps读取已安装应用列表,对explorer这一个 id 路由到本插件的路由,其余应用(VS Code、Windows Terminal、Git Bash 等)原样转发给上游/open-in-app/open, 因此上游的应用探测、图标和启动策略都不受影响。
上游 @deepseek-ai/dsh-host-open-in-app 保持挂载,因为本插件需要它提供
应用目录与图标。
为什么要禁用上游的浏览器半边
上游 ui-open-in-app 与本插件会争抢同一个 slot cell id(open-in-app)。
插槽文档说明复用 id 会替换那个 cell,但两个注册者的胜出取决于激活顺序,
不确定。因此 patch 显式禁用上游浏览器半边,保证只有一个注册者:
- id: ui-open-in-app
disabled: true
配置
launchWatchMs(默认 1500):explorer.exe 交接的早期失败观察窗口。
启动成功与否不与进程退出绑定——Explorer 把请求交给正在运行的 shell 后就退出,
所以只有 spawn 错误(如 ENOENT)才算失败。
Config 必须导出 Standard Schema
这是本项目踩过的坑,改 Config 前务必读完。
cordis 的 resolveConfig 只要发现插件导出了 Config,就无条件读
Config['~standard'].validate(config):
// @deepseek-ai/cordis lib/index.js
function resolveConfig(runtime, config) {
if (!runtime.Config) return config
const result = runtime.Config['~standard'].validate(config) // ← 无保护的属性读取
...
}
因此 Config 必须是符合 Standard Schema 的对象
(含 ~standard.validate)。如果写成看起来更自然的样子:
// ✗ 错误:会让整个 dsh 起不来
export const Config = {
launchWatchMs: { type: 'number', min: 1, max: 600000, default: 1500 },
}
普通对象没有 ~standard,上面那行读出 undefined,紧接着 .validate(...)
抛 TypeError。这个异常发生在组合树启动阶段,结果是整个 dsh 无法启动,
而不只是本插件失效——排查时会以为坏的是别处。
正确写法见 index.js 的 Config:自带 validate,负责填默认值、
校验范围,并返回 { value } 或 { issues }。
已知边界
- 仅 Windows 有意义。非 win32 上路由返回 501;macOS/Linux 请沿用上游行为。
- 图标沿用上游
/open-in-app/icon/explorer。上游的目录表里本来就有explorer条目,因此图标正常解析。 - 与上游
@deepseek-ai/dsh-native-command的revealNativePath(explorer.exe /select,,用于"在资源管理器中显示某个文件")无关: 那条路径是按文件定位,不是打开工作目录,本次未改动。
依赖说明
本插件只用 Node 内置模块。不要在这里 import
@deepseek-ai/dsh-host-open-in-app:该包只导出 Config、apply、inject、
name,目录解析不在公开面上,依赖它等于依赖 alpha 版本的内部实现。
与上游的全部交互都走公开 HTTP 路由。
重新安装
同路径重复 install_bundle 会因上游 plugin-manager 的判定逻辑报
ambiguous-install(它靠"dependencies 是否变化"来识别装了哪个包,
包已存在时识别不到)。包在 profile 里是符号链接,所以改完代码刷新页面
即可生效,不需要重装。
Links
More in this category
zhu1090093659/dsh-web#packages/dsh-task-board★ 10209
Task board for the dsh web GUI: a sidebar multi-column kanban whose cards run in real DSH agent sessions and can also be scheduled with cron expressions, executed host-side even with the browser closed.
zhu1090093659/dsh-web#packages/dsh-web-all★ 10209
Plugin and skin collection for the DSH Web UI: task board, Git graph, right-side panel, remote mobile UI, pet, live token stats, and a skin center.
ccch1mneyyy/dsh-TUI★ 7166
Claude Code-style full-screen terminal UI: pixel-whale header, live status line, and streaming thought expansion.
MeteorNOX/DeepSeek-Balance-Whale-Widget★ 7008
A fixed-corner whale widget for the DSH web GUI — balance, today's usage and per-turn cost with peak/off-peak pricing, editable balance-alert and daily-budget bubbles, a module-based custom bubble queue with A/B weighted choices and random lines or images, 30+ vendor templates (OpenAI, OpenRouter, Kimi, SiliconFlow, Ark, Zhipu, MiniMax and more) with per-model balance and subscription quota, plus task-end sound, imported audio, custom roles and a resource manager. Local-only, no telemetry.
omdsh-dev/DSH-better-sidebar★ 4985
Full sidebar workbench with file rendering and editing, terminal, Git, and subagents; third-party plugins can register new tabs.
Devin-AXIS/deepseek-design#deepseek-idesign★ 1762
Visual design studio for websites, app prototypes, posters, cards, reports, and magazines, with templates, direct element editing, selection-aware AI draft handoff, and export.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.