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

桌面应用打包器 Olympianz/dsh-desktop-packager

DeepSeek Harnessv0.4.1
Olympianz61 天前有提交已提供中文文档扫描:含敏感能力

把已构建项目封装成可分发 macOS 应用与 dmg 安装包

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

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

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add dsh-desktop-packager
npm 源 · dsh-desktop-packager 的发布者账号已校验与本仓库一致

状态速览

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

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

验证判据与等级说明 →

可信度证据

安装兼容性检查✓ 自动检查通过
展开查看检查明细 ↓

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

✓npm 包dsh-desktop-packager @ 0.4.1
✓Node 引擎要求 >=22 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/9/22 05:57:42

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

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

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 74 处,展示前 51 条)
  • 发起网络请求
    client.js:22
    return fetch(path).then(function (r) { return r.json() })
  • 发起网络请求
    client.js:25
    return fetch(path, {
  • 读取本地文件
    src/packager.ts:118
    if (!existsSync(script)) {
  • 执行外部命令来自配置/变量
    src/packager.ts:139
    const child = spawn(process.execPath, [script, ...args], {
  • 读取本地文件
    src/desktop-config.ts:299
    if (!existsSync(logoPath)) return { ok: false, error: `logo 文件不存在:${logoPath}` }
  • 读取本地文件
    src/desktop-config.ts:303
    const data = readFileSync(logoPath).toString('base64')
  • 读取本地文件
    src/desktop-config.ts:466
    if (existsSync(this.configPath)) {
  • 读取本地文件
    src/desktop-config.ts:467
    const parsed = JSON.parse(readFileSync(this.configPath, 'utf8')) as Partial<DesktopConfig>
  • 写入本地文件
    src/desktop-config.ts:477
    mkdirSync(dirname(this.configPath), { recursive: true })
  • 写入本地文件
    src/desktop-config.ts:478
    writeFileSync(this.configPath, JSON.stringify(next, null, 2) + '\n')
  • 读取本地文件
    src/desktop-config.ts:486
    if (!existsSync(this.statePath)) return null
  • 读取本地文件
    src/desktop-config.ts:487
    const raw = JSON.parse(readFileSync(this.statePath, 'utf8')) as PackageJobState | null
  • 写入本地文件
    src/desktop-config.ts:507
    mkdirSync(dirname(this.statePath), { recursive: true })
  • 写入本地文件
    src/desktop-config.ts:508
    writeFileSync(this.statePath, JSON.stringify(this.packageState, null, 2) + '\n')
  • 执行外部命令来自配置/变量
    packaging/build-macos.mjs:83
    const r = spawnSync(cmd, args, { stdio: 'inherit', encoding: 'utf8', ...opts })
  • 执行外部命令来自配置/变量
    packaging/build-macos.mjs:91
    const r = spawnSync(cmd, { shell: true, stdio: 'inherit', encoding: 'utf8' })
  • 读取本地文件
    packaging/build-macos.mjs:108
    const raw = readFileSync(resolve(configPath), 'utf8')
  • 读取本地文件
    packaging/build-macos.mjs:136
    entries = readdirSync(dir, { withFileTypes: true })
  • 删除本地文件递归
    packaging/build-macos.mjs:154
    rmSync(d, { recursive: true, force: true })
  • 删除本地文件
    packaging/build-macos.mjs:157
    rmSync(f, { force: true })
  • 删除本地文件递归
    packaging/build-macos.mjs:161
    rmSync(join(root, name), { recursive: true, force: true })
  • 删除本地文件
    packaging/build-macos.mjs:165
    rmSync(join(root, f), { force: true })
  • 删除本地文件递归
    packaging/build-macos.mjs:171
    rmSync(dest, { recursive: true, force: true })
  • 写入本地文件
    packaging/build-macos.mjs:172
    mkdirSync(dest, { recursive: true })
  • 执行外部命令固定命令
    packaging/build-macos.mjs:177
    const size = spawnSync('du', ['-sh', dest], { encoding: 'utf8' }).stdout?.trim()
  • 删除本地文件
    packaging/build-macos.mjs:191
    rmSync(tarPath, { force: true })
  • 执行外部命令固定命令
    packaging/build-macos.mjs:194
    const size = spawnSync('du', ['-sh', tarPath], { encoding: 'utf8' }).stdout?.trim()
  • 发起网络请求
    packaging/build-macos.mjs:203
    const res = await fetch(`${base}/index.json`)
  • 写入本地文件
    packaging/build-macos.mjs:215
    mkdirSync(join(dest, 'bin'), { recursive: true })
  • 读取本地文件
    packaging/build-macos.mjs:220
    if (!existsSync(src)) fail(`--node-bin path does not exist: ${src}`)
  • 写入本地文件
    packaging/build-macos.mjs:222
    copyFileSync(src, join(dest, 'bin', 'node'))
  • 写入本地文件
    packaging/build-macos.mjs:250
    mkdirSync(join(appDir, 'build'), { recursive: true })
  • 读取本地文件
    packaging/build-macos.mjs:255
    if (!existsSync(srcMain)) fail(`missing packaging/electron/main.js (expected at ${srcMain})`)
  • 写入本地文件
    packaging/build-macos.mjs:256
    copyFileSync(srcMain, join(appDir, 'main.js'))
  • 写入本地文件
    packaging/build-macos.mjs:257
    copyFileSync(srcEnt, join(appDir, 'entitlements.mac.plist'))
  • 读取本地文件
    packaging/build-macos.mjs:258
    if (existsSync(srcPreload)) copyFileSync(srcPreload, join(appDir, 'preload.js'))
  • 读取本地文件
    packaging/build-macos.mjs:328
    if (logoPath && existsSync(logoPath) && logoPath.toLowerCase().endsWith('.icns')) {
  • 执行外部命令固定命令
    packaging/build-macos.mjs:350
    const px = spawnSync('sips', ['-g', 'pixelWidth', rendered], { encoding: 'utf8' }).stdout ?? ''
  • 执行外部命令固定命令
    packaging/build-macos.mjs:366
    spawnSync('sips', ['-z', String(px), String(px), rendered, '--out', join(iconset, name)], { stdio: 'ignore' })
  • 读取本地文件
    packaging/electron/main.js:51
    if (!fs.existsSync(DESKTOP_CONFIG)) return undefined
  • 读取本地文件
    packaging/electron/main.js:52
    const parsed = JSON.parse(fs.readFileSync(DESKTOP_CONFIG, 'utf8'))
  • 写入本地文件
    packaging/electron/main.js:84
    fs.mkdirSync(logDir(), { recursive: true })
  • 写入本地文件
    packaging/electron/main.js:85
    fs.appendFileSync(logFile(), line)
  • 发起网络请求
    packaging/electron/main.js:109
    const req = http.get(url, (res) => {
  • 执行外部命令来自配置/变量
    packaging/electron/main.js:128
    const child = spawn(cmd, args, { stdio: ['ignore', 'pipe', 'pipe'] })
  • 读取本地文件
    packaging/electron/main.js:147
    if (fs.existsSync(cli)) {
  • 写入本地文件
    packaging/electron/main.js:152
    fs.mkdirSync(base, { recursive: true })
  • 读取本地文件
    packaging/electron/main.js:155
    if (r.code !== 0 || !fs.existsSync(cli)) {
  • 写入本地文件
    packaging/electron/main.js:169
    fs.mkdirSync(home, { recursive: true })
  • 写入本地文件
    packaging/electron/main.js:171
    fs.writeFileSync(
  • 执行外部命令来自配置/变量
    packaging/electron/main.js:208
    const child = spawn(
扫描于 2026/11/4(1 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包

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

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

功能亮点

  • 封装为macOS应用
  • 生成dmg安装包
  • 内置Node运行时
展开完整介绍(126 字)↓

把构建好的 DeepSeek Harness 检出打包成可分发的 macOS 桌面应用(.app + .dmg),内置独立 Node 运行时和 Electron 外壳,并通过 Web 设置区定制(logo/VI 主题、内置插件、内置模型、用户与权限)。

v0.4.12026-09-05
· Fixed · 修复桌面应用登录门「账号或密码错误」:verifyPassword 之前按 split('$') 后取 parts[0..2],而哈希格式 $scrypt$$ 开头的 $ 会拆出一个空首段,导致所有正确密码都被判错。现按真实分段(['', 'scrypt', salt, hash])解析,登录门恢复正常。 · 关于本插件 dsh-desktop-packager 把已构建的 DeepSeek Harness checkout 封装成可分发的 macOS 桌面应用(.app + .dmg),内置独立 Node 运行时与 Electron 壳,并在 Web Settings 提供「桌面打包」面板(设计方案 / 插件 / 模型 / 用户权限)。 · 安装:dsh plugin --profile web add dsh-desktop-packager · npm:
v0.4.02026-09-01
dsh-desktop-packager v0.4.0
原文(未汉化)
v0.3.02026-08-31
· What's new in v0.3.0 · 插件设置分组:把已安装插件按来源分成「deepseek harness 原生」与「后续安装」两类。 · 分组全选交互:每类插件支持全选 / 取消全选(三态复选框 + 按钮);顶部支持「全选全部 / 清空全部」,并显示「已选 N / M 个」。 · Earlier · Settings「桌面打包」面板 4 Tab:设计方案 / 插件设置 / 模型设置 / 权限设置。 · dsh_package_desktop 工具:把已构建的 deepseek-harness 封装成可分发的 macOS .app + .dmg。 · Electron 桌面壳:登录门(scrypt 校验)+ 主题注入 + 拉起 dsh web。
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。

README

由 DeepSeek 最新模型翻译生成
dsh-desktop-packager 把 DeepSeek Harness 封装成一个可分发的 macOS 桌面应用(.app + .dmg)的 DSH 插件,并在 dsh web 的 *Settings 里新增一个「桌面打包」菜单,通过 4 个配置模块定制化生成 dmg。 能力 - 注册工具 dsh_package_desktop:把一个已构建的 deepseek-harness checkout 封装成 .app + .dmg,并按「桌面打包」配置定制化生成。 - Settings 菜单「桌面打包」(浏览器半侧,settings.section),含 4 个 Tab: 1. 设计方案 — 显示当前 logo、应用名、VI 方案(主题色 / 背景色),可更换 logo 或整体 VI。 2. 插件设置 — 从已安装插件中勾选「打包进桌面 app」的插件。 3. 模型设置 — 从已支持的模型中勾选「打包进桌面 app」的模型。 4. 权限设置 — 配置桌面 app 的用户(账号 + 密码)与权限(查看/修改 settings 各页)。
展开完整文档(README 剩余 1 节)
dsh-desktop-packager

把 DeepSeek Harness 封装成一个可分发的 macOS 桌面应用(.app + .dmg)的 DSH 插件,并在 dsh web 的 *Settings 里新增一个「桌面打包」菜单,通过 4 个配置模块定制化生成 dmg。

能力

- 注册工具 dsh_package_desktop:把一个已构建的 deepseek-harness checkout 封装成 .app + .dmg,并按「桌面打包」配置定制化生成。
- Settings 菜单「桌面打包」(浏览器半侧,settings.section),含 4 个 Tab:
1. 设计方案 — 显示当前 logo、应用名、VI 方案(主题色 / 背景色),可更换 logo 或整体 VI。
2. 插件设置 — 从已安装插件中勾选「打包进桌面 app」的插件。
3. 模型设置 — 从已支持的模型中勾选「打包进桌面 app」的模型。
4. 权限设置 — 配置桌面 app 的用户(账号 + 密码)与权限(查看/修改 settings 各页)。
- 打包定制化落地:
- appName → Electron productName;logo → 应用图标(.icns/.png/.svg);
- themeColor / backgroundColor → 注入 DSH web 主题变量(--dsw-alias-brand-primary / --dsw-alias-bg-base)与窗口背景色;
- selectedPlugins / selectedModels → 写入 app 的 DSH_HOME/desktop-config.json,供 app 内的运行时插件做插件/模型过滤;
- users → 打包进 Contents/Resources/desktop.json,由 Electron 主进程实现登录门(账号 + 密码,加盐 scrypt 校验)并注入当前用户权限。

架构

一个 dual-face bundle(Host 原生 TS + 手写浏览器 client bundle):

| 半侧 | 入口 | 形态 | 说明 |
| Host(入口) | src/index.ts | 原生 TS(dsh plugin add 直接加载) | 提供 desktopConfig 服务 + 注册 dsh_package_desktop 工具 + 注册 HTTP 路由 |
| Host(服务) | src/desktop-config.ts | 原生 TS | 持久化 + 插件/模型枚举 + 用户管理(scrypt 加盐哈希)+ HTTP 路由 |
| Client(浏览器) | client.js | 手写 bundle(window.__ModuleLoader__.load) | settings.section「桌面打包」4 Tab,fetch 同源调 HTTP 路由 |
| 打包驱动 | packaging/build-macos.mjs | 独立 .mjs | 定制化生成 .app/.dmg |
| 桌面壳 | packaging/electron/ | 独立 .js | 登录门 + 主题注入 + 拉起 dsh web |

配置状态存于单个 JSON 文件(默认 ~/.dsh/desktop-packager/config.json),由 Host 服务、打包工具与 build-macos.mjs 共享。

浏览器 ↔ Host 桥接(同源 HTTP)

浏览器半侧不依赖 Typert/Remote,直接用同源 fetch 访问 Host 半侧在 webServer 上注册的 JSON 路由:

| 路由 | 方法 | 作用 |
| /api/desktop-config | GET | 返回 { view }(脱敏后的 design/plugins/models/users) |
| /api/desktop-config | POST | { action, payload } → 应用改动并返回 { view } |
| /api/desktop-config/catalog | GET | 返回 { plugins, models }(枚举) |
| /api/desktop-config/logo | GET | 返回 { ok, dataUrl? }(logo 预览) |

安装

dsh plugin --profile web add /path/to/dsh-desktop-packager

或在 dsh web 会话里让助手调用 dsh_master_install(需 DSH-master)。

本包已在当前机器以符号链接方式装进 ~/.dsh/profiles/web/node_modules/dsh-desktop-packager,指向源码目录;改动源码后重启 dsh web 即生效。

使用

1. 在 Settings 里配置

打开 dsh web → 左下角 Settings → 「桌面打包」,分别配置:

- 设计方案:填 logo 绝对路径(.png/.svg/.icns)、应用名、主题色、背景色,点「保存」。
- 插件设置:勾选要打进桌面 app 的插件(留空 = 全部)。
- 模型设置:勾选要打进桌面 app 的模型(留空 = 全部)。
- 权限设置:新增/删除用户(账号 + 密码),并为每个用户勾选权限(查看/修改 settings、settings-design、settings-plugins、settings-models、 settings-permissions)。

2. 打包

对助手说「帮我用 dsh_package_desktop 打包我的 deepseek-harness」,助手会调用该工具。
核心参数:

| 参数 | 默认 | 说明 |
| dshCheckoutDir | 配置 dshCheckoutDir | 已构建(pnpm run build 完成)的 harness 源码目录 |
| out | ~/Desktop/DeepSeek-Harness-dist | 产物输出目录 |
| appName | 设计方案里的 appName | 应用显示名(工具参数优先级高于设计方案) |
| bundleId | com.deepseek.harness | bundle identifier |
| version | 0.1.1 | 版本号 |
| arch | arm64 | arm64 / x64 |
| identity | 空(ad-hoc) | Developer ID 签名身份 |
| nodeBin / nodeVersion | 空 | 复用本地 Node / 指定下载版本 |
| skipDmg / keepStaging | false | 只出 .app / 保留中间目录 |

打包时工具把 configPath 传给 build-macos.mjs,后者读取同一份 JSON 并应用 logo / 主题色 / 插件 / 模型 / 用户权限定制。

配置项(cordis.yml)

- id: dsh-desktop-packager
name: dsh-desktop-packager
config:
dshCheckoutDir: /absolute/path/to/deepseek-harness
out: ~/Desktop/DeepSeek-Harness-dist
appName: DeepSeek Harness
bundleId: com.deepseek.harness
version: 0.1.1
arch: arm64 # arm64 | x64
identity: "" # Developer ID Application: …
nodeBin: "" # 复用本地 node 二进制 nodeVersion: "" nodeDistBase: https://nodejs.org/dist electronMirror: "" # 国内镜像加速 electronBuilderBinariesMirror: "" skipDmg: false keepStaging: false configPath: ~/.dsh/desktop-packager/config.json # 定制化状态文件

定制化状态文件(configPath)

{
"design": {
"appName": "DeepSeek Harness",
"logoPath": "/absolute/path/to/logo.svg",
"themeColor": "#4D6BFE",
"backgroundColor": "#0f1115"
},
"selectedPlugins": ["@deepseek-ai/dsh-web-app"],
"selectedModels": [{ "provider": "deepseek", "model": "deepseek-chat" }],
"users": [
{ "username": "admin", "passwordHash": "$scrypt$$", "permissions": ["settings:view", "settings:edit"] }
]
}

密码只保存加盐 scrypt 哈希,绝不落明文。

权限模型

权限键为 ::settings(面板整体)、settings-design、 settings-plugins、settings-models、settings-permissions,每个都有 :view 与 :edit。
打包后桌面 app 的登录门校验账号密码后,把该用户权限通过 DSH_DESKTOP_USER / DSH_DESKTOP_PERMISSIONS 环境变量与 DSH_HOME/desktop-config.json 注入运行时,由 app 内的运行时插件据此对设置页做可见性 / 可编辑控制。

前置条件(打包机)

- macOS(默认 arm64 / Apple Silicon)。
- Node.js ≥ 22.19(跑打包脚本用)。
- 网络(下载 Node 运行时 / Electron / electron-builder),或提供 nodeBin 复用本地 Node。
- 一个已构建的 deepseek-harness checkout:apps/cli/lib/bin.js 与 apps/web/dist 必须存在。

目录结构

dsh-desktop-packager/
├── src/
│ ├── index.ts # Host 入口:desktopConfig 服务 + 打包工具 + HTTP 路由 │ ├── config.ts # 部署配置 schema(含 configPath) │ ├── desktop-config.ts # 定制化状态模型 + 服务(持久化/枚举/用户/哈希/HTTP) │ └── packager.ts # 子进程 runner(跑 build-macos.mjs) ├── client.js # 浏览器半侧 bundle(settings.section「桌面打包」4 Tab) ├── packaging/ │ ├── build-macos.mjs # 打包驱动(应用 logo/名称/主题/配置) │ └── electron/ │ ├── main.js # 登录门 + 主题注入 + 拉起 dsh web │ ├── preload.js # 登录窗 contextBridge │ └── entitlements.mac.plist ├── package.json # dual-face manifest(dsh.bundle + dsh.client) ├── cordis.patch.yml └── tsconfig.json

签名与公证(分发建议)

默认 ad-hoc 签名:本机可正常打开,发给别人时 Gatekeeper 会提示「无法验证开发者」。
彻底免除拦截需 Developer ID 签名 + 公证,详见 packaging/README.md。
预览分享卡片长什么样 ↓Olympianz/dsh-desktop-packager
分享 · DeepSeek Harness Hub
Olympianz/dsh-desktop-packager

把已构建项目封装成可分发 macOS 应用与 dmg 安装包

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