fix(mac): terminal posix_spawn ENOENT; run package commands on the Electron Helper and drop bundled Node - #646
Merged
Merged
Conversation
Packaged Harness loads its dependencies from the physical app.asar.unpacked directory (runtimePackageRoot, since #570). node-pty derives spawn-helper by replacing "app.asar" with "app.asar.unpacked", which turned the already-unpacked path into app.asar.unpacked.unpacked, so every macOS terminal failed with "posix_spawn failed: No such file or directory". Patch node-pty 1.2.0-beta.15 to skip paths that are already unpacked. Windows does not use spawn-helper and is unaffected. The patched-JS identifier check now also allows the CommonJS host globals __dirname and clearImmediate, as node-pty is the first CJS patch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed Node macOS Harness already runs in an Electron utility process, but Profile repair, market baseline, generation installs and the .desktop-bin node/pnpm shims still used a bundled standalone Node 24.9.0, so two Node runtimes installed into one Profile and the main-process and Harness-side shim writers disagreed. - bundledNodePath() now returns the app's Helper on macOS (main executable elsewhere); package commands and shims always set ELECTRON_RUN_AS_NODE=1, and the generation installer injects it under any Electron runtime instead of only on Windows. Without it the Helper boots a full GUI app (seen as a bogus gpu-crash report). - Main-process generation installs (plugin upgrade, legacy migration) now rewrite the .desktop-bin shims first and put them on PATH: pnpm only adds its runtime's directory for lifecycle scripts, and the Electron runtime has no `node` there; rewriting also replaces shims an upgraded install left pointing at the removed Node. - Linux development launches Harness through Electron Node mode too. - Remove the `node` dependency; verify-target and the afterPack runtime check use the Electron executable / Helper instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The host-singleton patterns cover the whole @deepseek-ai scope, so installing a plugin from that scope (e.g. @deepseek-ai/dsh-subagent-claude-code) hoisted the plugin itself out of its generation. Legacy migration then failed with ENOENT on its package.json and rolled the Profile back, and peer validation would report it as a private host singleton. Exempt only the generation's top-level plugin package from the singleton walk used by both hoisting and validation; nested copies of the same name and every other host package are still removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… files Align the package layout with upstream deepseek-harness apps/desktop. Since #646 every consumer of the bundled packages runs on the Electron runtime (main, utility process, Helper in Node mode), which reads app.asar, so unpacking all of node_modules bought nothing: about 21k loose files that slowed installs and put "app.asar" in physical paths for dependencies' path heuristics to trip over. - asarUnpack keeps only native addons/libraries, spawn-helper, ripgrep, the LibreOffice engine and sherpa-onnx platform packages and the PPT runtime (macOS arm64: 1.6k files / 235 MB unpacked, was 21k / 549 MB). - Remove runtimePackageRoot; the bundled runtime root is app.getAppPath(). - build/office-engine-resolution.mjs (adapted from upstream desktop-host office-engine.ts) resolves the LibreOffice engine package to app.asar.unpacked so the OS can spawn its executable; registered in harness-node-entry. - Drop the node-pty patch: upstream node-pty maps app.asar to app.asar.unpacked itself, which is correct in this layout. - afterPack (scripts/after-pack.cjs) now verifies the PPT runtime through app.asar and fails when a Mach-O/ELF/PE file is packed inline. - Windows release smoke loads koffi and pnpm through app.asar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…g findings With blocking compatibility findings the sidebar button reopened the Safe Mode manager and returned. When the manager was already open the click had no visible effect, while the manager's own restart button asked for confirmation and exited. Ask the same question as the manager's restart button (shared safeModeExitConfirmation / safeModeBlockingGroupCount): "Exit anyway" leaves Safe Mode, "Manage plugins" opens the manager. When the manager is open and waiting, the exit is handed to its restart action so relaunch, the unresolved-findings note and a fall-back into Safe Mode are handled in one place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
feat(packaging): load JavaScript from app.asar and unpack only native files
feat(office): offline Python authoring on Windows and macOS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two related macOS runtime fixes, from packaged Harness and package commands running from
app.asar.unpackedon the Electron runtime, plus a generation-installer fix found while verifying them.1. Sidebar terminal:
posix_spawn failed: No such file or directorynode-pty starts the shell through
spawn-helper. It derives the helper path withreplace('app.asar', 'app.asar.unpacked'), so a path that is already unpacked becameapp.asar.unpacked.unpacked/.../spawn-helper. Since #570, packaged Harness loads dependencies from that physical directory.patches/node-pty+1.2.0-beta.15.patchskips paths already followed by.unpacked. 1.2.0-beta.15 is the newest beta; remove the patch once upstream fixes it.app.asarpaths.2. One Node runtime on macOS: Electron Helper; bundled Node removed
macOS Harness already ran in an Electron utility process. Profile repair, market baseline, generation installs and the
.desktop-binnode/pnpmshims still used a bundled standalone Node 24.9.0, so two Node runtimes installed into one Profile. The main-process shim writer and the Harness-side shim writer also disagreed with each other.bundledNodePath()→electronNodeExecutable(): the app's Helper on macOS, the main executable elsewhere. Package commands and shims always setELECTRON_RUN_AS_NODE=1. The generation installer now injects it under any Electron runtime, not only on Windows. Without it the Helper boots a full GUI app; during testing this showed up as a bogus "gpu-crash" report.packageCommandEnvironment(), which rewrites the shims and puts them on PATH. pnpm adds only its runtime's directory for lifecycle scripts, and the Electron runtime has nonodethere, so a dependency'snode …install script failed withnode: command not found. Rewriting the shims also replaces ones an upgraded install left pointing at the removed Node.nodedependency (lockfile:node,node-bin-setup) and thewin.filesexclusion for it.verify-targetand the afterPack runtime check use the Electron executable / Helper instead. Docs updated.3. Generation installer hoisted an
@deepseek-ai/*plugin out of its own generationThe host-singleton patterns cover the whole
@deepseek-aiscope. Installing a plugin from that scope (seen with@deepseek-ai/dsh-subagent-claude-codeduring legacy migration) therefore removed the plugin itself from the generation. Migration then failed with ENOENT on itspackage.jsonand rolled the Profile back; peer validation would also have reported it as a private host singleton. The generation walk used by hoisting and by validation now exempts only the top-level plugin package. Nested copies of the same name and every other host package are still removed. Regression:test/generation-installer.test.ts(fails without the fix).Checks
npm ci(node-pty patch applied, nonode_modules/node);npm test179 files / 1600 tests;npm run typecheck;git diff --check;node scripts/verify-target.mjs darwin arm64(Electron 43.0.0 / Node 24.17.0 loads the Harness native loader)app.asar.unpackedspawns a PTYELECTRON_RUN_AS_NODEin the base env (hangs without the fix)nodesucceeds with a Node-free base PATHpackage:dev:dir(arm64):node_modules/node;app.asar.unpacked661 MB → 549 MB; the afterPack check ran on the Helperapp.asar.unpackedstarts a login shell@deepseek-ai/*plugin out of its own generation is fixed in section 3.dshmarket@1.65.1declaring peers that do not include dsh 0.2.0-rc.2 needs a market release and is not addressed here.Not verified: signed/notarized build, x64 mac, Windows packaging and installer (CI), market install and upgrade through the real UI.
🤖 Generated with Claude Code