


































@@ -8,11 +8,17 @@ import {
88import { createSuiteTempRootTracker } from "./test-helpers/fs-fixtures.js";
991010const runCommandWithTimeoutMock = vi.fn();
11+const resolveOpenClawPackageRootSyncMock = vi.fn();
11121213vi.mock("../process/exec.js", () => ({
1314runCommandWithTimeout: (...args: unknown[]) => runCommandWithTimeoutMock(...args),
1415}));
151617+vi.mock("../infra/openclaw-root.js", () => ({
18+resolveOpenClawPackageRootSync: (...args: unknown[]) =>
19+resolveOpenClawPackageRootSyncMock(...args),
20+}));
21+1622vi.resetModules();
17231824const { installPluginFromNpmPackArchive, installPluginFromNpmSpec, PLUGIN_INSTALL_ERROR_CODE } =
@@ -372,6 +378,14 @@ afterAll(() => {
372378373379beforeEach(() => {
374380runCommandWithTimeoutMock.mockReset();
381+resolveOpenClawPackageRootSyncMock.mockReset();
382+const hostRoot = suiteTempRootTracker.makeTempDir();
383+fs.writeFileSync(
384+path.join(hostRoot, "package.json"),
385+`${JSON.stringify({ name: "openclaw", version: "0.0.0-test" }, null, 2)}\n`,
386+"utf8",
387+);
388+resolveOpenClawPackageRootSyncMock.mockReturnValue(hostRoot);
375389vi.unstubAllEnvs();
376390});
377391@@ -713,6 +727,43 @@ describe("installPluginFromNpmSpec", () => {
713727},
714728);
715729730+it("rejects managed npm plugins when their openclaw peer link cannot be repaired", async () => {
731+const stateDir = suiteTempRootTracker.makeTempDir();
732+const npmRoot = path.join(stateDir, "npm");
733+const warnings: string[] = [];
734+735+resolveOpenClawPackageRootSyncMock.mockReturnValue(null);
736+mockNpmViewAndInstall({
737+spec: "@openclaw/codex@2026.5.7",
738+packageName: "@openclaw/codex",
739+version: "2026.5.7",
740+pluginId: "@openclaw/codex",
741+ npmRoot,
742+peerDependencies: { openclaw: ">=2026.5.7" },
743+});
744+745+const result = await installPluginFromNpmSpec({
746+spec: "@openclaw/codex@2026.5.7",
747+npmDir: npmRoot,
748+logger: { info: () => {}, warn: (message) => warnings.push(message) },
749+});
750+751+expect(result.ok).toBe(false);
752+if (result.ok) {
753+return;
754+}
755+expect(result.error).toContain("@openclaw/codex");
756+expect(result.error).toContain("plugin-local node_modules/openclaw link");
757+expect(warnings).toEqual(
758+expect.arrayContaining([expect.stringContaining("Could not locate openclaw package root")]),
759+);
760+expect(fs.existsSync(path.join(npmRoot, "node_modules", "@openclaw", "codex"))).toBe(false);
761+const managedManifest = JSON.parse(
762+fs.readFileSync(path.join(npmRoot, "package.json"), "utf8"),
763+) as { dependencies?: Record<string, string> };
764+expect(managedManifest.dependencies?.["@openclaw/codex"]).toBeUndefined();
765+});
766+716767it.runIf(process.platform !== "win32")(
717768"repairs root openclaw materialized by npm peer handling",
718769async () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。