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

原生桌面壳 TommyFang2077/dsh-linux-desktop

DeepSeek Harness
TommyFang20776 天前有提交已提供中文文档扫描:含敏感能力

非官方 DeepSeek Harness Linux 桌面适配:复用固定官方 Electron、内核与 WebUI…

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

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

自动兼容检查未通过:仓库根目录及浅层子目录均无 dsh 插件清单,无法用 dsh 插件安装命令安装。以下命令仍可尝试,但可能失败。
无法用 dsh 插件命令安装(仓库内无 dsh 插件清单)
git clone https://github.com/TommyFang2077/dsh-linux-desktop.git
按该仓库 README 的原生方式安装;若作者另行发布了 DSH 插件包,请以其文档为准

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:需留意
是什么
dsh 原生插件 · market
装得上吗
作者未把包发布到 npm,只能用源码安装
插件本身没问题,是分发方式不同
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 6 天前
档位由哪些信号合成

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装

仓库根目录及浅层子目录均无 dsh 插件清单,无法用 dsh 插件安装命令安装

展开查看检查明细 ↓

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

✗npm 包dsh-linux-desktop(未发布到 npm,仅可源码安装)
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✗入口文件缺少入口声明

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

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

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 189 处,展示前 98 条)
  • 读取本地文件
    electron-builder.config.mjs:6
    const desktop = JSON.parse(readFileSync(join(root, 'desktop.json'), 'utf8'))
  • 读取本地文件
    electron-builder.config.mjs:7
    const officialVersion = JSON.parse(readFileSync(join(root, 'upstream.json'), 'utf8')).version
  • 读取本地文件
    electron-builder.config.mjs:19
    const stat = lstatSync(path)
  • 写入本地文件
    electron-builder.config.mjs:20
    chmodSync(path, ((stat.mode & 0o777) | (stat.isDirectory() ? 0o700 : 0o600)) & ~0o022)
  • 读取本地文件
    electron-builder.config.mjs:21
    if (lstatSync(path).mode & 0o022) throw new Error('Packaging requires a filesystem with Unix permissions')
  • 读取本地文件
    electron-builder.config.mjs:28
    if (!lstatSync(directory).isDirectory() || lstatSync(directory).isSymbolicLink()) {
  • 读取本地文件
    electron-builder.config.mjs:36
    for (const entry of readdirSync(pending.pop(), { withFileTypes: true })) {
  • 写入本地文件
    electron-builder.config.mjs:64
    writeFileSync(file, bytes, { flag: 'wx', mode })
  • 写入本地文件
    electron-builder.config.mjs:65
    chmodSync(file, mode)
  • 写入本地文件
    electron-builder.config.mjs:67
    for (const path of aliases) renameSync(join(temporary, relative(bin, path)), path)
  • 删除本地文件递归
    electron-builder.config.mjs:68
    } finally { rmSync(temporary, { recursive: true, force: true }) }
  • 删除本地文件
    electron-builder.config.mjs:90
    rmSync(join(context.appOutDir, 'resources/default_app.asar'), { force: true })
  • 读取本地文件
    updates/core.mjs:76
    const metadata = JSON.parse(await fs.readFile(join(root, 'runtime/primary-runtime/runtime.json'), 'utf8'))
  • 读取本地文件
    updates/core.mjs:88
    for await (const chunk of createReadStream(path)) digest.update(chunk)
  • 读取本地文件
    updates/core.mjs:104
    const directoryStat = await fs.lstat(directory)
  • 读取本地文件
    updates/core.mjs:106
    for (const name of (await fs.readdir(directory)).sort()) {
  • 读取本地文件
    updates/core.mjs:110
    const stat = await fs.lstat(absolute)
  • 读取本地文件
    updates/core.mjs:125
    const stat = await fs.lstat(manifestPath)
  • 写入本地文件
    updates/core.mjs:197
    await fs.rename(temporary, path)
  • 删除本地文件
    updates/core.mjs:200
    } finally { await fs.rm(temporary, { force: true }) }
  • 写入本地文件
    updates/core.mjs:217
    await fs.mkdir(this.root, { recursive: true, mode: 0o700 })
  • 写入本地文件
    updates/core.mjs:219
    await fs.chmod(this.root, 0o700)
  • 发起网络请求
    updates/core.mjs:332
    const response = await this.fetch(address.href, { redirect: manual ? 'manual' : 'error', signal })
  • 写入本地文件
    updates/core.mjs:457
    await fs.mkdir(directory, { recursive: true, mode: 0o700 })
  • 写入本地文件
    updates/core.mjs:466
    await fs.rename(temporary, path)
  • 删除本地文件
    updates/core.mjs:467
    } finally { await fs.rm(temporary, { force: true }) }
  • 删除本地文件
    updates/core.mjs:472
    await fs.rm(join(directory, name), { force: true })
  • 写入本地文件
    updates/core.mjs:488
    await fs.copyFile(cached, destination)
  • 删除本地文件
    updates/core.mjs:532
    } finally { await fs.rm(staging, { recursive: true, force: true }) }
  • 删除本地文件
    updates/core.mjs:555
    } finally { await fs.rm(directory, { recursive: true, force: true }) }
  • 删除本地文件
    updates/core.mjs:574
    } finally { await fs.rm(directory, { recursive: true, force: true }) }
  • 写入剪贴板
    updates/desktop.mjs:31
    try { clipboard.writeText(details); feedback = '报告已复制。' }
  • 读取本地文件
    updates/desktop.mjs:42
    const metadata = JSON.parse(await fs.readFile(join(resources, 'desktop.json'), 'utf8'))
  • 读取本地文件
    updates/desktop.mjs:43
    const config = JSON.parse(await fs.readFile(process.env.DSH_WORKBENCH_UPDATE_CONFIG ?? join(resources, 'updates.json'), 'utf8'))
  • 读取本地文件
    updates/desktop.mjs:44
    const descriptor = JSON.parse(await fs.readFile(join(options.bundled.dsh, 'desktop-runtime.json'), 'utf8'))
  • 读取本地文件
    updates/desktop.mjs:45
    const primaryRuntime = JSON.parse(await fs.readFile(join(options.bundled.runtime, 'primary-runtime/runtime.json'), 'utf8'))
  • 执行外部命令来自配置/变量
    updates/market.mjs:11
    const child = spawn(node, ['--expose-internals', cli, kernel.dsh, kernel.runtime,
  • 读取本地文件
    updates/market.mjs:48
    try { await fs.access(marker); return 'already-provisioned' } catch (error) { if (error.code !== 'ENOENT') throw error }
  • 读取本地文件
    updates/market.mjs:50
    const manifest = JSON.parse(await fs.readFile(join(profile, 'package.json'), 'utf8'))
  • 写入本地文件
    updates/market.mjs:53
    await fs.mkdir(data, { recursive: true, mode: 0o700 })
  • 写入本地文件
    updates/market.mjs:54
    await fs.writeFile(marker, JSON.stringify({ name: MARKET_PACKAGE, managedByUser: true }) + '\n', { flag: 'wx', mode: 0o600 })
  • 读取本地文件
    updates/market.mjs:58
    const installed = JSON.parse(await fs.readFile(join(profile, 'node_modules/dshmarket/package.json'), 'utf8'))
  • 写入本地文件
    updates/market.mjs:60
    await fs.mkdir(data, { recursive: true, mode: 0o700 })
  • 写入本地文件
    updates/market.mjs:61
    await fs.writeFile(marker, JSON.stringify({ name: installed.name, version: installed.version }) + '\n', { flag: 'wx', mode: 0o600 })
  • 读取本地文件
    updates/migration-processes.mjs:10
    const contents = await fs.readFile(`/proc/${pid}/stat`, 'utf8')
  • 读取本地文件
    updates/migration-processes.mjs:13
    bootId: (await fs.readFile('/proc/sys/kernel/random/boot_id', 'utf8')).trim() }
  • 读取本地文件
    updates/migration-processes.mjs:25
    for (const name of await fs.readdir('/proc')) {
  • 读取本地文件
    updates/migration-processes.mjs:28
    if ((await fs.stat(`/proc/${name}`)).uid !== process.getuid()) continue
  • 读取本地文件
    updates/migration-processes.mjs:46
    return (await fs.readFile(`/proc/${pid}/cmdline`, 'utf8')).split('\0').filter(Boolean)
  • 读取本地文件
    updates/migration-processes.mjs:50
    const environment = (await fs.readFile(`/proc/${pid}/environ`, 'utf8')).split('\0')
  • 执行外部命令来自配置/变量
    updates/migration-processes.mjs:179
    const child = spawn(python, ['-I', '-c', signalProcesses], { stdio: ['pipe', 'pipe', 'pipe'] })
  • 执行外部命令来自配置/变量
    updates/migration-progress-launch.mjs:13
    child = spawn(process.execPath, ['--dsh-migration-progress', transactionPath], {
  • 读取本地文件
    updates/migration-progress.mjs:12
    const stat = readFileSync(`/proc/${identity.pid}/stat`, 'utf8')
  • 读取本地文件
    updates/migration-progress.mjs:15
    && readFileSync('/proc/sys/kernel/random/boot_id', 'utf8').trim() === identity.bootId
  • 写入本地文件
    updates/migration-progress.mjs:23
    mkdirSync(profile, { recursive: true, mode: 0o700 })
  • 读取本地文件
    updates/migration-progress.mjs:24
    const info = lstatSync(profile)
  • 写入剪贴板
    updates/migration-progress.mjs:76
    try { clipboard.writeText(`${details}\n${feedback}`); feedback = '报告已复制。' }
  • 写入剪贴板
    updates/migration-progress.mjs:114
    clipboard.writeText(migrationReportText(report, context.reportPath))
  • 读取本地文件
    updates/migration-report.mjs:19
    const info = lstatSync(path)
  • 读取本地文件
    updates/migration-report.mjs:22
    return JSON.parse(readFileSync(path, 'utf8'))
  • 读取本地文件
    updates/migration-report.mjs:37
    const info = lstatSync(directory)
  • 读取本地文件
    updates/user-install.mjs:17
    try { await fs.lstat(path); return true } catch (error) { if (error.code === 'ENOENT') return false; throw error }
  • 读取本地文件
    updates/user-install.mjs:21
    const stat = await fs.lstat(path)
  • 读取本地文件
    updates/user-install.mjs:31
    const state = JSON.parse(await fs.readFile(path, 'utf8'))
  • 读取本地文件
    updates/user-install.mjs:39
    if (await exists(path) && !(await fs.lstat(path)).isSymbolicLink()) fail('安装入口已被其他文件占用')
  • 删除本地文件
    updates/user-install.mjs:40
    if (value === null) { await fs.rm(path, { force: true }); return }
  • 写入本地文件
    updates/user-install.mjs:44
    await fs.rename(temporary, path)
  • 删除本地文件
    updates/user-install.mjs:45
    } finally { await fs.rm(temporary, { force: true }) }
  • 读取本地文件
    updates/user-install.mjs:56
    home = await fs.realpath(home)
  • 读取本地文件
    updates/user-install.mjs:60
    const real = await fs.realpath(parent)
  • 写入本地文件
    updates/user-install.mjs:65
    } else await fs.mkdir(root, { recursive: true, mode: 0o700 })
  • 写入本地文件
    updates/user-install.mjs:73
    try { await fs.mkdir(lock, { mode: 0o700 }) } catch (error) {
  • 删除本地文件
    updates/user-install.mjs:77
    try { return await action() } finally { await fs.rmdir(lock) }
  • 写入本地文件
    updates/user-install.mjs:171
    await fs.mkdir(join(root, 'versions'), { recursive: true, mode: 0o700 })
  • 写入本地文件
    updates/user-install.mjs:178
    } else await fs.rename(tree, destination)
  • 写入本地文件
    updates/user-install.mjs:186
    await fs.mkdir(dirname(path), { recursive: true, mode: 0o755 })
  • 删除本地文件
    updates/user-install.mjs:194
    await fs.rm(journal)
  • 删除本地文件
    updates/user-install.mjs:199
    for (const path of created) await fs.rm(path)
  • 删除本地文件
    updates/user-install.mjs:200
    await fs.rm(journal, { force: true })
  • 写入本地文件
    updates/whole-desktop.mjs:16
    await fs.mkdir(path, { recursive: true, mode: 0o700 })
  • 读取本地文件
    updates/whole-desktop.mjs:17
    const info = await fs.lstat(path)
  • 读取本地文件
    updates/whole-desktop.mjs:28
    const actual = await fs.realpath(parent)
  • 读取本地文件
    updates/whole-desktop.mjs:39
    const info = await fs.lstat(request.archive)
  • 读取本地文件
    updates/whole-desktop.mjs:76
    try { owner = JSON.parse(await fs.readFile(join(path, 'owner.json'), 'utf8')) }
  • 删除本地文件
    updates/whole-desktop.mjs:79
    await fs.rm(path, { recursive: true })
  • 读取本地文件
    updates/whole-desktop.mjs:92
    try { await fs.lstat(gate) }
  • 读取本地文件
    updates/whole-desktop.mjs:94
    owner = JSON.parse(await fs.readFile(join(gate, 'owner.json'), 'utf8'))
  • 写入本地文件
    updates/whole-desktop.mjs:165
    await fs.mkdir(parent, { recursive: true, mode: 0o700 })
  • 写入本地文件
    updates/whole-desktop.mjs:171
    try { await fs.mkdir(gate, { mode: 0o700 }) } catch (error) {
  • 写入本地文件
    updates/whole-desktop.mjs:185
    await fs.copyFile(request.archive, transaction.archive, constants.COPYFILE_EXCL)
  • 写入本地文件
    updates/whole-desktop.mjs:186
    await fs.chmod(transaction.archive, 0o600)
  • 写入本地文件
    updates/whole-desktop.mjs:209
    await fs.writeFile(transaction.workerPath, worker, { flag: 'wx', mode: 0o600 })
  • 删除本地文件
    updates/whole-desktop.mjs:226
    } finally { await fs.rm(candidate, { recursive: true, force: true }) }
  • 执行外部命令来自配置/变量
    updates/whole-desktop.mjs:259
    child = spawn(transaction.node, [transaction.workerPath, 'run', join(transaction.directory, 'transaction.json')], {
  • 删除本地文件
    updates/whole-desktop.mjs:401
    } finally { await fs.rm(staging, { recursive: true, force: true }) }
  • 删除本地文件
    updates/whole-desktop.mjs:459
    await fs.rm(join(transaction.directory, 'backup.staging'), { recursive: true, force: true })
  • 执行外部命令来自配置/变量
    updates/whole-desktop.mjs:481
    const child = spawn(result.executable, [], { detached: true, env, stdio: ['ignore', log.fd, log.fd] })
  • 执行外部命令来自配置/变量
    updates/whole-desktop.mjs:545
    const child = spawn(transaction.oldExecutable, [], { detached: true, stdio: 'ignore', env: { ...process.env,
扫描于 2026/11/3(1 天前) · 范围 全包扫描 · 来源 GitHub 仓库

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

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • Tauri2原生桌面窗口
  • 复用官方WebUI
  • 内置离线语音输入
展开完整介绍(127 字)↓

官方 DeepSeek Harness(dsh)原生桌面壳:Tauri 2 把官方 WebUI 变成原生桌面窗口,保留会话/工作区/插件/技能。内置 SenseVoice 离线语音输入、插件市场、视觉模型配置;锚定模式解决 DeepSeek 思考模式问题

README

由 DeepSeek 最新模型翻译生成
dsh-linux-desktop 基于 DeepSeek Harness 官方源码构建的非官方 Linux 桌面版,支持 Linux x86_64。 程序统一安装到 ~/Applications/dsh-linux-desktop/,不安装到 /opt。升级保留现有配置、会话和插件。 安装 从 Releases 下载适合系统的软件包: - Ubuntu / Debian:下载 .deb。 - Fedora / RHEL:下载 .rpm。 在下载目录打开终端,执行对应命令(文件名换成你下载的版本): Ubuntu / Debian sudo apt install ./dsh-workbench-0.2.1-alpha.2-r1-linux-x64.deb Fedora / RHEL sudo dnf install ./dsh-workbench-0.2.1-alpha.2-r1-linux-x64.rpm 安装后,从应用菜单打开 dsh-workbench。首次启动会自动将程序安装到 HOME,请稍等;不需要手动解压,也不需要另外安装 Node.js 或 Python。 只有安装软件包需要系统授权,打开应用和应用内更新不需要。不要使用 sudo 启动应用。 从旧版升级前,请先通过托盘的「退出」完全关闭应用。
展开完整文档(README 剩余 1 节)
dsh-linux-desktop

基于 DeepSeek Harness 官方源码构建的非官方 Linux 桌面版,支持 Linux x86_64。

程序统一安装到 ~/Applications/dsh-linux-desktop/,不安装到 /opt。升级保留现有配置、会话和插件。

安装

从 Releases 下载适合系统的软件包:

- Ubuntu / Debian:下载 .deb。
- Fedora / RHEL:下载 .rpm。

在下载目录打开终端,执行对应命令(文件名换成你下载的版本):

Ubuntu / Debian
sudo apt install ./dsh-workbench-0.2.1-alpha.2-r1-linux-x64.deb

Fedora / RHEL
sudo dnf install ./dsh-workbench-0.2.1-alpha.2-r1-linux-x64.rpm

安装后,从应用菜单打开 dsh-workbench。首次启动会自动将程序安装到 HOME,请稍等;不需要手动解压,也不需要另外安装 Node.js 或 Python。

只有安装软件包需要系统授权,打开应用和应用内更新不需要。不要使用 sudo 启动应用。 从旧版升级前,请先通过托盘的「退出」完全关闭应用。

更新

打开 设置 → 通用 → 软件更新,检查并更新桌面或内核。两者独立更新,下载后会校验签名和兼容性。

内核优先跟随官方正式版和预发布 Release。项目自动应用 Linux 适配、构建、测试,并用已发布的旧壳实际升级到新内核;通过后签名发布,失败则保留候选和失败证据供适配。客户端启动完成后及每 6 小时检查、下载内核,安装必须由用户确认;忙或任务状态未知时暂缓,不强停任务。

新的跟随更新器保留原壳,每次安装新内核前,在旧程序完全退出后备份实际配置、会话及程序状态;备份校验通过后才激活。新内核启动失败时保留备份并阻止旧内核读取新数据,恢复须使用对应的整组快照。alpha.5 r2 用户需要先安装一次 alpha.5 r3 更新器过渡包,后续以内核独立升级为主。实现与发布验收状态见 自动更新策略与维护;源码变更不会直接替换本机安装。

alpha.1 → alpha.2 是跨数据兼容边界升级:在「软件更新」里点「检查更新」再点「更新」即可。alpha.1 用户会先更新到 alpha.1 r9 过渡包,再次更新时升级到 alpha.2 r3;旧应用完全退出、自动数据备份验证成功后才切换。仅更新 deb/rpm 引导包不会替换已有 HOME 版本;回退到 alpha.1 必须同时恢复旧格式数据。支持范围与恢复说明见 过渡升级与恢复。

托盘菜单提供「打开」「重启」「退出」。重启或退出前,如果有正在执行的任务,应用会请求确认。

常见问题

程序和数据放在哪里?

- 程序:~/Applications/dsh-linux-desktop/
- 配置、会话、插件和相关状态:实际 DSH_HOME,默认 ~/.dsh/,桌面 profile 位于其 profiles/desktop/。
- 桌面设置:实际 Electron userData,默认 ~/.config/dsh-workbench/;XDG 配置可能改变该路径。

不要通过删除数据目录来升级或迁移程序。

GNOME 下没有托盘图标?

安装并启用适配当前 GNOME 版本的 AppIndicator and KStatusNotifierItem Support 扩展,只保留一个同类托盘扩展。优先使用 v66 或包含其修复的发行版版本;更换扩展后注销并重新登录,再启动应用。

应用不会自动修改系统扩展或关闭 Chromium 沙箱。

不使用 deb/rpm:手动安装 tar.gz

仅使用本仓库可信发行版或自己构建的归档。先核对下载来源和校验值,再执行其中的程序;未知来源的归档不能靠自己附带的校验值证明可信。

以下示例使用 r3 归档,在仓库根目录运行,归档位于 dist/:

(cd dist && sha256sum --check SHA256SUMS)
archive="$PWD/dist/dsh-workbench-0.2.1-alpha.2-r3-linux-x64.tar.gz"
checksum=$(sha256sum "$archive" | cut -d ' ' -f 1)
temporary=$(mktemp -d)
tar -xzf "$archive" -C "$temporary"
"$temporary/app/resources/runtime/primary-runtime/dependencies/node/bin/node" \
"$temporary/app/resources/app/workbench/install-user.mjs" \
--archive "$archive" --sha256 "$checksum" --version 0.2.1-alpha.2
~/.local/bin/dsh-workbench

安装完成后可删除临时解压目录。此方式同样安装到 HOME,之后从应用菜单启动和更新。

从源码构建

需要 Node.js 24+、npm、Git、Python 3.11+、C/C++ 编译器、make、tar、dpkg-deb 和 rpmbuild。首次构建需要联网和数 GB 空间。

make prepare
make test
make package

产物位于 dist/:deb、rpm、tar.gz、SHA256SUMS 和 build-info.json。

make verify # 检查打包产物
make run # 临时运行,不安装
make gui-smoke # 使用隔离 HOME 验证窗口、图片和沙箱

上游版本固定在 upstream.json,Linux 构建号在 desktop.json,上游适配补丁在 patches/linux.patch。安装机制、签名发布和回滚详见 更新说明。

许可证

本项目构建脚本和 Linux 补丁采用 MIT,官方代码及捆绑依赖保留原许可证。DeepSeek 名称与图案归各自权利人所有,本项目并非 DeepSeek 官方发行版。
预览分享卡片长什么样 ↓TommyFang2077/dsh-linux-desktop
分享 · DeepSeek Harness Hub
TommyFang2077/dsh-linux-desktop

非官方 DeepSeek Harness Linux 桌面适配:复用固定官方 Electron、内核与 WebUI…

这是你的仓库?认领条目

回到顶部