fix(natives): preserve workspace addons

This commit is contained in:
David Andrews
2026-07-30 15:27:08 -04:00
committed by can1357
parent 89f00d9a9b
commit 726844b019
4 changed files with 40 additions and 15 deletions
+4 -1
View File
@@ -20,6 +20,10 @@
- Fixed the `computer` tool advertising Wayland support that never worked: on the default rootless XWayland (GNOME/KDE/sway) the X11 root window has no readable pixmap, so root `GetImage` failed on every screenshot with a raw `BadMatch` protocol dump. `Monitor::all` now probes root drawability at initialization and fails fast with an actionable `DESKTOP_BACKEND_UNAVAILABLE` message naming the rootless-XWayland constraint, and `docs/computer-use.md` now lists rootless XWayland as unsupported ([#7085](https://github.com/can1357/oh-my-pi/issues/7085)).
### Fixed
- Fixed workspace native addon loads preferring an installed leaf package over the workspace build ([#7059](https://github.com/can1357/oh-my-pi/pull/7059) by [@GratefulDave](https://github.com/GratefulDave)).
## [17.2.0] - 2026-07-30
### Changed
@@ -29,7 +33,6 @@
### Fixed
- Fixed workspace native addon loads preferring an installed leaf package over the workspace build ([#7059](https://github.com/can1357/oh-my-pi/pull/7059) by [@GratefulDave](https://github.com/GratefulDave)).
## [17.1.8] - 2026-07-28
+1
View File
@@ -57,6 +57,7 @@ export function resolveLoaderCandidates(input: ResolveLoaderCandidatesInput): st
export interface InitLoaderContextOverrides {
nativeDir?: string;
platform?: NodeJS.Platform | string;
isCompiledBinary?: boolean;
leafPackageDir?: string | null;
}
+9 -7
View File
@@ -129,7 +129,8 @@ export function shouldStageNodeModulesAddon({ platform, isCompiledBinary, native
// Check both separators independently of the host's `path.sep`: this helper
// is shared by the loader (running on Windows with `\`) and the test suite
// (typically running on POSIX hosts when CI executes the regression test).
return nativeDir.includes("\\node_modules\\") || nativeDir.includes("/node_modules/");
const normalizedNativeDir = nativeDir.toLowerCase();
return normalizedNativeDir.includes("\\node_modules\\") || normalizedNativeDir.includes("/node_modules/");
}
/**
@@ -699,17 +700,18 @@ function buildHelpMessage(ctx) {
* helpers from this file doesn't trigger AVX2 detection or filesystem probes.
*/
/**
* @param {{ nativeDir?: string; isCompiledBinary?: boolean; leafPackageDir?: string | null }} [overrides]
* @param {{ nativeDir?: string; platform?: NodeJS.Platform | string; isCompiledBinary?: boolean; leafPackageDir?: string | null }} [overrides]
*/
export function initLoaderContext(overrides = {}) {
const platformTag = `${process.platform}-${process.arch}`;
const platform = overrides.platform ?? process.platform;
const platformTag = `${platform}-${process.arch}`;
const packageVersion = packageJson.version;
const nativeDir = overrides.nativeDir ?? path.join(import.meta.dir, "..", "native");
const execDir = path.dirname(process.execPath);
const nativesDir = getNativesDir();
const versionedDir = path.join(nativesDir, packageVersion);
const userDataDir =
process.platform === "win32"
platform === "win32"
? path.join(process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local"), "omp")
: path.join(os.homedir(), ".local", "bin");
@@ -720,7 +722,7 @@ export function initLoaderContext(overrides = {}) {
env: process.env,
importMetaUrl: import.meta.url,
});
const normalizedNativeDir = nativeDir.toLowerCase();
const normalizedNativeDir = platform === "win32" ? nativeDir.toLowerCase() : nativeDir;
const isWorkspaceLoad =
!isCompiledBinary &&
!normalizedNativeDir.includes("\\node_modules\\") &&
@@ -732,9 +734,9 @@ export function initLoaderContext(overrides = {}) {
? resolveLeafPackageDir(platformTag)
: overrides.leafPackageDir;
const stageFromNodeModules = shouldStageNodeModulesAddon({
platform: process.platform,
platform,
isCompiledBinary,
nativeDir,
nativeDir: normalizedNativeDir,
});
const selectedVariant = resolveCpuVariant(getVariantOverride());
+26 -7
View File
@@ -115,27 +115,39 @@ describe("windows native addon staging", () => {
expect(candidates).not.toContain(userDataBaseline);
});
it("omits leaf-package candidates only for workspace loads", () => {
it("classifies only Windows node_modules paths case-insensitively", () => {
const leafPackageDir = "/tmp/node_modules/@oh-my-pi/pi-natives-darwin-arm64";
const uppercaseNodeModulesNativeDir = "/tmp/NODE_MODULES/@oh-my-pi/pi-natives/native";
const variantCacheKey = "__PI_NATIVE_VARIANT_CACHE";
const previousVariantCache = process.env[variantCacheKey];
try {
const workspace = initLoaderContext({
platform: "linux",
isCompiledBinary: false,
nativeDir: "/tmp/oh-my-pi/packages/natives/native",
leafPackageDir,
});
const installed = initLoaderContext({
platform: "linux",
isCompiledBinary: false,
nativeDir: "/tmp/node_modules/@oh-my-pi/pi-natives/native",
leafPackageDir,
});
const installedWithUppercaseNodeModules = initLoaderContext({
const uppercaseWorkspace = initLoaderContext({
platform: "linux",
isCompiledBinary: false,
nativeDir: "/tmp/NODE_MODULES/@oh-my-pi/pi-natives/native",
nativeDir: uppercaseNodeModulesNativeDir,
leafPackageDir,
});
const leafCandidate = path.join(leafPackageDir, workspace.addonFilenames[0]);
const uppercaseWindowsInstall = initLoaderContext({
platform: "win32",
isCompiledBinary: false,
nativeDir: uppercaseNodeModulesNativeDir,
leafPackageDir,
});
const leafCandidate = path.join(leafPackageDir, installed.addonFilenames[0]);
const windowsLeafCandidate = path.join(leafPackageDir, uppercaseWindowsInstall.addonFilenames[0]);
const workspaceCandidate = path.join(uppercaseNodeModulesNativeDir, uppercaseWorkspace.addonFilenames[0]);
expect(workspace.isWorkspaceLoad).toBe(true);
expect(workspace.leafPackageDir).toBeNull();
@@ -143,9 +155,16 @@ describe("windows native addon staging", () => {
expect(installed.isWorkspaceLoad).toBe(false);
expect(installed.leafPackageDir).toBe(leafPackageDir);
expect(installed.candidates).toContain(leafCandidate);
expect(installedWithUppercaseNodeModules.isWorkspaceLoad).toBe(false);
expect(installedWithUppercaseNodeModules.leafPackageDir).toBe(leafPackageDir);
expect(installedWithUppercaseNodeModules.candidates).toContain(leafCandidate);
expect(installed.candidates[0]).toBe(leafCandidate);
expect(uppercaseWorkspace.isWorkspaceLoad).toBe(true);
expect(uppercaseWorkspace.leafPackageDir).toBeNull();
expect(uppercaseWorkspace.candidates[0]).toBe(workspaceCandidate);
expect(uppercaseWindowsInstall.isWorkspaceLoad).toBe(false);
expect(uppercaseWindowsInstall.leafPackageDir).toBe(leafPackageDir);
expect(uppercaseWindowsInstall.stageFromNodeModules).toBe(true);
expect(uppercaseWindowsInstall.candidates).toContain(windowsLeafCandidate);
} finally {
if (previousVariantCache === undefined) delete process.env[variantCacheKey];
else process.env[variantCacheKey] = previousVariantCache;