每次打开 Harness 都要手动全屏,烦不烦?这个插件帮你把屏幕填满。自包含:安装完成后,插件的宿主端会自己打开…
30.8/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/18 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add YunLiZeMeng/dsh-window-control该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · chat
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 16 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令
查看扫描证据(共 2 处,展示前 2 条)
- 执行外部命令固定命令lib/asar-bridge.js:1041
const out = execFileSync('tasklist', ['/FI', 'IMAGENAME eq ' + image, '/NH'], { - 执行外部命令固定命令lib/asar-bridge.js:1054
const out = execFileSync('ps', ['-eo', 'args='], { encoding: 'utf8' })
扫描于 2026/11/2(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
每次打开 Harness 都要手动全屏,烦不烦?这个插件帮你铺满屏幕——启动时自动最大化或真全屏,最小化,窗口始终置顶,退出时弹出原生确认对话框(Esc、连按两次 Esc 或 Ctrl+Q)。
README
由 DeepSeek 最新模型翻译生成dsh-window-control
English · 中文正文见下方
每次打开 Harness 都要手动全屏,烦不烦?这个插件帮你把屏幕填满。自包含:安装完成后,插件的宿主端会自己打开 DSH 的窗口通道,不用额外脚本,不用 Python,也不用手动改文件。
功能一览
| 能力 | 说明 |
| 总开关 | 默认开启。关掉后插件完全失效(不自动填满、快捷键不退出、其他控件全部隐藏),并还原它改过的窗口状态 |
| 全屏 / 退出全屏 | 真正的窗口全屏(盖住任务栏),不是网页级全屏 |
| 最大化 / 还原 | 最大化、还原窗口 |
| 最小化 | 把窗口收进任务栏 |
| 窗口置顶 | 让 DSH 始终压在其他窗口上面 |
| 退出 DeepSeek Harness | 一定先弹出原生确认对话框 |
| 启动时填满屏幕 | 三选一:最大化(默认) / 真全屏 / 关闭。立即生效,无需重启 |
| 退出快捷键 | 四选一:Esc(默认) / 双击 Esc / Ctrl+Q / 关闭 |
| Esc 退出全屏 | 默认开启。真全屏状态下按 Esc 退出全屏,而不是询问是否退出 |
入口:设置 → 窗口与退出。
安装
第 1 步 · 添加插件。 打开应用内市场,搜索 dsh-window-control,安装。
dsh plugin add …
展开完整文档(README 剩余 1 节)
dsh-window-control
English · 中文正文见下方
每次打开 Harness 都要手动全屏,烦不烦?这个插件帮你把屏幕填满。自包含:安装完成后,插件的宿主端会自己打开 DSH 的窗口通道,不用额外脚本,不用 Python,也不用手动改文件。
功能一览
| 能力 | 说明 |
| 总开关 | 默认开启。关掉后插件完全失效(不自动填满、快捷键不退出、其他控件全部隐藏),并还原它改过的窗口状态 |
| 全屏 / 退出全屏 | 真正的窗口全屏(盖住任务栏),不是网页级全屏 |
| 最大化 / 还原 | 最大化、还原窗口 |
| 最小化 | 把窗口收进任务栏 |
| 窗口置顶 | 让 DSH 始终压在其他窗口上面 |
| 退出 DeepSeek Harness | 一定先弹出原生确认对话框 |
| 启动时填满屏幕 | 三选一:最大化(默认) / 真全屏 / 关闭。立即生效,无需重启 |
| 退出快捷键 | 四选一:Esc(默认) / 双击 Esc / Ctrl+Q / 关闭 |
| Esc 退出全屏 | 默认开启。真全屏状态下按 Esc 退出全屏,而不是询问是否退出 |
入口:设置 → 窗口与退出。
安装
第 1 步 · 添加插件。 打开应用内市场,搜索 dsh-window-control,安装。
dsh plugin add … 只在有 npm 安装的 DSH 命令行时可用;桌面版默认不带 dsh 命令,会提示「'dsh' 不是内部或外部命令」。
找不到? 上架最长要一天才同步。刷新市场(或先更新市场本身),然后退出并重开 DSH。条目已经在目录里了(registry yml),所以搜不到几乎都是市场缓存没跟上,不是没上架。
第 2 步 · 完全退出 DSH。 托盘图标 → 退出。关窗口只是隐藏到托盘,不算退出,通道也不会装上。
第 3 步 · 重新打开 DSH(第一次启动)。 这次启动会把窗口通道写进 app.asar。打开设置 → 窗口与退出,你会看到通道还没生效——这是正常的,不是失败。
第 4 步 · 再次完全退出并重开(第二次启动)。 这时才生效:警告消失,所有按钮都能用。之后不用再重启。
验证是否装好(可选)——把下面这行粘进 cmd 回车:
"%USERPROFILE%\.dsh\dsh-runtimes\dsh-primary-runtime\dependencies\node\bin\node.exe" "%USERPROFILE%\.dsh\profiles\desktop\node_modules\dsh-window-control\lib\bridge-cli.mjs" status
显示 桥状态 : 已安装 v3(含最小化 / 窗口置顶) 就是装好了。这条命令不依赖 PATH,也不挑当前目录;可能有两处要改——dsh-primary-runtime(先看真实名字在 dir /b /ad "%USERPROFILE%\.dsh\dsh-runtimes" 里找到运行时目录,配置档名是 desktop。
在 macOS / Linux 上,等价命令是
cd ~/.dsh/profiles/desktop/node_modules/dsh-window-control && node lib/bridge-cli.mjs status。
⚠️ 为什么要重启两次? DSH 主进程在启动时加载代码,而插件只能在 DSH 已经运行时才能给窗口通道打补丁——所以第一次启动装好通道,第二次启动才是读取通道的那次。两次启动之间要彻底退出 DSH(托盘图标 → 退出;关窗口只是把它藏到托盘)。
为什么要动宿主。 DSH 主进程没有开放任何 IPC 通道来处理最大化、最小化、置顶或退出——原版 app.asar 一个都不管,而且主窗口关闭时会藏到托盘。所以插件会往 resources/app.asar 里注入一小批 IPC 通道,只做一次(等长替换,归档大小不变,重新计算完整性哈希,备份保留为 app.asar.orig)。其他需要宿主行为的 DSH 插件也是这么做的。字节预算、升级和安全细节见中文小节「窗口通道是怎么装上去的」。
跨平台。 Windows(用户级安装)已测试可用。在 Windows 的 Program Files 下,首次安装需要提权运行一次。在 macOS 上,修改 app.asar 可能让代码签名失效——真发生了插件会告诉你,用 restore 可以还原。在 Linux 上能用,但路径回退少一些。归档位置是从正在运行的 DSH 可执行文件反推安装目录得到的,所以自定义安装路径也支持。
手动命令(排查问题)
宿主那一半自己会做完这些事。如果你确实要用命令行,把下面这行粘进 cmd 回车——不需要 PATH,也不依赖当前目录:
"%USERPROFILE%\.dsh\dsh-runtimes\dsh-primary-runtime\dependencies\node\bin\node.exe" "%USERPROFILE%\.dsh\profiles\desktop\node_modules\dsh-window-control\lib\bridge-cli.mjs" status
把末尾的 status 换成下面任意一个:
| 子命令 | 作用 |
| status | 通道是否已安装、哪个版本、锁是否空闲、DSH 是否在运行 |
| check | 构建 + 完整自检,不写入(当前归档已桥接时回退到 app.asar.orig) |
| install | 安装(幂等);install --force 先还原再重做 |
| restore | 把原始 app.asar 放回去,保留备份(--force 即使没有桥接也写入) |
| uninstall | 还原并删掉备份 |
| bench | 给读取/构建步骤计时 |
只有两处可能需要改:dsh-primary-runtime(见 dir /b /ad "%USERPROFILE%\.dsh\dsh-runtimes")和配置档名 desktop。
卸载。 干净卸载永远是两步:先还原桥接,再删插件——顺序很重要。
为什么不能自动:插件一旦从配置档里删掉,它的宿主那一半就加载时间更长,所以不会有代码再跑一遍——也就没有「下次启动发现桥还在,再把它还原」的机会。这是 DSH 插件模型的硬性限制。
路径 A · 命令行(推荐)
第 1 步 · 还原桥并清理备份——把下面这行粘贴到 cmd 里,按回车:
"%USERPROFILE%\.dsh\dsh-runtimes\dsh-primary-runtime\dependencies\node\bin\node.exe" "%USERPROFILE%\.dsh\profiles\desktop\node_modules\dsh-window-control\lib\bridge-cli.mjs" uninstall
预期输出:
[OK] 已还原为原始 app.asar;备份已清理桥状态 : 未安装
有两处可能需要改:dsh-primary-runtime(用 dir /b /ad "%USERPROFILE%\.dsh\dsh-runtimes" 查看)和配置档名 desktop。在 Program Files 下要用提权的 cmd。如果 node 已在 PATH 里,也可以 cd 进插件目录,运行 node lib\bridge-cli.mjs uninstall。macOS / Linux 上用这种写法:cd ~/.dsh/profiles/desktop/node_modules/dsh-window-control && node lib/bridge-cli.mjs uninstall。
第 2 步 · 从市场里移除插件。
路径 B · 不用命令行
第 1 步 · 把下面四行追加到 %USERPROFILE%\.dsh\profiles\desktop\cordis.patch.yml:
- id: dsh-window-control
config:
autoInstall: false
第 2 步 · 完全退出 DSH,再打开一次——宿主那一半会还原桥并清理备份。
第 3 步 · 验证(可选):把路径 A 命令的最后一个词改成 status;输出 桥状态 : 未安装 就说明归档已恢复原样。
此时设置里的条目不会消失:autoInstall 只影响宿主那一半,渲染进程照样加载,那一栏只会显示「通道未启用」的提示。
第 4 步 · 从市场里移除插件。
只还原、保留插件——和路径 A 同一行,结尾换成 restore:
"%USERPROFILE%\.dsh\dsh-runtimes\dsh-primary-runtime\dependencies\node\bin\node.exe" "%USERPROFILE%\.dsh\profiles\desktop\node_modules\dsh-window-control\lib\bridge-cli.mjs" restore
和 uninstall 不同,它会保留 app.asar.orig 备份(以后想重装时有用)。归档里没有桥时它什么都不写,只报「nothing to do」——这是正常返回,不是错误。
把桥留着、只移除插件也「能用」——桥会变成死代码——而且会整体替换 app.asar,桥也就没了。
测试范围。 这个插件只在作者自己的 Windows 机器上跑过,环境是 DeepSeek Harness 0.2.0-rc.2。其他操作系统、DSH 版本或环境都没验证过——表现可能一样,也可能不一样。
问题反馈。 如果你的环境里出了问题,请到 https://github.com/YunLiZeMeng/dsh-window-control/issues 提 issue——附上你的操作系统、DSH 版本和复现步骤。
自测(两项都跑在 mock 上,都不会碰你的 DSH 安装)
node _selftest.mjs # browser half, 79 checksbat
node _bridge-selftest.mjs # bridge engine, 60 checks, uses a tiny fake archive
MIT 协议。本文档其余部分用中文写成,讲得更细。
中文文档
如果你嫌每次打开 Harness 都要手动全屏,这个插件可以帮你自动铺满。自包含:装好之后由插件自己在后台一次性打通 DSH 的窗口通道,不需要额外脚本、不需要 Python、也不需要你手动改任何文件。
功能
| 能力 | 说明 |
| 插件总开关 | 置顶开关,默认开。关闭后本插件不产生任何效果(不自动铺满、快捷键不退出、其余控件全部隐藏),行为等同「未安装本插件」;同时会撤回它自己改过的窗口状态(含置顶),不影响你手动调过的窗口 |
| 全屏 / 退出全屏 | 真窗口全屏(盖住任务栏),不是网页级全屏 |
| 最大化 / 还原 | 窗口最大化与还原 |
| 最小化 | 把窗口收进任务栏(v3 桥) |
| 窗口置顶 | 让 DSH 始终显示在其他窗口之上(v3 桥);勾选后会被记住,下次启动自动补上 |
| 退出 DeepSeek Harness | 无条件先弹原生确认对话框,确认后才退出 |
| 启动时自动铺满窗口 | 三选一:最大化(默认) / 真全屏 / 不自动;改了立即生效,不用重启 |
| 退出快捷键 | 四选一:Esc(默认) / 双击 Esc / Ctrl+Q / 关闭。某些界面里 Esc 另有用途时,改成后两者可避免误触 |
| 按 Esc 退出全屏 | 开关,默认开;真全屏下 Esc 先退出全屏,不弹退出确认(否则全屏里没有可见出口)。此项始终认 Esc,与上面「退出快捷键」的取值无关 |
入口:设置 → 窗口与退出。
窗口区:当前状态 / 最大化 / 最小化 / 窗口置顶 / 退出按钮
启动行为三选一与退出快捷键,含真全屏提示
安装
桌面版没有 dsh 命令行工具,也不需要 —— 按下面四步走就行。
第 1 步 · 装插件
应用内打开插件市场 → 搜 dsh-window-control → 安装。
dsh plugin add … 只适用于自己装了 npm 版 DSH CLI 的环境;桌面版照抄会报「'dsh' 不是内部或外部命令」。
搜不到怎么办:收录生效通常要一天。先在市场里刷新一下(或更新市场本身),仍没有就完全退出 DSH 再打开一次。本插件已经在目录里(registry 条目),所以搜不到基本只是你的市场缓存还没更新 —— 不是没上架。
第 2 步 · 完全退出 DSH
托盘图标右键 →「退出」。
只关窗口只是隐藏到托盘,进程还在跑 —— 不算退出,窗口通道不会被装。
第 3 步 · 重新打开 DSH(第 1 次启动)
这次启动会把窗口通道写进 DSH 的 app.asar。启动完打开「设置 → 窗口与退出」,看到「窗口通道尚未生效」是预期内的,不是装失败。
第 4 步 · 再次完全退出并重新打开(第 2 次启动)
这次才真正生效:提示消失、按钮全部可用。此后不再需要重启。
验证装好了没(可选) —— 复制这一行粘进 cmd 回车: bat "%USERPROFILE%\.dsh\dsh-runtimes\dsh-primary-runtime\dependencies\node\bin\node.exe" "%USERPROFILE%\.dsh\profiles\desktop\node_modules\dsh-window-control\lib\bridge-cli.mjs" status
看到 桥状态 : 已安装 v3(含最小化 / 窗口置顶) 就成了。这条命令不依赖 PATH、也不依赖当前目录;只有两处可能要改:
- dsh-primary-runtime:用 dir /b /ad "%USERPROFILE%\.dsh\dsh-runtimes" 看实际目录名
- profile 名 desktop:按你自己的配置改
macOS / Linux 不用这套 cmd 写法,等价命令是:
cd ~/.dsh/profiles/desktop/node_modules/dsh-window-control && node lib/bridge-cli.mjs status
为什么必须重启两次:DSH 主进程的代码在启动时就被载入内存,插件只能在 DSH 跑起来之后才能去装窗口通道,所以这一轮改动要等下次启动才被读到;而装插件本身也要重启一次插件才第一次运行。于是时间线是「装插件(未运行)→ 第 1 次启动(装上桥)→ 第 2 次启动(生效)」。
如果自动安装真的被挡住(装在 Program Files 没写权限、macOS 签名、杀软拦截、DSH 刚升级导致拒绝注入),插件不会让你一直空转重启:连续 3 次启动读不到通道时,设置页会列出具体原因、宿主诊断文件的位置和自查命令,还有一个「复制排障信息」按钮。手动排障命令见下方「手动操作(排障用)」。
宿主半边在每次 DSH 启动后都会检查一次:已经装好就什么都不做(幂等),所以 DSH 升级覆盖安装目录之后,下一次启动会自动重新装好,你不需要做任何事。打桥前它还会把 app.asar.orig 与当前归档核对一遍,对不上就重做备份 —— 这样备份永远对应你正在用的这一版。
插件总开关(「关闭 = 等同未安装」)
总开关是唯一的全局开关,它关掉后:
1. 启动不再自动铺满;
2. Esc 不再触发退出(监听器仍在,但直接返回);
3. 设置区块只剩总开关本身 + 一行「插件已停用」提示,其余控件全部隐藏;
4. 如果本次会话的窗口状态是本插件自动改的,会一并退回普通窗口。
这些判断每次都重新读配置,所以改开关立即生效,不需要重启。
为什么必须改一次宿主(而不是像普通插件那样)
DSH 的插件体系有两半,两边都碰不到主进程:
- 宿主半边(lib/index.js)跑在独立 Node 子进程里,环境变量为 ELECTRON_RUN_AS_NODE=1。整个宿主包里 BrowserWindow 出现 0 次 —— 没有 Electron 能力。
- 渲染端半边(lib/client.js)跑在 dsh-app://app 的沙箱页面里,只能调用 preload 暴露出来的东西。
对未打补丁的官方 app.asar 逐条测绘后,结论是渲染端拿不到任何窗口控制能力:
- DESKTOP_IPC 清单里确实有 window-fullscreen,但它只被 webContents.send() 单向推送(在 enter-full-screen / leave-full-screen 时告诉页面去调整 40px 标题栏),没有任何 ipcMain.handle 注册它 —— 页面只能「听」,不能「设」。
- 官方 dshDesktop 暴露出的 API 只有 browser / deviceInfo / keyboard / shortcuts / updates,一条窗口控制都没有。
- windows-menu(Windows 系统菜单)只认 application / edit 两种菜单(撤销、复制、粘贴),而且它只挂在 preload 自己的 shadow DOM 上、没走 contextBridge,页面调不到。
- profile 的 cordis.patch.yml 是加载器层补丁(改配置、禁用插件、插入插件 id),注入不了主进程代码。
所以窗口控制必须改宿主。这也正是 dsh-desktop-tray 之类插件同样自带 main-patch.js 的原因。
能不能做到「装完即用」或「只重启一次」
做不到 —— 这是 DSH 的结构决定的,不是没做优化。三条都验证过:
1. 窗口能力只能活在主进程里,而主进程的代码在启动那一刻就载入了。
对原版 lib/main.js 逐字统计:maximize(、minimize(、setAlwaysOnTop( 各 0 次;
setFullScreen( 只有 1 次,还是 macOS 上「隐藏窗口前先退出全屏」用的;
restore() 只在开文件对话框前把最小化的窗口叫醒。也就是说没有任何一条现成通道能被
外部触发来控制窗口,桥必须自己写进去;而写进去的字节,只有等主进程下一次启动才会被读到。
2. 主进程没有对外暴露「重启」。 app.relaunch() 确实存在,但它只挂在崩溃恢复对象 DesktopFatalRecovery 内部(exit / restart / writeReport),没有任何 IPC 通道通向它,所以「装完桥自动重启一次」也没法做;从外部强杀再拉起更是不可接受。
3. 插件只有等 DSH 起来之后才有执行权。 main.js 里没有任何文件监听(fs.watch / watchFile / chokidar 均为 0),cordis.patch.yml 的「hot-reloaded」指的是长驻面重新加载时重读,不是监听文件。所以装插件本身就要重启一次,插件才第一次运行。
于是时间线只能是:装插件(插件未运行)→ 第 1 次启动(插件装上桥,本次读不到)→ 第 2 次启动(桥生效)。其中第 1 次本来就是装插件必需的重启,本插件额外增加的是 1 次。
三条「理论上能绕过去」的路,都不成立或不划算:
| 思路 | 为什么不采用 |
| 安装期执行代码(postinstall) | 市场明确把「install-time code execution」列为红线;而且用户还没看到插件就改了他的安装目录 |
| 纯渲染端 API(requestFullscreen / moveTo / close) | 只能做视觉全屏;最大化、还原、最小化、置顶一律做不到,close() 还会被主窗口 preventDefault() 收进托盘,根本不是退出 |
| 系统级操控窗口(Win32 ShowWindow / AppleScript / xdotool) | 只在 Windows 上可行,还要 PowerShell 或原生依赖;macOS 要辅助功能授权,Wayland 下直接失效;且「Esc 退出」还要另开一条渲染端 → 宿主的通道。比打桥脏得多 |
真正的零重启出路在官方这边:只要 DSH 哪天开放窗口通道,本插件立刻变成「装完即用」—— client.js 的 bridge() 已经同时认 window.dshDesktop.windowControl 这个官方预留位,届时不需要改一行代码,也不用再重启两次。
窗口通道是怎么装上去的
lib/asar-bridge.js 对 resources/app.asar 里两个文件做等长就地替换:把两段纯文档注释换成注入代码(空格补齐到原长度),并原位更新头部里这两个文件的 integrity.hash。归档总大小、头部长度都不变,其余条目逐字节未动(改了几个条目就只写几个条目的字节;实际条目数随 DSH 版本变化,用 bridge-cli status 就能看到当前值,命令见「手动操作」)。
lib/main.js 加 839 字节 → 注册 4 条 IPC lib/preload-app.cjs 加 384 字节 → 暴露 window.dshWindowControl
新增的 6 条通道(v3 起):
dsh-desktop:window-state 读取 { fullscreen, maximized, minimized, top } dsh-desktop:window-fullscreen-set 设置窗口全屏 dsh-desktop:window-maximize 最大化 / 还原 dsh-desktop:window-minimize 最小化(v3) dsh-desktop:window-top 窗口置顶 / 取消置顶(v3) dsh-desktop:app-quit 弹原生确认框,确认后退出
preload 另暴露 window.dshWindowControl.v(当前为 3),供渲染端判断能力级别: = 2 表示支持退出前的原生确认框,>= 3 额外支持最小化与窗口置顶。
渲染端对 v3 能力做了方法级探测。旧桥上「最小化 / 置顶」会自动置灰,而不是点了没反应。
字节预算
注入块是等长替换,容量有限,实测占用:
| 文件 | 替换块 | 已注入 | 剩余 |
| lib/main.js | 1000 | 953 | 47 |
| lib/preload-app.cjs | 504 | 432 | 72 |
所以代码里把通道名统一拼成 P + 后缀(P = "dsh-desktop:")—— 写完整字符串的话, 6 条通道就放不下了。想再加功能,得先压缩现有实现腾字节。
版本升级
桥一旦装上,锚点注释就没了,无法原地把旧代码换成新代码 —— 升级只能「先用 app.asar.orig 还原成原版,再重新注入」。所以 install() 会比较归档里的实际版本与本版插件的目标版本(BRIDGE_VERSION),落后就自动走这条路,老用户不需要手动做什么。
唯一的例外是备份丢失:没有原版备份就无法还原,也就无法升级。这种情况下插件会明确提示,而不是静默停留在旧版本。
安全性
- 首次改动前一定先备份到 app.asar.orig。
- 注入前全量自检:按官方同一套算法重建目标条目并复算 sha256、核对头部等长、确认其余条目不受影响;自检不通过绝不动线上文件。
- 为什么用就地替换而不是重写归档:实测在 DSH 运行期间
open(asar, 'r+') 成功、rename(asar, …) 会 EBUSY(Electron 持有句柄)。
等长替换天然只需要原地覆盖几百字节,不需要换文件,因此运行中也能安全落盘。
- 注入代码是纯 ASCII,长度受「可替换块大小」硬约束,超长会直接拒绝而不是静默截断。
- 注入前符号预检:先确认目标文件里存在注入代码要依赖的符号(assertDesktopSender 等)。
DSH 升级改了这些代码时会直接拒绝,而不是把代码塞进不匹配的位置。
- 锚点是候选列表:每类锚点可以挂多个候选,build() 按顺序逐个尝试、第一个能用的就采用(全部失配才拒绝,报错里带上每个候选各自的原因)。DSH 改掉某段注释时,只要把新锚点追加进数组就还能装 —— 新旧版本同时兼容。锚点只用于搜索、不写进归档,不占注入字节预算。
- 头部哈希按条目精确定位:正常情况旧哈希在头部只出现两次(integrity.hash 与
integrity.blocks[0]),直接等长替换;如果归档里恰好有内容完全相同的另一个条目
(sha256 一样、旧哈希会出现 4 次),改成用本条目唯一的 offset 做锚点只改自己的两处,不会把别的条目的完整性改坏。
- 没有完整性块的条目也能装:归档没写 integrity 时跳过写哈希(那种情况下 DSH 也不校验),不会因为归档格式上的一点差异就永久拒装。
- 并发锁:改动期间持有锁文件,两个 DSH 实例同时启动时第二个直接跳过。
- 写入权限预检:动手前先确认写得了。装在 Program Files 这类需要提权的位置时,会给出「以管理员身份运行一次」这类可操作的提示,而不是抛一个 EACCES。
- 读取不再整档载入:只解析归档头 + 定向读两个目标条目(实测 93 ms/115 MB → 约 25 ms/4 MB),内存峰值也从约 120 MB 降到几 MB。
手动操作(排障用)
宿主半边会自动处理,正常情况用不到。真要用时,复制这一行粘进 cmd —— 它就是桥的命令行,不依赖 PATH、也不依赖当前目录: bat "%USERPROFILE%\.dsh\dsh-runtimes\dsh-primary-runtime\dependencies\node\bin\node.exe" "%USERPROFILE%\.dsh\profiles\desktop\node_modules\dsh-window-control\lib\bridge-cli.mjs" status
把最后那个 status 换成下表任一个(要参数就在后面接,比如 install --force):
| 子命令 | 作用 |
| status | 看当前状态(未安装 / 已安装 v3 / 锁 / DSH 是否在运行) |
| check | 只构建 + 全量自检,不落盘(已打桥时自动改为校验 .orig 备份) |
| install | 手动安装(幂等);install --force 先还原再重打一遍 |
| restore | 从备份还原 app.asar;restore --force 归档没打过桥也照写 |
| uninstall | 撤桥 + 清理备份(卸载插件时用它) |
| bench | 测一次读取 / 构建耗时 |
只有两处可能要改:dsh-primary-runtime(用 dir /b /ad "%USERPROFILE%\.dsh\dsh-runtimes" 看实际目录名)和配置档名 desktop。
check / bench 在已经打过桥的机器上照样能用:桥装上之后锚点注释已被注入代码替代,对着线上归档原地构建必然失败 —— 而这恰恰是最常见的排障现场。所以它们会自动改看 app.asar.orig(那就是原版),它能否构建通过,正好回答「DSH 升级后本版插件还能不能注入」。
排障还可以看这两个地方:
- 宿主日志:搜 [dsh-window-control]。
- 状态文件:~/.dsh/profiles/desktop/.dsh-window-control.json(记录上次启动装没装、为什么没装)。
宿主侧也可以关闭自动安装(例如企业镜像不允许插件碰安装目录)。在配置档的 cordis.patch.yml 里给本插件加 config: { autoInstall: false } 即可,此时插件只剩渲染端那部分能力。
卸载
本插件除了代码,还会往 DSH 安装目录的 resources/app.asar 里打一段桥(用来真正控制窗口)。
所以干净卸载永远是两步:先撤桥、再删插件 —— 顺序不能反。
为什么不能反过来、也不能「删插件自动还原」:插件一旦被移出配置档,它的宿主半边就不再被加载,没有任何代码会再执行,也就不存在「下次启动发现桥还在、插件已不在、顺手还原」的机会。这是 DSH 插件机制的硬限制,不是偷懒。
第 0 步 · 完全退出 DSH:托盘图标右键 →「退出」。
运行中撤桥只能就地覆盖 100 MB+;退出后是整档替换,更快也更安全。
路径 A · 命令行(推荐)
第 1 步 · 撤桥并清掉备份 —— 复制这一行粘进 cmd 回车: bat "%USERPROFILE%\.dsh\dsh-runtimes\dsh-primary-runtime\dependencies\node\bin\node.exe" "%USERPROFILE%\.dsh\profiles\desktop\node_modules\dsh-window-control\lib\bridge-cli.mjs" uninstall
期望看到:
[OK] 已还原为原始 app.asar;备份已清理桥状态 : 未安装
可能要改两处:dsh-primary-runtime(用 dir /b /ad "%USERPROFILE%\.dsh\dsh-runtimes" 看实际目录名)、 profile 名 desktop。装在 Program Files 下要用管理员 cmd。
node 已在 PATH 上的话,也可以 cd 到插件目录跑 node lib\bridge-cli.mjs uninstall。
macOS / Linux 就用这一种:
cd ~/.dsh/profiles/desktop/node_modules/dsh-window-control && node lib/bridge-cli.mjs uninstall
第 2 步 · 移除插件:在市场里点卸载即可。
路径 B · 不碰命令行
第 1 步 · 给 profile 加一条配置。用记事本打开 %USERPROFILE%\.dsh\profiles\desktop\cordis.patch.yml,在末尾追加四行: yaml
- id: dsh-window-control
config:
autoInstall: false
第 2 步 · 完全退出 DSH,再打开一次 —— 宿主半边会撤桥并删掉 app.asar.orig。
第 3 步 · 确认撤干净(可选):把路径 A 那条命令最后的 uninstall 换成 status,看到桥状态 : 未安装 即可。
「设置页里那一项消失」不能当判断依据:autoInstall 只关宿主半边,渲染端照常加载,那一项会一直在,只是里面显示「窗口通道尚未生效」。
第 4 步 · 在市场里卸载插件。
只想还原、暂时不删 —— 把路径 A 那条命令最后的 uninstall 换成 restore: bat "%USERPROFILE%\.dsh\dsh-runtimes\dsh-primary-runtime\dependencies\node\bin\node.exe" "%USERPROFILE%\.dsh\profiles\desktop\node_modules\dsh-window-control\lib\bridge-cli.mjs" restore
它与 uninstall 的区别是保留 app.asar.orig 备份(方便日后重装)。restore 有护栏:归档没打过桥时直接跳过、不写任何字节(提示「当前归档未打过桥,无需还原」—— 这是正常返回,不是报错),避免把旧备份写回去造成「新 exe + 旧资源」的降级。
懒得管那层桥也可以:只删插件、不撤桥也「能用」—— 桥留在 app.asar 里只是没人调用的死代码;而且 DSH 下次自动更新会整体替换 app.asar,桥自然就没了。只是那之前你的 app.asar 不是原版。
写入失败会自动回滚:install 落盘后一定复验一次,对不上就立刻用备份把归档还原回去,绝不留半写状态的 app.asar(那种状态下 DSH 连启动都做不到)。
跨平台情况(换一台电脑还能不能用)
先说结论:Windows 上换机能用;其它平台能用,但各有前提。
| 平台 | 状态 | 前提 / 说明 |
| Windows(默认装到用户目录) | ✅ 可用 | 本机就是这种,已实测:自动装桥 → 重启 → 生效 |
| Windows(装在 Program Files) | ⚠️ 需要提权一次 | 写入需要管理员权限。插件会明确提示,以管理员身份运行一次 DSH 即可,之后日常使用不再需要 |
| Windows(任意自定义路径) | ✅ 可用 | 不靠猜路径:宿主进程由 DSH 自己的 exe 拉起,据此推出安装目录,所以装在哪都能找到 |
| macOS | ⚠️ 有签名风险 | 若 DSH 做过代码签名与公证,改 app.asar 可能使签名失效,Gatekeeper 可能拒绝启动。插件装成功后会如实提示;出问题执行 restore 即可恢复,无需重装 |
| Linux | ⚠️ 可用但覆盖有限 | 主路径同样是「由 exe 位置推出」,另有 /opt、~/.local/share 等兜底候选 |
首装请重启两次(第一次装上、第二次生效);DSH 自己升级覆盖掉归档之后同理 ——下一次启动会自动重新装好,再下一次生效。装不上时界面会自己给出原因和自查办法,不会让你反复重启(见上一节的提示框)。
定位顺序是:显式指定 → DSH_ASAR 环境变量 → 进程信息(最可靠,覆盖自定义路径)→ 平台默认路径。一次都找不到时不会静默失败,状态文件里会留下完整诊断(argv / execPath / 每个候选是否存在 / 用的是哪套 fs)。
还有两条与机器无关的保险:
- 并发锁:同时开两个 DSH 实例时,第二个会看到锁并跳过,不会两个进程同时改同一个归档。
- 注入前符号预检:DSH 升级后如果锚点代码变了,会明确拒绝并说明「DSH 版本可能已变化」,而不会把注入代码塞进不匹配的位置。
改安装目录这件事还有一类绕不开的干扰:杀毒软件 / 企业管控可能拦截对安装目录的写入。
这不属于插件能处理的范围,遇到时表现为写入失败(插件会自动回滚并保持原样),把安装目录加入白名单即可。
测试范围与 Bug 反馈
本插件只在作者自己的 Windows 设备上、配合 DeepSeek Harness 0.2.0-rc.2 实测过;其它操作系统、DSH 版本或环境均未验证,能否正常工作说不准。
如果在你机器上出现异常,欢迎去 Issues 提一个 issue,最好带上:
- 操作系统与 DSH 版本
- 复现步骤
- 插件状态文件 ~/.dsh/profiles/desktop/.dsh-window-control.json 的内容(出问题时界面会提示它的位置)
为什么「启动自动铺满」的默认值是「最大化」而不是「真全屏」
DSH 主窗口在 Windows 上是这么建的: js new BrowserWindow({ titleBarStyle: 'hidden', // 不要系统标题栏
titleBarOverlay: { height: 40, color, symbolColor }, // 但让系统在右上角画 40px 覆盖层
})
也就是说,那个覆盖层是 Windows 系统画的最小化 / 最大化 / 关闭按钮。而 Windows 的规则是:
窗口一旦进入真全屏(setFullScreen(true)),整个标题栏覆盖层会被一起隐藏 ——
任何应用都如此,不是插件写坏了。这就是曾经「插件跑起来后右上角三个按钮都不见了」的原因。
最大化和真全屏一样铺满屏幕,但保留覆盖层,右上角三个按钮照旧可用,所以现在是默认值。
真全屏仍保留为可选项(需要连任务栏一起遮住时用),选中时设置页会给出提示。
顺带一提:DSH 渲染端用 html[data-windows-titlebar] + --dsh-windows-titlebar-height: 40px 给这 40px 预留了位置,所以最大化状态下布局与原生按钮是严丝合缝的。
退出为什么自己弹框(而不是用 DSH 自带的)
DSH 主进程确实有退出确认(before-quit → DesktopQuitConfirmation),但它是有条件的: js function resolveDesktopQuitPrompt(inspection) {
if (inspection === 'unknown') return 'quitActiveTasks'
if (inspection.activeTasks) return 'quitActiveTasks'
if (inspection.scheduledTasks) return 'quitScheduledTasks'
// 两者都没有 → 返回 undefined } // decide(): if (prompt === undefined) return true ← 不弹框,直接退出
也就是说:没有运行中的任务、也没有武装的定时提醒时,按 Esc 会静默退出(这就是曾经「按了 ESC 就直接退出」的原因)。
所以本插件改成自己弹框:
1. 渲染端把文案(标题 / 正文 / 说明 / 按钮)通过 dsh-desktop:app-quit 传给主进程;
2. 主进程用 dialog.showMessageBox(window, options) 弹真·原生框(带应用图标、窗口模态);
3. 用户点「退出」才 app.quit();点「取消」什么都不做;
4. 退出前顺便置位模块级 skipQuitConfirmation,让 DSH 自己的那次条件弹框不再重复出现,而 finishQuit() 的清理照常执行。
桥缺失时渲染端退回 window.confirm,保证「退出必确认」在任何情况下都成立。
配置存放位置
渲染端把设置存在 localStorage(origin = dsh-app://app),键名 dsh-window-control:settings,字段:enabled(总开关)、startMode ('maximized' / 'fullscreen' / 'off')、escQuit、escGuarded、 escExitFullscreen。同步可读,因此「启动时自动铺满」能在页面加载的最早时机生效。
旧版本(≤1.0.0)只存布尔量 startFullscreen,读取时自动迁移: true(旧默认)→ 'maximized'(不再默认真全屏,避免右上角按钮消失), false → 'off'。旧字段不会被删除,只是不再使用。
目录结构
lib/index.js 宿主半边:每次启动自动检测 / 安装 / 记状态;autoInstall=false 时撤桥 lib/asar-bridge.js 桥引擎:asar 读写、注入、自检、回滚(纯 Node,零依赖) lib/bridge-cli.mjs 同一个引擎的命令行入口(排障用) lib/client.js 渲染端:设置区块 UI + Esc 处理 + 启动铺满 locale/zh.json 插件在市场里的标题与描述(中文;界面文案在 lib/client.js) locale/en.json 插件在市场里的标题与描述(英文;界面文案在 lib/client.js) icon.svg 插件图标 cordis.patch.yml bundle 层挂载声明
界面文案同时提供两套:优先接 DSH 的 Client locale 服务(跟随应用语言设置),拿不到就按 navigator.language 选内置字典 —— 两条路都可用,所以不会因为 locale 服务缺席而没文案。
开发自测 bat
node _selftest.mjs :: 渲染端沙箱自测,当前 79/79 通过
node _bridge-selftest.mjs :: 桥引擎自测,当前 60/60 通过
桥引擎自测在临时目录里造微型假 asar(不碰真的归档文件,也不依赖它),复刻了真归档的全部结构约束,覆盖:安装 / 幂等 / 自检 / 回滚 / 卸载还原 / 并发锁 / 符号预检 / 锚点漂移 / 权限不足。
真实归档上的端到端验证另外做过:构建 / 自检 / 安装 / 回滚全部通过,全量逐条目 sha256 复算零不一致(条目数随 DSH 版本变化;对你这一版复测用 bridge-cli check,命令见「手动操作」,它的「其余 N 个条目不参与改动」一行就是当前条目数),且产物与早期 Python 版实现逐字节完全相同。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
YunLiZeMeng/dsh-window-control
每次打开 Harness 都要手动全屏,烦不烦?这个插件帮你把屏幕填满。自包含:安装完成后,插件的宿主端会自己打开…
相关插件 · 对话 / 记忆
上下文中枢oceanbase/powercontext
上下文跟着工作走 —— 跨会话、跨智能体、跨人。
★ 1,487已汉化DSH
73
智能体记忆库plastic-labs/honcho
Honcho 是记忆基础设施,用来构建有状态的智能体。这些智能体能理解不断变化的人、智能体、群组、项目和想法。
★ 7,894已汉化DSH
71.7
长程智能体控制面loopx-project/loopx
给 Agent 一个目标,让工作持续向前。
★ 6,358已汉化DSH
71.3
智能体上下文数据库volcengine/OpenViking
给 AI 智能体用的上下文数据库
★ 41,379已汉化DSH
71.3
长效机器人团队Fei2411/DS-Bot
DS Bot 是一个基于 DeepSeek Harness简称 dsh的 bot 插件,插件将 dsh 改造为 bot…
★ 5,084已汉化DSH
71.1
可携带记忆层EverMind-AI/EverOS
为什么选择 EverOS
★ 13,778已汉化DSH
71.1
这是你的仓库?认领条目