dsh-plugin-desktop
已验证dsh-plugin-desktop · v2.0.0 · MIT · Web 界面
DSH Desktop: an Electron shell composed as a DeepSeek Harness Cordis plugin
安装
dsh plugin add dsh-plugin-desktop 用 dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南。
源码
作者
说明文档
DSH Desktop
English | 中文
dsh-plugin-desktop 在 Electron 中运行 DSH,同时仍然参与普通 Cordis 组合。安装后的应用名称为 DSH Desktop。该包提供 dsh-plugin-desktop 可执行命令和 dsh-desktop 别名;已注册的 npm 包名是可靠的 npx 入口。
架构
Electron 可执行文件只包含最小启动代码。它获取单实例锁、解析当前选中的 DSH profile、提供原生运行时能力,并在 Electron main 进程中启动 Host Cordis 根。desktop-shell Host 插件通过 Cordis effect 拥有 BrowserWindow、导航策略、settings namespace,以及关闭与退出生命周期。原生 runtime 拥有实体托盘;desktop-shell、desktop-profiles、desktop-terminal 与 desktop-updates 则通过有序 item registry 提供 effect-scoped 命令。
两种呈现模式都复用现有 loopback Web carrier。profile 挂载普通 dsh-base 与 dsh-web-app bundle;Host 把 HTTP 与 WebSocket surface 绑定到 127.0.0.1 的临时端口;Electron 在沙箱 renderer 中加载该同源页面。Electron 不维护自有插件 roster,不使用 preload bridge,renderer 也不会获得原始 Electron API。
desktop package 拥有普通 Host 与 Web Client 两个 face。它的 Client face 会在两种模式下校验 Host 提供的模式与平台 marker。兼容模式随后直接返回,不注册 service、slot、样式或呈现;高级模式则安装下文所述的 desktop layout service 与 root 呈现。两种模式下,第三方 Web client 都继续使用普通 DSH 模块图。
托盘中的 profile 选择器会列出现有 profile,以及可延迟创建的 desktop 与 web 默认项。可选 profile 必须直接按顺序组合 dsh-base 与 dsh-web-app;headless、损坏或已经内嵌 desktop bundle 的 profile 仍会显示,但不可选择。只有 desktop 是 Launcher 管理的 profile:它会修复安装方拥有的前缀,同时保留第三方 bundle 的相对顺序。其他被选 profile 的 manifest、用户 patch 与依赖均保持不变。Launcher 只会为当前 generation 在 dsh-web-app 后插入自有 desktop layer,不会把该 layer 持久化到被选 bundle 列表。
Profile 选择保存在 Electron user data 下的 desktop 自有状态中,而不是被选 profile 内的另一个字段。切换会先记为 pending,再通过有序重启生效。只有 Cordis 树与原生窗口成功挂载后,新 profile 才会成为 last-known-good;托盘会在 Web surface 加载后才创建,而且该状态提交会在托盘命令能够运行前同步完成。Pending generation 启动失败时会回滚并自动重启一次。官方 profile 默认共用同一个 DSH home 中的 sessions、settings 与 storage,因此切换不会复制或迁移记录;自定义 profile patch 仍可主动重定向其中某个持久化根。
Launcher 会在 Loader entry 挂载前注册作用于当前 generation 的 ctx.desktopProfiles service。其不可变 current 值包含激活 profile 的 name 与绝对 dir;list() 只读执行发现,select(name) 会串行化“先持久化、再重启”的切换,而不会就地改变当前 generation。该 service 是 Desktop Host capability,不是 renderer bridge,也不是当前上游 DSH 已提供的 active-profile API。
Cordis 的裸插件导入从持久化 profile 解析。一个范围受限的 Node resolve hook 只处理由 @deepseek-ai/cordis-plugin-loader 发起的导入,因此即使打包后的 Electron 不暴露 Node 内部 ESM Loader,profile 本地第三方包与修复后的 launcher fallback 仍使用同一条解析路径。
在 profile 准备与 Cordis boot 之前,Launcher 会把只包含固定版本内置 pnpm 命令的私有命令目录前置到当前 Electron main 进程的 PATH。因此 Host 与第三方插件从启动开始即可发现该 package manager,也可以通过普通 DSH subprocess provider 使用它,而无需系统安装 Node.js。该 ambient path 是兼容 surface,不是正式的插件管理 contract。
desktop-pnpm Host row 会提供 ctx.desktopPnpm,用于针对不可变激活 profile 执行受管 package operation。run(args, signal?) 会在激活 profile 目录中直接执行内置 pnpm;它是低层 operation,不承诺 DSH profile 初始化、调用方相对 source 锚定或 bundle reconcile。runPlugin(args, invokingDir, signal?) 则会从调用方绝对目录启动内置的 dsh plugin --profile <active>。插件安装、卸载、更新与依赖修复必须使用 runPlugin(),使上游 CLI 继续拥有相对 file: 与 link: spec、pnpm profile working directory、首次初始化,以及成功后 dsh.profile.bundles reconcile 的权威语义。
两个方法都会返回实时 stdout 与 stderr stream、在完整 process tree 退出后才 settle 的 done promise,以及 cancel()。每个 generation 同时最多运行一个 operation。Service 使用普通 DSH subprocess provider、准确的已打包 JavaScript entry、无 shell argv,以及只属于 child 的 DSH home、Electron-backed Node、CI 与 native-module ABI 值。公开 runtime path 仍不会暴露 node 或 dsh;其中私有 helper、ELECTRON_RUN_AS_NODE 与 npm ABI 变量只存在于 package-manager subprocess tree 内。Launcher 不会修改系统 PATH、shell 启动文件、profile 配置或 .env 文档。
插件作者应遵循 Desktop 插件 service 架构中记录的受支持 contract import、生命周期规则与适配模式。
模式设置与重启边界
DSH home settings.yaml 文档中的 dsh-desktop.mode 字段是单一事实源:
dsh-desktop:
mode: compatibility # 或 advanced
Launcher 会在组合一个 generation 之前,读取当前 @deepseek-ai/dsh-settings-file row 解析到的同一份文件。Host 通过标准 settings service 注册 dsh-desktop namespace。profile manifest 中没有平行的模式值。
用户可以从托盘选择另一种模式,也可以手工编辑 DSH home 中的 settings.yaml 文档。托盘会更新已注册的 dsh-desktop settings namespace,手工编辑则修改 settings provider 观察的同一文件。修改提交后会请求一次有序重启:先 dispose 当前 Cordis 树,仅当零退出码的 shutdown 成功时才让 Electron relaunch。应用绝不会在存活的 renderer generation 中热切换 root slot、原生窗口材质或 Loader row。
Linux 只支持兼容模式。其托盘模式命令会被禁用,advanced 值会被拒绝,而不会静默降级。
兼容模式
dsh-desktop.mode 默认为 compatibility。该模式创建带有操作系统原生边框的普通窗口,并加载当前 DSH profile 中的官方 Web surface。macOS 会隐藏可见的页面标题。Windows 保留原生标题栏图标并显示 DeepSeek Harness Desktop,但会移除窗口菜单栏。原生标题栏颜色与外观由操作系统拥有。
desktop Client module 会校验模式与平台 marker,随后在兼容模式下不产生任何 effect。它不提供或替换 layout service,不注册 root 或 sidebar occupant,不安装样式,也不改动 conversation surface。兼容模式会保留被选 profile 自身的 layout、sidebar 与 conversation 组合;普通 desktop 与 web profile 因而会原样保留官方 row。
Cordis row 会在 profile 激活期间登记原生窗口参数。Launcher 只在 app-boot 完成并审计整个 profile 后创建窗口,因此首个 renderer manifest 会包含所有已激活的官方、desktop 与第三方 client plugin,同时插件自身不会在 Loader entry 内等待整棵 Loader tree。
在 Windows 上,Launcher 会固定使用现有 browse 目录选择 backend 与 client surface,而不使用自适应 native chooser。因此 workspace 选择始终在 Web UI 内完成,也不会在 Electron main 进程中加载原生 N-API 对话框 worker。macOS 与 Linux 仍使用上游自适应 chooser。
在两种呈现模式下,Windows PowerShell 都会保留上游 pwsh-sandbox 行为与 Windows ACL confinement。Launcher generation 只会把该 Host provider 替换为同一 package 中的 dsh-plugin-desktop/windows-pwsh-sandbox 子路径。对于与上游 ACL runner 完全匹配的 argv,adapter 会让打包后的 Electron executable 通过私有 trampoline 以 Node 模式启动,在创建受限 PowerShell 进程前移除 Node-mode 环境变量,然后把全部 policy 与失败处理重新委托给上游 runner。Desktop deploy root 还会固定一个 Yarn patch,在两条原生受限进程路径上把 STARTF_USESHOWWINDOW、现有的 STARTF_USESTDHANDLES 与 SW_HIDE 组合起来。这会保留已捕获的 stdio 而不抑制 console 分配,并在 Windows 为 GUI Host 启动的 PowerShell 进程创建首个 console 窗口时,请求使用隐藏的初始显示状态。它不会使用与上游实现不兼容的 CREATE_NO_WINDOW 或 CREATE_NEW_CONSOLE flag。直接使用 danger-full-access 的 PowerShell、macOS 与 Linux 执行路径保持不变;Windows confinement 失败时不会自动回退到不受限执行。
高级模式
高级模式是为 macOS 与 Windows 显式组合的 desktop 呈现。Launcher 会在读取全部用户 patch 后禁用官方 ui-layout Loader row,保持官方 ui-sidebar 与 ui-conversation row 启用,并把所选模式应用到 desktop-shell。
desktop Client 随后在自身 Cordis fiber 生命期内提供 layout service,并且只注册 root slot occupant。其 root 为不变的上游 sidebar、conversation、details 与 overlay contribution 声明 seat。官方 sidebar 继续作为 sidebar occupant,并继续声明 workspace browser、settings shell 与纯新增 footer action seat。这样会保留其组件行为、收起动画与第三方扩展点,而 desktop package 只拥有 frame 几何与原生材质。
高级 theme presenter 会把当前上游 theme snapshot 投影到 document,包括 color scheme、解析后的 token 值、深色模式 marker 与 theme-color metadata。它订阅普通 theme 变化,generation dispose 时只移除由自身投影的状态。
对于高级 generation,Electron adapter 还会在 Host boot 完成后读取已注册的 ui-theme.preference,并在创建窗口前把内置 light、dark 或 system 值同步到 Electron 原生外观。窗口存续期间提交的 preference 变化会更新原生材质,dispose 则恢复此前的 Electron 外观。仅存在于 Client 的第三方 theme id 不会改变该 Host preference。
desktop sidebar surface 会把上游 sidebar-fill token 局部设为透明,因此官方 sidebar 与 session 列表渐隐可以透出原生材质,而无需改变其组件样式。
在 macOS 上,高级窗口使用透明 hidden-inset 标题栏、定位后的红黄绿按钮与原生 sidebar vibrancy。其 90 CSS 像素收起列会把官方 56 像素 rail 居中放在 desktop 自有的红绿灯顶部 inset 下方。Sidebar surface 本身不可拖动;红绿灯右侧由 desktop 自有的透明 32 CSS 像素条提供窗口拖动目标。Conversation 与 details 完整 surface 上方的 caption row 会保留 20 CSS 像素视觉间距,同时提供另一块透明的 32 CSS 像素拖动命中区域。按钮、链接、输入框、对话框与显式声明 app-region: no-drag 的 contribution 仍可交互;放在顶部 32 像素内的自定义 pointer target 也必须声明同一排除规则。在 Windows 上,官方 sidebar 保持兼容模式几何:收起 56 像素、默认展开 280 像素,并沿用相同的上游过渡行为;透明 surface 会透出 Mica。窗口使用带原生控件的隐藏标题栏、透明 overlay、Mica 背景材质、阴影、圆角与粗可调整边框。Electron 仅在 Windows 11 22H2 及以上版本提供由系统绘制的 Mica 材质。Desktop 自有的 32 CSS 像素 caption row 会横跨 Windows 的 conversation 与 details 两列;完整的上游 slot surface 从该行下方开始,因此官方与第三方 Header contribution 会保持原有相对布局,无需针对具体元素设置 caption offset。Linux 会拒绝高级模式,而不会静默降级到与持久化设置不同的呈现。
开发
该包由仓库根目录的 Yarn workspace 管理。相邻的 deepseek-harness/ checkout 仍是独立的上游 pnpm 项目,不属于 Yarn workspace。请从仓库根目录安装并验证 DSH Desktop:
yarn install
yarn check
该检查会验证生产依赖图中的每个必需第一方 peer 都由 desktop deploy root 声明。Headless Loader smoke 会激活 launcher 拥有的 desktop row 与 profile 本地第三方 row,然后启动已发布 Web profile 并检查其 loopback 根页面与 client manifest。单元和类型测试覆盖两种 profile 组合、重启栅栏、client environment 校验、desktop layout 状态与各平台原生窗口选项。
有图形会话时,显式启动桌面应用:
yarn dev
dev 会在启动前自动构建,不需要另行手动构建。
以下 headless-safe 启动器入口不会导入或启动 Electron:
node lib/bin.js --help
node lib/bin.js --version
插件工作流
使用普通 DSH 命令管理任意 profile:
dsh plugin --profile desktop add third-party-plugin
dsh plugin --profile desktop remove third-party-plugin
dsh plugin --profile desktop update
应用默认使用 desktop。可以在托盘的 Profile 子菜单中选择其他 Web-capable profile;切换时应用会重启。生成的 DSH 终端会让裸命令默认作用于当前激活 profile,因此以下短命令可以直接修改它:
dsh plugin add third-party-plugin
dsh plugin remove third-party-plugin
dsh plugin update
显式 --profile <name> 始终具有更高优先级,可用于在切换前准备其他 profile。
[email protected] 尚未预装,也不是 DSH Desktop 的 dependency。该版本仍从 config/argv 解析 profile,并通过私有 child-process 代码启动 dsh plugin;它既不读取 desktopProfiles,也不使用 desktopPnpm,package exports 也没有 runner injection seam。后续兼容版本必须动态探测 Desktop service,同时在普通 DSH 中保留现有 CLI fallback。此外,1.2.3 的源码仓库与 npm tarball 均未包含完整 MIT 许可文本或版权通知,因此该版本尚未通过内置再分发 gate。用户主动安装第三方 package 与 Desktop 将其嵌入 application archive 或 installer 是两个独立边界。
Required injection、可选 Desktop 适配、TypeScript 示例、cancellation 与 fallback 指南详见面向插件作者的 service 文档。
随后可以通过 npm 启动该包:
npx dsh-plugin-desktop
第三方 Host 插件只需提供普通 dsh.bundle patch。包含浏览器 UI 的插件还要发布普通 dsh.client 元数据,将 platform 设为 "web",并导出 ./client 产物。上游 Web 客户端模块图会在两种模式下发现它;Electron 不要求单独的客户端构建,也不引入 desktop 专用注册 API。高级模式 contribution 必须面向该显式组合中存在的 service 与 slot,不能假设官方 layout 或 sidebar occupant 拥有它们。
桌面操作
打包后的 macOS 与 Windows 应用会在启动 60 秒后查询 https://www.dshdesktop.cn/api/desktop/version,并在每次检查完成六小时后再次查询。每次 no-cache 请求的期限为 15 秒,并与托盘中的 Check for Updates… 命令共用一个 in-flight operation。响应只有在包含规范的 stable Semantic Versioning 时才会被接受。后台检查遇到网络、HTTP、超时、无效响应、相同版本或服务端旧版本时保持静默。手工检查一定会显示原生结果对话框:相同或旧版本会显示当前安装版本,失败会提示用户重试,严格更新的版本则显示 Download 或 Later。自动更新提示会按版本记录,用户仍可从托盘显式重试。开发运行、未打包启动与 Linux 不会下载安装包。
选择 Download 后,应用会先重新确认服务端版本没有变化,然后才首次请求对应平台用于计数的固定下载入口。DSH Desktop 使用 Electron 网络跟随 service redirect,把不超过 1 GiB 的文件流式写入私有、按版本划分的 user-data 目录,并在交付前拒绝不完整的 DMG 或 Windows PE。macOS 会打开下载好的 DMG,并提示用户替换 Applications 中的应用后重新打开。Windows 会在 NSIS 安装器准备完成后再次确认;选择 Restart and Install 会启动安装器,并在当前进程退出前请求 Cordis 有序 teardown。下载、文件系统与安装器打开失败都会保持静默,同时保留托盘中的可重试版本操作。
Release operator 必须先发布两个平台产物,再让版本可被发现。产物与 download redirect 准备完成后,在 Upstash Redis console 中把 deepseek-harness-desktop:release:version 设置为规范的 stable 版本,例如 SET deepseek-harness-desktop:release:version 2.0.1。版本 API 会立即生效;key 缺失、服务不可用或值无效时,Desktop 不会显示任何提示。
在 macOS 与 Windows 上,Open DSH Terminal 会打开以当前激活 profile 为工作目录的系统终端。欢迎信息会显示应用版本、当前 profile、profile 目录与 DSH home,并列出配置与插件管理命令。在该终端内,裸 dsh、dsh --dump-config,以及没有选择 profile 的 plugin 子命令都会默认使用当前激活 profile;显式 --profile 与上游 web alias 会保留原有含义。DSH Desktop 会在自身 user-data 目录下按 profile 生成私有 dsh、pnpm 与 node shim,设置 DSH_HOME,使用当前 profile 作为工作目录,并且只在该终端的 PATH 前置 shim 目录;之后切换 profile 不会改变已经打开的终端命令。它不会修改全局环境或 shell 启动文件。macOS launcher 会先保留用户的交互式 zsh 或 bash 设置,再恢复 desktop 自有变量。Windows 会依次选择 PowerShell 7、Windows PowerShell 或命令提示符,并在新的 Windows Terminal 窗口中打开;如果 wt.exe 不可用,则由私有 cmd start broker 创建可见控制台。同步启动失败与 broker 非正常退出会显示在原生错误对话框中。Linux 不组合该终端命令。
原生生命周期
关闭窗口会隐藏窗口,Host Cordis 树继续运行。托盘可以重新打开窗口、选择激活 profile、打开隔离的 DSH 终端、检查 stable release、通过标准 settings namespace 更改模式,或请求显式退出。Profile 与模式切换都会先 dispose 当前 Cordis 树,再让 Electron relaunch。原生退出、SIGINT 与 SIGTERM 也会在退出前请求 dispose;超过五秒或收到重复请求时会强制完成最终退出。导航与重定向被限制在确切的 loopback origin;外部 HTTP、HTTPS 与邮件链接由操作系统打开;renderer 启用 contextIsolation 与 Chromium sandbox,并关闭 Node integration。
打包
yarn package:dir 为当前宿主平台创建未封装目录。如果应用归档缺少 desktop 更新与终端模块、DSH CLI bootstrap、内置 pnpm 入口或物理 deployment package,packaged-runtime gate 会拒绝该产物。Electron Builder 会把根 manifest、desktop runtime 与完整依赖树输出到 app.asar.unpacked;Host profile boot 与 CLI bootstrap 都会使用这棵物理树,因此 DSH profile fallback 的符号链接不会指向虚拟 ASAR 目录。build/app-icon.png 保持为未经修改的 iOS Default 源图,并继续作为 Windows 与 Linux 应用图标。构建过程会运行 scripts/generate-mac-app-icon.mjs,把该图缩放为 824 × 824 像素并居中放入透明的 1024 × 1024 画布;macOS 打包与运行中的 Dock 都使用生成的 build/app-icon-mac.png。build/tray-icon.svg 是品牌蓝托盘源文件:构建过程会派生由 macOS 系统自动着色的模板图,以及固定品牌蓝的 Windows 与 Linux 托盘图。
Windows x64 本地安装包
请使用原生 Windows x64 电脑,并安装 Git 与 x64 Node 22.23.2(与 CI 使用的版本相同)。打包命令接受官方发行版仍包含所需 Corepack 命令的 Node 22.19+ 与 Node 24.x。在一个最新的 v2 checkout 中打开 PowerShell,然后执行:
git submodule update --init --recursive
corepack.cmd yarn install --immutable
corepack.cmd yarn dist:win
该流程不要求 Python 或 Visual Studio C++ Build Tools。Windows 命令会直接使用 node-pty 内置的 x64 Node-API 二进制,而不会让 Electron Builder 从源码重新编译;如果安装包 staging tree 缺少这些二进制,packaged-runtime gate 会直接拒绝产物。
dist:win 会拒绝非 Windows 或非 x64 宿主,先执行一组 Windows 可运行的 gate,其中包括 build、全部 TypeScript compiler face、打包与原生 shell 聚焦测试,以及 runtime-closure verifier;随后再构建 NSIS 安装向导,并校验生成的两个 PE 文件。完整跨平台 suite 仍由 CI 持有,因为其中部分 POSIX 执行测试不是 Windows 程序。安装向导支持当前用户安装或提升权限后的所有用户安装,可更改安装目录,会创建开始菜单与桌面快捷方式,并且卸载应用时保留 DSH 用户数据。版本 2.0.0 会输出到 dsh-plugin-desktop\dist\DSH-Desktop-2.0.0-x64-Setup.exe;用于 smoke 测试的未封装程序仍位于 dsh-plugin-desktop\dist\win-unpacked\DSH Desktop.exe。
该本地命令会主动移除 Windows 证书变量,并设置 signExecutable=false。产物可以安装测试,但没有 Authenticode publisher,因此 Windows 可能显示 Unknown publisher 或 SmartScreen 警告。签名后的 Windows release、证书校验、安装器升级与卸载测试,以及原生 UI 和 sandbox smoke 仍是独立的发布 gate。
模型体验
无。desktop package 只改变应用组合与原生呈现,不增加任何模型可见的指令、工具、事件或请求字段。
KV Cache 影响
无。模型请求仍由同一套 DSH Host 与 client feature plugin 组装。
已知限制与暂缓事项
- 添加或删除 profile bundle 后必须重启 DSH Desktop;Launcher 不监听 profile manifest。从托盘选择其他 profile 时会自动完成该重启。
- 切换 compatibility/advanced 模式按设计必然重启应用;存活的 generation 不会热切换 Loader row、slot 所有权或原生材质。
- Linux 不支持高级模式。Linux 继续使用兼容呈现。
- macOS 与 Windows 托盘终端会提供私有
dsh、pnpm与nodeshim。除此之外,Host runtime 会在当前 Electron 进程的PATH中公开内置pnpm命令作为 ambient compatibility,并提供受管desktopPnpmservice;这些命令都不会加入系统PATH,Linux 目前也没有 desktop 终端命令。 - 在 Windows 上,ambient
pnpm命令与 lifecycle Node helper 是.cmdshim。desktopPnpm.run()与runPlugin()会启动准确的已打包 entry,从而避免 manager process 的 shell lookup;上游dsh plugin、PowerShell 与命令提示符则可通过 command interpreter 解析 ambient shim。第三方插件直接调用 Nodespawn('pnpm', { shell: false }),或 lifecycle script 直接以shell: false执行其.cmdnpm_node_execpath,仍属于不可移植行为,应改用受管 service 或 shell-aware 启动路径。 [email protected]仍是用户可选安装的第三方 package,而不是内置 marketplace。只有重新审计的版本同时消费可选 Desktop service、保留普通 DSH fallback,并包含再分发所需的完整 license notice 后,才会重新评估预装。- 更新交接只验证下载容器,不验证 publisher 身份。macOS 仍要求用户从已打开的 DMG 替换应用;Windows 会运行已下载的 NSIS 安装器,但本地
dist:win产物没有签名。签名产物、Authenticode/publisher 校验、SmartScreen 信誉与原生升级测试仍是发布 gate。 - 共享 carrier 使用 loopback HTTP 与 WebSocket,而不是 Electron IPC。替换它需要上游 DSH 提供 transport 扩展点,不属于该独立包的范围。
- 该项目目前固定使用已发布的 DSH
0.1.0-rc.6family,而相邻的deepseek-harness/源码 checkout 早于该版本。因此,测试验证的是已发布包接口,而非上游未发布源码。 package:dir是用于 smoke 的未封装产物。dist:win会额外生成未签名的 NSIS 测试安装包,但不会建立 Authenticode 身份或 SmartScreen 信誉。安装与升级行为、原生通知与终端、Windows ACL sandbox,以及每台目标机器上的原生材质外观仍属于目标平台验证边界。