From db1d900f6dfcb79f86f23d575ee03e87a8f2ea22 Mon Sep 17 00:00:00 2001 From: kingwl Date: Wed, 30 Sep 2026 19:51:06 +0800 Subject: [PATCH] fix: omit ConPTY sources from non-Windows Desktop packages --- apps/desktop/README.i18n.yaml | 4 +- apps/desktop/README.md | 2 +- apps/desktop/README.zh.md | 2 +- apps/desktop/scripts/runtime-file-policy.ts | 7 ++- .../desktop/tests/runtime-file-policy.spec.ts | 56 +++++++++++++++++++ 5 files changed, 65 insertions(+), 6 deletions(-) diff --git a/apps/desktop/README.i18n.yaml b/apps/desktop/README.i18n.yaml index 6f4cf94723..dfcdd5dfdb 100644 --- a/apps/desktop/README.i18n.yaml +++ b/apps/desktop/README.i18n.yaml @@ -45,8 +45,8 @@ en: f461b255133384cc zh: 2b9db718f22fdd04 /deepseek-harness-desktop/package/runtime-file-selection: - en: f0e345fd89868803 - zh: fd2c775ad23fa2f0 + en: 8554894a83bceee8 + zh: c9dfe823c06abc9e /deepseek-harness-desktop/package/upload-updates: en: 72073b2e72590067 zh: 63b1f9790395c37d diff --git a/apps/desktop/README.md b/apps/desktop/README.md index 13e742edcb..ca17f2acea 100644 --- a/apps/desktop/README.md +++ b/apps/desktop/README.md @@ -237,7 +237,7 @@ Each target owns its packed package inputs, prepared runtime, package set, dsh t ### Runtime file selection -Desktop packs workspace packages locally and installs external dependencies through the target's bundled Node and pnpm. [Desktop's file policy](scripts/runtime-file-policy.ts) then filters the immutable `resources/app.asar/dsh/node_modules` copy before signing and integrity sealing. It omits TypeScript declarations, recognized JavaScript/CSS/TypeScript source maps, TypeScript build caches, Domino's test directory, selected native compiler outputs, and node-pty prebuilds for other platforms. It preserves runtime JavaScript, native modules and their DLL/EXE helpers, WASM, unknown assets, licenses, and notices. Dependency manifests pass through electron-builder's metadata cleanup before integrity sealing, so archiving preserves their recorded bytes. The policy does not alter npm tarballs, the bundled package manager, or user-installed plugin files. +Desktop packs workspace packages locally and installs external dependencies through the target's bundled Node and pnpm. [Desktop's file policy](scripts/runtime-file-policy.ts) then filters the immutable `resources/app.asar/dsh/node_modules` copy before signing and integrity sealing. It omits TypeScript declarations, recognized JavaScript/CSS/TypeScript source maps, TypeScript build caches, Domino's test directory, selected native compiler outputs, and node-pty prebuilds for other platforms. Non-Windows targets also omit `node-pty/third_party/conpty`; Windows targets retain that directory. It preserves runtime JavaScript, target native modules and their DLL/EXE helpers, WASM, unknown assets, licenses, and notices. Dependency manifests pass through electron-builder's metadata cleanup before integrity sealing, so archiving preserves their recorded bytes. The policy does not alter npm tarballs, the bundled package manager, or user-installed plugin files. The [Office conversion provider](../../packages/document/office-to-pdf/README.md) carries the target’s declared native engine, or WASM when the kit declares no matching native target. Preparation rejects a missing target engine before packaging. The complete Office dependencies, including CLI, JavaScript libraries, and the selected engine’s executables, data, licenses, and notices, are unpacked under `resources/app.asar.unpacked/dsh/node_modules/`. The Desktop Host resolves engine manifests to these physical directories and supplies loaded skills with absolute standalone Node and unpacked CLI paths. Node lives in `resources/runtime/primary-runtime/dependencies/node/bin/`; the CLI is the unpacked `@deepseek-ai/libreoffice-kit/lib/cli.js`. On macOS, the native helper receives the JIT entitlement required by [LibreOffice’s UNO bridge](https://github.com/LibreOffice/core/blob/master/sysui/desktop/macosx/hardened_runtime.xcent.in). diff --git a/apps/desktop/README.zh.md b/apps/desktop/README.zh.md index dc3283bb00..18dbf5d92f 100644 --- a/apps/desktop/README.zh.md +++ b/apps/desktop/README.zh.md @@ -239,7 +239,7 @@ macOS arm64 命令要求 Apple Silicon。macOS x64 命令可以在 Intel macOS ### 运行时文件筛选 -Desktop 在本地打包工作区包,并通过目标捆绑的 Node 和 pnpm 安装外部依赖。[Desktop 文件策略](scripts/runtime-file-policy.ts)随后在签名和完整性封装前过滤不可变的 `resources/app.asar/dsh/node_modules` 副本。它排除 TypeScript 声明、已识别的 JavaScript/CSS/TypeScript source map、TypeScript 构建缓存、Domino 测试目录、选定的原生编译器输出和其他平台的 node-pty 预构建文件。它保留运行时 JavaScript、原生模块及其 DLL/EXE 辅助文件、WASM、未知资源、许可证和 notices。依赖清单在完整性封装前经过 electron-builder 的元数据清理,确保归档保持已记录的字节。该策略不修改 npm tarball、捆绑的包管理器或用户安装的插件文件。 +Desktop 在本地打包工作区包,并通过目标捆绑的 Node 和 pnpm 安装外部依赖。[Desktop 文件策略](scripts/runtime-file-policy.ts)随后在签名和完整性封装前过滤不可变的 `resources/app.asar/dsh/node_modules` 副本。它排除 TypeScript 声明、已识别的 JavaScript/CSS/TypeScript source map、TypeScript 构建缓存、Domino 测试目录、选定的原生编译器输出和其他平台的 node-pty 预构建文件。非 Windows 目标还会排除 `node-pty/third_party/conpty`;Windows 目标保留该目录。它保留运行时 JavaScript、目标平台的原生模块及其 DLL/EXE 辅助文件、WASM、未知资源、许可证和 notices。依赖清单在完整性封装前经过 electron-builder 的元数据清理,确保归档保持已记录的字节。该策略不修改 npm tarball、捆绑的包管理器或用户安装的插件文件。 [Office 转换提供方](../../packages/document/office-to-pdf/README.zh.md)携带目标已声明的原生引擎;kit 未声明匹配原生目标时携带 WASM 引擎。准备阶段在打包前拒绝缺少目标引擎的情况。完整 Office 依赖(CLI、JavaScript 库和选定引擎的可执行文件、数据、许可证及 notices)解包到 `resources/app.asar.unpacked/dsh/node_modules/` 下。Desktop Host 将引擎清单解析到这些物理目录,并向加载的技能提供独立 Node 和解包后 CLI 的绝对路径。Node 位于 `resources/runtime/primary-runtime/dependencies/node/bin/`;CLI 位于解包后的 `@deepseek-ai/libreoffice-kit/lib/cli.js`。macOS 上的原生辅助程序获得 [LibreOffice UNO 桥](https://github.com/LibreOffice/core/blob/master/sysui/desktop/macosx/hardened_runtime.xcent.in)所需的 JIT entitlement。 diff --git a/apps/desktop/scripts/runtime-file-policy.ts b/apps/desktop/scripts/runtime-file-policy.ts index 1bc16b2a1c..a292e7d80f 100644 --- a/apps/desktop/scripts/runtime-file-policy.ts +++ b/apps/desktop/scripts/runtime-file-policy.ts @@ -1,7 +1,7 @@ /** Desktop-only omissions from already installed production npm packages. */ /** - * Identify build and diagnostic files omitted from the immutable Desktop runtime. + * Identify build, diagnostic and other-platform files omitted from the immutable Desktop runtime. * Unrecognized assets and target runtime binaries are retained. Paths name the copied * node_modules tree, including nested package containers. * @param path - Path relative to the production node_modules directory. @@ -16,7 +16,7 @@ export function desktopRuntimeFileExclusion( if (parts.some(part => ['.bin', '.pnpm', '.modules.yaml', '.pnpm-workspace-state-v1.json'].includes(part))) { return 'package-manager metadata' } - const file = parts.at(-1) ?? '' + const file = parts.at(-1)! if (/\.(?:[cm]?[jt]s|css)\.map$/u.test(file)) return 'source map' if (/\.d\.[cm]?ts$/u.test(file)) return 'TypeScript declaration' if (/\.tsbuildinfo$/u.test(file)) return 'TypeScript build cache' @@ -34,6 +34,9 @@ export function desktopRuntimeFileExclusion( return 'fs-ext build configuration' } if (name === '@mixmark-io/domino' && (entry === 'test' || entry.startsWith('test/'))) return 'Domino test fixtures' + if (name === 'node-pty' && target.platform !== 'win32' && /^third_party\/conpty(?:\/|$)/u.test(entry)) { + return 'node-pty Windows ConPTY sources' + } if (name === 'node-pty' && entry.startsWith('prebuilds/')) { const platform = packageParts[nameParts + 1] if (platform !== undefined && platform !== `${target.platform}-${target.arch}`) return 'node-pty other platform' diff --git a/apps/desktop/tests/runtime-file-policy.spec.ts b/apps/desktop/tests/runtime-file-policy.spec.ts index 76f5c8d95e..a0b392f41e 100644 --- a/apps/desktop/tests/runtime-file-policy.spec.ts +++ b/apps/desktop/tests/runtime-file-policy.spec.ts @@ -85,6 +85,62 @@ it('retains native prebuilds for the selected macOS architecture', () => { expect(desktopRuntimeFileExclusion('node-pty/prebuilds/win32-x64/conpty.node', mac, 'darwin-arm64')).toBeDefined() }) +it.each([ + ['darwin', 'arm64'], ['darwin', 'x64'], + ['linux', 'arm64'], ['linux', 'x64'], + ['win32', 'arm64'], ['win32', 'x64'], +] as const)('copies ConPTY sources only for Windows targets (%s/%s)', (platform, arch) => { + const root = mkdtempSync(join(tmpdir(), 'desktop-conpty-policy-')) + try { + const source = join(root, 'source') + const output = join(root, 'output') + const prefixes = ['', '@example/outer/node_modules/'] + const conpty = ['arm64', 'x64'].flatMap(cpu => ['conpty.dll', 'OpenConsole.exe'].map( + file => `node-pty/third_party/conpty/1.25.260303002/win10-${cpu}/${file}`, + )) + const native = platform === 'win32' + ? ['conpty.node', 'conpty_console_list.node', 'conpty/conpty.dll', 'conpty/OpenConsole.exe'] + : ['pty.node', 'spawn-helper'] + const retained = [ + ...native.map(file => `node-pty/prebuilds/${platform}-${arch}/${file}`), + ...native.map(file => `node-pty/build/Release/${file}`), + 'node-pty/lib/index.js', 'node-pty/LICENSE', + 'node-pty/third_party/conpty-notes/README.md', 'node-pty/third_party/conpty.dll', + 'other/third_party/conpty/conpty.dll', + ] + for (const prefix of prefixes) { + for (const path of [...conpty, ...retained]) { + mkdirSync(join(source, prefix, path, '..'), { recursive: true }) + writeFileSync(join(source, prefix, path), `payload:${prefix}${path}`) + } + } + cpSync(source, output, { + recursive: true, dereference: true, + filter: (path) => { + const entry = relative(source, path) + return desktopRuntimeFileExclusion( + platform === 'win32' ? entry.replaceAll('/', '\\') : entry, + { platform, arch }, `${platform}-${arch}`, + ) === undefined + }, + }) + for (const prefix of prefixes) { + expect(existsSync(join(output, prefix, 'node-pty/third_party/conpty'))).toBe(platform === 'win32') + for (const path of conpty) { + expect(existsSync(join(output, prefix, path)), `${prefix}${path}`).toBe(platform === 'win32') + } + for (const path of [...retained, ...(platform === 'win32' ? conpty : [])]) { + expect(readFileSync(join(output, prefix, path), 'utf8')).toBe(`payload:${prefix}${path}`) + } + for (const path of conpty) { + expect(readFileSync(join(source, prefix, path), 'utf8')).toBe(`payload:${prefix}${path}`) + } + } + } finally { + rmSync(root, { recursive: true, force: true }) + } +}) + it.each([ ['linux', 'x64', 'linux-x64'], ['linux', 'arm64', 'wasm'],