🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

桌面客户端 yuwenbin521wl-cell/dsh-desktop

DeepSeek Harnessv0.2.0
yuwenbin521wl-cell47 天前有提交已提供中文文档扫描:高危

在 Windows 桌面窗口里运行本地 DSH 并自动更新

30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/9/18 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

自动兼容检查未通过:仓库 package.json 标记 private,未发布到 npm,需从源码安装。以下命令仍可尝试,但可能失败。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add yuwenbin521wl-cell/dsh-desktop
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么[目录]—
dsh 原生插件 · ui
暂无时间戳·判据 →
装得上吗[实装验证]✓
本站已在 dsh 0.2.1-rc.5 下真实安装成功
非静态推断
L4 · 真实安装等级说明 →
数据截至 2026/11/04·判据 →
安全吗[自动扫描]—
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
数据截至 2026/11/04·判据 →
还在维护吗[仓库]—
更新放缓:最近一次提交在 47 天前
暂无时间戳·判据 →
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →

可信度证据

安装兼容性检查需源码安装

仓库 package.json 标记 private,未发布到 npm,需从源码安装

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包dsh-desktop @ 0.2.0
✓Node 引擎要求 >=22.19.0 · 基线 Node 22.19 满足
✓dsh CLI 依赖要求 ^0.1.0-rc.7 · 最新 0.2.1-rc.2 兼容
✓入口文件main/exports/bin 已声明

源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/20 04:30:24

🔴 安全扫描发现高危能力自动 · 静态分析源码
v2.7.0
🔴 扫描:高危尚无人工评估

静态扫描发现安装期即执行代码,或命令参数由运行时变量拼接(参数来源无法静态判定,从严处理)。

安装时即执行脚本可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 56 处,展示前 50 条)
  • 写入本地文件
    src/diag.ts:20
    if (logFile !== null) appendFileSync(logFile, `${new Date().toISOString()} ${message}\n`)
  • 读取本地文件
    src/i18n.ts:237
    const yaml = readFileSync(join(home, 'settings.yaml'), 'utf8')
  • 写入本地文件
    src/main.ts:508
    mkdirSync(isolatedHome, { recursive: true })
  • 读取本地文件
    src/plugin-health.ts:94
    if (!existsSync(manifestPath)) return []
  • 读取本地文件
    src/plugin-health.ts:97
    manifest = JSON.parse(readFileSync(manifestPath, 'utf8'))
  • 读取本地文件
    src/plugin-health.ts:109
    if (!existsSync(pkgPath)) {
  • 读取本地文件
    src/plugin-health.ts:115
    pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
  • 读取本地文件
    src/plugin-health.ts:136
    if (!existsSync(manifestPath)) return []
  • 读取本地文件
    src/plugin-health.ts:138
    const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as {
  • 写入本地文件
    src/plugin-health.ts:173
    try { copyFileSync(manifestPath, backupPath) } catch { }
  • 写入本地文件
    src/plugin-health.ts:185
    try { writeFileSync(manifestPath, `${JSON.stringify(nextManifest, null, 2)}\n`, 'utf8') } catch { return false }
  • 写入本地文件
    src/runtime-updater.ts:72
    writeFileSync(fd, String(process.pid))
  • 删除本地文件
    src/runtime-updater.ts:83
    rmSync(join(app.getPath('userData'), 'runtime-update.lock'), { force: true })
  • 发起网络请求
    src/runtime-updater.ts:129
    return await fetch(url, { ...init, signal: AbortSignal.timeout(CHECK_TIMEOUT_MS) })
  • 写入本地文件
    src/runtime-updater.ts:205
    mkdirSync(zipDir, { recursive: true })
  • 发起网络请求
    src/runtime-updater.ts:213
    const sumRes = await fetch(`${info.url}.sha256`, { signal: AbortSignal.timeout(15000) })
  • 读取本地文件
    src/runtime-updater.ts:222
    if (existsSync(zipPath)) start = statSync(zipPath).size
  • 读取本地文件
    src/runtime-updater.ts:222
    if (existsSync(zipPath)) start = statSync(zipPath).size
  • 发起网络请求
    src/runtime-updater.ts:233
    res = await fetch(info.url, { signal, headers })
  • 删除本地文件
    src/runtime-updater.ts:245
    rmSync(zipPath, { force: true })
  • 读取本地文件
    src/runtime-updater.ts:259
    const rs = createReadStream(zipPath)
  • 写入本地文件
    src/runtime-updater.ts:265
    const file = createWriteStream(zipPath, { flags: start > 0 ? 'a' : 'w' })
  • 删除本地文件
    src/runtime-updater.ts:317
    rmSync(zipPath, { force: true })
  • 写入本地文件
    src/runtime-updater.ts:335
    mkdirSync(tmp, { recursive: true })
  • 执行外部命令固定命令
    src/runtime-updater.ts:338
    execFile('tar', ['-xf', zipPath, '-C', tmp], (error) => {
  • 读取本地文件
    src/runtime-updater.ts:343
    if (!existsSync(join(tmp, 'node', 'node.exe'))
  • 读取本地文件
    src/runtime-updater.ts:344
    || !existsSync(join(tmp, 'dsh-deploy', 'node_modules', '@deepseek-ai', 'dsh', 'lib', 'bin.js'))) {
  • 写入本地文件
    src/runtime-updater.ts:349
    renameSync(tmp, target)
  • 读取本地文件
    src/runtime-updater.ts:577
    for (const entry of readdirSync(userData)) {
  • 读取本地文件
    src/runtime.ts:32
    const parsed = JSON.parse(readFileSync(runtimeStateFile(), 'utf8')) as Partial<RuntimeState>
  • 读取本地文件
    src/runtime.ts:34
    && existsSync(join(parsed.dir, 'node', 'node.exe'))
  • 读取本地文件
    src/runtime.ts:35
    && existsSync(join(parsed.dir, 'dsh-deploy', 'node_modules', '@deepseek-ai', 'dsh', 'lib', 'bin.js'))) {
  • 写入本地文件
    src/runtime.ts:46
    mkdirSync(dirname(runtimeStateFile()), { recursive: true })
  • 写入本地文件
    src/runtime.ts:47
    writeFileSync(runtimeStateFile(), JSON.stringify(state, null, 2))
  • 删除本地文件
    src/runtime.ts:53
    rmSync(runtimeStateFile(), { force: true })
  • 读取本地文件
    src/runtime.ts:87
    if (!existsSync(bundled)) {
  • 读取本地文件
    src/runtime.ts:106
    if (!existsSync(bundled)) {
  • 读取本地文件
    src/runtime.ts:112
    if (existsSync(dev)) return dev
  • 删除本地文件递归
    src/runtime.ts:147
    rmSync(dir, { recursive: true, force: true })
  • 执行外部命令固定命令
    src/server.ts:90
    execFile('taskkill', ['/PID', String(child.pid), '/T', '/F'], () => finish())
  • 执行外部命令来自配置/变量
    src/server.ts:114
    const child = spawn(nodeExe, [dshBin, 'web', '--port', '0', '--no-open'], {
  • 写入本地文件
    src/server.ts:136
    try { appendFileSync(join(app.getPath('userData'), 'dsh-server.log'), chunk) } catch { }
  • 写入本地文件
    src/server.ts:155
    try { appendFileSync(join(app.getPath('userData'), 'dsh-server.log'), chunk) } catch { }
  • 执行外部命令固定命令
    src/shortcut.ts:32
    execFile('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script], { windowsHide: true }, (err, stdout) => {
  • 执行外部命令固定命令
    src/shortcut.ts:50
    execFile('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script], { windowsHide: true }, (err, stdout) => {
  • 读取本地文件
    src/shortcut.ts:68
    if (!existsSync(exe)) return
  • 读取本地文件
    src/shortcut.ts:69
    if (!existsSync(lnkPath)) return
  • 读取本地文件
    src/updater.ts:130
    } else if (!app.isPackaged && existsSync(join(app.getAppPath(), 'dev-app-update.yml'))) {
  • 读取本地文件
    src/window-size.ts:65
    const raw = readFileSync(windowSizeFile(), 'utf8')
  • 写入本地文件
    src/window-size.ts:76
    writeFileSync(windowSizeFile(), JSON.stringify({ width: size.width, height: size.height }, null, 2), 'utf8')
扫描于 2026/11/4(1 天前) · 范围 入口闭包 + 全包扫描 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

社区实装还没有人打卡 · 装过的第一个就是你
依赖的 DSH / Cordis 模块
@deepseek-ai/dsh
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 运行本地DSH
  • 原生窗口与系统托盘
  • 双通道自动更新

README

由 DeepSeek 最新模型翻译生成
DeepSeek Harness 桌面版(Windows 客户端) Topics / 关键词:dsh, deepseek-harness, desktop, electron, windows, ai, agent, harness, automation, deepseek DSH(DeepSeek Harness)Windows 桌面客户端:Electron 壳 + 本地 dsh web + 系统托盘 + 双通道自动更新。 把 DeepSeek Harness 装进桌面的 Electron 客户端:内置拉起本地 dsh web 服务器,在桌面窗口里嵌入 Web UI(不再依赖浏览器标签页),并支持自我检查更新——发现新版本时弹出对话框询问“是否更新”,确认后才下载,下载完再询问是否重启安装。 该目录刻意不加入 pnpm workspace:它使用独立的依赖树(npm install),安装或升级桌面客户端都不会影响你正在使用的 harness 仓库;它只在构建时通过 pnpm deploy 消费仓库产物。
展开完整文档(README 剩余 1 节)
DeepSeek Harness 桌面版(Windows 客户端)

Topics / 关键词:dsh, deepseek-harness, desktop, electron, windows, ai, agent, harness, automation, deepseek

DSH(DeepSeek Harness)Windows 桌面客户端:Electron 壳 + 本地 dsh web + 系统托盘 + 双通道自动更新。

把 DeepSeek Harness 装进桌面的 Electron 客户端:内置拉起本地 dsh web 服务器,在桌面窗口里嵌入 Web UI(不再依赖浏览器标签页),并支持自我检查更新——发现新版本时弹出对话框询问“是否更新”,确认后才下载,下载完再询问是否重启安装。

该目录刻意不加入 pnpm workspace:它使用独立的依赖树(npm install),安装或升级桌面客户端都不会影响你正在使用的 harness 仓库;它只在构建时通过 pnpm deploy 消费仓库产物。

数据目录(DSH_HOME)独立:桌面版始终使用自己的 DSH_HOME——默认 /home(即 %APPDATA%\dsh-desktop\home),不继承外部 DSH_HOME 环境变量(它通常指向 web 版的 ~/.dsh;两个 dsh 实例并发写同一会话文件会导致日志损坏 corrupt session log: seq gap)。历史会话、模型配置、API key 互不相通。如需覆盖,设置桌面版专属的 DSH_DESKTOP_HOME 环境变量(例如刻意指向 web 版目录以共享);想沿用 web 版配置时,可手动把 ~/.dsh 下的 credentials 等文件复制到桌面版数据目录。

环境要求

- Windows 10/11 x64。
- 仓库根目录可用的 Node.js >= 22.19 与 pnpm >= 9(harness 本身也需要)。
- 网络:npm、nodejs.org(下载内置独立 Node 运行时);更新源默认 GitHub Releases,可覆盖(见下文)。

快速开始(开发模式)

cd desktop
npm install # 安装 electron + electron-builder + electron-updater
npm run dev # 编译外壳,并用仓库已构建的 dsh(apps/cli/lib/bin.js)运行

npm run dev 会拉起仓库自身的 dsh web(请先至少执行过一次仓库根的 pnpm run build),并在桌面窗口打开 UI;端口由操作系统分配(--port 0),不会和你正在跑的 3080 webui 冲突。

识图 / 图片识别配置

桌面客户端内置了 dsh-vision-router 插件(首次运行会自动写入桌面版 profile),因此含图片的对话默认即可用。默认会回退到免 Key 的 OVH 匿名视觉端点(有限流);想要更稳定、更高吞吐的识别,请配置你自己的视觉后端 Key。

插件从以下任一处读取视觉 API Key:

1. DSH 凭据文件(推荐)——DSH 本就读取的同一个 .credentials.yaml:
- Web 版:~/.dsh/.credentials.yaml
- 桌面版:%APPDATA%\dsh-desktop\home\.credentials.yaml(隔离的 DSH_HOME)
2. 环境变量——名称对应该 httpProviders 条目里的 apiKeyEnv 字段。

示例:启用内置的智谱(glm-4v-flash)与 DashScope(qwen-vl)后端。在凭据文件里加入:

ZHIPU_API_KEY: 你的智谱key DASHSCOPE_API_KEY: 你的千问key

或者等价地,把它们设成用户环境变量:

setx ZHIPU_API_KEY "你的智谱key" setx DASHSCOPE_API_KEY "你的千问key"

重要:通过 setx 或控制面板设置的环境变量,只有新启动的进程才会读取。已经运行的桌面客户端(以及它拉起的 dsh web)会保留启动那一刻的环境快照——设置变量后必须完全退出并重新打开客户端。而凭据文件没有这个限制:DSH 会在运行时重新读取,因此即时生效、重启不丢。由于桌面版使用隔离的 DSH_HOME,它的 Key 必须放在桌面版的凭据文件(或桌面版自身的环境)里,而不是 web 版的。

免费视觉 Key 渠道与完整配置项见插件 README。

构建安装包

cd desktop
npm run dist # tsc 编译 + 准备运行时 + electron-builder 打 NSIS 包(不上传)

npm run dist 分三步:

1. npm run build 把 Electron 主进程编译到 dist/。
2. npm run prepare:runtime 下载独立 node.exe(默认 v22.19.0,可用 DSH_DESKTOP_NODE_VERSION 覆盖),用 pnpm pack 把 @deepseek-ai/dsh 在本仓库内的整个 @deepseek-ai/ workspace 依赖闭包打成 tarball,再把这些 tarball 与全部 registry 依赖安装进 .runtime/(隔离 pnpm store + copy 导入,产物完全自包含、不会与你正在运行的 webui 争用共享 store 硬链接),并用隔离的 DSH_HOME 冒烟测试打包出的服务器能打印就绪 URL。
3. electron-builder 产出 release/DeepSeek Harness Setup-.exe(NSIS,按用户安装、可选安装目录)与 release/latest.yml(更新清单)。

安装包完全自包含:独立 Node 运行时与整套 dsh 运行时都放在 resources/dsh-runtime/(asar 之外,因为独立 node.exe 读不了 asar),一次安装、一次更新同时覆盖外壳与 harness 本体。

自更新的工作方式(两条独立通道)

更新拆成两条完全独立的通道——桌面版(壳) 和 dsh 引擎(运行时) 各自版本、各自更新,互不干扰:

1. 桌面版更新(菜单“帮助 → 检查更新…”):向 electron-updater 查询新版安装包。发现新版本弹“发现新版本 vX,是否立即下载并更新?”,确认后下载,下载完再询问是否重启安装。更新源来自 electron-builder.yml 的 publish 段,打包时写入安装包的 app-update.yml(GitHub Releases provider,owner deepseek-ai、repo deepseek-harness)。
2. dsh 引擎更新(菜单“帮助 → 检查引擎更新…”):向引擎源查询新版 dsh-runtime-vX.zip(完整的 node.exe + dsh 依赖树)。发现新版弹“检测到新的 dsh 引擎 vX(当前 vY)”,确认后下载、sha256 校验、停掉本地服务器、把运行时换入 /runtime-vX、询问是否重启。重启(或选“稍后”,会直接以新引擎拉起服务器)后新引擎生效。桌面版更新从不触碰 ,两条通道不会互相覆盖。菜单“帮助 → 恢复内置引擎…”可随时回退到随壳发布的引擎版本(与壳最适配的版本)。

引擎通道(“检查引擎更新…”)每次启动和每 6 小时查询官方 npm @deepseek-ai/dsh 的最新版本——官方发布节奏,新版本一落地就能感知;然后按固定模式从 GitHub Releases 下载对应版本的运行时 zip:https://github.com//releases/download/dsh-engine-v/dsh-runtime-v.zip(可选附带 .sha256 校验文件)。用 DSH_DESKTOP_RUNTIME_REPO=/ 环境变量(或改 src/runtime-updater.ts 里的 RUNTIME_RELEASE_REPO 常量后重新打包)指向你的发布仓库;DSH_DESKTOP_RUNTIME_URL(自建源:latest.json + zip)优先级最高。

运行时 zip 由 engine-sync 自动构建 Action 生成(见 desktop/ci/engine-sync.yml):把它复制到你的发布仓库 .github/workflows/ 下,它会定时(每 6 小时)+ 手动触发:拉官方 harness 最新代码 → 构建完整运行时 → 发布 dsh-runtime-vX.zip + .sha256 到 dsh-engine-vX Release。之后桌面版全自动更新引擎——你不需要手动构建或同步。

壳更新源可在运行时覆盖以适配其它网络或联调:DSH_DESKTOP_UPDATE_URL=http://host/feed 切换到 generic 源(自建服务器,或国内常用的 ghproxy.com 等 GitHub 加速代理),该源需提供 latest.yml 与安装包;开发模式(未打包)下,把 dev-app-update.yml.example 复制为 dev-app-update.yml,npm run dev 即会启用壳更新检查(forceDevUpdateConfig),另开终端跑 node scripts/dev-update-feed.mjs 模拟“发现新版本”。引擎通道联调用 node scripts/dev-runtime-feed.mjs 起本地引擎源,再以 DSH_DESKTOP_RUNTIME_URL 启动应用。

发布新版本

给仓库打 dsh-desktop-v 标签(如 dsh-desktop-v0.1.0)并推送;.github/workflows/desktop-release.yml 会在 Windows runner 上构建并发布安装包与 latest.yml(壳通道)以及 dsh-runtime-vX.zip(引擎通道)到对应 tag 的 GitHub Release(草稿)。electron-updater 按应用 version 字段比较版本,因此发版时要把 desktop/package.json 的版本与 harness 一起递增;引擎 zip 的版本取内置 @deepseek-ai/dsh 的版本。预发布版(rc 等)默认被 electron-updater 跳过:测试时把 Release 标记为 latest,或用环境变量把两个通道分别指向 generic 源。

签名与 SmartScreen

未签名的 NSIS 安装包会触发 Windows SmartScreen 警告。需要签名时,在 npm run dist 前设置 CSC_LINK(.pfx 路径或 base64: 前缀)与 CSC_KEY_PASSWORD,并在 GitHub Action 中配置相同 secrets。electron-updater 安装 NSIS 更新本身不要求签名,但首次启动没有签名会有 SmartScreen 提示。

网络说明

desktop/.npmrc 把 Electron 与 electron-builder 的二进制下载指向 npmmirror(大陆网络通常无法直连 GitHub);能直连 GitHub 的环境可删除该文件后重装依赖。内置 Node 运行时从 nodejs.org 下载。运行时的更新检查需要能访问你的更新源——上面的环境变量覆盖就是大陆网络的出口。

开发中修复过的已知问题

- NSIS 安装后运行时文件缺失(启动报"打包版 node.exe 缺失"):pnpm 的隔离布局(node_modules/.pnpm/@pkg@hash/node_modules/...)会产生约 2.2 万条超过 Windows 260 字符路径限制的文件,NSIS 安装时写文件失败被静默跳过,导致装出来的应用缺运行时。已修复:运行时改用 npm 安装(扁平 node_modules,最长路径 ≤ 260),安装包也从约 682MB 降到约 185MB。
- 关闭窗口时应用崩溃(托盘创建崩溃):electron-builder 会自动排除 build/ 目录(它是 buildResources),build/icon.png 从未被打进应用包;关闭窗口懒创建托盘时 new Tray() 因图标缺失抛异常。已修复:图标改走 extraResources(resources/icon.png),并给托盘创建加 try/catch 容错。
- 更新安装后新版不自动启动:autoUpdater.quitAndInstall(true, false) 不会传 --force-run,NSIS 安装器装完不会启动应用。已修复:改传 quitAndInstall(true, true)。
- 退出流程不能强杀进程:before-quit 里用 app.exit(0) 可能打断 electron-updater 的退出/安装序列;应使用 app.quit() 并让第二次 before-quit 自然放行。
- 对已安装版本静默重装是空操作:NSIS 检测到已安装会静默跳过;测试重装前先卸载(或删除 HKCU\Software\ 与 HKCU\...\Uninstall\ 注册表项)。
- 更新模式跳过未变化文件:更新安装只覆盖变化的文件、跳过内容相同的(例如 DeepSeek Harness.exe 时间戳可能不变)——这是预期行为,真正变化的是 app.asar 内的应用代码。

目录结构

- src/main.ts — 应用生命周期、单实例锁、窗口、菜单、托盘、退出时回收服务器进程树。
- src/preload.ts — 壳渲染进程 preload:暴露窗口尺寸 IPC,并在页面右上角注入「尺寸」控件(预设 / 自定义 / 最大化)。
- src/window-size.ts — 窗口尺寸的默认值、预设、持久化(/window-size.json)与工作区钳制。
- src/server.ts — spawn dsh web --port 0,解析打印的 URL,TCP 探活,退出时 taskkill 兜底。
- src/runtime.ts — 打包版 / 开发版的 node 与 dsh bin 路径解析,以及 userData 覆盖的运行时根目录。
- src/updater.ts — electron-updater 接线与中文更新提示(壳通道)。
- src/runtime-updater.ts — dsh 引擎更新通道:检查源、下载、sha256 校验、换入 userData、重启提示。
- src/diag.ts — 主进程诊断日志(/main-debug.log),用于排查问题。
- scripts/prepare-runtime.ts — 生成 .runtime/(node + 部署后的 dsh 树)与图标。
- scripts/build-runtime-zip.ts — 把 .runtime/ 打成可发布的 dsh-runtime-vX.zip。
- scripts/make-icon.ts — 零依赖 PNG 图标生成器。
- scripts/dev-update-feed.mjs — 本地模拟壳更新源,演示“发现新版本”提示。
- scripts/dev-runtime-feed.mjs — 本地模拟引擎更新源(latest.json + 运行时 zip),演示引擎更新。
预览分享卡片长什么样 ↓yuwenbin521wl-cell/dsh-desktop
分享 · DeepSeek Harness Hub
yuwenbin521wl-cell/dsh-desktop

在 Windows 桌面窗口里运行本地 DSH 并自动更新

这个插件是你维护的吗?
作者本人可认领并核对本条目;认领通过后,详情页会显示「作者已认领」标记。
认领条目 →
回到顶部