Commit Graph

215 Commits

Author SHA1 Message Date
ethernet
7e4139d967 test(release): isolate desktop CI fixtures 2026-09-22 12:28:01 -04:00
ethernet
74ae184245 test(desktop): isolate expected payload fact failures
The macOS digest-order test emitted the strict PM failure from its expected
negative case into the shared Vitest log. That traceback looked like a second
packaging failure even though the test passed.

Run corrupt and missing fact checks in a captured child process. Keep the PM
reader strict, verify both errors, and retain the real builder ordering proof
for valid payload facts.
2026-09-22 12:01:37 -04:00
ethernet
8da6c5f386 Merge branch 'ethie/release-machinery' into ethie/pm-clean
# Conflicts:
#	tests/ci/test_desktop_store_eligibility.py
2026-09-22 11:05:14 -04:00
ethernet
dcd678d6fd fix(release): close publication custody gaps 2026-09-22 02:19:38 -04:00
ethernet
14b1e7220b feat(release): derive canaries from stable build identity 2026-09-21 22:02:46 -04:00
ethernet
a835f0e52f Merge branch 'fix/e2e-windows-native' into ethie/pm-clean 2026-09-21 21:22:54 -04:00
ethernet
f56bdde757 fix(desktop): preserve freshness without X11 headers 2026-09-21 21:16:52 -04:00
ethernet
4d39f74c71 fix(desktop): stage Windows HUD helper sources 2026-09-21 21:14:07 -04:00
ethernet
fcfd001dc0 feat(release): stamp canary Windows builds as yy.mmdd.hh.mmss 2026-09-21 21:10:49 -04:00
ethernet
c4d2bb5575 feat(release): one native quad, the build time, for stable and canary
Windows VERSIONINFO and the MSIX package version were two derivations of
one fact, and the sideload one counted minutes since the last stable, which
overflows a 16-bit field after 45 days. Both now come from
storePackageVersionAt's year.hourOfYear.secondOfHour.0, so a later build
always sorts above an earlier one and the cap has nothing left to cap.
2026-09-21 20:28:41 -04:00
ethernet
fa9e899d4e desktop: escape the attribute values interpolated into the MSIX fragments
before-build.mjs builds the msix-extensions.xml fragment and the hidden
CLI <Application> entries from string templates, interpolating the
display name and the payload-declared launcher stems raw. A value with
`&`, `<` or `"` would corrupt the manifest makeappx reads (an opaque
0x80080204 at best, a differently named alias at worst). Every
interpolated attribute now goes through xmlAttribute().
2026-09-21 18:51:46 -04:00
ethernet
f999b5ecd2 wip fixin stuff 2026-09-21 18:19:03 -04:00
ethernet
fc4b1958dc Merge remote-tracking branch 'origin/main' into ethie/pm-clean 2026-09-21 17:55:47 -04:00
brooklyn!
d0381cea91 fix(desktop): build and verify the Windows HUD helper without an SDK 2026-09-21 16:47:50 -05:00
ethernet
4efb36f81e merge: integrate upstream desktop features through PM preparation
Merge origin/main at 8e806ae1b2. Keep native helper compilation in
prepareDesktopNativeDependencies and keep bundling/beforePack consume-only.
Bind helper sources and headers into preparation identities and cache keys;
copy admitted executable resources beside node_modules and preserve signing
semantics in product freshness checks.

Verified desktop typecheck, focused native/packaging/UI and gateway/cache
tests, and the real Linux preparation/copy/Xvfb execution path. Incoming
upstream anti-slop findings remain unchanged; no baseline was raised.
2026-09-21 15:17:44 -04:00
brooklyn!
9ff6e071fa feat(desktop): detect clean modifier taps with passive native listeners 2026-09-21 13:44:49 -05:00
ethernet
9214174e07 fix(ci): lint legs after the main merge
- check_no_tmp_literals: resolve scratch via tempfile/os.tmpdir; the termux
  container mount point is one marked variable per script
- ruff TID251: desktop E2E fixtures may reach PM internals like tests do;
  the pm.runtime_stage ban message no longer names a module that never existed
- auth_codex: build the capped httpx stream subclass on first use so importing
  hermes_cli.auth_codex no longer forces httpx (the lazy proxy in auth_constants
  was defeated by a module-scope base class; broke lanes without httpx)
- desktop-smoke: launchApp is a parameter; the bundle-env test substitutes a
  refusing launcher instead of letting Playwright spawn a dying binary
  (3 unhandled rejections failed the tests-js lane)
2026-09-19 23:25:18 -04:00
ethernet
e1576d06a6 Merge remote-tracking branch 'origin/main' into ethie/pm-clean
Resolved toward the branch: PM provisions uv/python (main's install.ps1 uv-shim
salvage + its test and workflow steps dropped), the shim re-exec stays retired,
package.json carries no electron-builder block (afterExtract identity stamp wired
into electron-builder.config.cjs instead; after-pack.mjs keeps signing only),
Desktop workspace-deps helpers stay retired. Main's scratch-dir bootstrap
(export_scratch_tmp_env) is taken and re-run after profile resolution.
2026-09-19 22:57:07 -04:00
teknium1
0d3fe24dce docs(repo): contributor and desktop dev docs stop suggesting /tmp
Throwaway HERMES_HOME, perf JSON/cpuprofile outputs and typing-lag profiles now land in the Hermes scratch dir; the CONTRIBUTING anti-pattern mention keeps its literal with a no-tmp marker.
2026-09-19 10:44:26 -07:00
teknium1
956732d4a8 chore(evals,desktop): drop literal /tmp from eval harnesses and desktop scripts
Eval probes wrote fixtures, receipts and evidence to hard-coded /tmp paths and
two live A/B tasks literally instructed the model to work in /tmp. They now
derive locations from tempfile.gettempdir() / os.tmpdir() (env overrides kept),
usage examples use relative output names, and the desktop e2e screenshot dirs,
perf scripts and the short-session repro fixture stop naming /tmp. Also adds
the explicit encoding= the windows-footgun check wants in the touched files.
2026-09-19 10:44:26 -07:00
teknium1
f97f8d5fd5 fix(desktop): remove a half-installed get-windows dir before the workspace npm install (#90829)
An interrupted extract leaves node_modules/get-windows without package.json
and npm never revisits an existing directory, so the tree stayed broken on
every later update until a manual `npm install get-windows`. Delete such a
dir (workspace hoist or app-local copy) in _install_desktop_workspace_deps
before npm runs so the same update re-extracts it; the staging repair hint
now points at `hermes desktop --force-build` instead of the manual install.

Also drops the unused `exists` injection on findHalfInstalledGetWindowsDir
and the stale "exported for tests" note on missingGetWindowsWarning.
2026-09-19 02:32:35 -07:00
teknium1
048e7b974f fix(desktop): degrade instead of failing the build when get-windows lacks its binding or helper
The unresolvable-package case already degraded, but a half-extracted install
(#90829) that keeps package.json while losing lib/binding (win32) or the
macOS helper (darwin) still threw from stageGetWindowsInto and before-pack
rethrew, so the Desktop rebuild died on the optional dep anyway. Stage the
fail-soft JS surface without a binding on win32 (the win32-arm64 precedent),
skip staging on darwin without the helper, and treat a failing native
installer the same way; the runtime reports read_window_below as unavailable
in every one of those states. The classify gate still refuses a present
binary compiled for the wrong platform.
2026-09-19 02:32:35 -07:00
teknium1
7ee8f5528d fix(desktop): name the half-installed get-windows dir and its repair when staging degrades
Follow-up to the cherry-picked #109251 (which stops a missing optional
get-windows from killing `npm run build` on win32-x64/darwin):

- The reporter's install (#90829) was not "npm skipped an optional dep" but a
  Windows in-place update interrupted by a running Desktop/gateway
  (TAR_ENTRY_ERROR): node_modules/get-windows exists with its binding but no
  package.json, so require.resolve fails while npm never revisits the
  directory. Degrading silently would leave read_window_below dead forever.
  `findHalfInstalledGetWindowsDir` walks the same node_modules ancestors
  require.resolve does; when the dir is there the warning names it and the
  repair (`npm install get-windows --save-exact` in apps/desktop, then
  `hermes desktop --force-build`).
- Warning text lives in `missingGetWindowsWarning` (pure, exported) and the
  locator is injectable, so tests assert behaviour instead of source text.
- Tests: the old per-platform "fails when absent" cases are replaced by one
  every-platform degrade invariant and one half-install → repair-hint case
  (both red on origin/main). Docs: desktop.md build-troubleshooting note.
2026-09-19 02:32:35 -07:00
salch-cred
2a9ba25994 fix(desktop): do not fail build if get-windows is missing on any platform 2026-09-19 02:32:35 -07:00
teknium1
7009611013 fix(desktop): document the afterExtract ordering, trim tests, refresh stale hook references
Follow-up to the salvaged #106846 commit (@JoaoMarcos44):

- after-extract.mjs: spell out WHY the stamp moved (electron-builder's
  beforeCopyExtraFiles rebuilds the PE with resedit for the ELECTRONASAR
  resource; rcedit then cannot commit to that exe, deterministically —
  #105629), and why disableAsarIntegrity was not taken.
- after-extract.test.mjs: two invariants — the hook wiring (afterExtract set,
  afterPack unset, ASAR integrity still on) and the stamp target
  (electron.exe on win32, nothing on other platforms). Red on origin/main.
- set-exe-identity.mjs / scripts/install.ps1: comments still named the
  afterPack hook / after-pack.mjs.
2026-09-19 02:31:27 -07:00
joaomarcos
168e0f78ad fix(desktop): order PE stamping before ASAR integrity 2026-09-19 02:31:27 -07:00
ethernet
a6ae6ace51 Merge remote-tracking branch 'origin/main' into ethie/pm-clean
# Conflicts:
#	.github/workflows/js-tests.yml
#	agent/model_metadata.py
#	apps/desktop/electron/main.ts
#	apps/desktop/scripts/bundle-electron-main.mjs
#	apps/desktop/src/app/settings/about-settings.tsx
#	apps/desktop/src/app/settings/gateway-settings.test.tsx
#	apps/desktop/src/app/settings/gateway-settings.tsx
#	apps/desktop/src/app/updates-overlay.tsx
#	gateway/shutdown_flush.py
#	hermes_bootstrap.py
#	hermes_cli/local_runtime/binaries.py
#	hermes_cli/main.py
#	hermes_cli/managed_uv.py
#	hermes_cli/update_cmd.py
#	hermes_cli/update_cmd_deps.py
#	hermes_cli/update_cmd_fleet.py
#	hermes_cli/update_cmd_maint.py
#	hermes_cli/update_receipt.py
#	hermes_cli/update_serve_obligations.py
#	hermes_constants.py
#	tests/hermes_cli/test_doctor.py
#	tests/hermes_cli/test_managed_uv.py
#	tests/hermes_cli/test_pending_supervisor_recovery.py
#	tests/hermes_cli/test_startup_fast_guards.py
#	tests/hermes_cli/test_update_desktop_stale_warning.py
#	tests/hermes_cli/test_update_fleet_restart_pending.py
#	tests/hermes_state/test_hermes_state.py
#	tests/tools/test_tirith_security.py
#	tools/bot_relay.py
#	tools/checkpoint_manager.py
#	tools/write_approval.py
#	website/docs/getting-started/updating.md
#	website/docs/reference/environment-variables.md
2026-09-18 17:26:10 -04:00
teknium1
ed45828c8b fix: inject platform into buildCommandScreenshotMonitor instead of faking process.platform in tests
The builder takes an optional `platform` (default process.platform) so the
argv test can drive the darwin and non-darwin branches explicitly. The test
no longer redefines process.platform via Object.defineProperty.
2026-09-18 11:00:02 -07:00
teknium1
b72a2bd802 test(desktop): prove the screenshot-monitor xcrun argv on every host
The salvaged test returned early off macOS, so Linux CI never executed the
assertion. Stub process.platform to 'darwin' (execFileSync is already mocked)
so the `--sdk macosx clang` argv contract is checked wherever vitest runs, and
add the off-macOS no-op as the control case.
2026-09-18 11:00:02 -07:00
Yagna Vudathu
5bb1ded879 fix(desktop): pin macosx SDK for the screenshot-monitor native build
A bare xcrun clang inherits the host default SDK, which may be newer than
the active linker (unknown-arch .tbd stubs at link time). Naming --sdk
macosx explicitly forces the driver and linker to agree on one SDK.

Fixes #113708.

(cherry picked from commit d5fcb6c6ab3de96602528dade2d7f75b7e47ddd5)
2026-09-18 11:00:02 -07:00
ethernet
35ea96cd33 fix(desktop): import rmSync in stage-native-deps
prepareDesktopNativeDependencies called a bare `rmSync` that the file never
imported (every sibling here uses `fs.rmSync`), so `npm run build` in
apps/desktop died with "ReferenceError: rmSync is not defined" at
stage-native-deps.mjs:696. That fails EVERY desktop build, which is why the
install e2e's products stage reported "[hermes] app products or command
publication failed" and install.sh exited 1 at HEAD.

Proof: the covering native case in tests-js/desktop-builder.test.mjs is red on
base with exactly that ReferenceError and green with the import.
2026-09-17 16:58:26 -04:00
Austin Pickett
8ffc2f0369 fix(desktop): working WSLg window controls and native Wayland launch (#113247)
* refactor(desktop): extract titleBarOverlayOptions into a tested helper

getTitleBarOverlayOptions in main.ts branched inline on mac/windows/wsl.
Move the decision into titlebar-overlay-width.ts so the WSLg → false rule
(renderer paints its own controls there) is unit-tested next to the width
reservation it pairs with. main.ts keeps a thin caller.

Co-authored-by: null-runner <nicholas.mariani@hotmail.it>

* fix(desktop): renderer-drawn window controls on WSLg

Under WSLg the frameless window (titleBarStyle: 'hidden') had no
minimize/maximize/close. getTitleBarOverlayOptions returned false on the
premise that the RDP host paints replacement controls; it does not for a
frameless window. Electron's native overlay is not the fix either: its
cluster's hit-region drifts from the rendered buttons under the RAIL
compositor.

The renderer now paints Windows-style min/max/close (wslg-window-controls)
routed over a hermes:window-control IPC channel (window-controls.ts →
preload → main). getWindowState reports customWindowControls/isMaximized so
the renderer knows when to mount them and which glyph to show. The cluster
is pinned to TITLEBAR_HEIGHT px (the contrib shell zeroes --titlebar-height
for content subtrees) with 46px native-width caption buttons.

Co-authored-by: Austin Pickett <pickett.austin@gmail.com>

* fix(desktop): wire WSLg window-control IPC and snap maximize onto the work area

Register hermes:window-control in main, report windowControlState from
getWindowState, and re-send window state on maximize/unmaximize so the
renderer's maximize/restore glyph tracks the window.

maximizedBoundsCorrection snaps a WSLg-maximized frameless window back onto
the display work area (RAIL can settle it offset — reported on WSLg 1.0.65).
It is a no-op wherever native maximize already fills the work area, so
healthy compositors are never fought and setBounds cannot loop.

Co-authored-by: null-runner <nicholas.mariani@hotmail.it>

* fix(desktop): call WSLg window-control bridge without the click event

contextBridge structured-clones every argument, and a React SyntheticEvent
is not cloneable, so onClick={controls.minimize} threw "An object could
not be cloned" before ipcRenderer.send ran. The buttons rendered but did
nothing. Wrap the handlers so the bridge is called with no arguments, and
pin that in the test.

* fix(desktop): remove recursive WSLg maximize bounds correction

* fix(desktop): select native Wayland before Electron initialization on WSLg

* fix(desktop): preserve ready pipe and debugger across WSLg launch

* fix(desktop): keep WSLg caption controls scoped to every window

* style(desktop): order window chrome event import

---------

Co-authored-by: null-runner <nicholas.mariani@hotmail.it>
2026-09-17 14:51:18 -04:00
ethernet
b4a294fff9 Merge origin/main; keep PM as plugin dependency owner
Reconcile plugin declarations and validation through PM's atomic generation publication; preserve external runtimes, target markers, and conflict refusal. Keep one source-update completion owner and port upstream lifecycle changes to the PM desktop/runtime paths.
2026-09-17 13:52:05 -04:00
teknium1
2bbff2a405 fix(desktop): exe identity stamping stops retrying when the rcedit binary itself is missing
apps/desktop/scripts/set-exe-identity.mjs::stampExeIdentity retried every rcedit
rejection with 0.5/1/2 s backoff. A failure to SPAWN rcedit (bin/rcedit-x64.exe
missing or not executable) is permanent, so the only effect was a 3.5 s delay
before after-pack.mjs's warning.

The npm rcedit wrapper (@malept/cross-spawn-promise) reports a spawn failure as
CrossSpawnError with the errno on `originalError.code`; a non-zero rcedit exit
("Unable to commit changes") is an ExitCodeError with a numeric `code`. Skip the
retry loop when `originalError.code ?? code` is ENOENT/EACCES and rethrow as-is.

Probe: stub rcedit rejecting with that shape -> before 4 attempts, sleeps
[500,1000,2000]; after 1 attempt, no sleeps. Transient-lock retry unchanged.

Part of #112544 (optional review atom).
2026-09-17 09:19:19 -07:00
liuhao1024
b08ec00a70 fix(desktop): hand preview guest links to the audited opener via a guest preload
Preview-pane guest pages (Streamlit's traceback "Ask Google" / "Ask …"
buttons, plain `<a target="_blank">` anchors) could not open anything: the
`<webview>` has no `allowpopups`, so Chromium drops the popup before any
handler runs. Opening from `setWindowOpenHandler` is banned
(GHSA-9f4c-93c8-jc8g, window-open-policy.ts), so this adds an explicit
click bridge instead:

- main.ts installs a guest preload through `will-attach-webview`, keyed on
  the `persist:hermes-preview` partition only.
- The preload forwards a clicked `_blank` anchor's resolved href to the
  host renderer via `ipcRenderer.sendToHost`; it opens nothing itself.
- PreviewPane admits the URL and routes it through the existing audited
  `hermes:openExternal` IPC.

Salvaged from PR #112959 (squash of its two commits).

Fixes #112941
2026-09-17 09:05:24 -07:00
teknium1
4146fcbd7a test(desktop): drop the source-text cpSync/rmSync guard
Root AGENTS.md bans tests that read source files and regex-match call sites;
the behavioral accented-tree restage test stays and is the invariant.
2026-09-17 00:26:32 -07:00
teknium1
afb9243f9c fix(desktop): widen libuv-only staging to get-windows and the rollback .bak wipe
The salvaged commits drop the cpSync/rmSync imports, but stageGetWindowsInto
landed on main after the PR was opened and still called both, so the module
threw ReferenceError on every platform (7 vitest failures on the cherry-picked
head). Route its six sites through copyFileSync/removeDirSync so get-windows
staging survives the same non-ASCII profile paths as node-pty.

preserveRollbackBackup had the same rmSync no-op as cleanStaleAppOutDir: a
surviving .bak makes renameSync fail, so the previous working build gets wiped
instead of kept as rollback material. Same existsSync -> removeDirSync fallback.

Earlier fixes for the same crash: #60480 (@liuhao1024), #61832 (@danilofalcao),
#76211, #103458, #109273, #111590.
Co-authored-by: liuhao1024 <liuhao1024@users.noreply.github.com>
Co-authored-by: danilofalcao <danilofalcao@users.noreply.github.com>
2026-09-17 00:26:32 -07:00
Kósa
8317fc8f4e fix(desktop): harden before-pack cleanup against the same non-ASCII rmSync no-op
Review follow-ups to the previous commit:

- before-pack.mjs: cleanStaleAppOutDir kept the retrying rmSync (its
  EBUSY resilience is worth keeping) but now verifies the tree is gone
  and falls back to the libuv-backed removeDirSync when the native
  rmSync silently no-ops on a non-ASCII path — previously it logged
  'removed stale unpacked dir' while the stale tree survived.
- removeDirSync: export it for reuse; handle a plain file or symlink at
  the path (rm -rf semantics) instead of throwing ENOTDIR, and tolerate
  broken symlinks via lstat.
- Add a source-level guard test: repo CI runs on Linux where the native
  implementations happen to work, so the accented staging test cannot
  catch a cpSync/rmSync reintroduction there.
- Link the upstream Node issues in the banner (nodejs/node#61878, fixed
  in v24.15.0; nodejs/node#56049, fixed in v24.13.1) and stop
  attributing the native rewrite to Node 24 — only the observation was
  on v24.11.1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-17 00:26:32 -07:00
Kósa
2d88b9b4df fix(desktop): stage node-pty without fs.cpSync/fs.rmSync (non-ASCII Windows paths)
Node 24's native rewrite of fs.cpSync/fs.rmSync mishandles non-ASCII
Windows paths (observed on v24.11.1 with an accented Windows user name,
i.e. the default %LOCALAPPDATA%\hermes install home):

- recursive cpSync fails with EIO "Access is denied" (errno 5) or
  hard-crashes the process (0xC0000409),
- cpSync over an existing file fails with a bogus errno-0
  'unlink' / "The operation completed successfully" error,
- rmSync({recursive, force}) silently deletes nothing, so the
  half-staged dist/node_modules/node-pty tree poisons every retry.

In practice this bricked the desktop build (and thus install/update)
for every Windows user whose profile path contains accented characters:
stage-native-deps.mjs died copying the conpty prebuild, and each rerun
failed earlier on the stale tree the silent rmSync left behind.

Replace all cpSync/rmSync uses with libuv-backed primitives that handle
these paths correctly on the same node.exe: copyFileSync for single
files, a manual mkdirSync+readdirSync+copyFileSync walk for directory
copies, and a manual unlinkSync/rmdirSync walk (verified with existsSync
afterwards, failing loudly instead of silently) for the dest cleanup.

Add a regression test that stages a fake node-pty tree - including a
nested conpty/ payload - into an accented src/dest path twice, so the
restage exercises the delete-then-recopy path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-17 00:26:32 -07:00
teknium1
3dcf0d49ad fix(install): let Rolldown name the missing binding; repair on every OS
The first cut derived the package from `binding-${platform}-${arch}` with an
exact-suffix match, which never matches Windows (`-msvc`) or Linux
(`-gnu`/`-musl`) names, so the repair only ever worked on macOS and
install.sh had to gate it there. Rolldown's own loader already resolves
platform, arch and libc and prints the exact `@rolldown/binding-*` it wanted
in its error chain; parse that instead and drop the gate. Also spawn npm
through a shell on Windows (Node refuses to spawn npm.cmd directly) and trim
the tests to the two invariants (no-op when it loads; installs exactly what
the loader asked for, then re-probes).
2026-09-17 00:25:57 -07:00
Gille
f67cad172e test(desktop): register Rolldown repair tests with Vitest 2026-09-17 00:25:57 -07:00
Gille
ae9f42accf fix(install): repair missing Rolldown bindings 2026-09-17 00:25:57 -07:00
teknium1
f501ebcf1e fix(desktop): size the rcedit retry to the observed lock window and retry any failure
The field report saw the commit still failing 5 s after a first attempt,
so 100/300 ms could exhaust before the scanner released the exe. Use
500 ms / 1 s / 2 s (four attempts, 3.5 s worst case) and retry every
rcedit failure instead of one error-message shape: a held handle can also
surface as a load/open error, and a permanent failure only costs 3.5 s
before after-pack.mjs swallows it.
2026-09-16 17:46:56 -07:00
KoNit-K
8ca920b52a fix(desktop): retry transient rcedit commit failures
Windows desktop rebuilds intermittently hit rcedit's `Fatal error: Unable
to commit changes` while a real-time file scanner holds the freshly packed
Hermes.exe; `stampExeIdentity` called rcedit exactly once, so the stamp
failed on the first lock. Retry the commit with bounded backoff before
surfacing the original error.

Trimmed from #112552: the after-pack.mjs injection seam and its test —
the hook's swallow-and-warn is pre-existing behaviour on main and needs no
change (the reporter's "Could not install the rebuilt desktop app" comes
from hermes_cli/main_desktop.py::_swap_staged_desktop_app's directory
rename, not from the stamp).
2026-09-16 17:46:56 -07:00
brooklyn!
76a4d3b62a feat(desktop): detect the dual-Command gesture on macOS 2026-09-16 19:13:25 -05:00
brooklyn!
691228447d perf(desktop): observe live workspace frame pacing without synthetic sessions 2026-09-16 06:20:40 -05:00
ethernet
0ecfcc2881 fix(ci): predict the desktop smoke's Hermes home from the baked bundle env
Commit desktop bundles can bake environment defaults/clears (--bundle-unset
HERMES_DESKTOP_USER_DATA_DIR, HERMES_HOME=null) that stomp the smoke driver's
--home/--user-data pin, so every native smoke threw "Desktop did not honor the
isolated home and userData directories".

Instead of pinning, the driver replays the bundle env over its launch env
through the same resolver the app runs, seeds the predicted home (bailing
rather than wiping when it is not empty), and verifies the app landed there
via a new hermesHome report on the version bridge. The --user-data equality
check now applies only when the artifact bakes no env.

- Extract the pure path resolver into electron/data-paths.mjs (data-paths.ts
  is now a typed re-export) so Node's type-stripped driver can import it.
- Add applyBundleEnvironment/validateBundleEnvironment as the pure twin of the
  bundle banner, pinned by a lockstep test.
- Record bundleEnv in the install stamp and add readBundledBundleEnv.
- Report hermesHome from hermes:version and assert it equals the predicted home.
2026-09-15 20:32:31 -04:00
ethie
3bbb10c250 fix(desktop): sign Mach-O members inside payload wheel zips before notarization
Apple's notary extracts every zip in the submitted archive and validates
each Mach-O; the notarization failure (err 4000) traced to uv-cache wheel
zips whose native members were never signed — electron-osx-sign reaches
only the extracted archive buckets, not zip members.

signWheelZipMembers (afterPack, darwin) walks the payload's uv-cache
wheel zips, signs .so/.dylib members with the same Developer ID identity
and keychain the chromium signer resolves, repacks, then re-extracts and
runs codesign --verify --strict on every signed member — the same
predicate the notary applies — failing the build loudly if a host's zip
semantics ever drop the embedded signature.

Proven on a real macOS 26 host: codesign may write signatures to extended
attributes for ad-hoc/generic binaries, and plain zip repack silently
drops xattrs; the verify gate converts that host behavior from a silent
notary rejection into a same-run build failure.
2026-09-14 22:22:29 -04:00
ethernet
936fb81005 fix(desktop): cap MSIX copilot extension Id at the 39-char manifest limit
Channel identities splice a 16-hex token into artifactNamePascal
(HermesChannel<token>), so Id="${artifactNamePascal}CopilotKeyProvider"
reached 47 chars and makeappx rejects the manifest with an opaque
maxLength 0x80080204. The Id only discriminates within the package;
DisplayName already carries the variant name, so pin the literal.
2026-09-14 20:30:57 -04:00
ethernet
5b30ae9991 refactor(desktop): delete cross-package retirement migration machinery (round 3 slice 3) 2026-09-14 12:59:17 -04:00