chengxiaosheng/dsh-desktop--packages-dsh-plugin-desktop ↗★ 0
dsh-plugin-desktop
DSH Desktop —— 虚拟 Web 服务器拦截器 Spike 插件
AI 分析
核心用途是提供无套接字的虚拟 Web 服务器拦截器、Shell 栏和 Electron 主进程。适合需要构建或运行 DSH 桌面端外壳及进行路由拦截的开发者和系统任务。
安装
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:chengxiaosheng/dsh-desktop#a791939ec53d431ff974200f5fbda3be5c419c2d&path:packages/dsh-plugin-desktop说明文档
阅读完整 README ↗dsh-plugin-desktop
The desktop shell package: the socketless webServer interceptor, the shell row, and the Electron main. See the repository README and architecture.
Entries
All runtime sources are TypeScript; pnpm build compiles src/ + electron/ with tsc into lib/ (the compiled paths below) and bundles the client half (src/client/ → lib/client.js) with esbuild, and pnpm check adds a strict type-check of sources, scripts, and tests.
| Entry (source → compiled) | Provides |
|---|---|
dsh-plugin-desktop/webserver (src/webserver.ts → lib/src/webserver.js) | webServer as VirtualWebServer — the official route-registry contract with no socket |
dsh-plugin-desktop (default) (src/index.ts → lib/src/index.js) | the shell row: registers the durable desktop settings namespace (the close-window preference) |
dsh-plugin-desktop/client (src/client/ → lib/client.js) | the browser half: the General-settings close-behavior and restart-host rows, the settings.desktop locale dictionaries, and the preference policy — present only in the desktop composition |
electron/main.ts → lib/electron/main.js | the Electron main: profile boot, file:// page, IPC bridge (envelope RPC + raw virtual-host http-request download surface), the close-to-tray interception, and the tray |
electron/tray.ts → lib/electron/tray.js | the system tray: always-visible show/restart/quit seat (labels published by the renderer from the settings.desktop locale dictionaries) |
electron/ipc.ts → lib/electron/ipc.js | the bridge: envelope RPC + raw virtual-host http-request download dispatch + the dsh:close-behavior preference channel |
electron/boot-desktop.ts → lib/electron/boot-desktop.js | headless-testable bootDesktop + composeDesktopPatches + composeDesktopManifest + the full-route dispatchHttpRequest (req/res stand-ins for in-process webserver dispatch) |
electron/desktop-services.ts → lib/electron/desktop-services.js | the desktopProfiles + desktopPnpm host services (the plugin market's cross-environment contract) |
electron/package-root.ts → lib/electron/package-root.js | shared package-root resolution for boot + services |
electron/preload.cts → lib/electron/preload.cjs | the preload bridge (__DSH_BOOT__ + dshDesktop), plain CJS by Electron's preload contract |
Config
dsh-plugin-desktop/webserverconfig:{ host: '127.0.0.1', port: 0 }— reported, never bound.- The
desktopsettings namespace ({ closeToTray: boolean }, defaultfalse, applies live) owns the close-window behavior:falsequits on window close,truehides to the tray. The settings row reads and writes it over thedsh:close-behaviorbridge channel (the host ApiProxy's configuration-client allowlist does not serve thedesktopnamespace, so the main process mediates the write against the in-process provider); the Electron main reads the same value at window-close time. - The tray menu labels (
show/restart/quit) come from thesettings.desktoplocale dictionaries: the renderer publishes them over thedsh:localechannel at boot and on every locale change, so the tray matches the language the app displays; English stands until the first publication. The tray's "Restart host" item applies pending plugin changes through the same in-process reboot as the settings action.
Model Experience
No model-facing behavior: the package changes the host transport only (socketless webServer, in-process /api dispatch, and the raw virtual-host http-request bridge that serves the session-log download). Model-visible inputs and outputs are unchanged.
Virtual-host HTTP proxy
The renderer's patched fetch sends every file:// (same-origin) and http://dsh.internal request over the bridge with method, headers, and body. dispatchHttpRequest dispatches through the full virtual webServer route registry (match → exact/prefix, then the fallback seat) with synthesized IncomingMessage/ServerResponse stand-ins and loopback Origin/Host (127.0.0.1) — so ANY plugin route dispatches in-process with no per-plugin bridge, and an exact route under /api/* (e.g. a plugin's endpoints) beats the connection's /api prefix exactly as the official server resolves it. The proven /api fast path (toFetchHandler(apiProxy): unary RPC + the session-log download) serves the plane only when no exact route wins.
Plugin market and desktop host services
The package depends on and mounts dshmarket (dshmarket, insert row - id: dsh-market), the in-app plugin market. bootDesktop registers the market's Desktop host contract before Loader entries mount: desktopProfiles ({ current: { name: 'desktop', dir } }) and desktopPnpm. desktopPnpm.runPlugin re-invokes the published dsh plugin --profile desktop … CLI under Electron's plain-Node mode (ELECTRON_RUN_AS_NODE), so pnpm and dsh.profile.bundles reconciliation happen through the ordinary DSH CLI; desktopPnpm.run runs pnpm directly. pnpm resolution prefers the bundled standalone binary (@pnpm/exe, materialized into the packaged closure for offline installs) over the system pnpm on PATH.
Plugins the market installs live in the profile's node_modules; the boot installs a profile-anchored ctx.loader.internal (electron/loader-internal.ts) so the Cordis loader resolves them at the next boot even under Electron, where the loader's native internal module loader is unavailable. Boot also self-heals a broken install: a bundle whose package or whose patch-referenced package cannot be resolved is dropped from dsh.profile.bundles (with a warning) so one bad plugin never prevents the app from opening.
Host reboot
The settings General-section "Restart host" action (dsh:reboot-host channel) and the tray's "Restart host" item apply pending plugin changes by re-booting the host in-process: dispose the current generation (ctx.fiber.dispose()), bootDesktop again (new bundles compose), re-install the IPC bridge (installIpc returns a disposer), and reload the renderer. The Electron process, window, and tray stay up; the trigger is manual (auto-trigger waits on a durable pending-restart signal).
Agent presets
composeDesktopPatches appends the shipped agent-preset root (@deepseek-ai/dsh's config/agent-presets) to the agent-presets row, the same assembly the official CLI's composeProfile performs. Without it the roster resolves no presets and every session start fails with preset "cordis" not found (available: none).
Known Limitations and Deferred Work
- The tray always exists on every platform (a hidden window must always have a restore path); there is no setting to hide it.
- The shell is otherwise minimal (window + tray + IPC + bundled pnpm); terminal, profile management, updates, and installers are deferred.
- The host reboot is manual (a settings action); automatic reboot waits on a durable pending-restart signal from plugins.
VirtualWebServerreimplements the official registry semantics (~90 lines) to avoid shipping-package changes; an upstreamlisten: falsemode would retire it.