Files
workdsh/docs/architecture.md
T

6.0 KiB
Raw Blame History

DSH Desktop 架构

总览

DSH Desktop 是一个薄的 Electron 宿主。它在 Electron main 进程中启动官方 DSH Host,Host 再通过 loopback HTTP/WebSocket 提供普通 Web UI。Desktop 没有另造一条 renderer IPC 插件系统,也不把 Electron API暴露给页面。

flowchart LR
  User[用户] --> Native[Electron main / tray / window]
  Native --> Launcher[Profile launcher]
  Launcher --> Host[Host Cordis generation]
  Host --> Carrier[Loopback HTTP + WebSocket]
  Carrier --> Renderer[Sandboxed Web renderer]
  Host --> Upstream[Upstream DSH services]
  Host --> Desktop[Desktop-owned plugins]
  Host --> ThirdParty[Third-party plugins]
  Launcher --> Services[desktopProfiles + desktopPnpm]
  Services --> ThirdParty

启动顺序

  1. Electron 获取单实例锁,并读取 Desktop 私有的 profile/mode 状态。
  2. Launcher 准备激活 profile,但不会为了列举 profile 而改写用户 profile。
  3. Launcher 提供当前 generation 的 native runtime、desktopProfiles bootstrap 和内置 pnpm 环境。
  4. Host Cordis root 启动 Loader entries。Desktop service 在第三方插件可读取前注册。
  5. 官方 dsh-base、dsh-web-app 和 profile 中的第三方 bundle 组成 Web carrier。
  6. Host 绑定 loopback 端口,Electron 创建 BrowserWindow 并加载同源页面。
  7. Web surface 成功加载后才创建托盘并提交 profile 的 last-known-good 状态。

任何 profile 或模式切换都会 dispose 当前 generation,再启动新的 generation。Service reference、窗口对象和 subprocess handle 都不能跨 generation 缓存。

Host、Client 和 native runtime

  • Upstream Host:agent、model、tool、session、settings、webServer 和 subprocess 等官方能力。
  • Desktop Host:窗口、托盘、profile、终端、更新,以及对第三方开放的两个 service。
  • Web Client:官方 Web UI 和第三方浏览器界面。它通过 loopback carrier 工作,不直接调用 Electron。
  • Native runtime:Electron BrowserWindow、系统托盘、文件/网络/安装器适配。desktopRuntime 只供 Desktop 自有 row 使用。

兼容模式的 Client face 会校验环境,并且只通过 overlay slot 加入一条独立的 36 像素 Desktop frame;官方 layout、root、sidebar 与 conversation 作为完全无关的内容 viewport 从它下方开始。扩展窗口会禁用官方 root layout,安装自己独立注册的 Desktop layout/sidebar surface,并在倒 L 材质 frame 中继续承载官方 sidebar、conversation 与 details occupant。增强模式保留独立 root registration 与最初的紧凑内部 caption 几何。macOS 与 Windows 会按系统能力使用原生材质,同时不改变上游 occupant slot 的所有权。

Desktop 级确认、警告、错误与结果不会进入 Web Client 组件树。DesktopDialogWindow 会创建独立、沙箱化的模态 BrowserWindow,应用共享的空白 utility frame,并在可能时以当前 generation 窗口为 parent,只接受一次有界本地结果。恢复模式与新增 Profile 是使用同一套无标题 frame 的独立 Desktop-owned 窗口。恢复页面本身使用 shadcn,先展示原因,再提供四个工作流 Tab;破坏性恢复操作会把确认交回 DesktopDialogWindow。

原生 Shell generation 与平台 adapter

ElectronRuntime 负责协调 Host 与原生桌面环境,但不直接拥有窗口和托盘的细节。每次启动由一个 ElectronShellGeneration module 完整拥有 BrowserWindow、Tray、相关 Electron listener、导航限制、外链处理和缩放快捷键。释放 generation 必须通过其幂等 release() interface 完成,调用方不能跨 generation 缓存或单独销毁这些资源。

平台差异集中在启动时选择一次的 ElectronPlatformStrategy seam。Windows、macOS 与 Linux adapter 声明目录选择、Shell 模式切换和更新下载能力,并负责各自的菜单、Dock 图标与原生材质操作。新的平台分支应进入对应 adapter;generation 与 runtime 中只保留各平台共享的生命周期流程。

Profile 与服务边界

profile 的名字和绝对目录由 desktopProfiles.current 提供,不能从 argv、settings 或 URL 猜测。list() 是只读发现;select() 记录 pending target,并通过重启完成切换。

desktopPnpm.run() 直接跑内置 pnpm;runPlugin() 通过打包的 DSH CLI 维持 profile 初始化、相对 source 和 bundle reconcile。两者都属于当前 generation,并由 subprocess service 管理完整进程树。

Launcher 私有的 desktopRuntime、desktopPnpmBootstrap、Electron executable、Node helper 和 ABI 环境不是第三方 API。公开 contract 只有 dsh-plugin-desktop/profile-service 与 dsh-plugin-desktop/pnpm。

打包与运行时闭包

发布包使用 Electron Builder 和 app.asar,但需要物理 unpack 的依赖(例如 pnpm、node-pty、Windows ACL/native 文件)会放在 app.asar.unpacked。Packaged runtime gate 会检查 ASAR 入口和物理运行时入口,profile fallback 不能把符号链接指向无法被 Node 解析的虚拟 ASAR 路径。

根 workspace 使用 Yarn;固定的 deepseek-harness/ 子模块保持上游自己的 pnpm workspace。桌面代码、测试、打包配置和发布脚本属于 dsh-plugin-desktop/,不修改上游子模块。

维护者深入阅读