4 Commits

Author SHA1 Message Date
Pine 7ebc1c9b36 Merge branch 'feat/desktop-electron-app'
CI / python runtime / release-shaped Linux x64 (push) Has been skipped
CI / node 24 / static (push) Has been cancelled
CI / node 24 / coverage (push) Has been cancelled
CI / node 24 / snapshots and artifacts (push) Has been cancelled
CI / node 22.19 (push) Has been cancelled
CI / node 26 (push) Has been cancelled
CI / python 3.10 / keyless SDK (push) Has been cancelled
CI / windows node 24 / wine blocking (push) Has been cancelled
CI / wine apt cache (push) Has been cancelled
CI / windows node 24 / native complete (push) Has been cancelled
CI / serial / linux (push) Has been cancelled
CI / serial / linux (self-hosted standby) (push) Has been cancelled
CI / serial / macos (push) Has been cancelled
CI / serial / windows (self-hosted standby) (push) Has been cancelled
CI / larger-runner-benchmark (16, linux, dsh-ubuntu-24-04-16core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (16, windows, dsh-windows-2025-16core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (32, linux, dsh-ubuntu-24-04-32core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (32, windows, dsh-windows-2025-32core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (4, linux, dsh-ubuntu-24-04-4core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (4, windows, dsh-windows-2025-4core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (64, linux, dsh-ubuntu-24-04-64core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (64, windows, dsh-windows-2025-64core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (8, linux, dsh-ubuntu-24-04-8core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (8, windows, dsh-windows-2025-8core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (96, linux, dsh-ubuntu-24-04-96core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (96, windows, dsh-windows-2025-96core, production-site) (push) Has been cancelled
CI / consolidated-runner-benchmark (16, linux, dsh-ubuntu-24-04-16core, 16) (push) Has been cancelled
CI / consolidated-runner-benchmark (16, windows, dsh-windows-2025-16core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (32, linux, dsh-ubuntu-24-04-32core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (32, windows, dsh-windows-2025-32core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (4, linux, dsh-ubuntu-24-04-4core, 4) (push) Has been cancelled
CI / consolidated-runner-benchmark (4, windows, dsh-windows-2025-4core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (64, linux, dsh-ubuntu-24-04-64core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (64, windows, dsh-windows-2025-64core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (8, linux, dsh-ubuntu-24-04-8core, 8) (push) Has been cancelled
CI / consolidated-runner-benchmark (8, windows, dsh-windows-2025-8core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (96, linux, dsh-ubuntu-24-04-96core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (96, windows, dsh-windows-2025-96core, 2) (push) Has been cancelled
CI / all checks passed (push) Has been cancelled
E2E (real DeepSeek API) / e2e (push) Has been cancelled
Release (vendor) / Pack npm tarballs (push) Has been cancelled
Release (vendor) / Publish to npm (push) Has been cancelled
Release (dsh) / Pack npm tarballs (push) Has been cancelled
Release (dsh) / Publish to npm (push) Has been cancelled
Sandbox / sandbox e2e (seatbelt, macos-latest) (push) Has been cancelled
Sandbox / sandbox e2e (landlock, ubuntu-24.04) (push) Has been cancelled
Sandbox / sandbox e2e (landlock, ubuntu-24.04-arm) (push) Has been cancelled
Sandbox / sandbox e2e (bwrap, ubuntu-latest) (push) Has been cancelled
Deploy documentation / build (push) Has been cancelled
Landlock Run / Matrix (push) Has been cancelled
Landlock Run / darwin (no platform package — degradation proof) (push) Has been cancelled
Deploy documentation / deploy (push) Has been cancelled
Landlock Run / ${{ matrix.platform }} (push) Has been cancelled
# Conflicts:
#	apps/desktop/scripts/build-harness.mjs
2026-08-16 10:22:09 +08:00
Pine 9855358304 feat(build-harness): implement node_modules pruning and add exclusion configuration
CI / python runtime / release-shaped Linux x64 (push) Has been skipped
CI / node 24 / static (push) Has been cancelled
CI / node 24 / coverage (push) Has been cancelled
CI / node 24 / snapshots and artifacts (push) Has been cancelled
CI / node 22.19 (push) Has been cancelled
CI / node 26 (push) Has been cancelled
CI / python 3.10 / keyless SDK (push) Has been cancelled
CI / windows node 24 / wine blocking (push) Has been cancelled
CI / wine apt cache (push) Has been cancelled
CI / windows node 24 / native complete (push) Has been cancelled
CI / serial / linux (push) Has been cancelled
CI / serial / linux (self-hosted standby) (push) Has been cancelled
CI / serial / macos (push) Has been cancelled
CI / serial / windows (self-hosted standby) (push) Has been cancelled
CI / larger-runner-benchmark (16, linux, dsh-ubuntu-24-04-16core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (16, windows, dsh-windows-2025-16core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (32, linux, dsh-ubuntu-24-04-32core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (32, windows, dsh-windows-2025-32core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (4, linux, dsh-ubuntu-24-04-4core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (4, windows, dsh-windows-2025-4core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (64, linux, dsh-ubuntu-24-04-64core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (64, windows, dsh-windows-2025-64core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (8, linux, dsh-ubuntu-24-04-8core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (8, windows, dsh-windows-2025-8core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (96, linux, dsh-ubuntu-24-04-96core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (96, windows, dsh-windows-2025-96core, production-site) (push) Has been cancelled
CI / consolidated-runner-benchmark (16, linux, dsh-ubuntu-24-04-16core, 16) (push) Has been cancelled
CI / consolidated-runner-benchmark (16, windows, dsh-windows-2025-16core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (32, linux, dsh-ubuntu-24-04-32core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (32, windows, dsh-windows-2025-32core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (4, linux, dsh-ubuntu-24-04-4core, 4) (push) Has been cancelled
CI / consolidated-runner-benchmark (4, windows, dsh-windows-2025-4core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (64, linux, dsh-ubuntu-24-04-64core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (64, windows, dsh-windows-2025-64core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (8, linux, dsh-ubuntu-24-04-8core, 8) (push) Has been cancelled
CI / consolidated-runner-benchmark (8, windows, dsh-windows-2025-8core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (96, linux, dsh-ubuntu-24-04-96core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (96, windows, dsh-windows-2025-96core, 2) (push) Has been cancelled
CI / all checks passed (push) Has been cancelled
E2E (real DeepSeek API) / e2e (push) Has been cancelled
Release (vendor) / Pack npm tarballs (push) Has been cancelled
Release (vendor) / Publish to npm (push) Has been cancelled
Release (dsh) / Pack npm tarballs (push) Has been cancelled
Release (dsh) / Publish to npm (push) Has been cancelled
Sandbox / sandbox e2e (seatbelt, macos-latest) (push) Has been cancelled
Sandbox / sandbox e2e (landlock, ubuntu-24.04) (push) Has been cancelled
Sandbox / sandbox e2e (landlock, ubuntu-24.04-arm) (push) Has been cancelled
Sandbox / sandbox e2e (bwrap, ubuntu-latest) (push) Has been cancelled
2026-08-15 20:34:38 +08:00
Pine 89eef567e1 fix(build-harness): improve POSIX launcher path resolution for dsh script
CI / python runtime / release-shaped Linux x64 (push) Has been skipped
CI / node 24 / static (push) Has been cancelled
CI / node 24 / coverage (push) Has been cancelled
CI / node 24 / snapshots and artifacts (push) Has been cancelled
CI / node 22.19 (push) Has been cancelled
CI / node 26 (push) Has been cancelled
CI / python 3.10 / keyless SDK (push) Has been cancelled
CI / windows node 24 / wine blocking (push) Has been cancelled
CI / wine apt cache (push) Has been cancelled
CI / windows node 24 / native complete (push) Has been cancelled
CI / serial / linux (push) Has been cancelled
CI / serial / linux (self-hosted standby) (push) Has been cancelled
CI / serial / macos (push) Has been cancelled
CI / serial / windows (self-hosted standby) (push) Has been cancelled
CI / larger-runner-benchmark (16, linux, dsh-ubuntu-24-04-16core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (16, windows, dsh-windows-2025-16core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (32, linux, dsh-ubuntu-24-04-32core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (32, windows, dsh-windows-2025-32core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (4, linux, dsh-ubuntu-24-04-4core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (4, windows, dsh-windows-2025-4core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (64, linux, dsh-ubuntu-24-04-64core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (64, windows, dsh-windows-2025-64core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (8, linux, dsh-ubuntu-24-04-8core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (8, windows, dsh-windows-2025-8core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (96, linux, dsh-ubuntu-24-04-96core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (96, windows, dsh-windows-2025-96core, production-site) (push) Has been cancelled
CI / consolidated-runner-benchmark (16, linux, dsh-ubuntu-24-04-16core, 16) (push) Has been cancelled
CI / consolidated-runner-benchmark (16, windows, dsh-windows-2025-16core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (32, linux, dsh-ubuntu-24-04-32core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (32, windows, dsh-windows-2025-32core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (4, linux, dsh-ubuntu-24-04-4core, 4) (push) Has been cancelled
CI / consolidated-runner-benchmark (4, windows, dsh-windows-2025-4core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (64, linux, dsh-ubuntu-24-04-64core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (64, windows, dsh-windows-2025-64core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (8, linux, dsh-ubuntu-24-04-8core, 8) (push) Has been cancelled
CI / consolidated-runner-benchmark (8, windows, dsh-windows-2025-8core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (96, linux, dsh-ubuntu-24-04-96core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (96, windows, dsh-windows-2025-96core, 2) (push) Has been cancelled
CI / all checks passed (push) Has been cancelled
E2E (real DeepSeek API) / e2e (push) Has been cancelled
Release (vendor) / Pack npm tarballs (push) Has been cancelled
Release (vendor) / Publish to npm (push) Has been cancelled
Release (dsh) / Pack npm tarballs (push) Has been cancelled
Release (dsh) / Publish to npm (push) Has been cancelled
Sandbox / sandbox e2e (seatbelt, macos-latest) (push) Has been cancelled
Sandbox / sandbox e2e (landlock, ubuntu-24.04) (push) Has been cancelled
Sandbox / sandbox e2e (landlock, ubuntu-24.04-arm) (push) Has been cancelled
Sandbox / sandbox e2e (bwrap, ubuntu-latest) (push) Has been cancelled
2026-08-15 19:11:19 +08:00
Pine 6c820772a3 docs(desktop): add project README, MIT license, and license field
CI / python runtime / release-shaped Linux x64 (push) Has been skipped
CI / node 24 / static (push) Has been cancelled
CI / node 24 / coverage (push) Has been cancelled
CI / node 24 / snapshots and artifacts (push) Has been cancelled
CI / node 22.19 (push) Has been cancelled
CI / node 26 (push) Has been cancelled
CI / python 3.10 / keyless SDK (push) Has been cancelled
CI / windows node 24 / wine blocking (push) Has been cancelled
CI / wine apt cache (push) Has been cancelled
CI / windows node 24 / native complete (push) Has been cancelled
CI / serial / linux (push) Has been cancelled
CI / serial / linux (self-hosted standby) (push) Has been cancelled
CI / serial / macos (push) Has been cancelled
CI / serial / windows (self-hosted standby) (push) Has been cancelled
CI / larger-runner-benchmark (16, linux, dsh-ubuntu-24-04-16core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (16, windows, dsh-windows-2025-16core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (32, linux, dsh-ubuntu-24-04-32core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (32, windows, dsh-windows-2025-32core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (4, linux, dsh-ubuntu-24-04-4core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (4, windows, dsh-windows-2025-4core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (64, linux, dsh-ubuntu-24-04-64core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (64, windows, dsh-windows-2025-64core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (8, linux, dsh-ubuntu-24-04-8core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (8, windows, dsh-windows-2025-8core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (96, linux, dsh-ubuntu-24-04-96core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (96, windows, dsh-windows-2025-96core, production-site) (push) Has been cancelled
CI / consolidated-runner-benchmark (16, linux, dsh-ubuntu-24-04-16core, 16) (push) Has been cancelled
CI / consolidated-runner-benchmark (16, windows, dsh-windows-2025-16core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (32, linux, dsh-ubuntu-24-04-32core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (32, windows, dsh-windows-2025-32core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (4, linux, dsh-ubuntu-24-04-4core, 4) (push) Has been cancelled
CI / consolidated-runner-benchmark (4, windows, dsh-windows-2025-4core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (64, linux, dsh-ubuntu-24-04-64core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (64, windows, dsh-windows-2025-64core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (8, linux, dsh-ubuntu-24-04-8core, 8) (push) Has been cancelled
CI / consolidated-runner-benchmark (8, windows, dsh-windows-2025-8core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (96, linux, dsh-ubuntu-24-04-96core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (96, windows, dsh-windows-2025-96core, 2) (push) Has been cancelled
CI / all checks passed (push) Has been cancelled
E2E (real DeepSeek API) / e2e (push) Has been cancelled
Release (vendor) / Pack npm tarballs (push) Has been cancelled
Release (vendor) / Publish to npm (push) Has been cancelled
Release (dsh) / Pack npm tarballs (push) Has been cancelled
Release (dsh) / Publish to npm (push) Has been cancelled
Sandbox / sandbox e2e (seatbelt, macos-latest) (push) Has been cancelled
Sandbox / sandbox e2e (landlock, ubuntu-24.04) (push) Has been cancelled
Sandbox / sandbox e2e (landlock, ubuntu-24.04-arm) (push) Has been cancelled
Sandbox / sandbox e2e (bwrap, ubuntu-latest) (push) Has been cancelled
- Add a normative header to apps/desktop/README.md: project overview, license,
  and a modification note (a community derivative of deepseek-harness).
- Add apps/desktop/LICENSE (MIT) preserving the original DeepSeek copyright.
- Declare "license": "MIT" in apps/desktop/package.json.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-15 18:12:07 +08:00
6 changed files with 326 additions and 7 deletions
+26
View File
@@ -0,0 +1,26 @@
MIT License
本项目(DeepSeek Harness Desktop,即 deepseek-harness-desktop)是基于开源项目
deepseek-harness(https://github.com/deepseek-ai/deepseek-harness)的衍生作品,
同样以 MIT 协议开源,并保留原项目版权声明。
Copyright (c) 2026 PineSound
Original project deepseek-harness Copyright (c) 2026 DeepSeek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+30
View File
@@ -1,5 +1,35 @@
# @deepseek-ai/dsh-desktop # @deepseek-ai/dsh-desktop
> **DeepSeek Harness Desktop(deepseek-harness-desktop)** — 由 PineSound 基于开源项目
> [deepseek-harness](https://github.com/deepseek-ai/deepseek-harness) 构建的**社区版**桌面应用。
> 为官方 CLI/Web 形态的 harness 提供原生桌面外壳:双击即用、零外部依赖,发布 macOS 与 Windows 安装包。
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
- **基于**: [deepseek-ai/deepseek-harness](https://github.com/deepseek-ai/deepseek-harness)(MIT)
- **源码**: https://github.com/PineKings/deepseek-harness-desktop
- **发布站**: https://deepseek.pinesound.cn/
## 开源协议(License)
本项目基于 MIT 授权的 deepseek-harness 构建,同样以 **MIT 协议**开源(见 [LICENSE](./LICENSE)),并保留原项目版权声明。
## 修改说明(Modification Note)
本项目是 [deepseek-harness](https://github.com/deepseek-ai/deepseek-harness) 的**衍生作品**,在**完全保留原项目后端与核心 harness 行为**的基础上,面向最终用户增加了以下能力:
- **桌面外壳**:基于 Electron 的原生桌面窗口,双击即用、无需浏览器标签页;安装包内置完整运行时与 Node,目标机器零外部依赖;发布 macOS(`.dmg`)与 Windows(`.nsis`)安装包。
- **插件系统**:图形化的插件安装 / 开关、黑白名单守卫、内置 pnpm 与多镜像回退、免重启实时生效。
- **图像识别**:通过 OpenAI 兼容接口(DashScope)补齐视觉能力,密钥 / 地址 / 模型与对话主模型完全独立。
- **更新机制**:读取线上 `releases.json` 自动检测更新,桌面与发布站同渠道。
- **发布站**:配套的**非官方社区发布站**(deepseek.pinesound.cn),复刻并重构官方页面为中文单语言纯静态站点,**非 DeepSeek 官方站点**,与 DeepSeek 无隶属或赞助关系,商标归原权利方所有。
> 说明:本项目的后端与核心 harness 行为**完全保留原项目**;桌面外壳只是薄包装,原生 ABI 兼容,无需为 Electron 重编译。
---
## 技术说明(Technical Reference)
Electron desktop shell for DeepSeek Harness. The Electron main process is a thin Electron desktop shell for DeepSeek Harness. The Electron main process is a thin
wrapper: it spawns the real `dsh` CLI running the `web` profile on loopback (an wrapper: it spawns the real `dsh` CLI running the `web` profile on loopback (an
OS-assigned port), parses the readiness URL line the profile prints OS-assigned port), parses the readiness URL line the profile prints
+1
View File
@@ -4,6 +4,7 @@
"version": "0.1.0-rc.5", "version": "0.1.0-rc.5",
"private": true, "private": true,
"type": "module", "type": "module",
"license": "MIT",
"author": "PineSound", "author": "PineSound",
"main": "lib/types/main.js", "main": "lib/types/main.js",
"files": [ "files": [
+175 -7
View File
@@ -10,15 +10,22 @@
* native, apps/cli, apps/web — plus the platform Node binary, into * native, apps/cli, apps/web — plus the platform Node binary, into
* `build/harness`, which electron-builder ships as `extraResources`. * `build/harness`, which electron-builder ships as `extraResources`.
* *
* Run from the repository root (`pnpm --filter @deepseek-ai/dsh-desktop run * The packaged harness must stay fully self-contained: the app is not a mere
* build:harness`; the root `desktop:pack` script runs it automatically). The * launcher, it must let users in the most barren environments use dsh entirely
* copy is followed by a relink pass (harness-relink.mjs) that rewrites every * through the app. We therefore copy the whole workspace node_modules and then
* prune a curated, VERIFIED-safe exclusion list (see pruneNodeModules below and
* `scripts/harness-excludes.json`). Nothing is excluded unless it is provably
* outside the dsh runtime dependency closure.
*
* The copy is followed by a relink pass (harness-relink.mjs) that rewrites every
* Windows junction / absolute link into a RELATIVE in-tree symlink, so the * Windows junction / absolute link into a RELATIVE in-tree symlink, so the
* packaged harness no longer references the build machine's dev tree, and by a * packaged harness no longer references the build machine's dev tree, then by a
* self-containment check plus a `dsh --version` smoke test. * self-containment check and a `dsh --version` smoke test.
*
* Run from the repository root before `desktop:pack`.
*/ */
import { cpSync, chmodSync, existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs' import { cpSync, chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, rmSync, writeFileSync } from 'node:fs'
import { dirname, join, resolve } from 'node:path' import { dirname, join, resolve } from 'node:path'
import { fileURLToPath } from 'node:url' import { fileURLToPath } from 'node:url'
import { execFileSync } from 'node:child_process' import { execFileSync } from 'node:child_process'
@@ -71,6 +78,12 @@ for (const [src, dst] of extraDirs) {
cpSync(join(root, src), join(out, dst), { recursive: true }) cpSync(join(root, src), join(out, dst), { recursive: true })
} }
// Prune verified-safe build/packaging tooling out of the copied node_modules.
// This is the ONLY sanctioned way to shrink the self-contained runtime: every
// exclusion is validated against the dependency closure of the kept packages
// and refused if any kept package depends on it.
pruneNodeModules(join(out, 'node_modules'))
// Bundle the platform Node binary for the harness child. Windows needs a // Bundle the platform Node binary for the harness child. Windows needs a
// `.exe` extension so both the child spawn and the `dsh.cmd` shim can execute // `.exe` extension so both the child spawn and the `dsh.cmd` shim can execute
// it; POSIX uses a bare `node`. // it; POSIX uses a bare `node`.
@@ -97,6 +110,13 @@ try {
// Node/pnpm install is needed on the target. POSIX ships an executable `dsh` // Node/pnpm install is needed on the target. POSIX ships an executable `dsh`
// shell script; Windows ships a `dsh.cmd` shim (the desktop registers the // shell script; Windows ships a `dsh.cmd` shim (the desktop registers the
// harness directory on the user PATH on first launch). // harness directory on the user PATH on first launch).
//
// The POSIX launcher resolves its own real path through symlinks before looking
// up `bin/node` and `apps/cli/lib/bin.js`: the desktop registers `dsh` on PATH
// as a symlink into a bin dir, so a bare `dirname "$0"` would resolve to the
// link's directory (no siblings there). `readlink -f` is unavailable on macOS,
// so resolve portably with the readlink loop below. `dsh.cmd` needs no such
// handling because `%~dp0` already expands to the real script location.
if (process.platform === 'win32') { if (process.platform === 'win32') {
writeFileSync( writeFileSync(
join(out, 'dsh.cmd'), join(out, 'dsh.cmd'),
@@ -106,11 +126,159 @@ if (process.platform === 'win32') {
const dshLauncher = join(out, 'dsh') const dshLauncher = join(out, 'dsh')
writeFileSync( writeFileSync(
dshLauncher, dshLauncher,
'#!/bin/sh\nexec "$(dirname "$0")/bin/node" "$(dirname "$0")/apps/cli/lib/bin.js" "$@"\n', '#!/bin/sh\nSELF="$0"\nwhile [ -h "$SELF" ]; do\n DIR=$(cd "$(dirname "$SELF")" && pwd)\n LINK=$(readlink "$SELF")\n case "$LINK" in /*) SELF="$LINK";; *) SELF="$DIR/$LINK";; esac\ndone\nDIR=$(cd "$(dirname "$SELF")" && pwd)\nexec "$DIR/bin/node" "$DIR/apps/cli/lib/bin.js" "$@"\n',
) )
chmodSync(dshLauncher, 0o755) chmodSync(dshLauncher, 0o755)
} }
/**
* Remove the curated build/packaging-time packages from a copied pnpm
* `node_modules`. Safe by construction:
*
* 1. Reads `scripts/harness-excludes.json` — a visible, configurable list of
* package names + reasons (users add/remove entries without touching the
* copy logic).
* 2. For each exclusion, walks every KEPT `.pnpm/<pkg>@<ver>` entry and checks
* whether any kept package symlinks this name as a dependency. If so, the
* exclusion is REFUSED (throws) — the runtime must stay whole.
* 3. If safe, deletes the `.pnpm/<name>@*` entries, the top-level
* `node_modules/<name>` link, and any `.bin` shims that pointed into them,
* reporting bytes freed per entry and a total.
*/
function pruneNodeModules(nmDir) {
const cfgPath = join(root, 'apps/desktop/scripts/harness-excludes.json')
if (!existsSync(cfgPath)) return
const { excludes } = JSON.parse(readFileSync(cfgPath, 'utf8'))
if (!Array.isArray(excludes) || excludes.length === 0) return
// De-dupe by name so a duplicate config entry can't double-report or double-count.
const unique = new Map()
for (const e of excludes) if (e && e.name && !unique.has(e.name)) unique.set(e.name, e)
const list = [...unique.values()]
const pnpmDir = join(nmDir, '.pnpm')
if (!existsSync(pnpmDir)) return
const entries = readdirSync(pnpmDir)
// All packages that will be removed — used to ignore cross-excluded refs
// during the safety check (a dependency among two removed packages is fine).
const excludedSet = new Set(list.map((e) => e.name))
// Package name of a .pnpm entry. Entries look like "name@1.0.0" for a bare
// package or "@scope+name@1.0.0" for a scoped one, but peer-dependency
// suffixes add further "@"s (e.g. "dmg-builder@25.1.8_peer@25.1.8"). Parse
// from the FIRST "@" — the base name never contains one after the version
// separator — so peer suffixes can't leak into the parsed name.
const entryPkg = (entry) => {
const at = entry.indexOf('@')
if (at === -1) return entry
if (at === 0) {
const rest = entry.slice(1) // "@scope+name@ver..." -> "scope+name@ver..."
const sep = rest.indexOf('@')
const name = sep === -1 ? rest : rest.slice(0, sep)
return '@' + name.replace('+', '/')
}
return entry.slice(0, at)
}
let totalFreed = 0
for (const ex of list) {
const name = ex.name
if (excludedSet.size === 0) continue
// --- 2. Safety: does any KEPT package depend on this package? ---
const dependents = []
for (const entry of entries) {
const ep = entryPkg(entry)
if (ep === name || excludedSet.has(ep)) continue // itself or also excluded
const depDir = join(pnpmDir, entry, 'node_modules')
if (!existsSync(depDir)) continue
if (readdirSync(depDir).includes(name)) dependents.push(ep)
}
if (dependents.length > 0) {
throw new Error(
`[harness-excludes] REFUSING to exclude "${name}": kept packages depend on it ` +
`(${dependents.join(', ')}). The runtime must stay whole — remove it from ` +
`${cfgPath} or it is not actually safe to exclude.`,
)
}
// --- 3. Delete the .pnpm store entries for this package ---
const matching = entries.filter((e) => entryPkg(e) === name)
for (const m of matching) {
const p = join(pnpmDir, m)
const sz = dirSize(p)
rmSync(p, { recursive: true, force: true })
totalFreed += sz
console.log(` [exclude] ${name} — removed .pnpm/${m} (${fmtSize(sz)})`)
}
// --- 4. Delete the top-level link/dir (node_modules/@scope/name too) ---
const top = join(nmDir, ...name.split('/'))
if (existsSync(top)) {
const sz = dirSize(top)
rmSync(top, { recursive: true, force: true })
totalFreed += sz
console.log(` [exclude] ${name} — removed top-level link (${fmtSize(sz)})`)
}
// --- 5. Drop .bin shims that pointed into the removed store entries ---
removeBinShims(join(nmDir, '.bin'), matching, pnpmDir)
}
if (totalFreed > 0) {
console.log(
`\n[harness-excludes] freed ${fmtSize(totalFreed)} by excluding ` +
`${list.map((e) => e.name).join(', ')} (config: ${cfgPath})`,
)
}
}
/** Recursive size of a dir or link target. Symlinks are counted as 0 bytes and
* never followed (a pnpm store is full of links; following would loop). */
function dirSize(p) {
let st
try {
st = lstatSync(p)
} catch {
return 0
}
if (st.isSymbolicLink() || st.isFile()) return st.size
if (!st.isDirectory()) return 0
let total = 0
for (const child of readdirSync(p)) total += dirSize(join(p, child))
return total
}
/** Remove `.bin` shims whose resolved target lives inside any removed store
* entry, so no dangling executables remain. */
function removeBinShims(binDir, removedEntries, pnpmDir) {
if (!existsSync(binDir)) return
const removedStoreDirs = removedEntries.map((e) => join(pnpmDir, e))
for (const shim of readdirSync(binDir)) {
const p = join(binDir, shim)
let target
try {
target = readlinkSync(p)
} catch {
continue // not a symlink (e.g. a real .cmd file) — leave it
}
const resolved = resolve(binDir, target)
if (removedStoreDirs.some((d) => resolved.startsWith(d))) {
rmSync(p, { recursive: true, force: true })
}
}
}
/** Human-readable byte size, e.g. "553 MiB". */
function fmtSize(bytes) {
if (bytes >= 1024 ** 3) return `${(bytes / 1024 ** 3).toFixed(2)} GiB`
if (bytes >= 1024 ** 2) return `${(bytes / 1024 ** 2).toFixed(1)} MiB`
if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KiB`
return `${bytes} B`
}
console.log(`assembled self-contained harness at ${out}`) console.log(`assembled self-contained harness at ${out}`)
// Normalize every link to a relative in-tree target, then prove the tree no // Normalize every link to a relative in-tree target, then prove the tree no
@@ -0,0 +1,63 @@
{
"$schema": "harness-excludes.schema.json",
"version": 1,
"description": "Packages excluded from the packaged harness node_modules (.pnpm store). The app must stay fully self-contained — it is not a launcher, it lets users in the most barren environments use dsh entirely through the app. So an entry here is a HARD guarantee that the package is NOT part of the dsh runtime dependency closure: the build refuses to exclude anything a kept workspace package actually depends on (see build-harness.mjs pruneNodeModules). Add entries only after verifying the package is build-time / packaging-time tooling that the harness never imports at runtime.",
"excludes": [
{
"name": "electron",
"reason": "Electron shell runtime. electron-builder ships it separately into resources/app; the dsh harness runs under the bundled system-Node child and never imports Electron. Verified: only apps/desktop/package.json depends on it, only apps/desktop/lib/types/{main,preload}.js import it (those are the shell, already packaged)."
},
{
"name": "electron-builder",
"reason": "Packaging toolchain (group). The harness is pre-built and distributed, so nothing packages installers at runtime. The electron-builder toolchain is mutually interdependent, so it is excluded as a group; build-harness.mjs refuses any exclusion a kept package still depends on, which is how this group stays coherent."
},
{
"name": "app-builder-lib",
"reason": "electron-builder toolchain (group): app-builder-lib is packaging logic only, never imported by the dsh runtime."
},
{
"name": "builder-util",
"reason": "electron-builder toolchain (group): build utilities (spawns app-builder-bin, 7zip-bin). Build-time only."
},
{
"name": "builder-util-runtime",
"reason": "electron-builder toolchain (group): runtime helpers used only by the builder at package time, not by dsh."
},
{
"name": "dmg-builder",
"reason": "electron-builder toolchain (group): mac .dmg packaging; not present on this Windows build and never used at runtime."
},
{
"name": "electron-builder-squirrel-windows",
"reason": "electron-builder toolchain (group): Squirrel.Windows packaging; build-time only."
},
{
"name": "app-builder-bin",
"reason": "electron-builder's build binary (7za/signtool/app-builder). Build-time only, never imported by the dsh runtime. Depended on by builder-util, which is itself excluded in this group."
},
{
"name": "electron-publish",
"reason": "electron-builder toolchain (group): publishing (upload to GitHub etc.); never used by the dsh runtime."
},
{
"name": "7zip-bin",
"reason": "electron-builder toolchain (group): 7-zip binary used to compress installers at package time; not a dsh runtime dependency."
},
{
"name": "@electron/asar",
"reason": "electron-builder toolchain (group): asar archive packing; build-time only (the desktop app ships asar:false anyway)."
},
{
"name": "@electron/notarize",
"reason": "electron-builder toolchain (group): macOS notarization; build-time only, and irrelevant on this Windows build."
},
{
"name": "@electron/osx-sign",
"reason": "electron-builder toolchain (group): macOS code signing; build-time only."
},
{
"name": "@electron/universal",
"reason": "electron-builder toolchain (group): macOS universal binaries; build-time only."
}
]
}
@@ -0,0 +1,31 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://deepseek-harness.dev/schemas/harness-excludes.schema.json",
"title": "Harness dependency exclusion list",
"type": "object",
"additionalProperties": false,
"required": ["version", "description", "excludes"],
"properties": {
"version": { "type": "integer", "const": 1 },
"description": { "type": "string" },
"excludes": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["name", "reason"],
"properties": {
"name": {
"type": "string",
"description": "Package name to exclude, e.g. \"electron\" or \"@scope/name\". Matches pnpm store entries by unscoped/scoped base name.",
"minLength": 1
},
"reason": {
"type": "string",
"description": "Why this package is safely outside the dsh runtime dependency closure. Required so the exclusion stays auditable."
}
}
}
}
}
}