DeepSeek Harness 的非官方开源桌面应用,目标是提供更简单、更顺手的桌面体验。
37/100
综合分
11
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/9/18 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
自动兼容检查未通过:要求 dsh 0.1.0-rc.6,而最新版为 0.2.1-rc.5;仓库 package.json 标记 private,未发布到 npm,需从源码安装。以下命令仍可尝试,但可能失败。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add yxccai/dsh-desktop该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:需留意
是什么
dsh 原生插件 · desktop
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 73 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装要求 dsh 0.1.0-rc.6,而最新版为 0.2.1-rc.5;仓库 package.json 标记 private,未发布到 npm,需从源码安装
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✓npm 包dsh-desktop-shell @ 1.1.3
✓Node 引擎要求 >=20 · 基线 Node 22.19 满足
✗dsh CLI 依赖要求 0.1.0-rc.6 · 最新 0.2.1-rc.5 不兼容
✓入口文件main/exports/bin 已声明
源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/11/2 20:48:56
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 183 处,展示前 118 条)
- 写入本地文件src/main.js:73
fs.mkdirSync(path.dirname(file), { recursive: true }); - 写入本地文件src/main.js:75
fs.writeFileSync(temp, JSON.stringify(value, null, 2), 'utf8'); - 写入本地文件src/main.js:76
fs.renameSync(temp, file); - 写入本地文件src/main.js:82
fs.mkdirSync(app.getPath('userData'), { recursive: true }); - 写入本地文件src/main.js:83
fs.appendFileSync(userFile('dsh-desktop.log'), line); - 读取本地文件src/main.js:200
try { const meta = JSON.parse(fs.readFileSync(file, 'utf8')); const image = userFile(path.join('themes', meta.file)); if (!fs.existsSync(image)) retur - 读取本地文件src/main.js:205
const source = result.filePaths[0]; const stat = fs.statSync(source); if (!stat.isFile() || stat.size > 20 * 1024 * 1024) throw new Error('背景图片必须小于 20 - 写入本地文件src/main.js:207
const themes = userFile('themes'); fs.mkdirSync(themes, { recursive: true }); const name = `custom-background${ext}`; const target = path.join(themes, - 读取本地文件src/main.js:208
return { name: path.basename(source), dataUrl: `data:${mime};base64,${fs.readFileSync(target).toString('base64')}`, opacity: 100 }; - 读取本地文件src/main.js:214
const meta = JSON.parse(fs.readFileSync(file, 'utf8')); - 读取本地文件src/main.js:218
if (!fs.existsSync(image)) return null; - 读取本地文件src/main.js:219
return { name: meta.name, dataUrl: `data:${meta.mime};base64,${fs.readFileSync(image).toString('base64')}`, opacity }; - 执行外部命令来自配置/变量src/runtime-manager.js:88
return spawn(comspec, ['/d', '/s', '/c', line], options); - 执行外部命令来自配置/变量src/runtime-manager.js:90
return spawn(spec.command, spec.args, options); - 执行外部命令固定命令src/runtime-manager.js:334
const killer = spawn('taskkill.exe', args, { windowsHide: true, stdio: 'ignore' }); - 读取本地文件src/config.js:43
const value = JSON.parse(fs.readFileSync(file, 'utf8')); - 写入本地文件src/config.js:47
fs.mkdirSync(path.dirname(file), { recursive: true }); - 写入本地文件src/config.js:48
fs.writeFileSync(file, `${JSON.stringify(defaults, null, 2)}\n`); - 读取本地文件src/environment-detector.js:8
try { return fs.statSync(file).isFile(); } catch { return false; } - 执行外部命令来自配置/变量src/environment-detector.js:30
const child = spawn(spec.command, spec.args, { windowsHide: true, stdio: 'ignore' }); - 读取本地文件src/pnpm-runtime.js:32
try { return fs.statSync(file).isFile(); } catch { return false; } - 读取本地文件src/pnpm-runtime.js:71
if (fs.readFileSync(file, 'utf8') === content) return false; - 写入本地文件src/pnpm-runtime.js:74
fs.writeFileSync(temp, content, { encoding: 'utf8', mode }); - 写入本地文件src/pnpm-runtime.js:75
fs.renameSync(temp, file); - 写入本地文件src/pnpm-runtime.js:95
fs.mkdirSync(dir, { recursive: true }); - 写入本地文件src/pnpm-runtime.js:100
try { fs.chmodSync(path.join(dir, 'pnpm'), 0o755); } catch { } - 读取本地文件src/plugin-manager.js:18
const raw = fs.readFileSync(target); - 读取本地文件src/plugin-manager.js:21
return fs.readFileSync(target); - 读取本地文件src/plugin-manager.js:25
try { return JSON.parse(fs.readFileSync(file, 'utf8')); } - 读取本地文件src/plugin-manager.js:30
try { return fs.statSync(target).isDirectory(); } - 读取本地文件src/plugin-manager.js:35
try { return fs.readdirSync(root, { withFileTypes: true }); } - 读取本地文件src/plugin-manager.js:80
const stat = fs.lstatSync(target); - 写入本地文件src/plugin-manager.js:166
fs.mkdirSync(this.enabledRoot, { recursive: true }); - 写入本地文件src/plugin-manager.js:170
fs.writeFileSync(path.join(temp, MARKER_FILE), JSON.stringify({ owner: 'dsh-desktop', id, version: plugin.version, digest: plugin.digest, installedAt: - 写入本地文件src/plugin-manager.js:171
fs.renameSync(temp, enabled); - 删除本地文件src/plugin-manager.js:173
fs.rmSync(temp, { recursive: true, force: true }); - 写入本地文件src/plugin-manager.js:189
fs.mkdirSync(toRoot, { recursive: true }); - 写入本地文件src/plugin-manager.js:190
fs.renameSync(current.dir, target); - 写入本地文件src/plugin-manager.js:202
fs.renameSync(current.dir, trash); - 删除本地文件src/plugin-manager.js:203
fs.rmSync(trash, { recursive: true, force: true }); - 读取本地文件src/web-market-manager.js:59
try { return JSON.parse(fs.readFileSync(file, 'utf8')); } - 读取本地文件src/web-market-manager.js:64
try { return fs.statSync(target).isDirectory(); } - 读取本地文件src/web-market-manager.js:69
try { return fs.readdirSync(root, { withFileTypes: true }); } - 读取本地文件src/web-market-manager.js:90
const stat = fs.lstatSync(target); - 读取本地文件src/web-market-manager.js:105
const data = fs.readFileSync(file.target); - 读取本地文件src/web-market-manager.js:148
valid: manifest?.name === PACKAGE_NAME && fs.existsSync(path.join(dir, 'lib', 'host.js')) && fs.existsSync(path.join(dir, 'lib', 'client.js')), - 写入本地文件src/web-market-manager.js:206
fs.mkdirSync(this.dshHome, { recursive: true }); - 写入本地文件src/web-market-manager.js:208
fs.writeFileSync(temp, next, { encoding: 'utf8', mode: 0o600 }); - 写入本地文件src/web-market-manager.js:209
fs.renameSync(temp, this.patchPath); - 写入本地文件src/web-market-manager.js:226
fs.mkdirSync(path.dirname(this.packageDir), { recursive: true }); - 写入本地文件src/web-market-manager.js:230
fs.writeFileSync(path.join(temp, MARKER_FILE), JSON.stringify({ owner: 'dsh-desktop', package: PACKAGE_NAME, version: manifest.version, digest, instal - 写入本地文件src/web-market-manager.js:231
fs.renameSync(temp, this.packageDir); - 删除本地文件src/web-market-manager.js:233
fs.rmSync(temp, { recursive: true, force: true }); - 删除本地文件src/web-market-manager.js:240
try { fs.rmSync(this.packageDir, { recursive: true, force: true }); } catch {} - 删除本地文件src/web-market-manager.js:277
try { fs.rmSync(trash, { recursive: true, force: true }); } catch {} - 读取本地文件src/dsh-integration.js:57
if (!fs.existsSync(file)) continue; - 读取本地文件src/dsh-integration.js:59
hash.update(fs.readFileSync(file)); - 读取本地文件src/dsh-integration.js:68
const sourceManifest = JSON.parse(fs.readFileSync(path.join(source, 'package.json'), 'utf8')); - 读取本地文件src/dsh-integration.js:70
const currentManifest = fs.existsSync(path.join(target, 'package.json')) - 读取本地文件src/dsh-integration.js:71
? JSON.parse(fs.readFileSync(path.join(target, 'package.json'), 'utf8')) - 读取本地文件src/dsh-integration.js:74
if (fs.existsSync(target) && currentManifest?.name !== sourceManifest.name) throw new Error('DSH_HOME 中存在冲突的插件中心包'); - 写入本地文件src/dsh-integration.js:75
fs.mkdirSync(path.dirname(target), { recursive: true }); - 写入本地文件src/dsh-integration.js:81
fs.renameSync(temp, target); - 删除本地文件src/dsh-integration.js:82
fs.rmSync(backup, { recursive: true, force: true }); - 删除本地文件src/dsh-integration.js:84
fs.rmSync(temp, { recursive: true, force: true }); - 写入本地文件src/dsh-integration.js:94
fs.mkdirSync(home, { recursive: true }); - 写入本地文件src/dsh-integration.js:100
fs.writeFileSync(temp, next, { encoding: 'utf8', mode: 0o600 }); - 写入本地文件src/dsh-integration.js:101
fs.renameSync(temp, patchPath); - 写入本地文件src/project-panel-manager.js:18
fs.mkdirSync(path.dirname(file), { recursive: true }); - 写入本地文件src/project-panel-manager.js:20
fs.writeFileSync(temp, JSON.stringify(value, null, 2), 'utf8'); - 写入本地文件src/project-panel-manager.js:21
fs.renameSync(temp, file); - 读取本地文件src/project-panel-manager.js:41
const data = JSON.parse(fs.readFileSync(registry, 'utf8')); - 读取本地文件src/project-panel-manager.js:53
if (!fs.statSync(root).isDirectory()) throw new Error('工作区不是目录'); - 读取本地文件src/project-panel-manager.js:64
if (fs.existsSync(target)) { - 读取本地文件src/project-panel-manager.js:85
if (fs.existsSync(path.join(cursor, '.git'))) { - 读取本地文件src/project-panel-manager.js:98
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) { - 读取本地文件src/project-panel-manager.js:103
const stat = fs.statSync(target); - 写入本地文件src/project-panel-manager.js:193
fs.writeFileSync(temp, content, { encoding: 'utf8', mode: stat.mode }); - 写入本地文件src/project-panel-manager.js:194
fs.renameSync(temp, target); - 删除本地文件src/project-panel-manager.js:265
if (change.untracked) fs.rmSync(target, { recursive: true, force: false }); - 写入本地文件src/project-panel-manager.js:289
fs.mkdirSync(path.dirname(tempIndex), { recursive: true }); - 删除本地文件src/project-panel-manager.js:306
} finally { fs.rmSync(tempIndex, { force: true }); } - 删除本地文件src/project-panel-manager.js:342
finally { fs.rmSync(tempRoot, { recursive: true, force: true }); } - 删除本地文件src/project-panel-manager.js:364
if (!entry) fs.rmSync(target, { recursive: true, force: true }); - 删除本地文件src/project-panel-manager.js:378
try { await this.git(root, ['cat-file','-e',`${current.tree}:${clean}`]); fs.rmSync(target, { recursive: true, force: true }); } - 删除本地文件src/project-panel-manager.js:386
for (const relative of Object.keys(current)) if (!target.files?.[relative]) fs.rmSync(this.resolvePath(root, relative).target, { force: true }); - 写入本地文件src/ownership-marker.js:22
fs.mkdirSync(path.dirname(file), { recursive: true }); - 写入本地文件src/ownership-marker.js:24
fs.writeFileSync(temp, `${JSON.stringify(value, null, 2)}\n`, 'utf8'); - 写入本地文件src/ownership-marker.js:25
fs.renameSync(temp, file); - 读取本地文件src/ownership-marker.js:38
const value = JSON.parse(fs.readFileSync(file, 'utf8')); - 删除本地文件src/ownership-marker.js:51
fs.rmSync(file, { force: true }); - 读取本地文件src/ownership-marker.js:55
for (const name of fs.readdirSync(dir)) { - 删除本地文件src/ownership-marker.js:56
if (name.startsWith(`${base}.tmp-`)) fs.rmSync(path.join(dir, name), { force: true }); - 执行外部命令固定命令src/ownership-marker.js:93
const out = execFileSync('tasklist.exe', ['/FI', `PID eq ${pid}`, '/FO', 'CSV', '/NH'], { - 执行外部命令固定命令src/ownership-marker.js:100
const out = execFileSync('powershell.exe', - 执行外部命令固定命令src/ownership-marker.js:108
const out = execFileSync('ps', ['-p', String(pid), '-o', 'comm='], { encoding: 'utf8', timeout: 3000 }); - 执行外部命令固定命令src/ownership-marker.js:114
const out = execFileSync('ps', ['-p', String(pid), '-o', 'args='], { encoding: 'utf8', timeout: 3000 }); - 发起网络请求resources/market-plugin/lib/client.js:32
return fetch('/api/dsh-market', { - 读取本地文件resources/market-plugin/lib/host.js:84
if (existsSync(probe)) shim = candidate - 读取本地文件resources/market-plugin/lib/host.js:106
const yaml = readFileSync(join(profileDir(profile), 'node_modules', '.modules.yaml'), 'utf8') - 读取本地文件resources/market-plugin/lib/host.js:193
try { if (existsSync(cand)) return cand } catch {} - 读取本地文件resources/market-plugin/lib/host.js:227
if (existsSync(cand)) return { file: process.execPath, args: [cand], cwd: undefined } - 执行外部命令固定命令resources/market-plugin/lib/host.js:362
spawn('taskkill', ['/pid', String(child.pid), '/T', '/F'], { stdio: 'ignore' }) - 读取本地文件resources/market-plugin/lib/host.js:406
if (!existsSync(yamlPath)) return [] - 读取本地文件resources/market-plugin/lib/host.js:408
const yaml = readFileSync(yamlPath, 'utf8') - 写入本地文件resources/market-plugin/lib/host.js:441
writeFileSync(yamlPath, next) - 执行外部命令来自配置/变量resources/market-plugin/lib/host.js:614
const child = spawn(op.inv.file, [...op.inv.args, 'plugin', '--profile', op.profile, op.kind === 'uninstall' ? 'remove' : 'add', op.target], { - 发起网络请求resources/market-plugin/lib/host.js:761
const r = await fetch(`${base}/${owner}/${repo}/HEAD/package.json`, { - 写入本地文件resources/market-plugin/lib/host.js:800
mkdirSync(profileDir, { recursive: true }) - 写入本地文件resources/market-plugin/lib/host.js:801
writeFileSync(join(profileDir, 'package.json'), JSON.stringify({ - 写入本地文件resources/market-plugin/lib/host.js:807
writeFileSync(join(profileDir, 'cordis.patch.yml'), '[]\n') - 写入本地文件resources/market-plugin/lib/host.js:808
writeFileSync(join(profileDir, 'pnpm-workspace.yaml'), 'packages:\n - .\n\nnodeLinker: hoisted\nautoInstallPeers: false\n') - 删除本地文件递归resources/market-plugin/lib/host.js:831
try { rmSync(home, { recursive: true, force: true, maxRetries: 3 }) } catch {} - 执行外部命令来自配置/变量resources/market-plugin/lib/host.js:838
const child = spawn(exe, args, { cwd, env, shell: shell === true, stdio: ['ignore', 'pipe', 'pipe'] }) - 写入本地文件resources/market-plugin/lib/host.js:885
writeFileSync(snap, readFileSync(p, 'utf8')) - 发起网络请求resources/market-plugin/lib/host.js:1011
const r = await fetch(REGISTRY_URL, { redirect: 'follow', signal: AbortSignal.timeout(CATALOG_FETCH_TIMEOUT_MS) }) - 删除本地文件递归resources/market-plugin/lib/host.js:1080
try { rmSync(profileDir(profile) + '/.dsh-market', { force: true, recursive: true, maxRetries: 3 }) } catch {} - 发起网络请求resources/market-plugin/lib/host.js:1478
const res = await fetch(url, {
扫描于 2026/11/2(1 天前) · 范围 入口闭包 + 全包扫描 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis-plugin-group@deepseek-ai/dsh@deepseek-ai/dsh-invariants给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
界面预览
6 张 · 点击查看大图功能亮点
- 非官方桌面应用
- 内置运行时
非官方 DeepSeek Harness 桌面应用,支持 Windows 和 macOS,内置运行时,自动复用已有 DSH 环境。
README
由 DeepSeek 最新模型翻译生成DSH Desktop
DeepSeek Harness 的非官方开源桌面应用,目标是提供更简单、更顺手的桌面体验。
亮点
官方 DSH Web 外面套一层桌面外壳 —— 功能照单全收,安全,还容易跟上新版本
DSH Desktop 不重新实现任何功能,只是把官方 DSH Web 装进原生桌面窗口:
- 一键启动:不用开浏览器标签页,不用记 URL,不用敲命令行 —— 双击桌面图标,进入的就是官方 Web 那套运行时环境。
- 功能继承:官方 DSH Web 作为宿主运行时,加载它完整的 UI 和插件系统,会话、工作区数据、插件生态全部保留;内置插件里的 Web 插件市场组件,一处就能同时管理桌面版和 Web 版的插件。
- 安全隔离:渲染进程拿不到 Node.js、文件系统或任意命令的直接访问权限;Electron 沙箱、contextIsolation、仅限回环地址的导航,再加一层白名单 IPC,共同构成隔离边界。
- 升级简单:官方 DSH 发布新版本时,桌面版只需升个版本号、重新打包。原有的 DSH_HOME、会话、Agent Presets 和持久化插件原样沿用,不受影响。
底子保持精简 —— 只加几项实用的桌面增强
基础版本不堆功能,保留官方 Web 的体验,只补上桌面场景里最有用的几项:
- 项目面板:在聊天旁边列出每次回复实际改动或生成的文件。
展开完整文档(README 剩余 1 节)
DSH Desktop
DeepSeek Harness 的非官方开源桌面应用,目标是提供更简单、更顺手的桌面体验。
亮点
官方 DSH Web 外面套一层桌面外壳 —— 功能照单全收,安全,还容易跟上新版本
DSH Desktop 不重新实现任何功能,只是把官方 DSH Web 装进原生桌面窗口:
- 一键启动:不用开浏览器标签页,不用记 URL,不用敲命令行 —— 双击桌面图标,进入的就是官方 Web 那套运行时环境。
- 功能继承:官方 DSH Web 作为宿主运行时,加载它完整的 UI 和插件系统,会话、工作区数据、插件生态全部保留;内置插件里的 Web 插件市场组件,一处就能同时管理桌面版和 Web 版的插件。
- 安全隔离:渲染进程拿不到 Node.js、文件系统或任意命令的直接访问权限;Electron 沙箱、contextIsolation、仅限回环地址的导航,再加一层白名单 IPC,共同构成隔离边界。
- 升级简单:官方 DSH 发布新版本时,桌面版只需升个版本号、重新打包。原有的 DSH_HOME、会话、Agent Presets 和持久化插件原样沿用,不受影响。
底子保持精简 —— 只加几项实用的桌面增强
基础版本不堆功能,保留官方 Web 的体验,只补上桌面场景里最有用的几项:
- 项目面板:在聊天旁边列出每次回复实际改动或生成的文件。多标签预览支持 Markdown、HTML、源代码、diff、CSV、PDF、图片和文本,可分屏编辑,保存时按原子写入并处理冲突。Git 工作区显示真实状态,支持按文件丢弃改动;非 Git 目录改用内容快照(不会 git init,也不会往项目里写任何东西)。每次回复都会记录一份可恢复的快照,恢复前总会先把当前状态存成恢复点。
- 内置插件(可选安装):内置插件以可选目录的形式提供,不预装。目前已上架:多套配色主题(深海蓝、极光绿紫、玫瑰晚霞粉、暖沙琥珀,每套都有浅色和深色模式)、自定义图片背景(原生文件选择器,透明度可调,随时关闭),以及轻量写作助手(起草、改写、总结、翻译)。都支持按需安装、停用和卸载。
- Windows 系统通知(可选):智能体等待审批、完成任务或出错停止时,弹出原生通知 —— 不用一直盯着页面。随时可在「设置 → 插件 → 内置插件」里开关,默认关闭。
可扩展、有创意 —— 精简底座上的完整插件生态
底座保持轻量,扩展能力直接可用:
- 内置插件持续增加:官方精选插件通过可信渠道分发,带完整性校验,安装、禁用、卸载都不会碰你的内容。
- 社区插件市场:内置一份固定且经过校验的社区市场组件。启用后,进入「设置 → 插件 → 插件市场」即可浏览 awesome-dsh-plugin.com —— 集中安装、更新、卸载社区插件,自带 pnpm,无需全局安装任何东西。安装内容进入共享的 DSH_HOME,桌面版和浏览器版都能用。
- 按需扩展:需要时从内置目录和社区目录添加能力;不需要时,基础体验保持精简。
下载
两种使用方式
| 方式 | 适合谁 | 你会得到什么 |
| 一键安装包 | 大多数用户 | 双击即可安装运行,无需环境。数据是你自己的(DSH_HOME、会话、插件),但 DSH 由桌面版自带的运行时启动 |
| 连接模式 | 装了 Node.js、想用完整官方 DSH 的用户 | 不只是数据 —— DSH Web 本身也由你用系统 Node 启动(npx 拉取最新版本);桌面版只作为外壳连接,绝不使用自带运行时 |
两种方式共用同一份数据(DSH_HOME、会话、插件),可以随时切换。开发者也可以从源码运行;npm start 使用 auto 模式,优先用系统环境。
一键安装包
从 GitHub Releases 下载:
- Windows x64 Setup
- SHA-256 校验和
macOS(测试版)
Apple Silicon(M 系列):
- macOS arm64 DMG
- macOS arm64 ZIP
Intel(x64)版本会在 runner 跑完后出现在 Releases 页面。
首次启动时,在 Finder 里按住 Control 点击应用,选择 打开。如果 macOS 仍然拦截,去 系统设置 → 隐私与安全性 → 仍要打开。签名和公证计划在后续版本中提供。
截图
感知会话的项目面板
带完整上下文文件变更的项目面板
会话快照与按文件回滚
会话快照与回滚控件
托管插件中心
DSH Desktop Plugin Center
社区插件市场
内置的社区市场组件挂载到共享的 DSH_HOME 之后,会管理桌面端和 Web 端已经装好的插件,还能随时浏览并安装新加入的社区插件:
Community plugin market - plugin management
Community plugin market - plugin install
工作原理
DSH Desktop 会自动挑一个可用的运行时:
1. 连接已有的 DSH 服务。
2. 复用全局安装的 dsh。
3. 复用系统的 npx/npm 环境。
4. 回退到内置的 DSH 运行时。
老用户通常可以继续用原来的 DSH_HOME、会话、Agent Presets 和持久化插件。新用户不用先装 DSH 或 Node.js。
项目面板
对话真正改动或生成的文件,会显示在对应回复的下方。点一下,就在右侧多标签预览里打开,带文件名和关闭按钮;聊天布局会收窄,而不是被盖住。预览支持 Markdown、HTML、源代码、diff、CSV、PDF、图片和文本,还支持分屏编辑,保存是原子的,并且能识别冲突。Git 工作区会给出真实的状态 / diff / 丢弃操作;普通的非 Git 目录则改用 DSH Desktop 的内容 blob 快照,不会执行 git init,也不会往项目里写元数据。两种模式都保留回合改动、全上下文 diff、单文件撤销和可恢复快照;恢复快照前,会先把当前状态记成一个恢复点。
Office 文件目前调用系统应用打开;原生 DOCX/XLSX/PPTX 渲染还在计划中。
Web 插件市场(可选)
DSH Desktop 内置了社区插件市场 @sanqi-normal/dsh-webui-market-plugin(MIT,源码放在 resources/market-plugin),按需挂载到共享的 DSH_HOME,于是 DSH Web GUI 多出一个 Settings → Plugins → Plugin Market 标签页,可以浏览 awesome-dsh-plugin.com,把社区插件装进 web 配置档或从中卸载。
- 两个地方都能管:桌面端 Plugin Center 窗口(Web 插件市场标签页)和 Web 设置桥(GUI 里的内置插件标签卡片)。安装、启用、禁用、卸载都是对 $DSH_HOME/cordis.patch.yml 加 $DSH_HOME/node_modules/@sanqi-normal/dsh-webui-market-plugin 的互斥事务操作;$DSH_HOME/profiles/web 永远不会被改动。
- 没有全局 pnpm 也能用:应用自带 pnpm,并提供一个 PATH shim(src/pnpm-runtime.js),所有 DSH 进程都会继承它。所以即使在 Windows 上从没装过全局 pnpm,网页内安装也能成功(npx/全局/自带启动都一样)。从终端手动启动的 DSH 服务,也会回退到 $DSH_HOME/bin 下的自带 pnpm。如果 pnpm 确实不可用,市场会给出清楚的提示,而不是甩一堆控制台乱码。
- 常见安装问题自动处理:允许 git 托管的构建脚本(dangerously-allow-all-builds);迁移过的配置档复用其记录的 pnpm store(npm_config_store_dir);lockfile 缺少 tarball 完整性信息时(旧版 pnpm 写下的裸 GitHub archive URL),改写成 github: 语法并重试一次。
- 第三方警告:市场插件由社区作者编写,运行在 DSH 进程内,拥有宿主权限(文件、网络、命令)。只安装你信任的来源。桌面应用自带一份固定、经过摘要校验的副本,拒绝操作外来或被篡改的内容,但对第三方插件的行为不承担责任。
- 需要重启:改动在 DSH web 服务重启后生效;正在进行的会话不会中断。
- 来源与复现:resources/market-plugin/VENDORED.md —— Host 部分是上游代码加上两个 DSH Desktop 可移植性补丁(自带 pnpm 的 PATH shim 和人类可读的 pnpm 错误信息),Client 部分是 MIT 协议下重新设计的原创 UI。
插件兼容性
基于官方 DSH/Cordis 服务、Host 工具、UI 插槽、主题和持久化 Presets 构建的插件,兼容性通常最好。依赖固定 DOM 结构、浏览器扩展或开发 HMR 的插件,需要单独测试。
连接模式(用完整的官方 DSH Web,而不只是你的数据)
一键安装器会保留你的数据(DSH_HOME、会话、插件),但 DSH 进程由桌面自带的固定版本(rc.6)以 Electron-as-Node 启动。连接模式更进一步:DSH Web 本身也由你用自己系统的 Node 启动(npx 拉取最新官方版本),桌面只作为外壳连接 —— 数据、进程、版本全由你掌控。
DSH Desktop 只是一个外壳 —— 默认按 auto 顺序挑选运行时(连接已有服务 → 全局 dsh → 系统 npx → 自带运行时)。如果你想让 DSH Web 由自己系统的 Node 启动,就用连接模式:
完整步骤
1. 安装 Node.js(连接模式的前提):从 nodejs.org 下载 LTS 版本并安装。
2. 安装桌面应用(二选一):
- 下载一键安装器并安装;
- 或者从源码运行(开发者)。
3. 用系统 Node 启动 DSH Web(在终端里操作;保持窗口打开):
npx @deepseek-ai/dsh web --host 127.0.0.1 --port 3080
4. 把桌面端配置成只连接、不启动服务: json { "launchMode": "connect", "url": "http://127.0.0.1:3080" }
配置文件位于 <userData>\config.json(Windows 默认路径:C:\Users\<username>\AppData\Roaming\dsh-desktop-shell\config.json;如果设置了 DSH_DESKTOP_HOME,则以它指向的位置为准)。
5. 启动桌面端 —— 它会检测 3080 端口上的 DSH 服务并直接连接,跳过内置运行时。
这样你的 DSH 就跑在真正的 Node 上(process.execPath 是 node.exe),桌面端只负责提供窗口、项目面板、内置插件和系统通知 —— 不再有 Electron-as-Node 内置运行时,上游一发布更新你就能拿到。
注意:在 connect 模式下,DSH Web 由你的终端启动和管理 —— 关掉终端服务就停了;桌面端不会替你启动它。
从源码运行 powershell
git clone https://github.com/yxccai/dsh-desktop.git
cd dsh-desktop
npm ci
npm run check
npm test
npm start
npm start 默认使用 auto 模式:优先用你系统里的 dsh 或 npx,所以同样不会强制使用内置运行时。
平台支持
- Windows x64:提供 Setup 构建,已测试。
- macOS Intel / Apple Silicon:未签名的测试构建以预发布形式发布。首次启动时,在 Finder 里按住 Control 点击应用,选择 打开;如果 macOS 仍然拦截,去 系统设置 → 隐私与安全性 → 仍要打开。签名与公证已在计划中。
- 后续版本可能会考虑支持更多平台。
开发 powershell
npm ci
npm run check
npm test
npm start
更多信息:
- 更新日志
- 安全
- 贡献指南
- 运行时准备
许可
本项目采用 MIT 许可证。DeepSeek Harness、Electron 及其他依赖仍遵循各自的许可证。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
yxccai/dsh-desktop
DeepSeek Harness 的非官方开源桌面应用,目标是提供更简单、更顺手的桌面体验。
相关插件 · 桌面端
Codex 任务面板chuspeeism/dashi-taskboard
现代化可灵活嵌入的任务面板,支持 Codex、DeepSeek Harness
★ 3,409已汉化DSH
71.7
多智能体自进化开发平台Prism-Shadow/penguin-harness
统一、稳定的 Agent 自进化框架全自动优化:任意模型 · 任意算法 · 任意 Benchmark
★ 3,111已汉化DSH
71.1
桌面宠物PC2005-cloud/dsh-pet
DSH 桌面宠物:一行命令装好即用的透明动画小桌宠,支持多开、大小位置随心配置;还内置 DIY 素材链,能用 AI…
★ 5,341已汉化DSH
71
多端 Agent 工作台GCWing/OpenBitFun
OpenBitFun 是一个高度可定制的开源 Agent 工作台,让任务按你的方式持续推进。
★ 3,449已汉化DSH
70.8
DSH Desktopanywhere-labs/dsh-desktop
为 DeepSeek Harness DSH 插件生态打造的现代化桌面端解决方案。万物皆「插件」,桌面本身也是「插件」。
★ 34,532已汉化DSH
70.7
桌面宠物伴侣OpenPetsHQ/openpets
桌面伴侣平台,带宠物、插件,还能选配本地智能体集成。
★ 1,353已汉化DSH
70.2