

























@@ -128,6 +128,42 @@ describe("doctor plugin manifest legacy contract repair", () => {
128128]);
129129});
130130131+it("collects legacy top-level plugin tool keys for migration", () => {
132+const pluginsRoot = makeTrustedBundledPluginsDir();
133+const root = path.join(pluginsRoot, "cortex");
134+fs.mkdirSync(root, { recursive: true });
135+writePackageJson(root);
136+writeManifest(root, {
137+id: "cortex",
138+tools: ["cortex_search", "cortex_remember"],
139+configSchema: { type: "object" },
140+});
141+142+const migrations = collectLegacyPluginManifestContractMigrations({
143+config: configWithPluginLoadPath(pluginsRoot),
144+env: {
145+ ...process.env,
146+},
147+manifestRoots: [pluginsRoot],
148+});
149+150+const manifestPath = path.join(root, "openclaw.plugin.json");
151+expect(migrations).toStrictEqual([
152+{
153+changeLines: [`- ${manifestPath}: moved tools to contracts.tools`],
154+ manifestPath,
155+nextRaw: {
156+id: "cortex",
157+contracts: {
158+tools: ["cortex_search", "cortex_remember"],
159+},
160+configSchema: { type: "object" },
161+},
162+pluginId: "cortex",
163+},
164+]);
165+});
166+131167it("rewrites legacy top-level capability keys into contracts", async () => {
132168const pluginsRoot = makeTrustedBundledPluginsDir();
133169const root = path.join(pluginsRoot, "openai");
@@ -169,6 +205,41 @@ describe("doctor plugin manifest legacy contract repair", () => {
169205});
170206});
171207208+it("removes duplicate legacy top-level plugin tools while keeping contracts.tools", async () => {
209+const pluginsRoot = makeTrustedBundledPluginsDir();
210+const root = path.join(pluginsRoot, "cortex");
211+fs.mkdirSync(root, { recursive: true });
212+writePackageJson(root);
213+writeManifest(root, {
214+id: "cortex",
215+tools: ["legacy_tool"],
216+contracts: {
217+tools: ["contract_tool"],
218+},
219+configSchema: { type: "object" },
220+});
221+222+await maybeRepairLegacyPluginManifestContracts({
223+config: configWithPluginLoadPath(pluginsRoot),
224+env: {
225+ ...process.env,
226+},
227+manifestRoots: [pluginsRoot],
228+runtime: createRuntime(),
229+prompter: createPrompter(),
230+note: vi.fn(),
231+});
232+233+const next = JSON.parse(fs.readFileSync(path.join(root, "openclaw.plugin.json"), "utf-8")) as {
234+tools?: string[];
235+contracts?: Record<string, string[]>;
236+};
237+expect(next.tools).toBeUndefined();
238+expect(next.contracts).toEqual({
239+tools: ["contract_tool"],
240+});
241+});
242+172243it("ignores non-object contracts payloads when collecting migrations", () => {
173244const pluginsRoot = makeTrustedBundledPluginsDir();
174245const root = path.join(pluginsRoot, "openai");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。