Skip to content

fix(desktop): verify the installed payload before launching on Windows - #1439

Open
RainMona wants to merge 2 commits into
TraderAlice:devfrom
RainMona:cursor/desktop-install-integrity-23ff
Open

fix(desktop): verify the installed payload before launching on Windows#1439
RainMona wants to merge 2 commits into
TraderAlice:devfrom
RainMona:cursor/desktop-install-integrity-23ff

Conversation

@RainMona

@RainMona RainMona commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Why

Community reports of A JavaScript error occurred in the main process — Cannot find module 'fs-extra' (require stack resources\app\node_modules\electron-updater\out\main.js) on Windows desktop.

Root cause is not packaging. I unpacked the shipped NSIS installers from Linux (7z x OpenAlice.Setup.<v>.exe '$PLUGINSDIR/app-64.7z'):

release fs-extra in payload layout
0.91.0-beta.4 / 0.91.0 / 0.91.1 present, 42 entries loose resources/app/ (15 934 files)
0.92.0 present inside app.asar app.asar + resources/runtime/ (19 874 files)

The module goes missing between the installer and disk. electron-builder's extractUsing7za copies the 7z output atomically with 5 retries, then on failure falls through to a direct Nsis7z::Extract that ignores per-file errors (templates/nsis/include/extractAppPackage.nsh). In silent --updated mode there is no user prompt, and --force-run then launches whatever landed. Locked handles from Guardian children, antivirus scans of freshly extracted files, and MAX_PATH on long user profiles are the usual triggers. Nothing checked the result, so the first missing module or toolchain surfaced as an unrelated crash.

The ASAR migration (#1406) removed the node_modules half of this surface. resources/runtime/ still carries ~21k files (vendor/pi 10 959, vendor/git 10 427; longest relative path 183 chars), so the same interruption now shows up as a broken Pi or Git Bash instead of a missing module.

What

  1. afterPack inventory. scripts/desktop-after-pack.mjs writes resources/openalice-integrity.json (version + every file under app.asar, app.asar.unpacked/, runtime/ with byte size). Shared logic in scripts/desktop-install-integrity.mjs. ~1.7 MB for the 0.92.0 payload; collect 95 ms, verify 65 ms.
  2. Installer verification. installer.nsh gains customInstall: after extraction and before electron-builder's force-run launch it re-checks the inventory via PowerShell and aborts (exit level 3, reinstall message) instead of handing off to a partial tree. customInit replaces Sleep 1000 with a bounded wait (20 s) for processes under $INSTDIR to exit before RD /S /Q. Helper scripts are written to $PLUGINSDIR one FileWrite per line so NSIS string limits and $/${} expansion never touch the script body; the inventory is parsed with JavaScriptSerializer because Windows PowerShell's ConvertFrom-Json caps input near 2 MB (fallback to ConvertFrom-Json when the assembly is unavailable).
  3. Startup self-check. apps/desktop/src/install-integrity.ts; the packaged main process verifies the inventory before resolving the data home. Damaged → error dialog with the first missing/truncated paths, "Download installer" (opens latest release) / "Quit", result logged as install-integrity in desktop.log. OPENALICE_DESKTOP_SKIP_INSTALL_INTEGRITY=1 bypasses for diagnosis.
  4. Package assertion. assert-desktop-package validates the inventory against the unpacked package and rejects node-pty compiler intermediates in the archive.
  5. Trim. build/**/obj/** and .exp/.iobj/.ipdb/.lib/.pdb/.tlog under node_modules/node-pty are excluded (124 files in 0.92.0).

Design choice recorded per AGENTS.md: a hard stop with a reinstall dialog rather than a "continue anyway" option, because a partial tree can fail later in a data-mutating path. This is an autonomous contribution; no maintainer approval is implied.

Verification

  • cd apps/desktop && npx tsc --noEmit clean (after pnpm -F @traderalice/guardian-runtime build).
  • pnpm test:owner:desktop: 18 files / 71 tests pass, including new install-integrity.spec.ts, inventory cases in desktop-after-pack.spec.ts and assert-desktop-package.spec.ts, and the installer contract in desktop-upgrade-smoke-lib.spec.ts (also asserts the helper bodies contain no ${}/$( NSIS expansions).
  • Real payload: extracted the 0.92.0 Windows resources/ tree, generated the inventory with the new hook logic, then ran the rendered openalice-verify-install.ps1 under PowerShell 7.6 on Linux: intact → checked 19874 files, problems 0 exit 0 (1.7 s); after deleting pi-coding-agent/dist/cli.js, truncating ui/dist/index.html and removing vendor/git/win32-x64/mingw64problems 4555 exit 1 with the first five paths; missing/corrupt inventory → exit 2.
  • pnpm test:changed (the package.json change widens it to the full closure, 767 files): 764 pass; the 3 failing files (project-transfer*.spec.ts, template-upgrade.spec.ts, workspace-absorb.spec.ts) are git-heavy 5 s/10 s timeouts in this VM and untouched by this change.

Residual risk

  • No Windows host here. The NSIS macro and the JavaScriptSerializer path have not executed on Windows PowerShell 5.1; only the rendered script under pwsh 7 (which exercised the ConvertFrom-Json fallback). The release desktop-upgrade-smoke on windows-latest (previous release → candidate via real Setup.exe /S --updated) is the gate that runs both customInit and customInstall for real; a wrong Abort there would fail that job, not ship.
  • macOS: the inventory is written before signing and lives in Contents/Resources, so it is covered by the signature; the startup check adds ~130 ms of lstat on 20k files before the data-home prompt.
  • Follow-up not in this PR: vendor/pi/node_modules/**/src/**/*.ts sources account for the longest paths (183 chars); trimming them in vendor-managed-runtime.mjs would lower MAX_PATH exposure further.

cursoragent and others added 2 commits September 9, 2026 08:10
node-pty's build/**/obj trees and .exp/.iobj/.ipdb/.lib/.pdb/.tlog link
outputs shipped inside app.asar and app.asar.unpacked (124 files in the
0.92.0 Windows installer). Only the .node/.dll/.exe outputs are runtime
payload; the rest just widens the NSIS extraction surface.

Co-authored-by: RainMona <RainMona@users.noreply.github.com>
Community reports of `Cannot find module 'fs-extra'` on Windows came
from installs whose resources tree was only partially extracted: the
0.91.x/0.92.0 installers ship the file, and electron-builder's NSIS
extraction falls back to a non-atomic 7z extract that ignores per-file
errors after its atomic copy fails (locked handles, antivirus, MAX_PATH).
Nothing checked the result, so the app started from a partial tree and
failed on the first missing module or toolchain.

- afterPack writes resources/openalice-integrity.json: version plus every
  file under app.asar, app.asar.unpacked/ and runtime/ with its size.
- installer.nsh customInstall re-checks that inventory after extraction
  and aborts (exit 3) with a reinstall message instead of handing off to
  --force-run. customInit now waits up to 20 s for processes under
  $INSTDIR to exit instead of sleeping one second before RD /S.
- The packaged desktop main verifies the inventory before resolving the
  data home; a damaged install shows a reinstall dialog linking to the
  latest release and quits. OPENALICE_DESKTOP_SKIP_INSTALL_INTEGRITY=1
  bypasses it for diagnosis.
- assert-desktop-package validates the inventory against the unpacked
  package and rejects node-pty compiler intermediates in the archive.

The helper PowerShell is written to $PLUGINSDIR one line per FileWrite so
NSIS string limits and $/${} expansion never touch the script body; the
inventory is parsed with JavaScriptSerializer because Windows PowerShell's
ConvertFrom-Json caps input near 2 MB and the inventory is ~1.7 MB.

Co-authored-by: RainMona <RainMona@users.noreply.github.com>
@vercel

vercel Bot commented Sep 9, 2026

Copy link
Copy Markdown

@cursoragent is attempting to deploy a commit to the luokerenx4's Team Team on Vercel.

A member of the Team first needs to authorize it.

@luokerenx4
luokerenx4 marked this pull request as ready for review September 9, 2026 11:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants