6 Commits

Author SHA1 Message Date
Pine 98b3221eec feat(plugin): add registry name handling and release age exclusion for pnpm workspace
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
E2E (real DeepSeek API) / e2e (push) Has been cancelled
Release (vendor) / Pack npm tarballs (push) Has been cancelled
Release (dsh) / Pack npm tarballs (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
CI / all checks passed (push) Has been cancelled
Release (vendor) / Publish to npm (push) Has been cancelled
Release (dsh) / Publish to npm (push) Has been cancelled
2026-08-16 16:11:22 +08:00
Pine 43c4b825e8 feat(build-harness): enhance robustness of harness removal and update exclusion list
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-16 10:53:01 +08:00
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
11 changed files with 522 additions and 57 deletions
+54 -1
View File
@@ -11,8 +11,9 @@
*/
import { spawnSync } from 'node:child_process'
import { existsSync } from 'node:fs'
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
import { join, resolve } from 'node:path'
import { dump, load } from 'js-yaml'
import {
DEFAULT_PROFILE_BUNDLES,
initProfile,
@@ -26,6 +27,52 @@ import { INSTALL_ANCHOR } from './profile-boot.ts'
const NAME = 'dsh'
/**
* The bare package name when `spec` is a registry specifier, else undefined —
* the same shape the in-app install uses to decide registry participation.
* Everything pnpm treats as a non-registry source (paths, `file:`/`link:`/
* `github:`/`git+`, tarballs, http(s) repo URLs, `.git` markers) is not a
* registry spec.
* @param spec - the pnpm specifier verbatim.
*/
export function registryNameOf(spec: string): string | undefined {
const trimmed = spec.trim()
if (trimmed.length === 0) return undefined
if (/^(?:file:|link:|github:|gitlab:|bitbucket:|git\+|git@)/.test(trimmed)) return undefined
if (/^(?:\.{1,2}|~|[/\\])/.test(trimmed) || /^[a-zA-Z]:[\\/]/.test(trimmed)) return undefined
if (/\.(?:tgz|tar\.gz)(?:[?#]|$)/.test(trimmed)) return undefined
if (/^https?:\/\//.test(trimmed) && trimmed.replace(/^https?:\/\//, '').split('/').length > 1) return undefined
if (/\.git(?:[#@]|$)/.test(trimmed)) return undefined
return trimmed.replace(/@[^/@]+$/, '')
}
/**
* Exempt registry package names from pnpm's `minimumReleaseAge` check by writing
* the `minimumReleaseAgeExclude` setting into the profile's `pnpm-workspace.yaml`
* (honored by pnpm ≥10.16; an older pnpm ignores the key rather than aborting,
* unlike the `--minimum-release-age-exclude` CLI flag). This mirrors the in-app
* install so an external `dsh plugin add <name>` gets the same latest-version
* behavior as the desktop's install, not a stale age-blocked fallback.
* @param profileDir - the profile directory.
* @param names - the registry package names to exempt.
*/
export function writeReleaseAgeExclude(profileDir: string, names: readonly string[]): void {
const workspacePath = join(profileDir, 'pnpm-workspace.yaml')
let doc: Record<string, unknown>
try {
const parsed = load(readFileSync(workspacePath, 'utf8'))
doc = parsed !== null && typeof parsed === 'object'
? parsed as Record<string, unknown>
: {}
} catch {
doc = {}
}
const excluded = new Set<string>((doc.minimumReleaseAgeExclude ?? []) as string[])
for (const name of names) excluded.add(name)
if (excluded.size > 0) doc.minimumReleaseAgeExclude = [...excluded]
writeFileSync(workspacePath, dump(doc))
}
/**
* Rewrite relative filesystem specs against the user's invoking directory.
* pnpm runs with cwd = the profile directory, so a bare `.` or `../plugin`
@@ -66,6 +113,12 @@ export function runPlugin(profile: string, args: readonly string[]): number {
// its .cmd shim, which spawn() refuses without a shell since the
// CVE-2024-27980 hardening; the vendored path runs node against pnpm.cjs.
const anchored = args.map(argument => anchorPathSpec(argument, process.cwd()))
// An external `add <registry-name>` gets the same minimum-release-age exemption
// the in-app install applies, so a just-published plugin installs at latest.
if (args[0] === 'add') {
const registryNames = args.slice(1).map(registryNameOf).filter((name): name is string => name !== undefined)
if (registryNames.length > 0) writeReleaseAgeExclude(dir, registryNames)
}
const vendored = resolvePnpm(process.execPath)
const result = vendored === undefined
? spawnSync('pnpm', anchored, { cwd: dir, stdio: 'inherit', shell: process.platform === 'win32' })
+49
View File
@@ -0,0 +1,49 @@
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { load } from 'js-yaml'
import { registryNameOf, writeReleaseAgeExclude } from '../src/plugin.ts'
const dirs: string[] = []
function tempDir(): string {
const dir = mkdtempSync(join(tmpdir(), 'dsh-cli-plugin-'))
dirs.push(dir)
return dir
}
afterEach(() => { for (const dir of dirs.splice(0)) rmSync(dir, { recursive: true, force: true }) })
describe('registryNameOf', () => {
it('returns the bare name for a registry specifier', () => {
expect(registryNameOf('dsh-theme-plugin')).toBe('dsh-theme-plugin')
expect(registryNameOf('dsh-theme-plugin@latest')).toBe('dsh-theme-plugin')
expect(registryNameOf('@scope/pkg@1.2.0')).toBe('@scope/pkg')
})
it('returns undefined for non-registry sources', () => {
expect(registryNameOf('github:user/repo')).toBeUndefined()
expect(registryNameOf('git+https://github.com/user/repo.git')).toBeUndefined()
expect(registryNameOf('file:../plugin')).toBeUndefined()
expect(registryNameOf('./plugin')).toBeUndefined()
expect(registryNameOf('https://example.com/p.tgz')).toBeUndefined()
})
})
describe('writeReleaseAgeExclude', () => {
it('writes the exclusion into pnpm-workspace.yaml, preserving existing settings', () => {
const dir = tempDir()
writeFileSync(join(dir, 'pnpm-workspace.yaml'), 'packages:\n - .\nallowBuilds:\n node-pty: true\n')
writeReleaseAgeExclude(dir, ['dsh-theme-plugin'])
writeReleaseAgeExclude(dir, ['@scope/pkg'])
const doc = load(readFileSync(join(dir, 'pnpm-workspace.yaml'), 'utf8')) as Record<string, unknown>
expect(doc.minimumReleaseAgeExclude).toEqual(['dsh-theme-plugin', '@scope/pkg'])
expect((doc.allowBuilds as Record<string, unknown>)['node-pty']).toBe(true)
})
it('creates the file when absent', () => {
const dir = tempDir()
writeReleaseAgeExclude(dir, ['x'])
const doc = load(readFileSync(join(dir, 'pnpm-workspace.yaml'), 'utf8')) as Record<string, unknown>
expect(doc.minimumReleaseAgeExclude).toEqual(['x'])
})
})
+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 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
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
+1
View File
@@ -4,6 +4,7 @@
"version": "0.1.0-rc.5",
"private": true,
"type": "module",
"license": "MIT",
"author": "PineSound",
"main": "lib/types/main.js",
"files": [
+201 -8
View File
@@ -10,15 +10,22 @@
* native, apps/cli, apps/web — plus the platform Node binary, into
* `build/harness`, which electron-builder ships as `extraResources`.
*
* Run from the repository root (`pnpm --filter @deepseek-ai/dsh-desktop run
* build:harness`; the root `desktop:pack` script runs it automatically). The
* copy is followed by a relink pass (harness-relink.mjs) that rewrites every
* The packaged harness must stay fully self-contained: the app is not a mere
* launcher, it must let users in the most barren environments use dsh entirely
* 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
* packaged harness no longer references the build machine's dev tree, and by a
* self-containment check plus a `dsh --version` smoke test.
* packaged harness no longer references the build machine's dev tree, then by a
* 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 { fileURLToPath } from 'node:url'
import { execFileSync } from 'node:child_process'
@@ -60,7 +67,11 @@ for (const [label, path] of [
}
}
rmSync(out, { recursive: true, force: true })
// Recursively removing the previous (multi-GB, symlink-heavy) harness can hit
// ENOTEMPTY/EBUSY transiently — on Windows especially, and on macOS under load.
// Retry so the rebuild is robust; maxRetries/retryDelay are Node's built-in
// answer to exactly these codes.
rmSync(out, { recursive: true, force: true, maxRetries: 10, retryDelay: 250 })
mkdirSync(join(out, 'apps'), { recursive: true })
mkdirSync(join(out, 'bin'), { recursive: true })
@@ -71,6 +82,12 @@ for (const [src, dst] of extraDirs) {
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
// `.exe` extension so both the child spawn and the `dsh.cmd` shim can execute
// it; POSIX uses a bare `node`.
@@ -97,6 +114,13 @@ try {
// 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
// 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') {
writeFileSync(
join(out, 'dsh.cmd'),
@@ -106,11 +130,180 @@ if (process.platform === 'win32') {
const dshLauncher = join(out, 'dsh')
writeFileSync(
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)
}
/**
* 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. Delete the flat .pnpm/node_modules/<name> copy. cpSync dereferences
// links while copying, so an excluded package lands here as a REAL directory
// (e.g. electron → 837 MB) that steps 24 never touch; it was shipping anyway.
// This shares the same safety check in step 2 — no kept package depends on it.
// Probe with lstatSync (not existsSync): it inspects the link itself rather
// than following the target, so a dangling symlink/junction (its .pnpm store
// entry already removed in step 3) is still found and removed on Windows. ---
const flat = join(pnpmDir, 'node_modules', ...name.split('/'))
let flatStat
try {
flatStat = lstatSync(flat)
} catch {
flatStat = null
}
if (flatStat) {
const sz = flatStat.isSymbolicLink() || flatStat.isFile() ? flatStat.size : dirSize(flat)
rmSync(flat, { recursive: true, force: true })
totalFreed += sz
console.log(` [exclude] ${name} — removed flat .pnpm/node_modules copy (${fmtSize(sz)})`)
}
// --- 6. 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}`)
// Normalize every link to a relative in-tree target, then prove the tree no
@@ -0,0 +1,75 @@
{
"$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."
},
{
"name": "@electron/get",
"reason": "Orphan of the excluded electron toolchain: @electron/rebuild's download helper. No kept package imports it at runtime."
},
{
"name": "@electron/rebuild",
"reason": "Orphan of the excluded electron toolchain: rebuilds native addons for the Electron ABI, which this app never needs (harness runs under system-Node)."
},
{
"name": "@electron-internal/extract-zip",
"reason": "Orphan of the excluded electron-builder toolchain: internal zip extraction used only during packaging. 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."
}
}
}
}
}
}
+6 -2
View File
@@ -17,6 +17,7 @@ import {
runPnpmRemove,
uninstallBundle,
writeAllowBuilds,
writeReleaseAgeExclude,
} from './install.ts'
import {
fetchMarketplaceCatalog,
@@ -253,8 +254,12 @@ export class PluginInventoryGateway extends TypertRemoteService {
writeAllowBuilds(profileDir, spec.consentBuilds)
}
// A registry-name spec participates in the registry fallback loop and the
// minimum-release-age exemption; a git, tarball, or path spec runs once.
// minimum-release-age exemption; a git, tarball, or path spec runs once. The
// release-age exemption is written into pnpm-workspace.yaml (not passed as a
// CLI flag) so an older pnpm ignores the setting instead of aborting on an
// unknown option.
const registryName = registryPackageName(spec.spec)
if (registryName !== undefined) writeReleaseAgeExclude(profileDir, [registryName])
const result = registryName === undefined
? runPnpmInstall({
binName: 'dsh',
@@ -273,7 +278,6 @@ export class PluginInventoryGateway extends TypertRemoteService {
pnpmCjs: pnpm.pnpmCjs,
spec: spec.spec,
before,
minimumReleaseAgeExclude: registryName,
})
if (result.pendingBuilds !== undefined) {
return { ok: true, restartRequired: false, pendingBuilds: result.pendingBuilds }
+32 -9
View File
@@ -150,8 +150,6 @@ export interface PnpmInstallOptions {
readonly before: ProfileManifest
/** An npm registry to install from (`pnpm add --registry`); defaults to pnpm's configured one. */
readonly registry?: string
/** A bare registry package name to exempt from pnpm's minimum-release-age check. */
readonly minimumReleaseAgeExclude?: string
}
/**
@@ -164,15 +162,9 @@ export interface PnpmInstallOptions {
* @returns `pendingBuilds` when pnpm blocked build scripts, else an empty result.
*/
export function runPnpmInstall(options: PnpmInstallOptions): PnpmAddResult {
const { binName, profileDir, installAnchor, nodeBin, pnpmCjs, spec, before, registry, minimumReleaseAgeExclude } = options
const { binName, profileDir, installAnchor, nodeBin, pnpmCjs, spec, before, registry } = options
const args = ['add', spec]
if (registry !== undefined) args.push('--registry', registry)
// `--minimum-release-age-exclude` is understood only by pnpm ≥10.7. It is safe
// with the vendored pnpm (pinned 11.7); a PATH-pnpm fallback in a development
// checkout may be older and reject the option, so skip it there.
if (minimumReleaseAgeExclude !== undefined && nodeBin !== undefined) {
args.push(`--minimum-release-age-exclude=${minimumReleaseAgeExclude}`)
}
const result = nodeBin === undefined ? spawn(pnpmCjs, args, profileDir) : spawn(nodeBin, [pnpmCjs, ...args], profileDir)
if (result.exitCode !== 0) {
const pendingBuilds = parseBlockedBuilds(result.output)
@@ -264,6 +256,37 @@ export function writeAllowBuilds(profileDir: string, names: readonly string[]):
writeFileSync(workspacePath, dump(doc))
}
/**
* Exempt the given registry package names from pnpm's `minimumReleaseAge` check
* by writing the `minimumReleaseAgeExclude` setting into the profile's
* `pnpm-workspace.yaml`. Writing the config directly is the robust way to beat
* the release-age check: the setting is honored by pnpm ≥10.16 (the documented
* mechanism, which pnpm reads only from `pnpm-workspace.yaml`, not `.npmrc`),
* and — unlike the `--minimum-release-age-exclude` CLI flag — an unknown-key pnpm
* ignores it instead of aborting with `Unknown option`. This lets a plugin
* published minutes ago install at its latest version on whichever pnpm the
* runtime resolves. Existing workspace settings are preserved and the exclusion
* list is merged.
* @param profileDir - the writable profile directory.
* @param names - the registry package names to exempt from the release-age check.
*/
export function writeReleaseAgeExclude(profileDir: string, names: readonly string[]): void {
const workspacePath = join(profileDir, 'pnpm-workspace.yaml')
let doc: Record<string, unknown>
try {
const parsed = load(readFileSync(workspacePath, 'utf8'))
doc = parsed !== null && typeof parsed === 'object'
? parsed as Record<string, unknown>
: { ...PROFILE_WORKSPACE_BASE }
} catch {
doc = { ...PROFILE_WORKSPACE_BASE }
}
const excluded = new Set<string>((doc.minimumReleaseAgeExclude ?? []) as string[])
for (const name of names) excluded.add(name)
if (excluded.size > 0) doc.minimumReleaseAgeExclude = [...excluded]
writeFileSync(workspacePath, dump(doc))
}
/** Options for removing one plugin dependency. */
export interface PnpmRemoveOptions {
readonly binName: string
@@ -1,11 +1,13 @@
import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { load } from 'js-yaml'
import { readProfileManifest } from '@deepseek-ai/dsh-app-boot'
import {
composeOfflineBundle, INSTALL_REGISTRIES, parseBlockedBuilds, registryPackageName,
resolvePnpm, resolvePnpmCommand, runPnpmInstall, writeAllowBuilds,
writeReleaseAgeExclude,
runPnpmInstallWithRegistries, runPnpmRemove, uninstallBundle,
} from '../src/install.ts'
@@ -67,24 +69,6 @@ function makeBlockedBuildPnpm(dir: string): string {
return file
}
/**
* A self-executable fake pnpm (shebang + executable bit) that records its argv
* and exits 0 — simulates a `pnpm` command invoked directly off PATH, without a
* `node` prefix (as the PATH-pnpm fallback does).
*/
function makeExecutablePnpm(dir: string): string {
const file = join(dir, 'pnpm-path')
writeFileSync(file, [
'#!/usr/bin/env node',
"const fs = require('fs')",
'const args = process.argv.slice(2)',
'fs.writeFileSync(process.env.RECORD, JSON.stringify(args))',
'process.exit(0)',
].join('\n'))
chmodSync(file, 0o755)
return file
}
/**
* A fake pnpm that, for `remove <name>`, drops the named dependency from the
* profile's package.json (as pnpm does) and exits 0.
@@ -171,7 +155,7 @@ describe('runPnpmInstall', () => {
}
})
it('passes minimum-release-age-exclude for a registry name', () => {
it('never passes a release-age CLI flag (the exemption is a workspace setting, not a flag)', () => {
const dir = makeProfile()
const record = join(dir, 'record.json')
const pnpm = makeRecordingPnpm(dir)
@@ -181,32 +165,28 @@ describe('runPnpmInstall', () => {
runPnpmInstall({
binName: 'dsh', profileDir: dir, installAnchor: join(dir, 'package.json'),
nodeBin: process.execPath, pnpmCjs: pnpm, spec: 'x',
before: readProfileManifest('dsh', dir), minimumReleaseAgeExclude: 'x',
before: readProfileManifest('dsh', dir),
})
const args = JSON.parse(readFileSync(record, 'utf8')) as string[]
expect(args).toContain('--minimum-release-age-exclude=x')
expect(args).toEqual(['add', 'x'])
expect(args.some(arg => arg.includes('minimum-release-age'))).toBe(false)
} finally {
delete process.env.RECORD
delete process.env.EXIT
}
})
it('omits minimum-release-age-exclude for a PATH-pnpm fallback (may be an older pnpm)', () => {
it('writes the minimum-release-age exemption into pnpm-workspace.yaml, preserving existing settings', () => {
const dir = makeProfile()
const record = join(dir, 'record.json')
const pnpm = makeExecutablePnpm(dir)
process.env.RECORD = record
try {
runPnpmInstall({
binName: 'dsh', profileDir: dir, installAnchor: join(dir, 'package.json'),
nodeBin: undefined, pnpmCjs: pnpm, spec: 'x',
before: readProfileManifest('dsh', dir), minimumReleaseAgeExclude: 'x',
})
const args = JSON.parse(readFileSync(record, 'utf8')) as string[]
expect(args).toEqual(['add', 'x'])
} finally {
delete process.env.RECORD
}
const workspacePath = join(dir, 'pnpm-workspace.yaml')
writeFileSync(workspacePath, 'autoInstallPeers: false\nallowBuilds:\n node-pty: true\n')
writeReleaseAgeExclude(dir, ['dsh-theme-plugin'])
writeReleaseAgeExclude(dir, ['@scope/pkg'])
const doc = JSON.parse(JSON.stringify(load(readFileSync(workspacePath, 'utf8')))) as Record<string, unknown>
expect(doc.minimumReleaseAgeExclude).toEqual(['dsh-theme-plugin', '@scope/pkg'])
// Existing settings survive.
expect((doc.allowBuilds as Record<string, unknown>).node_pty ?? (doc.allowBuilds as Record<string, unknown>)['node-pty']).toBe(true)
expect(doc.autoInstallPeers).toBe(false)
})
it('returns pendingBuilds when pnpm blocks build scripts', () => {