ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

deepseek harness桌面端改造(二)把“Node 运行时 + 3.4 万文件的插件闭包“装进桌面安装包:sidecar 载荷装配全揭秘

deepseek harness桌面端改造(二)把“Node 运行时 + 3.4 万文件的插件闭包“装进桌面安装包:sidecar 载荷装配全揭秘 二把Node 运行时 3.4 万文件的插件闭包装进桌面安装包sidecar 载荷装配全揭秘承接系列第一篇把插件化 Agent 框架打包成桌面应用。这次不聊壳怎么干活而是聊桌面版最硬核、也最容易被忽略的部分怎么把一个「自带 Node 运行时 一个 framework 的完整依赖闭包、约 3.4 万个文件」物化成可分发、可校验、装完就能跑的 sidecar 载荷。这是一个 Electron 壳和一个真正能用的产品之间的分水岭。我们要打包的不是一个文件夹是一个运行时 全世界普通 Electron 应用把app目录塞进 asar 就完事了。但我们的桌面版不一样它要在用户机器上跑一个插件化 Agent 框架这意味着安装包里必须自带一个固定版本的 Node 运行时用户没装 Node也得能跑一堆原生.node模块node-pty 之类的终端能力还绑定特定 Node ABI整个web UI 的 dist桌面窗口加载的就是它通过require.resolve提供。这三样加起来实测是app 约 247 MB / 3.2 万 文件node 约 101 MB / 约 2000 文件最终安装包 180 MB 出头。把这些物化出来是装配脚本assemble-sidecar.mjs的全部意义。第一步用 pnpm deploy 拉出生产依赖闭包我们不手工复制文件而是从纯依赖部署根一个只声明依赖、不写逻辑的 package拉闭包pnpm--filterdsh-desktop-sidecar-runtime deploy\--legacy--prod--config.node-linkerhoisted\--config.auto-install-peersfalse --config.link-workspace-packagestrue\apps/desktop/.sidecar/app两个关键开关要理解--prod hoisted只装生产依赖、拍平成单层node_modules保证载荷是无 symlink 的扁平树拷贝/搬迁时才不会碎。auto-install-peersfalse故意不自动装 peer 依赖——因为 Service Definition 包是以 peer 身份骑行的自动装会把一些本不该进载荷的东西也拖进来。第二步机械补全被 peer 丢失的包auto-install-peersfalse的副作用来了一堆以 peer 身份骑行的插件包被丢掉了。而且这种漂移是会再犯的——一旦某个已部署插件新增了一个 workspace peer下次装配就又缺。所以我们做了一个机械安全网死循环里遍历每个已部署包的peerDependencies凡是在 workspace 里存在的一律补进载荷直到没有新的缺失为止。for(;;){constmissingnewSet()for(constpkgofdeployedPackages()){for(constpeerofObject.keys(pkg.manifest.peerDependencies??{}))if(!existsSync(join(app,node_modules,peer)))missing.add(peer)}constcompletable[...missing].filter(peerworkspaceIndex.has(peer)).sort()if(completable.length0)breakfor(constpeerofcompletable)awaitcopyPackage(workspaceIndex.get(peer),target)}注意这里的取舍workspace 里的 peer 补进来注册表 peer如react保持缺席——因为浏览器 bundle 本来就在apps/web/distclient 的 node 半边永远不会import它们。盲目全补反而会污染载荷。第三步staging 里所有 symlink 一律物化pnpm 链接出来的可能是符号链接而载荷要能被随意拷贝进安装包。所以对残留的 symlink.bin目录直接删、其余用真实目录替换直到整个node_modules一棵软链接都不剩。这一步保证载荷搬迁即用、无关立场。第四步自带 Node 运行时跨平台下载 校验载荷要带 Node从 nodejs.org可用DSH_NODE_DIST_MIRROR替换镜像拉固定版本然后用官方SHASUMS256.txt逐文件校验 sha256最后解压到staging/nodeconstexpected(awaitfetchShasums(version)).get(file)awaitdownloadVerified(url,expected,archive)一个反直觉的坑EXDEV: cross-device link not permitted下载/解压 Node 时如果你的工作目录放在系统临时目录最后把解压结果rename到 staging 会跨卷失败Windows 尤其常见。解法是把工作目录建在 staging 内部同卷rename干净落地constworkawaitmkdtemp(join(staging,.node-work-))// ... 下载、解压到 work/extractawaitrename(join(work,extract,root),join(staging,node))第五步用载荷自己的 Node做冒烟验证载荷装没装对光看文件存在不算数。让staging/node里的 Node 去执行载荷里 dsh 的入口跑--versionapps/desktop/.sidecar/node/node.exe\apps/desktop/.sidecar/app/node_modules/deepseek-ai/dsh/lib/bin.js--version只有这个命令答出0.1.0-rc.5才证明运行时、依赖图、入口三者一起是通的。第六步把载荷放进安装包的正确方式electron-builder里sidecar 是extraResources在 asar 外extraResources:-from:.sidecarto:sidecarfilter:[**/*]原因在前一篇说过原生.node模块和子进程路径穿不过 asar 虚拟文件系统。运行时通过process.resourcesPath/sidecar找到它。别忘了复原工作区pnpm deploy有个隐蔽的副作用legacy deploy 会把工作区根node_modules标记成待生产修复下一次pnpm run会把 devDependencies 剪掉。所以不管装配成功与否都在finally里补一次pnpm install复原开发环境——否则你本人的开发环境会被一次打包悄悄改坏。try{awaitdeployClosure()awaitinstallNodeRuntime(version)awaitsmokePayload(version)}finally{awaithealWorkspace()// 还原工作区决不让打包污染开发环境}小结把东西拷进来人人会让一个多运行时、几万文件的闭包以可复现、可校验、可搬迁的方式进安装包才是工程。记住四个关键词物化闭包deploy peer 补全、去链接symlink 实化、验校验sha256 冒烟、不污染复原工作区。下一期我会接着讲这套载荷启动之后Electron 壳是怎么管好它的一生、以及把它体面地送走的。如果你被pnpm deploy 把工作区改坏了或EXDEV 跨卷坑过评论区见——我很想听听你的那一版解法。
返回列表