From aaa6b05f3ba8834d99c9b64ecb632298c253418d Mon Sep 17 00:00:00 2001 From: Ayaan Zaidi Date: Wed, 15 Apr 2026 10:23:05 +0530 Subject: [PATCH] fix(update): preserve legacy global verify --- src/infra/update-global.test.ts | 22 ++++++++++++++++++++++ src/infra/update-global.ts | 24 ++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/infra/update-global.test.ts b/src/infra/update-global.test.ts index 92237821770..c6f768f183d 100644 --- a/src/infra/update-global.test.ts +++ b/src/infra/update-global.test.ts @@ -397,4 +397,26 @@ describe("update global helpers", () => { ); }); }); + + it("falls back to legacy sidecar verification when the inventory is missing", async () => { + await withTempDir({ prefix: "openclaw-update-global-legacy-" }, async (packageRoot) => { + await fs.writeFile( + path.join(packageRoot, "package.json"), + JSON.stringify({ name: "openclaw", version: "1.0.0" }), + "utf-8", + ); + for (const relativePath of BUNDLED_RUNTIME_SIDECAR_PATHS) { + const absolutePath = path.join(packageRoot, relativePath); + await fs.mkdir(path.dirname(absolutePath), { recursive: true }); + await fs.writeFile(absolutePath, "export {};\n", "utf-8"); + } + + await expect(collectInstalledGlobalPackageErrors({ packageRoot })).resolves.toEqual([]); + + await fs.rm(path.join(packageRoot, MATRIX_HELPER_API)); + await expect(collectInstalledGlobalPackageErrors({ packageRoot })).resolves.toContain( + `missing bundled runtime sidecar ${MATRIX_HELPER_API}`, + ); + }); + }); }); diff --git a/src/infra/update-global.ts b/src/infra/update-global.ts index 3761319fe9e..45e568b1dcf 100644 --- a/src/infra/update-global.ts +++ b/src/infra/update-global.ts @@ -2,9 +2,13 @@ import fsSync from "node:fs"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "../plugins/runtime-sidecar-paths.js"; import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js"; import { pathExists } from "../utils.js"; -import { collectPackageDistInventoryErrors } from "./package-dist-inventory.js"; +import { + PACKAGE_DIST_INVENTORY_RELATIVE_PATH, + collectPackageDistInventoryErrors, +} from "./package-dist-inventory.js"; import { readPackageVersion } from "./package-json.js"; import { applyPathPrepend } from "./path-prepend.js"; @@ -35,6 +39,7 @@ const NPM_GLOBAL_INSTALL_OMIT_OPTIONAL_FLAGS = [ "--omit=optional", ...NPM_GLOBAL_INSTALL_QUIET_FLAGS, ] as const; +const MISSING_PACKAGE_DIST_INVENTORY_ERROR = `missing package dist inventory ${PACKAGE_DIST_INVENTORY_RELATIVE_PATH}`; function normalizePackageTarget(value: string): string { return value.trim(); @@ -89,7 +94,22 @@ export async function collectInstalledGlobalPackageErrors(params: { `expected installed version ${params.expectedVersion}, found ${installedVersion ?? ""}`, ); } - errors.push(...(await collectPackageDistInventoryErrors(params.packageRoot))); + const distErrors = await collectPackageDistInventoryErrors(params.packageRoot); + if (distErrors.length === 1 && distErrors[0] === MISSING_PACKAGE_DIST_INVENTORY_ERROR) { + errors.push(...(await collectLegacyInstalledGlobalPackageErrors(params.packageRoot))); + return errors; + } + errors.push(...distErrors); + return errors; +} + +async function collectLegacyInstalledGlobalPackageErrors(packageRoot: string): Promise { + const errors: string[] = []; + for (const relativePath of BUNDLED_RUNTIME_SIDECAR_PATHS) { + if (!(await pathExists(path.join(packageRoot, relativePath)))) { + errors.push(`missing bundled runtime sidecar ${relativePath}`); + } + } return errors; }