Desktop primary runtime
Desktop 第一方 Runtime
Desktop agents need predictable Python data-processing libraries and an independent Node interpreter on machines without development environments. System interpreter selection must remain under user control.
English
Problem
Desktop agents need predictable Python data-processing libraries and an independent Node interpreter on machines without development environments. System interpreter selection must remain under user control.
Decision
Desktop ships Python, Node.js, pnpm, data-processing libraries and Office authoring libraries as one release-bound payload. The path-query tool installs the payload from application resources into the fixed Harness-home directory and returns absolute paths and the bundled Python distribution versions. The version report excludes packages added by users. It does not change PATH, environment variables or package-manager configuration. pnpm uses its native global-install rules.
The application version, top-level interpreter/package-manager versions, Python distribution map and locked-input digest live in runtime.json, not the directory name. The digest covers the selected target's interpreter and wheel archives, shared wheel inputs, Python distribution versions, pnpm version and assembly format; other targets do not invalidate it. Key order within the selected target, wheel records and distribution map, plus wheel-entry order, participates in this identity; top-level lock key order does not. Extraction or assembly changes that alter payload bytes without changing locked inputs require an explicit format bump. Installation publishes a completed staged copy and retains the previous directory until replacement succeeds. Matching payloads reuse installed files; replacements also replace user-added Python dependencies inside the managed tree. Older manifests without a digest remain readable and differ from the current payload. Distribution names use PEP 503 normalization; duplicate normalized names are rejected. The reader normalizes legacy components files and retains their numpy/pandas consistency checks; current manifests carry Python libraries only in the distribution map. The Desktop single-instance owner and the tool's shared installation promise serialize normal installation requests.
The shared Node build entry downloads and hash-verifies the complete locked wheel set and unpacks library files into site-packages. This avoids build-host Python and pip version selection without implementing dependency resolution or general wheel installation. Auxiliary scripts, including XlsxWriter's VBA extraction script, remain under the wheel's .data/scripts directory; command-line entry-point wrappers are outside this library payload. Other .data installation schemes are rejected. Native smoke executes the final payload after temporary files are removed, checking the exact locked distribution set plus bundled pip, Python and pinned wheel versions, dependency completeness and editable Office document round trips, so interpreter links must survive relocation. Smoke checks disable bytecode writes to keep validation artifacts out of the shipped payload.
macOS grants com.apple.security.cs.allow-jit to the standalone Node executable and LibreOffice helpers. The x64 Node executable additionally requires com.apple.security.cs.allow-unsigned-executable-memory: with JIT-only signing, the bundled x64 Node fails during startup under Rosetta in V8 OS::SetPermissions. ARM64 Node and LibreOffice helpers retain JIT-only permissions; the additional permission is confined to the interpreter that requires it. Interpreter and library smoke checks run after signing as well as after staging cleanup; a valid signature alone does not establish executable behavior.
Windows signed packaging separates materialization from execution with supervised signing stages for the primary runtime and application production dependencies. PE inspection identifies content independently of extensions, excludes non-PE foreign-platform Node addons and refuses directory links. Public-key verification uses batches of 32 files with at most four processes and drains each batch before returning failure; hardware operations remain serial. Valid vendor signatures remain intact; only unsigned files receive the configured EV signature. Invalid existing signatures fail before hardware access, and each new signature is checked for validity, timestamp and certificate identity before the next file. Electron-builder's copy-time signing hook preserves runtime executables only after exact-byte and signature verification; the same serial queue rejects later tasks if preservation fails. The existing per-user interlock, serialized signer and redacted journal own hardware calls; hardware failures prohibit retries and later stages. Public timestamp attempts follow the signature completion policy. Runtime execution receives no signing credentials and follows complete verification. Development and unsigned preparation retain native smoke without automatic hardware access. Runtime inventories record signed bytes. Electron-builder unpacks detected dependency PE files and preserves signed copies. Final signature verification and ASAR payload and Host smoke use a private native cache before release completion.
Desktop ZIP extraction pins extract-zip to yauzl 3.4.0 through a scoped dependency override. The 2.x reader can leave large deflate entries unfinished on Node 26 (upstream issue); retaining the existing extractor preserves its path validation and wheel-entry checks. The development launcher uses top-level await so unfinished preparation cannot exit successfully. A large compressed wheel regression checks the complete extracted bytes.
The shared-runtime reference owns builder, lock and query-package placement for Desktop and SDK carriers, including GNU/Linux x64 and Python-only payloads. Desktop retains the installation and signing behavior described here and requires the full Python, Node.js and pnpm payload.
Alternatives considered
System interpreters only. They do not provide predictable availability or preinstalled numpy and pandas.
PATH injection and dedicated pnpm global directories. They change command selection or require pnpm's global command directory to be on PATH. Absolute interpreter paths and native pnpm behavior satisfy the requested scope without those changes.
Independent updates and version-named directories. Runtime releases are coupled to Desktop, and the requested installation location is stable.
Multi-file SignTool requests. A software-certificate probe with a missing first file still signs the second file before returning an error. One process cannot guarantee stopping hardware operations at the first failure; token signing remains one file per supervised request.
Signing only the interpreter or bypassing native smoke. Windows code integrity also evaluates DLLs and Python extensions. A signed launcher cannot make an unsigned extension load, and skipping execution would hide unusable installed dependencies. Preserving valid upstream signatures avoids unnecessary hardware operations and retains upstream attribution.
Dependency manifests use electron-builder's own cleanup before inventory sealing. Its archive writer otherwise changes package metadata after signing; pre-applying that transformation keeps the final archive verifiable without replacing the sealed inventory after packaging.
Consequences
The application carries additional native files and replaces the complete managed payload on upgrade. Running interpreters can prevent replacement on Windows. Native build smoke, install/reuse/recovery tests and a keyless tool-error session cover distinct installation and model-output paths; macOS signing uses the existing native-runtime signer. Interpreter archives and Python wheels are hash-pinned, and licenses remain with their distributions.
中文
Problem
Desktop 代理需要在没有开发环境的机器上获得确定的 Python 数据处理库和独立 Node 解释器。系统解释器的选择必须继续由用户控制。
Decision
Desktop 将 Python、Node.js、pnpm、数据处理库和 Office 创作库作为绑定应用版本的产物交付。路径查询工具从应用资源将产物安装到 Harness home 下的固定目录,并返回绝对路径与内置 Python 分发包版本。版本报告不包含用户自行添加的包。它不修改 PATH、环境变量或包管理器配置。pnpm 使用原生全局安装规则。
应用版本、顶层解释器和包管理器版本、Python 分发包版本表和锁定输入摘要记录在 runtime.json 中,不放在目录名里。摘要涵盖所选目标的解释器与 wheel 压缩包、共享 wheel 输入、Python 分发包版本、pnpm 版本和组装格式;其他目标不会使其失效。所选目标、wheel 记录及分发包映射内部的键顺序,以及 wheel 条目顺序参与该身份计算;锁文件顶层键的顺序不参与。解压或组装逻辑在锁定输入不变时改变产物字节,必须显式提升格式版本。安装发布完整的暂存副本,并在替换成功前保留之前的目录。相同产物复用已安装文件;替换也会移除受管目录内用户添加的 Python 依赖。不含摘要的旧清单仍可读取,并与当前产物区分。分发包名称按 PEP 503 归一化,归一化后重复的名称会被拒绝。读取器归一化旧 components 文件并保留其 numpy/pandas 一致性校验;当前清单仅在分发包版本表中记录 Python 库。Desktop 单实例所有者和工具共享的安装 Promise 串行处理正常安装请求。
共享 Node 构建入口下载并校验完整锁定 wheel 集的哈希,将库文件解压到 site-packages。这避免选择构建主机的 Python 和 pip 版本,也无需实现依赖解析或通用 wheel 安装。XlsxWriter 的 VBA 提取脚本等辅助脚本保留在 wheel 的 .data/scripts 目录中;命令行入口包装器不属于该库产物。其他 .data 安装方案会被拒绝。本机 smoke 在临时文件删除后执行最终产物,检查精确锁定的分发包集合与内置 pip、Python 与固定 wheel 版本、依赖完整性及可编辑 Office 文档的写入和读取,因此解释器链接必须在迁移后仍有效。Smoke 检查禁用字节码写入,避免把验证产物纳入分发内容。
macOS 向独立 Node 可执行文件和 LibreOffice 辅助程序授予 com.apple.security.cs.allow-jit。x64 Node 可执行文件还需要 com.apple.security.cs.allow-unsigned-executable-memory:仅授予 JIT 权限时,内置 x64 Node 在 Rosetta 下启动会在 V8 OS::SetPermissions 中失败。ARM64 Node 和 LibreOffice 辅助程序保持仅有 JIT 权限;额外权限仅授予需要它的解释器。解释器和库的 smoke 检查在签名后以及暂存清理后执行;签名有效本身不能证明程序可运行。
Windows 签名打包通过受监督的签名阶段覆盖第一方运行时和应用生产依赖,将文件准备与执行分开。PE 检查独立于扩展名识别文件内容,排除其他平台的非 PE Node 原生插件,并拒绝目录链接。公钥验签每个进程处理最多 32 个文件,同时最多运行四个进程,返回失败前等待当前批次全部结束;硬件操作仍串行执行。有效的上游签名保持不变;仅未签名文件使用配置的 EV 证书签名。已有签名无效时,在访问硬件前失败;每个新签名通过有效性、时间戳和证书身份检查后,才处理下一个文件。electron-builder 复制阶段的签名钩子仅在逐字节比对和验签通过后保留运行时可执行文件;保留验证失败时,同一串行队列拒绝后续任务。硬件调用复用现有的用户级互锁、串行签名器和脱敏日志;硬件失败不允许重试或继续后续阶段。公开时间戳尝试遵循签名完成规则。运行时执行不接收签名凭据,并在完整验签后进行。开发和未签名准备保留本机 smoke,不自动访问硬件。运行时清单记录签名后的字节。electron-builder 将检测到的依赖 PE 文件放在归档外,并保留已签名的副本。发布完成前,最终签名检查以及 ASAR 载荷和 Host 冒烟检查使用私有原生缓存。
Desktop ZIP 解压通过定向依赖覆盖为 extract-zip 固定 yauzl 3.4.0。2.x 读取器在 Node 26 上可能无法完成较大 deflate 条目的读取(上游问题);保留现有解压器可保留其路径校验和 wheel 条目检查。开发启动器使用顶层 await,避免准备未完成却成功退出。大压缩 wheel 回归测试检查完整的解压字节。
共享运行时参考持有 Desktop 与 SDK 载体的构建器、锁文件和查询包归属,覆盖 GNU/Linux x64 与 Python-only payload。Desktop 保留本文的安装与签名行为,并要求完整的 Python、Node.js 和 pnpm payload。
Alternatives considered
只使用系统解释器。 无法保证可用性或预装 numpy 和 pandas。
注入 PATH 并指定 pnpm 全局目录。 这些方式会改变命令选择,或要求 pnpm 全局命令目录已在 PATH 中。绝对解释器路径和 pnpm 原生行为无需这些改动即可满足请求范围。
独立更新和版本目录。 Runtime 发布与 Desktop 绑定,且请求的安装位置固定。
多文件 SignTool 请求。 软件证书探针在第一个文件不存在时,仍会先给第二个文件签名,再返回错误。单个进程无法保证首个失败后停止硬件操作;令牌签名仍按每个受监督请求处理一个文件。
仅签名解释器或跳过本机 smoke。 Windows 代码完整性也检查 DLL 和 Python 扩展。启动程序有签名不能让未签名扩展被加载,跳过执行会隐藏安装后不可用的依赖。保留有效上游签名既减少硬件操作,也保留上游归属信息。
依赖清单在记录完整性清单前使用 electron-builder 自身的清理逻辑。否则其归档写入器会在签名后修改包元数据;提前应用该转换可让最终归档通过校验,无需在打包后替换已封装的完整性清单。
Consequences
应用携带额外的原生文件,并在升级时替换完整受管产物。Windows 上运行中的解释器可能阻止替换。本机构建 smoke、安装与复用及恢复测试、无密钥工具错误会话分别覆盖安装和模型输出路径;macOS 签名复用现有原生 Runtime 签名器。解释器压缩包和 Python wheel 固定哈希,许可证随各分发包保留。