fix(coding-agent): sync pi-natives on omp update

bun install -g <pkg>@<v> did not reliably re-resolve transitive
optionalDependencies, so @oh-my-pi/pi-natives and the platform leaf
@oh-my-pi/pi-natives-<tag> stayed at the previous version while
@oh-my-pi/pi-coding-agent moved. The loader’s validateLoadedBindings
then aborted because the .node file exposed the old
__piNativesV<old> sentinel instead of __piNativesV<new>.

buildBunInstallArgs now pins @oh-my-pi/pi-natives and (when the
running tag is one the release pipeline publishes) the platform
leaf to the same version it installs for @oh-my-pi/pi-coding-agent,
so bun replaces all three in lock-step. The leaf is gated by the
same SUPPORTED_PLATFORMS set the loader uses, so unsupported tags
still surface the original 'no matching version' diagnostic instead
of EBADPLATFORM.

Fixes #1824
This commit is contained in:
roboomp
2026-06-04 06:52:31 +00:00
parent f6fca1f5cd
commit 32f07b24f7
3 changed files with 88 additions and 3 deletions
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased]
### Fixed
- Fixed `omp update` leaving `@oh-my-pi/pi-natives` and the platform-specific `@oh-my-pi/pi-natives-<tag>` leaf at the previous version on `bun install -g` updates, so the next launch loaded a stale `.node` file and aborted at `validateLoadedBindings` with `The .node file on disk is from a different release than this loader`. `omp update` now pins the native addon core and the platform leaf to the same version it installs for `@oh-my-pi/pi-coding-agent` ([#1824](https://github.com/can1357/oh-my-pi/issues/1824)).
## [15.9.0] - 2026-06-04
### Breaking Changes
+57 -2
View File
@@ -27,6 +27,33 @@ const PACKAGE = "@oh-my-pi/pi-coding-agent";
*/
const NPM_REGISTRY = "https://registry.npmjs.org/";
/**
* Core native addon package. Bumped in lock-step with {@link PACKAGE} so the
* version sentinel the loader looks up at runtime matches the `.node` on
* disk; see {@link buildBunInstallArgs} for why this must be installed
* explicitly rather than inherited as a transitive dependency.
*/
const NATIVES_PACKAGE = "@oh-my-pi/pi-natives";
/**
* Platform tags the release pipeline publishes as
* `@oh-my-pi/pi-natives-<tag>` leaves. Mirrors `SUPPORTED_PLATFORMS` in
* `packages/natives/native/loader-state.js` and `LEAF_TARGETS` in
* `packages/natives/scripts/gen-npm-packages.ts`; kept here as the local
* source of truth so the update path stays free of cross-package imports.
*/
const SUPPORTED_NATIVE_TAGS: ReadonlySet<string> = new Set([
"linux-x64",
"linux-arm64",
"darwin-x64",
"darwin-arm64",
"win32-x64",
]);
function currentNativeTag(): string {
return `${process.platform}-${process.arch}`;
}
interface ReleaseInfo {
tag: string;
version: string;
@@ -319,9 +346,37 @@ export async function replaceBinaryForUpdate(options: BinaryReplacementOptions):
*
* Together these two flags make `omp update` produce exactly the registry
* lookup the version check just performed. See #1686.
*
* Also pins {@link NATIVES_PACKAGE} and the platform-specific
* `@oh-my-pi/pi-natives-<tag>` leaf to `expectedVersion`. `bun install -g`
* does not reliably refresh transitive `optionalDependencies` when the
* top-level package is the only one bumped, so the native addon and its
* version sentinel can drift out of sync with the freshly installed
* `@oh-my-pi/pi-coding-agent` and the loader aborts at
* `validateLoadedBindings` on the next launch
* (`The .node file on disk is from a different release than this loader`).
* Listing the natives explicitly forces bun to replace them in lock-step.
* The leaf is added only on tags the release pipeline actually publishes
* ({@link SUPPORTED_NATIVE_TAGS}) so unsupported platforms still fail with
* the original "no matching version" message instead of `EBADPLATFORM`.
* See #1824.
*/
export function buildBunInstallArgs(expectedVersion: string): string[] {
return ["install", "-g", "--no-cache", `--registry=${NPM_REGISTRY}`, `${PACKAGE}@${expectedVersion}`];
export function buildBunInstallArgs(
expectedVersion: string,
nativeTag: string = currentNativeTag(),
): string[] {
const args = [
"install",
"-g",
"--no-cache",
`--registry=${NPM_REGISTRY}`,
`${PACKAGE}@${expectedVersion}`,
`${NATIVES_PACKAGE}@${expectedVersion}`,
];
if (SUPPORTED_NATIVE_TAGS.has(nativeTag)) {
args.push(`${NATIVES_PACKAGE}-${nativeTag}@${expectedVersion}`);
}
return args;
}
/**
+27 -1
View File
@@ -45,7 +45,8 @@ describe("update-cli bun install command", () => {
// is already pointed at the official registry but its cache predates
// the release.
// See https://github.com/can1357/oh-my-pi/issues/1686.
expect(buildBunInstallArgs("15.7.6")).toEqual([
const args = buildBunInstallArgs("15.7.6", "linux-x64");
expect(args.slice(0, 5)).toEqual([
"install",
"-g",
"--no-cache",
@@ -53,6 +54,31 @@ describe("update-cli bun install command", () => {
"@oh-my-pi/pi-coding-agent@15.7.6",
]);
});
it("pins the native addon core and the platform-specific leaf to the same version so the loader sentinel cannot drift on supported tags", () => {
// Regression: bun install -g <pkg>@<v> would update only the top-level
// package, leaving @oh-my-pi/pi-natives and @oh-my-pi/pi-natives-<tag>
// at their previous version. The next launch then loaded a stale .node
// file and aborted at validateLoadedBindings with `The .node file on
// disk is from a different release than this loader`. See
// https://github.com/can1357/oh-my-pi/issues/1824.
for (const tag of ["linux-x64", "linux-arm64", "darwin-x64", "darwin-arm64", "win32-x64"]) {
const args = buildBunInstallArgs("15.9.0", tag);
expect(args).toContain("@oh-my-pi/pi-natives@15.9.0");
expect(args).toContain(`@oh-my-pi/pi-natives-${tag}@15.9.0`);
}
});
it("omits the leaf on unsupported platform tags so an EBADPLATFORM swap does not mask the underlying `no matching version` error", () => {
// Defensive: an unsupported tag (e.g. linux-arm32) still installs the
// core natives package — which will fail at module load if the platform
// truly is unsupported — but we never request a leaf the release
// pipeline doesn't publish, otherwise bun aborts with EBADPLATFORM
// and hides the real diagnostic from `loadNative`'s aggregated error.
const args = buildBunInstallArgs("15.9.0", "linux-arm");
expect(args).toContain("@oh-my-pi/pi-natives@15.9.0");
expect(args.some(arg => arg.startsWith("@oh-my-pi/pi-natives-"))).toBe(false);
});
});
describe("update-cli binary replacement", () => {