




















@@ -0,0 +1,98 @@
1+import fs from "node:fs/promises";
2+import os from "node:os";
3+import path from "node:path";
4+import { afterEach, describe, expect, it } from "vitest";
5+import {
6+resolveManagedNpmRootDependencySpec,
7+upsertManagedNpmRootDependency,
8+} from "./npm-managed-root.js";
9+10+const tempDirs: string[] = [];
11+12+async function makeTempRoot(): Promise<string> {
13+const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-npm-managed-root-"));
14+tempDirs.push(dir);
15+return dir;
16+}
17+18+afterEach(async () => {
19+await Promise.all(tempDirs.splice(0).map((dir) => fs.rm(dir, { recursive: true, force: true })));
20+});
21+22+describe("managed npm root", () => {
23+it("keeps existing plugin dependencies when adding another managed plugin", async () => {
24+const npmRoot = await makeTempRoot();
25+await fs.writeFile(
26+path.join(npmRoot, "package.json"),
27+`${JSON.stringify(
28+ {
29+ private: true,
30+ dependencies: {
31+ "@openclaw/discord": "2026.5.2",
32+ },
33+ devDependencies: {
34+ fixture: "1.0.0",
35+ },
36+ },
37+ null,
38+ 2,
39+ )}\n`,
40+);
41+42+await upsertManagedNpmRootDependency({
43+ npmRoot,
44+packageName: "@openclaw/feishu",
45+dependencySpec: "2026.5.2",
46+});
47+48+await expect(
49+fs.readFile(path.join(npmRoot, "package.json"), "utf8").then((raw) => JSON.parse(raw)),
50+).resolves.toEqual({
51+private: true,
52+dependencies: {
53+"@openclaw/discord": "2026.5.2",
54+"@openclaw/feishu": "2026.5.2",
55+},
56+devDependencies: {
57+fixture: "1.0.0",
58+},
59+});
60+});
61+62+it("uses the requested selector before falling back to resolved version", () => {
63+expect(
64+resolveManagedNpmRootDependencySpec({
65+parsedSpec: {
66+name: "@openclaw/discord",
67+raw: "@openclaw/discord@stable",
68+selector: "stable",
69+selectorKind: "tag",
70+selectorIsPrerelease: false,
71+},
72+resolution: {
73+name: "@openclaw/discord",
74+version: "2026.5.2",
75+resolvedSpec: "@openclaw/discord@2026.5.2",
76+resolvedAt: "2026-05-03T00:00:00.000Z",
77+},
78+}),
79+).toBe("stable");
80+81+expect(
82+resolveManagedNpmRootDependencySpec({
83+parsedSpec: {
84+name: "@openclaw/discord",
85+raw: "@openclaw/discord",
86+selectorKind: "none",
87+selectorIsPrerelease: false,
88+},
89+resolution: {
90+name: "@openclaw/discord",
91+version: "2026.5.2",
92+resolvedSpec: "@openclaw/discord@2026.5.2",
93+resolvedAt: "2026-05-03T00:00:00.000Z",
94+},
95+}),
96+).toBe("2026.5.2");
97+});
98+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。